From 685b69538841d56e689d31dc10560ea980b44bd8 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 5 May 2026 15:49:55 +0000 Subject: [PATCH 1/7] feat: add `filter` helper to dispatch assets per-minimizer Each built-in minimizer now exposes a `filter(name, info)` helper based on file extension. When `minify` is an array, each asset is dispatched only to the minimizers whose `filter` accepts it; chain semantics still apply when multiple minimizers claim the same asset. The `test` option defaults to `undefined` for the array form so that filters can decide. This lets a single `TerserPlugin` instance handle JS/CSS/HTML/JSON with one shared worker pool instead of forcing a separate plugin instance per type. --- README.md | 71 ++++++++- src/index.js | 88 ++++++++++- src/utils.js | 90 +++++++++++ test/minimizer-filter.test.js | 282 ++++++++++++++++++++++++++++++++++ types/index.d.ts | 6 + types/utils.d.ts | 75 +++++++++ 6 files changed, 599 insertions(+), 13 deletions(-) create mode 100644 test/minimizer-filter.test.js diff --git a/README.md b/README.md index 1265c25..dbf9178 100644 --- a/README.md +++ b/README.md @@ -321,7 +321,34 @@ Allows you to override the default minify function. By default plugin uses [terser](https://github.com/terser/terser) package. Useful for using and testing unpublished versions or forks. -An array of functions can also be provided to chain multiple minimizers — the output of each minimizer is fed as input to the next. When an array is used, the [`minimizerOptions`](#minimizeroptions) option may also be an array (index-paired with `minify`) or a single object that is reused for every minimizer. +An array of functions can also be provided. Each minimizer can expose a +`filter(name, info)` helper that decides whether it should run on a given +asset; the plugin dispatches each asset only to the minimizers whose `filter` +accepts it (or runs them all when no filter is set). All built-in minimizers +ship with a `filter` that matches their natural extension, so a single plugin +instance and a single worker pool can handle JS, CSS, HTML and JSON together +without juggling multiple `TerserPlugin` instances: + +```js +new TerserPlugin({ + minify: [ + TerserPlugin.terserMinify, + TerserPlugin.cssnanoMinify, + TerserPlugin.htmlMinifierTerser, + TerserPlugin.jsonMinify, + ], +}); +``` + +When more than one minimizer in the array claims the same asset, the chain +semantic still applies: the output of each accepting minimizer is fed as +input to the next. The [`minimizerOptions`](#minimizeroptions) option may +be an array (index-paired with `minify`) or a single object reused by every +minimizer. + +When `minify` is an array, the `test` option defaults to `undefined` so each +minimizer's `filter` decides which assets it processes. With a single `minify` +function, `test` keeps its JS-only default of `/\.[cm]?js(\?.*)?$/i`. > **Warning** > @@ -362,6 +389,12 @@ minify.getMinimizerVersion = () => { return packageJson && packageJson.version; }; +// Restrict the minimizer to the assets it can actually handle. The plugin +// skips assets for which `filter` returns `false` and (when an array of +// minimizers is used) dispatches each asset only to the minimizers that +// accept it. Returning `undefined` is treated as accept. +minify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name); + module.exports = { optimization: { minimize: true, @@ -379,11 +412,13 @@ module.exports = { #### `array` -If an array of functions is passed to the `minify` option, the output of each -minimizer is fed as input to the next one. The `minimizerOptions` option can be -either an array of option objects (index-paired with `minify`) or a single -object that will be shared by all minimizers. Warnings, errors and extracted -comments from all minimizers are merged together. +If an array of functions is passed to the `minify` option, each asset is +dispatched to the minimizers whose `filter` accepts it. When more than one +minimizer accepts the same asset the output of each is fed as input to the +next one (the chain semantic). The `minimizerOptions` option can be either an +array of option objects (index-paired with `minify`) or a single object that +will be shared by all minimizers. Warnings, errors and extracted comments +from all running minimizers are merged together. **webpack.config.js** @@ -407,6 +442,30 @@ module.exports = { }; ``` +A single plugin instance can also handle multiple asset types — the built-in +minimizers each ship with a `filter` matching their natural extension, so JS, +CSS, HTML and JSON can all be minified by one shared worker pool: + +```js +module.exports = { + optimization: { + minimize: true, + minimizer: [ + new TerserPlugin({ + // `test` defaults to `undefined` here so each filter decides which + // assets it handles. Override `test` to narrow further if needed. + minify: [ + TerserPlugin.terserMinify, + TerserPlugin.cssnanoMinify, + TerserPlugin.htmlMinifierTerser, + TerserPlugin.jsonMinify, + ], + }), + ], + }, +}; +``` + ### `minimizerOptions` Type: diff --git a/src/index.js b/src/index.js index 64a7e09..0ead03c 100644 --- a/src/index.js +++ b/src/index.js @@ -124,6 +124,7 @@ const { * @property {() => string | undefined=} getMinimizerVersion function that returns version of minimizer * @property {() => boolean | undefined=} supportsWorkerThreads true when minimizer support worker threads, otherwise false * @property {() => boolean | undefined=} supportsWorker true when minimizer support worker, otherwise false + * @property {(name: string, info?: AssetInfo) => boolean | undefined=} filter return true when the minimizer supports the asset, otherwise false. When an array of minimizers is configured, each asset is dispatched only to the minimizers whose `filter` accepts it. Assets rejected by every minimizer in the array are skipped entirely. */ /** @@ -189,13 +190,17 @@ class TerserPlugin { // TODO handle json and etc in the next major release // TODO make `minimizer` option instead `minify` and `terserOptions` in the next major release, also rename `terserMinify` to `terserMinimize` + // When an array of minimizers is supplied, leave `test` undefined so that + // each minimizer's own `filter` decides which assets it processes; + // otherwise fall back to the JS-only default. + const isMinifyArray = Array.isArray(options && options.minify); const { minify = /** @type {MinimizerImplementation} */ ( /** @type {unknown} */ (terserMinify) ), minimizerOptions, terserOptions, - test = /\.[cm]?js(\?.*)?$/i, + test = isMinifyArray ? undefined : /\.[cm]?js(\?.*)?$/i, extractComments = true, parallel = true, include, @@ -377,6 +382,37 @@ class TerserPlugin { async optimize(compiler, compilation, assets, optimizeOptions) { const cache = compilation.getCache("TerserWebpackPlugin"); let numberOfAssets = 0; + const allImplementations = Array.isArray( + this.options.minimizer.implementation, + ) + ? this.options.minimizer.implementation + : null; + /** + * Apply each minimizer's `filter` helper. For array form, an asset is + * accepted when at least one configured minimizer accepts it; for the + * single form, the asset must be accepted by that minimizer. + * @param {string} name asset name + * @param {AssetInfo} info asset info + * @returns {boolean} whether at least one minimizer accepts this asset + */ + const isAcceptedByAnyMinimizer = (name, info) => { + if (allImplementations) { + return allImplementations.some( + (impl) => + typeof impl.filter !== "function" || + // eslint-disable-next-line unicorn/no-array-method-this-argument + impl.filter(name, info) !== false, + ); + } + const single = + /** @type {BasicMinimizerImplementation & MinimizeFunctionHelpers} */ + (this.options.minimizer.implementation); + return ( + typeof single.filter !== "function" || + // eslint-disable-next-line unicorn/no-array-method-this-argument + single.filter(name, info) !== false + ); + }; const assetsForMinify = await Promise.all( Object.keys(assets) .filter((name) => { @@ -400,6 +436,10 @@ class TerserPlugin { return false; } + if (!isAcceptedByAnyMinimizer(name, info)) { + return false; + } + return true; }) .map(async (name) => { @@ -523,11 +563,45 @@ class TerserPlugin { input = input.toString(); } - const clonedMinimizerOptions = Array.isArray( - this.options.minimizer.options, - ) - ? this.options.minimizer.options.map((item) => ({ ...item })) - : { .../** @type {T} */ (this.options.minimizer.options) }; + // Dispatch to only the minimizers whose `filter` accepts this asset. + // For the single-implementation form `assetImplementation` matches + // the original; for the array form it's the matching subset (still + // an array, even when only one entry remains, so chained behavior + // is preserved when multiple match). + let assetImplementation = this.options.minimizer.implementation; + let assetMinimizerOptions = this.options.minimizer.options; + + if (allImplementations) { + const matchedIndices = []; + + for (let i = 0; i < allImplementations.length; i++) { + const impl = allImplementations[i]; + + if ( + typeof impl.filter !== "function" || + // eslint-disable-next-line unicorn/no-array-method-this-argument + impl.filter(name, info) !== false + ) { + matchedIndices.push(i); + } + } + + assetImplementation = + /** @type {MinimizerImplementation} */ + (matchedIndices.map((i) => allImplementations[i])); + + if (Array.isArray(this.options.minimizer.options)) { + const sourceOptions = this.options.minimizer.options; + + assetMinimizerOptions = + /** @type {MinimizerOptions} */ + (matchedIndices.map((i) => sourceOptions[i] || {})); + } + } + + const clonedMinimizerOptions = Array.isArray(assetMinimizerOptions) + ? assetMinimizerOptions.map((item) => ({ ...item })) + : { .../** @type {T} */ (assetMinimizerOptions) }; /** * @type {InternalOptions} @@ -537,7 +611,7 @@ class TerserPlugin { input, inputSourceMap, minimizer: { - implementation: this.options.minimizer.implementation, + implementation: assetImplementation, options: /** @type {MinimizerOptions} */ (clonedMinimizerOptions), diff --git a/src/utils.js b/src/utils.js index 86933fc..efe055e 100644 --- a/src/utils.js +++ b/src/utils.js @@ -355,6 +355,12 @@ terserMinify.getMinimizerVersion = () => { */ terserMinify.supportsWorkerThreads = () => true; +/** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like a JavaScript file + */ +terserMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name); + /* istanbul ignore next */ /** * @param {Input} input input @@ -586,6 +592,12 @@ uglifyJsMinify.getMinimizerVersion = () => { */ uglifyJsMinify.supportsWorkerThreads = () => true; +/** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like a JavaScript file + */ +uglifyJsMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name); + /* istanbul ignore next */ /** * @param {Input} input input @@ -817,6 +829,12 @@ swcMinify.getMinimizerVersion = () => { */ swcMinify.supportsWorkerThreads = () => false; +/** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like a JavaScript file + */ +swcMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name); + /* istanbul ignore next */ /** * @param {Input} input input @@ -932,6 +950,12 @@ esbuildMinify.getMinimizerVersion = () => { */ esbuildMinify.supportsWorkerThreads = () => false; +/** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like a JavaScript file + */ +esbuildMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name); + /* istanbul ignore next */ /** * @param {Input} input input @@ -958,6 +982,12 @@ jsonMinify.getMinimizerVersion = () => "1.0.0"; jsonMinify.supportsWorker = () => false; jsonMinify.supportsWorkerThreads = () => false; +/** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like a JSON file + */ +jsonMinify.filter = (name) => /\.json(\?.*)?$/i.test(name); + /* istanbul ignore next */ /** * Minify HTML using `html-minifier-terser`. @@ -1024,6 +1054,12 @@ htmlMinifierTerser.getMinimizerVersion = () => { */ htmlMinifierTerser.supportsWorkerThreads = () => true; +/** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like an HTML file + */ +htmlMinifierTerser.filter = (name) => /\.html?(\?.*)?$/i.test(name); + /* istanbul ignore next */ /** * Minify HTML using `@minify-html/node`. @@ -1071,6 +1107,12 @@ minifyHtmlNode.getMinimizerVersion = () => { */ minifyHtmlNode.supportsWorkerThreads = () => false; +/** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like an HTML file + */ +minifyHtmlNode.filter = (name) => /\.html?(\?.*)?$/i.test(name); + /* istanbul ignore next */ /** * Map an `@swc/html` diagnostic to a regular `Error`. @@ -1141,6 +1183,12 @@ swcMinifyHtml.getMinimizerVersion = () => { */ swcMinifyHtml.supportsWorkerThreads = () => false; +/** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like an HTML file + */ +swcMinifyHtml.filter = (name) => /\.html?(\?.*)?$/i.test(name); + /* istanbul ignore next */ /** * Minify an HTML fragment using `@swc/html`. @@ -1195,6 +1243,12 @@ swcMinifyHtmlFragment.getMinimizerVersion = () => { */ swcMinifyHtmlFragment.supportsWorkerThreads = () => false; +/** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like an HTML file + */ +swcMinifyHtmlFragment.filter = (name) => /\.html?(\?.*)?$/i.test(name); + /* istanbul ignore next */ /** * Minify CSS using `cssnano` (via `postcss`). @@ -1341,6 +1395,12 @@ cssnanoMinify.getMinimizerVersion = () => { */ cssnanoMinify.supportsWorkerThreads = () => true; +/** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like a CSS file + */ +cssnanoMinify.filter = (name) => /\.css(\?.*)?$/i.test(name); + /* istanbul ignore next */ /** * Minify CSS using `csso`. @@ -1395,6 +1455,12 @@ cssoMinify.getMinimizerVersion = () => { */ cssoMinify.supportsWorkerThreads = () => true; +/** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like a CSS file + */ +cssoMinify.filter = (name) => /\.css(\?.*)?$/i.test(name); + /* istanbul ignore next */ /** * Minify CSS using `clean-css`. @@ -1467,6 +1533,12 @@ cleanCssMinify.getMinimizerVersion = () => { */ cleanCssMinify.supportsWorkerThreads = () => true; +/** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like a CSS file + */ +cleanCssMinify.filter = (name) => /\.css(\?.*)?$/i.test(name); + /* istanbul ignore next */ /** * Minify CSS using `esbuild` (with the CSS loader). @@ -1582,6 +1654,12 @@ esbuildMinifyCss.getMinimizerVersion = () => { */ esbuildMinifyCss.supportsWorkerThreads = () => false; +/** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like a CSS file + */ +esbuildMinifyCss.filter = (name) => /\.css(\?.*)?$/i.test(name); + /* istanbul ignore next */ /** * Minify CSS using `lightningcss`. @@ -1651,6 +1729,12 @@ lightningCssMinify.getMinimizerVersion = () => { */ lightningCssMinify.supportsWorkerThreads = () => false; +/** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like a CSS file + */ +lightningCssMinify.filter = (name) => /\.css(\?.*)?$/i.test(name); + /* istanbul ignore next */ /** * Map a `@swc/css` diagnostic to a regular `Error`. @@ -1735,6 +1819,12 @@ swcMinifyCss.getMinimizerVersion = () => { */ swcMinifyCss.supportsWorkerThreads = () => false; +/** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like a CSS file + */ +swcMinifyCss.filter = (name) => /\.css(\?.*)?$/i.test(name); + /** * @template T * @typedef {() => T} FunctionReturning diff --git a/test/minimizer-filter.test.js b/test/minimizer-filter.test.js new file mode 100644 index 0000000..2fc6707 --- /dev/null +++ b/test/minimizer-filter.test.js @@ -0,0 +1,282 @@ +import path from "path"; + +import TerserPlugin from "../src"; + +import { + EmitNewAsset, + compile, + getCompiler, + getErrors, + getWarnings, + readAsset, + readsAssets, +} from "./helpers"; + +// jest.fn() can't be used directly because schema-utils calls +// `value instanceof Function` on each minify entry, and the mock function +// is constructed in a different VM context than the schema-utils Function +// global, so the instanceof check fails. Wrap a plain function instead and +// track calls manually. +function makeMinify(impl) { + /** + * @param {...EXPECTED_ANY} args minify arguments + * @returns {EXPECTED_ANY} result + */ + function fn(...args) { + fn.calls.push(args); + return impl(...args); + } + fn.calls = []; + return fn; +} + +describe("minimizer filter", () => { + it("skips assets when the only minimizer's filter rejects them", async () => { + const compiler = getCompiler({ + entry: path.resolve(__dirname, "./fixtures/entry.js"), + }); + + const minify = makeMinify(() => ({ code: "/* minified */" })); + + minify.filter = () => false; + + new TerserPlugin({ + parallel: false, + minify, + }).apply(compiler); + + const stats = await compile(compiler); + + expect(minify.calls).toHaveLength(0); + expect(readAsset("main.js", compiler, stats)).not.toContain( + "/* minified */", + ); + expect(getErrors(stats)).toEqual([]); + expect(getWarnings(stats)).toEqual([]); + }); + + it("dispatches each asset to the minimizer whose filter accepts it", async () => { + const compiler = getCompiler({ + entry: path.resolve(__dirname, "./fixtures/entry.js"), + }); + + new EmitNewAsset({ name: "extra.css" }).apply(compiler); + + const jsMinify = makeMinify(() => ({ code: "var a=1;/*JS*/" })); + + jsMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name); + + const cssMinify = makeMinify(() => ({ code: "/*CSS*/a{}" })); + + cssMinify.filter = (name) => /\.css(\?.*)?$/i.test(name); + + new TerserPlugin({ + parallel: false, + minify: [jsMinify, cssMinify], + }).apply(compiler); + + const stats = await compile(compiler); + + expect(jsMinify.calls).toHaveLength(1); + expect(cssMinify.calls).toHaveLength(1); + expect(Object.keys(jsMinify.calls[0][0])[0]).toBe("main.js"); + expect(Object.keys(cssMinify.calls[0][0])[0]).toBe("extra.css"); + + const assets = readsAssets(compiler, stats); + + expect(assets["main.js"]).toContain("/*JS*/"); + expect(assets["extra.css"]).toContain("/*CSS*/"); + expect(getErrors(stats)).toEqual([]); + expect(getWarnings(stats)).toEqual([]); + }); + + it("treats a filter returning undefined as accept", async () => { + const compiler = getCompiler({ + entry: path.resolve(__dirname, "./fixtures/entry.js"), + }); + + const minify = makeMinify(() => ({ code: "/*UNDEF-FILTER*/" })); + + minify.filter = () => undefined; + + new TerserPlugin({ + parallel: false, + minify, + }).apply(compiler); + + const stats = await compile(compiler); + + expect(minify.calls).toHaveLength(1); + expect(readAsset("main.js", compiler, stats)).toContain("/*UNDEF-FILTER*/"); + expect(getErrors(stats)).toEqual([]); + expect(getWarnings(stats)).toEqual([]); + }); + + it("skips an asset when every minimizer in the array rejects it", async () => { + const compiler = getCompiler({ + entry: path.resolve(__dirname, "./fixtures/entry.js"), + }); + + new EmitNewAsset({ name: "stranger.txt" }).apply(compiler); + + const jsMinify = makeMinify(() => ({ code: "/*JS*/" })); + + jsMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name); + + const cssMinify = makeMinify(() => ({ code: "/*CSS*/" })); + + cssMinify.filter = (name) => /\.css(\?.*)?$/i.test(name); + + new TerserPlugin({ + parallel: false, + minify: [jsMinify, cssMinify], + }).apply(compiler); + + const stats = await compile(compiler); + + expect(jsMinify.calls).toHaveLength(1); + expect(cssMinify.calls).toHaveLength(0); + + const txt = readAsset("stranger.txt", compiler, stats); + + expect(txt).not.toContain("/*JS*/"); + expect(txt).not.toContain("/*CSS*/"); + expect(getErrors(stats)).toEqual([]); + expect(getWarnings(stats)).toEqual([]); + }); + + it("keeps the chain semantic when multiple minimizers in the array accept the same asset", async () => { + const compiler = getCompiler({ + entry: path.resolve(__dirname, "./fixtures/entry.js"), + }); + + const firstMinify = makeMinify((input) => { + const [[, code]] = Object.entries(input); + + return { code: `${code}/*FIRST*/` }; + }); + + firstMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name); + + const secondMinify = makeMinify((input) => { + const [[, code]] = Object.entries(input); + + return { code: `${code}/*SECOND*/` }; + }); + + secondMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name); + + new TerserPlugin({ + parallel: false, + minify: [firstMinify, secondMinify], + }).apply(compiler); + + const stats = await compile(compiler); + + expect(firstMinify.calls).toHaveLength(1); + expect(secondMinify.calls).toHaveLength(1); + + const [[secondInput]] = secondMinify.calls; + const [secondCode] = Object.values(secondInput); + + expect(secondCode).toContain("/*FIRST*/"); + + const out = readAsset("main.js", compiler, stats); + + expect(out).toContain("/*FIRST*/"); + expect(out).toContain("/*SECOND*/"); + expect(getErrors(stats)).toEqual([]); + expect(getWarnings(stats)).toEqual([]); + }); + + it("relies on filters when an array of minimizers is provided without an explicit `test`", async () => { + const compiler = getCompiler({ + entry: path.resolve(__dirname, "./fixtures/entry.js"), + }); + + new EmitNewAsset({ name: "extra.css" }).apply(compiler); + + const jsMinify = makeMinify(() => ({ code: "/*JS*/" })); + + jsMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name); + + const cssMinify = makeMinify(() => ({ code: "/*CSS*/" })); + + cssMinify.filter = (name) => /\.css(\?.*)?$/i.test(name); + + // No `test` here — defaulting must let the .css asset through so + // cssMinify.filter can claim it. Without the array-aware default this + // would gate on /\.[cm]?js$/ and skip the .css asset entirely. + new TerserPlugin({ + parallel: false, + minify: [jsMinify, cssMinify], + }).apply(compiler); + + const stats = await compile(compiler); + + expect(jsMinify.calls).toHaveLength(1); + expect(cssMinify.calls).toHaveLength(1); + expect(readAsset("extra.css", compiler, stats)).toContain("/*CSS*/"); + expect(getErrors(stats)).toEqual([]); + expect(getWarnings(stats)).toEqual([]); + }); + + it("keeps the JS-only `test` default when a single minimizer is provided", async () => { + const compiler = getCompiler({ + entry: path.resolve(__dirname, "./fixtures/entry.js"), + }); + + new EmitNewAsset({ name: "extra.css" }).apply(compiler); + + const minify = makeMinify(() => ({ code: "/*ONLY*/" })); + + new TerserPlugin({ + parallel: false, + minify, + }).apply(compiler); + + const stats = await compile(compiler); + + expect(minify.calls).toHaveLength(1); + expect(Object.keys(minify.calls[0][0])[0]).toBe("main.js"); + expect(getErrors(stats)).toEqual([]); + expect(getWarnings(stats)).toEqual([]); + }); + + it("pairs the per-asset implementation subset with the matching options entries", async () => { + const compiler = getCompiler({ + entry: path.resolve(__dirname, "./fixtures/entry.js"), + }); + + new EmitNewAsset({ name: "extra.css" }).apply(compiler); + + const jsMinify = makeMinify(() => ({ code: "/*JS*/" })); + + jsMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name); + + const cssMinify = makeMinify(() => ({ code: "/*CSS*/" })); + + cssMinify.filter = (name) => /\.css(\?.*)?$/i.test(name); + + new TerserPlugin({ + parallel: false, + minify: [jsMinify, cssMinify], + minimizerOptions: [{ flavor: "js" }, { flavor: "css" }], + }).apply(compiler); + + const stats = await compile(compiler); + + expect(jsMinify.calls).toHaveLength(1); + expect(cssMinify.calls).toHaveLength(1); + + const [[, , jsOptions]] = jsMinify.calls; + + expect(jsOptions.flavor).toBe("js"); + + const [[, , cssOptions]] = cssMinify.calls; + + expect(cssOptions.flavor).toBe("css"); + expect(getErrors(stats)).toEqual([]); + expect(getWarnings(stats)).toEqual([]); + }); +}); diff --git a/types/index.d.ts b/types/index.d.ts index 6ccffd7..f076566 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -249,6 +249,12 @@ type MinimizeFunctionHelpers = { * true when minimizer support worker, otherwise false */ supportsWorker?: (() => boolean | undefined) | undefined; + /** + * return true when the minimizer supports the asset, otherwise false. When an array of minimizers is configured, each asset is dispatched only to the minimizers whose `filter` accepts it. Assets rejected by every minimizer in the array are skipped entirely. + */ + filter?: + | ((name: string, info?: AssetInfo) => boolean | undefined) + | undefined; }; type MinimizerImplementation = T extends EXPECTED_ANY[] ? { diff --git a/types/utils.d.ts b/types/utils.d.ts index 8979e94..ab68a12 100644 --- a/types/utils.d.ts +++ b/types/utils.d.ts @@ -33,6 +33,11 @@ export namespace cleanCssMinify { * @returns {boolean | undefined} true if worker threads are supported */ function supportsWorkerThreads(): boolean | undefined; + /** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like a CSS file + */ + function filter(name: string): boolean; } /** * Minify CSS using `cssnano` (via `postcss`). @@ -55,6 +60,11 @@ export namespace cssnanoMinify { * @returns {boolean | undefined} true if worker threads are supported */ function supportsWorkerThreads(): boolean | undefined; + /** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like a CSS file + */ + function filter(name: string): boolean; } /** * Minify CSS using `csso`. @@ -77,6 +87,11 @@ export namespace cssoMinify { * @returns {boolean | undefined} true if worker threads are supported */ function supportsWorkerThreads(): boolean | undefined; + /** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like a CSS file + */ + function filter(name: string): boolean; } /** * @param {Input} input input @@ -98,6 +113,11 @@ export namespace esbuildMinify { * @returns {boolean | undefined} true if worker thread is supported, false otherwise */ function supportsWorkerThreads(): boolean | undefined; + /** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like a JavaScript file + */ + function filter(name: string): boolean; } /** * Minify CSS using `esbuild` (with the CSS loader). @@ -120,6 +140,11 @@ export namespace esbuildMinifyCss { * @returns {boolean | undefined} false because `esbuild` is a native binding */ function supportsWorkerThreads(): boolean | undefined; + /** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like a CSS file + */ + function filter(name: string): boolean; } /** @typedef {import("./index.js").ExtractCommentsOptions} ExtractCommentsOptions */ /** @typedef {import("./index.js").ExtractCommentsFunction} ExtractCommentsFunction */ @@ -165,6 +190,11 @@ export namespace htmlMinifierTerser { * @returns {boolean | undefined} true if worker threads are supported */ function supportsWorkerThreads(): boolean | undefined; + /** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like an HTML file + */ + function filter(name: string): boolean; } /** * @param {Input} input input @@ -181,6 +211,11 @@ export namespace jsonMinify { function getMinimizerVersion(): string; function supportsWorker(): boolean; function supportsWorkerThreads(): boolean; + /** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like a JSON file + */ + function filter(name: string): boolean; } /** * Minify CSS using `lightningcss`. @@ -203,6 +238,11 @@ export namespace lightningCssMinify { * @returns {boolean | undefined} false because `lightningcss` is a native binding */ function supportsWorkerThreads(): boolean | undefined; + /** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like a CSS file + */ + function filter(name: string): boolean; } /** * @template T @@ -235,6 +275,11 @@ export namespace minifyHtmlNode { * @returns {boolean | undefined} false because `@minify-html/node` is a native binding */ function supportsWorkerThreads(): boolean | undefined; + /** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like an HTML file + */ + function filter(name: string): boolean; } /** * @param {Input} input input @@ -258,6 +303,11 @@ export namespace swcMinify { * @returns {boolean | undefined} true if worker thread is supported, false otherwise */ function supportsWorkerThreads(): boolean | undefined; + /** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like a JavaScript file + */ + function filter(name: string): boolean; } /** * Minify CSS using `@swc/css`. @@ -280,6 +330,11 @@ export namespace swcMinifyCss { * @returns {boolean | undefined} false because `@swc/css` is a native binding */ function supportsWorkerThreads(): boolean | undefined; + /** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like a CSS file + */ + function filter(name: string): boolean; } /** * Minify a complete HTML document using `@swc/html`. @@ -302,6 +357,11 @@ export namespace swcMinifyHtml { * @returns {boolean | undefined} false because `@swc/html` is a native binding */ function supportsWorkerThreads(): boolean | undefined; + /** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like an HTML file + */ + function filter(name: string): boolean; } /** * Minify an HTML fragment using `@swc/html`. @@ -327,6 +387,11 @@ export namespace swcMinifyHtmlFragment { * @returns {boolean | undefined} false because `@swc/html` is a native binding */ function supportsWorkerThreads(): boolean | undefined; + /** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like an HTML file + */ + function filter(name: string): boolean; } /** * @param {Input} input input @@ -350,6 +415,11 @@ export namespace terserMinify { * @returns {boolean | undefined} true if worker thread is supported, false otherwise */ function supportsWorkerThreads(): boolean | undefined; + /** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like a JavaScript file + */ + function filter(name: string): boolean; } /** * @template T @@ -385,4 +455,9 @@ export namespace uglifyJsMinify { * @returns {boolean | undefined} true if worker thread is supported, false otherwise */ function supportsWorkerThreads(): boolean | undefined; + /** + * @param {string} name asset name + * @returns {boolean} true if `name` looks like a JavaScript file + */ + function filter(name: string): boolean; } From b45ab8ba6db162b57537b6f2b407d3794b8f0110 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 5 May 2026 16:17:09 +0000 Subject: [PATCH 2/7] refactor: collapse minimizer-filter dispatch into one helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `optimize()` was branching on whether the user passed a single minimizer or an array, both for the gate ("does anything accept this asset?") and for per-asset slicing. Normalize implementations and options to parallel arrays once at the top of `optimize()` and reuse a single `matchingMinimizers(name, info)` helper for both checks. Pass arrays to `minify.js` unconditionally — it already normalizes a single function to a one-element array internally. Also extract the four extension regexes (JS/JSON/HTML/CSS) into shared constants in `src/utils.js` so the 15 built-in `filter` helpers don't each repeat the same literal. --- src/index.js | 120 ++++++++++++++++++++--------------------------- src/utils.js | 35 ++++++++------ types/utils.d.ts | 15 +----- 3 files changed, 72 insertions(+), 98 deletions(-) diff --git a/src/index.js b/src/index.js index 0ead03c..64ca3a7 100644 --- a/src/index.js +++ b/src/index.js @@ -382,36 +382,47 @@ class TerserPlugin { async optimize(compiler, compilation, assets, optimizeOptions) { const cache = compilation.getCache("TerserWebpackPlugin"); let numberOfAssets = 0; - const allImplementations = Array.isArray( - this.options.minimizer.implementation, - ) + + // Normalize the configured minimizer(s) and their options into parallel + // arrays. The user can pass either a single `(impl, options)` pair or an + // array of pairs; for dispatch and per-asset slicing we treat both the + // same. The original `this.options.minimizer.implementation` shape is + // preserved for chunk hashing further down. + const implementations = Array.isArray(this.options.minimizer.implementation) ? this.options.minimizer.implementation - : null; + : [this.options.minimizer.implementation]; + const minimizerOptionsList = + /** @type {MinimizerOptions[]} */ + ( + Array.isArray(this.options.minimizer.options) + ? this.options.minimizer.options + : implementations.map(() => this.options.minimizer.options) + ); + /** - * Apply each minimizer's `filter` helper. For array form, an asset is - * accepted when at least one configured minimizer accepts it; for the - * single form, the asset must be accepted by that minimizer. + * Collect the indices of minimizers whose `filter` accepts `name`. + * Filters returning `undefined` are treated as accept (matches the + * convention used by `supportsWorkerThreads`). * @param {string} name asset name * @param {AssetInfo} info asset info - * @returns {boolean} whether at least one minimizer accepts this asset + * @returns {number[]} indices into `implementations` that accept the asset */ - const isAcceptedByAnyMinimizer = (name, info) => { - if (allImplementations) { - return allImplementations.some( - (impl) => - typeof impl.filter !== "function" || - // eslint-disable-next-line unicorn/no-array-method-this-argument - impl.filter(name, info) !== false, - ); + const matchingMinimizers = (name, info) => { + const matched = []; + + for (let i = 0; i < implementations.length; i++) { + const impl = implementations[i]; + + if ( + typeof impl.filter !== "function" || + // eslint-disable-next-line unicorn/no-array-method-this-argument + impl.filter(name, info) !== false + ) { + matched.push(i); + } } - const single = - /** @type {BasicMinimizerImplementation & MinimizeFunctionHelpers} */ - (this.options.minimizer.implementation); - return ( - typeof single.filter !== "function" || - // eslint-disable-next-line unicorn/no-array-method-this-argument - single.filter(name, info) !== false - ); + + return matched; }; const assetsForMinify = await Promise.all( Object.keys(assets) @@ -436,7 +447,7 @@ class TerserPlugin { return false; } - if (!isAcceptedByAnyMinimizer(name, info)) { + if (matchingMinimizers(name, info).length === 0) { return false; } @@ -470,10 +481,6 @@ class TerserPlugin { /** @type {undefined | number} */ let numberOfWorkers; - const implementations = Array.isArray(this.options.minimizer.implementation) - ? this.options.minimizer.implementation - : [this.options.minimizer.implementation]; - const needCreateWorker = optimizeOptions.availableNumberOfCores > 0 && implementations.every( @@ -564,44 +571,19 @@ class TerserPlugin { } // Dispatch to only the minimizers whose `filter` accepts this asset. - // For the single-implementation form `assetImplementation` matches - // the original; for the array form it's the matching subset (still - // an array, even when only one entry remains, so chained behavior - // is preserved when multiple match). - let assetImplementation = this.options.minimizer.implementation; - let assetMinimizerOptions = this.options.minimizer.options; - - if (allImplementations) { - const matchedIndices = []; - - for (let i = 0; i < allImplementations.length; i++) { - const impl = allImplementations[i]; - - if ( - typeof impl.filter !== "function" || - // eslint-disable-next-line unicorn/no-array-method-this-argument - impl.filter(name, info) !== false - ) { - matchedIndices.push(i); - } - } - - assetImplementation = - /** @type {MinimizerImplementation} */ - (matchedIndices.map((i) => allImplementations[i])); - - if (Array.isArray(this.options.minimizer.options)) { - const sourceOptions = this.options.minimizer.options; - - assetMinimizerOptions = - /** @type {MinimizerOptions} */ - (matchedIndices.map((i) => sourceOptions[i] || {})); - } - } - - const clonedMinimizerOptions = Array.isArray(assetMinimizerOptions) - ? assetMinimizerOptions.map((item) => ({ ...item })) - : { .../** @type {T} */ (assetMinimizerOptions) }; + // `minify.js` already normalizes a single implementation into a + // one-element array, so we always hand it the matching subset here. + const matched = matchingMinimizers(name, info); + const assetImplementation = + /** @type {MinimizerImplementation} */ + (matched.map((i) => implementations[i])); + const clonedMinimizerOptions = + /** @type {MinimizerOptions} */ + ( + matched.map((i) => ({ + .../** @type {T} */ (minimizerOptionsList[i]), + })) + ); /** * @type {InternalOptions} @@ -612,9 +594,7 @@ class TerserPlugin { inputSourceMap, minimizer: { implementation: assetImplementation, - options: - /** @type {MinimizerOptions} */ - (clonedMinimizerOptions), + options: clonedMinimizerOptions, }, extractComments: this.options.extractComments, }; diff --git a/src/utils.js b/src/utils.js index efe055e..ea5c035 100644 --- a/src/utils.js +++ b/src/utils.js @@ -11,6 +11,11 @@ * @typedef {string[]} ExtractedComments */ +const JS_FILE_RE = /\.[cm]?js(\?.*)?$/i; +const JSON_FILE_RE = /\.json(\?.*)?$/i; +const HTML_FILE_RE = /\.html?(\?.*)?$/i; +const CSS_FILE_RE = /\.css(\?.*)?$/i; + /** * Map a webpack `output.environment` configuration to the highest * ECMAScript version that the target is known to support. Returns `5` @@ -359,7 +364,7 @@ terserMinify.supportsWorkerThreads = () => true; * @param {string} name asset name * @returns {boolean} true if `name` looks like a JavaScript file */ -terserMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name); +terserMinify.filter = (name) => JS_FILE_RE.test(name); /* istanbul ignore next */ /** @@ -596,7 +601,7 @@ uglifyJsMinify.supportsWorkerThreads = () => true; * @param {string} name asset name * @returns {boolean} true if `name` looks like a JavaScript file */ -uglifyJsMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name); +uglifyJsMinify.filter = (name) => JS_FILE_RE.test(name); /* istanbul ignore next */ /** @@ -833,7 +838,7 @@ swcMinify.supportsWorkerThreads = () => false; * @param {string} name asset name * @returns {boolean} true if `name` looks like a JavaScript file */ -swcMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name); +swcMinify.filter = (name) => JS_FILE_RE.test(name); /* istanbul ignore next */ /** @@ -954,7 +959,7 @@ esbuildMinify.supportsWorkerThreads = () => false; * @param {string} name asset name * @returns {boolean} true if `name` looks like a JavaScript file */ -esbuildMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name); +esbuildMinify.filter = (name) => JS_FILE_RE.test(name); /* istanbul ignore next */ /** @@ -986,7 +991,7 @@ jsonMinify.supportsWorkerThreads = () => false; * @param {string} name asset name * @returns {boolean} true if `name` looks like a JSON file */ -jsonMinify.filter = (name) => /\.json(\?.*)?$/i.test(name); +jsonMinify.filter = (name) => JSON_FILE_RE.test(name); /* istanbul ignore next */ /** @@ -1058,7 +1063,7 @@ htmlMinifierTerser.supportsWorkerThreads = () => true; * @param {string} name asset name * @returns {boolean} true if `name` looks like an HTML file */ -htmlMinifierTerser.filter = (name) => /\.html?(\?.*)?$/i.test(name); +htmlMinifierTerser.filter = (name) => HTML_FILE_RE.test(name); /* istanbul ignore next */ /** @@ -1111,7 +1116,7 @@ minifyHtmlNode.supportsWorkerThreads = () => false; * @param {string} name asset name * @returns {boolean} true if `name` looks like an HTML file */ -minifyHtmlNode.filter = (name) => /\.html?(\?.*)?$/i.test(name); +minifyHtmlNode.filter = (name) => HTML_FILE_RE.test(name); /* istanbul ignore next */ /** @@ -1187,7 +1192,7 @@ swcMinifyHtml.supportsWorkerThreads = () => false; * @param {string} name asset name * @returns {boolean} true if `name` looks like an HTML file */ -swcMinifyHtml.filter = (name) => /\.html?(\?.*)?$/i.test(name); +swcMinifyHtml.filter = (name) => HTML_FILE_RE.test(name); /* istanbul ignore next */ /** @@ -1247,7 +1252,7 @@ swcMinifyHtmlFragment.supportsWorkerThreads = () => false; * @param {string} name asset name * @returns {boolean} true if `name` looks like an HTML file */ -swcMinifyHtmlFragment.filter = (name) => /\.html?(\?.*)?$/i.test(name); +swcMinifyHtmlFragment.filter = (name) => HTML_FILE_RE.test(name); /* istanbul ignore next */ /** @@ -1399,7 +1404,7 @@ cssnanoMinify.supportsWorkerThreads = () => true; * @param {string} name asset name * @returns {boolean} true if `name` looks like a CSS file */ -cssnanoMinify.filter = (name) => /\.css(\?.*)?$/i.test(name); +cssnanoMinify.filter = (name) => CSS_FILE_RE.test(name); /* istanbul ignore next */ /** @@ -1459,7 +1464,7 @@ cssoMinify.supportsWorkerThreads = () => true; * @param {string} name asset name * @returns {boolean} true if `name` looks like a CSS file */ -cssoMinify.filter = (name) => /\.css(\?.*)?$/i.test(name); +cssoMinify.filter = (name) => CSS_FILE_RE.test(name); /* istanbul ignore next */ /** @@ -1537,7 +1542,7 @@ cleanCssMinify.supportsWorkerThreads = () => true; * @param {string} name asset name * @returns {boolean} true if `name` looks like a CSS file */ -cleanCssMinify.filter = (name) => /\.css(\?.*)?$/i.test(name); +cleanCssMinify.filter = (name) => CSS_FILE_RE.test(name); /* istanbul ignore next */ /** @@ -1658,7 +1663,7 @@ esbuildMinifyCss.supportsWorkerThreads = () => false; * @param {string} name asset name * @returns {boolean} true if `name` looks like a CSS file */ -esbuildMinifyCss.filter = (name) => /\.css(\?.*)?$/i.test(name); +esbuildMinifyCss.filter = (name) => CSS_FILE_RE.test(name); /* istanbul ignore next */ /** @@ -1733,7 +1738,7 @@ lightningCssMinify.supportsWorkerThreads = () => false; * @param {string} name asset name * @returns {boolean} true if `name` looks like a CSS file */ -lightningCssMinify.filter = (name) => /\.css(\?.*)?$/i.test(name); +lightningCssMinify.filter = (name) => CSS_FILE_RE.test(name); /* istanbul ignore next */ /** @@ -1823,7 +1828,7 @@ swcMinifyCss.supportsWorkerThreads = () => false; * @param {string} name asset name * @returns {boolean} true if `name` looks like a CSS file */ -swcMinifyCss.filter = (name) => /\.css(\?.*)?$/i.test(name); +swcMinifyCss.filter = (name) => CSS_FILE_RE.test(name); /** * @template T diff --git a/types/utils.d.ts b/types/utils.d.ts index ab68a12..0f1e50e 100644 --- a/types/utils.d.ts +++ b/types/utils.d.ts @@ -1,3 +1,5 @@ +export type Task = () => Promise; +export type FunctionReturning = () => T; export type ExtractCommentsOptions = import("./index.js").ExtractCommentsOptions; export type ExtractCommentsFunction = @@ -10,8 +12,6 @@ export type CustomOptions = import("./index.js").CustomOptions; export type RawSourceMap = import("./index.js").RawSourceMap; export type EXPECTED_OBJECT = import("./index.js").EXPECTED_OBJECT; export type ExtractedComments = string[]; -export type Task = () => Promise; -export type FunctionReturning = () => T; /** * Minify CSS using `clean-css`. * @param {Input} input input @@ -146,17 +146,6 @@ export namespace esbuildMinifyCss { */ function filter(name: string): boolean; } -/** @typedef {import("./index.js").ExtractCommentsOptions} ExtractCommentsOptions */ -/** @typedef {import("./index.js").ExtractCommentsFunction} ExtractCommentsFunction */ -/** @typedef {import("./index.js").ExtractCommentsCondition} ExtractCommentsCondition */ -/** @typedef {import("./index.js").Input} Input */ -/** @typedef {import("./index.js").MinimizedResult} MinimizedResult */ -/** @typedef {import("./index.js").CustomOptions} CustomOptions */ -/** @typedef {import("./index.js").RawSourceMap} RawSourceMap */ -/** @typedef {import("./index.js").EXPECTED_OBJECT} EXPECTED_OBJECT */ -/** - * @typedef {string[]} ExtractedComments - */ /** * Map a webpack `output.environment` configuration to the highest * ECMAScript version that the target is known to support. Returns `5` From 12e271d21fb24b30134d012f57b9e0afdf4e658f Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 5 May 2026 16:59:59 +0000 Subject: [PATCH 3/7] refactor: drop the array-aware `test` default and cache matches per asset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep the original `test` default (`/\.[cm]?js(\?.*)?$/i`) for both single- and array-of-minimizers form. Users who want to dispatch across asset types in one plugin instance widen `test` themselves — the README example now spells that out explicitly. In `optimize()`, fold the gate and the per-asset record into a single async pass: each asset returns either an empty array (skipped) or a one-element array containing the record (with the matched-implementation indices already computed). `Array.prototype.flat()` produces the final list. The per-asset task reads `matched` directly from the record so the filter regexes only run once per asset. --- README.md | 15 ++++++---- src/index.js | 55 +++++++++++++++++++---------------- test/minimizer-filter.test.js | 13 ++++++--- 3 files changed, 48 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index dbf9178..246ade3 100644 --- a/README.md +++ b/README.md @@ -327,10 +327,12 @@ asset; the plugin dispatches each asset only to the minimizers whose `filter` accepts it (or runs them all when no filter is set). All built-in minimizers ship with a `filter` that matches their natural extension, so a single plugin instance and a single worker pool can handle JS, CSS, HTML and JSON together -without juggling multiple `TerserPlugin` instances: +without juggling multiple `TerserPlugin` instances — just widen `test` to +let those asset types reach the dispatcher: ```js new TerserPlugin({ + test: /\.(?:[cm]?js|css|html?|json)(\?.*)?$/i, minify: [ TerserPlugin.terserMinify, TerserPlugin.cssnanoMinify, @@ -346,9 +348,9 @@ input to the next. The [`minimizerOptions`](#minimizeroptions) option may be an array (index-paired with `minify`) or a single object reused by every minimizer. -When `minify` is an array, the `test` option defaults to `undefined` so each -minimizer's `filter` decides which assets it processes. With a single `minify` -function, `test` keeps its JS-only default of `/\.[cm]?js(\?.*)?$/i`. +The `test` option always defaults to `/\.[cm]?js(\?.*)?$/i`. When you mix +asset types in a single plugin instance, widen `test` so non-JS assets reach +the dispatcher (for example `test: /\.(?:[cm]?js|css|html?|json)(\?.*)?$/i`). > **Warning** > @@ -452,8 +454,9 @@ module.exports = { minimize: true, minimizer: [ new TerserPlugin({ - // `test` defaults to `undefined` here so each filter decides which - // assets it handles. Override `test` to narrow further if needed. + // `test` still defaults to JS only, so widen it to catch every + // asset type you want the dispatcher to consider. + test: /\.(?:[cm]?js|css|html?|json)(\?.*)?$/i, minify: [ TerserPlugin.terserMinify, TerserPlugin.cssnanoMinify, diff --git a/src/index.js b/src/index.js index 64ca3a7..20f2994 100644 --- a/src/index.js +++ b/src/index.js @@ -190,17 +190,13 @@ class TerserPlugin { // TODO handle json and etc in the next major release // TODO make `minimizer` option instead `minify` and `terserOptions` in the next major release, also rename `terserMinify` to `terserMinimize` - // When an array of minimizers is supplied, leave `test` undefined so that - // each minimizer's own `filter` decides which assets it processes; - // otherwise fall back to the JS-only default. - const isMinifyArray = Array.isArray(options && options.minify); const { minify = /** @type {MinimizerImplementation} */ ( /** @type {unknown} */ (terserMinify) ), minimizerOptions, terserOptions, - test = isMinifyArray ? undefined : /\.[cm]?js(\?.*)?$/i, + test = /\.[cm]?js(\?.*)?$/i, extractComments = true, parallel = true, include, @@ -424,10 +420,12 @@ class TerserPlugin { return matched; }; - const assetsForMinify = await Promise.all( - Object.keys(assets) - .filter((name) => { - const { info } = /** @type {Asset} */ (compilation.getAsset(name)); + const assetsForMinify = ( + await Promise.all( + Object.keys(assets).map(async (name) => { + const { info, source } = /** @type {Asset} */ ( + compilation.getAsset(name) + ); if ( // Skip double minimize assets from child compilation @@ -435,7 +433,7 @@ class TerserPlugin { // Skip minimizing for extracted comments assets info.extractedComments ) { - return false; + return []; } if ( @@ -444,19 +442,16 @@ class TerserPlugin { this.options, )(name) ) { - return false; + return []; } - if (matchingMinimizers(name, info).length === 0) { - return false; - } + // Compute the matching minimizers once and carry them through to + // the per-asset task so we don't pay the lookup twice. + const matched = matchingMinimizers(name, info); - return true; - }) - .map(async (name) => { - const { info, source } = /** @type {Asset} */ ( - compilation.getAsset(name) - ); + if (matched.length === 0) { + return []; + } const eTag = cache.getLazyHashedEtag(source); const cacheItem = cache.getItemCache(name, eTag); @@ -466,9 +461,19 @@ class TerserPlugin { numberOfAssets += 1; } - return { name, info, inputSource: source, output, cacheItem }; + return [ + { + name, + info, + inputSource: source, + output, + cacheItem, + matched, + }, + ]; }), - ); + ) + ).flat(); if (assetsForMinify.length === 0) { return; @@ -543,7 +548,7 @@ class TerserPlugin { for (const asset of assetsForMinify) { scheduledTasks.push(async () => { - const { name, inputSource, info, cacheItem } = asset; + const { name, inputSource, info, cacheItem, matched } = asset; let { output } = asset; if (!output) { @@ -570,10 +575,10 @@ class TerserPlugin { input = input.toString(); } - // Dispatch to only the minimizers whose `filter` accepts this asset. + // Dispatch to only the minimizers whose `filter` accepted this + // asset (computed once when collecting `assetsForMinify`). // `minify.js` already normalizes a single implementation into a // one-element array, so we always hand it the matching subset here. - const matched = matchingMinimizers(name, info); const assetImplementation = /** @type {MinimizerImplementation} */ (matched.map((i) => implementations[i])); diff --git a/test/minimizer-filter.test.js b/test/minimizer-filter.test.js index 2fc6707..7fee111 100644 --- a/test/minimizer-filter.test.js +++ b/test/minimizer-filter.test.js @@ -72,6 +72,7 @@ describe("minimizer filter", () => { new TerserPlugin({ parallel: false, + test: /\.(?:[cm]?js|css)(\?.*)?$/i, minify: [jsMinify, cssMinify], }).apply(compiler); @@ -129,6 +130,9 @@ describe("minimizer filter", () => { new TerserPlugin({ parallel: false, + // Widen `test` so the .txt asset reaches the dispatcher and we can + // verify that no minimizer in the array claims it. + test: /.*/, minify: [jsMinify, cssMinify], }).apply(compiler); @@ -189,7 +193,7 @@ describe("minimizer filter", () => { expect(getWarnings(stats)).toEqual([]); }); - it("relies on filters when an array of minimizers is provided without an explicit `test`", async () => { + it("dispatches across asset types when `test` is widened", async () => { const compiler = getCompiler({ entry: path.resolve(__dirname, "./fixtures/entry.js"), }); @@ -204,11 +208,11 @@ describe("minimizer filter", () => { cssMinify.filter = (name) => /\.css(\?.*)?$/i.test(name); - // No `test` here — defaulting must let the .css asset through so - // cssMinify.filter can claim it. Without the array-aware default this - // would gate on /\.[cm]?js$/ and skip the .css asset entirely. new TerserPlugin({ parallel: false, + // `test` still defaults to JS only; widen it (or set it to a regex + // that catches every asset) so the dispatcher gets to see CSS too. + test: /\.(?:[cm]?js|css)(\?.*)?$/i, minify: [jsMinify, cssMinify], }).apply(compiler); @@ -260,6 +264,7 @@ describe("minimizer filter", () => { new TerserPlugin({ parallel: false, + test: /\.(?:[cm]?js|css)(\?.*)?$/i, minify: [jsMinify, cssMinify], minimizerOptions: [{ flavor: "js" }, { flavor: "css" }], }).apply(compiler); From 0dfbbcc6e7b3b9923a2702808cc2c9ff78008cf2 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 5 May 2026 17:16:58 +0000 Subject: [PATCH 4/7] refactor: stop cloning minimizer options on every asset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The per-asset shallow clone in `optimize()` only existed because `minify.js` mutated `module`/`ecma` on the caller's options object, which would have leaked across assets when a single options object was shared. Move that overlay inside the `minify.js` loop and build it as a fresh object via spread, so the same source options reference is safe to reuse. `optimize()` no longer pre-expands a `minimizerOptionsList`: when the configured options are an array, slice the matching subset (references, not clones); otherwise pass the single object straight through. With a shared options object and N minimizers across M assets that drops the cloning from N×M to a single overlay per minimizer call. --- src/index.js | 31 +++++++++++++------------------ src/minify.js | 21 +++++++++++---------- 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/index.js b/src/index.js index 20f2994..1148b8d 100644 --- a/src/index.js +++ b/src/index.js @@ -379,21 +379,13 @@ class TerserPlugin { const cache = compilation.getCache("TerserWebpackPlugin"); let numberOfAssets = 0; - // Normalize the configured minimizer(s) and their options into parallel - // arrays. The user can pass either a single `(impl, options)` pair or an - // array of pairs; for dispatch and per-asset slicing we treat both the - // same. The original `this.options.minimizer.implementation` shape is - // preserved for chunk hashing further down. + // Normalize the implementation list to an array so dispatch and the + // worker-pool capability checks below can iterate uniformly. The + // original shape on `this.options.minimizer.implementation` is preserved + // for chunk hashing. const implementations = Array.isArray(this.options.minimizer.implementation) ? this.options.minimizer.implementation : [this.options.minimizer.implementation]; - const minimizerOptionsList = - /** @type {MinimizerOptions[]} */ - ( - Array.isArray(this.options.minimizer.options) - ? this.options.minimizer.options - : implementations.map(() => this.options.minimizer.options) - ); /** * Collect the indices of minimizers whose `filter` accepts `name`. @@ -578,16 +570,19 @@ class TerserPlugin { // Dispatch to only the minimizers whose `filter` accepted this // asset (computed once when collecting `assetsForMinify`). // `minify.js` already normalizes a single implementation into a - // one-element array, so we always hand it the matching subset here. + // one-element array, so we always hand it the matching subset. + // Options are sliced as references — `minify.js` overlays + // `module`/`ecma` without mutating the caller's object. const assetImplementation = /** @type {MinimizerImplementation} */ (matched.map((i) => implementations[i])); - const clonedMinimizerOptions = + const sourceOptions = this.options.minimizer.options; + const assetMinimizerOptions = /** @type {MinimizerOptions} */ ( - matched.map((i) => ({ - .../** @type {T} */ (minimizerOptionsList[i]), - })) + Array.isArray(sourceOptions) + ? matched.map((i) => sourceOptions[i] || {}) + : sourceOptions ); /** @@ -599,7 +594,7 @@ class TerserPlugin { inputSourceMap, minimizer: { implementation: assetImplementation, - options: clonedMinimizerOptions, + options: assetMinimizerOptions, }, extractComments: this.options.extractComments, }; diff --git a/src/minify.js b/src/minify.js index c2bb298..fab6467 100644 --- a/src/minify.js +++ b/src/minify.js @@ -323,8 +323,8 @@ async function minify(options) { const currentImplementation = /** @type {import("./index.js").BasicMinimizerImplementation & import("./index.js").MinimizeFunctionHelpers} */ (implementations[i]); - const currentOptions = - /** @type {import("./index.js").MinimizerOptions} */ + const baseOptions = + /** @type {import("./index.js").MinimizerOptions & { module?: boolean, ecma?: number | string }} */ ( Array.isArray(minimizerOptions) ? minimizerOptions[i] || {} @@ -333,14 +333,15 @@ async function minify(options) { const currentInput = typeof lastCode === "string" ? lastCode : input; const currentMap = typeof lastCode === "string" ? lastMap : inputSourceMap; - /** @type {MinimizerOptions} */ - (currentOptions).module = - /** @type {MinimizerOptions} */ - (currentOptions).module || module; - /** @type {MinimizerOptions} */ - (currentOptions).ecma = - /** @type {MinimizerOptions} */ - (currentOptions).ecma || ecma; + // Overlay `module` and `ecma` without mutating the caller's options so + // a single options object can be reused safely across assets. + const currentOptions = + /** @type {import("./index.js").MinimizerOptions} */ + ({ + ...baseOptions, + module: baseOptions.module || module, + ecma: baseOptions.ecma || ecma, + }); const result = await currentImplementation( { [name]: currentInput }, From 6cb7807d075e606b6648048472d0c3d01fb4b19f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 7 May 2026 23:07:45 +0000 Subject: [PATCH 5/7] fix: avoid Array#flat() so optimize() runs on Node 10 `Array.prototype.flat` only landed in Node 11; the engines field is `>= 10.13.0`. Restore the `.filter` + `.map` split and stash each asset's matched-minimizer indices in a `Map` keyed by name, so the matching regexes still run only once per asset and the array of records is built without `flat`. --- src/index.js | 53 +++++++++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/src/index.js b/src/index.js index 1148b8d..8a16685 100644 --- a/src/index.js +++ b/src/index.js @@ -412,12 +412,13 @@ class TerserPlugin { return matched; }; - const assetsForMinify = ( - await Promise.all( - Object.keys(assets).map(async (name) => { - const { info, source } = /** @type {Asset} */ ( - compilation.getAsset(name) - ); + /** @type {Map} */ + const matchedByName = new Map(); + + const assetsForMinify = await Promise.all( + Object.keys(assets) + .filter((name) => { + const { info } = /** @type {Asset} */ (compilation.getAsset(name)); if ( // Skip double minimize assets from child compilation @@ -425,7 +426,7 @@ class TerserPlugin { // Skip minimizing for extracted comments assets info.extractedComments ) { - return []; + return false; } if ( @@ -434,17 +435,26 @@ class TerserPlugin { this.options, )(name) ) { - return []; + return false; } - // Compute the matching minimizers once and carry them through to - // the per-asset task so we don't pay the lookup twice. + // Compute the matching minimizers once and carry the result to the + // per-asset task via `matchedByName` so the regexes don't run again. const matched = matchingMinimizers(name, info); if (matched.length === 0) { - return []; + return false; } + matchedByName.set(name, matched); + + return true; + }) + .map(async (name) => { + const { info, source } = /** @type {Asset} */ ( + compilation.getAsset(name) + ); + const eTag = cache.getLazyHashedEtag(source); const cacheItem = cache.getItemCache(name, eTag); const output = await cacheItem.getPromise(); @@ -453,19 +463,16 @@ class TerserPlugin { numberOfAssets += 1; } - return [ - { - name, - info, - inputSource: source, - output, - cacheItem, - matched, - }, - ]; + return { + name, + info, + inputSource: source, + output, + cacheItem, + matched: /** @type {number[]} */ (matchedByName.get(name)), + }; }), - ) - ).flat(); + ); if (assetsForMinify.length === 0) { return; From 324e3699ed7f1c0255642742dc3b1b2696ac632f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 7 May 2026 23:54:47 +0000 Subject: [PATCH 6/7] fix(deps): bump terser to 5.47.1 and refresh snapshots terser 5.47.1 was published after the previous lockfile update; the CI test matrix on Node 10/12/14/16/18 uses `npm install` (which re-resolves to the latest within `^5.31.1`) so it pulled 5.47.1, while Node 20+ ran `npm ci` and stayed on 5.46.2. The two terser versions mangle the same input to slightly different identifiers, which left the committed snapshots only valid for one half of the matrix. Run `npm update terser` to align the lockfile with what the older-Node install path resolves to, then `jest -u` to refresh the affected snapshots in TerserPlugin/test-option/extractComments-option suites. --- package-lock.json | 6 +- test/__snapshots__/TerserPlugin.test.js.snap | 6 +- .../extractComments-option.test.js.snap | 52 +++++++-------- test/__snapshots__/test-option.test.js.snap | 64 +++++++++---------- 4 files changed, 64 insertions(+), 64 deletions(-) diff --git a/package-lock.json b/package-lock.json index 188a27b..bbe7c35 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18820,9 +18820,9 @@ } }, "node_modules/terser": { - "version": "5.46.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.2.tgz", - "integrity": "sha512-uxfo9fPcSgLDYob/w1FuL0c99MWiJDnv+5qXSQc5+Ki5NjVNsYi66INnMFBjf6uFz6OnX12piJQPF4IpjJTNTw==", + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.47.1.tgz", + "integrity": "sha512-tPbLXTI6ohPASb/1YViL428oEHu6/qv1OxqYnfaonVCFHqx4+wCd95pHrQWsL5X4pl90CTyW9piSAsS2L0VoMw==", "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", diff --git a/test/__snapshots__/TerserPlugin.test.js.snap b/test/__snapshots__/TerserPlugin.test.js.snap index 5da5302..8fd0f40 100644 --- a/test/__snapshots__/TerserPlugin.test.js.snap +++ b/test/__snapshots__/TerserPlugin.test.js.snap @@ -58,7 +58,7 @@ exports[`TerserPlugin should emit an error on a broken code in parallel mode: wa exports[`TerserPlugin should regenerate hash: assets 1`] = ` Object { "389.389.0217a88dbfe5de109e59.js": "\\"use strict\\";(self.webpackChunkterser_webpack_plugin=self.webpackChunkterser_webpack_plugin||[]).push([[389],{389(e,s,p){p.r(s),p.d(s,{default:()=>c});const c=\\"async-dep\\"}}]);", - "AsyncImportExport.86388917ed5e520d8bf6.js": "(()=>{\\"use strict\\";var e,r,t={},o={};function n(e){var r=o[e];if(void 0!==r)return r.exports;var a=o[e]={exports:{}};return t[e](a,a.exports,n),a.exports}n.m=t,n.d=(e,r)=>{for(var t in r)n.o(r,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce((r,t)=>(n.f[t](e,r),r),[])),n.u=e=>e+\\".\\"+e+\\".0217a88dbfe5de109e59.js\\",n.g=function(){if(\\"object\\"==typeof globalThis)return globalThis;try{return this||new Function(\\"return this\\")()}catch(e){if(\\"object\\"==typeof window)return window}}(),n.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),e={},r=\\"terser-webpack-plugin:\\",n.l=(t,o,a,i)=>{if(e[t])e[t].push(o);else{var l,c;if(void 0!==a)for(var u=document.getElementsByTagName(\\"script\\"),s=0;s{l.onerror=l.onload=null,clearTimeout(f);var n=e[t];if(delete e[t],l.parentNode&&l.parentNode.removeChild(l),n&&n.forEach(e=>e(o)),r)return r(o)},f=setTimeout(d.bind(null,void 0,{type:\\"timeout\\",target:l}),12e4);l.onerror=d.bind(null,l.onerror),l.onload=d.bind(null,l.onload),c&&document.head.appendChild(l)}},n.r=e=>{\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},(()=>{var e;n.g.importScripts&&(e=n.g.location+\\"\\");var r=n.g.document;if(!e&&r&&(r.currentScript&&\\"SCRIPT\\"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName(\\"script\\");if(t.length)for(var o=t.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=t[o--].src}if(!e)throw new Error(\\"Automatic publicPath is not supported in this browser\\");e=e.replace(/^blob:/,\\"\\").replace(/#.*$/,\\"\\").replace(/\\\\?.*$/,\\"\\").replace(/\\\\/[^\\\\/]+$/,\\"/\\"),n.p=e})(),(()=>{var e={988:0};n.f.j=(r,t)=>{var o=n.o(e,r)?e[r]:void 0;if(0!==o)if(o)t.push(o[2]);else{var a=new Promise((t,n)=>o=e[r]=[t,n]);t.push(o[2]=a);var i=n.p+n.u(r),l=new Error;n.l(i,t=>{if(n.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var a=t&&(\\"load\\"===t.type?\\"missing\\":t.type),i=t&&t.target&&t.target.src;l.message=\\"Loading chunk \\"+r+\\" failed.\\\\n(\\"+a+\\": \\"+i+\\")\\",l.name=\\"ChunkLoadError\\",l.type=a,l.request=i,o[1](l)}},\\"chunk-\\"+r,r)}};var r=(r,t)=>{var o,a,[i,l,c]=t,u=0;if(i.some(r=>0!==e[r])){for(o in l)n.o(l,o)&&(n.m[o]=l[o]);if(c)c(n)}for(r&&r(t);u{console.log(\\"Good\\")})})();", + "AsyncImportExport.59fa0f8610c254ee3627.js": "(()=>{\\"use strict\\";var e,r,t={},o={};function n(e){var r=o[e];if(void 0!==r)return r.exports;var a=o[e]={exports:{}};return t[e](a,a.exports,n),a.exports}n.m=t,n.d=(e,r)=>{for(var t in r)n.o(r,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce((r,t)=>(n.f[t](e,r),r),[])),n.u=e=>e+\\".\\"+e+\\".0217a88dbfe5de109e59.js\\",n.g=function(){if(\\"object\\"==typeof globalThis)return globalThis;try{return this||new Function(\\"return this\\")()}catch(e){if(\\"object\\"==typeof window)return window}}(),n.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),e={},r=\\"terser-webpack-plugin:\\",n.l=(t,o,a,i)=>{if(e[t])e[t].push(o);else{var c,l;if(void 0!==a)for(var u=document.getElementsByTagName(\\"script\\"),s=0;s{c.onerror=c.onload=null,clearTimeout(f);var n=e[t];if(delete e[t],c.parentNode&&c.parentNode.removeChild(c),n&&n.forEach(e=>e(o)),r)return r(o)},f=setTimeout(d.bind(null,void 0,{type:\\"timeout\\",target:c}),12e4);c.onerror=d.bind(null,c.onerror),c.onload=d.bind(null,c.onload),l&&document.head.appendChild(c)}},n.r=e=>{\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},(()=>{var e;n.g.importScripts&&(e=n.g.location+\\"\\");var r=n.g.document;if(!e&&r&&(r.currentScript&&\\"SCRIPT\\"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName(\\"script\\");if(t.length)for(var o=t.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=t[o--].src}if(!e)throw new Error(\\"Automatic publicPath is not supported in this browser\\");e=e.replace(/^blob:/,\\"\\").replace(/#.*$/,\\"\\").replace(/\\\\?.*$/,\\"\\").replace(/\\\\/[^\\\\/]+$/,\\"/\\"),n.p=e})(),(()=>{var e={988:0};n.f.j=(r,t)=>{var o=n.o(e,r)?e[r]:void 0;if(0!==o)if(o)t.push(o[2]);else{var a=new Promise((t,n)=>o=e[r]=[t,n]);t.push(o[2]=a);var i=n.p+n.u(r),c=new Error;n.l(i,t=>{if(n.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var a=t&&(\\"load\\"===t.type?\\"missing\\":t.type),i=t&&t.target&&t.target.src;c.message=\\"Loading chunk \\"+r+\\" failed.\\\\n(\\"+a+\\": \\"+i+\\")\\",c.name=\\"ChunkLoadError\\",c.type=a,c.request=i,o[1](c)}},\\"chunk-\\"+r,r)}};var r=(r,t)=>{var o,a,[i,c,l]=t,u=0;if(i.some(r=>0!==e[r])){for(o in c)n.o(c,o)&&(n.m[o]=c[o]);if(l)l(n)}for(r&&r(t);u{console.log(\\"Good\\")})})();", "importExport.99516598f0f48417cbb9.js": "(()=>{\\"use strict\\";function o(){const o=\`baz\${Math.random()}\`;return()=>({a:\\"foobar\\"+o,b:\\"foo\\",baz:o})}console.log(o())})();", "js.e6d921fb046a3426b75b.js": "(()=>{var r={921(r){r.exports=function(){console.log(7)}}},o={};(function t(e){var n=o[e];if(void 0!==n)return n.exports;var s=o[e]={exports:{}};return r[e](s,s.exports,t),s.exports})(921)})();", "mjs.46bcd65d7e1972401425.js": "(()=>{\\"use strict\\";function o(){console.log(11)}o()})();", @@ -140,8 +140,8 @@ exports[`TerserPlugin should work and do not use memory cache when the "cache" o exports[`TerserPlugin should work and generate real content hash: assets 1`] = ` Object { - "389.0217a88dbfe5de109e59.ce6ed80c24bae7bf314e.818a0f6b89f90f0869f1.js": "\\"use strict\\";(self.webpackChunkterser_webpack_plugin=self.webpackChunkterser_webpack_plugin||[]).push([[389],{389(e,s,p){p.r(s),p.d(s,{default:()=>c});const c=\\"async-dep\\"}}]);", - "app.84b993f5eae54cc91298.0b534e1f361c3876ed7c.818a0f6b89f90f0869f1.js": "(()=>{\\"use strict\\";var e,r,t={},o={};function n(e){var r=o[e];if(void 0!==r)return r.exports;var a=o[e]={exports:{}};return t[e](a,a.exports,n),a.exports}n.m=t,n.d=(e,r)=>{for(var t in r)n.o(r,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce((r,t)=>(n.f[t](e,r),r),[])),n.u=e=>e+\\".0217a88dbfe5de109e59.ce6ed80c24bae7bf314e.\\"+n.h()+\\".js\\",n.h=()=>\\"818a0f6b89f90f0869f1\\",n.g=function(){if(\\"object\\"==typeof globalThis)return globalThis;try{return this||new Function(\\"return this\\")()}catch(e){if(\\"object\\"==typeof window)return window}}(),n.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),e={},r=\\"terser-webpack-plugin:\\",n.l=(t,o,a,i)=>{if(e[t])e[t].push(o);else{var c,l;if(void 0!==a)for(var u=document.getElementsByTagName(\\"script\\"),s=0;s{c.onerror=c.onload=null,clearTimeout(f);var n=e[t];if(delete e[t],c.parentNode&&c.parentNode.removeChild(c),n&&n.forEach(e=>e(o)),r)return r(o)},f=setTimeout(d.bind(null,void 0,{type:\\"timeout\\",target:c}),12e4);c.onerror=d.bind(null,c.onerror),c.onload=d.bind(null,c.onload),l&&document.head.appendChild(c)}},n.r=e=>{\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},(()=>{var e;n.g.importScripts&&(e=n.g.location+\\"\\");var r=n.g.document;if(!e&&r&&(r.currentScript&&\\"SCRIPT\\"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName(\\"script\\");if(t.length)for(var o=t.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=t[o--].src}if(!e)throw new Error(\\"Automatic publicPath is not supported in this browser\\");e=e.replace(/^blob:/,\\"\\").replace(/#.*$/,\\"\\").replace(/\\\\?.*$/,\\"\\").replace(/\\\\/[^\\\\/]+$/,\\"/\\"),n.p=e})(),(()=>{var e={524:0};n.f.j=(r,t)=>{var o=n.o(e,r)?e[r]:void 0;if(0!==o)if(o)t.push(o[2]);else{var a=new Promise((t,n)=>o=e[r]=[t,n]);t.push(o[2]=a);var i=n.p+n.u(r),c=new Error;n.l(i,t=>{if(n.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var a=t&&(\\"load\\"===t.type?\\"missing\\":t.type),i=t&&t.target&&t.target.src;c.message=\\"Loading chunk \\"+r+\\" failed.\\\\n(\\"+a+\\": \\"+i+\\")\\",c.name=\\"ChunkLoadError\\",c.type=a,c.request=i,o[1](c)}},\\"chunk-\\"+r,r)}};var r=(r,t)=>{var o,a,[i,c,l]=t,u=0;if(i.some(r=>0!==e[r])){for(o in c)n.o(c,o)&&(n.m[o]=c[o]);if(l)l(n)}for(r&&r(t);u{console.log(\\"Good\\")})})();", + "389.0217a88dbfe5de109e59.b2a2af0d3cf31489a500.08d0e4db29e9469898ae.js": "\\"use strict\\";(self.webpackChunkterser_webpack_plugin=self.webpackChunkterser_webpack_plugin||[]).push([[389],{389(e,s,p){p.r(s),p.d(s,{default:()=>c});const c=\\"async-dep\\"}}]);", + "app.6c8a1cae85a224935d60.946a9b3a6a244597bf93.08d0e4db29e9469898ae.js": "(()=>{\\"use strict\\";var e,r,t={},o={};function n(e){var r=o[e];if(void 0!==r)return r.exports;var a=o[e]={exports:{}};return t[e](a,a.exports,n),a.exports}n.m=t,n.d=(e,r)=>{for(var t in r)n.o(r,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce((r,t)=>(n.f[t](e,r),r),[])),n.u=e=>e+\\".0217a88dbfe5de109e59.b2a2af0d3cf31489a500.\\"+n.h()+\\".js\\",n.h=()=>\\"08d0e4db29e9469898ae\\",n.g=function(){if(\\"object\\"==typeof globalThis)return globalThis;try{return this||new Function(\\"return this\\")()}catch(e){if(\\"object\\"==typeof window)return window}}(),n.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),e={},r=\\"terser-webpack-plugin:\\",n.l=(t,o,a,i)=>{if(e[t])e[t].push(o);else{var c,l;if(void 0!==a)for(var u=document.getElementsByTagName(\\"script\\"),s=0;s{c.onerror=c.onload=null,clearTimeout(f);var n=e[t];if(delete e[t],c.parentNode&&c.parentNode.removeChild(c),n&&n.forEach(e=>e(o)),r)return r(o)},f=setTimeout(d.bind(null,void 0,{type:\\"timeout\\",target:c}),12e4);c.onerror=d.bind(null,c.onerror),c.onload=d.bind(null,c.onload),l&&document.head.appendChild(c)}},n.r=e=>{\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},(()=>{var e;n.g.importScripts&&(e=n.g.location+\\"\\");var r=n.g.document;if(!e&&r&&(r.currentScript&&\\"SCRIPT\\"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName(\\"script\\");if(t.length)for(var o=t.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=t[o--].src}if(!e)throw new Error(\\"Automatic publicPath is not supported in this browser\\");e=e.replace(/^blob:/,\\"\\").replace(/#.*$/,\\"\\").replace(/\\\\?.*$/,\\"\\").replace(/\\\\/[^\\\\/]+$/,\\"/\\"),n.p=e})(),(()=>{var e={524:0};n.f.j=(r,t)=>{var o=n.o(e,r)?e[r]:void 0;if(0!==o)if(o)t.push(o[2]);else{var a=new Promise((t,n)=>o=e[r]=[t,n]);t.push(o[2]=a);var i=n.p+n.u(r),c=new Error;n.l(i,t=>{if(n.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var a=t&&(\\"load\\"===t.type?\\"missing\\":t.type),i=t&&t.target&&t.target.src;c.message=\\"Loading chunk \\"+r+\\" failed.\\\\n(\\"+a+\\": \\"+i+\\")\\",c.name=\\"ChunkLoadError\\",c.type=a,c.request=i,o[1](c)}},\\"chunk-\\"+r,r)}};var r=(r,t)=>{var o,a,[i,c,l]=t,u=0;if(i.some(r=>0!==e[r])){for(o in c)n.o(c,o)&&(n.m[o]=c[o]);if(l)l(n)}for(r&&r(t);u{console.log(\\"Good\\")})})();", } `; diff --git a/test/__snapshots__/extractComments-option.test.js.snap b/test/__snapshots__/extractComments-option.test.js.snap index 73bf2c7..f142cd8 100644 --- a/test/__snapshots__/extractComments-option.test.js.snap +++ b/test/__snapshots__/extractComments-option.test.js.snap @@ -4570,10 +4570,10 @@ exports[`extractComments option should match snapshot for a "function" value: wa exports[`extractComments option should match snapshot for comment file when filename is nested: assets 1`] = ` Object { - "nested/directory/203.js?8a13dd8f829eb92cd580": "/*! For license information please see ../../one.js */ + "nested/directory/203.js?feb1c3d9fc68922c4485": "/*! For license information please see ../../one.js */ (self.webpackChunkterser_webpack_plugin=self.webpackChunkterser_webpack_plugin||[]).push([[203],{203(e){e.exports=Math.random()}}]);", - "nested/directory/one.js?5dd3b3b60013d2b3d41e": "/*! For license information please see ../../one.js */ -(()=>{var e,t,r,o,n={855(e,t,r){r.e(203).then(r.t.bind(r,203,23)),e.exports=Math.random()}},a={};function i(e){var t=a[e];if(void 0!==t)return t.exports;var r=a[e]={exports:{}};return n[e](r,r.exports,i),r.exports}i.m=n,t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,i.t=function(r,o){if(1&o&&(r=this(r)),8&o)return r;if(\\"object\\"==typeof r&&r){if(4&o&&r.__esModule)return r;if(16&o&&\\"function\\"==typeof r.then)return r}var n=Object.create(null);i.r(n);var a={};e=e||[null,t({}),t([]),t(t)];for(var c=2&o&&r;(\\"object\\"==typeof c||\\"function\\"==typeof c)&&!~e.indexOf(c);c=t(c))Object.getOwnPropertyNames(c).forEach(e=>a[e]=()=>r[e]);return a.default=()=>r,i.d(n,a),n},i.d=(e,t)=>{for(var r in t)i.o(t,r)&&!i.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},i.f={},i.e=e=>Promise.all(Object.keys(i.f).reduce((t,r)=>(i.f[r](e,t),t),[])),i.u=e=>\\"nested/directory/\\"+e+\\".js?8a13dd8f829eb92cd580\\",i.g=function(){if(\\"object\\"==typeof globalThis)return globalThis;try{return this||new Function(\\"return this\\")()}catch(e){if(\\"object\\"==typeof window)return window}}(),i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r={},o=\\"terser-webpack-plugin:\\",i.l=(e,t,n,a)=>{if(r[e])r[e].push(t);else{var c,u;if(void 0!==n)for(var l=document.getElementsByTagName(\\"script\\"),p=0;p{c.onerror=c.onload=null,clearTimeout(d);var n=r[e];if(delete r[e],c.parentNode&&c.parentNode.removeChild(c),n&&n.forEach(e=>e(o)),t)return t(o)},d=setTimeout(f.bind(null,void 0,{type:\\"timeout\\",target:c}),12e4);c.onerror=f.bind(null,c.onerror),c.onload=f.bind(null,c.onload),u&&document.head.appendChild(c)}},i.r=e=>{\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},(()=>{var e;i.g.importScripts&&(e=i.g.location+\\"\\");var t=i.g.document;if(!e&&t&&(t.currentScript&&\\"SCRIPT\\"===t.currentScript.tagName.toUpperCase()&&(e=t.currentScript.src),!e)){var r=t.getElementsByTagName(\\"script\\");if(r.length)for(var o=r.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=r[o--].src}if(!e)throw new Error(\\"Automatic publicPath is not supported in this browser\\");e=e.replace(/^blob:/,\\"\\").replace(/#.*$/,\\"\\").replace(/\\\\?.*$/,\\"\\").replace(/\\\\/[^\\\\/]+$/,\\"/\\"),i.p=e+\\"../../\\"})(),(()=>{var e={101:0};i.f.j=(t,r)=>{var o=i.o(e,t)?e[t]:void 0;if(0!==o)if(o)r.push(o[2]);else{var n=new Promise((r,n)=>o=e[t]=[r,n]);r.push(o[2]=n);var a=i.p+i.u(t),c=new Error;i.l(a,r=>{if(i.o(e,t)&&(0!==(o=e[t])&&(e[t]=void 0),o)){var n=r&&(\\"load\\"===r.type?\\"missing\\":r.type),a=r&&r.target&&r.target.src;c.message=\\"Loading chunk \\"+t+\\" failed.\\\\n(\\"+n+\\": \\"+a+\\")\\",c.name=\\"ChunkLoadError\\",c.type=n,c.request=a,o[1](c)}},\\"chunk-\\"+t,t)}};var t=(t,r)=>{var o,n,[a,c,u]=r,l=0;if(a.some(t=>0!==e[t])){for(o in c)i.o(c,o)&&(i.m[o]=c[o]);if(u)u(i)}for(t&&t(r);l{var e,t,r,o,n={855(e,t,r){r.e(203).then(r.t.bind(r,203,23)),e.exports=Math.random()}},a={};function i(e){var t=a[e];if(void 0!==t)return t.exports;var r=a[e]={exports:{}};return n[e](r,r.exports,i),r.exports}i.m=n,t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,i.t=function(r,o){if(1&o&&(r=this(r)),8&o)return r;if(\\"object\\"==typeof r&&r){if(4&o&&r.__esModule)return r;if(16&o&&\\"function\\"==typeof r.then)return r}var n=Object.create(null);i.r(n);var a={};e=e||[null,t({}),t([]),t(t)];for(var c=2&o&&r;(\\"object\\"==typeof c||\\"function\\"==typeof c)&&!~e.indexOf(c);c=t(c))Object.getOwnPropertyNames(c).forEach(e=>a[e]=()=>r[e]);return a.default=()=>r,i.d(n,a),n},i.d=(e,t)=>{for(var r in t)i.o(t,r)&&!i.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},i.f={},i.e=e=>Promise.all(Object.keys(i.f).reduce((t,r)=>(i.f[r](e,t),t),[])),i.u=e=>\\"nested/directory/\\"+e+\\".js?feb1c3d9fc68922c4485\\",i.g=function(){if(\\"object\\"==typeof globalThis)return globalThis;try{return this||new Function(\\"return this\\")()}catch(e){if(\\"object\\"==typeof window)return window}}(),i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r={},o=\\"terser-webpack-plugin:\\",i.l=(e,t,n,a)=>{if(r[e])r[e].push(t);else{var c,u;if(void 0!==n)for(var l=document.getElementsByTagName(\\"script\\"),p=0;p{c.onerror=c.onload=null,clearTimeout(d);var n=r[e];if(delete r[e],c.parentNode&&c.parentNode.removeChild(c),n&&n.forEach(e=>e(o)),t)return t(o)},d=setTimeout(f.bind(null,void 0,{type:\\"timeout\\",target:c}),12e4);c.onerror=f.bind(null,c.onerror),c.onload=f.bind(null,c.onload),u&&document.head.appendChild(c)}},i.r=e=>{\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},(()=>{var e;i.g.importScripts&&(e=i.g.location+\\"\\");var t=i.g.document;if(!e&&t&&(t.currentScript&&\\"SCRIPT\\"===t.currentScript.tagName.toUpperCase()&&(e=t.currentScript.src),!e)){var r=t.getElementsByTagName(\\"script\\");if(r.length)for(var o=r.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=r[o--].src}if(!e)throw new Error(\\"Automatic publicPath is not supported in this browser\\");e=e.replace(/^blob:/,\\"\\").replace(/#.*$/,\\"\\").replace(/\\\\?.*$/,\\"\\").replace(/\\\\/[^\\\\/]+$/,\\"/\\"),i.p=e+\\"../../\\"})(),(()=>{var e={101:0};i.f.j=(t,r)=>{var o=i.o(e,t)?e[t]:void 0;if(0!==o)if(o)r.push(o[2]);else{var n=new Promise((r,n)=>o=e[t]=[r,n]);r.push(o[2]=n);var a=i.p+i.u(t),c=new Error;i.l(a,r=>{if(i.o(e,t)&&(0!==(o=e[t])&&(e[t]=void 0),o)){var n=r&&(\\"load\\"===r.type?\\"missing\\":r.type),a=r&&r.target&&r.target.src;c.message=\\"Loading chunk \\"+t+\\" failed.\\\\n(\\"+n+\\": \\"+a+\\")\\",c.name=\\"ChunkLoadError\\",c.type=n,c.request=a,o[1](c)}},\\"chunk-\\"+t,t)}};var t=(t,r)=>{var o,n,[a,c,u]=r,l=0;if(a.some(t=>0!==e[t])){for(o in c)i.o(c,o)&&(i.m[o]=c[o]);if(u)u(i)}for(t&&t(r);l{var r={250(r){r.exports=Math.random()}},t={};(function o(e){var a=t[e];if(void 0!==a)return a.exports;var n=t[e]={exports:{}};return r[e](n,n.exports,o),n.exports})(250)})();", - "filename/one.js.LICENSE.txt?d6977202ed375725f74a": "/*! Legal Comment */ + "filename/one.js.LICENSE.txt?c1b5a0c51e6d7fb1dc7b": "/*! Legal Comment */ /*! Legal Foo */ @@ -5560,9 +5560,9 @@ Object { // @lic ", - "filename/one.js?d6977202ed375725f74a": "/*! For license information please see one.js.LICENSE.txt?d6977202ed375725f74a */ -(()=>{var e,t,r,o,n={855(e,t,r){r.e(203).then(r.t.bind(r,203,23)),e.exports=Math.random()}},a={};function i(e){var t=a[e];if(void 0!==t)return t.exports;var r=a[e]={exports:{}};return n[e](r,r.exports,i),r.exports}i.m=n,t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,i.t=function(r,o){if(1&o&&(r=this(r)),8&o)return r;if(\\"object\\"==typeof r&&r){if(4&o&&r.__esModule)return r;if(16&o&&\\"function\\"==typeof r.then)return r}var n=Object.create(null);i.r(n);var a={};e=e||[null,t({}),t([]),t(t)];for(var c=2&o&&r;(\\"object\\"==typeof c||\\"function\\"==typeof c)&&!~e.indexOf(c);c=t(c))Object.getOwnPropertyNames(c).forEach(e=>a[e]=()=>r[e]);return a.default=()=>r,i.d(n,a),n},i.d=(e,t)=>{for(var r in t)i.o(t,r)&&!i.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},i.f={},i.e=e=>Promise.all(Object.keys(i.f).reduce((t,r)=>(i.f[r](e,t),t),[])),i.u=e=>\\"chunks/\\"+e+\\".\\"+e+\\".js?8a13dd8f829eb92cd580\\",i.g=function(){if(\\"object\\"==typeof globalThis)return globalThis;try{return this||new Function(\\"return this\\")()}catch(e){if(\\"object\\"==typeof window)return window}}(),i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r={},o=\\"terser-webpack-plugin:\\",i.l=(e,t,n,a)=>{if(r[e])r[e].push(t);else{var c,u;if(void 0!==n)for(var l=document.getElementsByTagName(\\"script\\"),p=0;p{c.onerror=c.onload=null,clearTimeout(d);var n=r[e];if(delete r[e],c.parentNode&&c.parentNode.removeChild(c),n&&n.forEach(e=>e(o)),t)return t(o)},d=setTimeout(f.bind(null,void 0,{type:\\"timeout\\",target:c}),12e4);c.onerror=f.bind(null,c.onerror),c.onload=f.bind(null,c.onload),u&&document.head.appendChild(c)}},i.r=e=>{\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},(()=>{var e;i.g.importScripts&&(e=i.g.location+\\"\\");var t=i.g.document;if(!e&&t&&(t.currentScript&&\\"SCRIPT\\"===t.currentScript.tagName.toUpperCase()&&(e=t.currentScript.src),!e)){var r=t.getElementsByTagName(\\"script\\");if(r.length)for(var o=r.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=r[o--].src}if(!e)throw new Error(\\"Automatic publicPath is not supported in this browser\\");e=e.replace(/^blob:/,\\"\\").replace(/#.*$/,\\"\\").replace(/\\\\?.*$/,\\"\\").replace(/\\\\/[^\\\\/]+$/,\\"/\\"),i.p=e+\\"../\\"})(),(()=>{var e={101:0};i.f.j=(t,r)=>{var o=i.o(e,t)?e[t]:void 0;if(0!==o)if(o)r.push(o[2]);else{var n=new Promise((r,n)=>o=e[t]=[r,n]);r.push(o[2]=n);var a=i.p+i.u(t),c=new Error;i.l(a,r=>{if(i.o(e,t)&&(0!==(o=e[t])&&(e[t]=void 0),o)){var n=r&&(\\"load\\"===r.type?\\"missing\\":r.type),a=r&&r.target&&r.target.src;c.message=\\"Loading chunk \\"+t+\\" failed.\\\\n(\\"+n+\\": \\"+a+\\")\\",c.name=\\"ChunkLoadError\\",c.type=n,c.request=a,o[1](c)}},\\"chunk-\\"+t,t)}};var t=(t,r)=>{var o,n,[a,c,u]=r,l=0;if(a.some(t=>0!==e[t])){for(o in c)i.o(c,o)&&(i.m[o]=c[o]);if(u)u(i)}for(t&&t(r);l{var e,t,r,o,n={855(e,t,r){r.e(203).then(r.t.bind(r,203,23)),e.exports=Math.random()}},a={};function i(e){var t=a[e];if(void 0!==t)return t.exports;var r=a[e]={exports:{}};return n[e](r,r.exports,i),r.exports}i.m=n,t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,i.t=function(r,o){if(1&o&&(r=this(r)),8&o)return r;if(\\"object\\"==typeof r&&r){if(4&o&&r.__esModule)return r;if(16&o&&\\"function\\"==typeof r.then)return r}var n=Object.create(null);i.r(n);var a={};e=e||[null,t({}),t([]),t(t)];for(var c=2&o&&r;(\\"object\\"==typeof c||\\"function\\"==typeof c)&&!~e.indexOf(c);c=t(c))Object.getOwnPropertyNames(c).forEach(e=>a[e]=()=>r[e]);return a.default=()=>r,i.d(n,a),n},i.d=(e,t)=>{for(var r in t)i.o(t,r)&&!i.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},i.f={},i.e=e=>Promise.all(Object.keys(i.f).reduce((t,r)=>(i.f[r](e,t),t),[])),i.u=e=>\\"chunks/\\"+e+\\".\\"+e+\\".js?feb1c3d9fc68922c4485\\",i.g=function(){if(\\"object\\"==typeof globalThis)return globalThis;try{return this||new Function(\\"return this\\")()}catch(e){if(\\"object\\"==typeof window)return window}}(),i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r={},o=\\"terser-webpack-plugin:\\",i.l=(e,t,n,a)=>{if(r[e])r[e].push(t);else{var c,u;if(void 0!==n)for(var l=document.getElementsByTagName(\\"script\\"),p=0;p{c.onerror=c.onload=null,clearTimeout(d);var n=r[e];if(delete r[e],c.parentNode&&c.parentNode.removeChild(c),n&&n.forEach(e=>e(o)),t)return t(o)},d=setTimeout(f.bind(null,void 0,{type:\\"timeout\\",target:c}),12e4);c.onerror=f.bind(null,c.onerror),c.onload=f.bind(null,c.onload),u&&document.head.appendChild(c)}},i.r=e=>{\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},(()=>{var e;i.g.importScripts&&(e=i.g.location+\\"\\");var t=i.g.document;if(!e&&t&&(t.currentScript&&\\"SCRIPT\\"===t.currentScript.tagName.toUpperCase()&&(e=t.currentScript.src),!e)){var r=t.getElementsByTagName(\\"script\\");if(r.length)for(var o=r.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=r[o--].src}if(!e)throw new Error(\\"Automatic publicPath is not supported in this browser\\");e=e.replace(/^blob:/,\\"\\").replace(/#.*$/,\\"\\").replace(/\\\\?.*$/,\\"\\").replace(/\\\\/[^\\\\/]+$/,\\"/\\"),i.p=e+\\"../\\"})(),(()=>{var e={101:0};i.f.j=(t,r)=>{var o=i.o(e,t)?e[t]:void 0;if(0!==o)if(o)r.push(o[2]);else{var n=new Promise((r,n)=>o=e[t]=[r,n]);r.push(o[2]=n);var a=i.p+i.u(t),c=new Error;i.l(a,r=>{if(i.o(e,t)&&(0!==(o=e[t])&&(e[t]=void 0),o)){var n=r&&(\\"load\\"===r.type?\\"missing\\":r.type),a=r&&r.target&&r.target.src;c.message=\\"Loading chunk \\"+t+\\" failed.\\\\n(\\"+n+\\": \\"+a+\\")\\",c.name=\\"ChunkLoadError\\",c.type=n,c.request=a,o[1](c)}},\\"chunk-\\"+t,t)}};var t=(t,r)=>{var o,n,[a,c,u]=r,l=0;if(a.some(t=>0!==e[t])){for(o in c)i.o(c,o)&&(i.m[o]=c[o]);if(u)u(i)}for(t&&t(r);l{var r={35(r){r.exports=Math.random()}},t={};(function o(e){var a=t[e];if(void 0!==a)return a.exports;var n=t[e]={exports:{}};return r[e](n,n.exports,o),n.exports})(35)})();", - "filename/two.js.LICENSE.txt?f0e2aecdcc89eab49b44": "/** + "filename/two.js.LICENSE.txt?41945d353512df22e788": "/** * Information. * @license MIT */ ", - "filename/two.js?f0e2aecdcc89eab49b44": "/*! For license information please see two.js.LICENSE.txt?f0e2aecdcc89eab49b44 */ + "filename/two.js?41945d353512df22e788": "/*! For license information please see two.js.LICENSE.txt?41945d353512df22e788 */ (()=>{var r={12(r){r.exports=Math.random()}},t={};(function o(e){var a=t[e];if(void 0!==a)return a.exports;var n=t[e]={exports:{}};return r[e](n,n.exports,o),n.exports})(12)})();", } `; @@ -5594,14 +5594,14 @@ Object { /** @license Copyright 2112 Moon. **/ ", - "chunks/203.203.js?8a13dd8f829eb92cd580": "/*! License information can be found in chunks/203.203.js.LICENSE.txt?query=&filebase=203.203.js */ + "chunks/203.203.js?feb1c3d9fc68922c4485": "/*! License information can be found in chunks/203.203.js.LICENSE.txt?query=&filebase=203.203.js */ (self.webpackChunkterser_webpack_plugin=self.webpackChunkterser_webpack_plugin||[]).push([[203],{203(e){e.exports=Math.random()}}]);", "filename/four.js.LICENSE.txt?query=&filebase=four.js": "/** * Duplicate comment in difference files. * @license MIT */ ", - "filename/four.js?0f89877a1b2619db055e": "/*! License information can be found in filename/four.js.LICENSE.txt?query=&filebase=four.js */ + "filename/four.js?0c2215c34600f4968cf1": "/*! License information can be found in filename/four.js.LICENSE.txt?query=&filebase=four.js */ (()=>{var r={250(r){r.exports=Math.random()}},t={};(function o(e){var a=t[e];if(void 0!==a)return a.exports;var n=t[e]={exports:{}};return r[e](n,n.exports,o),n.exports})(250)})();", "filename/one.js.LICENSE.txt?query=&filebase=one.js": "/*! Legal Comment */ @@ -5621,8 +5621,8 @@ Object { // @lic ", - "filename/one.js?d6977202ed375725f74a": "/*! License information can be found in filename/one.js.LICENSE.txt?query=&filebase=one.js */ -(()=>{var e,t,r,o,n={855(e,t,r){r.e(203).then(r.t.bind(r,203,23)),e.exports=Math.random()}},a={};function i(e){var t=a[e];if(void 0!==t)return t.exports;var r=a[e]={exports:{}};return n[e](r,r.exports,i),r.exports}i.m=n,t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,i.t=function(r,o){if(1&o&&(r=this(r)),8&o)return r;if(\\"object\\"==typeof r&&r){if(4&o&&r.__esModule)return r;if(16&o&&\\"function\\"==typeof r.then)return r}var n=Object.create(null);i.r(n);var a={};e=e||[null,t({}),t([]),t(t)];for(var c=2&o&&r;(\\"object\\"==typeof c||\\"function\\"==typeof c)&&!~e.indexOf(c);c=t(c))Object.getOwnPropertyNames(c).forEach(e=>a[e]=()=>r[e]);return a.default=()=>r,i.d(n,a),n},i.d=(e,t)=>{for(var r in t)i.o(t,r)&&!i.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},i.f={},i.e=e=>Promise.all(Object.keys(i.f).reduce((t,r)=>(i.f[r](e,t),t),[])),i.u=e=>\\"chunks/\\"+e+\\".\\"+e+\\".js?8a13dd8f829eb92cd580\\",i.g=function(){if(\\"object\\"==typeof globalThis)return globalThis;try{return this||new Function(\\"return this\\")()}catch(e){if(\\"object\\"==typeof window)return window}}(),i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r={},o=\\"terser-webpack-plugin:\\",i.l=(e,t,n,a)=>{if(r[e])r[e].push(t);else{var c,u;if(void 0!==n)for(var l=document.getElementsByTagName(\\"script\\"),p=0;p{c.onerror=c.onload=null,clearTimeout(d);var n=r[e];if(delete r[e],c.parentNode&&c.parentNode.removeChild(c),n&&n.forEach(e=>e(o)),t)return t(o)},d=setTimeout(f.bind(null,void 0,{type:\\"timeout\\",target:c}),12e4);c.onerror=f.bind(null,c.onerror),c.onload=f.bind(null,c.onload),u&&document.head.appendChild(c)}},i.r=e=>{\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},(()=>{var e;i.g.importScripts&&(e=i.g.location+\\"\\");var t=i.g.document;if(!e&&t&&(t.currentScript&&\\"SCRIPT\\"===t.currentScript.tagName.toUpperCase()&&(e=t.currentScript.src),!e)){var r=t.getElementsByTagName(\\"script\\");if(r.length)for(var o=r.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=r[o--].src}if(!e)throw new Error(\\"Automatic publicPath is not supported in this browser\\");e=e.replace(/^blob:/,\\"\\").replace(/#.*$/,\\"\\").replace(/\\\\?.*$/,\\"\\").replace(/\\\\/[^\\\\/]+$/,\\"/\\"),i.p=e+\\"../\\"})(),(()=>{var e={101:0};i.f.j=(t,r)=>{var o=i.o(e,t)?e[t]:void 0;if(0!==o)if(o)r.push(o[2]);else{var n=new Promise((r,n)=>o=e[t]=[r,n]);r.push(o[2]=n);var a=i.p+i.u(t),c=new Error;i.l(a,r=>{if(i.o(e,t)&&(0!==(o=e[t])&&(e[t]=void 0),o)){var n=r&&(\\"load\\"===r.type?\\"missing\\":r.type),a=r&&r.target&&r.target.src;c.message=\\"Loading chunk \\"+t+\\" failed.\\\\n(\\"+n+\\": \\"+a+\\")\\",c.name=\\"ChunkLoadError\\",c.type=n,c.request=a,o[1](c)}},\\"chunk-\\"+t,t)}};var t=(t,r)=>{var o,n,[a,c,u]=r,l=0;if(a.some(t=>0!==e[t])){for(o in c)i.o(c,o)&&(i.m[o]=c[o]);if(u)u(i)}for(t&&t(r);l{var e,t,r,o,n={855(e,t,r){r.e(203).then(r.t.bind(r,203,23)),e.exports=Math.random()}},a={};function i(e){var t=a[e];if(void 0!==t)return t.exports;var r=a[e]={exports:{}};return n[e](r,r.exports,i),r.exports}i.m=n,t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,i.t=function(r,o){if(1&o&&(r=this(r)),8&o)return r;if(\\"object\\"==typeof r&&r){if(4&o&&r.__esModule)return r;if(16&o&&\\"function\\"==typeof r.then)return r}var n=Object.create(null);i.r(n);var a={};e=e||[null,t({}),t([]),t(t)];for(var c=2&o&&r;(\\"object\\"==typeof c||\\"function\\"==typeof c)&&!~e.indexOf(c);c=t(c))Object.getOwnPropertyNames(c).forEach(e=>a[e]=()=>r[e]);return a.default=()=>r,i.d(n,a),n},i.d=(e,t)=>{for(var r in t)i.o(t,r)&&!i.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},i.f={},i.e=e=>Promise.all(Object.keys(i.f).reduce((t,r)=>(i.f[r](e,t),t),[])),i.u=e=>\\"chunks/\\"+e+\\".\\"+e+\\".js?feb1c3d9fc68922c4485\\",i.g=function(){if(\\"object\\"==typeof globalThis)return globalThis;try{return this||new Function(\\"return this\\")()}catch(e){if(\\"object\\"==typeof window)return window}}(),i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r={},o=\\"terser-webpack-plugin:\\",i.l=(e,t,n,a)=>{if(r[e])r[e].push(t);else{var c,u;if(void 0!==n)for(var l=document.getElementsByTagName(\\"script\\"),p=0;p{c.onerror=c.onload=null,clearTimeout(d);var n=r[e];if(delete r[e],c.parentNode&&c.parentNode.removeChild(c),n&&n.forEach(e=>e(o)),t)return t(o)},d=setTimeout(f.bind(null,void 0,{type:\\"timeout\\",target:c}),12e4);c.onerror=f.bind(null,c.onerror),c.onload=f.bind(null,c.onload),u&&document.head.appendChild(c)}},i.r=e=>{\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},(()=>{var e;i.g.importScripts&&(e=i.g.location+\\"\\");var t=i.g.document;if(!e&&t&&(t.currentScript&&\\"SCRIPT\\"===t.currentScript.tagName.toUpperCase()&&(e=t.currentScript.src),!e)){var r=t.getElementsByTagName(\\"script\\");if(r.length)for(var o=r.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=r[o--].src}if(!e)throw new Error(\\"Automatic publicPath is not supported in this browser\\");e=e.replace(/^blob:/,\\"\\").replace(/#.*$/,\\"\\").replace(/\\\\?.*$/,\\"\\").replace(/\\\\/[^\\\\/]+$/,\\"/\\"),i.p=e+\\"../\\"})(),(()=>{var e={101:0};i.f.j=(t,r)=>{var o=i.o(e,t)?e[t]:void 0;if(0!==o)if(o)r.push(o[2]);else{var n=new Promise((r,n)=>o=e[t]=[r,n]);r.push(o[2]=n);var a=i.p+i.u(t),c=new Error;i.l(a,r=>{if(i.o(e,t)&&(0!==(o=e[t])&&(e[t]=void 0),o)){var n=r&&(\\"load\\"===r.type?\\"missing\\":r.type),a=r&&r.target&&r.target.src;c.message=\\"Loading chunk \\"+t+\\" failed.\\\\n(\\"+n+\\": \\"+a+\\")\\",c.name=\\"ChunkLoadError\\",c.type=n,c.request=a,o[1](c)}},\\"chunk-\\"+t,t)}};var t=(t,r)=>{var o,n,[a,c,u]=r,l=0;if(a.some(t=>0!==e[t])){for(o in c)i.o(c,o)&&(i.m[o]=c[o]);if(u)u(i)}for(t&&t(r);l{var r={35(r){r.exports=Math.random()}},t={};(function o(e){var a=t[e];if(void 0!==a)return a.exports;var n=t[e]={exports:{}};return r[e](n,n.exports,o),n.exports})(35)})();", "filename/two.js.LICENSE.txt?query=&filebase=two.js": "/** * Information. * @license MIT */ ", - "filename/two.js?f0e2aecdcc89eab49b44": "/*! License information can be found in filename/two.js.LICENSE.txt?query=&filebase=two.js */ + "filename/two.js?41945d353512df22e788": "/*! License information can be found in filename/two.js.LICENSE.txt?query=&filebase=two.js */ (()=>{var r={12(r){r.exports=Math.random()}},t={};(function o(e){var a=t[e];if(void 0!==a)return a.exports;var n=t[e]={exports:{}};return r[e](n,n.exports,o),n.exports})(12)})();", } `; @@ -5655,14 +5655,14 @@ Object { /** @license Copyright 2112 Moon. **/ ", - "chunks/203.203.js?8a13dd8f829eb92cd580": "/*! For license information please see 203.203.js.LICENSE.txt */ + "chunks/203.203.js?feb1c3d9fc68922c4485": "/*! For license information please see 203.203.js.LICENSE.txt */ (self.webpackChunkterser_webpack_plugin=self.webpackChunkterser_webpack_plugin||[]).push([[203],{203(e){e.exports=Math.random()}}]);", "filename/four.js.LICENSE.txt": "/** * Duplicate comment in difference files. * @license MIT */ ", - "filename/four.js?0f89877a1b2619db055e": "/*! For license information please see four.js.LICENSE.txt */ + "filename/four.js?0c2215c34600f4968cf1": "/*! For license information please see four.js.LICENSE.txt */ (()=>{var r={250(r){r.exports=Math.random()}},t={};(function o(e){var a=t[e];if(void 0!==a)return a.exports;var n=t[e]={exports:{}};return r[e](n,n.exports,o),n.exports})(250)})();", "filename/one.js.LICENSE.txt": "/*! Legal Comment */ @@ -5682,8 +5682,8 @@ Object { // @lic ", - "filename/one.js?d6977202ed375725f74a": "/*! For license information please see one.js.LICENSE.txt */ -(()=>{var e,t,r,o,n={855(e,t,r){r.e(203).then(r.t.bind(r,203,23)),e.exports=Math.random()}},a={};function i(e){var t=a[e];if(void 0!==t)return t.exports;var r=a[e]={exports:{}};return n[e](r,r.exports,i),r.exports}i.m=n,t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,i.t=function(r,o){if(1&o&&(r=this(r)),8&o)return r;if(\\"object\\"==typeof r&&r){if(4&o&&r.__esModule)return r;if(16&o&&\\"function\\"==typeof r.then)return r}var n=Object.create(null);i.r(n);var a={};e=e||[null,t({}),t([]),t(t)];for(var c=2&o&&r;(\\"object\\"==typeof c||\\"function\\"==typeof c)&&!~e.indexOf(c);c=t(c))Object.getOwnPropertyNames(c).forEach(e=>a[e]=()=>r[e]);return a.default=()=>r,i.d(n,a),n},i.d=(e,t)=>{for(var r in t)i.o(t,r)&&!i.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},i.f={},i.e=e=>Promise.all(Object.keys(i.f).reduce((t,r)=>(i.f[r](e,t),t),[])),i.u=e=>\\"chunks/\\"+e+\\".\\"+e+\\".js?8a13dd8f829eb92cd580\\",i.g=function(){if(\\"object\\"==typeof globalThis)return globalThis;try{return this||new Function(\\"return this\\")()}catch(e){if(\\"object\\"==typeof window)return window}}(),i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r={},o=\\"terser-webpack-plugin:\\",i.l=(e,t,n,a)=>{if(r[e])r[e].push(t);else{var c,u;if(void 0!==n)for(var l=document.getElementsByTagName(\\"script\\"),p=0;p{c.onerror=c.onload=null,clearTimeout(d);var n=r[e];if(delete r[e],c.parentNode&&c.parentNode.removeChild(c),n&&n.forEach(e=>e(o)),t)return t(o)},d=setTimeout(f.bind(null,void 0,{type:\\"timeout\\",target:c}),12e4);c.onerror=f.bind(null,c.onerror),c.onload=f.bind(null,c.onload),u&&document.head.appendChild(c)}},i.r=e=>{\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},(()=>{var e;i.g.importScripts&&(e=i.g.location+\\"\\");var t=i.g.document;if(!e&&t&&(t.currentScript&&\\"SCRIPT\\"===t.currentScript.tagName.toUpperCase()&&(e=t.currentScript.src),!e)){var r=t.getElementsByTagName(\\"script\\");if(r.length)for(var o=r.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=r[o--].src}if(!e)throw new Error(\\"Automatic publicPath is not supported in this browser\\");e=e.replace(/^blob:/,\\"\\").replace(/#.*$/,\\"\\").replace(/\\\\?.*$/,\\"\\").replace(/\\\\/[^\\\\/]+$/,\\"/\\"),i.p=e+\\"../\\"})(),(()=>{var e={101:0};i.f.j=(t,r)=>{var o=i.o(e,t)?e[t]:void 0;if(0!==o)if(o)r.push(o[2]);else{var n=new Promise((r,n)=>o=e[t]=[r,n]);r.push(o[2]=n);var a=i.p+i.u(t),c=new Error;i.l(a,r=>{if(i.o(e,t)&&(0!==(o=e[t])&&(e[t]=void 0),o)){var n=r&&(\\"load\\"===r.type?\\"missing\\":r.type),a=r&&r.target&&r.target.src;c.message=\\"Loading chunk \\"+t+\\" failed.\\\\n(\\"+n+\\": \\"+a+\\")\\",c.name=\\"ChunkLoadError\\",c.type=n,c.request=a,o[1](c)}},\\"chunk-\\"+t,t)}};var t=(t,r)=>{var o,n,[a,c,u]=r,l=0;if(a.some(t=>0!==e[t])){for(o in c)i.o(c,o)&&(i.m[o]=c[o]);if(u)u(i)}for(t&&t(r);l{var e,t,r,o,n={855(e,t,r){r.e(203).then(r.t.bind(r,203,23)),e.exports=Math.random()}},a={};function i(e){var t=a[e];if(void 0!==t)return t.exports;var r=a[e]={exports:{}};return n[e](r,r.exports,i),r.exports}i.m=n,t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,i.t=function(r,o){if(1&o&&(r=this(r)),8&o)return r;if(\\"object\\"==typeof r&&r){if(4&o&&r.__esModule)return r;if(16&o&&\\"function\\"==typeof r.then)return r}var n=Object.create(null);i.r(n);var a={};e=e||[null,t({}),t([]),t(t)];for(var c=2&o&&r;(\\"object\\"==typeof c||\\"function\\"==typeof c)&&!~e.indexOf(c);c=t(c))Object.getOwnPropertyNames(c).forEach(e=>a[e]=()=>r[e]);return a.default=()=>r,i.d(n,a),n},i.d=(e,t)=>{for(var r in t)i.o(t,r)&&!i.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},i.f={},i.e=e=>Promise.all(Object.keys(i.f).reduce((t,r)=>(i.f[r](e,t),t),[])),i.u=e=>\\"chunks/\\"+e+\\".\\"+e+\\".js?feb1c3d9fc68922c4485\\",i.g=function(){if(\\"object\\"==typeof globalThis)return globalThis;try{return this||new Function(\\"return this\\")()}catch(e){if(\\"object\\"==typeof window)return window}}(),i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r={},o=\\"terser-webpack-plugin:\\",i.l=(e,t,n,a)=>{if(r[e])r[e].push(t);else{var c,u;if(void 0!==n)for(var l=document.getElementsByTagName(\\"script\\"),p=0;p{c.onerror=c.onload=null,clearTimeout(d);var n=r[e];if(delete r[e],c.parentNode&&c.parentNode.removeChild(c),n&&n.forEach(e=>e(o)),t)return t(o)},d=setTimeout(f.bind(null,void 0,{type:\\"timeout\\",target:c}),12e4);c.onerror=f.bind(null,c.onerror),c.onload=f.bind(null,c.onload),u&&document.head.appendChild(c)}},i.r=e=>{\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},(()=>{var e;i.g.importScripts&&(e=i.g.location+\\"\\");var t=i.g.document;if(!e&&t&&(t.currentScript&&\\"SCRIPT\\"===t.currentScript.tagName.toUpperCase()&&(e=t.currentScript.src),!e)){var r=t.getElementsByTagName(\\"script\\");if(r.length)for(var o=r.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=r[o--].src}if(!e)throw new Error(\\"Automatic publicPath is not supported in this browser\\");e=e.replace(/^blob:/,\\"\\").replace(/#.*$/,\\"\\").replace(/\\\\?.*$/,\\"\\").replace(/\\\\/[^\\\\/]+$/,\\"/\\"),i.p=e+\\"../\\"})(),(()=>{var e={101:0};i.f.j=(t,r)=>{var o=i.o(e,t)?e[t]:void 0;if(0!==o)if(o)r.push(o[2]);else{var n=new Promise((r,n)=>o=e[t]=[r,n]);r.push(o[2]=n);var a=i.p+i.u(t),c=new Error;i.l(a,r=>{if(i.o(e,t)&&(0!==(o=e[t])&&(e[t]=void 0),o)){var n=r&&(\\"load\\"===r.type?\\"missing\\":r.type),a=r&&r.target&&r.target.src;c.message=\\"Loading chunk \\"+t+\\" failed.\\\\n(\\"+n+\\": \\"+a+\\")\\",c.name=\\"ChunkLoadError\\",c.type=n,c.request=a,o[1](c)}},\\"chunk-\\"+t,t)}};var t=(t,r)=>{var o,n,[a,c,u]=r,l=0;if(a.some(t=>0!==e[t])){for(o in c)i.o(c,o)&&(i.m[o]=c[o]);if(u)u(i)}for(t&&t(r);l{var r={35(r){r.exports=Math.random()}},t={};(function o(e){var a=t[e];if(void 0!==a)return a.exports;var n=t[e]={exports:{}};return r[e](n,n.exports,o),n.exports})(35)})();", "filename/two.js.LICENSE.txt": "/** * Information. * @license MIT */ ", - "filename/two.js?f0e2aecdcc89eab49b44": "/*! For license information please see two.js.LICENSE.txt */ + "filename/two.js?41945d353512df22e788": "/*! For license information please see two.js.LICENSE.txt */ (()=>{var r={12(r){r.exports=Math.random()}},t={};(function o(e){var a=t[e];if(void 0!==a)return a.exports;var n=t[e]={exports:{}};return r[e](n,n.exports,o),n.exports})(12)})();", } `; diff --git a/test/__snapshots__/test-option.test.js.snap b/test/__snapshots__/test-option.test.js.snap index 8931093..9c9aabb 100644 --- a/test/__snapshots__/test-option.test.js.snap +++ b/test/__snapshots__/test-option.test.js.snap @@ -2,11 +2,11 @@ exports[`test option should match snapshot and uglify "mjs": assets 1`] = ` Object { - "389.389.mjs?ver=265eb92bce83b7be4c6e": "\\"use strict\\";(self.webpackChunkterser_webpack_plugin=self.webpackChunkterser_webpack_plugin||[]).push([[389],{389(e,s,p){p.r(s),p.d(s,{default:()=>c});const c=\\"async-dep\\"}}]);", - "AsyncImportExport.mjs?var=265eb92bce83b7be4c6e": "(()=>{\\"use strict\\";var e,r,t={},o={};function n(e){var r=o[e];if(void 0!==r)return r.exports;var a=o[e]={exports:{}};return t[e](a,a.exports,n),a.exports}n.m=t,n.d=(e,r)=>{for(var t in r)n.o(r,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce((r,t)=>(n.f[t](e,r),r),[])),n.u=e=>e+\\".\\"+e+\\".mjs?ver=\\"+n.h(),n.h=()=>\\"265eb92bce83b7be4c6e\\",n.g=function(){if(\\"object\\"==typeof globalThis)return globalThis;try{return this||new Function(\\"return this\\")()}catch(e){if(\\"object\\"==typeof window)return window}}(),n.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),e={},r=\\"terser-webpack-plugin:\\",n.l=(t,o,a,i)=>{if(e[t])e[t].push(o);else{var l,c;if(void 0!==a)for(var u=document.getElementsByTagName(\\"script\\"),s=0;s{l.onerror=l.onload=null,clearTimeout(b);var n=e[t];if(delete e[t],l.parentNode&&l.parentNode.removeChild(l),n&&n.forEach(e=>e(o)),r)return r(o)},b=setTimeout(d.bind(null,void 0,{type:\\"timeout\\",target:l}),12e4);l.onerror=d.bind(null,l.onerror),l.onload=d.bind(null,l.onload),c&&document.head.appendChild(l)}},n.r=e=>{\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},(()=>{var e;n.g.importScripts&&(e=n.g.location+\\"\\");var r=n.g.document;if(!e&&r&&(r.currentScript&&\\"SCRIPT\\"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName(\\"script\\");if(t.length)for(var o=t.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=t[o--].src}if(!e)throw new Error(\\"Automatic publicPath is not supported in this browser\\");e=e.replace(/^blob:/,\\"\\").replace(/#.*$/,\\"\\").replace(/\\\\?.*$/,\\"\\").replace(/\\\\/[^\\\\/]+$/,\\"/\\"),n.p=e})(),(()=>{var e={988:0};n.f.j=(r,t)=>{var o=n.o(e,r)?e[r]:void 0;if(0!==o)if(o)t.push(o[2]);else{var a=new Promise((t,n)=>o=e[r]=[t,n]);t.push(o[2]=a);var i=n.p+n.u(r),l=new Error;n.l(i,t=>{if(n.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var a=t&&(\\"load\\"===t.type?\\"missing\\":t.type),i=t&&t.target&&t.target.src;l.message=\\"Loading chunk \\"+r+\\" failed.\\\\n(\\"+a+\\": \\"+i+\\")\\",l.name=\\"ChunkLoadError\\",l.type=a,l.request=i,o[1](l)}},\\"chunk-\\"+r,r)}};var r=(r,t)=>{var o,a,[i,l,c]=t,u=0;if(i.some(r=>0!==e[r])){for(o in l)n.o(l,o)&&(n.m[o]=l[o]);if(c)c(n)}for(r&&r(t);u{console.log(\\"Good\\")})})();", - "importExport.mjs?var=265eb92bce83b7be4c6e": "(()=>{\\"use strict\\";function o(){const o=\`baz\${Math.random()}\`;return()=>({a:\\"foobar\\"+o,b:\\"foo\\",baz:o})}console.log(o())})();", - "js.mjs?var=265eb92bce83b7be4c6e": "(()=>{var r={921(r){r.exports=function(){console.log(7)}}},o={};(function t(e){var n=o[e];if(void 0!==n)return n.exports;var s=o[e]={exports:{}};return r[e](s,s.exports,t),s.exports})(921)})();", - "mjs.mjs?var=265eb92bce83b7be4c6e": "(()=>{\\"use strict\\";function o(){console.log(11)}o()})();", + "389.389.mjs?ver=8cabdf2fe0b1cdf45bee": "\\"use strict\\";(self.webpackChunkterser_webpack_plugin=self.webpackChunkterser_webpack_plugin||[]).push([[389],{389(e,s,p){p.r(s),p.d(s,{default:()=>c});const c=\\"async-dep\\"}}]);", + "AsyncImportExport.mjs?var=8cabdf2fe0b1cdf45bee": "(()=>{\\"use strict\\";var e,r,t={},o={};function n(e){var r=o[e];if(void 0!==r)return r.exports;var a=o[e]={exports:{}};return t[e](a,a.exports,n),a.exports}n.m=t,n.d=(e,r)=>{for(var t in r)n.o(r,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce((r,t)=>(n.f[t](e,r),r),[])),n.u=e=>e+\\".\\"+e+\\".mjs?ver=\\"+n.h(),n.h=()=>\\"8cabdf2fe0b1cdf45bee\\",n.g=function(){if(\\"object\\"==typeof globalThis)return globalThis;try{return this||new Function(\\"return this\\")()}catch(e){if(\\"object\\"==typeof window)return window}}(),n.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),e={},r=\\"terser-webpack-plugin:\\",n.l=(t,o,a,i)=>{if(e[t])e[t].push(o);else{var l,c;if(void 0!==a)for(var u=document.getElementsByTagName(\\"script\\"),s=0;s{l.onerror=l.onload=null,clearTimeout(f);var n=e[t];if(delete e[t],l.parentNode&&l.parentNode.removeChild(l),n&&n.forEach(e=>e(o)),r)return r(o)},f=setTimeout(d.bind(null,void 0,{type:\\"timeout\\",target:l}),12e4);l.onerror=d.bind(null,l.onerror),l.onload=d.bind(null,l.onload),c&&document.head.appendChild(l)}},n.r=e=>{\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},(()=>{var e;n.g.importScripts&&(e=n.g.location+\\"\\");var r=n.g.document;if(!e&&r&&(r.currentScript&&\\"SCRIPT\\"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName(\\"script\\");if(t.length)for(var o=t.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=t[o--].src}if(!e)throw new Error(\\"Automatic publicPath is not supported in this browser\\");e=e.replace(/^blob:/,\\"\\").replace(/#.*$/,\\"\\").replace(/\\\\?.*$/,\\"\\").replace(/\\\\/[^\\\\/]+$/,\\"/\\"),n.p=e})(),(()=>{var e={988:0};n.f.j=(r,t)=>{var o=n.o(e,r)?e[r]:void 0;if(0!==o)if(o)t.push(o[2]);else{var a=new Promise((t,n)=>o=e[r]=[t,n]);t.push(o[2]=a);var i=n.p+n.u(r),l=new Error;n.l(i,t=>{if(n.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var a=t&&(\\"load\\"===t.type?\\"missing\\":t.type),i=t&&t.target&&t.target.src;l.message=\\"Loading chunk \\"+r+\\" failed.\\\\n(\\"+a+\\": \\"+i+\\")\\",l.name=\\"ChunkLoadError\\",l.type=a,l.request=i,o[1](l)}},\\"chunk-\\"+r,r)}};var r=(r,t)=>{var o,a,[i,l,c]=t,u=0;if(i.some(r=>0!==e[r])){for(o in l)n.o(l,o)&&(n.m[o]=l[o]);if(c)c(n)}for(r&&r(t);u{console.log(\\"Good\\")})})();", + "importExport.mjs?var=8cabdf2fe0b1cdf45bee": "(()=>{\\"use strict\\";function o(){const o=\`baz\${Math.random()}\`;return()=>({a:\\"foobar\\"+o,b:\\"foo\\",baz:o})}console.log(o())})();", + "js.mjs?var=8cabdf2fe0b1cdf45bee": "(()=>{var r={921(r){r.exports=function(){console.log(7)}}},o={};(function t(e){var n=o[e];if(void 0!==n)return n.exports;var s=o[e]={exports:{}};return r[e](s,s.exports,t),s.exports})(921)})();", + "mjs.mjs?var=8cabdf2fe0b1cdf45bee": "(()=>{\\"use strict\\";function o(){console.log(11)}o()})();", } `; @@ -16,7 +16,7 @@ exports[`test option should match snapshot and uglify "mjs": warnings 1`] = `Arr exports[`test option should match snapshot for a single "test" value ({String}): assets 1`] = ` Object { - "389.389.js?ver=d569fb48b799414bf71c": "\\"use strict\\"; + "389.389.js?ver=aa28aebb3049d51e5197": "\\"use strict\\"; (self[\\"webpackChunkterser_webpack_plugin\\"] = self[\\"webpackChunkterser_webpack_plugin\\"] || []).push([[389],{ /***/ 389 @@ -32,7 +32,7 @@ __webpack_require__.r(__webpack_exports__); /***/ } }]);", - "AsyncImportExport.js?var=d569fb48b799414bf71c": "/******/ (() => { // webpackBootstrap + "AsyncImportExport.js?var=aa28aebb3049d51e5197": "/******/ (() => { // webpackBootstrap /******/ \\"use strict\\"; /******/ var __webpack_modules__ = ({}); /************************************************************************/ @@ -100,7 +100,7 @@ __webpack_require__.r(__webpack_exports__); /******/ /******/ /* webpack/runtime/getFullHash */ /******/ (() => { -/******/ __webpack_require__.h = () => (\\"d569fb48b799414bf71c\\") +/******/ __webpack_require__.h = () => (\\"aa28aebb3049d51e5197\\") /******/ })(); /******/ /******/ /* webpack/runtime/global */ @@ -299,7 +299,7 @@ __webpack_require__.e(/* import() */ 389).then(__webpack_require__.bind(__webpac /******/ })() ;", - "importExport.js?var=d569fb48b799414bf71c": "/******/ (() => { // webpackBootstrap + "importExport.js?var=aa28aebb3049d51e5197": "/******/ (() => { // webpackBootstrap /******/ \\"use strict\\"; // UNUSED EXPORTS: default @@ -329,8 +329,8 @@ console.log(Foo()); /******/ })() ;", - "js.js?var=d569fb48b799414bf71c": "(()=>{var r={921(r){r.exports=function(){console.log(7)}}},o={};(function t(e){var n=o[e];if(void 0!==n)return n.exports;var s=o[e]={exports:{}};return r[e](s,s.exports,t),s.exports})(921)})();", - "mjs.js?var=d569fb48b799414bf71c": "/******/ (() => { // webpackBootstrap + "js.js?var=aa28aebb3049d51e5197": "(()=>{var r={921(r){r.exports=function(){console.log(7)}}},o={};(function t(e){var n=o[e];if(void 0!==n)return n.exports;var s=o[e]={exports:{}};return r[e](s,s.exports,t),s.exports})(921)})();", + "mjs.js?var=aa28aebb3049d51e5197": "/******/ (() => { // webpackBootstrap /******/ \\"use strict\\"; // foo // bar @@ -356,7 +356,7 @@ exports[`test option should match snapshot for a single "test" value ({String}): exports[`test option should match snapshot for a single \`test\` value ({RegExp}): assets 1`] = ` Object { - "389.389.js?ver=d569fb48b799414bf71c": "\\"use strict\\"; + "389.389.js?ver=aa28aebb3049d51e5197": "\\"use strict\\"; (self[\\"webpackChunkterser_webpack_plugin\\"] = self[\\"webpackChunkterser_webpack_plugin\\"] || []).push([[389],{ /***/ 389 @@ -372,7 +372,7 @@ __webpack_require__.r(__webpack_exports__); /***/ } }]);", - "AsyncImportExport.js?var=d569fb48b799414bf71c": "/******/ (() => { // webpackBootstrap + "AsyncImportExport.js?var=aa28aebb3049d51e5197": "/******/ (() => { // webpackBootstrap /******/ \\"use strict\\"; /******/ var __webpack_modules__ = ({}); /************************************************************************/ @@ -440,7 +440,7 @@ __webpack_require__.r(__webpack_exports__); /******/ /******/ /* webpack/runtime/getFullHash */ /******/ (() => { -/******/ __webpack_require__.h = () => (\\"d569fb48b799414bf71c\\") +/******/ __webpack_require__.h = () => (\\"aa28aebb3049d51e5197\\") /******/ })(); /******/ /******/ /* webpack/runtime/global */ @@ -639,7 +639,7 @@ __webpack_require__.e(/* import() */ 389).then(__webpack_require__.bind(__webpac /******/ })() ;", - "importExport.js?var=d569fb48b799414bf71c": "/******/ (() => { // webpackBootstrap + "importExport.js?var=aa28aebb3049d51e5197": "/******/ (() => { // webpackBootstrap /******/ \\"use strict\\"; // UNUSED EXPORTS: default @@ -669,8 +669,8 @@ console.log(Foo()); /******/ })() ;", - "js.js?var=d569fb48b799414bf71c": "(()=>{var r={921(r){r.exports=function(){console.log(7)}}},o={};(function t(e){var n=o[e];if(void 0!==n)return n.exports;var s=o[e]={exports:{}};return r[e](s,s.exports,t),s.exports})(921)})();", - "mjs.js?var=d569fb48b799414bf71c": "(()=>{\\"use strict\\";function o(){console.log(11)}o()})();", + "js.js?var=aa28aebb3049d51e5197": "(()=>{var r={921(r){r.exports=function(){console.log(7)}}},o={};(function t(e){var n=o[e];if(void 0!==n)return n.exports;var s=o[e]={exports:{}};return r[e](s,s.exports,t),s.exports})(921)})();", + "mjs.js?var=aa28aebb3049d51e5197": "(()=>{\\"use strict\\";function o(){console.log(11)}o()})();", } `; @@ -680,7 +680,7 @@ exports[`test option should match snapshot for a single \`test\` value ({RegExp} exports[`test option should match snapshot for multiple "test" values ({RegExp}): assets 1`] = ` Object { - "389.389.js?ver=d569fb48b799414bf71c": "\\"use strict\\"; + "389.389.js?ver=aa28aebb3049d51e5197": "\\"use strict\\"; (self[\\"webpackChunkterser_webpack_plugin\\"] = self[\\"webpackChunkterser_webpack_plugin\\"] || []).push([[389],{ /***/ 389 @@ -696,8 +696,8 @@ __webpack_require__.r(__webpack_exports__); /***/ } }]);", - "AsyncImportExport.js?var=d569fb48b799414bf71c": "(()=>{\\"use strict\\";var e,r,t={},o={};function n(e){var r=o[e];if(void 0!==r)return r.exports;var a=o[e]={exports:{}};return t[e](a,a.exports,n),a.exports}n.m=t,n.d=(e,r)=>{for(var t in r)n.o(r,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce((r,t)=>(n.f[t](e,r),r),[])),n.u=e=>e+\\".\\"+e+\\".js?ver=\\"+n.h(),n.h=()=>\\"d569fb48b799414bf71c\\",n.g=function(){if(\\"object\\"==typeof globalThis)return globalThis;try{return this||new Function(\\"return this\\")()}catch(e){if(\\"object\\"==typeof window)return window}}(),n.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),e={},r=\\"terser-webpack-plugin:\\",n.l=(t,o,a,i)=>{if(e[t])e[t].push(o);else{var l,c;if(void 0!==a)for(var u=document.getElementsByTagName(\\"script\\"),s=0;s{l.onerror=l.onload=null,clearTimeout(f);var n=e[t];if(delete e[t],l.parentNode&&l.parentNode.removeChild(l),n&&n.forEach(e=>e(o)),r)return r(o)},f=setTimeout(d.bind(null,void 0,{type:\\"timeout\\",target:l}),12e4);l.onerror=d.bind(null,l.onerror),l.onload=d.bind(null,l.onload),c&&document.head.appendChild(l)}},n.r=e=>{\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},(()=>{var e;n.g.importScripts&&(e=n.g.location+\\"\\");var r=n.g.document;if(!e&&r&&(r.currentScript&&\\"SCRIPT\\"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName(\\"script\\");if(t.length)for(var o=t.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=t[o--].src}if(!e)throw new Error(\\"Automatic publicPath is not supported in this browser\\");e=e.replace(/^blob:/,\\"\\").replace(/#.*$/,\\"\\").replace(/\\\\?.*$/,\\"\\").replace(/\\\\/[^\\\\/]+$/,\\"/\\"),n.p=e})(),(()=>{var e={988:0};n.f.j=(r,t)=>{var o=n.o(e,r)?e[r]:void 0;if(0!==o)if(o)t.push(o[2]);else{var a=new Promise((t,n)=>o=e[r]=[t,n]);t.push(o[2]=a);var i=n.p+n.u(r),l=new Error;n.l(i,t=>{if(n.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var a=t&&(\\"load\\"===t.type?\\"missing\\":t.type),i=t&&t.target&&t.target.src;l.message=\\"Loading chunk \\"+r+\\" failed.\\\\n(\\"+a+\\": \\"+i+\\")\\",l.name=\\"ChunkLoadError\\",l.type=a,l.request=i,o[1](l)}},\\"chunk-\\"+r,r)}};var r=(r,t)=>{var o,a,[i,l,c]=t,u=0;if(i.some(r=>0!==e[r])){for(o in l)n.o(l,o)&&(n.m[o]=l[o]);if(c)c(n)}for(r&&r(t);u{console.log(\\"Good\\")})})();", - "importExport.js?var=d569fb48b799414bf71c": "/******/ (() => { // webpackBootstrap + "AsyncImportExport.js?var=aa28aebb3049d51e5197": "(()=>{\\"use strict\\";var e,r,t={},o={};function n(e){var r=o[e];if(void 0!==r)return r.exports;var a=o[e]={exports:{}};return t[e](a,a.exports,n),a.exports}n.m=t,n.d=(e,r)=>{for(var t in r)n.o(r,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce((r,t)=>(n.f[t](e,r),r),[])),n.u=e=>e+\\".\\"+e+\\".js?ver=\\"+n.h(),n.h=()=>\\"aa28aebb3049d51e5197\\",n.g=function(){if(\\"object\\"==typeof globalThis)return globalThis;try{return this||new Function(\\"return this\\")()}catch(e){if(\\"object\\"==typeof window)return window}}(),n.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),e={},r=\\"terser-webpack-plugin:\\",n.l=(t,o,a,i)=>{if(e[t])e[t].push(o);else{var l,c;if(void 0!==a)for(var u=document.getElementsByTagName(\\"script\\"),s=0;s{l.onerror=l.onload=null,clearTimeout(f);var n=e[t];if(delete e[t],l.parentNode&&l.parentNode.removeChild(l),n&&n.forEach(e=>e(o)),r)return r(o)},f=setTimeout(d.bind(null,void 0,{type:\\"timeout\\",target:l}),12e4);l.onerror=d.bind(null,l.onerror),l.onload=d.bind(null,l.onload),c&&document.head.appendChild(l)}},n.r=e=>{\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},(()=>{var e;n.g.importScripts&&(e=n.g.location+\\"\\");var r=n.g.document;if(!e&&r&&(r.currentScript&&\\"SCRIPT\\"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName(\\"script\\");if(t.length)for(var o=t.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=t[o--].src}if(!e)throw new Error(\\"Automatic publicPath is not supported in this browser\\");e=e.replace(/^blob:/,\\"\\").replace(/#.*$/,\\"\\").replace(/\\\\?.*$/,\\"\\").replace(/\\\\/[^\\\\/]+$/,\\"/\\"),n.p=e})(),(()=>{var e={988:0};n.f.j=(r,t)=>{var o=n.o(e,r)?e[r]:void 0;if(0!==o)if(o)t.push(o[2]);else{var a=new Promise((t,n)=>o=e[r]=[t,n]);t.push(o[2]=a);var i=n.p+n.u(r),l=new Error;n.l(i,t=>{if(n.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var a=t&&(\\"load\\"===t.type?\\"missing\\":t.type),i=t&&t.target&&t.target.src;l.message=\\"Loading chunk \\"+r+\\" failed.\\\\n(\\"+a+\\": \\"+i+\\")\\",l.name=\\"ChunkLoadError\\",l.type=a,l.request=i,o[1](l)}},\\"chunk-\\"+r,r)}};var r=(r,t)=>{var o,a,[i,l,c]=t,u=0;if(i.some(r=>0!==e[r])){for(o in l)n.o(l,o)&&(n.m[o]=l[o]);if(c)c(n)}for(r&&r(t);u{console.log(\\"Good\\")})})();", + "importExport.js?var=aa28aebb3049d51e5197": "/******/ (() => { // webpackBootstrap /******/ \\"use strict\\"; // UNUSED EXPORTS: default @@ -727,8 +727,8 @@ console.log(Foo()); /******/ })() ;", - "js.js?var=d569fb48b799414bf71c": "(()=>{var r={921(r){r.exports=function(){console.log(7)}}},o={};(function t(e){var n=o[e];if(void 0!==n)return n.exports;var s=o[e]={exports:{}};return r[e](s,s.exports,t),s.exports})(921)})();", - "mjs.js?var=d569fb48b799414bf71c": "(()=>{\\"use strict\\";function o(){console.log(11)}o()})();", + "js.js?var=aa28aebb3049d51e5197": "(()=>{var r={921(r){r.exports=function(){console.log(7)}}},o={};(function t(e){var n=o[e];if(void 0!==n)return n.exports;var s=o[e]={exports:{}};return r[e](s,s.exports,t),s.exports})(921)})();", + "mjs.js?var=aa28aebb3049d51e5197": "(()=>{\\"use strict\\";function o(){console.log(11)}o()})();", } `; @@ -738,7 +738,7 @@ exports[`test option should match snapshot for multiple "test" values ({RegExp}) exports[`test option should match snapshot for multiple "test" values ({String}): assets 1`] = ` Object { - "389.389.js?ver=d569fb48b799414bf71c": "\\"use strict\\"; + "389.389.js?ver=aa28aebb3049d51e5197": "\\"use strict\\"; (self[\\"webpackChunkterser_webpack_plugin\\"] = self[\\"webpackChunkterser_webpack_plugin\\"] || []).push([[389],{ /***/ 389 @@ -754,8 +754,8 @@ __webpack_require__.r(__webpack_exports__); /***/ } }]);", - "AsyncImportExport.js?var=d569fb48b799414bf71c": "(()=>{\\"use strict\\";var e,r,t={},o={};function n(e){var r=o[e];if(void 0!==r)return r.exports;var a=o[e]={exports:{}};return t[e](a,a.exports,n),a.exports}n.m=t,n.d=(e,r)=>{for(var t in r)n.o(r,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce((r,t)=>(n.f[t](e,r),r),[])),n.u=e=>e+\\".\\"+e+\\".js?ver=\\"+n.h(),n.h=()=>\\"d569fb48b799414bf71c\\",n.g=function(){if(\\"object\\"==typeof globalThis)return globalThis;try{return this||new Function(\\"return this\\")()}catch(e){if(\\"object\\"==typeof window)return window}}(),n.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),e={},r=\\"terser-webpack-plugin:\\",n.l=(t,o,a,i)=>{if(e[t])e[t].push(o);else{var l,c;if(void 0!==a)for(var u=document.getElementsByTagName(\\"script\\"),s=0;s{l.onerror=l.onload=null,clearTimeout(f);var n=e[t];if(delete e[t],l.parentNode&&l.parentNode.removeChild(l),n&&n.forEach(e=>e(o)),r)return r(o)},f=setTimeout(d.bind(null,void 0,{type:\\"timeout\\",target:l}),12e4);l.onerror=d.bind(null,l.onerror),l.onload=d.bind(null,l.onload),c&&document.head.appendChild(l)}},n.r=e=>{\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},(()=>{var e;n.g.importScripts&&(e=n.g.location+\\"\\");var r=n.g.document;if(!e&&r&&(r.currentScript&&\\"SCRIPT\\"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName(\\"script\\");if(t.length)for(var o=t.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=t[o--].src}if(!e)throw new Error(\\"Automatic publicPath is not supported in this browser\\");e=e.replace(/^blob:/,\\"\\").replace(/#.*$/,\\"\\").replace(/\\\\?.*$/,\\"\\").replace(/\\\\/[^\\\\/]+$/,\\"/\\"),n.p=e})(),(()=>{var e={988:0};n.f.j=(r,t)=>{var o=n.o(e,r)?e[r]:void 0;if(0!==o)if(o)t.push(o[2]);else{var a=new Promise((t,n)=>o=e[r]=[t,n]);t.push(o[2]=a);var i=n.p+n.u(r),l=new Error;n.l(i,t=>{if(n.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var a=t&&(\\"load\\"===t.type?\\"missing\\":t.type),i=t&&t.target&&t.target.src;l.message=\\"Loading chunk \\"+r+\\" failed.\\\\n(\\"+a+\\": \\"+i+\\")\\",l.name=\\"ChunkLoadError\\",l.type=a,l.request=i,o[1](l)}},\\"chunk-\\"+r,r)}};var r=(r,t)=>{var o,a,[i,l,c]=t,u=0;if(i.some(r=>0!==e[r])){for(o in l)n.o(l,o)&&(n.m[o]=l[o]);if(c)c(n)}for(r&&r(t);u{console.log(\\"Good\\")})})();", - "importExport.js?var=d569fb48b799414bf71c": "/******/ (() => { // webpackBootstrap + "AsyncImportExport.js?var=aa28aebb3049d51e5197": "(()=>{\\"use strict\\";var e,r,t={},o={};function n(e){var r=o[e];if(void 0!==r)return r.exports;var a=o[e]={exports:{}};return t[e](a,a.exports,n),a.exports}n.m=t,n.d=(e,r)=>{for(var t in r)n.o(r,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce((r,t)=>(n.f[t](e,r),r),[])),n.u=e=>e+\\".\\"+e+\\".js?ver=\\"+n.h(),n.h=()=>\\"aa28aebb3049d51e5197\\",n.g=function(){if(\\"object\\"==typeof globalThis)return globalThis;try{return this||new Function(\\"return this\\")()}catch(e){if(\\"object\\"==typeof window)return window}}(),n.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),e={},r=\\"terser-webpack-plugin:\\",n.l=(t,o,a,i)=>{if(e[t])e[t].push(o);else{var l,c;if(void 0!==a)for(var u=document.getElementsByTagName(\\"script\\"),s=0;s{l.onerror=l.onload=null,clearTimeout(f);var n=e[t];if(delete e[t],l.parentNode&&l.parentNode.removeChild(l),n&&n.forEach(e=>e(o)),r)return r(o)},f=setTimeout(d.bind(null,void 0,{type:\\"timeout\\",target:l}),12e4);l.onerror=d.bind(null,l.onerror),l.onload=d.bind(null,l.onload),c&&document.head.appendChild(l)}},n.r=e=>{\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},(()=>{var e;n.g.importScripts&&(e=n.g.location+\\"\\");var r=n.g.document;if(!e&&r&&(r.currentScript&&\\"SCRIPT\\"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName(\\"script\\");if(t.length)for(var o=t.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=t[o--].src}if(!e)throw new Error(\\"Automatic publicPath is not supported in this browser\\");e=e.replace(/^blob:/,\\"\\").replace(/#.*$/,\\"\\").replace(/\\\\?.*$/,\\"\\").replace(/\\\\/[^\\\\/]+$/,\\"/\\"),n.p=e})(),(()=>{var e={988:0};n.f.j=(r,t)=>{var o=n.o(e,r)?e[r]:void 0;if(0!==o)if(o)t.push(o[2]);else{var a=new Promise((t,n)=>o=e[r]=[t,n]);t.push(o[2]=a);var i=n.p+n.u(r),l=new Error;n.l(i,t=>{if(n.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var a=t&&(\\"load\\"===t.type?\\"missing\\":t.type),i=t&&t.target&&t.target.src;l.message=\\"Loading chunk \\"+r+\\" failed.\\\\n(\\"+a+\\": \\"+i+\\")\\",l.name=\\"ChunkLoadError\\",l.type=a,l.request=i,o[1](l)}},\\"chunk-\\"+r,r)}};var r=(r,t)=>{var o,a,[i,l,c]=t,u=0;if(i.some(r=>0!==e[r])){for(o in l)n.o(l,o)&&(n.m[o]=l[o]);if(c)c(n)}for(r&&r(t);u{console.log(\\"Good\\")})})();", + "importExport.js?var=aa28aebb3049d51e5197": "/******/ (() => { // webpackBootstrap /******/ \\"use strict\\"; // UNUSED EXPORTS: default @@ -785,8 +785,8 @@ console.log(Foo()); /******/ })() ;", - "js.js?var=d569fb48b799414bf71c": "(()=>{var r={921(r){r.exports=function(){console.log(7)}}},o={};(function t(e){var n=o[e];if(void 0!==n)return n.exports;var s=o[e]={exports:{}};return r[e](s,s.exports,t),s.exports})(921)})();", - "mjs.js?var=d569fb48b799414bf71c": "/******/ (() => { // webpackBootstrap + "js.js?var=aa28aebb3049d51e5197": "(()=>{var r={921(r){r.exports=function(){console.log(7)}}},o={};(function t(e){var n=o[e];if(void 0!==n)return n.exports;var s=o[e]={exports:{}};return r[e](s,s.exports,t),s.exports})(921)})();", + "mjs.js?var=aa28aebb3049d51e5197": "/******/ (() => { // webpackBootstrap /******/ \\"use strict\\"; // foo // bar @@ -812,11 +812,11 @@ exports[`test option should match snapshot for multiple "test" values ({String}) exports[`test option should match snapshot with empty value: assets 1`] = ` Object { - "389.389.js?ver=d569fb48b799414bf71c": "\\"use strict\\";(self.webpackChunkterser_webpack_plugin=self.webpackChunkterser_webpack_plugin||[]).push([[389],{389(e,s,p){p.r(s),p.d(s,{default:()=>c});const c=\\"async-dep\\"}}]);", - "AsyncImportExport.js?var=d569fb48b799414bf71c": "(()=>{\\"use strict\\";var e,r,t={},o={};function n(e){var r=o[e];if(void 0!==r)return r.exports;var a=o[e]={exports:{}};return t[e](a,a.exports,n),a.exports}n.m=t,n.d=(e,r)=>{for(var t in r)n.o(r,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce((r,t)=>(n.f[t](e,r),r),[])),n.u=e=>e+\\".\\"+e+\\".js?ver=\\"+n.h(),n.h=()=>\\"d569fb48b799414bf71c\\",n.g=function(){if(\\"object\\"==typeof globalThis)return globalThis;try{return this||new Function(\\"return this\\")()}catch(e){if(\\"object\\"==typeof window)return window}}(),n.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),e={},r=\\"terser-webpack-plugin:\\",n.l=(t,o,a,i)=>{if(e[t])e[t].push(o);else{var l,c;if(void 0!==a)for(var u=document.getElementsByTagName(\\"script\\"),s=0;s{l.onerror=l.onload=null,clearTimeout(f);var n=e[t];if(delete e[t],l.parentNode&&l.parentNode.removeChild(l),n&&n.forEach(e=>e(o)),r)return r(o)},f=setTimeout(d.bind(null,void 0,{type:\\"timeout\\",target:l}),12e4);l.onerror=d.bind(null,l.onerror),l.onload=d.bind(null,l.onload),c&&document.head.appendChild(l)}},n.r=e=>{\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},(()=>{var e;n.g.importScripts&&(e=n.g.location+\\"\\");var r=n.g.document;if(!e&&r&&(r.currentScript&&\\"SCRIPT\\"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName(\\"script\\");if(t.length)for(var o=t.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=t[o--].src}if(!e)throw new Error(\\"Automatic publicPath is not supported in this browser\\");e=e.replace(/^blob:/,\\"\\").replace(/#.*$/,\\"\\").replace(/\\\\?.*$/,\\"\\").replace(/\\\\/[^\\\\/]+$/,\\"/\\"),n.p=e})(),(()=>{var e={988:0};n.f.j=(r,t)=>{var o=n.o(e,r)?e[r]:void 0;if(0!==o)if(o)t.push(o[2]);else{var a=new Promise((t,n)=>o=e[r]=[t,n]);t.push(o[2]=a);var i=n.p+n.u(r),l=new Error;n.l(i,t=>{if(n.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var a=t&&(\\"load\\"===t.type?\\"missing\\":t.type),i=t&&t.target&&t.target.src;l.message=\\"Loading chunk \\"+r+\\" failed.\\\\n(\\"+a+\\": \\"+i+\\")\\",l.name=\\"ChunkLoadError\\",l.type=a,l.request=i,o[1](l)}},\\"chunk-\\"+r,r)}};var r=(r,t)=>{var o,a,[i,l,c]=t,u=0;if(i.some(r=>0!==e[r])){for(o in l)n.o(l,o)&&(n.m[o]=l[o]);if(c)c(n)}for(r&&r(t);u{console.log(\\"Good\\")})})();", - "importExport.js?var=d569fb48b799414bf71c": "(()=>{\\"use strict\\";function o(){const o=\`baz\${Math.random()}\`;return()=>({a:\\"foobar\\"+o,b:\\"foo\\",baz:o})}console.log(o())})();", - "js.js?var=d569fb48b799414bf71c": "(()=>{var r={921(r){r.exports=function(){console.log(7)}}},o={};(function t(e){var n=o[e];if(void 0!==n)return n.exports;var s=o[e]={exports:{}};return r[e](s,s.exports,t),s.exports})(921)})();", - "mjs.js?var=d569fb48b799414bf71c": "(()=>{\\"use strict\\";function o(){console.log(11)}o()})();", + "389.389.js?ver=aa28aebb3049d51e5197": "\\"use strict\\";(self.webpackChunkterser_webpack_plugin=self.webpackChunkterser_webpack_plugin||[]).push([[389],{389(e,s,p){p.r(s),p.d(s,{default:()=>c});const c=\\"async-dep\\"}}]);", + "AsyncImportExport.js?var=aa28aebb3049d51e5197": "(()=>{\\"use strict\\";var e,r,t={},o={};function n(e){var r=o[e];if(void 0!==r)return r.exports;var a=o[e]={exports:{}};return t[e](a,a.exports,n),a.exports}n.m=t,n.d=(e,r)=>{for(var t in r)n.o(r,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce((r,t)=>(n.f[t](e,r),r),[])),n.u=e=>e+\\".\\"+e+\\".js?ver=\\"+n.h(),n.h=()=>\\"aa28aebb3049d51e5197\\",n.g=function(){if(\\"object\\"==typeof globalThis)return globalThis;try{return this||new Function(\\"return this\\")()}catch(e){if(\\"object\\"==typeof window)return window}}(),n.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),e={},r=\\"terser-webpack-plugin:\\",n.l=(t,o,a,i)=>{if(e[t])e[t].push(o);else{var l,c;if(void 0!==a)for(var u=document.getElementsByTagName(\\"script\\"),s=0;s{l.onerror=l.onload=null,clearTimeout(f);var n=e[t];if(delete e[t],l.parentNode&&l.parentNode.removeChild(l),n&&n.forEach(e=>e(o)),r)return r(o)},f=setTimeout(d.bind(null,void 0,{type:\\"timeout\\",target:l}),12e4);l.onerror=d.bind(null,l.onerror),l.onload=d.bind(null,l.onload),c&&document.head.appendChild(l)}},n.r=e=>{\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},(()=>{var e;n.g.importScripts&&(e=n.g.location+\\"\\");var r=n.g.document;if(!e&&r&&(r.currentScript&&\\"SCRIPT\\"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName(\\"script\\");if(t.length)for(var o=t.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=t[o--].src}if(!e)throw new Error(\\"Automatic publicPath is not supported in this browser\\");e=e.replace(/^blob:/,\\"\\").replace(/#.*$/,\\"\\").replace(/\\\\?.*$/,\\"\\").replace(/\\\\/[^\\\\/]+$/,\\"/\\"),n.p=e})(),(()=>{var e={988:0};n.f.j=(r,t)=>{var o=n.o(e,r)?e[r]:void 0;if(0!==o)if(o)t.push(o[2]);else{var a=new Promise((t,n)=>o=e[r]=[t,n]);t.push(o[2]=a);var i=n.p+n.u(r),l=new Error;n.l(i,t=>{if(n.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var a=t&&(\\"load\\"===t.type?\\"missing\\":t.type),i=t&&t.target&&t.target.src;l.message=\\"Loading chunk \\"+r+\\" failed.\\\\n(\\"+a+\\": \\"+i+\\")\\",l.name=\\"ChunkLoadError\\",l.type=a,l.request=i,o[1](l)}},\\"chunk-\\"+r,r)}};var r=(r,t)=>{var o,a,[i,l,c]=t,u=0;if(i.some(r=>0!==e[r])){for(o in l)n.o(l,o)&&(n.m[o]=l[o]);if(c)c(n)}for(r&&r(t);u{console.log(\\"Good\\")})})();", + "importExport.js?var=aa28aebb3049d51e5197": "(()=>{\\"use strict\\";function o(){const o=\`baz\${Math.random()}\`;return()=>({a:\\"foobar\\"+o,b:\\"foo\\",baz:o})}console.log(o())})();", + "js.js?var=aa28aebb3049d51e5197": "(()=>{var r={921(r){r.exports=function(){console.log(7)}}},o={};(function t(e){var n=o[e];if(void 0!==n)return n.exports;var s=o[e]={exports:{}};return r[e](s,s.exports,t),s.exports})(921)})();", + "mjs.js?var=aa28aebb3049d51e5197": "(()=>{\\"use strict\\";function o(){console.log(11)}o()})();", } `; From 3cb43493ba3c945413263335ffe703df000bcfd5 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 8 May 2026 09:58:02 +0000 Subject: [PATCH 7/7] test: move filter dispatch coverage into minify-option suite Drop the standalone `test/minimizer-filter.test.js` and re-express the same coverage as snapshot tests in `test/minify-option.test.js`, matching the surrounding style (real fixtures, `readsAssets` / `getErrors` / `getWarnings` snapshots). The four new cases cover: - a single TerserPlugin instance dispatching to terser + html-minifier via the array-of-minimizers form - a single minimizer whose `filter` returns false (asset stays as-is) - a single minimizer whose `filter` returns undefined (treated as accept) - an array of minimizers whose filters all reject the asset --- test/__snapshots__/minify-option.test.js.snap | 104 +++++++ test/minify-option.test.js | 115 +++++++ test/minimizer-filter.test.js | 287 ------------------ 3 files changed, 219 insertions(+), 287 deletions(-) delete mode 100644 test/minimizer-filter.test.js diff --git a/test/__snapshots__/minify-option.test.js.snap b/test/__snapshots__/minify-option.test.js.snap index 656c070..8a7dc3e 100644 --- a/test/__snapshots__/minify-option.test.js.snap +++ b/test/__snapshots__/minify-option.test.js.snap @@ -77,6 +77,68 @@ The 'extractComments' option for 'swcMinify' only supports booleans, \\"some\\", exports[`minify option should report an error when the \`extractComments\` option for \`swcMinify\` uses a function condition: warnings 1`] = `Array []`; +exports[`minify option should skip assets when every minimizer in the \`minify\` array rejects them via \`filter\`: assets 1`] = ` +Object { + "main.js": "/******/ (() => { // webpackBootstrap +/******/ \\"use strict\\"; +class Point { + constructor(x, y) { + this.x = x; + this.y = y; + } + + static distance(a, b) { + const dx = a.x - b.x; + const dy = a.y - b.y; + + return Math.hypot(dx, dy); + } +} + +console.log('HERE'); + +/* unused harmony default export */ var __WEBPACK_DEFAULT_EXPORT__ = ((/* unused pure expression or super */ null && (Point))); + +/******/ })() +;", +} +`; + +exports[`minify option should skip assets when every minimizer in the \`minify\` array rejects them via \`filter\`: errors 1`] = `Array []`; + +exports[`minify option should skip assets when every minimizer in the \`minify\` array rejects them via \`filter\`: warnings 1`] = `Array []`; + +exports[`minify option should skip assets when the only minimizer's \`filter\` returns \`false\`: assets 1`] = ` +Object { + "main.js": "/******/ (() => { // webpackBootstrap +/******/ \\"use strict\\"; +class Point { + constructor(x, y) { + this.x = x; + this.y = y; + } + + static distance(a, b) { + const dx = a.x - b.x; + const dy = a.y - b.y; + + return Math.hypot(dx, dy); + } +} + +console.log('HERE'); + +/* unused harmony default export */ var __WEBPACK_DEFAULT_EXPORT__ = ((/* unused pure expression or super */ null && (Point))); + +/******/ })() +;", +} +`; + +exports[`minify option should skip assets when the only minimizer's \`filter\` returns \`false\`: errors 1`] = `Array []`; + +exports[`minify option should skip assets when the only minimizer's \`filter\` returns \`false\`: warnings 1`] = `Array []`; + exports[`minify option should snapshot with extracting comments: assets 1`] = ` Object { "main.js": "/*! For license information please see main.js.LICENSE.txt */ @@ -117,6 +179,37 @@ Error", exports[`minify option should throw an error when an error: warnings 1`] = `Array []`; +exports[`minify option should treat a \`filter\` returning \`undefined\` as accept: assets 1`] = ` +Object { + "main.js": "/* undef-filter *//******/ (() => { // webpackBootstrap +/******/ \\"use strict\\"; +class Point { + constructor(x, y) { + this.x = x; + this.y = y; + } + + static distance(a, b) { + const dx = a.x - b.x; + const dy = a.y - b.y; + + return Math.hypot(dx, dy); + } +} + +console.log('HERE'); + +/* unused harmony default export */ var __WEBPACK_DEFAULT_EXPORT__ = ((/* unused pure expression or super */ null && (Point))); + +/******/ })() +;", +} +`; + +exports[`minify option should treat a \`filter\` returning \`undefined\` as accept: errors 1`] = `Array []`; + +exports[`minify option should treat a \`filter\` returning \`undefined\` as accept: warnings 1`] = `Array []`; + exports[`minify option should work using when the \`minify\` option is \`esbuildMinify\` and ECMA modules output: assets 1`] = ` Object { "main.js": "var t={};t.d=(_,e)=>{for(var o in e)t.o(e,o)&&!t.o(_,o)&&Object.defineProperty(_,o,{enumerable:!0,get:e[o]})},t.o=(_,e)=>Object.prototype.hasOwnProperty.call(_,e);var r={};t.d(r,{A:()=>p});const c=4;function a(){console.log(7+c)}a();const p=a,s=r.A;export{s as default}; @@ -895,6 +988,17 @@ exports[`minify option should work when \`minify\` and \`terserOptions\` are bot exports[`minify option should work when \`minify\` and \`terserOptions\` are both arrays: warnings 1`] = `Array []`; +exports[`minify option should work when \`minify\` is an array of functions and dispatches by \`filter\`: assets 1`] = ` +Object { + "1d477fcae4c6c3852830.html": " Hello

Hello, World!

Hello there

", + "main.js": "(()=>{var t={740(t,r,e){\\"use strict\\";t.exports=e.p+\\"1d477fcae4c6c3852830.html\\"}},r={};function e(o){var n=r[o];if(void 0!==n)return n.exports;var c=r[o]={exports:{}};return t[o](c,c.exports,e),c.exports}e.m=t,e.g=function(){if(\\"object\\"==typeof globalThis)return globalThis;try{return this||new Function(\\"return this\\")()}catch(t){if(\\"object\\"==typeof window)return window}}(),e.o=(t,r)=>Object.prototype.hasOwnProperty.call(t,r),(()=>{var t;e.g.importScripts&&(t=e.g.location+\\"\\");var r=e.g.document;if(!t&&r&&(r.currentScript&&\\"SCRIPT\\"===r.currentScript.tagName.toUpperCase()&&(t=r.currentScript.src),!t)){var o=r.getElementsByTagName(\\"script\\");if(o.length)for(var n=o.length-1;n>-1&&(!t||!/^http(s?):/.test(t));)t=o[n--].src}if(!t)throw new Error(\\"Automatic publicPath is not supported in this browser\\");t=t.replace(/^blob:/,\\"\\").replace(/#.*$/,\\"\\").replace(/\\\\?.*$/,\\"\\").replace(/\\\\/[^\\\\/]+$/,\\"/\\"),e.p=t})(),e.b=\\"undefined\\"!=typeof document&&document.baseURI||self.location.href,console.log(new URL(e(740),e.b))})();", +} +`; + +exports[`minify option should work when \`minify\` is an array of functions and dispatches by \`filter\`: errors 1`] = `Array []`; + +exports[`minify option should work when \`minify\` is an array of functions and dispatches by \`filter\`: warnings 1`] = `Array []`; + exports[`minify option should work when \`minify\` is an array of functions using \`htmlMinifierTerser\`: assets 1`] = ` Object { "1d477fcae4c6c3852830.html": " Hello

Hello, World!

Hello there

", diff --git a/test/minify-option.test.js b/test/minify-option.test.js index b87dcf1..c4aa9cf 100644 --- a/test/minify-option.test.js +++ b/test/minify-option.test.js @@ -1301,4 +1301,119 @@ describe("minify option", () => { expect(getErrors(stats)).toMatchSnapshot("errors"); expect(getWarnings(stats)).toMatchSnapshot("warnings"); }); + + it("should work when `minify` is an array of functions and dispatches by `filter`", async () => { + const compiler = getCompiler({ + entry: path.resolve(__dirname, "./fixtures/html.js"), + }); + + new TerserPlugin({ + test: /\.(?:[cm]?js|html?)(\?.*)?$/i, + minify: [TerserPlugin.terserMinify, TerserPlugin.htmlMinifierTerser], + }).apply(compiler); + + const stats = await compile(compiler); + + expect(readsAssets(compiler, stats)).toMatchSnapshot("assets"); + expect(getErrors(stats)).toMatchSnapshot("errors"); + expect(getWarnings(stats)).toMatchSnapshot("warnings"); + }); + + it("should skip assets when the only minimizer's `filter` returns `false`", async () => { + const compiler = getCompiler({ + entry: path.resolve(__dirname, "./fixtures/minify/es6.js"), + output: { + path: path.resolve(__dirname, "./dist-terser"), + filename: "[name].js", + chunkFilename: "[id].[name].js", + }, + }); + + const minify = (file) => { + const [[, code]] = Object.entries(file); + + return { code: `/* minified */${code}` }; + }; + + minify.filter = () => false; + + new TerserPlugin({ + parallel: false, + minify, + }).apply(compiler); + + const stats = await compile(compiler); + + expect(readsAssets(compiler, stats)).toMatchSnapshot("assets"); + expect(getErrors(stats)).toMatchSnapshot("errors"); + expect(getWarnings(stats)).toMatchSnapshot("warnings"); + }); + + it("should treat a `filter` returning `undefined` as accept", async () => { + const compiler = getCompiler({ + entry: path.resolve(__dirname, "./fixtures/minify/es6.js"), + output: { + path: path.resolve(__dirname, "./dist-terser"), + filename: "[name].js", + chunkFilename: "[id].[name].js", + }, + }); + + const minify = (file) => { + const [[, code]] = Object.entries(file); + + return { code: `/* undef-filter */${code}` }; + }; + + minify.filter = () => undefined; + + new TerserPlugin({ + parallel: false, + minify, + }).apply(compiler); + + const stats = await compile(compiler); + + expect(readsAssets(compiler, stats)).toMatchSnapshot("assets"); + expect(getErrors(stats)).toMatchSnapshot("errors"); + expect(getWarnings(stats)).toMatchSnapshot("warnings"); + }); + + it("should skip assets when every minimizer in the `minify` array rejects them via `filter`", async () => { + const compiler = getCompiler({ + entry: path.resolve(__dirname, "./fixtures/minify/es6.js"), + output: { + path: path.resolve(__dirname, "./dist-terser"), + filename: "[name].js", + chunkFilename: "[id].[name].js", + }, + }); + + const cssOnly = (file) => { + const [[, code]] = Object.entries(file); + + return { code: `/* css */${code}` }; + }; + + cssOnly.filter = (name) => /\.css(\?.*)?$/i.test(name); + + const htmlOnly = (file) => { + const [[, code]] = Object.entries(file); + + return { code: `/* html */${code}` }; + }; + + htmlOnly.filter = (name) => /\.html?(\?.*)?$/i.test(name); + + new TerserPlugin({ + parallel: false, + minify: [cssOnly, htmlOnly], + }).apply(compiler); + + const stats = await compile(compiler); + + expect(readsAssets(compiler, stats)).toMatchSnapshot("assets"); + expect(getErrors(stats)).toMatchSnapshot("errors"); + expect(getWarnings(stats)).toMatchSnapshot("warnings"); + }); }); diff --git a/test/minimizer-filter.test.js b/test/minimizer-filter.test.js deleted file mode 100644 index 7fee111..0000000 --- a/test/minimizer-filter.test.js +++ /dev/null @@ -1,287 +0,0 @@ -import path from "path"; - -import TerserPlugin from "../src"; - -import { - EmitNewAsset, - compile, - getCompiler, - getErrors, - getWarnings, - readAsset, - readsAssets, -} from "./helpers"; - -// jest.fn() can't be used directly because schema-utils calls -// `value instanceof Function` on each minify entry, and the mock function -// is constructed in a different VM context than the schema-utils Function -// global, so the instanceof check fails. Wrap a plain function instead and -// track calls manually. -function makeMinify(impl) { - /** - * @param {...EXPECTED_ANY} args minify arguments - * @returns {EXPECTED_ANY} result - */ - function fn(...args) { - fn.calls.push(args); - return impl(...args); - } - fn.calls = []; - return fn; -} - -describe("minimizer filter", () => { - it("skips assets when the only minimizer's filter rejects them", async () => { - const compiler = getCompiler({ - entry: path.resolve(__dirname, "./fixtures/entry.js"), - }); - - const minify = makeMinify(() => ({ code: "/* minified */" })); - - minify.filter = () => false; - - new TerserPlugin({ - parallel: false, - minify, - }).apply(compiler); - - const stats = await compile(compiler); - - expect(minify.calls).toHaveLength(0); - expect(readAsset("main.js", compiler, stats)).not.toContain( - "/* minified */", - ); - expect(getErrors(stats)).toEqual([]); - expect(getWarnings(stats)).toEqual([]); - }); - - it("dispatches each asset to the minimizer whose filter accepts it", async () => { - const compiler = getCompiler({ - entry: path.resolve(__dirname, "./fixtures/entry.js"), - }); - - new EmitNewAsset({ name: "extra.css" }).apply(compiler); - - const jsMinify = makeMinify(() => ({ code: "var a=1;/*JS*/" })); - - jsMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name); - - const cssMinify = makeMinify(() => ({ code: "/*CSS*/a{}" })); - - cssMinify.filter = (name) => /\.css(\?.*)?$/i.test(name); - - new TerserPlugin({ - parallel: false, - test: /\.(?:[cm]?js|css)(\?.*)?$/i, - minify: [jsMinify, cssMinify], - }).apply(compiler); - - const stats = await compile(compiler); - - expect(jsMinify.calls).toHaveLength(1); - expect(cssMinify.calls).toHaveLength(1); - expect(Object.keys(jsMinify.calls[0][0])[0]).toBe("main.js"); - expect(Object.keys(cssMinify.calls[0][0])[0]).toBe("extra.css"); - - const assets = readsAssets(compiler, stats); - - expect(assets["main.js"]).toContain("/*JS*/"); - expect(assets["extra.css"]).toContain("/*CSS*/"); - expect(getErrors(stats)).toEqual([]); - expect(getWarnings(stats)).toEqual([]); - }); - - it("treats a filter returning undefined as accept", async () => { - const compiler = getCompiler({ - entry: path.resolve(__dirname, "./fixtures/entry.js"), - }); - - const minify = makeMinify(() => ({ code: "/*UNDEF-FILTER*/" })); - - minify.filter = () => undefined; - - new TerserPlugin({ - parallel: false, - minify, - }).apply(compiler); - - const stats = await compile(compiler); - - expect(minify.calls).toHaveLength(1); - expect(readAsset("main.js", compiler, stats)).toContain("/*UNDEF-FILTER*/"); - expect(getErrors(stats)).toEqual([]); - expect(getWarnings(stats)).toEqual([]); - }); - - it("skips an asset when every minimizer in the array rejects it", async () => { - const compiler = getCompiler({ - entry: path.resolve(__dirname, "./fixtures/entry.js"), - }); - - new EmitNewAsset({ name: "stranger.txt" }).apply(compiler); - - const jsMinify = makeMinify(() => ({ code: "/*JS*/" })); - - jsMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name); - - const cssMinify = makeMinify(() => ({ code: "/*CSS*/" })); - - cssMinify.filter = (name) => /\.css(\?.*)?$/i.test(name); - - new TerserPlugin({ - parallel: false, - // Widen `test` so the .txt asset reaches the dispatcher and we can - // verify that no minimizer in the array claims it. - test: /.*/, - minify: [jsMinify, cssMinify], - }).apply(compiler); - - const stats = await compile(compiler); - - expect(jsMinify.calls).toHaveLength(1); - expect(cssMinify.calls).toHaveLength(0); - - const txt = readAsset("stranger.txt", compiler, stats); - - expect(txt).not.toContain("/*JS*/"); - expect(txt).not.toContain("/*CSS*/"); - expect(getErrors(stats)).toEqual([]); - expect(getWarnings(stats)).toEqual([]); - }); - - it("keeps the chain semantic when multiple minimizers in the array accept the same asset", async () => { - const compiler = getCompiler({ - entry: path.resolve(__dirname, "./fixtures/entry.js"), - }); - - const firstMinify = makeMinify((input) => { - const [[, code]] = Object.entries(input); - - return { code: `${code}/*FIRST*/` }; - }); - - firstMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name); - - const secondMinify = makeMinify((input) => { - const [[, code]] = Object.entries(input); - - return { code: `${code}/*SECOND*/` }; - }); - - secondMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name); - - new TerserPlugin({ - parallel: false, - minify: [firstMinify, secondMinify], - }).apply(compiler); - - const stats = await compile(compiler); - - expect(firstMinify.calls).toHaveLength(1); - expect(secondMinify.calls).toHaveLength(1); - - const [[secondInput]] = secondMinify.calls; - const [secondCode] = Object.values(secondInput); - - expect(secondCode).toContain("/*FIRST*/"); - - const out = readAsset("main.js", compiler, stats); - - expect(out).toContain("/*FIRST*/"); - expect(out).toContain("/*SECOND*/"); - expect(getErrors(stats)).toEqual([]); - expect(getWarnings(stats)).toEqual([]); - }); - - it("dispatches across asset types when `test` is widened", async () => { - const compiler = getCompiler({ - entry: path.resolve(__dirname, "./fixtures/entry.js"), - }); - - new EmitNewAsset({ name: "extra.css" }).apply(compiler); - - const jsMinify = makeMinify(() => ({ code: "/*JS*/" })); - - jsMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name); - - const cssMinify = makeMinify(() => ({ code: "/*CSS*/" })); - - cssMinify.filter = (name) => /\.css(\?.*)?$/i.test(name); - - new TerserPlugin({ - parallel: false, - // `test` still defaults to JS only; widen it (or set it to a regex - // that catches every asset) so the dispatcher gets to see CSS too. - test: /\.(?:[cm]?js|css)(\?.*)?$/i, - minify: [jsMinify, cssMinify], - }).apply(compiler); - - const stats = await compile(compiler); - - expect(jsMinify.calls).toHaveLength(1); - expect(cssMinify.calls).toHaveLength(1); - expect(readAsset("extra.css", compiler, stats)).toContain("/*CSS*/"); - expect(getErrors(stats)).toEqual([]); - expect(getWarnings(stats)).toEqual([]); - }); - - it("keeps the JS-only `test` default when a single minimizer is provided", async () => { - const compiler = getCompiler({ - entry: path.resolve(__dirname, "./fixtures/entry.js"), - }); - - new EmitNewAsset({ name: "extra.css" }).apply(compiler); - - const minify = makeMinify(() => ({ code: "/*ONLY*/" })); - - new TerserPlugin({ - parallel: false, - minify, - }).apply(compiler); - - const stats = await compile(compiler); - - expect(minify.calls).toHaveLength(1); - expect(Object.keys(minify.calls[0][0])[0]).toBe("main.js"); - expect(getErrors(stats)).toEqual([]); - expect(getWarnings(stats)).toEqual([]); - }); - - it("pairs the per-asset implementation subset with the matching options entries", async () => { - const compiler = getCompiler({ - entry: path.resolve(__dirname, "./fixtures/entry.js"), - }); - - new EmitNewAsset({ name: "extra.css" }).apply(compiler); - - const jsMinify = makeMinify(() => ({ code: "/*JS*/" })); - - jsMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name); - - const cssMinify = makeMinify(() => ({ code: "/*CSS*/" })); - - cssMinify.filter = (name) => /\.css(\?.*)?$/i.test(name); - - new TerserPlugin({ - parallel: false, - test: /\.(?:[cm]?js|css)(\?.*)?$/i, - minify: [jsMinify, cssMinify], - minimizerOptions: [{ flavor: "js" }, { flavor: "css" }], - }).apply(compiler); - - const stats = await compile(compiler); - - expect(jsMinify.calls).toHaveLength(1); - expect(cssMinify.calls).toHaveLength(1); - - const [[, , jsOptions]] = jsMinify.calls; - - expect(jsOptions.flavor).toBe("js"); - - const [[, , cssOptions]] = cssMinify.calls; - - expect(cssOptions.flavor).toBe("css"); - expect(getErrors(stats)).toEqual([]); - expect(getWarnings(stats)).toEqual([]); - }); -});