From 146bcac953e850356cd7e3ee9d9c3f7567a9cc06 Mon Sep 17 00:00:00 2001 From: Titus Date: Tue, 1 Jun 2021 10:39:42 +0200 Subject: [PATCH 01/16] Use `pull_request_target` in bb --- .github/workflows/bb.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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: From 1343abc0576687c88ae70589d4ac3f57d7691850 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 21 Jul 2021 14:30:59 +0200 Subject: [PATCH 02/16] Update `tsd` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7072d53..a8a0580 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "remark-preset-wooorm": "^8.0.0", "rimraf": "^3.0.0", "tape": "^5.0.0", - "tsd": "^0.14.0", + "tsd": "^0.17.0", "type-coverage": "^2.0.0", "typescript": "^4.0.0", "xo": "^0.40.0" From 63e73cfbbbffbfe0791a66d73cb54cc7e8c7296b Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 21 Jul 2021 14:33:44 +0200 Subject: [PATCH 03/16] Refactor code-style --- index.js | 25 +++++++++++-------------- index.test-d.ts | 6 +++--- inspect.js | 2 +- package.json | 7 +------ test/children.js | 10 +++++----- test/literal.js | 10 +++++----- test/node.js | 10 +++++----- test/non-defined.js | 6 +++--- test/parent.js | 10 +++++----- test/position.js | 36 ++++++++++++++++++------------------ test/type.js | 12 ++++++------ test/value.js | 12 ++++++------ test/void.js | 10 +++++----- 13 files changed, 74 insertions(+), 82 deletions(-) diff --git a/index.js b/index.js index 765976e..1aeef84 100644 --- a/index.js +++ b/index.js @@ -8,10 +8,10 @@ * @typedef {Node & {children: never, value: never}} _Void */ -import nodeAssert from 'assert' +import nodeAssert from 'node:assert' import {inspect} from './inspect.js' -var own = {}.hasOwnProperty +const own = {}.hasOwnProperty /** * Assert that `node` is a valid unist node. @@ -60,10 +60,10 @@ export function _void(node, parent) { } // Identifier to check if a value is seen. -var ID = '__unist__' +const ID = '__unist__' // List of specced properties. -var defined = new Set(['type', 'value', 'children', 'position']) +const defined = new Set(['type', 'value', 'children', 'position']) /** * Wrapper that adds the current node (and parent, if available) to error @@ -103,13 +103,7 @@ export function wrap(fn) { * @returns {asserts node is Node} */ function assertNode(node) { - var index = -1 - /** @type {Parent} */ - var parent - /** @type {unknown} */ - var child - /** @type {string} */ - var key + let index = -1 nodeAssert.ok( node && typeof node === 'object' && !Array.isArray(node), @@ -139,6 +133,9 @@ function assertNode(node) { // @ts-expect-error Looks like an indexed object. position(node.position) + /** @type {string} */ + let key + for (key in node) { if (!defined.has(key)) { vanilla(key, node[key]) @@ -147,8 +144,9 @@ function assertNode(node) { // @ts-expect-error Looks like an indexed object. if (node.children !== null && node.children !== undefined) { + /** @type {Parent} */ // @ts-expect-error Looks like parent. - parent = node + const parent = node nodeAssert.ok( Array.isArray(parent.children), '`children` should be an array' @@ -156,8 +154,7 @@ function assertNode(node) { index = -1 while (++index < parent.children.length) { - child = parent.children[index] - assert(child, parent) + assert(parent.children[index], parent) } } } diff --git a/index.test-d.ts b/index.test-d.ts index a763e3b..96465a2 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -2,9 +2,9 @@ import {expectType, expectNotType} from 'tsd' import {Node, Parent} from 'unist' import {assert, parent} from './index.js' -var emptyNode = {type: 'a'} -var literalNode = {type: 'b', value: 'c'} -var parentNode = {type: 'd', children: [emptyNode, literalNode]} +const emptyNode = {type: 'a'} +const literalNode = {type: 'b', value: 'c'} +const parentNode = {type: 'd', children: [emptyNode, literalNode]} expectNotType(emptyNode) expectNotType(literalNode) diff --git a/inspect.js b/inspect.js index 99f3c98..389b9aa 100644 --- a/inspect.js +++ b/inspect.js @@ -1,4 +1,4 @@ -import {inspect as utilInspect} from 'util' +import {inspect as utilInspect} from 'node:util' /** * @param {unknown} value diff --git a/package.json b/package.json index a8a0580..59ff0d6 100644 --- a/package.json +++ b/package.json @@ -73,12 +73,7 @@ "trailingComma": "none" }, "xo": { - "prettier": true, - "rules": { - "no-var": "off", - "prefer-arrow-callback": "off", - "unicorn/prefer-node-protocol": "off" - } + "prettier": true }, "remarkConfig": { "plugins": [ diff --git a/test/children.js b/test/children.js index 2e0ab62..c197d8a 100644 --- a/test/children.js +++ b/test/children.js @@ -1,9 +1,9 @@ import test from 'tape' import {assert} from '../index.js' -test('children', function (t) { +test('children', (t) => { t.throws( - function () { + () => { assert({type: 'foo', children: {alpha: 'bravo'}}) }, /`children` should be an array: `{ type: 'foo', children: { alpha: 'bravo' } }`$/, @@ -11,19 +11,19 @@ test('children', function (t) { ) t.throws( - function () { + () => { assert({type: 'foo', children: ['one']}) }, /node should be an object: `'one'` in `{ type: 'foo', children: \[ 'one' ] }`$/, 'should throw if given a non-node child in children' ) - t.doesNotThrow(function () { + t.doesNotThrow(() => { assert({type: 'parent', children: [{type: 'text', value: 'alpha'}]}) }, 'should not throw on vald children') t.throws( - function () { + () => { assert({ type: 'foo', children: [ diff --git a/test/literal.js b/test/literal.js index 1ea51fb..7ee315e 100644 --- a/test/literal.js +++ b/test/literal.js @@ -1,9 +1,9 @@ import test from 'tape' import {literal} from '../index.js' -test('literal()', function (t) { +test('literal()', (t) => { t.throws( - function () { + () => { literal({}) }, /node should have a type: `{}`$/, @@ -11,7 +11,7 @@ test('literal()', function (t) { ) t.throws( - function () { + () => { literal({type: 'strong', children: []}) }, /literal should not have `children`: `{ type: 'strong', children: \[] }`$/, @@ -19,14 +19,14 @@ test('literal()', function (t) { ) t.throws( - function () { + () => { literal({type: 'break'}) }, /literal should have `value`: `{ type: 'break' }`$/, 'should throw if the given node has no `value`' ) - t.doesNotThrow(function () { + t.doesNotThrow(() => { literal({type: 'text', value: 'foo'}) }, 'should not throw on valid text nodes') diff --git a/test/node.js b/test/node.js index a557703..a20a7a6 100644 --- a/test/node.js +++ b/test/node.js @@ -1,9 +1,9 @@ import test from 'tape' import {assert} from '../index.js' -test('node', function (t) { +test('node', (t) => { t.throws( - function () { + () => { assert() }, /node should be an object: `undefined`$/, @@ -11,7 +11,7 @@ test('node', function (t) { ) t.throws( - function () { + () => { assert(null) }, /node should be an object: `null`$/, @@ -19,7 +19,7 @@ test('node', function (t) { ) t.throws( - function () { + () => { assert('foo') }, /node should be an object: `'foo'`$/, @@ -27,7 +27,7 @@ test('node', function (t) { ) t.throws( - function () { + () => { assert(6) }, /node should be an object: `6`$/, diff --git a/test/non-defined.js b/test/non-defined.js index 5632088..bae8870 100644 --- a/test/non-defined.js +++ b/test/non-defined.js @@ -1,8 +1,8 @@ import test from 'tape' import {assert} from '../index.js' -test('non-defined', function (t) { - t.doesNotThrow(function () { +test('non-defined', (t) => { + t.doesNotThrow(() => { assert({ type: 'element', properties: { @@ -18,7 +18,7 @@ test('non-defined', function (t) { }, 'should not throw if non-defined properties are found') t.throws( - function () { + () => { assert({ type: 'break', data: {foo: Function} diff --git a/test/parent.js b/test/parent.js index 0879528..0f76cba 100644 --- a/test/parent.js +++ b/test/parent.js @@ -1,9 +1,9 @@ import test from 'tape' import {parent} from '../index.js' -test('parent()', function (t) { +test('parent()', (t) => { t.throws( - function () { + () => { parent({}) }, /node should have a type: `{}`$/, @@ -11,7 +11,7 @@ test('parent()', function (t) { ) t.throws( - function () { + () => { parent({type: 'text', value: 'foo'}) }, /parent should not have `value`: `{ type: 'text', value: 'foo' }`$/, @@ -19,14 +19,14 @@ test('parent()', function (t) { ) t.throws( - function () { + () => { parent({type: 'break'}) }, /parent should have `children`: `{ type: 'break' }`$/, 'should throw if the given node has `children`' ) - t.doesNotThrow(function () { + t.doesNotThrow(() => { parent({type: 'strong', children: []}) }, 'should not throw on valid void nodes') diff --git a/test/position.js b/test/position.js index 55244c7..fa4139d 100644 --- a/test/position.js +++ b/test/position.js @@ -1,73 +1,73 @@ import test from 'tape' import {assert} from '../index.js' -test('position', function (t) { +test('position', (t) => { t.throws( - function () { + () => { assert({type: 'foo', position: 1}) }, /`position` should be an object: `{ type: 'foo', position: 1 }`$/, 'should throw if given a non-object `position`' ) - t.doesNotThrow(function () { + t.doesNotThrow(() => { assert({type: 'foo', position: null}) }, 'should not throw if given a null `position`') - t.doesNotThrow(function () { + t.doesNotThrow(() => { assert({type: 'foo', position: {}}) }, 'should not throw if given an empty `position` object') t.throws( - function () { + () => { assert({type: 'foo', position: {start: 1}}) }, /`position.start` should be an object: `{ type: 'foo', position: { start: 1 } }`$/, 'should throw if given a non-object `position.start`' ) - t.doesNotThrow(function () { + t.doesNotThrow(() => { assert({type: 'foo', position: {start: null}}) }, 'should not throw if given a null `position.start`') - t.doesNotThrow(function () { + t.doesNotThrow(() => { assert({type: 'foo', position: {start: {}}}) }, 'should not throw if given an empty `position.start` object') t.throws( - function () { + () => { assert({type: 'foo', position: {end: 1}}) }, /`position.end` should be an object: `{ type: 'foo', position: { end: 1 } }`$/, 'should throw if given a non-object `position.end`' ) - t.doesNotThrow(function () { + t.doesNotThrow(() => { assert({type: 'foo', position: {end: null}}) }, 'should not throw if given a null `position.end`') - t.doesNotThrow(function () { + t.doesNotThrow(() => { assert({type: 'foo', position: {end: {}}}) }, 'should not throw if given an empty `position.end` object') - t.doesNotThrow(function () { + t.doesNotThrow(() => { assert({type: 'foo', position: {start: {line: null}}}) }, 'should not throw if given a `position.start.line` to `null`') - t.doesNotThrow(function () { + t.doesNotThrow(() => { assert({type: 'foo', position: {start: {column: null}}}) }, 'should not throw if given a `position.start.column` to `null`') - t.doesNotThrow(function () { + t.doesNotThrow(() => { assert({type: 'foo', position: {end: {line: null}}}) }, 'should not throw if given a `position.end.line` to `null`') - t.doesNotThrow(function () { + t.doesNotThrow(() => { assert({type: 'foo', position: {end: {column: null}}}) }, 'should not throw if given a `position.end.column` to `null`') t.throws( - function () { + () => { assert({type: 'foo', position: {start: {line: 0}}}) }, /`position.start.line` should be gte `1`: `{ type: 'foo', position: { start: { line: 0 } } }`$/, @@ -75,7 +75,7 @@ test('position', function (t) { ) t.throws( - function () { + () => { assert({type: 'foo', position: {start: {column: 0}}}) }, /`position.start.column` should be gte `1`: `{ type: 'foo', position: { start: { column: 0 } } }`$/, @@ -83,7 +83,7 @@ test('position', function (t) { ) t.throws( - function () { + () => { assert({type: 'foo', position: {end: {line: 0}}}) }, /`position.end.line` should be gte `1`: `{ type: 'foo', position: { end: { line: 0 } } }`$/, @@ -91,7 +91,7 @@ test('position', function (t) { ) t.throws( - function () { + () => { assert({type: 'foo', position: {end: {column: 0}}}) }, /`position.end.column` should be gte `1`: `{ type: 'foo', position: { end: { column: 0 } } }`$/, diff --git a/test/type.js b/test/type.js index 5112d5d..1086770 100644 --- a/test/type.js +++ b/test/type.js @@ -1,9 +1,9 @@ import test from 'tape' import {assert} from '../index.js' -test('type', function (t) { +test('type', (t) => { t.throws( - function () { + () => { assert({}) }, /node should have a type: `{}`$/, @@ -11,7 +11,7 @@ test('type', function (t) { ) t.throws( - function () { + () => { assert({value: 'foo'}) }, /node should have a type: `{ value: 'foo' }`$/, @@ -19,7 +19,7 @@ test('type', function (t) { ) t.throws( - function () { + () => { assert({type: 1}) }, /`type` should be a string: `{ type: 1 }`$/, @@ -27,14 +27,14 @@ test('type', function (t) { ) t.throws( - function () { + () => { assert({type: ''}) }, /`type` should not be empty: `{ type: '' }`$/, 'should throw if given an empty string type' ) - t.doesNotThrow(function () { + t.doesNotThrow(() => { assert({type: 'foo'}) }, 'should not throw if given a non-empty type string') diff --git a/test/value.js b/test/value.js index e180874..2cc1ed9 100644 --- a/test/value.js +++ b/test/value.js @@ -1,28 +1,28 @@ import test from 'tape' import {assert} from '../index.js' -test('value', function (t) { +test('value', (t) => { t.throws( - function () { + () => { assert({type: 'foo', value: 1}) }, /`value` should be a string: `{ type: 'foo', value: 1 }`$/, 'should throw if given a non-string `value`' ) - t.doesNotThrow(function () { + t.doesNotThrow(() => { assert({type: 'foo', value: ''}) }, 'should not throw if given an empty string `value`') - t.doesNotThrow(function () { + t.doesNotThrow(() => { assert({type: 'foo', value: 'foo'}) }, 'should not throw if given an string `value`') - t.doesNotThrow(function () { + t.doesNotThrow(() => { assert({type: 'foo', value: undefined}) }, 'should not throw if given an undefined `value`') - t.doesNotThrow(function () { + t.doesNotThrow(() => { assert({type: 'foo', value: null}) }, 'should not throw if given an null `value`') diff --git a/test/void.js b/test/void.js index 2cb03d4..cc1ea54 100644 --- a/test/void.js +++ b/test/void.js @@ -1,9 +1,9 @@ import test from 'tape' import {_void} from '../index.js' -test('_void()', function (t) { +test('_void()', (t) => { t.throws( - function () { + () => { _void({}) }, /node should have a type: `{}`$/, @@ -11,7 +11,7 @@ test('_void()', function (t) { ) t.throws( - function () { + () => { _void({type: 'text', value: 'foo'}) }, /void should not have `value`: `{ type: 'text', value: 'foo' }`$/, @@ -19,14 +19,14 @@ test('_void()', function (t) { ) t.throws( - function () { + () => { _void({type: 'strong', children: []}) }, /void should not have `children`: `{ type: 'strong', children: \[] }`$/, 'should throw if the given node has `children`' ) - t.doesNotThrow(function () { + t.doesNotThrow(() => { _void({type: 'break'}) }, 'should not throw on valid void nodes') From d9d0d642ee9f8d17642eb1ec7c0a891d7f5224a9 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 21 Jul 2021 14:38:46 +0200 Subject: [PATCH 04/16] Add `strict` to `tsconfig` --- index.js | 13 ++++++++----- package.json | 6 +++++- tsconfig.json | 3 ++- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index 1aeef84..cac22fa 100644 --- a/index.js +++ b/index.js @@ -69,17 +69,17 @@ const defined = new Set(['type', 'value', 'children', 'position']) * Wrapper that adds the current node (and parent, if available) to error * messages. * - * @param {(node?: unknown, parent?: Parent|null) => asserts node is Node} fn - * @returns {(node?: unknown, parent?: Parent|null) => asserts node is Node} + * @param {(node?: any, parent?: Parent|null) => asserts node is Node} fn + * @returns {(node?: any, parent?: Parent|null) => asserts node is Node} */ export function wrap(fn) { return wrapped /** * @param {unknown} node - * @param {Parent} [parent] + * @param {Parent|null|undefined} [parent] * @throws {AssertionError} - * @returns {asserts node is T} + * @returns {void} */ function wrapped(node, parent) { try { @@ -138,7 +138,10 @@ function assertNode(node) { for (key in node) { if (!defined.has(key)) { - vanilla(key, node[key]) + /** @type {unknown} */ + // @ts-expect-error: hush. + const value = node[key] + vanilla(key, value) } } diff --git a/package.json b/package.json index 59ff0d6..849d6b9 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,10 @@ "atLeast": 100, "detail": true, "strict": true, - "ignoreCatch": true + "ignoreCatch": true, + "#": "Couple of needed any’s", + "ignoreFiles": [ + "index.d.ts" + ] } } diff --git a/tsconfig.json b/tsconfig.json index 1cb61bf..ab9f0b7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,7 @@ "declaration": true, "emitDeclarationOnly": true, "allowSyntheticDefaultImports": true, - "skipLibCheck": true + "skipLibCheck": true, + "strict": true } } From b5a566c068ef3acf34993dbe3f64d945e3bfd3f7 Mon Sep 17 00:00:00 2001 From: Christian Murphy Date: Thu, 3 Mar 2022 11:02:05 -0700 Subject: [PATCH 05/16] Add `ignore-scripts` to `.npmrc` --- .npmrc | 1 + 1 file changed, 1 insertion(+) diff --git a/.npmrc b/.npmrc index 43c97e7..9951b11 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,2 @@ package-lock=false +ignore-scripts=true From dffaa98924f7a1806ff84ba237056d6627857479 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sun, 5 Jun 2022 11:45:17 +0200 Subject: [PATCH 06/16] Update dev-dependencies --- index.js | 14 ++++++++++---- package.json | 8 ++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index cac22fa..65b4393 100644 --- a/index.js +++ b/index.js @@ -6,6 +6,11 @@ * @typedef {import('unist').Position} Position * @typedef {import('unist').Point} Point * @typedef {Node & {children: never, value: never}} _Void + * + * @typedef SeenErrorFields + * @property {true} [__unist__] + * + * @typedef {Error & SeenErrorFields} SeenError */ import nodeAssert from 'node:assert' @@ -85,10 +90,11 @@ export function wrap(fn) { try { fn(node, parent) } catch (error) { - if (!own.call(error, ID)) { - error[ID] = true - error.message += ': `' + view(node) + '`' - if (parent) error.message += ' in `' + view(parent) + '`' + const exception = /** @type {SeenError} */ (error) + if (!own.call(exception, ID)) { + exception[ID] = true + exception.message += ': `' + view(node) + '`' + if (parent) exception.message += ' in `' + view(parent) + '`' } throw error diff --git a/package.json b/package.json index 849d6b9..12d488b 100644 --- a/package.json +++ b/package.json @@ -47,14 +47,14 @@ "@types/tape": "^4.0.0", "c8": "^7.0.0", "prettier": "^2.0.0", - "remark-cli": "^9.0.0", - "remark-preset-wooorm": "^8.0.0", + "remark-cli": "^10.0.0", + "remark-preset-wooorm": "^9.0.0", "rimraf": "^3.0.0", "tape": "^5.0.0", - "tsd": "^0.17.0", + "tsd": "^0.20.0", "type-coverage": "^2.0.0", "typescript": "^4.0.0", - "xo": "^0.40.0" + "xo": "^0.49.0" }, "scripts": { "prepack": "npm run build && npm run format", From 93a6640fff53e555980f260a09a35a86baf0865c Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sun, 5 Jun 2022 11:53:08 +0200 Subject: [PATCH 07/16] Add improved docs --- readme.md | 139 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 105 insertions(+), 34 deletions(-) diff --git a/readme.md b/readme.md index 839bf47..349d461 100644 --- a/readme.md +++ b/readme.md @@ -8,19 +8,63 @@ [![Backers][backers-badge]][collective] [![Chat][chat-badge]][chat] -[**unist**][unist] utility to assert trees. +[unist][] utility to assert 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) + * [`assert(node[, parent])`](#assertnode-parent) + * [`parent(node[, parent])`](#parentnode-parent) + * [`literal(node[, parent])`](#literalnode-parent) + * [`_void(node[, parent])`](#_voidnode-parent) + * [`wrap(fn)`](#wrapfn) +* [Extensions](#extensions) +* [Types](#types) +* [Compatibility](#compatibility) +* [Related](#related) +* [Contribute](#contribute) +* [License](#license) + +## What is this? + +This package is a tiny utility that helps you deal with nodes. + +## When should I use this? -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. +This utility is typically useful when you expect certain nodes in your APIs +and want to make sure they’re valid and as expected. -[npm][]: +Different utilities, [`mdast-util-assert`][mdast-util-assert], +[`hast-util-assert`][hast-util-assert], and [`nlcst-test`][nlcst-test], +do the same but for mdast, hast, and nlcst nodes, respectively. + +## Install + +This package is [ESM only][esm]. +In Node.js (version 12.20+, 14.14+, 16.0+, or 18.0+), install with [npm][]: ```sh npm install unist-util-assert ``` +In Deno with [`esm.sh`][esmsh]: + +```js +import {assert} from 'https://esm.sh/unist-util-assert@3' +``` + +In browsers with [`esm.sh`][esmsh]: + +```html + +``` + ## Use ```js @@ -49,37 +93,33 @@ assert({type: 'paragraph', children: ['foo']}) ## API -This package exports the following identifiers: `assert`, `parent`, `literal`, -`_void`, `wrap`. +This package exports the identifiers `assert`, `parent`, `literal`, `_void`, +and `wrap`. There is no default export. ### `assert(node[, parent])` -Assert that `node` is a valid [unist][] [node][]. -If `node` is a [parent][], all [child][]ren will be asserted too. +Assert that `node` is a valid unist [`Node`][node]. +If `tree` is a [parent][], all children will be asserted as well. + +###### Throws + +When `node`, or one of its children, is not a valid node. ### `parent(node[, parent])` -Assert that `node` is a valid [unist][] [parent][]. -All [child][]ren will be asserted too. +Assert that `node` is a valid unist [`Parent`][parent]. +All children will be asserted as well. ### `literal(node[, parent])` -Assert that `node` is a valid [unist][] [literal][]. +Assert that `node` is a valid unist [`Literal`][literal]. ### `_void(node[, parent])` -Assert that `node` is a valid [unist][] [node][], but neither [parent][] nor -[literal][]. - -## Extensions - -This module can be used as a base to test subsets of [unist][] (for an example, -see [`mdast-util-assert`][mdast-util-assert]). -All functions that are exposed from such a module, and functions used internally -to test [child][]ren, should be wrapped in `wrap`, which adds an inspectable -string of the respective node, and its parent when available, to the exposed -error message. +Assert that `node` is a valid unist [`Node`][node], but neither +[`Parent`][parent] nor +[`Literal`][literal]. ### `wrap(fn)` @@ -87,19 +127,40 @@ Wraps `fn` (which is passed a node, and an optional parent node), so that any errors thrown inside it will contain information regarding the node (and the parent, when given). +## Extensions + +This module can be used as a base to test subsets of unist (for an example, see +[`mdast-util-assert`][mdast-util-assert]). +All functions that are exposed from such a module, and functions used internally +to test children, should be wrapped in `wrap`, which adds an inspectable string +of the respective node, and its parent when available, to the exposed error +message. + +## Types + +This package is fully typed with [TypeScript][]. +It does not export additional types. + +## 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. + ## Related * [`mdast-util-assert`][mdast-util-assert] - — Check [mdast](https://github.com/syntax-tree/mdast) nodes + — assert mdast nodes * [`hast-util-assert`](https://github.com/syntax-tree/hast-util-assert) - — Check [hast](https://github.com/syntax-tree/hast) nodes + — assert hast nodes * [`nlcst-test`](https://github.com/syntax-tree/nlcst-test) - — Check [nlcst](https://github.com/syntax-tree/nlcst) nodes + — assert nlcst nodes ## 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]. @@ -140,24 +201,34 @@ abide by its terms. [npm]: https://docs.npmjs.com/cli/install +[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 -[support]: https://github.com/syntax-tree/.github/blob/HEAD/support.md +[contributing]: https://github.com/syntax-tree/.github/blob/main/contributing.md -[coc]: https://github.com/syntax-tree/.github/blob/HEAD/code-of-conduct.md +[support]: https://github.com/syntax-tree/.github/blob/main/support.md + +[coc]: https://github.com/syntax-tree/.github/blob/main/code-of-conduct.md [unist]: https://github.com/syntax-tree/unist +[node]: https://github.com/syntax-tree/unist#node + [parent]: https://github.com/syntax-tree/unist#parent [literal]: https://github.com/syntax-tree/unist#literal -[node]: https://github.com/syntax-tree/unist#node +[mdast-util-assert]: https://github.com/syntax-tree/mdast-util-assert -[child]: https://github.com/syntax-tree/unist#child +[hast-util-assert]: https://github.com/syntax-tree/hast-util-assert -[mdast-util-assert]: https://github.com/syntax-tree/mdast-util-assert +[nlcst-test]: https://github.com/syntax-tree/nlcst-test From d7a0967b3cc29df35f48a6ea36759f7736a96bd5 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 6 Jan 2023 17:43:45 +0100 Subject: [PATCH 08/16] Update dev-dependencies --- index.test-d.ts | 2 +- package.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/index.test-d.ts b/index.test-d.ts index 96465a2..e9f0264 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -1,5 +1,5 @@ import {expectType, expectNotType} from 'tsd' -import {Node, Parent} from 'unist' +import {type Node, type Parent} from 'unist' import {assert, parent} from './index.js' const emptyNode = {type: 'a'} diff --git a/package.json b/package.json index 12d488b..4fb2074 100644 --- a/package.json +++ b/package.json @@ -47,14 +47,14 @@ "@types/tape": "^4.0.0", "c8": "^7.0.0", "prettier": "^2.0.0", - "remark-cli": "^10.0.0", + "remark-cli": "^11.0.0", "remark-preset-wooorm": "^9.0.0", "rimraf": "^3.0.0", "tape": "^5.0.0", - "tsd": "^0.20.0", + "tsd": "^0.25.0", "type-coverage": "^2.0.0", "typescript": "^4.0.0", - "xo": "^0.49.0" + "xo": "^0.53.0" }, "scripts": { "prepack": "npm run build && npm run format", From 8406e39d34cf7ddf2a9dc07592d9691eb2c1d155 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 6 Jan 2023 17:43:56 +0100 Subject: [PATCH 09/16] Update Actions --- .github/workflows/main.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fe284ad..89dc06c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,15 +7,15 @@ jobs: name: ${{matrix.node}} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: dcodeIO/setup-node-nvm@master + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 with: node-version: ${{matrix.node}} - run: npm install - run: npm test - - uses: codecov/codecov-action@v1 + - uses: codecov/codecov-action@v3 strategy: matrix: node: - - lts/erbium + - lts/fermium - node From b6142059aee6a01e269508ac2abcb5ab5ffe137e Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 6 Jan 2023 17:44:32 +0100 Subject: [PATCH 10/16] Update `tsconfig.json` --- package.json | 7 +++---- tsconfig.json | 17 +++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 4fb2074..533e4c0 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,6 @@ "prettier": "^2.0.0", "remark-cli": "^11.0.0", "remark-preset-wooorm": "^9.0.0", - "rimraf": "^3.0.0", "tape": "^5.0.0", "tsd": "^0.25.0", "type-coverage": "^2.0.0", @@ -58,10 +57,10 @@ }, "scripts": { "prepack": "npm run build && npm run format", - "build": "rimraf \"{test/**,}*.d.ts\" && tsc && tsd && type-coverage", + "build": "tsc --build --clean && tsc --build && tsd && type-coverage", "format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", - "test-api": "node test/index.js", - "test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov node test/index.js", + "test-api": "node --conditions development test.js", + "test-coverage": "c8 --check-coverage --100 --reporter lcov npm run test-api", "test": "npm run build && npm run format && npm run test-coverage" }, "prettier": { diff --git a/tsconfig.json b/tsconfig.json index ab9f0b7..1bc9e99 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,16 +1,17 @@ { - "include": ["*.js", "test/**/*.js"], + "include": ["**/**.js"], + "exclude": ["coverage/", "node_modules/"], "compilerOptions": { - "target": "ES2020", - "lib": ["ES2020"], - "module": "ES2020", - "moduleResolution": "node", - "allowJs": true, "checkJs": true, "declaration": true, "emitDeclarationOnly": true, - "allowSyntheticDefaultImports": true, + "exactOptionalPropertyTypes": true, + "forceConsistentCasingInFileNames": true, + "lib": ["es2020"], + "module": "node16", + "newLine": "lf", "skipLibCheck": true, - "strict": true + "strict": true, + "target": "es2020" } } From b0889eadc286907c07a97c15f980b9b007db985e Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 6 Jan 2023 18:14:14 +0100 Subject: [PATCH 11/16] Refactor code-style * Add support for `null` in input of API * Add more docs to JSDoc * Add a type check --- index.js | 134 ++++++++++++++++++++++++++++++++++++++------------- package.json | 2 +- readme.md | 14 +++--- 3 files changed, 110 insertions(+), 40 deletions(-) diff --git a/index.js b/index.js index 65b4393..97b793a 100644 --- a/index.js +++ b/index.js @@ -19,46 +19,68 @@ import {inspect} from './inspect.js' const own = {}.hasOwnProperty /** - * Assert that `node` is a valid unist node. + * Assert that `tree` is a valid unist node. + * * If `node` is a parent, all children will be asserted too. * - * @param {unknown} [node] - * @param {Parent} [parent] - * @returns {asserts node is Node} + * @param {unknown} [tree] + * Thing to assert. + * @param {Parent | null | undefined} [parent] + * Optional, valid parent. + * @returns {asserts tree is Node} + * Whether `tree` (and its descendants) are valid nodes. + * @throws {AssertionError} + * When `tree` (or its descendants) is not a node. */ -export function assert(node, parent) { - return wrap(assertNode)(node, parent) +export function assert(tree, parent) { + return wrap(assertNode)(tree, parent) } /** - * Assert that `node` is a valid unist parent. + * Assert that `tree` is a valid unist parent. + * * All children will be asserted too. * - * @param {unknown} [node] - * @param {Parent} [parent] - * @returns {asserts node is Parent} + * @param {unknown} [tree] + * Thing to assert. + * @param {Parent | null | undefined} [parent] + * Optional, valid parent. + * @returns {asserts tree is Parent} + * Whether `tree` is a parent and its descendants are valid nodes. + * @throws {AssertionError} + * When `tree` is not a parent or its descendants are not nodes. */ -export function parent(node, parent) { - return wrap(assertParent)(node, parent) +export function parent(tree, parent) { + return wrap(assertParent)(tree, parent) } /** * Assert that `node` is a valid unist literal. * * @param {unknown} [node] - * @param {Parent} [parent] + * Thing to assert. + * @param {Parent | null | undefined} [parent] + * Optional, valid parent. * @returns {asserts node is Literal} + * Whether `node` is a literal. + * @throws {AssertionError} + * When `node` is not a literal. */ export function literal(node, parent) { return wrap(assertLiteral)(node, parent) } /** - * Assert that `node` is a valid unist node, but neither parent nor literal. + * Assert that `node` is a valid void node. * * @param {unknown} [node] - * @param {Parent} [parent] + * Thing to assert. + * @param {Parent | null | undefined} [parent] + * Optional, valid parent. * @returns {asserts node is _Void} + * Whether `node` is a node but neither parent nor literal. + * @throws {AssertionError} + * When `node` is not a node, a parent, or a literal. */ export function _void(node, parent) { return wrap(assertVoid)(node, parent) @@ -74,17 +96,25 @@ const defined = new Set(['type', 'value', 'children', 'position']) * Wrapper that adds the current node (and parent, if available) to error * messages. * - * @param {(node?: any, parent?: Parent|null) => asserts node is Node} fn - * @returns {(node?: any, parent?: Parent|null) => asserts node is Node} + * @template {Node} T + * Node type. + * @param {(node?: any, parent?: Parent | null | undefined) => asserts node is T} fn + * Custom assertion. + * @returns {(node?: any, parent?: Parent | null | undefined) => asserts node is T} + * Assertion. */ export function wrap(fn) { return wrapped /** * @param {unknown} node - * @param {Parent|null|undefined} [parent] + * Thing to check. + * @param {Parent | null | undefined} [parent] + * Optional, valid parent. * @throws {AssertionError} + * Whether `node` is a node but neither parent nor literal. * @returns {void} + * Nothing. */ function wrapped(node, parent) { try { @@ -103,10 +133,16 @@ export function wrap(fn) { } /** - * Assert. + * Assert that `node` is a valid unist parent. + * + * All children will be asserted too. * * @param {unknown} node + * Thing to assert. * @returns {asserts node is Node} + * Whether `node` (and its descendants) are valid nodes. + * @throws {AssertionError} + * When `node` (or its descendants) is not a node. */ function assertNode(node) { let index = -1 @@ -173,7 +209,9 @@ function assertNode(node) { * same (deep) value. * * @param {string} key + * Name of field. * @param {unknown} value + * Value of field. */ function vanilla(key, value) { try { @@ -185,10 +223,13 @@ function vanilla(key, value) { /** * Stringify a value to inspect it. + * * Tries `JSON.stringify()`, and if that fails uses `String()` instead. * * @param {unknown} value + * Anything (should be JSON). * @returns {string} + * User-visible preresentation. */ function view(value) { try { @@ -200,10 +241,16 @@ function view(value) { } /** - * Assert `node` is a parent node. + * Assert that `node` is a valid unist parent. + * + * All children will be asserted too. * * @param {Node} node + * Thing to assert. * @returns {asserts node is Parent} + * Whether `node` is a parent and its descendants are valid nodes. + * @throws {AssertionError} + * When `node` is not a parent or its descendants are not nodes. */ function assertParent(node) { assertNode(node) @@ -217,10 +264,14 @@ function assertParent(node) { } /** - * Assert `node` is a literal node. + * Assert that `node` is a valid unist literal. * - * @param {Node} node + * @param {unknown} [node] + * Thing to assert. * @returns {asserts node is Literal} + * Whether `node` is a literal. + * @throws {AssertionError} + * When `node` is not a literal. */ function assertLiteral(node) { assertNode(node) @@ -234,10 +285,14 @@ function assertLiteral(node) { } /** - * Assert `node` is a unist node, but neither parent nor literal. + * Assert that `node` is a valid void node. * - * @param {Node} node + * @param {unknown} [node] + * Thing to assert. * @returns {asserts node is _Void} + * Whether `node` is a node but neither parent nor literal. + * @throws {AssertionError} + * When `node` is not a node, a parent, or a literal. */ function assertVoid(node) { assertNode(node) @@ -251,19 +306,25 @@ function assertVoid(node) { } /** - * Assert `position` is a unist position. + * Assert that `position` is a unist position. * - * @param {Position} position + * @param {unknown} position + * Thing to assert. * @returns {asserts position is Position} + * Whether `position` is a unist position. + * @throws {AssertionError} + * When `position` is not a position. */ function position(position) { if (position !== null && position !== undefined) { nodeAssert.ok( - position === Object(position), + typeof position === 'object' && position === Object(position), '`position` should be an object' ) + // @ts-expect-error: indexable. point(position.start, 'position.start') + // @ts-expect-error: indexable. point(position.end, 'position.end') } } @@ -271,28 +332,35 @@ function position(position) { /** * Assert `point` is a unist point. * - * @param {Point} point + * @param {unknown} point + * Thing to assert. * @param {string} label + * Whether `point` is a unist point. * @returns {asserts point is Point} + * When `point` is not a point. */ function point(point, label) { if (point !== null && point !== undefined) { nodeAssert.ok( - point === Object(point), + typeof point === 'object' && point === Object(point), '`' + label + '` should be an object' ) - if (point.line !== null && point.line !== undefined) { + if ('line' in point && point.line !== null && point.line !== undefined) { nodeAssert.ok( - 'line' in point, + typeof point.line === 'number', '`' + label + '` should have numeric `line`' ) nodeAssert.ok(point.line >= 1, '`' + label + '.line` should be gte `1`') } - if (point.column !== null && point.column !== undefined) { + if ( + 'column' in point && + point.column !== null && + point.column !== undefined + ) { nodeAssert.ok( - 'column' in point, + typeof point.column === 'number', '`' + label + '` should have numeric `column`' ) nodeAssert.ok( diff --git a/package.json b/package.json index 533e4c0..c629571 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "prepack": "npm run build && npm run format", "build": "tsc --build --clean && tsc --build && tsd && type-coverage", "format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", - "test-api": "node --conditions development test.js", + "test-api": "node --conditions development test/index.js", "test-coverage": "c8 --check-coverage --100 --reporter lcov npm run test-api", "test": "npm run build && npm run format && npm run test-coverage" }, diff --git a/readme.md b/readme.md index 349d461..a4d157a 100644 --- a/readme.md +++ b/readme.md @@ -17,8 +17,8 @@ * [Install](#install) * [Use](#use) * [API](#api) - * [`assert(node[, parent])`](#assertnode-parent) - * [`parent(node[, parent])`](#parentnode-parent) + * [`assert(tree[, parent])`](#asserttree-parent) + * [`parent(tree[, parent])`](#parenttree-parent) * [`literal(node[, parent])`](#literalnode-parent) * [`_void(node[, parent])`](#_voidnode-parent) * [`wrap(fn)`](#wrapfn) @@ -97,18 +97,20 @@ This package exports the identifiers `assert`, `parent`, `literal`, `_void`, and `wrap`. There is no default export. -### `assert(node[, parent])` +### `assert(tree[, parent])` + +Assert that `tree` is a valid unist [`Node`][node]. -Assert that `node` is a valid unist [`Node`][node]. If `tree` is a [parent][], all children will be asserted as well. ###### Throws When `node`, or one of its children, is not a valid node. -### `parent(node[, parent])` +### `parent(tree[, parent])` + +Assert that `tree` is a valid unist [`Parent`][parent]. -Assert that `node` is a valid unist [`Parent`][parent]. All children will be asserted as well. ### `literal(node[, parent])` From 39273c7c8ca8007dab3625857bd78e094a0a5eb3 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 6 Jan 2023 18:23:21 +0100 Subject: [PATCH 12/16] Use Node test runner --- .github/workflows/main.yml | 2 +- package.json | 3 +-- test/children.js | 15 +++++++------- test/literal.js | 15 +++++++------- test/node.js | 15 +++++++------- test/non-defined.js | 11 +++++----- test/parent.js | 15 +++++++------- test/position.js | 41 +++++++++++++++++++------------------- test/type.js | 17 ++++++++-------- test/value.js | 17 ++++++++-------- test/void.js | 15 +++++++------- 11 files changed, 78 insertions(+), 88 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 89dc06c..ee318ca 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,5 +17,5 @@ jobs: strategy: matrix: node: - - lts/fermium + - lts/hydrogen - node diff --git a/package.json b/package.json index c629571..040af2a 100644 --- a/package.json +++ b/package.json @@ -44,12 +44,11 @@ "@types/unist": "^2.0.0" }, "devDependencies": { - "@types/tape": "^4.0.0", + "@types/node": "^18.0.0", "c8": "^7.0.0", "prettier": "^2.0.0", "remark-cli": "^11.0.0", "remark-preset-wooorm": "^9.0.0", - "tape": "^5.0.0", "tsd": "^0.25.0", "type-coverage": "^2.0.0", "typescript": "^4.0.0", diff --git a/test/children.js b/test/children.js index c197d8a..6a8e27c 100644 --- a/test/children.js +++ b/test/children.js @@ -1,8 +1,9 @@ -import test from 'tape' +import nodeAssert from 'node:assert/strict' +import test from 'node:test' import {assert} from '../index.js' -test('children', (t) => { - t.throws( +test('children', () => { + nodeAssert.throws( () => { assert({type: 'foo', children: {alpha: 'bravo'}}) }, @@ -10,7 +11,7 @@ test('children', (t) => { 'should throw if given a non-node child in children' ) - t.throws( + nodeAssert.throws( () => { assert({type: 'foo', children: ['one']}) }, @@ -18,11 +19,11 @@ test('children', (t) => { 'should throw if given a non-node child in children' ) - t.doesNotThrow(() => { + nodeAssert.doesNotThrow(() => { assert({type: 'parent', children: [{type: 'text', value: 'alpha'}]}) }, 'should not throw on vald children') - t.throws( + nodeAssert.throws( () => { assert({ type: 'foo', @@ -37,6 +38,4 @@ test('children', (t) => { /node should be an object: `'one'` in `{ type: 'bar', children: \[ 'one' ] }`$/, 'should throw on invalid descendants' ) - - t.end() }) diff --git a/test/literal.js b/test/literal.js index 7ee315e..b0c11fd 100644 --- a/test/literal.js +++ b/test/literal.js @@ -1,8 +1,9 @@ -import test from 'tape' +import assert from 'node:assert/strict' +import test from 'node:test' import {literal} from '../index.js' -test('literal()', (t) => { - t.throws( +test('literal()', () => { + assert.throws( () => { literal({}) }, @@ -10,7 +11,7 @@ test('literal()', (t) => { 'should throw the same errors as `assert()`' ) - t.throws( + assert.throws( () => { literal({type: 'strong', children: []}) }, @@ -18,7 +19,7 @@ test('literal()', (t) => { 'should throw if the given node has `children`' ) - t.throws( + assert.throws( () => { literal({type: 'break'}) }, @@ -26,9 +27,7 @@ test('literal()', (t) => { 'should throw if the given node has no `value`' ) - t.doesNotThrow(() => { + assert.doesNotThrow(() => { literal({type: 'text', value: 'foo'}) }, 'should not throw on valid text nodes') - - t.end() }) diff --git a/test/node.js b/test/node.js index a20a7a6..f9da6d6 100644 --- a/test/node.js +++ b/test/node.js @@ -1,8 +1,9 @@ -import test from 'tape' +import nodeAssert from 'node:assert/strict' +import test from 'node:test' import {assert} from '../index.js' -test('node', (t) => { - t.throws( +test('node', () => { + nodeAssert.throws( () => { assert() }, @@ -10,7 +11,7 @@ test('node', (t) => { 'should throw if not given a node (#1)' ) - t.throws( + nodeAssert.throws( () => { assert(null) }, @@ -18,7 +19,7 @@ test('node', (t) => { 'should throw if not given a node (#2)' ) - t.throws( + nodeAssert.throws( () => { assert('foo') }, @@ -26,13 +27,11 @@ test('node', (t) => { 'should throw if given a non-node (#1)' ) - t.throws( + nodeAssert.throws( () => { assert(6) }, /node should be an object: `6`$/, 'should throw if not given a non-node (#2)' ) - - t.end() }) diff --git a/test/non-defined.js b/test/non-defined.js index bae8870..abe7431 100644 --- a/test/non-defined.js +++ b/test/non-defined.js @@ -1,8 +1,9 @@ -import test from 'tape' +import nodeAssert from 'node:assert/strict' +import test from 'node:test' import {assert} from '../index.js' -test('non-defined', (t) => { - t.doesNotThrow(() => { +test('non-defined', () => { + nodeAssert.doesNotThrow(() => { assert({ type: 'element', properties: { @@ -17,7 +18,7 @@ test('non-defined', (t) => { }) }, 'should not throw if non-defined properties are found') - t.throws( + nodeAssert.throws( () => { assert({ type: 'break', @@ -27,6 +28,4 @@ test('non-defined', (t) => { /non-specced property `data` should be JSON: `{ type: 'break', data: { foo: \[Function: Function] } }`$/, 'should throw if non-defined properties are not serialisable' ) - - t.end() }) diff --git a/test/parent.js b/test/parent.js index 0f76cba..c247a19 100644 --- a/test/parent.js +++ b/test/parent.js @@ -1,8 +1,9 @@ -import test from 'tape' +import assert from 'node:assert/strict' +import test from 'node:test' import {parent} from '../index.js' -test('parent()', (t) => { - t.throws( +test('parent()', () => { + assert.throws( () => { parent({}) }, @@ -10,7 +11,7 @@ test('parent()', (t) => { 'should throw the same errors as `assert()`' ) - t.throws( + assert.throws( () => { parent({type: 'text', value: 'foo'}) }, @@ -18,7 +19,7 @@ test('parent()', (t) => { 'should throw if the given node has a `value`' ) - t.throws( + assert.throws( () => { parent({type: 'break'}) }, @@ -26,9 +27,7 @@ test('parent()', (t) => { 'should throw if the given node has `children`' ) - t.doesNotThrow(() => { + assert.doesNotThrow(() => { parent({type: 'strong', children: []}) }, 'should not throw on valid void nodes') - - t.end() }) diff --git a/test/position.js b/test/position.js index fa4139d..7c09655 100644 --- a/test/position.js +++ b/test/position.js @@ -1,8 +1,9 @@ -import test from 'tape' +import nodeAssert from 'node:assert/strict' +import test from 'node:test' import {assert} from '../index.js' -test('position', (t) => { - t.throws( +test('position', () => { + nodeAssert.throws( () => { assert({type: 'foo', position: 1}) }, @@ -10,15 +11,15 @@ test('position', (t) => { 'should throw if given a non-object `position`' ) - t.doesNotThrow(() => { + nodeAssert.doesNotThrow(() => { assert({type: 'foo', position: null}) }, 'should not throw if given a null `position`') - t.doesNotThrow(() => { + nodeAssert.doesNotThrow(() => { assert({type: 'foo', position: {}}) }, 'should not throw if given an empty `position` object') - t.throws( + nodeAssert.throws( () => { assert({type: 'foo', position: {start: 1}}) }, @@ -26,15 +27,15 @@ test('position', (t) => { 'should throw if given a non-object `position.start`' ) - t.doesNotThrow(() => { + nodeAssert.doesNotThrow(() => { assert({type: 'foo', position: {start: null}}) }, 'should not throw if given a null `position.start`') - t.doesNotThrow(() => { + nodeAssert.doesNotThrow(() => { assert({type: 'foo', position: {start: {}}}) }, 'should not throw if given an empty `position.start` object') - t.throws( + nodeAssert.throws( () => { assert({type: 'foo', position: {end: 1}}) }, @@ -42,31 +43,31 @@ test('position', (t) => { 'should throw if given a non-object `position.end`' ) - t.doesNotThrow(() => { + nodeAssert.doesNotThrow(() => { assert({type: 'foo', position: {end: null}}) }, 'should not throw if given a null `position.end`') - t.doesNotThrow(() => { + nodeAssert.doesNotThrow(() => { assert({type: 'foo', position: {end: {}}}) }, 'should not throw if given an empty `position.end` object') - t.doesNotThrow(() => { + nodeAssert.doesNotThrow(() => { assert({type: 'foo', position: {start: {line: null}}}) }, 'should not throw if given a `position.start.line` to `null`') - t.doesNotThrow(() => { + nodeAssert.doesNotThrow(() => { assert({type: 'foo', position: {start: {column: null}}}) }, 'should not throw if given a `position.start.column` to `null`') - t.doesNotThrow(() => { + nodeAssert.doesNotThrow(() => { assert({type: 'foo', position: {end: {line: null}}}) }, 'should not throw if given a `position.end.line` to `null`') - t.doesNotThrow(() => { + nodeAssert.doesNotThrow(() => { assert({type: 'foo', position: {end: {column: null}}}) }, 'should not throw if given a `position.end.column` to `null`') - t.throws( + nodeAssert.throws( () => { assert({type: 'foo', position: {start: {line: 0}}}) }, @@ -74,7 +75,7 @@ test('position', (t) => { 'should throw if `position.start.line` is less than 1' ) - t.throws( + nodeAssert.throws( () => { assert({type: 'foo', position: {start: {column: 0}}}) }, @@ -82,7 +83,7 @@ test('position', (t) => { 'should throw if `position.start.column` is less than 1' ) - t.throws( + nodeAssert.throws( () => { assert({type: 'foo', position: {end: {line: 0}}}) }, @@ -90,13 +91,11 @@ test('position', (t) => { 'should throw if `position.end.line` is less than 1' ) - t.throws( + nodeAssert.throws( () => { assert({type: 'foo', position: {end: {column: 0}}}) }, /`position.end.column` should be gte `1`: `{ type: 'foo', position: { end: { column: 0 } } }`$/, 'should throw if `position.end.column` is less than 1' ) - - t.end() }) diff --git a/test/type.js b/test/type.js index 1086770..52e2afd 100644 --- a/test/type.js +++ b/test/type.js @@ -1,8 +1,9 @@ -import test from 'tape' +import nodeAssert from 'node:assert/strict' +import test from 'node:test' import {assert} from '../index.js' -test('type', (t) => { - t.throws( +test('type', () => { + nodeAssert.throws( () => { assert({}) }, @@ -10,7 +11,7 @@ test('type', (t) => { 'should throw if not given a `type` (#1)' ) - t.throws( + nodeAssert.throws( () => { assert({value: 'foo'}) }, @@ -18,7 +19,7 @@ test('type', (t) => { 'should throw if not given a type (#2)' ) - t.throws( + nodeAssert.throws( () => { assert({type: 1}) }, @@ -26,7 +27,7 @@ test('type', (t) => { 'should throw if not given a non-string type' ) - t.throws( + nodeAssert.throws( () => { assert({type: ''}) }, @@ -34,9 +35,7 @@ test('type', (t) => { 'should throw if given an empty string type' ) - t.doesNotThrow(() => { + nodeAssert.doesNotThrow(() => { assert({type: 'foo'}) }, 'should not throw if given a non-empty type string') - - t.end() }) diff --git a/test/value.js b/test/value.js index 2cc1ed9..f2f50e5 100644 --- a/test/value.js +++ b/test/value.js @@ -1,8 +1,9 @@ -import test from 'tape' +import nodeAssert from 'node:assert/strict' +import test from 'node:test' import {assert} from '../index.js' -test('value', (t) => { - t.throws( +test('value', () => { + nodeAssert.throws( () => { assert({type: 'foo', value: 1}) }, @@ -10,21 +11,19 @@ test('value', (t) => { 'should throw if given a non-string `value`' ) - t.doesNotThrow(() => { + nodeAssert.doesNotThrow(() => { assert({type: 'foo', value: ''}) }, 'should not throw if given an empty string `value`') - t.doesNotThrow(() => { + nodeAssert.doesNotThrow(() => { assert({type: 'foo', value: 'foo'}) }, 'should not throw if given an string `value`') - t.doesNotThrow(() => { + nodeAssert.doesNotThrow(() => { assert({type: 'foo', value: undefined}) }, 'should not throw if given an undefined `value`') - t.doesNotThrow(() => { + nodeAssert.doesNotThrow(() => { assert({type: 'foo', value: null}) }, 'should not throw if given an null `value`') - - t.end() }) diff --git a/test/void.js b/test/void.js index cc1ea54..7384f2c 100644 --- a/test/void.js +++ b/test/void.js @@ -1,8 +1,9 @@ -import test from 'tape' +import assert from 'node:assert/strict' +import test from 'node:test' import {_void} from '../index.js' -test('_void()', (t) => { - t.throws( +test('_void()', () => { + assert.throws( () => { _void({}) }, @@ -10,7 +11,7 @@ test('_void()', (t) => { 'should throw the same errors as `assert()`' ) - t.throws( + assert.throws( () => { _void({type: 'text', value: 'foo'}) }, @@ -18,7 +19,7 @@ test('_void()', (t) => { 'should throw if the given node has a `value`' ) - t.throws( + assert.throws( () => { _void({type: 'strong', children: []}) }, @@ -26,9 +27,7 @@ test('_void()', (t) => { 'should throw if the given node has `children`' ) - t.doesNotThrow(() => { + assert.doesNotThrow(() => { _void({type: 'break'}) }, 'should not throw on valid void nodes') - - t.end() }) From e0f582e967e052fd1c4b98c4bb745407ab37ff52 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 6 Jan 2023 18:27:17 +0100 Subject: [PATCH 13/16] Refactor to move implementation to `lib/` --- index.js | 371 +----------------- index.test-d.ts | 2 +- lib/index.js | 372 +++++++++++++++++++ inspect.browser.js => lib/inspect.browser.js | 0 inspect.js => lib/inspect.js | 0 package.json | 13 +- readme.md | 2 +- 7 files changed, 381 insertions(+), 379 deletions(-) create mode 100644 lib/index.js rename inspect.browser.js => lib/inspect.browser.js (100%) rename inspect.js => lib/inspect.js (100%) diff --git a/index.js b/index.js index 97b793a..a286e7d 100644 --- a/index.js +++ b/index.js @@ -1,372 +1,5 @@ /** - * @typedef {import('assert').AssertionError} AssertionError - * @typedef {import('unist').Node} Node - * @typedef {import('unist').Parent} Parent - * @typedef {import('unist').Literal} Literal - * @typedef {import('unist').Position} Position - * @typedef {import('unist').Point} Point - * @typedef {Node & {children: never, value: never}} _Void - * - * @typedef SeenErrorFields - * @property {true} [__unist__] - * - * @typedef {Error & SeenErrorFields} SeenError + * @typedef {import('./lib/index.js').AssertionError} AssertionError */ -import nodeAssert from 'node:assert' -import {inspect} from './inspect.js' - -const own = {}.hasOwnProperty - -/** - * Assert that `tree` is a valid unist node. - * - * If `node` is a parent, all children will be asserted too. - * - * @param {unknown} [tree] - * Thing to assert. - * @param {Parent | null | undefined} [parent] - * Optional, valid parent. - * @returns {asserts tree is Node} - * Whether `tree` (and its descendants) are valid nodes. - * @throws {AssertionError} - * When `tree` (or its descendants) is not a node. - */ -export function assert(tree, parent) { - return wrap(assertNode)(tree, parent) -} - -/** - * Assert that `tree` is a valid unist parent. - * - * All children will be asserted too. - * - * @param {unknown} [tree] - * Thing to assert. - * @param {Parent | null | undefined} [parent] - * Optional, valid parent. - * @returns {asserts tree is Parent} - * Whether `tree` is a parent and its descendants are valid nodes. - * @throws {AssertionError} - * When `tree` is not a parent or its descendants are not nodes. - */ -export function parent(tree, parent) { - return wrap(assertParent)(tree, parent) -} - -/** - * Assert that `node` is a valid unist literal. - * - * @param {unknown} [node] - * Thing to assert. - * @param {Parent | null | undefined} [parent] - * Optional, valid parent. - * @returns {asserts node is Literal} - * Whether `node` is a literal. - * @throws {AssertionError} - * When `node` is not a literal. - */ -export function literal(node, parent) { - return wrap(assertLiteral)(node, parent) -} - -/** - * Assert that `node` is a valid void node. - * - * @param {unknown} [node] - * Thing to assert. - * @param {Parent | null | undefined} [parent] - * Optional, valid parent. - * @returns {asserts node is _Void} - * Whether `node` is a node but neither parent nor literal. - * @throws {AssertionError} - * When `node` is not a node, a parent, or a literal. - */ -export function _void(node, parent) { - return wrap(assertVoid)(node, parent) -} - -// Identifier to check if a value is seen. -const ID = '__unist__' - -// List of specced properties. -const defined = new Set(['type', 'value', 'children', 'position']) - -/** - * Wrapper that adds the current node (and parent, if available) to error - * messages. - * - * @template {Node} T - * Node type. - * @param {(node?: any, parent?: Parent | null | undefined) => asserts node is T} fn - * Custom assertion. - * @returns {(node?: any, parent?: Parent | null | undefined) => asserts node is T} - * Assertion. - */ -export function wrap(fn) { - return wrapped - - /** - * @param {unknown} node - * Thing to check. - * @param {Parent | null | undefined} [parent] - * Optional, valid parent. - * @throws {AssertionError} - * Whether `node` is a node but neither parent nor literal. - * @returns {void} - * Nothing. - */ - function wrapped(node, parent) { - try { - fn(node, parent) - } catch (error) { - const exception = /** @type {SeenError} */ (error) - if (!own.call(exception, ID)) { - exception[ID] = true - exception.message += ': `' + view(node) + '`' - if (parent) exception.message += ' in `' + view(parent) + '`' - } - - throw error - } - } -} - -/** - * Assert that `node` is a valid unist parent. - * - * All children will be asserted too. - * - * @param {unknown} node - * Thing to assert. - * @returns {asserts node is Node} - * Whether `node` (and its descendants) are valid nodes. - * @throws {AssertionError} - * When `node` (or its descendants) is not a node. - */ -function assertNode(node) { - let index = -1 - - nodeAssert.ok( - node && typeof node === 'object' && !Array.isArray(node), - 'node should be an object' - ) - - nodeAssert.ok(own.call(node, 'type'), 'node should have a type') - nodeAssert.strictEqual( - // @ts-expect-error Looks like an indexed object. - typeof node.type, - 'string', - '`type` should be a string' - ) - // @ts-expect-error Looks like an indexed object. - nodeAssert.notStrictEqual(node.type, '', '`type` should not be empty') - - // @ts-expect-error Looks like an indexed object. - if (node.value !== null && node.value !== undefined) { - nodeAssert.strictEqual( - // @ts-expect-error Looks like an indexed object. - typeof node.value, - 'string', - '`value` should be a string' - ) - } - - // @ts-expect-error Looks like an indexed object. - position(node.position) - - /** @type {string} */ - let key - - for (key in node) { - if (!defined.has(key)) { - /** @type {unknown} */ - // @ts-expect-error: hush. - const value = node[key] - vanilla(key, value) - } - } - - // @ts-expect-error Looks like an indexed object. - if (node.children !== null && node.children !== undefined) { - /** @type {Parent} */ - // @ts-expect-error Looks like parent. - const parent = node - nodeAssert.ok( - Array.isArray(parent.children), - '`children` should be an array' - ) - index = -1 - - while (++index < parent.children.length) { - assert(parent.children[index], parent) - } - } -} - -/** - * Assert `value` (which lives at `key`) can be stringified and re-parsed to the - * same (deep) value. - * - * @param {string} key - * Name of field. - * @param {unknown} value - * Value of field. - */ -function vanilla(key, value) { - try { - nodeAssert.deepStrictEqual(value, JSON.parse(JSON.stringify(value))) - } catch { - nodeAssert.fail('non-specced property `' + key + '` should be JSON') - } -} - -/** - * Stringify a value to inspect it. - * - * Tries `JSON.stringify()`, and if that fails uses `String()` instead. - * - * @param {unknown} value - * Anything (should be JSON). - * @returns {string} - * User-visible preresentation. - */ -function view(value) { - try { - return inspect(value) - /* c8 ignore next 3 */ - } catch { - return String(value) - } -} - -/** - * Assert that `node` is a valid unist parent. - * - * All children will be asserted too. - * - * @param {Node} node - * Thing to assert. - * @returns {asserts node is Parent} - * Whether `node` is a parent and its descendants are valid nodes. - * @throws {AssertionError} - * When `node` is not a parent or its descendants are not nodes. - */ -function assertParent(node) { - assertNode(node) - - nodeAssert.strictEqual( - 'value' in node, - false, - 'parent should not have `value`' - ) - nodeAssert.ok('children' in node, 'parent should have `children`') -} - -/** - * Assert that `node` is a valid unist literal. - * - * @param {unknown} [node] - * Thing to assert. - * @returns {asserts node is Literal} - * Whether `node` is a literal. - * @throws {AssertionError} - * When `node` is not a literal. - */ -function assertLiteral(node) { - assertNode(node) - - nodeAssert.strictEqual( - 'children' in node, - false, - 'literal should not have `children`' - ) - nodeAssert.ok('value' in node, 'literal should have `value`') -} - -/** - * Assert that `node` is a valid void node. - * - * @param {unknown} [node] - * Thing to assert. - * @returns {asserts node is _Void} - * Whether `node` is a node but neither parent nor literal. - * @throws {AssertionError} - * When `node` is not a node, a parent, or a literal. - */ -function assertVoid(node) { - assertNode(node) - - nodeAssert.strictEqual('value' in node, false, 'void should not have `value`') - nodeAssert.strictEqual( - 'children' in node, - false, - 'void should not have `children`' - ) -} - -/** - * Assert that `position` is a unist position. - * - * @param {unknown} position - * Thing to assert. - * @returns {asserts position is Position} - * Whether `position` is a unist position. - * @throws {AssertionError} - * When `position` is not a position. - */ -function position(position) { - if (position !== null && position !== undefined) { - nodeAssert.ok( - typeof position === 'object' && position === Object(position), - '`position` should be an object' - ) - - // @ts-expect-error: indexable. - point(position.start, 'position.start') - // @ts-expect-error: indexable. - point(position.end, 'position.end') - } -} - -/** - * Assert `point` is a unist point. - * - * @param {unknown} point - * Thing to assert. - * @param {string} label - * Whether `point` is a unist point. - * @returns {asserts point is Point} - * When `point` is not a point. - */ -function point(point, label) { - if (point !== null && point !== undefined) { - nodeAssert.ok( - typeof point === 'object' && point === Object(point), - '`' + label + '` should be an object' - ) - - if ('line' in point && point.line !== null && point.line !== undefined) { - nodeAssert.ok( - typeof point.line === 'number', - '`' + label + '` should have numeric `line`' - ) - nodeAssert.ok(point.line >= 1, '`' + label + '.line` should be gte `1`') - } - - if ( - 'column' in point && - point.column !== null && - point.column !== undefined - ) { - nodeAssert.ok( - typeof point.column === 'number', - '`' + label + '` should have numeric `column`' - ) - nodeAssert.ok( - point.column >= 1, - '`' + label + '.column` should be gte `1`' - ) - } - } -} +export {_void, assert, literal, parent, wrap} from './lib/index.js' diff --git a/index.test-d.ts b/index.test-d.ts index e9f0264..e1b4525 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -1,5 +1,5 @@ import {expectType, expectNotType} from 'tsd' -import {type Node, type Parent} from 'unist' +import type {Node, Parent} from 'unist' import {assert, parent} from './index.js' const emptyNode = {type: 'a'} diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..97b793a --- /dev/null +++ b/lib/index.js @@ -0,0 +1,372 @@ +/** + * @typedef {import('assert').AssertionError} AssertionError + * @typedef {import('unist').Node} Node + * @typedef {import('unist').Parent} Parent + * @typedef {import('unist').Literal} Literal + * @typedef {import('unist').Position} Position + * @typedef {import('unist').Point} Point + * @typedef {Node & {children: never, value: never}} _Void + * + * @typedef SeenErrorFields + * @property {true} [__unist__] + * + * @typedef {Error & SeenErrorFields} SeenError + */ + +import nodeAssert from 'node:assert' +import {inspect} from './inspect.js' + +const own = {}.hasOwnProperty + +/** + * Assert that `tree` is a valid unist node. + * + * If `node` is a parent, all children will be asserted too. + * + * @param {unknown} [tree] + * Thing to assert. + * @param {Parent | null | undefined} [parent] + * Optional, valid parent. + * @returns {asserts tree is Node} + * Whether `tree` (and its descendants) are valid nodes. + * @throws {AssertionError} + * When `tree` (or its descendants) is not a node. + */ +export function assert(tree, parent) { + return wrap(assertNode)(tree, parent) +} + +/** + * Assert that `tree` is a valid unist parent. + * + * All children will be asserted too. + * + * @param {unknown} [tree] + * Thing to assert. + * @param {Parent | null | undefined} [parent] + * Optional, valid parent. + * @returns {asserts tree is Parent} + * Whether `tree` is a parent and its descendants are valid nodes. + * @throws {AssertionError} + * When `tree` is not a parent or its descendants are not nodes. + */ +export function parent(tree, parent) { + return wrap(assertParent)(tree, parent) +} + +/** + * Assert that `node` is a valid unist literal. + * + * @param {unknown} [node] + * Thing to assert. + * @param {Parent | null | undefined} [parent] + * Optional, valid parent. + * @returns {asserts node is Literal} + * Whether `node` is a literal. + * @throws {AssertionError} + * When `node` is not a literal. + */ +export function literal(node, parent) { + return wrap(assertLiteral)(node, parent) +} + +/** + * Assert that `node` is a valid void node. + * + * @param {unknown} [node] + * Thing to assert. + * @param {Parent | null | undefined} [parent] + * Optional, valid parent. + * @returns {asserts node is _Void} + * Whether `node` is a node but neither parent nor literal. + * @throws {AssertionError} + * When `node` is not a node, a parent, or a literal. + */ +export function _void(node, parent) { + return wrap(assertVoid)(node, parent) +} + +// Identifier to check if a value is seen. +const ID = '__unist__' + +// List of specced properties. +const defined = new Set(['type', 'value', 'children', 'position']) + +/** + * Wrapper that adds the current node (and parent, if available) to error + * messages. + * + * @template {Node} T + * Node type. + * @param {(node?: any, parent?: Parent | null | undefined) => asserts node is T} fn + * Custom assertion. + * @returns {(node?: any, parent?: Parent | null | undefined) => asserts node is T} + * Assertion. + */ +export function wrap(fn) { + return wrapped + + /** + * @param {unknown} node + * Thing to check. + * @param {Parent | null | undefined} [parent] + * Optional, valid parent. + * @throws {AssertionError} + * Whether `node` is a node but neither parent nor literal. + * @returns {void} + * Nothing. + */ + function wrapped(node, parent) { + try { + fn(node, parent) + } catch (error) { + const exception = /** @type {SeenError} */ (error) + if (!own.call(exception, ID)) { + exception[ID] = true + exception.message += ': `' + view(node) + '`' + if (parent) exception.message += ' in `' + view(parent) + '`' + } + + throw error + } + } +} + +/** + * Assert that `node` is a valid unist parent. + * + * All children will be asserted too. + * + * @param {unknown} node + * Thing to assert. + * @returns {asserts node is Node} + * Whether `node` (and its descendants) are valid nodes. + * @throws {AssertionError} + * When `node` (or its descendants) is not a node. + */ +function assertNode(node) { + let index = -1 + + nodeAssert.ok( + node && typeof node === 'object' && !Array.isArray(node), + 'node should be an object' + ) + + nodeAssert.ok(own.call(node, 'type'), 'node should have a type') + nodeAssert.strictEqual( + // @ts-expect-error Looks like an indexed object. + typeof node.type, + 'string', + '`type` should be a string' + ) + // @ts-expect-error Looks like an indexed object. + nodeAssert.notStrictEqual(node.type, '', '`type` should not be empty') + + // @ts-expect-error Looks like an indexed object. + if (node.value !== null && node.value !== undefined) { + nodeAssert.strictEqual( + // @ts-expect-error Looks like an indexed object. + typeof node.value, + 'string', + '`value` should be a string' + ) + } + + // @ts-expect-error Looks like an indexed object. + position(node.position) + + /** @type {string} */ + let key + + for (key in node) { + if (!defined.has(key)) { + /** @type {unknown} */ + // @ts-expect-error: hush. + const value = node[key] + vanilla(key, value) + } + } + + // @ts-expect-error Looks like an indexed object. + if (node.children !== null && node.children !== undefined) { + /** @type {Parent} */ + // @ts-expect-error Looks like parent. + const parent = node + nodeAssert.ok( + Array.isArray(parent.children), + '`children` should be an array' + ) + index = -1 + + while (++index < parent.children.length) { + assert(parent.children[index], parent) + } + } +} + +/** + * Assert `value` (which lives at `key`) can be stringified and re-parsed to the + * same (deep) value. + * + * @param {string} key + * Name of field. + * @param {unknown} value + * Value of field. + */ +function vanilla(key, value) { + try { + nodeAssert.deepStrictEqual(value, JSON.parse(JSON.stringify(value))) + } catch { + nodeAssert.fail('non-specced property `' + key + '` should be JSON') + } +} + +/** + * Stringify a value to inspect it. + * + * Tries `JSON.stringify()`, and if that fails uses `String()` instead. + * + * @param {unknown} value + * Anything (should be JSON). + * @returns {string} + * User-visible preresentation. + */ +function view(value) { + try { + return inspect(value) + /* c8 ignore next 3 */ + } catch { + return String(value) + } +} + +/** + * Assert that `node` is a valid unist parent. + * + * All children will be asserted too. + * + * @param {Node} node + * Thing to assert. + * @returns {asserts node is Parent} + * Whether `node` is a parent and its descendants are valid nodes. + * @throws {AssertionError} + * When `node` is not a parent or its descendants are not nodes. + */ +function assertParent(node) { + assertNode(node) + + nodeAssert.strictEqual( + 'value' in node, + false, + 'parent should not have `value`' + ) + nodeAssert.ok('children' in node, 'parent should have `children`') +} + +/** + * Assert that `node` is a valid unist literal. + * + * @param {unknown} [node] + * Thing to assert. + * @returns {asserts node is Literal} + * Whether `node` is a literal. + * @throws {AssertionError} + * When `node` is not a literal. + */ +function assertLiteral(node) { + assertNode(node) + + nodeAssert.strictEqual( + 'children' in node, + false, + 'literal should not have `children`' + ) + nodeAssert.ok('value' in node, 'literal should have `value`') +} + +/** + * Assert that `node` is a valid void node. + * + * @param {unknown} [node] + * Thing to assert. + * @returns {asserts node is _Void} + * Whether `node` is a node but neither parent nor literal. + * @throws {AssertionError} + * When `node` is not a node, a parent, or a literal. + */ +function assertVoid(node) { + assertNode(node) + + nodeAssert.strictEqual('value' in node, false, 'void should not have `value`') + nodeAssert.strictEqual( + 'children' in node, + false, + 'void should not have `children`' + ) +} + +/** + * Assert that `position` is a unist position. + * + * @param {unknown} position + * Thing to assert. + * @returns {asserts position is Position} + * Whether `position` is a unist position. + * @throws {AssertionError} + * When `position` is not a position. + */ +function position(position) { + if (position !== null && position !== undefined) { + nodeAssert.ok( + typeof position === 'object' && position === Object(position), + '`position` should be an object' + ) + + // @ts-expect-error: indexable. + point(position.start, 'position.start') + // @ts-expect-error: indexable. + point(position.end, 'position.end') + } +} + +/** + * Assert `point` is a unist point. + * + * @param {unknown} point + * Thing to assert. + * @param {string} label + * Whether `point` is a unist point. + * @returns {asserts point is Point} + * When `point` is not a point. + */ +function point(point, label) { + if (point !== null && point !== undefined) { + nodeAssert.ok( + typeof point === 'object' && point === Object(point), + '`' + label + '` should be an object' + ) + + if ('line' in point && point.line !== null && point.line !== undefined) { + nodeAssert.ok( + typeof point.line === 'number', + '`' + label + '` should have numeric `line`' + ) + nodeAssert.ok(point.line >= 1, '`' + label + '.line` should be gte `1`') + } + + if ( + 'column' in point && + point.column !== null && + point.column !== undefined + ) { + nodeAssert.ok( + typeof point.column === 'number', + '`' + label + '` should have numeric `column`' + ) + nodeAssert.ok( + point.column >= 1, + '`' + label + '.column` should be gte `1`' + ) + } + } +} diff --git a/inspect.browser.js b/lib/inspect.browser.js similarity index 100% rename from inspect.browser.js rename to lib/inspect.browser.js diff --git a/inspect.js b/lib/inspect.js similarity index 100% rename from inspect.js rename to lib/inspect.js diff --git a/package.json b/package.json index 040af2a..e5c8271 100644 --- a/package.json +++ b/package.json @@ -26,19 +26,16 @@ "type": "module", "main": "index.js", "browser": { - "./inspect.js": "./inspect.browser.js" + "./lib/inspect.js": "./lib/inspect.browser.js" }, "react-native": { - "./inspect.js": "./inspect.browser.js" + "./lib/inspect.js": "./lib/inspect.browser.js" }, "types": "index.d.ts", "files": [ + "lib/", "index.d.ts", - "index.js", - "inspect.d.ts", - "inspect.js", - "inspect.browser.d.ts", - "inspect.browser.js" + "index.js" ], "dependencies": { "@types/unist": "^2.0.0" @@ -85,7 +82,7 @@ "ignoreCatch": true, "#": "Couple of needed any’s", "ignoreFiles": [ - "index.d.ts" + "lib/index.d.ts" ] } } diff --git a/readme.md b/readme.md index a4d157a..ec2e9d8 100644 --- a/readme.md +++ b/readme.md @@ -141,7 +141,7 @@ message. ## Types This package is fully typed with [TypeScript][]. -It does not export additional types. +It exports the additional type `AssertionError`. ## Compatibility From fbb5ebbe6c06027082fd8991c2b71950d7537314 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 6 Jan 2023 18:28:49 +0100 Subject: [PATCH 14/16] Add tests for exposed identifiers --- test/index.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/index.js b/test/index.js index 63a1f69..4f863a5 100644 --- a/test/index.js +++ b/test/index.js @@ -9,3 +9,15 @@ import './parent.js' import './literal.js' import './void.js' /* eslint-enable import/no-unassigned-import */ + +import assert from 'node:assert/strict' +import test from 'node:test' +import * as mod from '../index.js' + +test('assert', () => { + assert.deepEqual( + Object.keys(mod).sort(), + ['_void', 'assert', 'literal', 'parent', 'wrap'], + 'should expose the public api' + ) +}) From 61a3eedc6f35d536bd5fdbb6af76a539f84e6854 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 6 Jan 2023 18:40:34 +0100 Subject: [PATCH 15/16] Add improved docs --- lib/index.js | 28 +++++++----- readme.md | 121 +++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 119 insertions(+), 30 deletions(-) diff --git a/lib/index.js b/lib/index.js index 97b793a..dbdcd71 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,5 +1,7 @@ /** * @typedef {import('assert').AssertionError} AssertionError + * Assertion error from `node:assert`. + * * @typedef {import('unist').Node} Node * @typedef {import('unist').Parent} Parent * @typedef {import('unist').Literal} Literal @@ -21,14 +23,14 @@ const own = {}.hasOwnProperty /** * Assert that `tree` is a valid unist node. * - * If `node` is a parent, all children will be asserted too. + * If `tree` is a parent, all children will be asserted too. * * @param {unknown} [tree] * Thing to assert. * @param {Parent | null | undefined} [parent] * Optional, valid parent. * @returns {asserts tree is Node} - * Whether `tree` (and its descendants) are valid nodes. + * Nothing. * @throws {AssertionError} * When `tree` (or its descendants) is not a node. */ @@ -46,7 +48,7 @@ export function assert(tree, parent) { * @param {Parent | null | undefined} [parent] * Optional, valid parent. * @returns {asserts tree is Parent} - * Whether `tree` is a parent and its descendants are valid nodes. + * Nothing. * @throws {AssertionError} * When `tree` is not a parent or its descendants are not nodes. */ @@ -62,7 +64,7 @@ export function parent(tree, parent) { * @param {Parent | null | undefined} [parent] * Optional, valid parent. * @returns {asserts node is Literal} - * Whether `node` is a literal. + * Nothing. * @throws {AssertionError} * When `node` is not a literal. */ @@ -78,7 +80,7 @@ export function literal(node, parent) { * @param {Parent | null | undefined} [parent] * Optional, valid parent. * @returns {asserts node is _Void} - * Whether `node` is a node but neither parent nor literal. + * Nothing. * @throws {AssertionError} * When `node` is not a node, a parent, or a literal. */ @@ -140,7 +142,7 @@ export function wrap(fn) { * @param {unknown} node * Thing to assert. * @returns {asserts node is Node} - * Whether `node` (and its descendants) are valid nodes. + * Nothing. * @throws {AssertionError} * When `node` (or its descendants) is not a node. */ @@ -248,7 +250,7 @@ function view(value) { * @param {Node} node * Thing to assert. * @returns {asserts node is Parent} - * Whether `node` is a parent and its descendants are valid nodes. + * Nothing. * @throws {AssertionError} * When `node` is not a parent or its descendants are not nodes. */ @@ -269,7 +271,7 @@ function assertParent(node) { * @param {unknown} [node] * Thing to assert. * @returns {asserts node is Literal} - * Whether `node` is a literal. + * Nothing. * @throws {AssertionError} * When `node` is not a literal. */ @@ -290,7 +292,7 @@ function assertLiteral(node) { * @param {unknown} [node] * Thing to assert. * @returns {asserts node is _Void} - * Whether `node` is a node but neither parent nor literal. + * Nothing. * @throws {AssertionError} * When `node` is not a node, a parent, or a literal. */ @@ -311,7 +313,7 @@ function assertVoid(node) { * @param {unknown} position * Thing to assert. * @returns {asserts position is Position} - * Whether `position` is a unist position. + * Nothing. * @throws {AssertionError} * When `position` is not a position. */ @@ -335,9 +337,11 @@ function position(position) { * @param {unknown} point * Thing to assert. * @param {string} label - * Whether `point` is a unist point. + * `start` or `end` * @returns {asserts point is Point} - * When `point` is not a point. + * Nothing. + * @throws {AssertionError} + * When `point` is not a position. */ function point(point, label) { if (point !== null && point !== undefined) { diff --git a/readme.md b/readme.md index ec2e9d8..384f48b 100644 --- a/readme.md +++ b/readme.md @@ -22,6 +22,7 @@ * [`literal(node[, parent])`](#literalnode-parent) * [`_void(node[, parent])`](#_voidnode-parent) * [`wrap(fn)`](#wrapfn) + * [`AssertionError`](#assertionerror) * [Extensions](#extensions) * [Types](#types) * [Compatibility](#compatibility) @@ -45,7 +46,7 @@ do the same but for mdast, hast, and nlcst nodes, respectively. ## Install This package is [ESM only][esm]. -In Node.js (version 12.20+, 14.14+, 16.0+, or 18.0+), install with [npm][]: +In Node.js (version 14.14+ and 16.0+), install with [npm][]: ```sh npm install unist-util-assert @@ -93,41 +94,113 @@ assert({type: 'paragraph', children: ['foo']}) ## API -This package exports the identifiers `assert`, `parent`, `literal`, `_void`, -and `wrap`. +This package exports the identifiers [`_void`][void], [`assert`][assert], +[`literal`][literal], [`parent`][parent], and [`wrap`][wrap]. There is no default export. ### `assert(tree[, parent])` Assert that `tree` is a valid unist [`Node`][node]. -If `tree` is a [parent][], all children will be asserted as well. +If `tree` is a parent, all children will be asserted too. + +###### Parameters + +* `tree` (`unknown`) + — thing to assert +* `parent` ([`Parent`][parent-node], optional) + — optional, valid parent + +###### Returns + +Nothing. ###### Throws -When `node`, or one of its children, is not a valid node. +When `tree` (or its descendants) is not a node. ### `parent(tree[, parent])` -Assert that `tree` is a valid unist [`Parent`][parent]. +Assert that `tree` is a valid unist [`Parent`][parent-node]. + +All children will be asserted too. + +###### Parameters + +* `tree` (`unknown`) + — thing to assert +* `parent` ([`Parent`][parent-node], optional) + — optional, valid parent + +###### Returns + +Nothing. + +###### Throws -All children will be asserted as well. +When `tree` is not a parent or its descendants are not nodes. ### `literal(node[, parent])` -Assert that `node` is a valid unist [`Literal`][literal]. +Assert that `node` is a valid unist [`Literal`][literal-node]. + +###### Parameters + +* `tree` (`unknown`) + — thing to assert +* `parent` ([`Parent`][parent-node], optional) + — optional, valid parent + +###### Returns + +Nothing. + +###### Throws + +When `node` is not a literal. ### `_void(node[, parent])` -Assert that `node` is a valid unist [`Node`][node], but neither -[`Parent`][parent] nor -[`Literal`][literal]. +Assert that `node` is a valid void node. + +###### Parameters + +* `tree` (`unknown`) + — thing to assert +* `parent` ([`Parent`][parent-node], optional) + — optional, valid parent + +###### Returns + +Nothing. + +###### Throws + +When `node` is not a node, a parent, or a literal. ### `wrap(fn)` -Wraps `fn` (which is passed a node, and an optional parent node), so that any -errors thrown inside it will contain information regarding the node (and the -parent, when given). +Wrapper that adds the current node (and parent, if available) to error +messages. + +###### Parameters + +* `fn` (`(node?: any, parent?: Parent | null | undefined) => asserts node is Node)`) + — custom assertion + +###### Returns + +Wrapped `fn`. + +### `AssertionError` + +Assertion error from `node:assert` (TypeScript type). + +###### Type + +```ts +type AssertionError = import('node:assert').AssertionError +``` ## Extensions @@ -141,13 +214,13 @@ message. ## Types This package is fully typed with [TypeScript][]. -It exports the additional type `AssertionError`. +It exports the additional type [`AssertionError`][assertionerror]. ## 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+. +As of now, that is Node.js 14.14+ and 16.0+. Our projects sometimes work with older versions, but this is not guaranteed. ## Related @@ -225,12 +298,24 @@ abide by its terms. [node]: https://github.com/syntax-tree/unist#node -[parent]: https://github.com/syntax-tree/unist#parent +[parent-node]: https://github.com/syntax-tree/unist#parent -[literal]: https://github.com/syntax-tree/unist#literal +[literal-node]: https://github.com/syntax-tree/unist#literal [mdast-util-assert]: https://github.com/syntax-tree/mdast-util-assert [hast-util-assert]: https://github.com/syntax-tree/hast-util-assert [nlcst-test]: https://github.com/syntax-tree/nlcst-test + +[assert]: #asserttree-parent + +[parent]: #parenttree-parent + +[literal]: #literalnode-parent + +[void]: #_voidnode-parent + +[wrap]: #wrapfn + +[assertionerror]: #assertionerror From 4e06127cc7180d6fbe789ec0f9005aec6b9acf76 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 6 Jan 2023 18:42:12 +0100 Subject: [PATCH 16/16] 3.0.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e5c8271..9ecb287 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "unist-util-assert", - "version": "3.0.1", + "version": "3.0.2", "description": "unist utility to assert nodes", "license": "MIT", "keywords": [