From 0e9fc7052d616a6c71a4980bde4b6a62d257babc Mon Sep 17 00:00:00 2001 From: Christian Murphy Date: Thu, 3 Mar 2022 20:14:17 -0700 Subject: [PATCH 01/56] 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 dd64740abb2e56e1b6888e868a15177bba1c0cfd Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 1 Apr 2022 11:56:10 +0200 Subject: [PATCH 02/56] Update dev-dependencies --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index ba7f317..73ca84a 100644 --- a/package.json +++ b/package.json @@ -72,16 +72,16 @@ "c8": "^7.0.0", "estree-util-build-jsx": "^2.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", "svg-tag-names": "^3.0.0", "tape": "^5.0.0", - "tsd": "^0.17.0", + "tsd": "^0.19.0", "type-coverage": "^2.0.0", "typescript": "^4.0.0", "unist-builder": "^3.0.0", - "xo": "^0.42.0" + "xo": "^0.48.0" }, "scripts": { "prepack": "npm run build && npm run format", From e70ba2a0bf664f16c45a7947514dc754dae5452d Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 1 Apr 2022 12:01:13 +0200 Subject: [PATCH 03/56] Refactor code-style --- lib/core.js | 28 ++++++++++++++-------------- package.json | 4 ++-- readme.md | 4 ++-- test/core.js | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/core.js b/lib/core.js index d1d22f5..767ed9b 100644 --- a/lib/core.js +++ b/lib/core.js @@ -9,14 +9,14 @@ * * @typedef {Root|Element} HResult * @typedef {string|number} HStyleValue - * @typedef {Object.} HStyle + * @typedef {Record} HStyle * @typedef {string|number|boolean|null|undefined} HPrimitiveValue - * @typedef {Array.} HArrayValue + * @typedef {Array} HArrayValue * @typedef {HPrimitiveValue|HArrayValue} HPropertyValue * @typedef {{[property: string]: HPropertyValue|HStyle}} HProperties * * @typedef {string|number|null|undefined} HPrimitiveChild - * @typedef {Array.} HArrayChild + * @typedef {Array} HArrayChild * @typedef {Node|HPrimitiveChild|HArrayChild} HChild */ @@ -32,7 +32,7 @@ const own = {}.hasOwnProperty /** * @param {Schema} schema * @param {string} defaultTagName - * @param {Array.} [caseSensitive] + * @param {Array} [caseSensitive] */ export function core(schema, defaultTagName, caseSensitive) { const adjust = caseSensitive && createAdjustMap(caseSensitive) @@ -41,9 +41,9 @@ export function core(schema, defaultTagName, caseSensitive) { /** * @type {{ * (): Root - * (selector: null|undefined, ...children: HChild[]): Root - * (selector: string, properties?: HProperties, ...children: HChild[]): Element - * (selector: string, ...children: HChild[]): Element + * (selector: null|undefined, ...children: Array): Root + * (selector: string, properties?: HProperties, ...children: Array): Element + * (selector: string, ...children: Array): Element * }} */ ( @@ -52,7 +52,7 @@ export function core(schema, defaultTagName, caseSensitive) { * * @param {string|null} [selector] * @param {HProperties|HChild} [properties] - * @param {HChild[]} children + * @param {Array} children * @returns {HResult} */ function (selector, properties, ...children) { @@ -179,7 +179,7 @@ function addProperty(schema, properties, key, value) { } if (Array.isArray(result)) { - /** @type {Array.} */ + /** @type {Array} */ const finalResult = [] while (++index < result.length) { @@ -200,7 +200,7 @@ function addProperty(schema, properties, key, value) { } /** - * @param {Array.} nodes + * @param {Array} nodes * @param {HChild} value * @returns {void} */ @@ -256,7 +256,7 @@ function parsePrimitive(info, name, value) { * @returns {string} */ function style(value) { - /** @type {Array.} */ + /** @type {Array} */ const result = [] /** @type {string} */ let key @@ -271,11 +271,11 @@ function style(value) { } /** - * @param {Array.} values - * @returns {Object.} + * @param {Array} values + * @returns {Record} */ function createAdjustMap(values) { - /** @type {Object.} */ + /** @type {Record} */ const result = {} let index = -1 diff --git a/package.json b/package.json index 73ca84a..100864c 100644 --- a/package.json +++ b/package.json @@ -88,8 +88,8 @@ "build": "rimraf \"{script/**,test/**,}*.d.ts\" \"lib/{core,html,index,runtime-html,runtime-svg,runtime,svg-case-sensitive-tag-names,svg}.d.ts\" && tsc && tsd && type-coverage", "generate": "node script/generate-jsx && node script/build", "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/index.js", + "test-coverage": "c8 --check-coverage --100 --reporter lcov npm run test-api", "test": "npm run build && npm run generate && npm run format && npm run test-coverage" }, "prettier": { diff --git a/readme.md b/readme.md index 027966a..e39de42 100644 --- a/readme.md +++ b/readme.md @@ -171,14 +171,14 @@ When nullish, builds a [`Root`][root] instead. ###### `properties` -Map of properties (`Object.<*>`, optional). +Map of properties (`Record`, optional). Keys should match either the HTML attribute name, or the DOM property name, but are case-insensitive. Cannot be given when building a [`Root`][root]. ###### `children` -(Lists of) children (`string`, `number`, `Node`, `Array.`, optional). +(Lists of) children (`string`, `number`, `Node`, `Array`, optional). When strings or numbers are encountered, they are mapped to [`Text`][text] nodes. If [`Root`][root] nodes are given, their children are used instead. diff --git a/test/core.js b/test/core.js index 07dc9f7..c0247ec 100644 --- a/test/core.js +++ b/test/core.js @@ -776,7 +776,7 @@ test('hastscript', (t) => { {type: 'text', value: 'bar'} ] }, - 'should support `Array.` for a `Text`s' + 'should support `Array` for a `Text`s' ) t.deepEqual( From 2f8c133920fa57405815de3f3cdc21d1bc892444 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Fri, 1 Apr 2022 16:15:07 +0200 Subject: [PATCH 04/56] Add improved docs --- example.js | 21 ++++++ html.js | 4 +- index.js | 4 +- lib/core.js | 2 + lib/index.js | 4 +- lib/runtime.js | 2 +- lib/svg.js | 4 +- readme.md | 199 +++++++++++++++++++++++++------------------------ svg.js | 4 +- 9 files changed, 134 insertions(+), 110 deletions(-) create mode 100644 example.js diff --git a/example.js b/example.js new file mode 100644 index 0000000..7f79c4f --- /dev/null +++ b/example.js @@ -0,0 +1,21 @@ +import {h, s} from './index.js' + +// Children as an array: +console.log( + h('.foo#some-id', [ + h('span', 'some text'), + h('input', {type: 'text', value: 'foo'}), + h('a.alpha', {class: 'bravo charlie', download: 'download'}, [ + 'delta', + 'echo' + ]) + ]) +) + +// SVG: +console.log( + s('svg', {xmlns: 'http://www.w3.org/2000/svg', viewbox: '0 0 500 500'}, [ + s('title', 'SVG `` element'), + s('circle', {cx: 120, cy: 120, r: 100}) + ]) +) diff --git a/html.js b/html.js index 2abe80c..8cfe365 100644 --- a/html.js +++ b/html.js @@ -1,6 +1,6 @@ /** - * @typedef {import('./lib/index.js').Child} Child Acceptable child value - * @typedef {import('./lib/index.js').Properties} Properties Acceptable properties value. + * @typedef {import('./lib/index.js').Child} Child + * @typedef {import('./lib/index.js').Properties} Properties */ export {h} from './lib/html.js' diff --git a/index.js b/index.js index c5b0522..c2181f7 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,6 @@ /** - * @typedef {import('./lib/index.js').Child} Child Acceptable child value - * @typedef {import('./lib/index.js').Properties} Properties Acceptable properties value. + * @typedef {import('./lib/index.js').Child} Child + * @typedef {import('./lib/index.js').Properties} Properties */ export {h, s} from './lib/index.js' diff --git a/lib/core.js b/lib/core.js index 767ed9b..48ae826 100644 --- a/lib/core.js +++ b/lib/core.js @@ -14,10 +14,12 @@ * @typedef {Array} HArrayValue * @typedef {HPrimitiveValue|HArrayValue} HPropertyValue * @typedef {{[property: string]: HPropertyValue|HStyle}} HProperties + * Acceptable properties value. * * @typedef {string|number|null|undefined} HPrimitiveChild * @typedef {Array} HArrayChild * @typedef {Node|HPrimitiveChild|HArrayChild} HChild + * Acceptable child value */ import {find, normalize} from 'property-information' diff --git a/lib/index.js b/lib/index.js index 5e87b79..681441b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,6 +1,6 @@ /** - * @typedef {import('./core.js').HChild} Child Acceptable child value - * @typedef {import('./core.js').HProperties} Properties Acceptable properties value. + * @typedef {import('./core.js').HChild} Child + * @typedef {import('./core.js').HProperties} Properties */ export {h} from './html.js' diff --git a/lib/runtime.js b/lib/runtime.js index 7062781..5b0ed6c 100644 --- a/lib/runtime.js +++ b/lib/runtime.js @@ -8,7 +8,7 @@ * @typedef {import('./core.js').HStyle} HStyle * @typedef {import('./core.js').core} Core * - * @typedef {{[x: string]: HPropertyValue|HStyle|HChild}} JSXProps + * @typedef {Record} JSXProps */ /** diff --git a/lib/svg.js b/lib/svg.js index b04a847..0d89ff6 100644 --- a/lib/svg.js +++ b/lib/svg.js @@ -1,6 +1,6 @@ /** - * @typedef {import('./core.js').HChild} Child Acceptable child value - * @typedef {import('./core.js').HProperties} Properties Acceptable properties value. + * @typedef {import('./core.js').HChild} Child + * @typedef {import('./core.js').HProperties} Properties * * @typedef {import('./jsx-classic').Element} s.JSX.Element * @typedef {import('./jsx-classic').IntrinsicAttributes} s.JSX.IntrinsicAttributes diff --git a/readme.md b/readme.md index e39de42..d31b8de 100644 --- a/readme.md +++ b/readme.md @@ -8,31 +8,70 @@ [![Backers][backers-badge]][collective] [![Chat][chat-badge]][chat] -[**hast**][hast] utility to create [*trees*][tree] in HTML or SVG. - -Similar to [`hyperscript`][hyperscript], [`virtual-dom/h`][virtual-hyperscript], -[`React.createElement`][react], and [Vue’s `createElement`][vue], -but for [**hast**][hast]. - -Use [`unist-builder`][u] to create any [**unist**][unist] tree. +[hast][] utility to create trees with ease. + +## Contents + +* [What is this?](#what-is-this) +* [When should I use this?](#when-should-i-use-this) +* [Install](#install) +* [Use](#use) +* [API](#api) + * [`h(selector?[, properties][, …children])`](#hselector-properties-children) + * [`s(selector?[, properties][, …children])`](#sselector-properties-children) +* [JSX](#jsx) +* [Types](#types) +* [Compatibility](#compatibility) +* [Security](#security) +* [Related](#related) +* [Contribute](#contribute) +* [License](#license) + +## What is this? + +This package is a hyperscript interface (like `createElement` from React and +`h` from Vue and such) to help with creating hast trees. + +## When should I use this? + +You can use this utility in your project when you generate hast syntax trees +with code. +It helps because it replaces most of the repetition otherwise needed in a syntax +tree with function calls. +It also helps as it improves the attributes you pass by turning them into the +form that is required by hast. + +You can instead use [`unist-builder`][u] when creating any unist nodes and +[`xastscript`][x] when creating xast (XML) nodes. ## Install -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. - -[npm][]: +This package is [ESM only][esm]. +In Node.js (version 12.20+, 14.14+, or 16.0+), install with [npm][]: ```sh npm install hastscript ``` +In Deno with [`esm.sh`][esmsh]: + +```js +import {h} from 'https://esm.sh/hastscript@7' +``` + +In browsers with [`esm.sh`][esmsh]: + +```html + +``` + ## Use ```js import {h, s} from 'hastscript' -// Children as an array: console.log( h('.foo#some-id', [ h('span', 'some text'), @@ -44,18 +83,6 @@ console.log( ]) ) -// Children as arguments: -console.log( - h( - 'form', - {method: 'POST'}, - h('input', {type: 'text', name: 'foo'}), - h('input', {type: 'text', name: 'bar'}), - h('input', {type: 'submit', value: 'send'}) - ) -) - -// SVG: console.log( s('svg', {xmlns: 'http://www.w3.org/2000/svg', viewbox: '0 0 500 500'}, [ s('title', 'SVG `` element'), @@ -92,31 +119,6 @@ Yields: } ] } -{ - type: 'element', - tagName: 'form', - properties: {method: 'POST'}, - children: [ - { - type: 'element', - tagName: 'input', - properties: {type: 'text', name: 'foo'}, - children: [] - }, - { - type: 'element', - tagName: 'input', - properties: {type: 'text', name: 'bar'}, - children: [] - }, - { - type: 'element', - tagName: 'input', - properties: {type: 'submit', value: 'send'}, - children: [] - } - ] -} { type: 'element', tagName: 'svg', @@ -140,9 +142,13 @@ Yields: ## API -This package exports the following identifiers: `h` and `s`. +This package exports the identifiers `h` and `s`. There is no default export. +The export map supports the automatic JSX runtime. +You can pass `hastscript/html` (or `hastscript`) or `hastscript/svg` to your +build tool (TypeScript, Babel, SWC) as with an `importSource` option or similar. + ### `h(selector?[, properties][, …children])` ### `s(selector?[, properties][, …children])` @@ -153,7 +159,7 @@ Create virtual [**hast**][hast] [*trees*][tree] for HTML or SVG. * `h(): root` * `h(null[, …children]): root` -* `h(name[, properties][, …children]): element` +* `h(selector[, properties][, …children]): element` (and the same for `s`). @@ -190,11 +196,9 @@ If [`Root`][root] nodes are given, their children are used instead. ## JSX `hastscript` can be used with JSX. -Either use the automatic runtime set to `hastscript/html`, `hastscript/svg`, -or `hastscript` (shortcut for HTML). - -Or import `h` or `s` yourself and define it as the pragma (plus set the fragment -to `null`). +Either use the automatic runtime set to `hastscript/html` (or `hastscript`) or +`hastscript/svg` or import `h` or `s` yourself and define it as the pragma (plus +set the fragment to `null`). The example above can then be written like so, using inline pragmas, so that SVG can be used too: @@ -203,7 +207,6 @@ that SVG can be used too: ```jsx /** @jsxImportSource hastscript */ - console.log(
some text @@ -213,14 +216,6 @@ console.log(
) - -console.log( -
- - - -
-) ``` `example-svg.jsx`: @@ -235,20 +230,16 @@ console.log( ) ``` -Because JSX does not allow dots (`.`) or number signs (`#`) in tag names, you -have to pass class names and IDs in as attributes. +> 👉 **Note**: while `h` supports dots (`.`) for classes or number signs (`#`) +> for IDs in `selector`, those are not supported in JSX. You can use [`estree-util-build-jsx`][build-jsx] to compile JSX away. -You could also use [bublé][], but it’s not ideal (`jsxFragment` is currently -only available on the API, not the CLI, and it only allows a single pragma). - For [Babel][], use [`@babel/plugin-transform-react-jsx`][babel-jsx] and either pass `pragma: 'h'` and `pragmaFrag: 'null'`, or pass `importSource: 'hastscript'`. -This is less ideal because it allows a single pragma. - -Babel also lets you configure this in a script: +This is not perfect as it allows only a single pragma. +Alternatively, Babel also lets you configure this with a comment: ```jsx /** @jsx s @jsxFrag null */ @@ -257,9 +248,21 @@ import {s} from 'hastscript' console.log() ``` -This is useful because it allows using *both* `html` and `svg`, although in +This is useful because it allows using *both* `html` and `svg` when used in different files. +## Types + +This package is fully typed with [TypeScript][]. +It exports the additional types `Child` and `Properties`. + +## 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+, and 16.0+. +Our projects sometimes work with older versions, but this is not guaranteed. + ## Security Use of `hastscript` can open you up to a [cross-site scripting (XSS)][xss] @@ -268,7 +271,7 @@ The following example shows how a script is injected that runs when loaded in a browser. ```js -const tree = {type: 'root', children: []} +const tree = h() tree.children.push(h('script', 'alert(1)')) ``` @@ -283,7 +286,7 @@ The following example shows how an image is injected that fails loading and therefore runs code in a browser. ```js -const tree = {type: 'root', children: []} +const tree = h() // Somehow someone injected these properties instead of an expected `src` and // `alt`: @@ -302,7 +305,7 @@ The following example shows how code can run in a browser because someone stored an object in a database instead of the expected string. ```js -const tree = {type: 'root', children: []} +const tree = h() // Somehow this isn’t the expected `'wooorm'`. const username = { @@ -321,28 +324,28 @@ Yields: ``` Either do not use user input in `hastscript` or use -[`hast-util-santize`][sanitize]. +[`hast-util-santize`][hast-util-sanitize]. ## Related * [`unist-builder`](https://github.com/syntax-tree/unist-builder) - — Create any unist tree + — create unist trees * [`xastscript`](https://github.com/syntax-tree/xastscript) - — Create a xast tree + — create xast trees * [`hast-to-hyperscript`](https://github.com/syntax-tree/hast-to-hyperscript) - — Convert a Node to React, Virtual DOM, Hyperscript, and more + — turn hast into React, Preact, Vue, etc * [`hast-util-from-dom`](https://github.com/syntax-tree/hast-util-from-dom) - — Transform a DOM tree to hast + — turn DOM trees into hast * [`hast-util-select`](https://github.com/syntax-tree/hast-util-select) — `querySelector`, `querySelectorAll`, and `matches` * [`hast-util-to-html`](https://github.com/syntax-tree/hast-util-to-html) - — Stringify nodes to HTML + — turn hast into HTML * [`hast-util-to-dom`](https://github.com/syntax-tree/hast-util-to-dom) - — Transform to a DOM tree + — turn hast into DOM trees ## Contribute -See [`contributing.md` in `syntax-tree/.github`][contributing] for ways to get +See [`contributing.md`][contributing] in [`syntax-tree/.github`][health] for started. See [`support.md`][support] for ways to get help. @@ -384,25 +387,23 @@ abide by its terms. [npm]: https://docs.npmjs.com/cli/install -[license]: license +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c -[author]: https://wooorm.com - -[contributing]: https://github.com/syntax-tree/.github/blob/HEAD/contributing.md +[esmsh]: https://esm.sh -[support]: https://github.com/syntax-tree/.github/blob/HEAD/support.md +[typescript]: https://www.typescriptlang.org -[coc]: https://github.com/syntax-tree/.github/blob/HEAD/code-of-conduct.md +[license]: license -[hyperscript]: https://github.com/dominictarr/hyperscript +[author]: https://wooorm.com -[virtual-hyperscript]: https://github.com/Matt-Esch/virtual-dom/tree/HEAD/virtual-hyperscript +[health]: https://github.com/syntax-tree/.github -[react]: https://reactjs.org/docs/glossary.html#react-elements +[contributing]: https://github.com/syntax-tree/.github/blob/main/contributing.md -[vue]: https://vuejs.org/v2/guide/render-function.html#createElement-Arguments +[support]: https://github.com/syntax-tree/.github/blob/main/support.md -[unist]: https://github.com/syntax-tree/unist +[coc]: https://github.com/syntax-tree/.github/blob/main/code-of-conduct.md [tree]: https://github.com/syntax-tree/unist#tree @@ -416,9 +417,9 @@ abide by its terms. [u]: https://github.com/syntax-tree/unist-builder -[build-jsx]: https://github.com/wooorm/estree-util-build-jsx +[x]: https://github.com/syntax-tree/xastscript -[bublé]: https://github.com/Rich-Harris/buble +[build-jsx]: https://github.com/wooorm/estree-util-build-jsx [babel]: https://github.com/babel/babel @@ -428,4 +429,4 @@ abide by its terms. [xss]: https://en.wikipedia.org/wiki/Cross-site_scripting -[sanitize]: https://github.com/syntax-tree/hast-util-sanitize +[hast-util-sanitize]: https://github.com/syntax-tree/hast-util-sanitize diff --git a/svg.js b/svg.js index 0576ff1..e35ceea 100644 --- a/svg.js +++ b/svg.js @@ -1,6 +1,6 @@ /** - * @typedef {import('./lib/index.js').Child} Child Acceptable child value - * @typedef {import('./lib/index.js').Properties} Properties Acceptable properties value. + * @typedef {import('./lib/index.js').Child} Child + * @typedef {import('./lib/index.js').Properties} Properties */ export {s} from './lib/svg.js' From 8ab2c5f5702235495f09e1a7b5a23e8ac32931b8 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 10 Oct 2022 20:12:21 +0200 Subject: [PATCH 05/56] Update dev-dependencies --- .github/workflows/main.yml | 2 +- lib/jsx-automatic.d.ts | 5 +++-- lib/jsx-classic.d.ts | 5 +++-- package.json | 6 +++--- test-d/automatic-h.tsx | 2 +- test-d/automatic-s.tsx | 2 +- test-d/classic-h.tsx | 2 +- test-d/classic-s.tsx | 2 +- test-d/files.ts | 2 +- test-d/index.ts | 2 +- 10 files changed, 16 insertions(+), 14 deletions(-) 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/lib/jsx-automatic.d.ts b/lib/jsx-automatic.d.ts index cf7a71c..4b8d37d 100644 --- a/lib/jsx-automatic.d.ts +++ b/lib/jsx-automatic.d.ts @@ -1,4 +1,4 @@ -import {HProperties, HChild, HResult} from './core.js' +import type {HProperties, HChild, HResult} from './core.js' export namespace JSX { /** @@ -18,7 +18,7 @@ export namespace JSX { * * This **must** be an interface. */ - // eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style + // eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style, @typescript-eslint/consistent-type-definitions interface IntrinsicElements { [name: string]: | HProperties @@ -33,6 +33,7 @@ export namespace JSX { /** * The key of this interface defines as what prop children are passed. */ + // eslint-disable-next-line @typescript-eslint/consistent-type-definitions interface ElementChildrenAttribute { /** * Only the key matters, not the value. diff --git a/lib/jsx-classic.d.ts b/lib/jsx-classic.d.ts index 1b720d7..69ca32f 100644 --- a/lib/jsx-classic.d.ts +++ b/lib/jsx-classic.d.ts @@ -1,4 +1,4 @@ -import {HProperties, HChild, HResult} from './core.js' +import type {HProperties, HChild, HResult} from './core.js' /** * This unique symbol is declared to specify the key on which JSX children are passed, without conflicting @@ -23,7 +23,7 @@ export type IntrinsicAttributes = never * * This **must** be an interface. */ -// eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style +// eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style, @typescript-eslint/consistent-type-definitions export interface IntrinsicElements { [name: string]: | HProperties @@ -38,6 +38,7 @@ export interface IntrinsicElements { /** * The key of this interface defines as what prop children are passed. */ +// eslint-disable-next-line @typescript-eslint/consistent-type-definitions export interface ElementChildrenAttribute { /** * Only the key matters, not the value. diff --git a/package.json b/package.json index 100864c..f6aca2c 100644 --- a/package.json +++ b/package.json @@ -72,16 +72,16 @@ "c8": "^7.0.0", "estree-util-build-jsx": "^2.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", "svg-tag-names": "^3.0.0", "tape": "^5.0.0", - "tsd": "^0.19.0", + "tsd": "^0.24.0", "type-coverage": "^2.0.0", "typescript": "^4.0.0", "unist-builder": "^3.0.0", - "xo": "^0.48.0" + "xo": "^0.52.0" }, "scripts": { "prepack": "npm run build && npm run format", diff --git a/test-d/automatic-h.tsx b/test-d/automatic-h.tsx index a45c8df..223799a 100644 --- a/test-d/automatic-h.tsx +++ b/test-d/automatic-h.tsx @@ -2,7 +2,7 @@ /* @jsxImportSource .. */ import {expectType, expectError} from 'tsd' -import {Root, Element} from 'hast' +import type {Root, Element} from 'hast' import {h} from '../index.js' import {Fragment, jsx, jsxs} from '../jsx-runtime.js' diff --git a/test-d/automatic-s.tsx b/test-d/automatic-s.tsx index a7ccf46..41b8af2 100644 --- a/test-d/automatic-s.tsx +++ b/test-d/automatic-s.tsx @@ -2,7 +2,7 @@ /* @jsxImportSource ../svg */ import {expectType, expectError} from 'tsd' -import {Root, Element} from 'hast' +import type {Root, Element} from 'hast' import {s} from '../index.js' type Result = Element | Root diff --git a/test-d/classic-h.tsx b/test-d/classic-h.tsx index 6b143c0..819f0f2 100644 --- a/test-d/classic-h.tsx +++ b/test-d/classic-h.tsx @@ -1,7 +1,7 @@ /* @jsx h */ /* @jsxFrag null */ import {expectType, expectError} from 'tsd' -import {Root, Element} from 'hast' +import type {Root, Element} from 'hast' import {h} from '../index.js' type Result = Element | Root diff --git a/test-d/classic-s.tsx b/test-d/classic-s.tsx index 9431f7b..48efedd 100644 --- a/test-d/classic-s.tsx +++ b/test-d/classic-s.tsx @@ -1,7 +1,7 @@ /* @jsx s */ /* @jsxFrag null */ import {expectType, expectError} from 'tsd' -import {Root, Element} from 'hast' +import type {Root, Element} from 'hast' import {s} from '../index.js' type Result = Element | Root diff --git a/test-d/files.ts b/test-d/files.ts index 5d6c415..5340b5e 100644 --- a/test-d/files.ts +++ b/test-d/files.ts @@ -1,5 +1,5 @@ import {expectType} from 'tsd' -import {Root} from 'hast' +import type {Root} from 'hast' import {h as hFromRoot} from '../html.js' import {s as sFromRoot} from '../svg.js' import {h as hFromIndex, s as sFromIndex} from '../index.js' diff --git a/test-d/index.ts b/test-d/index.ts index c55abf7..3ef145e 100644 --- a/test-d/index.ts +++ b/test-d/index.ts @@ -1,5 +1,5 @@ import {expectType, expectError} from 'tsd' -import {Root, Element} from 'hast' +import type {Root, Element} from 'hast' import {h, s} from '../index.js' import {h as hFromRoot} from '../html.js' import {s as sFromRoot} from '../svg.js' From 464b23dad457acc7f2c7817fce950b9cb5367b3b Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 10 Oct 2022 20:15:31 +0200 Subject: [PATCH 06/56] Add export of `Result` type Closes GH-18. --- index.js | 1 + lib/index.js | 1 + readme.md | 6 +++++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index c2181f7..91f126c 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ /** * @typedef {import('./lib/index.js').Child} Child * @typedef {import('./lib/index.js').Properties} Properties + * @typedef {import('./lib/index.js').Result} Result */ export {h, s} from './lib/index.js' diff --git a/lib/index.js b/lib/index.js index 681441b..2b0c85b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,6 +1,7 @@ /** * @typedef {import('./core.js').HChild} Child * @typedef {import('./core.js').HProperties} Properties + * @typedef {import('./core.js').HResult} Result */ export {h} from './html.js' diff --git a/readme.md b/readme.md index d31b8de..1184c2a 100644 --- a/readme.md +++ b/readme.md @@ -254,7 +254,11 @@ different files. ## Types This package is fully typed with [TypeScript][]. -It exports the additional types `Child` and `Properties`. +It exports the additional types: + +* `Child` — valid value used as a child +* `Properties` — valid properties passed to an element +* `Result` — output of a `h` (or `s`) call ## Compatibility From 092557f4703bcf1b51412fcecfa7c9cd98bd28dd Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 10 Oct 2022 20:15:58 +0200 Subject: [PATCH 07/56] 7.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f6aca2c..f7f5a80 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hastscript", - "version": "7.0.2", + "version": "7.1.0", "description": "hast utility to create trees", "license": "MIT", "keywords": [ From 562b85a4bc88baa6f0645d57b6218abcfc328a1e Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 4 Jan 2023 13:20:59 +0100 Subject: [PATCH 08/56] Update dev-dependencies --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f7f5a80..34411ad 100644 --- a/package.json +++ b/package.json @@ -77,11 +77,11 @@ "rimraf": "^3.0.0", "svg-tag-names": "^3.0.0", "tape": "^5.0.0", - "tsd": "^0.24.0", + "tsd": "^0.25.0", "type-coverage": "^2.0.0", "typescript": "^4.0.0", "unist-builder": "^3.0.0", - "xo": "^0.52.0" + "xo": "^0.53.0" }, "scripts": { "prepack": "npm run build && npm run format", From a41c8b8de966d8723c405b469a5e669b508ea1ed Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 4 Jan 2023 13:21:05 +0100 Subject: [PATCH 09/56] Remove classic Babel test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Babel no longer supports `null` as a pragma as a keyword, instead it forces that as a string (`'null'`), which we can’t handle. --- script/generate-jsx.js | 10 ---------- test/index.js | 1 - 2 files changed, 11 deletions(-) diff --git a/script/generate-jsx.js b/script/generate-jsx.js index 3aa56a4..0eddbe2 100644 --- a/script/generate-jsx.js +++ b/script/generate-jsx.js @@ -36,16 +36,6 @@ fs.writeFileSync( ).replace(/\/jsx-runtime(?=["'])/g, './lib/runtime-html.js') ) -fs.writeFileSync( - path.join('test', 'jsx-babel-classic.js'), - // @ts-expect-error Result always given. - babel.transform(doc.replace(/'name'/, "'jsx (babel, classic)'"), { - plugins: [ - ['@babel/plugin-transform-react-jsx', {pragma: 'h', pragmaFrag: 'null'}] - ] - }).code -) - fs.writeFileSync( path.join('test', 'jsx-babel-automatic.js'), // @ts-expect-error Result always given. diff --git a/test/index.js b/test/index.js index b6b160d..11265e1 100644 --- a/test/index.js +++ b/test/index.js @@ -1,6 +1,5 @@ /* eslint-disable import/no-unassigned-import */ import './core.js' -import './jsx-babel-classic.js' import './jsx-babel-automatic.js' import './jsx-build-jsx-classic.js' import './jsx-build-jsx-automatic.js' From 848380b83ed0bfd59ce2c9bac09e510d83eef3fd Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 4 Jan 2023 13:22:25 +0100 Subject: [PATCH 10/56] Update Actions --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 69924a4..89dc06c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,13 +7,13 @@ 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: From 53361dd16555586f184c22a7aae272e938cb0fd6 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 4 Jan 2023 16:24:35 +0100 Subject: [PATCH 11/56] Update `tsconfig.json` --- lib/html.js | 8 ++++---- lib/svg.js | 8 ++++---- package.json | 5 ++--- tsconfig.json | 37 +++++++++++++------------------------ 4 files changed, 23 insertions(+), 35 deletions(-) diff --git a/lib/html.js b/lib/html.js index 162f5cb..ecabb6c 100644 --- a/lib/html.js +++ b/lib/html.js @@ -2,10 +2,10 @@ * @typedef {import('./core.js').HChild} Child Acceptable child value * @typedef {import('./core.js').HProperties} Properties Acceptable properties value. * - * @typedef {import('./jsx-classic').Element} h.JSX.Element - * @typedef {import('./jsx-classic').IntrinsicAttributes} h.JSX.IntrinsicAttributes - * @typedef {import('./jsx-classic').IntrinsicElements} h.JSX.IntrinsicElements - * @typedef {import('./jsx-classic').ElementChildrenAttribute} h.JSX.ElementChildrenAttribute + * @typedef {import('./jsx-classic.js').Element} h.JSX.Element + * @typedef {import('./jsx-classic.js').IntrinsicAttributes} h.JSX.IntrinsicAttributes + * @typedef {import('./jsx-classic.js').IntrinsicElements} h.JSX.IntrinsicElements + * @typedef {import('./jsx-classic.js').ElementChildrenAttribute} h.JSX.ElementChildrenAttribute */ import {html} from 'property-information' diff --git a/lib/svg.js b/lib/svg.js index 0d89ff6..da015d9 100644 --- a/lib/svg.js +++ b/lib/svg.js @@ -2,10 +2,10 @@ * @typedef {import('./core.js').HChild} Child * @typedef {import('./core.js').HProperties} Properties * - * @typedef {import('./jsx-classic').Element} s.JSX.Element - * @typedef {import('./jsx-classic').IntrinsicAttributes} s.JSX.IntrinsicAttributes - * @typedef {import('./jsx-classic').IntrinsicElements} s.JSX.IntrinsicElements - * @typedef {import('./jsx-classic').ElementChildrenAttribute} s.JSX.ElementChildrenAttribute + * @typedef {import('./jsx-classic.js').Element} s.JSX.Element + * @typedef {import('./jsx-classic.js').IntrinsicAttributes} s.JSX.IntrinsicAttributes + * @typedef {import('./jsx-classic.js').IntrinsicElements} s.JSX.IntrinsicElements + * @typedef {import('./jsx-classic.js').ElementChildrenAttribute} s.JSX.ElementChildrenAttribute */ import {svg} from 'property-information' diff --git a/package.json b/package.json index 34411ad..17bcd51 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,6 @@ "prettier": "^2.0.0", "remark-cli": "^11.0.0", "remark-preset-wooorm": "^9.0.0", - "rimraf": "^3.0.0", "svg-tag-names": "^3.0.0", "tape": "^5.0.0", "tsd": "^0.25.0", @@ -85,8 +84,8 @@ }, "scripts": { "prepack": "npm run build && npm run format", - "build": "rimraf \"{script/**,test/**,}*.d.ts\" \"lib/{core,html,index,runtime-html,runtime-svg,runtime,svg-case-sensitive-tag-names,svg}.d.ts\" && tsc && tsd && type-coverage", - "generate": "node script/generate-jsx && node script/build", + "build": "tsc --build --clean && tsc --build && type-coverage", + "generate": "node script/generate-jsx.js && node script/build.js", "format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", "test-api": "node --conditions development test/index.js", "test-coverage": "c8 --check-coverage --100 --reporter lcov npm run test-api", diff --git a/tsconfig.json b/tsconfig.json index 4479a13..1f6a154 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,33 +1,22 @@ { - "include": [ - "*.js", - "html/**/*.js", - "script/**/*.js", - "svg/**/*.js", - "test/**/*.js", - "lib/core.js", - "lib/html.js", - "lib/index.js", - "lib/runtime-html.js", - "lib/runtime-svg.js", - "lib/runtime.js", - "lib/svg-case-sensitive-tag-names.js", - "lib/svg.js", - "lib/jsx-automatic.d.ts", - "lib/jsx-classic.d.ts" + "include": ["**/**.js"], + "exclude": [ + "coverage/", + "node_modules/", + "lib/jsx-automatic.js", + "lib/jsx-classic.js" ], "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, - "strictNullChecks": true, - "strict": true + "strict": true, + "target": "es2020" } } From a0eaf6bf1f5b30967735c9a263e9c579eaa702c6 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 4 Jan 2023 16:24:45 +0100 Subject: [PATCH 12/56] Remove superfluous dev-dependencies --- package.json | 8 ++----- script/generate-jsx.js | 52 ++++++++++++++---------------------------- test/index.js | 1 - 3 files changed, 19 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index 17bcd51..98db083 100644 --- a/package.json +++ b/package.json @@ -61,16 +61,12 @@ "space-separated-tokens": "^2.0.0" }, "devDependencies": { - "@babel/core": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.0.0", - "@babel/plugin-transform-react-jsx": "^7.0.0", - "@types/babel__core": "^7.0.0", "@types/tape": "^4.0.0", - "acorn": "^8.0.0", "acorn-jsx": "^5.0.0", - "astring": "^1.0.0", "c8": "^7.0.0", + "esast-util-from-js": "^1.0.0", "estree-util-build-jsx": "^2.0.0", + "estree-util-to-js": "^1.0.0", "prettier": "^2.0.0", "remark-cli": "^11.0.0", "remark-preset-wooorm": "^9.0.0", diff --git a/script/generate-jsx.js b/script/generate-jsx.js index 0eddbe2..fbbcdbd 100644 --- a/script/generate-jsx.js +++ b/script/generate-jsx.js @@ -1,52 +1,34 @@ -import fs from 'node:fs' +import fs from 'node:fs/promises' import path from 'node:path' -import babel from '@babel/core' -import {Parser} from 'acorn' import acornJsx from 'acorn-jsx' -import {generate} from 'astring' +import {fromJs} from 'esast-util-from-js' +import {toJs} from 'estree-util-to-js' import {buildJsx} from 'estree-util-build-jsx' -const doc = String(fs.readFileSync(path.join('test', 'jsx.jsx'))) +const doc = String(await fs.readFile(path.join('test', 'jsx.jsx'))) -fs.writeFileSync( +await fs.writeFile( path.join('test', 'jsx-build-jsx-classic.js'), - generate( + toJs( buildJsx( - // @ts-expect-error Acorn nodes are assignable to ESTree nodes. - Parser.extend(acornJsx()).parse( - doc.replace(/'name'/, "'jsx (estree-util-build-jsx, classic)'"), - {sourceType: 'module', ecmaVersion: 2021} - ), + fromJs(doc.replace(/'name'/, "'jsx (estree-util-build-jsx, classic)'"), { + plugins: [acornJsx()], + module: true + }), {pragma: 'h', pragmaFrag: 'null'} ) - ) + ).value ) -fs.writeFileSync( +await fs.writeFile( path.join('test', 'jsx-build-jsx-automatic.js'), - generate( + toJs( buildJsx( - // @ts-expect-error Acorn nodes are assignable to ESTree nodes. - Parser.extend(acornJsx()).parse( + fromJs( doc.replace(/'name'/, "'jsx (estree-util-build-jsx, automatic)'"), - {sourceType: 'module', ecmaVersion: 2021} + {plugins: [acornJsx()], module: true} ), - {runtime: 'automatic', importSource: '.'} + {runtime: 'automatic', importSource: 'hastscript'} ) - ).replace(/\/jsx-runtime(?=["'])/g, './lib/runtime-html.js') -) - -fs.writeFileSync( - path.join('test', 'jsx-babel-automatic.js'), - // @ts-expect-error Result always given. - babel - .transformSync(doc.replace(/'name'/, "'jsx (babel, automatic)'"), { - plugins: [ - [ - '@babel/plugin-transform-react-jsx', - {runtime: 'automatic', importSource: '.'} - ] - ] - }) - .code.replace(/\/jsx-runtime(?=["'])/g, './lib/runtime-html.js') + ).value ) diff --git a/test/index.js b/test/index.js index 11265e1..372ef82 100644 --- a/test/index.js +++ b/test/index.js @@ -1,6 +1,5 @@ /* eslint-disable import/no-unassigned-import */ import './core.js' -import './jsx-babel-automatic.js' import './jsx-build-jsx-classic.js' import './jsx-build-jsx-automatic.js' /* eslint-enable import/no-unassigned-import */ From 3bf44c7460174aab8ea58885b9c6fb6d25e94087 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 4 Jan 2023 16:28:35 +0100 Subject: [PATCH 13/56] Refactor code-style --- script/build.js | 7 +++---- script/generate-jsx.js | 10 ++++++---- test-d/automatic-h.tsx | 1 + test-d/automatic-s.tsx | 1 + 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/script/build.js b/script/build.js index 9dd2f12..d83433f 100644 --- a/script/build.js +++ b/script/build.js @@ -1,11 +1,10 @@ -import fs from 'node:fs' -import path from 'node:path' +import fs from 'node:fs/promises' import {svgTagNames} from 'svg-tag-names' const casing = svgTagNames.filter((d) => d !== d.toLowerCase()) -fs.writeFileSync( - path.join('lib', 'svg-case-sensitive-tag-names.js'), +await fs.writeFile( + new URL('../lib/svg-case-sensitive-tag-names.js', import.meta.url), 'export const svgCaseSensitiveTagNames = ' + JSON.stringify(casing, null, 2) + '\n' diff --git a/script/generate-jsx.js b/script/generate-jsx.js index fbbcdbd..ec48f25 100644 --- a/script/generate-jsx.js +++ b/script/generate-jsx.js @@ -1,14 +1,15 @@ import fs from 'node:fs/promises' -import path from 'node:path' import acornJsx from 'acorn-jsx' import {fromJs} from 'esast-util-from-js' import {toJs} from 'estree-util-to-js' import {buildJsx} from 'estree-util-build-jsx' -const doc = String(await fs.readFile(path.join('test', 'jsx.jsx'))) +const doc = String( + await fs.readFile(new URL('../test/jsx.jsx', import.meta.url)) +) await fs.writeFile( - path.join('test', 'jsx-build-jsx-classic.js'), + new URL('../test/jsx-build-jsx-classic.js', import.meta.url), toJs( buildJsx( fromJs(doc.replace(/'name'/, "'jsx (estree-util-build-jsx, classic)'"), { @@ -21,7 +22,8 @@ await fs.writeFile( ) await fs.writeFile( - path.join('test', 'jsx-build-jsx-automatic.js'), + new URL('../test/jsx-build-jsx-automatic.js', import.meta.url), + toJs( buildJsx( fromJs( diff --git a/test-d/automatic-h.tsx b/test-d/automatic-h.tsx index 223799a..bc6559b 100644 --- a/test-d/automatic-h.tsx +++ b/test-d/automatic-h.tsx @@ -49,6 +49,7 @@ expectError() // This is where the automatic runtime differs from the classic runtime. // The automatic runtime the children prop to define JSX children, whereas it’s used as an attribute in the classic runtime. +// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment expectType(} />) declare function Bar(props?: Record): Element diff --git a/test-d/automatic-s.tsx b/test-d/automatic-s.tsx index 41b8af2..9b929f5 100644 --- a/test-d/automatic-s.tsx +++ b/test-d/automatic-s.tsx @@ -39,6 +39,7 @@ expectError() // This is where the automatic runtime differs from the classic runtime. // The automatic runtime the children prop to define JSX children, whereas it’s used as an attribute in the classic runtime. +// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment expectType(} />) declare function Bar(props?: Record): Element From afd5841eb08ce56fcf4f64276eac19ca1b850b3e Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 4 Jan 2023 16:36:26 +0100 Subject: [PATCH 14/56] Use Node test runner --- .github/workflows/main.yml | 2 +- package.json | 3 +- test/core.js | 255 ++++++++++++++++--------------------- test/jsx.jsx | 51 +++++--- tsconfig.json | 5 +- 5 files changed, 146 insertions(+), 170 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 98db083..3d7b3a6 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "space-separated-tokens": "^2.0.0" }, "devDependencies": { - "@types/tape": "^4.0.0", + "@types/node": "^18.0.0", "acorn-jsx": "^5.0.0", "c8": "^7.0.0", "esast-util-from-js": "^1.0.0", @@ -71,7 +71,6 @@ "remark-cli": "^11.0.0", "remark-preset-wooorm": "^9.0.0", "svg-tag-names": "^3.0.0", - "tape": "^5.0.0", "tsd": "^0.25.0", "type-coverage": "^2.0.0", "typescript": "^4.0.0", diff --git a/test/core.js b/test/core.js index c0247ec..4c6cca2 100644 --- a/test/core.js +++ b/test/core.js @@ -1,22 +1,23 @@ -import test from 'tape' +import assert from 'node:assert/strict' +import test from 'node:test' import {h, s} from '../index.js' import {h as hFromRoot} from '../html.js' import {s as sFromRoot} from '../svg.js' -test('hastscript', (t) => { - t.equal(h, hFromRoot, '`h` should be exposed from `/html.js`') - t.equal(s, sFromRoot, '`s` should be exposed from `/svg.js`') +test('hastscript', async (t) => { + assert.equal(h, hFromRoot, '`h` should be exposed from `/html.js`') + assert.equal(s, sFromRoot, '`s` should be exposed from `/svg.js`') - t.equal(typeof h, 'function', 'should expose a function') + assert.equal(typeof h, 'function', 'should expose a function') - t.test('selector', (t) => { - t.deepEqual( + await t.test('selector', () => { + assert.deepEqual( h(), {type: 'root', children: []}, 'should create a `root` node without arguments' ) - t.deepEqual( + assert.deepEqual( h(''), { type: 'element', @@ -27,7 +28,7 @@ test('hastscript', (t) => { 'should create a `div` element w/ an empty string name' ) - t.deepEqual( + assert.deepEqual( h('.bar', {class: 'baz'}), { type: 'element', @@ -38,7 +39,7 @@ test('hastscript', (t) => { 'should append to the selector’s classes' ) - t.deepEqual( + assert.deepEqual( h('#id'), { type: 'element', @@ -49,7 +50,7 @@ test('hastscript', (t) => { 'should create a `div` element when given an id selector' ) - t.deepEqual( + assert.deepEqual( h('#a#b'), { type: 'element', @@ -60,7 +61,7 @@ test('hastscript', (t) => { 'should create an element with the last ID when given multiple in a selector' ) - t.deepEqual( + assert.deepEqual( h('.foo'), { type: 'element', @@ -71,7 +72,7 @@ test('hastscript', (t) => { 'should create a `div` element when given a class selector' ) - t.deepEqual( + assert.deepEqual( h('foo'), { type: 'element', @@ -82,7 +83,7 @@ test('hastscript', (t) => { 'should create a `foo` element when given a tag selector' ) - t.deepEqual( + assert.deepEqual( h('foo#bar'), { type: 'element', @@ -93,7 +94,7 @@ test('hastscript', (t) => { 'should create a `foo` element with an ID when given a both as a selector' ) - t.deepEqual( + assert.deepEqual( h('foo.bar'), { type: 'element', @@ -104,7 +105,7 @@ test('hastscript', (t) => { 'should create a `foo` element with a class when given a both as a selector' ) - t.deepEqual( + assert.deepEqual( h('.foo.bar'), { type: 'element', @@ -114,13 +115,11 @@ test('hastscript', (t) => { }, 'should support multiple classes' ) - - t.end() }) - t.test('properties', (t) => { - t.test('known property names', (t) => { - t.deepEqual( + await t.test('properties', async (t) => { + await t.test('known property names', () => { + assert.deepEqual( h('', {className: 'foo'}), { type: 'element', @@ -131,7 +130,7 @@ test('hastscript', (t) => { 'should support correctly cased property names' ) - t.deepEqual( + assert.deepEqual( h('', {class: 'foo'}), { type: 'element', @@ -142,7 +141,7 @@ test('hastscript', (t) => { 'should map attributes to property names' ) - t.deepEqual( + assert.deepEqual( h('', {CLASS: 'foo'}), { type: 'element', @@ -153,7 +152,7 @@ test('hastscript', (t) => { 'should map attribute-like values to property names' ) - t.deepEqual( + assert.deepEqual( h('', {'class-name': 'foo'}), { type: 'element', @@ -163,12 +162,10 @@ test('hastscript', (t) => { }, 'should *not* map property-like values to property names' ) - - t.end() }) - t.test('unknown property names', (t) => { - t.deepEqual( + await t.test('unknown property names', () => { + assert.deepEqual( h('', {allowbigscreen: true}), { type: 'element', @@ -179,7 +176,7 @@ test('hastscript', (t) => { 'should keep lower-cased unknown names' ) - t.deepEqual( + assert.deepEqual( h('', {allowBigScreen: true}), { type: 'element', @@ -190,7 +187,7 @@ test('hastscript', (t) => { 'should keep camel-cased unknown names' ) - t.deepEqual( + assert.deepEqual( h('', {'allow_big-screen': true}), { type: 'element', @@ -200,12 +197,10 @@ test('hastscript', (t) => { }, 'should keep weirdly cased unknown names' ) - - t.end() }) - t.test('other namespaces', (t) => { - t.deepEqual( + await t.test('other namespaces', () => { + assert.deepEqual( h('', {'aria-valuenow': 1}), { type: 'element', @@ -216,7 +211,7 @@ test('hastscript', (t) => { 'should support aria attribute names' ) - t.deepEqual( + assert.deepEqual( h('', {ariaValueNow: 1}), { type: 'element', @@ -227,7 +222,7 @@ test('hastscript', (t) => { 'should support aria property names' ) - t.deepEqual( + assert.deepEqual( s('', {'color-interpolation-filters': 'sRGB'}), { type: 'element', @@ -238,7 +233,7 @@ test('hastscript', (t) => { 'should support svg attribute names' ) - t.deepEqual( + assert.deepEqual( s('', {colorInterpolationFilters: 'sRGB'}), { type: 'element', @@ -249,7 +244,7 @@ test('hastscript', (t) => { 'should support svg property names' ) - t.deepEqual( + assert.deepEqual( s('', {'xml:space': 'preserve'}), { type: 'element', @@ -260,7 +255,7 @@ test('hastscript', (t) => { 'should support xml attribute names' ) - t.deepEqual( + assert.deepEqual( s('', {xmlSpace: 'preserve'}), { type: 'element', @@ -271,7 +266,7 @@ test('hastscript', (t) => { 'should support xml property names' ) - t.deepEqual( + assert.deepEqual( s('', {'xmlns:xlink': 'http://www.w3.org/1999/xlink'}), { type: 'element', @@ -282,7 +277,7 @@ test('hastscript', (t) => { 'should support xmlns attribute names' ) - t.deepEqual( + assert.deepEqual( s('', {xmlnsXLink: 'http://www.w3.org/1999/xlink'}), { type: 'element', @@ -293,7 +288,7 @@ test('hastscript', (t) => { 'should support xmlns property names' ) - t.deepEqual( + assert.deepEqual( s('', {'xlink:arcrole': 'http://www.example.com'}), { type: 'element', @@ -304,7 +299,7 @@ test('hastscript', (t) => { 'should support xlink attribute names' ) - t.deepEqual( + assert.deepEqual( s('', {xLinkArcRole: 'http://www.example.com'}), { type: 'element', @@ -314,12 +309,10 @@ test('hastscript', (t) => { }, 'should support xlink property names' ) - - t.end() }) - t.test('data property names', (t) => { - t.deepEqual( + await t.test('data property names', () => { + assert.deepEqual( h('', {'data-foo': true}), { type: 'element', @@ -330,7 +323,7 @@ test('hastscript', (t) => { 'should support data attribute names' ) - t.deepEqual( + assert.deepEqual( h('', {'data-123': true}), { type: 'element', @@ -341,7 +334,7 @@ test('hastscript', (t) => { 'should support numeric-first data attribute names' ) - t.deepEqual( + assert.deepEqual( h('', {dataFooBar: true}), { type: 'element', @@ -352,7 +345,7 @@ test('hastscript', (t) => { 'should support data property names' ) - t.deepEqual( + assert.deepEqual( h('', {data123: true}), { type: 'element', @@ -363,7 +356,7 @@ test('hastscript', (t) => { 'should support numeric-first data property names' ) - t.deepEqual( + assert.deepEqual( h('', {'data-foo.bar': true}), { type: 'element', @@ -374,7 +367,7 @@ test('hastscript', (t) => { 'should support data attribute names with uncommon characters' ) - t.deepEqual( + assert.deepEqual( h('', {'dataFoo.bar': true}), { type: 'element', @@ -385,7 +378,7 @@ test('hastscript', (t) => { 'should support data property names with uncommon characters' ) - t.deepEqual( + assert.deepEqual( h('', {'data-foo!bar': true}), { type: 'element', @@ -396,7 +389,7 @@ test('hastscript', (t) => { 'should keep invalid data attribute names' ) - t.deepEqual( + assert.deepEqual( h('', {'dataFoo!bar': true}), { type: 'element', @@ -406,12 +399,10 @@ test('hastscript', (t) => { }, 'should keep invalid data property names' ) - - t.end() }) - t.test('unknown property values', (t) => { - t.deepEqual( + await t.test('unknown property values', () => { + assert.deepEqual( h('', {foo: 'bar'}), { type: 'element', @@ -422,7 +413,7 @@ test('hastscript', (t) => { 'should support unknown `string` values' ) - t.deepEqual( + assert.deepEqual( h('', {foo: 3}), { type: 'element', @@ -433,7 +424,7 @@ test('hastscript', (t) => { 'should support unknown `number` values' ) - t.deepEqual( + assert.deepEqual( h('', {foo: true}), { type: 'element', @@ -444,7 +435,7 @@ test('hastscript', (t) => { 'should support unknown `boolean` values' ) - t.deepEqual( + assert.deepEqual( h('', {list: ['bar', 'baz']}), { type: 'element', @@ -455,7 +446,7 @@ test('hastscript', (t) => { 'should support unknown `Array` values' ) - t.deepEqual( + assert.deepEqual( h('', {foo: null}), { type: 'element', @@ -466,7 +457,7 @@ test('hastscript', (t) => { 'should ignore properties with a value of `null`' ) - t.deepEqual( + assert.deepEqual( h('', {foo: undefined}), { type: 'element', @@ -477,7 +468,7 @@ test('hastscript', (t) => { 'should ignore properties with a value of `undefined`' ) - t.deepEqual( + assert.deepEqual( h('', {foo: Number.NaN}), { type: 'element', @@ -487,12 +478,10 @@ test('hastscript', (t) => { }, 'should ignore properties with a value of `NaN`' ) - - t.end() }) - t.test('known booleans', (t) => { - t.deepEqual( + await t.test('known booleans', () => { + assert.deepEqual( h('', {allowFullScreen: ''}), { type: 'element', @@ -503,7 +492,7 @@ test('hastscript', (t) => { 'should cast valid known `boolean` values' ) - t.deepEqual( + assert.deepEqual( h('', {allowFullScreen: 'yup'}), { type: 'element', @@ -514,7 +503,7 @@ test('hastscript', (t) => { 'should not cast invalid known `boolean` values' ) - t.deepEqual( + assert.deepEqual( h('img', {title: 'title'}), { type: 'element', @@ -524,12 +513,10 @@ test('hastscript', (t) => { }, 'should not cast unknown boolean-like values' ) - - t.end() }) - t.test('known overloaded booleans', (t) => { - t.deepEqual( + await t.test('known overloaded booleans', () => { + assert.deepEqual( h('', {download: ''}), { type: 'element', @@ -540,7 +527,7 @@ test('hastscript', (t) => { 'should cast known empty overloaded `boolean` values' ) - t.deepEqual( + assert.deepEqual( h('', {download: 'downLOAD'}), { type: 'element', @@ -551,7 +538,7 @@ test('hastscript', (t) => { 'should cast known named overloaded `boolean` values' ) - t.deepEqual( + assert.deepEqual( h('', {download: 'example.ogg'}), { type: 'element', @@ -561,12 +548,10 @@ test('hastscript', (t) => { }, 'should not cast overloaded `boolean` values for different values' ) - - t.end() }) - t.test('known numbers', (t) => { - t.deepEqual( + await t.test('known numbers', () => { + assert.deepEqual( h('textarea', {cols: '3'}), { type: 'element', @@ -577,7 +562,7 @@ test('hastscript', (t) => { 'should cast valid known `numeric` values' ) - t.deepEqual( + assert.deepEqual( h('textarea', {cols: 'one'}), { type: 'element', @@ -588,7 +573,7 @@ test('hastscript', (t) => { 'should not cast invalid known `numeric` values' ) - t.deepEqual( + assert.deepEqual( h('meter', {low: '40', high: '90'}), { type: 'element', @@ -598,12 +583,10 @@ test('hastscript', (t) => { }, 'should cast known `numeric` values' ) - - t.end() }) - t.test('known lists', (t) => { - t.deepEqual( + await t.test('known lists', () => { + assert.deepEqual( h('', {class: 'foo bar baz'}), { type: 'element', @@ -614,7 +597,7 @@ test('hastscript', (t) => { 'should cast know space-separated `array` values' ) - t.deepEqual( + assert.deepEqual( h('input', {type: 'file', accept: 'video/*, image/*'}), { type: 'element', @@ -625,7 +608,7 @@ test('hastscript', (t) => { 'should cast know comma-separated `array` values' ) - t.deepEqual( + assert.deepEqual( h('a', {coords: ['0', '0', '82', '126']}), { type: 'element', @@ -635,12 +618,10 @@ test('hastscript', (t) => { }, 'should cast a list of known `numeric` values' ) - - t.end() }) - t.test('style', (t) => { - t.deepEqual( + await t.test('style', () => { + assert.deepEqual( h('', {style: {color: 'red', '-webkit-border-radius': '3px'}}), { type: 'element', @@ -653,7 +634,7 @@ test('hastscript', (t) => { 'should support `style` as an object' ) - t.deepEqual( + assert.deepEqual( h('', {style: 'color:/*red*/purple; -webkit-border-radius: 3px'}), { type: 'element', @@ -665,15 +646,11 @@ test('hastscript', (t) => { }, 'should support `style` as a string' ) - - t.end() }) - - t.end() }) - t.test('children', (t) => { - t.deepEqual( + await t.test('children', () => { + assert.deepEqual( h('div', {}, []), { type: 'element', @@ -684,7 +661,7 @@ test('hastscript', (t) => { 'should ignore no children' ) - t.deepEqual( + assert.deepEqual( h('div', {}, 'foo'), { type: 'element', @@ -695,7 +672,7 @@ test('hastscript', (t) => { 'should support `string` for a `Text`' ) - t.deepEqual( + assert.deepEqual( h('div', {}, {type: 'text', value: 'foo'}), { type: 'element', @@ -706,7 +683,7 @@ test('hastscript', (t) => { 'should support a node' ) - t.deepEqual( + assert.deepEqual( h('div', {}, h('span', {}, 'foo')), { type: 'element', @@ -724,7 +701,7 @@ test('hastscript', (t) => { 'should support a node created by `h`' ) - t.deepEqual( + assert.deepEqual( h('div', {}, [ {type: 'text', value: 'foo'}, {type: 'text', value: 'bar'} @@ -741,7 +718,7 @@ test('hastscript', (t) => { 'should support nodes' ) - t.deepEqual( + assert.deepEqual( h('div', {}, [h('span', {}, 'foo'), h('strong', {}, 'bar')]), { type: 'element', @@ -765,7 +742,7 @@ test('hastscript', (t) => { 'should support nodes created by `h`' ) - t.deepEqual( + assert.deepEqual( h('div', {}, ['foo', 'bar']), { type: 'element', @@ -779,7 +756,7 @@ test('hastscript', (t) => { 'should support `Array` for a `Text`s' ) - t.deepEqual( + assert.deepEqual( h('strong', 'foo'), { type: 'element', @@ -790,7 +767,7 @@ test('hastscript', (t) => { 'should allow omitting `properties` for a `string`' ) - t.deepEqual( + assert.deepEqual( h('strong', h('span', 'foo')), { type: 'element', @@ -808,7 +785,7 @@ test('hastscript', (t) => { 'should allow omitting `properties` for a node' ) - t.deepEqual( + assert.deepEqual( h('strong', ['foo', 'bar']), { type: 'element', @@ -822,7 +799,7 @@ test('hastscript', (t) => { 'should allow omitting `properties` for an array' ) - t.deepEqual( + assert.deepEqual( h('input', {type: 'text', value: 'foo'}), { type: 'element', @@ -833,7 +810,7 @@ test('hastscript', (t) => { 'should *not* allow omitting `properties` for an `input[type=text][value]`, as those are void and clash' ) - t.deepEqual( + assert.deepEqual( h('a', {type: 'text/html'}), { type: 'element', @@ -844,7 +821,7 @@ test('hastscript', (t) => { 'should *not* allow omitting `properties` for a `[type]`, without `value` or `children`' ) - t.deepEqual( + assert.deepEqual( h('foo', {type: 'text/html', children: {bar: 'baz'}}), { type: 'element', @@ -855,7 +832,7 @@ test('hastscript', (t) => { 'should *not* allow omitting `properties` when `children` is not set to an array' ) - t.deepEqual( + assert.deepEqual( h('button', {type: 'submit', value: 'Send'}), { type: 'element', @@ -866,7 +843,7 @@ test('hastscript', (t) => { 'should *not* allow omitting `properties` when a button has a valid type' ) - t.deepEqual( + assert.deepEqual( h('button', {type: 'BUTTON', value: 'Send'}), { type: 'element', @@ -877,7 +854,7 @@ test('hastscript', (t) => { 'should *not* allow omitting `properties` when a button has a valid non-lowercase type' ) - t.deepEqual( + assert.deepEqual( h('button', {type: 'menu', value: 'Send'}), { type: 'element', @@ -888,7 +865,7 @@ test('hastscript', (t) => { 'should *not* allow omitting `properties` when a button has a valid type' ) - t.deepEqual( + assert.deepEqual( h('button', {type: 'text', value: 'Send'}), { type: 'element', @@ -899,7 +876,7 @@ test('hastscript', (t) => { 'should allow omitting `properties` when a button has an invalid type' ) - t.deepEqual( + assert.deepEqual( h('section', {id: 'test'}, h('p', 'first'), h('p', 'second')), { type: 'element', @@ -923,7 +900,7 @@ test('hastscript', (t) => { 'should allow passing multiple child nodes as arguments' ) - t.deepEqual( + assert.deepEqual( h('section', h('p', 'first'), h('p', 'second')), { type: 'element', @@ -947,7 +924,7 @@ test('hastscript', (t) => { 'should allow passing multiple child nodes as arguments when there is no properties argument present' ) - t.throws( + assert.throws( () => { // @ts-expect-error runtime. h('foo', {}, true) @@ -955,12 +932,10 @@ test('hastscript', (t) => { /Expected node, nodes, or string, got `true`/, 'should throw when given an invalid value' ) - - t.end() }) - t.test('