|
1 | 1 | /** |
2 | | - * @typedef {import('unist').Node} Node |
3 | | - * @typedef {import('unist').Position} Position |
4 | | - * @typedef {import('unist').Point} Point |
5 | | - * |
6 | | - * @typedef Options |
7 | | - * @property {boolean} [showPositions=true] |
| 2 | + * @typedef {import('./lib/index.js')} Options |
8 | 3 | * |
9 | 4 | * @typedef {Options} InspectOptions |
10 | 5 | * Deprecated, use `Options`. |
11 | 6 | */ |
12 | 7 |
|
13 | | -import {color} from './color.js' |
14 | | - |
15 | | -/* c8 ignore next */ |
16 | | -export const inspect = color ? inspectColor : inspectNoColor |
17 | | - |
18 | | -const own = {}.hasOwnProperty |
19 | | - |
20 | | -const bold = ansiColor(1, 22) |
21 | | -const dim = ansiColor(2, 22) |
22 | | -const yellow = ansiColor(33, 39) |
23 | | -const green = ansiColor(32, 39) |
24 | | - |
25 | | -// ANSI color regex. |
26 | | -/* eslint-disable no-control-regex */ |
27 | | -const colorExpression = |
28 | | - /(?:(?:\u001B\[)|\u009B)(?:\d{1,3})?(?:(?:;\d{0,3})*)?[A-M|f-m]|\u001B[A-M]/g |
29 | | -/* eslint-enable no-control-regex */ |
30 | | - |
31 | | -/** |
32 | | - * Inspects a node, without using color. |
33 | | - * |
34 | | - * @param {unknown} node |
35 | | - * @param {Options} [options] |
36 | | - * @returns {string} |
37 | | - */ |
38 | | -export function inspectNoColor(node, options) { |
39 | | - return inspectColor(node, options).replace(colorExpression, '') |
40 | | -} |
41 | | - |
42 | | -/** |
43 | | - * Inspects a node, using color. |
44 | | - * |
45 | | - * @param {unknown} tree |
46 | | - * @param {Options} [options] |
47 | | - * @returns {string} |
48 | | - */ |
49 | | -export function inspectColor(tree, options = {}) { |
50 | | - const positions = |
51 | | - options.showPositions === null || options.showPositions === undefined |
52 | | - ? true |
53 | | - : options.showPositions |
54 | | - |
55 | | - return inspectValue(tree) |
56 | | - |
57 | | - /** |
58 | | - * @param {unknown} node |
59 | | - * @returns {string} |
60 | | - */ |
61 | | - function inspectValue(node) { |
62 | | - if (node && typeof node === 'object' && 'length' in node) { |
63 | | - // @ts-expect-error looks like a list of nodes. |
64 | | - return inspectNodes(node) |
65 | | - } |
66 | | - |
67 | | - // @ts-expect-error looks like a single node. |
68 | | - if (node && node.type) { |
69 | | - // @ts-expect-error looks like a single node. |
70 | | - return inspectTree(node) |
71 | | - } |
72 | | - |
73 | | - return inspectNonTree(node) |
74 | | - } |
75 | | - |
76 | | - /** |
77 | | - * @param {unknown} value |
78 | | - * @returns {string} |
79 | | - */ |
80 | | - function inspectNonTree(value) { |
81 | | - return JSON.stringify(value) |
82 | | - } |
83 | | - |
84 | | - /** |
85 | | - * @param {Array<Node>} nodes |
86 | | - * @returns {string} |
87 | | - */ |
88 | | - function inspectNodes(nodes) { |
89 | | - const size = String(nodes.length - 1).length |
90 | | - /** @type {Array<string>} */ |
91 | | - const result = [] |
92 | | - let index = -1 |
93 | | - |
94 | | - while (++index < nodes.length) { |
95 | | - result.push( |
96 | | - dim( |
97 | | - (index < nodes.length - 1 ? '├' : '└') + |
98 | | - '─' + |
99 | | - String(index).padEnd(size) |
100 | | - ) + |
101 | | - ' ' + |
102 | | - indent( |
103 | | - inspectValue(nodes[index]), |
104 | | - (index < nodes.length - 1 ? dim('│') : ' ') + ' '.repeat(size + 2), |
105 | | - true |
106 | | - ) |
107 | | - ) |
108 | | - } |
109 | | - |
110 | | - return result.join('\n') |
111 | | - } |
112 | | - |
113 | | - /** |
114 | | - * @param {Record<string, unknown>} object |
115 | | - * @returns {string} |
116 | | - */ |
117 | | - // eslint-disable-next-line complexity |
118 | | - function inspectFields(object) { |
119 | | - /** @type {Array<string>} */ |
120 | | - const result = [] |
121 | | - /** @type {string} */ |
122 | | - let key |
123 | | - |
124 | | - for (key in object) { |
125 | | - /* c8 ignore next 1 */ |
126 | | - if (!own.call(object, key)) continue |
127 | | - |
128 | | - const value = object[key] |
129 | | - /** @type {string} */ |
130 | | - let formatted |
131 | | - |
132 | | - if ( |
133 | | - value === undefined || |
134 | | - // Standard keys defined by unist that we format differently. |
135 | | - // <https://github.com/syntax-tree/unist> |
136 | | - key === 'type' || |
137 | | - key === 'value' || |
138 | | - key === 'children' || |
139 | | - key === 'position' || |
140 | | - // Ignore `name` (from xast) and `tagName` (from `hast`) when string. |
141 | | - (typeof value === 'string' && (key === 'name' || key === 'tagName')) |
142 | | - ) { |
143 | | - continue |
144 | | - } |
145 | | - |
146 | | - // A single node. |
147 | | - if ( |
148 | | - value && |
149 | | - typeof value === 'object' && |
150 | | - // @ts-expect-error looks like a node. |
151 | | - value.type && |
152 | | - key !== 'data' && |
153 | | - key !== 'attributes' && |
154 | | - key !== 'properties' |
155 | | - ) { |
156 | | - // @ts-expect-error looks like a node. |
157 | | - formatted = inspectTree(value) |
158 | | - } |
159 | | - // A list of nodes. |
160 | | - else if ( |
161 | | - value && |
162 | | - Array.isArray(value) && |
163 | | - // Looks like a node. |
164 | | - // type-coverage:ignore-next-line |
165 | | - value[0] && |
166 | | - // Looks like a node. |
167 | | - // type-coverage:ignore-next-line |
168 | | - value[0].type |
169 | | - ) { |
170 | | - formatted = '\n' + inspectNodes(value) |
171 | | - } else { |
172 | | - formatted = inspectNonTree(value) |
173 | | - } |
174 | | - |
175 | | - result.push( |
176 | | - key + dim(':') + (/\s/.test(formatted.charAt(0)) ? '' : ' ') + formatted |
177 | | - ) |
178 | | - } |
179 | | - |
180 | | - return indent( |
181 | | - result.join('\n'), |
182 | | - // @ts-expect-error looks like a parent node. |
183 | | - (object.children && object.children.length > 0 ? dim('│') : ' ') + ' ' |
184 | | - ) |
185 | | - } |
186 | | - |
187 | | - /** |
188 | | - * @param {Node} node |
189 | | - * @returns {string} |
190 | | - */ |
191 | | - function inspectTree(node) { |
192 | | - const result = [formatNode(node)] |
193 | | - // @ts-expect-error: looks like a record. |
194 | | - const fields = inspectFields(node) |
195 | | - // @ts-expect-error looks like a parent. |
196 | | - const content = inspectNodes(node.children || []) |
197 | | - if (fields) result.push(fields) |
198 | | - if (content) result.push(content) |
199 | | - return result.join('\n') |
200 | | - } |
201 | | - |
202 | | - /** |
203 | | - * Colored node formatter. |
204 | | - * |
205 | | - * @param {Node} node |
206 | | - * @returns {string} |
207 | | - */ |
208 | | - function formatNode(node) { |
209 | | - const result = [bold(node.type)] |
210 | | - /** @type {string|undefined} */ |
211 | | - // @ts-expect-error: might be available. |
212 | | - const kind = node.tagName || node.name |
213 | | - const position = positions ? stringifyPosition(node.position) : '' |
214 | | - |
215 | | - if (typeof kind === 'string') { |
216 | | - result.push('<', kind, '>') |
217 | | - } |
218 | | - |
219 | | - // @ts-expect-error: looks like a parent. |
220 | | - if (node.children) { |
221 | | - // @ts-expect-error looks like a parent. |
222 | | - result.push(dim('['), yellow(node.children.length), dim(']')) |
223 | | - // @ts-expect-error: looks like a literal. |
224 | | - } else if (typeof node.value === 'string') { |
225 | | - // @ts-expect-error: looks like a literal. |
226 | | - result.push(' ', green(inspectNonTree(node.value))) |
227 | | - } |
228 | | - |
229 | | - if (position) { |
230 | | - result.push(' ', dim('('), position, dim(')')) |
231 | | - } |
232 | | - |
233 | | - return result.join('') |
234 | | - } |
235 | | -} |
236 | | - |
237 | | -/** |
238 | | - * @param {string} value |
239 | | - * @param {string} indentation |
240 | | - * @param {boolean} [ignoreFirst=false] |
241 | | - * @returns {string} |
242 | | - */ |
243 | | -function indent(value, indentation, ignoreFirst) { |
244 | | - const lines = value.split('\n') |
245 | | - let index = ignoreFirst ? 0 : -1 |
246 | | - |
247 | | - if (!value) return value |
248 | | - |
249 | | - while (++index < lines.length) { |
250 | | - lines[index] = indentation + lines[index] |
251 | | - } |
252 | | - |
253 | | - return lines.join('\n') |
254 | | -} |
255 | | - |
256 | | -/** |
257 | | - * @param {Position|undefined} [value] |
258 | | - * @returns {string} |
259 | | - */ |
260 | | -function stringifyPosition(value) { |
261 | | - /** @type {Position} */ |
262 | | - // @ts-expect-error: fine. |
263 | | - const position = value || {} |
264 | | - /** @type {Array<string>} */ |
265 | | - const result = [] |
266 | | - /** @type {Array<string>} */ |
267 | | - const positions = [] |
268 | | - /** @type {Array<string>} */ |
269 | | - const offsets = [] |
270 | | - |
271 | | - point(position.start) |
272 | | - point(position.end) |
273 | | - |
274 | | - if (positions.length > 0) result.push(positions.join('-')) |
275 | | - if (offsets.length > 0) result.push(offsets.join('-')) |
276 | | - |
277 | | - return result.join(', ') |
278 | | - |
279 | | - /** |
280 | | - * @param {Point} value |
281 | | - */ |
282 | | - function point(value) { |
283 | | - if (value) { |
284 | | - positions.push((value.line || 1) + ':' + (value.column || 1)) |
285 | | - |
286 | | - if ('offset' in value) { |
287 | | - offsets.push(String(value.offset || 0)) |
288 | | - } |
289 | | - } |
290 | | - } |
291 | | -} |
292 | | - |
293 | | -/** |
294 | | - * Factory to wrap values in ANSI colours. |
295 | | - * |
296 | | - * @param {number} open |
297 | | - * @param {number} close |
298 | | - * @returns {function(string): string} |
299 | | - */ |
300 | | -function ansiColor(open, close) { |
301 | | - return color |
302 | | - |
303 | | - /** |
304 | | - * @param {string} value |
305 | | - * @returns {string} |
306 | | - */ |
307 | | - function color(value) { |
308 | | - return '\u001B[' + open + 'm' + value + '\u001B[' + close + 'm' |
309 | | - } |
310 | | -} |
| 8 | +export {inspect, inspectColor, inspectNoColor} from './lib/index.js' |
0 commit comments