|
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