From ec0bda47f87d1e1190637d4916e8ae4aa5d8be31 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 31 Jul 2023 11:46:11 +0200 Subject: [PATCH 1/8] Update dev-dependencies --- lib/index.js | 3 +-- lib/types.js | 15 +-------------- package.json | 17 ++++++++++------- 3 files changed, 12 insertions(+), 23 deletions(-) diff --git a/lib/index.js b/lib/index.js index 1c72ef2..0566011 100644 --- a/lib/index.js +++ b/lib/index.js @@ -67,13 +67,11 @@ * @typedef {ResultMaybeSourceMapGenerator} Result */ -// @ts-expect-error: `astring` has broken types. import * as astring from 'astring' /** @type {Handlers} */ const GENERATOR = astring.GENERATOR -/** @type {(node: Program, options: unknown) => string} */ const generate = astring.generate /** @@ -108,6 +106,7 @@ export const toJs = const value = generate(tree, { comments: true, + // @ts-expect-error: assume all handlers are defined. generator: {...GENERATOR, ...handlers}, sourceMap }) diff --git a/lib/types.js b/lib/types.js index a67204a..4e0dfac 100644 --- a/lib/types.js +++ b/lib/types.js @@ -3,21 +3,8 @@ * @typedef {import('source-map').Mapping} Mapping */ -// To do: `astring` types are broken. -// Either `import('astring').State` if everything is fixed, or: -// `Omit & {write: ((code: string, node?: Node) => void)}` /** - * @typedef State - * @property {string} output - * @property {(code: string, node?: Node) => void} write - * @property {boolean} writeComments - * @property {string} indent - * @property {string} lineEnd - * @property {number} indentLevel - * @property {number | undefined} [line] - * @property {number | undefined} [column] - * @property {number | undefined} [lineEndSize] - * @property {Mapping | undefined} [mapping] + * @typedef {import('astring').State} State */ /** diff --git a/package.json b/package.json index 06f19aa..6db5ddc 100644 --- a/package.json +++ b/package.json @@ -42,19 +42,19 @@ "source-map": "^0.7.0" }, "devDependencies": { - "@types/node": "^18.0.0", - "c8": "^7.0.0", - "prettier": "^2.0.0", + "@types/node": "^20.0.0", + "c8": "^8.0.0", + "prettier": "^3.0.0", "remark-cli": "^11.0.0", "remark-preset-wooorm": "^9.0.0", "type-coverage": "^2.0.0", - "typescript": "^4.0.0", - "xo": "^0.53.0" + "typescript": "^5.0.0", + "xo": "^0.55.0" }, "scripts": { "prepack": "npm run build && npm run format", "build": "tsc --build --clean && tsc --build && type-coverage", - "format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", + "format": "remark . -qfo && prettier . -w --log-level warn && xo --fix", "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" @@ -68,7 +68,10 @@ "trailingComma": "none" }, "xo": { - "prettier": true + "prettier": true, + "rules": { + "unicorn/prefer-string-replace-all": "off" + } }, "remarkConfig": { "plugins": [ From 8b518851da6cfee68121a9cba84cea69af0f7304 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 31 Jul 2023 11:47:45 +0200 Subject: [PATCH 2/8] Refactor `package.json`, `tsconfig.json` --- package.json | 30 ++++++++++++++++-------------- tsconfig.json | 10 ++++------ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 6db5ddc..bbf21ad 100644 --- a/package.json +++ b/package.json @@ -56,34 +56,36 @@ "build": "tsc --build --clean && tsc --build && type-coverage", "format": "remark . -qfo && prettier . -w --log-level warn && xo --fix", "test-api": "node --conditions development test/index.js", - "test-coverage": "c8 --check-coverage --100 --reporter lcov npm run test-api", + "test-coverage": "c8 --100 --reporter lcov npm run test-api", "test": "npm run build && npm run format && npm run test-coverage" }, "prettier": { - "tabWidth": 2, - "useTabs": false, - "singleQuote": true, "bracketSpacing": false, "semi": false, - "trailingComma": "none" - }, - "xo": { - "prettier": true, - "rules": { - "unicorn/prefer-string-replace-all": "off" - } + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "none", + "useTabs": false }, "remarkConfig": { "plugins": [ - "preset-wooorm" + "remark-preset-wooorm" ] }, "typeCoverage": { "atLeast": 100, "detail": true, - "strict": true, + "ignoreCatch": true, + "#": "needed `any`s", "ignoreFiles": [ "lib/types.d.ts" - ] + ], + "strict": true + }, + "xo": { + "prettier": true, + "rules": { + "unicorn/prefer-string-replace-all": "off" + } } } diff --git a/tsconfig.json b/tsconfig.json index 1bc9e99..870d82c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,17 +1,15 @@ { - "include": ["**/**.js"], - "exclude": ["coverage/", "node_modules/"], "compilerOptions": { "checkJs": true, + "customConditions": ["development"], "declaration": true, "emitDeclarationOnly": true, "exactOptionalPropertyTypes": true, - "forceConsistentCasingInFileNames": true, "lib": ["es2020"], "module": "node16", - "newLine": "lf", - "skipLibCheck": true, "strict": true, "target": "es2020" - } + }, + "exclude": ["coverage/", "node_modules/"], + "include": ["**/*.js"] } From cbad1b66f8bc344289752cbfea0571875af5a160 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 31 Jul 2023 11:48:01 +0200 Subject: [PATCH 3/8] Refactor `.npmrc` --- .npmrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.npmrc b/.npmrc index 9951b11..3757b30 100644 --- a/.npmrc +++ b/.npmrc @@ -1,2 +1,2 @@ -package-lock=false ignore-scripts=true +package-lock=false From 9db76d979f404f6a2f8a8839f8c23c51825fe0c0 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 31 Jul 2023 12:20:34 +0200 Subject: [PATCH 4/8] Refactor code-style --- index.js | 6 +- lib/index.js | 158 ++++++++++++++++++++++++++------------------- lib/jsx.js | 170 ++++++++++++++++++++++++------------------------ lib/types.js | 27 -------- package.json | 2 +- readme.md | 4 +- test/index.js | 175 ++++++++++++++++++++++++++++---------------------- 7 files changed, 285 insertions(+), 257 deletions(-) delete mode 100644 lib/types.js diff --git a/index.js b/index.js index 868069d..13dd598 100644 --- a/index.js +++ b/index.js @@ -1,9 +1,9 @@ /** - * @typedef {import('./lib/types.js').Handler} Handler - * @typedef {import('./lib/types.js').Handlers} Handlers + * @typedef {import('./lib/index.js').Handler} Handler + * @typedef {import('./lib/index.js').Handlers} Handlers * @typedef {import('./lib/index.js').Options} Options * @typedef {import('./lib/index.js').Result} Result - * @typedef {import('./lib/types.js').State} State + * @typedef {import('./lib/index.js').State} State */ export {toJs} from './lib/index.js' diff --git a/lib/index.js b/lib/index.js index 0566011..09868e4 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,117 +1,145 @@ /** + * @typedef {import('astring').State} State + * @typedef {import('estree-jsx').Node} Nodes * @typedef {import('estree-jsx').Program} Program * @typedef {typeof import('source-map').SourceMapGenerator} SourceMapGenerator * @typedef {import('source-map').RawSourceMap} Map - * @typedef {import('./types.js').Handlers} Handlers */ /** - * @typedef BaseFields + * @typedef {Record} Generator + * + * @callback Handler + * Handle a particular node. + * @param {Generator} this + * `astring` generator. + * @param {any} node + * Node to serialize. + * @param {State} state + * Info passed around. + * @returns {undefined} + * Nothing. + * + * @typedef {Partial} Handlers + */ + +/** + * @typedef {OptionsWithMaybeMapGenerator} Options + * Configuration. + * + * @typedef OptionsFieldsBase + * Base shared option fields. * @property {Handlers | null | undefined} [handlers] * Object mapping node types to functions handling the corresponding nodes. * - * @typedef SourceMapFieldsWithoutSourceMapGenerator + * @typedef OptionsFieldsWithoutSourceMapGenerator + * Extra option fields where there’s definitely no source map generator. * @property {null | undefined} [SourceMapGenerator] * Generate a source map by passing a `SourceMapGenerator` from `source-map` - * in. - * This works if there is positional info on nodes. + * in; this works if there is positional info on nodes. * @property {null | undefined} [filePath] - * Path to input file. - * Only used in source map. + * Path to input file; only used in source map. * - * @typedef SourceMapFieldsWithSourceMapGenerator + * @typedef OptionsFieldsWithSourceMapGenerator + * Extra option fields where there’s definitely a source map generator. * @property {SourceMapGenerator} SourceMapGenerator * Generate a source map by passing a `SourceMapGenerator` from `source-map` - * in. - * This works if there is positional info on nodes. + * in; this works if there is positional info on nodes. * @property {string | null | undefined} [filePath] - * Path to input file. - * Only used in source map. + * Path to input file; only used in source map. * - * @typedef SourceMapFieldsMaybeSourceMapGenerator - * @property {SourceMapGenerator | null | undefined} SourceMapGenerator + * @typedef OptionsFieldsMaybeSourceMapGenerator + * Extra option fields where there may or may not be a source map generator. + * @property {SourceMapGenerator | null | undefined} [SourceMapGenerator] * Generate a source map by passing a `SourceMapGenerator` from `source-map` - * in. - * This works if there is positional info on nodes. + * in; this works if there is positional info on nodes. * @property {string | null | undefined} [filePath] - * Path to input file. - * Only used in source map. + * Path to input file; only used in source map. * - * @typedef {BaseFields & SourceMapFieldsWithoutSourceMapGenerator} OptionsWithoutSourceMapGenerator - * @typedef {BaseFields & SourceMapFieldsWithSourceMapGenerator} OptionsWithSourceMapGenerator - * @typedef {BaseFields & SourceMapFieldsMaybeSourceMapGenerator} OptionsWithMaybeMapGenerator + * @typedef {OptionsFieldsBase & OptionsFieldsWithoutSourceMapGenerator} OptionsWithoutSourceMapGenerator + * Options where there’s definitely no source map generator. + * @typedef {OptionsFieldsBase & OptionsFieldsWithSourceMapGenerator} OptionsWithSourceMapGenerator + * Options where there’s definitely a source map generator. + * @typedef {OptionsFieldsBase & OptionsFieldsMaybeSourceMapGenerator} OptionsWithMaybeMapGenerator + * Options where there may or may not be a source map generator. * - * @typedef {OptionsWithMaybeMapGenerator} Options - * Configuration (optional). + * @typedef {ResultWithMaybeSourceMapGenerator} Result + * Result. * - * @typedef BaseResultFields + * @typedef ResultFieldsBase + * Base shared result fields. * @property {string} value * Serialized JavaScript. * * @typedef ResultFieldsWithoutSourceMapGenerator + * Extra result fields where there’s definitely no source map generator. * @property {undefined} map * Source map as (parsed) JSON, if `SourceMapGenerator` is passed. * * @typedef ResultFieldsWithSourceMapGenerator + * Extra result fields where there’s definitely a source map generator. * @property {Map} map * Source map as (parsed) JSON, if `SourceMapGenerator` is not passed. * * @typedef ResultFieldsMaybeSourceMapGenerator + * Extra result fields where there may or may not be a source map generator. * @property {Map | undefined} map * Source map as (parsed) JSON, if `SourceMapGenerator` might be passed. * - * @typedef {BaseResultFields & ResultFieldsWithoutSourceMapGenerator} ResultWithoutSourceMapGenerator - * @typedef {BaseResultFields & ResultFieldsWithSourceMapGenerator} ResultWithSourceMapGenerator - * @typedef {BaseResultFields & ResultFieldsMaybeSourceMapGenerator} ResultMaybeSourceMapGenerator - * - * @typedef {ResultMaybeSourceMapGenerator} Result + * @typedef {ResultFieldsBase & ResultFieldsWithoutSourceMapGenerator} ResultWithoutSourceMapGenerator + * Result where there’s definitely no source map generator. + * @typedef {ResultFieldsBase & ResultFieldsWithSourceMapGenerator} ResultWithSourceMapGenerator + * Result where there’s definitely a source map generator. + * @typedef {ResultFieldsBase & ResultFieldsMaybeSourceMapGenerator} ResultWithMaybeSourceMapGenerator + * Result where there may or may not be a source map generator. */ -import * as astring from 'astring' +import {GENERATOR, generate} from 'astring' -/** @type {Handlers} */ -const GENERATOR = astring.GENERATOR - -const generate = astring.generate +/** @type {Options} */ +const emptyOptions = {} /** * Serialize an estree as JavaScript. * - * @param tree + * @overload + * @param {Program} tree + * @param {OptionsWithSourceMapGenerator} options + * @returns {ResultWithSourceMapGenerator} + * + * @overload + * @param {Program} tree + * @param {OptionsWithMaybeMapGenerator} options + * @returns {ResultWithMaybeSourceMapGenerator} + * + * @overload + * @param {Program} tree + * @param {OptionsWithoutSourceMapGenerator | null | undefined} [options] + * @returns {ResultWithoutSourceMapGenerator} + * + * @param {Program} tree * Estree (esast). - * @param options + * @param {Options | null | undefined} [options] * Configuration (optional). - * @returns + * @returns {Result} * Result, optionally with source map. */ -export const toJs = - /** - * @type {( - * ((value: Program, options: OptionsWithSourceMapGenerator) => ResultWithSourceMapGenerator) & - * ((value: Program, options: OptionsWithMaybeMapGenerator) => ResultMaybeSourceMapGenerator) & - * ((value: Program, options?: OptionsWithoutSourceMapGenerator | null | undefined) => ResultWithoutSourceMapGenerator) - * )} - */ - ( - /** - * @param {Program} tree - * @param {Options | null | undefined} [options] - * @returns {Result} - */ - function (tree, options) { - const {SourceMapGenerator, filePath, handlers} = options || {} - const sourceMap = SourceMapGenerator - ? new SourceMapGenerator({file: filePath || '.js'}) - : undefined +export function toJs(tree, options) { + const {SourceMapGenerator, filePath, handlers} = options || emptyOptions + const sourceMap = SourceMapGenerator + ? new SourceMapGenerator({file: filePath || '.js'}) + : undefined - const value = generate(tree, { - comments: true, - // @ts-expect-error: assume all handlers are defined. - generator: {...GENERATOR, ...handlers}, - sourceMap - }) - const map = sourceMap ? sourceMap.toJSON() : undefined - - return {value, map} + const value = generate( + tree, + // @ts-expect-error: `sourceMap` can be undefined, `astring` types are buggy. + { + comments: true, + generator: {...GENERATOR, ...handlers}, + sourceMap: sourceMap || undefined } ) + const map = sourceMap ? sourceMap.toJSON() : undefined + + return {value, map} +} diff --git a/lib/jsx.js b/lib/jsx.js index 448d6f6..47e567e 100644 --- a/lib/jsx.js +++ b/lib/jsx.js @@ -1,36 +1,37 @@ /** - * @typedef {import('estree-jsx').JSXAttribute} JSXAttribute - * @typedef {import('estree-jsx').JSXClosingElement} JSXClosingElement - * @typedef {import('estree-jsx').JSXClosingFragment} JSXClosingFragment - * @typedef {import('estree-jsx').JSXElement} JSXElement - * @typedef {import('estree-jsx').JSXExpressionContainer} JSXExpressionContainer - * @typedef {import('estree-jsx').JSXFragment} JSXFragment - * @typedef {import('estree-jsx').JSXIdentifier} JSXIdentifier - * @typedef {import('estree-jsx').JSXMemberExpression} JSXMemberExpression - * @typedef {import('estree-jsx').JSXNamespacedName} JSXNamespacedName - * @typedef {import('estree-jsx').JSXOpeningElement} JSXOpeningElement - * @typedef {import('estree-jsx').JSXOpeningFragment} JSXOpeningFragment - * @typedef {import('estree-jsx').JSXSpreadAttribute} JSXSpreadAttribute - * @typedef {import('estree-jsx').JSXText} JSXText - * @typedef {import('./types.js').Generator} Generator - * @typedef {import('./types.js').State} State + * @typedef {import('estree-jsx').JSXAttribute} JsxAttribute + * @typedef {import('estree-jsx').JSXClosingElement} JsxClosingElement + * @typedef {import('estree-jsx').JSXClosingFragment} JsxClosingFragment + * @typedef {import('estree-jsx').JSXElement} JsxElement + * @typedef {import('estree-jsx').JSXExpressionContainer} JsxExpressionContainer + * @typedef {import('estree-jsx').JSXFragment} JsxFragment + * @typedef {import('estree-jsx').JSXIdentifier} JsxIdentifier + * @typedef {import('estree-jsx').JSXMemberExpression} JsxMemberExpression + * @typedef {import('estree-jsx').JSXNamespacedName} JsxNamespacedName + * @typedef {import('estree-jsx').JSXOpeningElement} JsxOpeningElement + * @typedef {import('estree-jsx').JSXOpeningFragment} JsxOpeningFragment + * @typedef {import('estree-jsx').JSXSpreadAttribute} JsxSpreadAttribute + * @typedef {import('estree-jsx').JSXText} JsxText + * + * @typedef {import('./index.js').Generator} Generator + * @typedef {import('./index.js').State} State */ export const jsx = { - JSXAttribute, - JSXClosingElement, - JSXClosingFragment, - JSXElement, - JSXEmptyExpression, - JSXExpressionContainer, - JSXFragment, - JSXIdentifier, - JSXMemberExpression, - JSXNamespacedName, - JSXOpeningElement, - JSXOpeningFragment, - JSXSpreadAttribute, - JSXText + JSXAttribute: jsxAttribute, + JSXClosingElement: jsxClosingElement, + JSXClosingFragment: jsxClosingFragment, + JSXElement: jsxElement, + JSXEmptyExpression: jsxEmptyExpression, + JSXExpressionContainer: jsxExpressionContainer, + JSXFragment: jsxFragment, + JSXIdentifier: jsxIdentifier, + JSXMemberExpression: jsxMemberExpression, + JSXNamespacedName: jsxNamespacedName, + JSXOpeningElement: jsxOpeningElement, + JSXOpeningFragment: jsxOpeningFragment, + JSXSpreadAttribute: jsxSpreadAttribute, + JSXText: jsxText } /** @@ -40,17 +41,17 @@ export const jsx = { * * @this {Generator} * `astring` generator. - * @param {JSXAttribute} node + * @param {JsxAttribute} node * Node to serialize. * @param {State} state * Info passed around. - * @returns {void} + * @returns {undefined} * Nothing. */ -function JSXAttribute(node, state) { +function jsxAttribute(node, state) { this[node.name.type](node.name, state) - if (node.value !== undefined && node.value !== null) { + if (node.value !== null && node.value !== undefined) { state.write('=') // Encode double quotes in attribute values. @@ -70,14 +71,14 @@ function JSXAttribute(node, state) { * * @this {Generator} * `astring` generator. - * @param {JSXClosingElement} node + * @param {JsxClosingElement} node * Node to serialize. * @param {State} state * Info passed around. - * @returns {void} + * @returns {undefined} * Nothing. */ -function JSXClosingElement(node, state) { +function jsxClosingElement(node, state) { state.write('') @@ -88,14 +89,14 @@ function JSXClosingElement(node, state) { * * @this {Generator} * `astring` generator. - * @param {JSXClosingFragment} node + * @param {JsxClosingFragment} node * Node to serialize. * @param {State} state * Info passed around. - * @returns {void} + * @returns {undefined} * Nothing. */ -function JSXClosingFragment(node, state) { +function jsxClosingFragment(node, state) { state.write('', node) } @@ -105,14 +106,14 @@ function JSXClosingFragment(node, state) { * * @this {Generator} * `astring` generator. - * @param {JSXElement} node + * @param {JsxElement} node * Node to serialize. * @param {State} state * Info passed around. - * @returns {void} + * @returns {undefined} * Nothing. */ -function JSXElement(node, state) { +function jsxElement(node, state) { let index = -1 this[node.openingElement.type](node.openingElement, state) @@ -141,24 +142,24 @@ function JSXElement(node, state) { * * @this {Generator} * `astring` generator. - * @returns {void} + * @returns {undefined} * Nothing. */ -function JSXEmptyExpression() {} +function jsxEmptyExpression() {} /** * `{expression}` * * @this {Generator} * `astring` generator. - * @param {JSXExpressionContainer} node + * @param {JsxExpressionContainer} node * Node to serialize. * @param {State} state * Info passed around. - * @returns {void} + * @returns {undefined} * Nothing. */ -function JSXExpressionContainer(node, state) { +function jsxExpressionContainer(node, state) { state.write('{') this[node.expression.type](node.expression, state) state.write('}') @@ -169,14 +170,14 @@ function JSXExpressionContainer(node, state) { * * @this {Generator} * `astring` generator. - * @param {JSXFragment} node + * @param {JsxFragment} node * Node to serialize. * @param {State} state * Info passed around. - * @returns {void} + * @returns {undefined} * Nothing. */ -function JSXFragment(node, state) { +function jsxFragment(node, state) { let index = -1 this[node.openingFragment.type](node.openingFragment, state) @@ -203,14 +204,14 @@ function JSXFragment(node, state) { * * @this {Generator} * `astring` generator. - * @param {JSXIdentifier} node + * @param {JsxIdentifier} node * Node to serialize. * @param {State} state * Info passed around. - * @returns {void} + * @returns {undefined} * Nothing. */ -function JSXIdentifier(node, state) { +function jsxIdentifier(node, state) { state.write(node.name, node) } @@ -219,14 +220,14 @@ function JSXIdentifier(node, state) { * * @this {Generator} * `astring` generator. - * @param {JSXMemberExpression} node + * @param {JsxMemberExpression} node * Node to serialize. * @param {State} state * Info passed around. - * @returns {void} + * @returns {undefined} * Nothing. */ -function JSXMemberExpression(node, state) { +function jsxMemberExpression(node, state) { this[node.object.type](node.object, state) state.write('.') this[node.property.type](node.property, state) @@ -237,14 +238,14 @@ function JSXMemberExpression(node, state) { * * @this {Generator} * `astring` generator. - * @param {JSXNamespacedName} node + * @param {JsxNamespacedName} node * Node to serialize. * @param {State} state * Info passed around. - * @returns {void} + * @returns {undefined} * Nothing. */ -function JSXNamespacedName(node, state) { +function jsxNamespacedName(node, state) { this[node.namespace.type](node.namespace, state) state.write(':') this[node.name.type](node.name, state) @@ -255,14 +256,14 @@ function JSXNamespacedName(node, state) { * * @this {Generator} * `astring` generator. - * @param {JSXOpeningElement} node + * @param {JsxOpeningElement} node * Node to serialize. * @param {State} state * Info passed around. - * @returns {void} + * @returns {undefined} * Nothing. */ -function JSXOpeningElement(node, state) { +function jsxOpeningElement(node, state) { let index = -1 state.write('<') @@ -283,14 +284,14 @@ function JSXOpeningElement(node, state) { * * @this {Generator} * `astring` generator. - * @param {JSXOpeningFragment} node + * @param {JsxOpeningFragment} node * Node to serialize. * @param {State} state * Info passed around. - * @returns {void} + * @returns {undefined} * Nothing. */ -function JSXOpeningFragment(node, state) { +function jsxOpeningFragment(node, state) { state.write('<>', node) } @@ -299,14 +300,14 @@ function JSXOpeningFragment(node, state) { * * @this {Generator} * `astring` generator. - * @param {JSXSpreadAttribute} node + * @param {JsxSpreadAttribute} node * Node to serialize. * @param {State} state * Info passed around. - * @returns {void} + * @returns {undefined} * Nothing. */ -function JSXSpreadAttribute(node, state) { +function jsxSpreadAttribute(node, state) { state.write('{') // eslint-disable-next-line new-cap this.SpreadElement(node, state) @@ -318,26 +319,15 @@ function JSXSpreadAttribute(node, state) { * * @this {Generator} * `astring` generator. - * @param {JSXText} node + * @param {JsxText} node * Node to serialize. * @param {State} state * Info passed around. - * @returns {void} + * @returns {undefined} * Nothing. */ -function JSXText(node, state) { - state.write( - encodeJsx(node.value).replace(/[<>{}]/g, ($0) => - $0 === '<' - ? '<' - : $0 === '>' - ? '>' - : $0 === '{' - ? '{' - : '}' - ), - node - ) +function jsxText(node, state) { + state.write(encodeJsx(node.value).replace(/[<>{}]/g, replaceJsxChar), node) } /** @@ -358,3 +348,17 @@ function JSXText(node, state) { function encodeJsx(value) { return value.replace(/&(?=[#a-z])/gi, '&') } + +/** + * @param {string} $0 + * @returns {string} + */ +function replaceJsxChar($0) { + return $0 === '<' + ? '<' + : $0 === '>' + ? '>' + : $0 === '{' + ? '{' + : '}' +} diff --git a/lib/types.js b/lib/types.js deleted file mode 100644 index 4e0dfac..0000000 --- a/lib/types.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @typedef {import('estree-jsx').Node} Node - * @typedef {import('source-map').Mapping} Mapping - */ - -/** - * @typedef {import('astring').State} State - */ - -/** - * @typedef {Record} Generator - * @typedef {Partial} Handlers - * Handlers for different nodes. - * - * @callback Handler - * Handle a particular node. - * @param {Generator} this - * `astring` generator. - * @param {any} node - * Node to serialize. - * @param {State} state - * Info passed around. - * @returns {void} - * Nothing. - */ - -export {} diff --git a/package.json b/package.json index bbf21ad..7135ab1 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "ignoreCatch": true, "#": "needed `any`s", "ignoreFiles": [ - "lib/types.d.ts" + "lib/index.d.ts" ], "strict": true }, diff --git a/readme.md b/readme.md index 8997d38..443419a 100644 --- a/readme.md +++ b/readme.md @@ -140,7 +140,7 @@ Handle a particular node (TypeScript type). ###### Returns -Nothing (`void`). +Nothing (`undefined`). ### `Handlers` @@ -287,7 +287,7 @@ Modified example from § Use above: -import {toJs} from 'estree-util-to-js' +import {Parser} from 'acorn' +import acornJsx from 'acorn-jsx' -+import {toJs, jsx} from 'estree-util-to-js' ++import {jsx, toJs} from 'estree-util-to-js' -const file = String(await fs.readFile('index.js')) +const file = '<>{1 + 1}' diff --git a/test/index.js b/test/index.js index 6283b74..2f7a2e5 100644 --- a/test/index.js +++ b/test/index.js @@ -7,25 +7,25 @@ import test from 'node:test' import {Parser} from 'acorn' import acornJsx from 'acorn-jsx' import {SourceMapGenerator} from 'source-map' -import {toJs, jsx} from '../index.js' -import * as mod from '../index.js' +import {jsx, toJs} from '../index.js' -test('toJs', () => { - assert.deepEqual( - Object.keys(mod).sort(), - ['jsx', 'toJs'], - 'should expose the public api' - ) +test('toJs', async function (t) { + await t.test('should expose the public api', async function () { + assert.deepEqual(Object.keys(await import('../index.js')).sort(), [ + 'jsx', + 'toJs' + ]) + }) - assert.deepEqual( - toJs(fromJs('const a = 1')), - {value: 'const a = 1;\n', map: undefined}, - 'should serialize js' - ) + await t.test('should serialize js', async function () { + assert.deepEqual(toJs(fromJs('const a = 1')), { + value: 'const a = 1;\n', + map: undefined + }) + }) - assert.deepEqual( - toJs(fromJs('const a = 1'), {SourceMapGenerator}), - { + await t.test('should serialize js w/ a source map', async function () { + assert.deepEqual(toJs(fromJs('const a = 1'), {SourceMapGenerator}), { value: 'const a = 1;\n', map: { version: 3, @@ -34,84 +34,107 @@ test('toJs', () => { mappings: 'MAAMA,IAAI', file: '.js' } - }, - 'should serialize js w/ a source map' - ) + }) + }) - assert.deepEqual( - toJs(fromJs('const a = 1'), {SourceMapGenerator, filePath: 'example.js'}), - { - value: 'const a = 1;\n', - map: { - version: 3, - sources: ['example.js'], - names: ['a'], - mappings: 'MAAMA,IAAI', - file: 'example.js' - } - }, - 'should serialize js w/ a source map and a file path' + await t.test( + 'should serialize js w/ a source map and a file path', + async function () { + assert.deepEqual( + toJs(fromJs('const a = 1'), { + SourceMapGenerator, + filePath: 'example.js' + }), + { + value: 'const a = 1;\n', + map: { + version: 3, + sources: ['example.js'], + names: ['a'], + mappings: 'MAAMA,IAAI', + file: 'example.js' + } + } + ) + } ) - assert.equal( - toJs(fromJs('1', true), {handlers: jsx}).value, - '1;\n', - 'should supports jsx (opening and closing tag)' + await t.test( + 'should supports jsx (opening and closing tag)', + async function () { + assert.equal( + toJs(fromJs('1', true), {handlers: jsx}).value, + '1;\n' + ) + } ) - assert.equal( - toJs(fromJs('<>1', true), {handlers: jsx}).value, - '<>1;\n', - 'should supports jsx (opening and closing fragment)' + await t.test( + 'should supports jsx (opening and closing fragment)', + async function () { + assert.equal( + toJs(fromJs('<>1', true), {handlers: jsx}).value, + '<>1;\n' + ) + } ) - assert.equal( - toJs(fromJs('', true), {handlers: jsx}).value, - ';\n', - 'should supports jsx (member name)' - ) + await t.test('should supports jsx (member name)', async function () { + assert.equal( + toJs(fromJs('', true), {handlers: jsx}).value, + ';\n' + ) + }) - assert.equal( - toJs(fromJs('', true), {handlers: jsx}).value, - ';\n', - 'should supports jsx (namespaced name)' - ) + await t.test('should supports jsx (namespaced name)', async function () { + assert.equal( + toJs(fromJs('', true), {handlers: jsx}).value, + ';\n' + ) + }) - assert.equal( - toJs(fromJs('', true), {handlers: jsx}).value, - ';\n', - 'should supports jsx (attributes)' - ) + await t.test('should supports jsx (attributes)', async function () { + assert.equal( + toJs(fromJs('', true), {handlers: jsx}).value, + ';\n' + ) + }) - assert.equal( - toJs(fromJs('', true), {handlers: jsx}).value, - ';\n', - 'should supports jsx (namespaced attribute)' - ) + await t.test('should supports jsx (namespaced attribute)', async function () { + assert.equal( + toJs(fromJs('', true), {handlers: jsx}).value, + ';\n' + ) + }) - assert.equal( - toJs(fromJs('empty: {}, comment: {/*b*/}, value: {1}', true), { - handlers: jsx - }).value, - 'empty: {}, comment: {}, value: {1};\n', - 'should supports jsx (expressions)' - ) + await t.test('should supports jsx (expressions)', async function () { + assert.equal( + toJs(fromJs('empty: {}, comment: {/*b*/}, value: {1}', true), { + handlers: jsx + }).value, + 'empty: {}, comment: {}, value: {1};\n' + ) + }) - assert.equal( - toJs(fromJs('1 < 2 > 3 { 4 } 5', true), { - handlers: jsx - }).value, - '1 < 2 > 3 { 4 } 5;\n', - 'should supports jsx (text)' - ) + await t.test('should supports jsx (text)', async function () { + assert.equal( + toJs(fromJs('1 < 2 > 3 { 4 } 5', true), { + handlers: jsx + }).value, + '1 < 2 > 3 { 4 } 5;\n' + ) + }) }) /** * @param {string} value - * @param {boolean} [jsx=false] + * JavaScript. + * @param {boolean | null | undefined} [jsx=false] + * Whether to parse as JSX (default: `false`). * @returns {Program} + * ESTree program. */ -function fromJs(value, jsx = false) { +function fromJs(value, jsx) { const parser = jsx ? Parser.extend(acornJsx()) : Parser // @ts-expect-error: fine. return parser.parse(value, { From 0150c33b20063d6c67c4ea6bc7cb254c066d30b6 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 31 Jul 2023 12:22:56 +0200 Subject: [PATCH 5/8] Refactor docs --- readme.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/readme.md b/readme.md index 443419a..302edae 100644 --- a/readme.md +++ b/readme.md @@ -56,7 +56,7 @@ It turns JS into esast. ## Install This package is [ESM only][esm]. -In Node.js (version 14.14+ and 16.0+), install with [npm][]: +In Node.js (version 16+), install with [npm][]: ```sh npm install estree-util-to-js @@ -322,10 +322,13 @@ It exports the additional types [`Handler`][api-handler], ## Compatibility -Projects maintained by the unified collective are compatible with all maintained +Projects maintained by the unified collective are compatible with maintained versions of Node.js. -As of now, that is Node.js 14.14+, 16.0+, and 18.0+. -Our projects sometimes work with older versions, but this is not guaranteed. + +When we cut a new major release, we drop support for unmaintained versions of +Node. +This means we try to keep the current release line, `estree-util-to-js@^1`, +compatible with Node.js 12. ## Contribute @@ -355,9 +358,9 @@ abide by its terms. [downloads]: https://www.npmjs.com/package/estree-util-to-js -[size-badge]: https://img.shields.io/bundlephobia/minzip/estree-util-to-js.svg +[size-badge]: https://img.shields.io/badge/dynamic/json?label=minzipped%20size&query=$.size.compressedSize&url=https://deno.bundlejs.com/?q=estree-util-to-js -[size]: https://bundlephobia.com/result?p=estree-util-to-js +[size]: https://bundlejs.com/?q=estree-util-to-js [sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg From 65ae5ccbf47e5637e089001dcb28b6be1df23548 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 31 Jul 2023 12:24:30 +0200 Subject: [PATCH 6/8] Change to use `exports` --- package.json | 3 +-- test/index.js | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 7135ab1..3e56ca9 100644 --- a/package.json +++ b/package.json @@ -29,8 +29,7 @@ ], "sideEffects": false, "type": "module", - "main": "index.js", - "types": "index.d.ts", + "exports": "./index.js", "files": [ "lib/", "index.d.ts", diff --git a/test/index.js b/test/index.js index 2f7a2e5..c089613 100644 --- a/test/index.js +++ b/test/index.js @@ -6,12 +6,12 @@ import assert from 'node:assert/strict' import test from 'node:test' import {Parser} from 'acorn' import acornJsx from 'acorn-jsx' +import {jsx, toJs} from 'estree-util-to-js' import {SourceMapGenerator} from 'source-map' -import {jsx, toJs} from '../index.js' test('toJs', async function (t) { await t.test('should expose the public api', async function () { - assert.deepEqual(Object.keys(await import('../index.js')).sort(), [ + assert.deepEqual(Object.keys(await import('estree-util-to-js')).sort(), [ 'jsx', 'toJs' ]) From 6548f8fef38e54f5b5382d10c4348d870a18df67 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 31 Jul 2023 12:24:53 +0200 Subject: [PATCH 7/8] Change to require Node.js 16 --- readme.md | 4 ++-- tsconfig.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index 302edae..54752bc 100644 --- a/readme.md +++ b/readme.md @@ -327,8 +327,8 @@ versions of Node.js. When we cut a new major release, we drop support for unmaintained versions of Node. -This means we try to keep the current release line, `estree-util-to-js@^1`, -compatible with Node.js 12. +This means we try to keep the current release line, `estree-util-to-js@^2`, +compatible with Node.js 16. ## Contribute diff --git a/tsconfig.json b/tsconfig.json index 870d82c..82cc749 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,10 +5,10 @@ "declaration": true, "emitDeclarationOnly": true, "exactOptionalPropertyTypes": true, - "lib": ["es2020"], + "lib": ["es2022"], "module": "node16", "strict": true, - "target": "es2020" + "target": "es2022" }, "exclude": ["coverage/", "node_modules/"], "include": ["**/*.js"] From 1ffa93f86d69e9d94e0a82d65d78afa2e6296aaa Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 31 Jul 2023 12:25:11 +0200 Subject: [PATCH 8/8] 2.0.0 --- package.json | 2 +- readme.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 3e56ca9..781eae0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "estree-util-to-js", - "version": "1.2.0", + "version": "2.0.0", "description": "estree (and esast) utility to serialize to JavaScript", "license": "MIT", "keywords": [ diff --git a/readme.md b/readme.md index 54752bc..39f9a59 100644 --- a/readme.md +++ b/readme.md @@ -65,14 +65,14 @@ npm install estree-util-to-js In Deno with [`esm.sh`][esmsh]: ```js -import {toJs} from 'https://esm.sh/estree-util-to-js@1' +import {toJs} from 'https://esm.sh/estree-util-to-js@2' ``` In browsers with [`esm.sh`][esmsh]: ```html ``` pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy