Skip to content

add optional colors: boolean flag to Options #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 100 additions & 38 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,77 +7,117 @@
* Configuration.
* @property {boolean | null | undefined} [showPositions=true]
* Whether to include positional information (default: `true`).
* @property {boolean | null | undefined} [colors]
* Whether to include ANSI colors (default: `true` on Node, `false` otherwise).
*
* @typedef Style
* Styling functions.
* @property {(_: string) => string} bold
* Style a node type.
* @property {(_: string) => string} dim
* Style structural punctuation.
* @property {(_: string) => string} yellow
* Style the numeric count of node children.
* @property {(_: string) => string} green
* Style a non-tree value.
Comment on lines +21 to +22
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you type the function explicitly somewhere? So @callback StyleFunction ..., and use that in each case here? Then we can describe parameters/return types

*
* @typedef State
* Info passed around.
* @property {boolean} showPositions
* Whether to include positional information.
* @property {Style} style
* Rendering stylization.
*/

import {color} from '#conditional-color'
import {color as useColorByDefault} from '#conditional-color'

/**
* Inspect a node, with color in Node, without color in browsers.
*
* @param tree
* @param {unknown} tree
* Tree to inspect.
* @param options
* Configuration (optional).
* @returns
* @param {Options | null | undefined} [options]
* Configuration.
* @returns {string}
* Pretty printed `tree`.
*/
/* c8 ignore next */
export const inspect = color ? inspectColor : inspectNoColor
export function inspect(tree, options) {
const useColor =
!options || options.colors === null || options.colors === undefined
? useColorByDefault
: options.colors

const style = useColor
? {
bold: ansiColor(1, 22),
dim: ansiColor(2, 22),
yellow: ansiColor(33, 39),
green: ansiColor(32, 39)
}
: {
bold: noColor,
dim: noColor,
yellow: noColor,
green: noColor
}

const own = {}.hasOwnProperty
/** @type {State} */
const state = {
style,
showPositions:
!options ||
options.showPositions === null ||
options.showPositions === undefined
? true
: options.showPositions
}

const bold = ansiColor(1, 22)
const dim = ansiColor(2, 22)
const yellow = ansiColor(33, 39)
const green = ansiColor(32, 39)
return inspectValue(tree, state)
}

// ANSI color regex.
/* eslint-disable no-control-regex */
const colorExpression =
/(?:(?:\u001B\[)|\u009B)(?:\d{1,3})?(?:(?:;\d{0,3})*)?[A-M|f-m]|\u001B[A-M]/g
/* eslint-enable no-control-regex */
const own = {}.hasOwnProperty

/**
* Inspect a node, without color.
*
* @deprecated
* Use `inspect` with the option `{colors: false}`.
*
* @param {unknown} tree
* Tree to inspect.
* @param {Options | null | undefined} [options]
* @param {Omit<Options, 'colors'> | null | undefined} [options]
* Configuration.
* @returns {string}
* Pretty printed `tree`.
*/
export function inspectNoColor(tree, options) {
return inspectColor(tree, options).replace(colorExpression, '')
/* c8 ignore next 3 */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this ignored? Wouldn’t it be better to add a test?

const optionsWithNoColor = useColorByDefault
? Object.assign({}, options, {colors: false})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using spreads instead of object.assign is fine by me!

: options
return inspect(tree, optionsWithNoColor)
}

/**
* Inspects a node, using color.
*
* @deprecated
* Use `inspect` with the option `{colors: true}`.
*
* @param {unknown} tree
* Tree to inspect.
* @param {Options | null | undefined} [options]
* @param {Omit<Options, 'colors'> | null | undefined} [options]
* Configuration (optional).
* @returns {string}
* Pretty printed `tree`.
*/
export function inspectColor(tree, options) {
/** @type {State} */
const state = {
showPositions:
!options ||
options.showPositions === null ||
options.showPositions === undefined
? true
: options.showPositions
}

return inspectValue(tree, state)
/* c8 ignore next 3 */
const optionsWithColor = useColorByDefault
? options
: Object.assign({}, options, {colors: true})
return inspect(tree, optionsWithColor)
}

/**
Expand Down Expand Up @@ -125,22 +165,24 @@ function inspectNonTree(value) {
* Formatted nodes.
*/
function inspectNodes(nodes, state) {
const style = state.style
const size = String(nodes.length - 1).length
/** @type {Array<string>} */
const result = []
let index = -1

while (++index < nodes.length) {
result.push(
dim(
style.dim(
(index < nodes.length - 1 ? '├' : '└') +
'─' +
String(index).padEnd(size)
) +
' ' +
indent(
inspectValue(nodes[index], state),
(index < nodes.length - 1 ? dim('│') : ' ') + ' '.repeat(size + 2),
(index < nodes.length - 1 ? style.dim('│') : ' ') +
' '.repeat(size + 2),
true
)
)
Expand All @@ -161,6 +203,8 @@ function inspectNodes(nodes, state) {
*/
// eslint-disable-next-line complexity
function inspectFields(object, state) {
const style = state.style

/** @type {Array<string>} */
const result = []
/** @type {string} */
Expand Down Expand Up @@ -205,14 +249,17 @@ function inspectFields(object, state) {
}

result.push(
key + dim(':') + (/\s/.test(formatted.charAt(0)) ? '' : ' ') + formatted
key +
style.dim(':') +
(/\s/.test(formatted.charAt(0)) ? '' : ' ') +
formatted
)
}

return indent(
result.join('\n'),
(isArrayUnknown(object.children) && object.children.length > 0
? dim('│')
? style.dim('│')
: ' ') + ' '
)
}
Expand Down Expand Up @@ -253,7 +300,8 @@ function inspectTree(node, state) {
* Formatted node.
*/
function formatNode(node, state) {
const result = [bold(node.type)]
const style = state.style
const result = [style.bold(node.type)]
// Cast as record to allow indexing.
const map = /** @type {Record<string, unknown>} */ (
/** @type {unknown} */ (node)
Expand All @@ -266,13 +314,17 @@ function formatNode(node, state) {
}

if (isArrayUnknown(map.children)) {
result.push(dim('['), yellow(String(map.children.length)), dim(']'))
result.push(
style.dim('['),
style.yellow(String(map.children.length)),
style.dim(']')
)
} else if (typeof map.value === 'string') {
result.push(' ', green(inspectNonTree(map.value)))
result.push(' ', style.green(inspectNonTree(map.value)))
}

if (position) {
result.push(' ', dim('('), position, dim(')'))
result.push(' ', style.dim('('), position, style.dim(')'))
}

return result.join('')
Expand Down Expand Up @@ -377,6 +429,16 @@ function ansiColor(open, close) {
}
}

/**
* Style function which does not perform colorization.
*
* @param {string} value
* @return {string}
*/
function noColor(value) {
return value
}

/**
* @param {unknown} value
* @returns {value is Node}
Expand Down
55 changes: 55 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ const chalkEnabled = new Chalk({level: 1})

const paragraph = 'Some simple text. Other “sentence”.'

/**
* Split `text` on newlines, keeping the first `count`.
*
* @param {string} text
* @param {number} count
* @return string
* The first `count` lines of `text`.
*/
const lines = (text, count) => text.split('\n').slice(0, count).join('\n')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Describe the parameters, use a regular function, and move it to the bottom of the file. (and: use the braces for the return type, {string})


test('inspect()', async function (t) {
await t.test('should expose the public api', async function () {
assert.deepEqual(Object.keys(await import('unist-util-inspect')).sort(), [
Expand Down Expand Up @@ -420,6 +430,51 @@ test('inspect()', async function (t) {
].join('\n')
)
})

await t.test('inspect(…, {colors: false})', async function () {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
await t.test('inspect(…, {colors: false})', async function () {
await t.test('should support `colors: false`', async function () {

assert.equal(
lines(inspect(retext().parse(paragraph), {colors: false}), 2),
[
'RootNode[1] (1:1-1:36, 0-35)',
'└─0 ParagraphNode[3] (1:1-1:36, 0-35)'
].join('\n')
)
})

await t.test(
'inspect(…, {colors?: true | null | undefined})',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
'inspect(…, {colors?: true | null | undefined})',
'should support `colors: true` (default)',

async function () {
const expectedOutput = [
chalkEnabled.bold('RootNode') +
chalkEnabled.dim('[') +
chalkEnabled.yellow('1') +
chalkEnabled.dim(']') +
' ' +
chalkEnabled.dim('(') +
'1:1-1:36, 0-35' +
chalkEnabled.dim(')'),
chalkEnabled.dim('└─0') +
' ' +
chalkEnabled.bold('ParagraphNode') +
chalkEnabled.dim('[') +
chalkEnabled.yellow('3') +
chalkEnabled.dim(']') +
' ' +
chalkEnabled.dim('(') +
'1:1-1:36, 0-35' +
chalkEnabled.dim(')')
].join('\n')

const parsed = retext().parse(paragraph)

assert.equal(lines(inspect(parsed, {colors: true}), 2), expectedOutput)
assert.equal(lines(inspect(parsed, {colors: null}), 2), expectedOutput)
assert.equal(
lines(inspect(parsed, {colors: undefined}), 2),
expectedOutput
)
}
)
})

test('inspectNoColor()', async function () {
Expand Down
Loading