Skip to content

Commit d5e0dcd

Browse files
wooormrwe
andcommitted
Add color option
Closes GH-21. Co-authored-by: Robert Estelle <robertestelle@gmail.com>
1 parent 142f645 commit d5e0dcd

File tree

7 files changed

+123
-66
lines changed

7 files changed

+123
-66
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@
55
coverage/
66
node_modules/
77
yarn.lock
8+
!/lib/types.d.ts
89
!/index.d.ts

index.d.ts

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,2 @@
1+
export type {Options, Options as InspectOptions} from './lib/types.js'
12
export {inspectColor, inspectNoColor, inspect} from './lib/index.js'
2-
3-
// To do: next major: remove.
4-
/**
5-
* Deprecated, use `Options`.
6-
*/
7-
export type InspectOptions = Options
8-
9-
/**
10-
* Configuration.
11-
*/
12-
export interface Options {
13-
/**
14-
* Whether to include positional information (default: `true`).
15-
*/
16-
showPositions?: boolean | null | undefined
17-
}

lib/index.js

Lines changed: 63 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,77 @@
11
/**
22
* @import {Options} from 'unist-util-inspect'
33
* @import {Node} from 'unist'
4+
* @import {State} from './types.js'
45
*/
56

6-
/**
7-
* @typedef State
8-
* Info passed around.
9-
* @property {boolean} showPositions
10-
* Whether to include positional information.
11-
*/
7+
import {color as colorDefault} from '#conditional-color'
128

13-
import {color} from '#conditional-color'
9+
/** @type {Options} */
10+
const emptyOptions = {}
11+
12+
// To do: next major (?): use `Object.hasOwn`.
13+
const own = {}.hasOwnProperty
1414

1515
/**
16-
* Inspect a node, with color in Node, without color in browsers.
16+
* Inspect a node, without color.
1717
*
18-
* @param tree
18+
* @param {unknown} tree
1919
* Tree to inspect.
20-
* @param options
21-
* Configuration (optional).
22-
* @returns
20+
* @param {Options | null | undefined} [options]
21+
* Configuration.
22+
* @returns {string}
2323
* Pretty printed `tree`.
2424
*/
25-
/* c8 ignore next */
26-
export const inspect = color ? inspectColor : inspectNoColor
27-
28-
// To do: next major (?): use `Object.hasOwn`.
29-
const own = {}.hasOwnProperty
30-
31-
const bold = ansiColor(1, 22)
32-
const dim = ansiColor(2, 22)
33-
const yellow = ansiColor(33, 39)
34-
const green = ansiColor(32, 39)
25+
export function inspect(tree, options) {
26+
const settings = options || emptyOptions
27+
const color =
28+
typeof settings.color === 'boolean' ? settings.color : colorDefault
29+
const showPositions =
30+
typeof settings.showPositions === 'boolean' ? settings.showPositions : true
31+
/** @type {State} */
32+
const state = {
33+
bold: color ? ansiColor(1, 22) : identity,
34+
dim: color ? ansiColor(2, 22) : identity,
35+
green: color ? ansiColor(32, 39) : identity,
36+
showPositions,
37+
yellow: color ? ansiColor(33, 39) : identity
38+
}
3539

36-
// ANSI color regex.
37-
/* eslint-disable no-control-regex */
38-
const colorExpression =
39-
/(?:(?:\u001B\[)|\u009B)(?:\d{1,3})?(?:(?:;\d{0,3})*)?[A-M|f-m]|\u001B[A-M]/g
40-
/* eslint-enable no-control-regex */
40+
return inspectValue(tree, state)
41+
}
4142

43+
// To do: remove.
4244
/**
4345
* Inspect a node, without color.
4446
*
47+
* @deprecated
48+
* Use `inspect` instead, with `color: false`.
4549
* @param {unknown} tree
4650
* Tree to inspect.
47-
* @param {Options | null | undefined} [options]
51+
* @param {Omit<Options, 'color'> | null | undefined} [options]
4852
* Configuration.
4953
* @returns {string}
5054
* Pretty printed `tree`.
5155
*/
5256
export function inspectNoColor(tree, options) {
53-
return inspectColor(tree, options).replace(colorExpression, '')
57+
return inspect(tree, {...options, color: false})
5458
}
5559

60+
// To do: remove.
5661
/**
5762
* Inspects a node, using color.
5863
*
64+
* @deprecated
65+
* Use `inspect` instead, with `color: true`.
5966
* @param {unknown} tree
6067
* Tree to inspect.
61-
* @param {Options | null | undefined} [options]
68+
* @param {Omit<Options, 'color'> | null | undefined} [options]
6269
* Configuration (optional).
6370
* @returns {string}
6471
* Pretty printed `tree`.
6572
*/
6673
export function inspectColor(tree, options) {
67-
/** @type {State} */
68-
const state = {
69-
showPositions:
70-
!options ||
71-
options.showPositions === null ||
72-
options.showPositions === undefined
73-
? true
74-
: options.showPositions
75-
}
76-
77-
return inspectValue(tree, state)
74+
return inspect(tree, {...options, color: true})
7875
}
7976

8077
/**
@@ -129,15 +126,16 @@ function inspectNodes(nodes, state) {
129126

130127
while (++index < nodes.length) {
131128
result.push(
132-
dim(
129+
state.dim(
133130
(index < nodes.length - 1 ? '├' : '└') +
134131
'─' +
135132
String(index).padEnd(size)
136133
) +
137134
' ' +
138135
indent(
139136
inspectValue(nodes[index], state),
140-
(index < nodes.length - 1 ? dim('│') : ' ') + ' '.repeat(size + 2),
137+
(index < nodes.length - 1 ? state.dim('│') : ' ') +
138+
' '.repeat(size + 2),
141139
true
142140
)
143141
)
@@ -202,14 +200,17 @@ function inspectFields(object, state) {
202200
}
203201

204202
result.push(
205-
key + dim(':') + (/\s/.test(formatted.charAt(0)) ? '' : ' ') + formatted
203+
key +
204+
state.dim(':') +
205+
(/\s/.test(formatted.charAt(0)) ? '' : ' ') +
206+
formatted
206207
)
207208
}
208209

209210
return indent(
210211
result.join('\n'),
211212
(isArrayUnknown(object.children) && object.children.length > 0
212-
? dim('│')
213+
? state.dim('│')
213214
: ' ') + ' '
214215
)
215216
}
@@ -250,7 +251,7 @@ function inspectTree(node, state) {
250251
* Formatted node.
251252
*/
252253
function formatNode(node, state) {
253-
const result = [bold(node.type)]
254+
const result = [state.bold(node.type)]
254255
// Cast as record to allow indexing.
255256
const map = /** @type {Record<string, unknown>} */ (
256257
/** @type {unknown} */ (node)
@@ -263,13 +264,17 @@ function formatNode(node, state) {
263264
}
264265

265266
if (isArrayUnknown(map.children)) {
266-
result.push(dim('['), yellow(String(map.children.length)), dim(']'))
267+
result.push(
268+
state.dim('['),
269+
state.yellow(String(map.children.length)),
270+
state.dim(']')
271+
)
267272
} else if (typeof map.value === 'string') {
268-
result.push(' ', green(inspectNonTree(map.value)))
273+
result.push(' ', state.green(inspectNonTree(map.value)))
269274
}
270275

271276
if (position) {
272-
result.push(' ', dim('('), position, dim(')'))
277+
result.push(' ', state.dim('('), position, state.dim(')'))
273278
}
274279

275280
return result.join('')
@@ -394,3 +399,12 @@ function isNode(value) {
394399
function isArrayUnknown(node) {
395400
return Array.isArray(node)
396401
}
402+
403+
/**
404+
* @template T
405+
* @param {T} value
406+
* @returns {T}
407+
*/
408+
function identity(value) {
409+
return value
410+
}

lib/types.d.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* Configuration.
3+
*/
4+
export interface Options {
5+
/**
6+
* Whether to use ANSI colors (default: `true` in Node, `false` otherwise).
7+
*/
8+
color?: boolean | null | undefined
9+
/**
10+
* Whether to include positional info (default: `true`).
11+
*/
12+
showPositions?: boolean | null | undefined
13+
}
14+
15+
/**
16+
* Info passed around.
17+
*/
18+
export interface State {
19+
/**
20+
* Node type.
21+
*/
22+
bold: Style
23+
/**
24+
* Punctuation.
25+
*/
26+
dim: Style
27+
/**
28+
* Non-tree value.
29+
*/
30+
green: Style
31+
/**
32+
* Whether to include positional info.
33+
*/
34+
showPositions: boolean
35+
/**
36+
* Numeric count of node children.
37+
*/
38+
yellow: Style
39+
}
40+
41+
/**
42+
* Style a string.
43+
*
44+
* @param value
45+
* Value to style.
46+
* @returns
47+
* Styled value.
48+
*/
49+
type Style = (value: string) => string

lib/types.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Types only.
2+
export {}

readme.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,15 @@ Pretty printed `tree` (`string`).
113113

114114
### `inspectColor(tree[, options])`
115115

116+
> 🪦 **Deprecated**: use `color` option of `inspect`.
117+
116118
Inspect a tree, with color.
117119
Otherwise same as [`inspect`][api-inspect].
118120

119121
### `inspectNoColor(tree[, options])`
120122

123+
> 🪦 **Deprecated**: use `color` option of `inspect`.
124+
121125
Inspect a tree, without color.
122126
Otherwise same as [`inspect`][api-inspect].
123127

@@ -127,6 +131,8 @@ Configuration (TypeScript type).
127131

128132
###### Fields
129133

134+
* `color` (`boolean`, default: `true` in Node, `false` otherwise)
135+
— whether to use ANSI colors
130136
* `showPositions` (`boolean`, default: `true`)
131137
— whether to include positional information
132138

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@
1212
"target": "es2022"
1313
},
1414
"exclude": ["coverage/", "node_modules/"],
15-
"include": ["**/*.js", "index.d.ts"]
15+
"include": ["**/*.js", "lib/types.d.ts", "index.d.ts"]
1616
}

0 commit comments

Comments
 (0)