From eb5b9a89ce8988e23a8ef2d36cc90751dbce972a Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Tue, 9 Jan 2024 20:34:28 -0500 Subject: [PATCH] Add strict type checking via TypeScript The code is still JavaScript, but now we get strict type checking in Visual Studio Code and in continuous integration via `tsc` in `pnpm typecheck`. The docs generated by 'jsdoc' are a little funky, and we don't get as much documentation in Visual Studio Code as I expected. I believe I can fix these issues at some point with this foundation in place. The actual changes include: - Added @types/{chai,node}, jsdoc, and typescript as devDependencies. - Added JSDoc-based @typedefs, including the standalone lib/types.js based on: "Stack Overflow: How to 'import' a typedef from one file to another in JSDoc using Node.js?" - https://stackoverflow.com/a/76872194 - Set .eslintrc to disable the no-undefined-types rule by extending "plugin:jsdoc/recommended-typescript-flavor-error". This is because the Handlebars types in lib/parser.js weren't trivial to replicate, and TypeScript finds those types just fine. This was based on advice from: > ...the config plugin:jsdoc/recommended-typescript-error should > disable the jsdoc/no-undefined-types rule because TypeScript itself > is responsible for reporting errors about invalid JSDoc types. > > - https://github.com/gajus/eslint-plugin-jsdoc/issues/888#issuecomment-1544914446 And: > If you are not using TypeScript syntax (your source files are still > .js files) but you are using the TypeScript flavor within JSDoc > (i.e., the default "typescript" mode in eslint-plugin-jsdoc) and you > are perhaps using allowJs and checkJs options of TypeScript's > tsconfig.json), you may use: > > ```json > { > "extends": ["plugin:jsdoc/recommended-typescript-flavor"] > } > ``` > > ...or to report with failing errors instead of mere warnings: > > ```json > { > "extends": ["plugin:jsdoc/recommended-typescript-flavor-error"] > } > ``` > > - https://github.com/gajus/eslint-plugin-jsdoc#eslintrc More background: - https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-undefined-types.md - https://github.com/gajus/eslint-plugin-jsdoc/issues/99 - https://github.com/gajus/eslint-plugin-jsdoc/pull/1098 - https://github.com/jsdoc/jsdoc/issues/1537 - At the same time, extending "recommended-typescript-flavor-error" required adding the `// eslint-disable-next-line no-unused-vars` directive before each set of imports from lib/types.js. - Added test/vitest.d.ts so TypeScript could find the custom toStartWith and toEndWith expect extension matchers. - Added `pnpm typecheck && pnpm jsdoc` to `pnpm test:ci`. --- .eslintrc | 8 +- .gitignore | 2 + README.md | 2 +- ci/vitest.config.js | 2 +- index.js | 42 +++++- jsconfig.json | 16 +++ lib/index.js | 49 ++++++- lib/partials.js | 22 ++- lib/types.js | 121 +++++++++++++++++ package.json | 17 ++- pnpm-lock.yaml | 279 +++++++++++++++++++++++++++++++-------- test/plugin-impl.test.js | 20 ++- test/vitest.d.ts | 18 +++ vitest.config.js | 1 + 14 files changed, 515 insertions(+), 84 deletions(-) create mode 100644 jsconfig.json create mode 100644 lib/types.js create mode 100644 test/vitest.d.ts diff --git a/.eslintrc b/.eslintrc index aa7d52c..aee7c74 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,7 +1,7 @@ { - "env" : { + "env": { "node": true, - "es2023" : true + "es2023": true }, "parserOptions": { "ecmaVersion": "latest", @@ -14,7 +14,7 @@ ], "extends": [ "eslint:recommended", - "plugin:jsdoc/recommended" + "plugin:jsdoc/recommended-typescript-flavor-error" ], "overrides": [ { @@ -25,7 +25,7 @@ ] } ], - "rules" : { + "rules": { "@stylistic/js/comma-dangle": [ "error", "never" ], diff --git a/.gitignore b/.gitignore index c4c8336..f5251f3 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,6 @@ node_modules/ out/ pnpm-debug.log tmp/ +types/ *.log +*.tgz diff --git a/README.md b/README.md index 58ea472..a2abb92 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ _**Status**: I've still got a bit of work to do before publishing v1.0.0. I need to add tests based on the mbland/tomcat-servlet-testing-example project from whence this came and add more documentation. I plan to finish this by -2024-01-08._ +2024-01-11._ Source: diff --git a/ci/vitest.config.js b/ci/vitest.config.js index a52aab1..3d9480f 100644 --- a/ci/vitest.config.js +++ b/ci/vitest.config.js @@ -1,5 +1,5 @@ import { defineConfig, mergeConfig } from 'vitest/config' -import baseConfig from '../vitest.config' +import baseConfig from '../vitest.config.js' export default mergeConfig(baseConfig, defineConfig({ test: { diff --git a/index.js b/index.js index e9adabb..4a64607 100644 --- a/index.js +++ b/index.js @@ -37,24 +37,56 @@ */ import PluginImpl, { PLUGIN_NAME } from './lib/index.js' +// eslint-disable-next-line no-unused-vars +import { PluginOptions, Transform } from './lib/types.js' /** * A Rollup plugin object for precompiling Handlebars templates. * @module rollup-plugin-handlebars-precompiler */ +/** + * @typedef {object} RollupPlugin + * @property {string} name - plugin name + * @property {Function} resolveId - resolves the plugin's own import ID + * @property {Function} load - emits the plugin's helper module code + * @property {Function} transform - emits JavaScript code compiled from + * Handlebars templates + * @see https://rollupjs.org/plugin-development/ + */ + /** * Returns a Rollup plugin object for precompiling Handlebars templates. * @function default - * @param {object} options object containing Handlebars compiler API options - * @returns {object} a Rollup plugin that precompiles Handlebars templates + * @param {PluginOptions} options - plugin configuration options + * @returns {RollupPlugin} - the configured plugin object */ export default function HandlebarsPrecompiler(options) { const p = new PluginImpl(options) return { name: PLUGIN_NAME, - resolveId(id) { if (p.shouldEmitHelpersModule(id)) return id }, - load(id) { if (p.shouldEmitHelpersModule(id)) return p.helpersModule() }, - transform(code, id) { if (p.isTemplate(id)) return p.compile(code, id) } + + /** + * @param {string} id - import identifier to resolve + * @returns {(string | undefined)} - the plugin ID if id matches it + * @see https://rollupjs.org/plugin-development/#resolveid + */ + resolveId: function (id) { + return p.shouldEmitHelpersModule(id) ? id : undefined + }, + + /** + * @param {string} id - import identifier to load + * @returns {(string | undefined)} - the plugin helper module if id matches + * @see https://rollupjs.org/plugin-development/#load + */ + load: function (id) { + return p.shouldEmitHelpersModule(id) ? p.helpersModule() : undefined + }, + + /** @type {Transform} */ + transform: function (code, id) { + return p.isTemplate(id) ? p.compile(code, id) : undefined + } } } diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..50d2464 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "checkJs": true, + "lib": [ + "ES2022" + ], + "module": "node16", + "target": "es2020", + "strict": true + }, + "exclude": [ + "node_modules/**", + "coverage*/**", + "jsdoc/**" + ] +} diff --git a/lib/index.js b/lib/index.js index 379b4fb..eb3bd7b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -37,6 +37,10 @@ */ import collectPartials from './partials.js' +import { + // eslint-disable-next-line no-unused-vars + Compiled, PartialName, PartialPath, PluginOptions, SourceMap, Transform +} from './types.js' import { createFilter } from '@rollup/pluginutils' import Handlebars from 'handlebars' @@ -44,12 +48,16 @@ export const PLUGIN_NAME = 'handlebars-precompiler' const DEFAULT_INCLUDE = ['**/*.hbs', '**/*.handlebars', '**/*.mustache'] const DEFAULT_EXCLUDE = 'node_modules/**' const DEFAULT_PARTIALS = '**/_*' -const DEFAULT_PARTIAL_NAME = id => { + +/** @type {PartialName} */ +const DEFAULT_PARTIAL_NAME = function (id) { return id.replace(/.*\//, '') // extract the basename .replace(/\.[^.]*$/, '') // remove the file extension, if present .replace(/^[^[:alnum:]]*/, '') // strip leading non-alphanumeric characters } -const DEFAULT_PARTIAL_PATH = (partialName, importerPath) => { + +/** @type {PartialPath} */ +const DEFAULT_PARTIAL_PATH = function (partialName, importerPath) { return `./_${partialName}.${importerPath.replace(/.*\./, '')}` } @@ -58,6 +66,21 @@ const HANDLEBARS_PATH = 'handlebars/lib/handlebars.runtime' const IMPORT_HANDLEBARS = `import Handlebars from '${HANDLEBARS_PATH}'` const IMPORT_HELPERS = `import Render from '${PLUGIN_ID}'` +/** + * @callback CompilerOpts + * @param {string} id - import ID of module to compile + * @returns {object} - Handlebars compiler options based on id + */ + +/** + * @callback AdjustSourceMap + * @param {string} map - the Handlebars source map as a JSON string + * @param {number} numLinesBeforeTmpl - number of empty lines to add to the + * beginning of the source mappings to account for the generated code before + * the precompiled template + * @returns {SourceMap} - potentially modified Handlebars source map + */ + /** * Rollup Handlebars precompiler implementation */ @@ -67,10 +90,15 @@ export default class PluginImpl { #isPartial #partialName #partialPath + /** @type {CompilerOpts} */ #compilerOpts + /** @type {AdjustSourceMap} */ #adjustSourceMap - constructor(options = {}) { + /** + * @param {PluginOptions} options - plugin configuration options + */ + constructor(options = /** @type {PluginOptions} */ ({})) { this.#helpers = options.helpers || [] this.#isTemplate = createFilter( options.include || DEFAULT_INCLUDE, @@ -101,6 +129,10 @@ export default class PluginImpl { } } + /** + * @param {string} id - import identifier + * @returns {boolean} - true if id is the plugin's import identifier + */ shouldEmitHelpersModule(id) { return id === PLUGIN_ID } helpersModule() { @@ -118,12 +150,17 @@ export default class PluginImpl { ].join('\n') } + /** + * @param {string} id - import identifier + * @returns {boolean} - true if id matches the filter for template files + */ isTemplate(id) { return this.#isTemplate(id) } + /** @type {Transform} */ compile(code, id) { const opts = this.#compilerOpts(id) const ast = Handlebars.parse(code, opts) - const compiled = Handlebars.precompile(ast, opts) + const compiled = /** @type {Compiled} */ (Handlebars.precompile(ast, opts)) const { code: tmpl = compiled, map: srcMap } = compiled const beforeTmpl = [ @@ -143,6 +180,10 @@ export default class PluginImpl { } } + /** + * @param {string} id - id of the partial to register + * @returns {string} - Handlebars.registerPartial statement for the partial + */ #partialRegistration(id) { return `Handlebars.registerPartial('${this.#partialName(id)}', RawTemplate)` } diff --git a/lib/partials.js b/lib/partials.js index bd0b16b..88fe305 100644 --- a/lib/partials.js +++ b/lib/partials.js @@ -43,18 +43,28 @@ import Handlebars from 'handlebars' * @see https://github.com/handlebars-lang/handlebars.js/blob/master/docs/compiler-api.md */ class PartialCollector extends Handlebars.Visitor { + /** @type {string[]} */ partials = [] + /** + * @param {hbs.AST.PartialStatement} partial - partial name to evaluate + */ PartialStatement(partial) { this.collect(partial.name) - return super.PartialStatement(partial) + super.PartialStatement(partial) } - + /** + * @param {hbs.AST.PartialBlockStatement} partial - partial name to evaluate + */ PartialBlockStatement(partial) { this.collect(partial.name) - return super.PartialBlockStatement(partial) + super.PartialBlockStatement(partial) } + /** + * @param {hbs.AST.PathExpression | hbs.AST.SubExpression} n - potential + * partial name to collect + */ collect(n) { if (n.type === 'PathExpression' && n.original !== '@partial-block') { this.partials.push(n.original) @@ -64,11 +74,11 @@ class PartialCollector extends Handlebars.Visitor { /** * Returns the partial names parsed from a Handlebars template + * @param {hbs.AST.Program} ast - abstract syntax tree for a Handlebars template + * returned by Handlebars.parse() + * @returns {string[]} - a list of partial names parsed from the template * @see https://handlebarsjs.com/guide/partials.html * @see https://github.com/handlebars-lang/handlebars.js/blob/master/docs/compiler-api.md - * @param {object} ast - abstract syntax tree for a Handlebars template returned - * by Handlebars.parse() - * @returns {string[]} - a list of partial names parsed from the template */ export default function collectPartials(ast) { const collector = new PartialCollector() diff --git a/lib/types.js b/lib/types.js new file mode 100644 index 0000000..6db364e --- /dev/null +++ b/lib/types.js @@ -0,0 +1,121 @@ +/* + * Original work Copyright (c) 2016 Benjamin Legrand under the MIT License + * https://github.com/benjilegnard/rollup-plugin-handlebars + * + * Original work Copyright (c) 2016 Mixmax, Inc under the MIT License + * https://github.com/mixmaxhq/rollup-plugin-handlebars-plus + * + * Derived work Copyright (c) 2023 Mike Bland under the + * Mozilla Public License Version 2.0 + * https://github.com/mbland/rollup-plugin-handlebars-precompiler + * + * MIT License + * ----------- + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Mozilla Public License Version 2.0 + * ---------------------------------- + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +/** + * @callback PartialName + * @param {string} id - import identifier of a Handlebars partial template + * @returns {string} - the partial name derived from id + */ +/** @type {PartialName} */ +export let PartialName + +/** + * @callback PartialPath + * @param {string} partialName - name of a partial from which to derive its path + * @param {string} importerPath - path of the module importing the template + * @returns {string} - the path to the partial as derived from the arguments + */ +/** @type {PartialPath} */ +export let PartialPath + +/** + * An approximation of Handlebars's PrecompileOptions + * @typedef {object} PrecompileOptions + * @property {string} [srcName] - input file path used to generate source map; + * should not be set, and will be deleted if present + * @property {string} [destName] - destination file path used to generate source + * map; should not be set, and will be deleted if present + * @see https://handlebarsjs.com/api-reference/compilation.html + */ + +/** + * @typedef {object} PluginOptions + * @property {string[]} [helpers] - an array of file paths to modules containing + * Handlebars helper functions + * @property {(string | string[])} [include] - one or more patterns matching + * Handlebars template files to transform + * @property {(string | string[])} [exclude] - one or more patterns matching + * Handlebars template files to exclude from transformation + * @property {(string | string[])} [partials] - one or more patterns matching + * Handlebars template files containing partials + * @property {PartialName} [partialName] - function to transform a partial file + * name into the name used to apply the partial in other templates + * @property {PartialPath} [partialPath] - function to transform a partial's + * name and that of the module importing it into its import path + * @property {PrecompileOptions} [compiler] - compiler options passed through to + * Handlebars.parse() and Handlebars.precompile() + * @property {boolean} [sourcemap] - disables source map generation when false + * @property {boolean} [sourceMap] - disables source map generation when false + */ +/** @type {PluginOptions} */ +export let PluginOptions + +/** + * @typedef {object} Compiled + * @property {string} code - the precompiled Handlebars template code + * @property {string} map - the Handlebars source map as a JSON string + */ +/** @type {Compiled} */ +export let Compiled + +/** + * @typedef {object} SourceMap - a source map for transformed source code + * @property {string} mappings - encoded mapping data + * @see https://sourcemaps.info/spec.html + */ +/** @type {SourceMap} */ +export let SourceMap + +/** + * @typedef {object} TransformResult - result from RollupPlugin.transform() + * @property {string} code - the transformed source code + * @property {SourceMap} map - the source map for the transformed source code + */ +/** @type {TransformResult} */ +export let TransformResult + +/** + * @callback Transform + * @param {string} code - source code to potentially transform + * @param {string} id - import ID of source file + * @returns {(TransformResult | undefined)} - JavaScript precompiled from a + * Handlebars template, if id matches the configured template filter + * @see https://rollupjs.org/plugin-development/#transform + */ +/** @type {Transform} */ +export let Transform diff --git a/package.json b/package.json index 125e100..c1c3c36 100644 --- a/package.json +++ b/package.json @@ -3,13 +3,20 @@ "version": "1.0.0", "description": "Rollup plugin to precompile Handlebars templates into JavaScript modules", "main": "index.js", + "types": "types/index.d.ts", "scripts": { "lint": "eslint --color --max-warnings 0 .", "test": "vitest", - "test:ci": "eslint --color --max-warnings 0 . && vitest run -c ci/vitest.config.js", - "jsdoc": "jsdoc-cli-wrapper -c jsdoc.json ." + "test:ci": "pnpm lint && pnpm typecheck && pnpm jsdoc && vitest run -c ci/vitest.config.js", + "jsdoc": "jsdoc-cli-wrapper -c jsdoc.json .", + "typecheck": "npx -p typescript tsc -p jsconfig.json --noEmit --pretty", + "prepack": "npx -p typescript tsc ./index.js --allowJs --declaration --declarationMap --emitDeclarationOnly --outDir types" }, - "files": [ "index.js", "lib/*" ], + "files": [ + "index.js", + "lib/*", + "types/*" + ], "keywords": [ "rollup", "handlebars", @@ -26,13 +33,17 @@ "bugs": "https://github.com/mbland/rollup-plugin-handlebars-precompiler/issues", "devDependencies": { "@stylistic/eslint-plugin-js": "^1.5.3", + "@types/chai": "^4.3.11", + "@types/node": "^20.10.8", "@vitest/coverage-istanbul": "^1.1.3", "@vitest/coverage-v8": "^1.1.3", "@vitest/ui": "^1.1.3", "eslint": "^8.56.0", "eslint-plugin-jsdoc": "^46.10.1", "eslint-plugin-vitest": "^0.3.20", + "jsdoc": "^4.0.2", "jsdoc-cli-wrapper": "^1.0.4", + "typescript": "^5.3.3", "vitest": "^1.1.3" }, "dependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 00db6c0..cd84ffa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,6 +16,12 @@ devDependencies: '@stylistic/eslint-plugin-js': specifier: ^1.5.3 version: 1.5.3(eslint@8.56.0) + '@types/chai': + specifier: ^4.3.11 + version: 4.3.11 + '@types/node': + specifier: ^20.10.8 + version: 20.10.8 '@vitest/coverage-istanbul': specifier: ^1.1.3 version: 1.1.3(vitest@1.1.3) @@ -34,12 +40,18 @@ devDependencies: eslint-plugin-vitest: specifier: ^0.3.20 version: 0.3.20(eslint@8.56.0)(typescript@5.3.3)(vitest@1.1.3) + jsdoc: + specifier: ^4.0.2 + version: 4.0.2 jsdoc-cli-wrapper: specifier: ^1.0.4 version: 1.0.4 + typescript: + specifier: ^5.3.3 + version: 5.3.3 vitest: specifier: ^1.1.3 - version: 1.1.3(@vitest/ui@1.1.3) + version: 1.1.3(@types/node@20.10.8)(@vitest/ui@1.1.3) packages: @@ -566,6 +578,13 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true + /@jsdoc/salty@0.2.7: + resolution: {integrity: sha512-mh8LbS9d4Jq84KLw8pzho7XC2q2/IJGiJss3xwRoLD1A+EE16SjN4PfaG4jRCzKegTFLlN0Zd8SdUPE6XdoPFg==} + engines: {node: '>=v12.0.0'} + dependencies: + lodash: 4.17.21 + dev: true + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -605,104 +624,104 @@ packages: picomatch: 2.3.1 dev: false - /@rollup/rollup-android-arm-eabi@4.9.3: - resolution: {integrity: sha512-nvh9bB41vXEoKKvlWCGptpGt8EhrEwPQFDCY0VAto+R+qpSbaErPS3OjMZuXR8i/2UVw952Dtlnl2JFxH31Qvg==} + /@rollup/rollup-android-arm-eabi@4.9.4: + resolution: {integrity: sha512-ub/SN3yWqIv5CWiAZPHVS1DloyZsJbtXmX4HxUTIpS0BHm9pW5iYBo2mIZi+hE3AeiTzHz33blwSnhdUo+9NpA==} cpu: [arm] os: [android] requiresBuild: true dev: true optional: true - /@rollup/rollup-android-arm64@4.9.3: - resolution: {integrity: sha512-kffYCJ2RhDL1DlshLzYPyJtVeusHlA8Q1j6k6s4AEVKLq/3HfGa2ADDycLsmPo3OW83r4XtOPqRMbcFzFsEIzQ==} + /@rollup/rollup-android-arm64@4.9.4: + resolution: {integrity: sha512-ehcBrOR5XTl0W0t2WxfTyHCR/3Cq2jfb+I4W+Ch8Y9b5G+vbAecVv0Fx/J1QKktOrgUYsIKxWAKgIpvw56IFNA==} cpu: [arm64] os: [android] requiresBuild: true dev: true optional: true - /@rollup/rollup-darwin-arm64@4.9.3: - resolution: {integrity: sha512-Fo7DR6Q9/+ztTyMBZ79+WJtb8RWZonyCgkBCjV51rW5K/dizBzImTW6HLC0pzmHaAevwM0jW1GtB5LCFE81mSw==} + /@rollup/rollup-darwin-arm64@4.9.4: + resolution: {integrity: sha512-1fzh1lWExwSTWy8vJPnNbNM02WZDS8AW3McEOb7wW+nPChLKf3WG2aG7fhaUmfX5FKw9zhsF5+MBwArGyNM7NA==} cpu: [arm64] os: [darwin] requiresBuild: true dev: true optional: true - /@rollup/rollup-darwin-x64@4.9.3: - resolution: {integrity: sha512-5HcxDF9fqHucIlTiw/gmMb3Qv23L8bLCg904I74Q2lpl4j/20z9ogaD3tWkeguRuz+/17cuS321PT3PAuyjQdg==} + /@rollup/rollup-darwin-x64@4.9.4: + resolution: {integrity: sha512-Gc6cukkF38RcYQ6uPdiXi70JB0f29CwcQ7+r4QpfNpQFVHXRd0DfWFidoGxjSx1DwOETM97JPz1RXL5ISSB0pA==} cpu: [x64] os: [darwin] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.9.3: - resolution: {integrity: sha512-cO6hKV+99D1V7uNJQn1chWaF9EGp7qV2N8sGH99q9Y62bsbN6Il55EwJppEWT+JiqDRg396vWCgwdHwje8itBQ==} + /@rollup/rollup-linux-arm-gnueabihf@4.9.4: + resolution: {integrity: sha512-g21RTeFzoTl8GxosHbnQZ0/JkuFIB13C3T7Y0HtKzOXmoHhewLbVTFBQZu+z5m9STH6FZ7L/oPgU4Nm5ErN2fw==} cpu: [arm] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm64-gnu@4.9.3: - resolution: {integrity: sha512-xANyq6lVg6KMO8UUs0LjA4q7di3tPpDbzLPgVEU2/F1ngIZ54eli8Zdt3uUUTMXVbgTCafIO+JPeGMhu097i3w==} + /@rollup/rollup-linux-arm64-gnu@4.9.4: + resolution: {integrity: sha512-TVYVWD/SYwWzGGnbfTkrNpdE4HON46orgMNHCivlXmlsSGQOx/OHHYiQcMIOx38/GWgwr/po2LBn7wypkWw/Mg==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm64-musl@4.9.3: - resolution: {integrity: sha512-TZJUfRTugVFATQToCMD8DNV6jv/KpSwhE1lLq5kXiQbBX3Pqw6dRKtzNkh5wcp0n09reBBq/7CGDERRw9KmE+g==} + /@rollup/rollup-linux-arm64-musl@4.9.4: + resolution: {integrity: sha512-XcKvuendwizYYhFxpvQ3xVpzje2HHImzg33wL9zvxtj77HvPStbSGI9czrdbfrf8DGMcNNReH9pVZv8qejAQ5A==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-riscv64-gnu@4.9.3: - resolution: {integrity: sha512-4/QVaRyaB5tkEAGfjVvWrmWdPF6F2NoaoO5uEP7N0AyeBw7l8SeCWWKAGrbx/00PUdHrJVURJiYikazslSKttQ==} + /@rollup/rollup-linux-riscv64-gnu@4.9.4: + resolution: {integrity: sha512-LFHS/8Q+I9YA0yVETyjonMJ3UA+DczeBd/MqNEzsGSTdNvSJa1OJZcSH8GiXLvcizgp9AlHs2walqRcqzjOi3A==} cpu: [riscv64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-x64-gnu@4.9.3: - resolution: {integrity: sha512-koLC6D3pj1YLZSkTy/jsk3HOadp7q2h6VQl/lPX854twOmmLNekHB6yuS+MkWcKdGGdW1JPuPBv/ZYhr5Yhtdg==} + /@rollup/rollup-linux-x64-gnu@4.9.4: + resolution: {integrity: sha512-dIYgo+j1+yfy81i0YVU5KnQrIJZE8ERomx17ReU4GREjGtDW4X+nvkBak2xAUpyqLs4eleDSj3RrV72fQos7zw==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-x64-musl@4.9.3: - resolution: {integrity: sha512-0OAkQ4HBp+JO2ip2Lgt/ShlrveOMzyhwt2D0KvqH28jFPqfZco28KSq76zymZwmU+F6GRojdxtQMJiNSXKNzeA==} + /@rollup/rollup-linux-x64-musl@4.9.4: + resolution: {integrity: sha512-RoaYxjdHQ5TPjaPrLsfKqR3pakMr3JGqZ+jZM0zP2IkDtsGa4CqYaWSfQmZVgFUCgLrTnzX+cnHS3nfl+kB6ZQ==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-arm64-msvc@4.9.3: - resolution: {integrity: sha512-z5uvoMvdRWggigOnsb9OOCLERHV0ykRZoRB5O+URPZC9zM3pkoMg5fN4NKu2oHqgkzZtfx9u4njqqlYEzM1v9A==} + /@rollup/rollup-win32-arm64-msvc@4.9.4: + resolution: {integrity: sha512-T8Q3XHV+Jjf5e49B4EAaLKV74BbX7/qYBRQ8Wop/+TyyU0k+vSjiLVSHNWdVd1goMjZcbhDmYZUYW5RFqkBNHQ==} cpu: [arm64] os: [win32] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-ia32-msvc@4.9.3: - resolution: {integrity: sha512-wxomCHjBVKws+O4N1WLnniKCXu7vkLtdq9Fl9CN/EbwEldojvUrkoHE/fBLZzC7IT/x12Ut6d6cRs4dFvqJkMg==} + /@rollup/rollup-win32-ia32-msvc@4.9.4: + resolution: {integrity: sha512-z+JQ7JirDUHAsMecVydnBPWLwJjbppU+7LZjffGf+Jvrxq+dVjIE7By163Sc9DKc3ADSU50qPVw0KonBS+a+HQ==} cpu: [ia32] os: [win32] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-x64-msvc@4.9.3: - resolution: {integrity: sha512-1Qf/qk/iEtx0aOi+AQQt5PBoW0mFngsm7bPuxHClC/hWh2hHBktR6ktSfUg5b5rC9v8hTwNmHE7lBWXkgqluUQ==} + /@rollup/rollup-win32-x64-msvc@4.9.4: + resolution: {integrity: sha512-LfdGXCV9rdEify1oxlN9eamvDSjv9md9ZVMAbNHA87xqIfFCxImxan9qZ8+Un54iK2nnqPlbnSi4R54ONtbWBw==} cpu: [x64] os: [win32] requiresBuild: true @@ -726,6 +745,10 @@ packages: espree: 9.6.1 dev: true + /@types/chai@4.3.11: + resolution: {integrity: sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ==} + dev: true + /@types/estree@1.0.5: resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} @@ -737,6 +760,27 @@ packages: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} dev: true + /@types/linkify-it@3.0.5: + resolution: {integrity: sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==} + dev: true + + /@types/markdown-it@12.2.3: + resolution: {integrity: sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==} + dependencies: + '@types/linkify-it': 3.0.5 + '@types/mdurl': 1.0.5 + dev: true + + /@types/mdurl@1.0.5: + resolution: {integrity: sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==} + dev: true + + /@types/node@20.10.8: + resolution: {integrity: sha512-f8nQs3cLxbAFc00vEU59yf9UyGUftkPaLGfvbVOIDdx2i1b8epBqj2aNGyP19fiyXWvlmZ7qC1XLjAzw/OKIeA==} + dependencies: + undici-types: 5.26.5 + dev: true + /@types/semver@7.5.6: resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==} dev: true @@ -821,7 +865,7 @@ packages: magicast: 0.3.2 picocolors: 1.0.0 test-exclude: 6.0.0 - vitest: 1.1.3(@vitest/ui@1.1.3) + vitest: 1.1.3(@types/node@20.10.8)(@vitest/ui@1.1.3) transitivePeerDependencies: - supports-color dev: true @@ -844,7 +888,7 @@ packages: std-env: 3.7.0 test-exclude: 6.0.0 v8-to-istanbul: 9.2.0 - vitest: 1.1.3(@vitest/ui@1.1.3) + vitest: 1.1.3(@types/node@20.10.8)(@vitest/ui@1.1.3) transitivePeerDependencies: - supports-color dev: true @@ -891,7 +935,7 @@ packages: pathe: 1.1.1 picocolors: 1.0.0 sirv: 2.0.4 - vitest: 1.1.3(@vitest/ui@1.1.3) + vitest: 1.1.3(@types/node@20.10.8)(@vitest/ui@1.1.3) dev: true /@vitest/utils@1.1.3: @@ -977,6 +1021,10 @@ packages: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true + /bluebird@3.7.2: + resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} + dev: true + /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: @@ -1027,6 +1075,13 @@ packages: resolution: {integrity: sha512-BtYEK4r/iHt/txm81KBudCUcTy7t+s9emrIaHqjYurQ10x71zJ5VQ9x1dYPcz/b+pKSp4y/v1xSI67A+LzpNyg==} dev: true + /catharsis@0.9.0: + resolution: {integrity: sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==} + engines: {node: '>= 10'} + dependencies: + lodash: 4.17.21 + dev: true + /chai@4.4.0: resolution: {integrity: sha512-x9cHNq1uvkCdU+5xTkNh5WtgD4e4yDFCsp9jVc7N7qVeKeftv3gO/ZrviX5d+3ZfxdYnZXZYujjRInu1RogU6A==} engines: {node: '>=4'} @@ -1152,6 +1207,10 @@ packages: resolution: {integrity: sha512-lKoz10iCYlP1WtRYdh5MvocQPWVRoI7ysp6qf18bmeBgR8abE6+I2CsfyNKztRDZvhdWc+krKT6wS7Neg8sw3A==} dev: true + /entities@2.1.0: + resolution: {integrity: sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==} + dev: true + /esbuild@0.19.11: resolution: {integrity: sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==} engines: {node: '>=12'} @@ -1193,6 +1252,11 @@ packages: engines: {node: '>=0.8.0'} dev: true + /escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + dev: true + /escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -1233,7 +1297,7 @@ packages: dependencies: '@typescript-eslint/utils': 6.17.0(eslint@8.56.0)(typescript@5.3.3) eslint: 8.56.0 - vitest: 1.1.3(@vitest/ui@1.1.3) + vitest: 1.1.3(@types/node@20.10.8)(@vitest/ui@1.1.3) transitivePeerDependencies: - supports-color - typescript @@ -1500,6 +1564,10 @@ packages: slash: 3.0.0 dev: true + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: true + /graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} dev: true @@ -1660,6 +1728,12 @@ packages: argparse: 2.0.1 dev: true + /js2xmlparser@4.0.2: + resolution: {integrity: sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==} + dependencies: + xmlcreate: 2.0.4 + dev: true + /jsdoc-cli-wrapper@1.0.4: resolution: {integrity: sha512-JzdBSsLkS5Q8BIaO8b9SC3kfUc7NsHc7egpJLJGqQQsm+X4rXooG0hClwGVm1LVNuca3bpeq/Mw99BcUiD4rQw==} engines: {node: '>= 18.0.0'} @@ -1671,6 +1745,28 @@ packages: engines: {node: '>=12.0.0'} dev: true + /jsdoc@4.0.2: + resolution: {integrity: sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==} + engines: {node: '>=12.0.0'} + hasBin: true + dependencies: + '@babel/parser': 7.23.6 + '@jsdoc/salty': 0.2.7 + '@types/markdown-it': 12.2.3 + bluebird: 3.7.2 + catharsis: 0.9.0 + escape-string-regexp: 2.0.0 + js2xmlparser: 4.0.2 + klaw: 3.0.0 + markdown-it: 12.3.2 + markdown-it-anchor: 8.6.7(@types/markdown-it@12.2.3)(markdown-it@12.3.2) + marked: 4.3.0 + mkdirp: 1.0.4 + requizzle: 0.2.4 + strip-json-comments: 3.1.1 + underscore: 1.13.6 + dev: true + /jsesc@2.5.2: resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} engines: {node: '>=4'} @@ -1705,6 +1801,12 @@ packages: json-buffer: 3.0.1 dev: true + /klaw@3.0.0: + resolution: {integrity: sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==} + dependencies: + graceful-fs: 4.2.11 + dev: true + /levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -1713,6 +1815,12 @@ packages: type-check: 0.4.0 dev: true + /linkify-it@3.0.3: + resolution: {integrity: sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==} + dependencies: + uc.micro: 1.0.6 + dev: true + /local-pkg@0.5.0: resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} engines: {node: '>=14'} @@ -1732,6 +1840,10 @@ packages: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: true + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true + /loupe@2.3.7: resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} dependencies: @@ -1773,6 +1885,37 @@ packages: semver: 7.5.4 dev: true + /markdown-it-anchor@8.6.7(@types/markdown-it@12.2.3)(markdown-it@12.3.2): + resolution: {integrity: sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==} + peerDependencies: + '@types/markdown-it': '*' + markdown-it: '*' + dependencies: + '@types/markdown-it': 12.2.3 + markdown-it: 12.3.2 + dev: true + + /markdown-it@12.3.2: + resolution: {integrity: sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==} + hasBin: true + dependencies: + argparse: 2.0.1 + entities: 2.1.0 + linkify-it: 3.0.3 + mdurl: 1.0.1 + uc.micro: 1.0.6 + dev: true + + /marked@4.3.0: + resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==} + engines: {node: '>= 12'} + hasBin: true + dev: true + + /mdurl@1.0.1: + resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} + dev: true + /merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} dev: true @@ -1812,6 +1955,12 @@ packages: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} dev: false + /mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + dev: true + /mlly@1.4.2: resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==} dependencies: @@ -1993,6 +2142,12 @@ packages: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} dev: true + /requizzle@0.2.4: + resolution: {integrity: sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==} + dependencies: + lodash: 4.17.21 + dev: true + /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -2010,26 +2165,26 @@ packages: glob: 7.2.3 dev: true - /rollup@4.9.3: - resolution: {integrity: sha512-JnchF0ZGFiqGpAPjg3e89j656Ne4tTtCY1VZc1AxtoQcRIxjTu9jyYHBAtkDXE+X681n4un/nX9SU52AroSRzg==} + /rollup@4.9.4: + resolution: {integrity: sha512-2ztU7pY/lrQyXSCnnoU4ICjT/tCG9cdH3/G25ERqE3Lst6vl2BCM5hL2Nw+sslAvAf+ccKsAq1SkKQALyqhR7g==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true dependencies: '@types/estree': 1.0.5 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.9.3 - '@rollup/rollup-android-arm64': 4.9.3 - '@rollup/rollup-darwin-arm64': 4.9.3 - '@rollup/rollup-darwin-x64': 4.9.3 - '@rollup/rollup-linux-arm-gnueabihf': 4.9.3 - '@rollup/rollup-linux-arm64-gnu': 4.9.3 - '@rollup/rollup-linux-arm64-musl': 4.9.3 - '@rollup/rollup-linux-riscv64-gnu': 4.9.3 - '@rollup/rollup-linux-x64-gnu': 4.9.3 - '@rollup/rollup-linux-x64-musl': 4.9.3 - '@rollup/rollup-win32-arm64-msvc': 4.9.3 - '@rollup/rollup-win32-ia32-msvc': 4.9.3 - '@rollup/rollup-win32-x64-msvc': 4.9.3 + '@rollup/rollup-android-arm-eabi': 4.9.4 + '@rollup/rollup-android-arm64': 4.9.4 + '@rollup/rollup-darwin-arm64': 4.9.4 + '@rollup/rollup-darwin-x64': 4.9.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.9.4 + '@rollup/rollup-linux-arm64-gnu': 4.9.4 + '@rollup/rollup-linux-arm64-musl': 4.9.4 + '@rollup/rollup-linux-riscv64-gnu': 4.9.4 + '@rollup/rollup-linux-x64-gnu': 4.9.4 + '@rollup/rollup-linux-x64-musl': 4.9.4 + '@rollup/rollup-win32-arm64-msvc': 4.9.4 + '@rollup/rollup-win32-ia32-msvc': 4.9.4 + '@rollup/rollup-win32-x64-msvc': 4.9.4 fsevents: 2.3.3 dev: true @@ -2232,6 +2387,10 @@ packages: hasBin: true dev: true + /uc.micro@1.0.6: + resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==} + dev: true + /ufo@1.3.2: resolution: {integrity: sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==} dev: true @@ -2244,6 +2403,14 @@ packages: dev: false optional: true + /underscore@1.13.6: + resolution: {integrity: sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==} + dev: true + + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: true + /update-browserslist-db@1.0.13(browserslist@4.22.2): resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} hasBin: true @@ -2270,7 +2437,7 @@ packages: convert-source-map: 2.0.0 dev: true - /vite-node@1.1.3: + /vite-node@1.1.3(@types/node@20.10.8): resolution: {integrity: sha512-BLSO72YAkIUuNrOx+8uznYICJfTEbvBAmWClY3hpath5+h1mbPS5OMn42lrTxXuyCazVyZoDkSRnju78GiVCqA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -2279,7 +2446,7 @@ packages: debug: 4.3.4 pathe: 1.1.1 picocolors: 1.0.0 - vite: 5.0.11 + vite: 5.0.11(@types/node@20.10.8) transitivePeerDependencies: - '@types/node' - less @@ -2291,7 +2458,7 @@ packages: - terser dev: true - /vite@5.0.11: + /vite@5.0.11(@types/node@20.10.8): resolution: {integrity: sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -2319,14 +2486,15 @@ packages: terser: optional: true dependencies: + '@types/node': 20.10.8 esbuild: 0.19.11 postcss: 8.4.33 - rollup: 4.9.3 + rollup: 4.9.4 optionalDependencies: fsevents: 2.3.3 dev: true - /vitest@1.1.3(@vitest/ui@1.1.3): + /vitest@1.1.3(@types/node@20.10.8)(@vitest/ui@1.1.3): resolution: {integrity: sha512-2l8om1NOkiA90/Y207PsEvJLYygddsOyr81wLQ20Ra8IlLKbyQncWsGZjnbkyG2KwwuTXLQjEPOJuxGMG8qJBQ==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -2351,6 +2519,7 @@ packages: jsdom: optional: true dependencies: + '@types/node': 20.10.8 '@vitest/expect': 1.1.3 '@vitest/runner': 1.1.3 '@vitest/snapshot': 1.1.3 @@ -2370,8 +2539,8 @@ packages: strip-literal: 1.3.0 tinybench: 2.5.1 tinypool: 0.8.1 - vite: 5.0.11 - vite-node: 1.1.3 + vite: 5.0.11(@types/node@20.10.8) + vite-node: 1.1.3(@types/node@20.10.8) why-is-node-running: 2.2.2 transitivePeerDependencies: - less @@ -2408,6 +2577,10 @@ packages: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true + /xmlcreate@2.0.4: + resolution: {integrity: sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==} + dev: true + /yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} dev: true diff --git a/test/plugin-impl.test.js b/test/plugin-impl.test.js index 1ad3b36..def4c2b 100644 --- a/test/plugin-impl.test.js +++ b/test/plugin-impl.test.js @@ -92,6 +92,12 @@ describe('PluginImpl', () => { const templateStr = '

Hello, {{ recipient }}

' + /** + * @param {string} prefix - generated code before the precompiled template + * @returns {any} - a Vitest matcher that returns true if it matches a + * source map 'mappings' string that skips the number of lines in prefix + * @see https://vitest.dev/api/expect.html#expect-stringmatching + */ const mappingSkipsPrefix = (prefix) => { // Really? All the methods on String and Array, and no .count()? const numLines = prefix.length - prefix.replaceAll('\n', '').length @@ -122,7 +128,7 @@ describe('PluginImpl', () => { test('emits precompiled template module and source map', () => { const impl = new PluginImpl() - const { code, map } = impl.compile(templateStr, 'foo.hbs') + const { code, map } = impl.compile(templateStr, 'foo.hbs') ?? {} const expectedPrefix = `${PREFIX}\n${BEGIN_TEMPLATE}` expect(code).toStartWith(expectedPrefix) @@ -137,7 +143,7 @@ describe('PluginImpl', () => { test.each(['sourceMap', 'sourcemap'])('options.%s === false', key => { const impl = new PluginImpl({ [key]: false }) - const { map } = impl.compile(templateStr, 'foo.hbs') + const { map } = impl.compile(templateStr, 'foo.hbs') ?? {} expect(map).toStrictEqual({ mappings: '' }) }) @@ -148,7 +154,7 @@ describe('PluginImpl', () => { compiler: { srcName: 'bar/baz.handlebars', destName: 'quux/xyzzy.js' } }) - const { map } = impl.compile(templateStr, 'foo.hbs') + const { map } = impl.compile(templateStr, 'foo.hbs') ?? {} expect(map).toHaveProperty('sources', [ 'foo.hbs' ]) expect(map).not.toHaveProperty('file') @@ -160,7 +166,7 @@ describe('PluginImpl', () => { test('DEFAULT_PARTIAL_PATH', () => { const impl = new PluginImpl() - const { code, map } = impl.compile(templateStr, 'foo.hbs') + const { code, map } = impl.compile(templateStr, 'foo.hbs') ?? {} const expectedPrefix = [ PREFIX, @@ -182,7 +188,7 @@ describe('PluginImpl', () => { partialPath: (partialName) => `./${partialName}.partial.hbs` }) - const { code } = impl.compile(templateStr, 'foo.hbs') + const { code } = impl.compile(templateStr, 'foo.hbs') ?? {} const expectedPrefix = [ PREFIX, @@ -199,7 +205,7 @@ describe('PluginImpl', () => { test('DEFAULT_PARTIALS filter and DEFAULT_PARTIAL_NAME', () => { const impl = new PluginImpl() - const { code } = impl.compile(templateStr, '_foo.hbs') + const { code } = impl.compile(templateStr, '_foo.hbs') ?? {} const expected = 'Handlebars.registerPartial(\'foo\', RawTemplate)' expect(code).toEndWith(`${SUFFIX}\n${expected}`) @@ -211,7 +217,7 @@ describe('PluginImpl', () => { partialName(id) { return id.replace(/\.partial\.hbs$/, '') } }) - const { code } = impl.compile(templateStr, 'foo.partial.hbs') + const { code } = impl.compile(templateStr, 'foo.partial.hbs') ?? {} const expected = 'Handlebars.registerPartial(\'foo\', RawTemplate)' expect(code).toEndWith(`${SUFFIX}\n${expected}`) diff --git a/test/vitest.d.ts b/test/vitest.d.ts new file mode 100644 index 0000000..6048049 --- /dev/null +++ b/test/vitest.d.ts @@ -0,0 +1,18 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +// Based on: https://vitest.dev/guide/extending-matchers.html + +import type { Assertion, AsymmetricMatchersContaining } from 'vitest' + +interface CustomMatchers { + toStartWith(string): R + toEndWith(string): R +} + +declare module 'vitest' { + interface Assertion extends CustomMatchers {} +} diff --git a/vitest.config.js b/vitest.config.js index 66c1fe8..2245b8c 100644 --- a/vitest.config.js +++ b/vitest.config.js @@ -1,3 +1,4 @@ +// @ts-nocheck import { defineConfig, configDefaults } from 'vitest/config' export default defineConfig({