diff --git a/.github/workflows/bb.yml b/.github/workflows/bb.yml index 291ab09..0198fc3 100644 --- a/.github/workflows/bb.yml +++ b/.github/workflows/bb.yml @@ -2,7 +2,7 @@ name: bb on: issues: types: [opened, reopened, edited, closed, labeled, unlabeled] - pull_request: + pull_request_target: types: [opened, reopened, edited, closed, labeled, unlabeled] jobs: main: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fe284ad..69924a4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,5 +17,5 @@ jobs: strategy: matrix: node: - - lts/erbium + - lts/fermium - node diff --git a/.npmrc b/.npmrc index 43c97e7..9951b11 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,2 @@ package-lock=false +ignore-scripts=true diff --git a/color-browser.js b/color-browser.js index 8dc60f5..46a8f89 100644 --- a/color-browser.js +++ b/color-browser.js @@ -1 +1 @@ -export var color = false +export const color = false diff --git a/color.js b/color.js index 30ae76c..905c60a 100644 --- a/color.js +++ b/color.js @@ -1 +1 @@ -export var color = true +export const color = true diff --git a/index.js b/index.js index 1f3ea24..0081f26 100644 --- a/index.js +++ b/index.js @@ -1,33 +1,38 @@ -import {color} from './color.js' - /** * @typedef {import('unist').Node} Node * @typedef {import('unist').Position} Position * @typedef {import('unist').Point} Point * - * @typedef {Object} InspectOptions + * @typedef Options * @property {boolean} [showPositions=true] + * + * @typedef {Options} InspectOptions + * Deprecated, use `Options`. */ +import {color} from './color.js' + /* c8 ignore next */ -export var inspect = color ? inspectColor : inspectNoColor +export const inspect = color ? inspectColor : inspectNoColor -var own = {}.hasOwnProperty +const own = {}.hasOwnProperty -var bold = ansiColor(1, 22) -var dim = ansiColor(2, 22) -var yellow = ansiColor(33, 39) -var green = ansiColor(32, 39) +const bold = ansiColor(1, 22) +const dim = ansiColor(2, 22) +const yellow = ansiColor(33, 39) +const green = ansiColor(32, 39) // ANSI color regex. -/* eslint-disable-next-line no-control-regex */ -var colorExpression = /(?:(?:\u001B\[)|\u009B)(?:\d{1,3})?(?:(?:;\d{0,3})*)?[A-M|f-m]|\u001B[A-M]/g +/* 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 */ /** * Inspects a node, without using color. * * @param {unknown} node - * @param {InspectOptions} [options] + * @param {Options} [options] * @returns {string} */ export function inspectNoColor(node, options) { @@ -38,14 +43,12 @@ export function inspectNoColor(node, options) { * Inspects a node, using color. * * @param {unknown} tree - * @param {InspectOptions} [options] + * @param {Options} [options] * @returns {string} */ -export function inspectColor(tree, options) { - var positions = - !options || - options.showPositions === null || - options.showPositions === undefined +export function inspectColor(tree, options = {}) { + const positions = + options.showPositions === null || options.showPositions === undefined ? true : options.showPositions @@ -57,13 +60,13 @@ export function inspectColor(tree, options) { */ function inspectValue(node) { if (node && typeof node === 'object' && 'length' in node) { - // @ts-ignore looks like a list of nodes. + // @ts-expect-error looks like a list of nodes. return inspectNodes(node) } - // @ts-ignore looks like a single node. + // @ts-expect-error looks like a single node. if (node && node.type) { - // @ts-ignore looks like a single node. + // @ts-expect-error looks like a single node. return inspectTree(node) } @@ -79,21 +82,26 @@ export function inspectColor(tree, options) { } /** - * @param {Node[]} nodes + * @param {Array} nodes * @returns {string} */ function inspectNodes(nodes) { - /** @type {Array.} */ - var result = [] - var index = -1 + const size = String(nodes.length - 1).length + /** @type {Array} */ + const result = [] + let index = -1 while (++index < nodes.length) { result.push( - dim((index < nodes.length - 1 ? '├' : '└') + '─' + index) + + dim( + (index < nodes.length - 1 ? '├' : '└') + + '─' + + String(index).padEnd(size) + ) + ' ' + indent( inspectValue(nodes[index]), - (index < nodes.length - 1 ? dim('│') : ' ') + ' ', + (index < nodes.length - 1 ? dim('│') : ' ') + ' '.repeat(size + 2), true ) ) @@ -103,24 +111,23 @@ export function inspectColor(tree, options) { } /** - * @param {Object.} object + * @param {Record} object * @returns {string} */ + // eslint-disable-next-line complexity function inspectFields(object) { - /** @type {Array.} */ - var result = [] - /** @type {string} */ - var key - /** @type {unknown} */ - var value + /** @type {Array} */ + const result = [] /** @type {string} */ - var formatted + let key for (key in object) { /* c8 ignore next 1 */ if (!own.call(object, key)) continue - value = object[key] + const value = object[key] + /** @type {string} */ + let formatted if ( value === undefined || @@ -140,24 +147,26 @@ export function inspectColor(tree, options) { if ( value && typeof value === 'object' && - // @ts-ignore looks like a node. + // @ts-expect-error looks like a node. value.type && key !== 'data' && key !== 'attributes' && key !== 'properties' ) { - // @ts-ignore looks like a node. + // @ts-expect-error looks like a node. formatted = inspectTree(value) } // A list of nodes. else if ( value && - typeof value === 'object' && - 'length' in value && + Array.isArray(value) && + // Looks like a node. + // type-coverage:ignore-next-line value[0] && + // Looks like a node. + // type-coverage:ignore-next-line value[0].type ) { - // @ts-ignore looks like a list of nodes. formatted = '\n' + inspectNodes(value) } else { formatted = inspectNonTree(value) @@ -170,7 +179,7 @@ export function inspectColor(tree, options) { return indent( result.join('\n'), - // @ts-ignore looks like a parent node. + // @ts-expect-error looks like a parent node. (object.children && object.children.length > 0 ? dim('│') : ' ') + ' ' ) } @@ -180,10 +189,11 @@ export function inspectColor(tree, options) { * @returns {string} */ function inspectTree(node) { - var result = [formatNode(node)] - var fields = inspectFields(node) - // @ts-ignore looks like a parent. - var content = inspectNodes(node.children || []) + const result = [formatNode(node)] + // @ts-expect-error: looks like a record. + const fields = inspectFields(node) + // @ts-expect-error looks like a parent. + const content = inspectNodes(node.children || []) if (fields) result.push(fields) if (content) result.push(content) return result.join('\n') @@ -196,18 +206,23 @@ export function inspectColor(tree, options) { * @returns {string} */ function formatNode(node) { - var result = [bold(node.type)] - var kind = node.tagName || node.name - var position = positions ? stringifyPosition(node.position) : '' + const result = [bold(node.type)] + /** @type {string|undefined} */ + // @ts-expect-error: might be available. + const kind = node.tagName || node.name + const position = positions ? stringifyPosition(node.position) : '' if (typeof kind === 'string') { result.push('<', kind, '>') } + // @ts-expect-error: looks like a parent. if (node.children) { - // @ts-ignore looks like a parent. + // @ts-expect-error looks like a parent. result.push(dim('['), yellow(node.children.length), dim(']')) + // @ts-expect-error: looks like a literal. } else if (typeof node.value === 'string') { + // @ts-expect-error: looks like a literal. result.push(' ', green(inspectNonTree(node.value))) } @@ -226,8 +241,8 @@ export function inspectColor(tree, options) { * @returns {string} */ function indent(value, indentation, ignoreFirst) { - var lines = value.split('\n') - var index = ignoreFirst ? 0 : -1 + const lines = value.split('\n') + let index = ignoreFirst ? 0 : -1 if (!value) return value @@ -239,19 +254,19 @@ function indent(value, indentation, ignoreFirst) { } /** - * @param {Position} value + * @param {Position|undefined} [value] * @returns {string} */ function stringifyPosition(value) { /** @type {Position} */ - // @ts-ignore - var position = value || {} - /** @type {Array.} */ - var result = [] - /** @type {Array.} */ - var positions = [] - /** @type {Array.} */ - var offsets = [] + // @ts-expect-error: fine. + const position = value || {} + /** @type {Array} */ + const result = [] + /** @type {Array} */ + const positions = [] + /** @type {Array} */ + const offsets = [] point(position.start) point(position.end) diff --git a/package.json b/package.json index a367b3d..6557954 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "unist-util-inspect", - "version": "7.0.0", + "version": "7.0.1", "description": "unist utility to inspect nodes", "license": "MIT", "keywords": [ @@ -47,21 +47,21 @@ "devDependencies": { "@types/tape": "^4.0.0", "c8": "^7.0.0", - "chalk": "^4.0.0", - "hastscript": "^6.0.0", + "chalk": "^5.0.0", + "hastscript": "^7.0.0", "prettier": "^2.0.0", - "remark-cli": "^9.0.0", - "remark-preset-wooorm": "^8.0.0", - "retext": "^7.0.0", + "remark-cli": "^11.0.0", + "remark-preset-wooorm": "^9.0.0", + "retext": "^8.0.0", "rimraf": "^3.0.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^7.0.0", "tape": "^5.0.0", "type-coverage": "^2.0.0", "typescript": "^4.0.0", - "unist-builder": "^2.0.0", - "xast-util-from-xml": "^1.0.0", - "xastscript": "^2.0.0", - "xo": "^0.38.0" + "unist-builder": "^3.0.0", + "xast-util-from-xml": "^2.0.0", + "xastscript": "^3.0.0", + "xo": "^0.51.0" }, "scripts": { "prepack": "npm run build && npm run format", @@ -80,13 +80,7 @@ "trailingComma": "none" }, "xo": { - "prettier": true, - "rules": { - "complexity": "off", - "import/no-mutable-exports": "off", - "no-var": "off", - "prefer-arrow-callback": "off" - } + "prettier": true }, "remarkConfig": { "plugins": [ diff --git a/readme.md b/readme.md index b3f5fa7..7b65e77 100644 --- a/readme.md +++ b/readme.md @@ -8,26 +8,63 @@ [![Backers][backers-badge]][collective] [![Chat][chat-badge]][chat] -[**unist**][unist] utility to inspect nodes. +[unist][] utility to inspect trees. -## Install +## Contents + +* [What is this?](#what-is-this) +* [When should I use this?](#when-should-i-use-this) +* [Install](#install) +* [Use](#use) +* [API](#api) + * [`inspect(node[, options])`](#inspectnode-options) + * [`inspectColor(node[, options])`](#inspectcolornode-options) + * [`inspectNoColor(node[, options])`](#inspectnocolornode-options) +* [Types](#types) +* [Compatibility](#compatibility) +* [Contribute](#contribute) +* [License](#license) + +## What is this? + +This is a utility pretty prints the tree. -This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c): -Node 12+ is needed to use it and it must be `import`ed instead of `require`d. +## When should I use this? + +This utility pretty prints the tree in a format that is made custom for unist +trees, which is terser than the often verbose and repetitive JSON, +to more easily spot bugs and see what’s going on in the tree. + +## Install -[npm][]: +This package is [ESM only][esm]. +In Node.js (version 12.20+, 14.14+, 16.0+, 18.0+), install with [npm][]: ```sh npm install unist-util-inspect ``` +In Deno with [`esm.sh`][esmsh]: + +```js +import {inspect} from "https://esm.sh/unist-util-inspect@8" +``` + +In browsers with [`esm.sh`][esmsh]: + +```html + +``` + ## Use ```js -var u = require('unist-builder') -var inspect = require('unist-util-inspect') +import {u} from 'unist-builder' +import {inspect} from 'unist-util-inspect' -var tree = u('root', [ +const tree = u('root', [ u('literal', '1'), u('parent', [ u('void', {id: 'a'}), @@ -54,13 +91,13 @@ root[2] ## API -This package exports the following identifiers: `inspect`, `inspectColor`, and +This package exports the identifiers `inspect`, `inspectColor`, and `inspectNoColor`. There is no default export. ### `inspect(node[, options])` -Inspect the given [`node`][node]. +Inspect the given `node` ([`Node`][node]). By default, colors are added in Node, and not in other places. See below on how to change that. @@ -68,22 +105,34 @@ See below on how to change that. Whether to include positional information (`boolean`, default: `true`). -###### Returns +##### Returns -`string` — String representing `node`. +Pretty printed `node` (`string`). ### `inspectColor(node[, options])` -Inspect, with ANSI color sequences (default in Node). +Inspect with ANSI color sequences (default in Node, Deno). ### `inspectNoColor(node[, options])` -Inspect, but without ANSI color sequences (default in browser). +Inspect without ANSI color sequences (default in browser, `react-native`). + +## Types + +This package is fully typed with [TypeScript][]. +It exports the additional type `Options`. + +## Compatibility + +Projects maintained by the unified collective are compatible with all maintained +versions of Node.js. +As of now, that is Node.js 12.20+, 14.14+, 16.0+, and 18.0+. +Our projects sometimes work with older versions, but this is not guaranteed. ## Contribute -See [`contributing.md` in `syntax-tree/.github`][contributing] for ways to get -started. +See [`contributing.md`][contributing] in [`syntax-tree/.github`][health] for +ways to get started. See [`support.md`][support] for ways to get help. This project has a [code of conduct][coc]. @@ -122,18 +171,26 @@ abide by its terms. [chat]: https://github.com/syntax-tree/unist/discussions -[unist]: https://github.com/syntax-tree/unist - [npm]: https://docs.npmjs.com/cli/install -[node]: https://github.com/syntax-tree/unist#node +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + +[esmsh]: https://esm.sh + +[typescript]: https://www.typescriptlang.org [license]: license [author]: https://wooorm.com -[contributing]: https://github.com/syntax-tree/.github/blob/HEAD/contributing.md +[health]: https://github.com/syntax-tree/.github + +[contributing]: https://github.com/syntax-tree/.github/blob/main/contributing.md + +[support]: https://github.com/syntax-tree/.github/blob/main/support.md -[support]: https://github.com/syntax-tree/.github/blob/HEAD/support.md +[coc]: https://github.com/syntax-tree/.github/blob/main/code-of-conduct.md -[coc]: https://github.com/syntax-tree/.github/blob/HEAD/code-of-conduct.md +[unist]: https://github.com/syntax-tree/unist + +[node]: https://github.com/syntax-tree/unist#node diff --git a/test.js b/test.js index 5dbe2b9..2fd5caa 100644 --- a/test.js +++ b/test.js @@ -1,26 +1,25 @@ import test from 'tape' -import chalk from 'chalk' +/* eslint-disable-next-line unicorn/import-style */ +import {Chalk} from 'chalk' import strip from 'strip-ansi' -import u from 'unist-builder' -import h from 'hastscript' -import x from 'xastscript' -// @ts-ignore remove when typed. -import retext from 'retext' -// @ts-ignore remove when typed. -import fromXml from 'xast-util-from-xml' +import {u} from 'unist-builder' +import {h} from 'hastscript' +import {x} from 'xastscript' +import {retext} from 'retext' +import {fromXml} from 'xast-util-from-xml' import {inspect, inspectColor, inspectNoColor} from './index.js' -var chalkEnabled = new chalk.Instance({level: 1}) +const chalkEnabled = new Chalk({level: 1}) -var paragraph = 'Some simple text. Other “sentence”.' +const paragraph = 'Some simple text. Other “sentence”.' -test('inspect', function (t) { +test('inspect', (t) => { t.equal(typeof inspect, 'function', 'should be a `function`') t.end() }) -test('inspect()', function (t) { +test('inspect()', (t) => { t.equal( strip(inspect(retext().parse(paragraph))), [ @@ -56,7 +55,7 @@ test('inspect()', function (t) { 'should work with a list of nodes' ) - t.test('should work on non-nodes', function (st) { + t.test('should work on non-nodes', (st) => { st.equal(strip(inspect('foo')), '"foo"') st.equal(strip(inspect(null)), 'null') st.equal(strip(inspect(Number.NaN)), 'null') @@ -65,6 +64,43 @@ test('inspect()', function (t) { st.end() }) + t.equal( + strip( + inspect( + Array.from({length: 11}).map((/** @type {undefined} */ d, i) => ({ + type: 'text', + value: String(i), + data: {id: String.fromCodePoint(97 + i)} + })) + ) + ), + [ + '├─0 text "0"', + '│ data: {"id":"a"}', + '├─1 text "1"', + '│ data: {"id":"b"}', + '├─2 text "2"', + '│ data: {"id":"c"}', + '├─3 text "3"', + '│ data: {"id":"d"}', + '├─4 text "4"', + '│ data: {"id":"e"}', + '├─5 text "5"', + '│ data: {"id":"f"}', + '├─6 text "6"', + '│ data: {"id":"g"}', + '├─7 text "7"', + '│ data: {"id":"h"}', + '├─8 text "8"', + '│ data: {"id":"i"}', + '├─9 text "9"', + '│ data: {"id":"j"}', + '└─10 text "10"', + ' data: {"id":"k"}' + ].join('\n'), + 'should align and indent large numbers' + ) + t.equal( strip( inspect({ @@ -330,20 +366,31 @@ test('inspect()', function (t) { t.end() }) -test('inspectNoColor()', function (t) { +test('inspectNoColor()', (t) => { t.equal( - inspectNoColor(retext().parse(paragraph).children[0].children[0]), + inspectNoColor(retext().parse(paragraph)), [ - 'SentenceNode[6] (1:1-1:18, 0-17)', - '├─0 WordNode[1] (1:1-1:5, 0-4)', - '│ └─0 TextNode "Some" (1:1-1:5, 0-4)', - '├─1 WhiteSpaceNode " " (1:5-1:6, 4-5)', - '├─2 WordNode[1] (1:6-1:12, 5-11)', - '│ └─0 TextNode "simple" (1:6-1:12, 5-11)', - '├─3 WhiteSpaceNode " " (1:12-1:13, 11-12)', - '├─4 WordNode[1] (1:13-1:17, 12-16)', - '│ └─0 TextNode "text" (1:13-1:17, 12-16)', - '└─5 PunctuationNode "." (1:17-1:18, 16-17)' + 'RootNode[1] (1:1-1:36, 0-35)\n└─0 ParagraphNode[3] (1:1-1:36, 0-35)', + ' ├─0 SentenceNode[6] (1:1-1:18, 0-17)', + ' │ ├─0 WordNode[1] (1:1-1:5, 0-4)', + ' │ │ └─0 TextNode "Some" (1:1-1:5, 0-4)', + ' │ ├─1 WhiteSpaceNode " " (1:5-1:6, 4-5)', + ' │ ├─2 WordNode[1] (1:6-1:12, 5-11)', + ' │ │ └─0 TextNode "simple" (1:6-1:12, 5-11)', + ' │ ├─3 WhiteSpaceNode " " (1:12-1:13, 11-12)', + ' │ ├─4 WordNode[1] (1:13-1:17, 12-16)', + ' │ │ └─0 TextNode "text" (1:13-1:17, 12-16)', + ' │ └─5 PunctuationNode "." (1:17-1:18, 16-17)', + ' ├─1 WhiteSpaceNode " " (1:18-1:19, 17-18)', + ' └─2 SentenceNode[6] (1:19-1:36, 18-35)', + ' ├─0 WordNode[1] (1:19-1:24, 18-23)', + ' │ └─0 TextNode "Other" (1:19-1:24, 18-23)', + ' ├─1 WhiteSpaceNode " " (1:24-1:25, 23-24)', + ' ├─2 PunctuationNode "“" (1:25-1:26, 24-25)', + ' ├─3 WordNode[1] (1:26-1:34, 25-33)', + ' │ └─0 TextNode "sentence" (1:26-1:34, 25-33)', + ' ├─4 PunctuationNode "”" (1:34-1:35, 33-34)', + ' └─5 PunctuationNode "." (1:35-1:36, 34-35)' ].join('\n'), 'should work' ) @@ -351,8 +398,9 @@ test('inspectNoColor()', function (t) { t.end() }) -test('inspectColor()', function (t) { +test('inspectColor()', (t) => { t.equal( + // @ts-expect-error: fine. inspectColor(retext().parse(paragraph).children[0].children[0]), [ chalkEnabled.bold('SentenceNode') + diff --git a/tsconfig.json b/tsconfig.json index be08abe..e31adf8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,7 @@ "declaration": true, "emitDeclarationOnly": true, "allowSyntheticDefaultImports": true, - "skipLibCheck": true + "skipLibCheck": true, + "strict": true } }