Skip to content

Commit b45ab8b

Browse files
committed
refactor: collapse minimizer-filter dispatch into one helper
`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.
1 parent 685b695 commit b45ab8b

3 files changed

Lines changed: 72 additions & 98 deletions

File tree

src/index.js

Lines changed: 50 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -382,36 +382,47 @@ class TerserPlugin {
382382
async optimize(compiler, compilation, assets, optimizeOptions) {
383383
const cache = compilation.getCache("TerserWebpackPlugin");
384384
let numberOfAssets = 0;
385-
const allImplementations = Array.isArray(
386-
this.options.minimizer.implementation,
387-
)
385+
386+
// Normalize the configured minimizer(s) and their options into parallel
387+
// arrays. The user can pass either a single `(impl, options)` pair or an
388+
// array of pairs; for dispatch and per-asset slicing we treat both the
389+
// same. The original `this.options.minimizer.implementation` shape is
390+
// preserved for chunk hashing further down.
391+
const implementations = Array.isArray(this.options.minimizer.implementation)
388392
? this.options.minimizer.implementation
389-
: null;
393+
: [this.options.minimizer.implementation];
394+
const minimizerOptionsList =
395+
/** @type {MinimizerOptions<T>[]} */
396+
(
397+
Array.isArray(this.options.minimizer.options)
398+
? this.options.minimizer.options
399+
: implementations.map(() => this.options.minimizer.options)
400+
);
401+
390402
/**
391-
* Apply each minimizer's `filter` helper. For array form, an asset is
392-
* accepted when at least one configured minimizer accepts it; for the
393-
* single form, the asset must be accepted by that minimizer.
403+
* Collect the indices of minimizers whose `filter` accepts `name`.
404+
* Filters returning `undefined` are treated as accept (matches the
405+
* convention used by `supportsWorkerThreads`).
394406
* @param {string} name asset name
395407
* @param {AssetInfo} info asset info
396-
* @returns {boolean} whether at least one minimizer accepts this asset
408+
* @returns {number[]} indices into `implementations` that accept the asset
397409
*/
398-
const isAcceptedByAnyMinimizer = (name, info) => {
399-
if (allImplementations) {
400-
return allImplementations.some(
401-
(impl) =>
402-
typeof impl.filter !== "function" ||
403-
// eslint-disable-next-line unicorn/no-array-method-this-argument
404-
impl.filter(name, info) !== false,
405-
);
410+
const matchingMinimizers = (name, info) => {
411+
const matched = [];
412+
413+
for (let i = 0; i < implementations.length; i++) {
414+
const impl = implementations[i];
415+
416+
if (
417+
typeof impl.filter !== "function" ||
418+
// eslint-disable-next-line unicorn/no-array-method-this-argument
419+
impl.filter(name, info) !== false
420+
) {
421+
matched.push(i);
422+
}
406423
}
407-
const single =
408-
/** @type {BasicMinimizerImplementation<EXPECTED_ANY> & MinimizeFunctionHelpers} */
409-
(this.options.minimizer.implementation);
410-
return (
411-
typeof single.filter !== "function" ||
412-
// eslint-disable-next-line unicorn/no-array-method-this-argument
413-
single.filter(name, info) !== false
414-
);
424+
425+
return matched;
415426
};
416427
const assetsForMinify = await Promise.all(
417428
Object.keys(assets)
@@ -436,7 +447,7 @@ class TerserPlugin {
436447
return false;
437448
}
438449

439-
if (!isAcceptedByAnyMinimizer(name, info)) {
450+
if (matchingMinimizers(name, info).length === 0) {
440451
return false;
441452
}
442453

@@ -470,10 +481,6 @@ class TerserPlugin {
470481
/** @type {undefined | number} */
471482
let numberOfWorkers;
472483

473-
const implementations = Array.isArray(this.options.minimizer.implementation)
474-
? this.options.minimizer.implementation
475-
: [this.options.minimizer.implementation];
476-
477484
const needCreateWorker =
478485
optimizeOptions.availableNumberOfCores > 0 &&
479486
implementations.every(
@@ -564,44 +571,19 @@ class TerserPlugin {
564571
}
565572

566573
// Dispatch to only the minimizers whose `filter` accepts this asset.
567-
// For the single-implementation form `assetImplementation` matches
568-
// the original; for the array form it's the matching subset (still
569-
// an array, even when only one entry remains, so chained behavior
570-
// is preserved when multiple match).
571-
let assetImplementation = this.options.minimizer.implementation;
572-
let assetMinimizerOptions = this.options.minimizer.options;
573-
574-
if (allImplementations) {
575-
const matchedIndices = [];
576-
577-
for (let i = 0; i < allImplementations.length; i++) {
578-
const impl = allImplementations[i];
579-
580-
if (
581-
typeof impl.filter !== "function" ||
582-
// eslint-disable-next-line unicorn/no-array-method-this-argument
583-
impl.filter(name, info) !== false
584-
) {
585-
matchedIndices.push(i);
586-
}
587-
}
588-
589-
assetImplementation =
590-
/** @type {MinimizerImplementation<T>} */
591-
(matchedIndices.map((i) => allImplementations[i]));
592-
593-
if (Array.isArray(this.options.minimizer.options)) {
594-
const sourceOptions = this.options.minimizer.options;
595-
596-
assetMinimizerOptions =
597-
/** @type {MinimizerOptions<T>} */
598-
(matchedIndices.map((i) => sourceOptions[i] || {}));
599-
}
600-
}
601-
602-
const clonedMinimizerOptions = Array.isArray(assetMinimizerOptions)
603-
? assetMinimizerOptions.map((item) => ({ ...item }))
604-
: { .../** @type {T} */ (assetMinimizerOptions) };
574+
// `minify.js` already normalizes a single implementation into a
575+
// one-element array, so we always hand it the matching subset here.
576+
const matched = matchingMinimizers(name, info);
577+
const assetImplementation =
578+
/** @type {MinimizerImplementation<T>} */
579+
(matched.map((i) => implementations[i]));
580+
const clonedMinimizerOptions =
581+
/** @type {MinimizerOptions<T>} */
582+
(
583+
matched.map((i) => ({
584+
.../** @type {T} */ (minimizerOptionsList[i]),
585+
}))
586+
);
605587

606588
/**
607589
* @type {InternalOptions<T>}
@@ -612,9 +594,7 @@ class TerserPlugin {
612594
inputSourceMap,
613595
minimizer: {
614596
implementation: assetImplementation,
615-
options:
616-
/** @type {MinimizerOptions<T>} */
617-
(clonedMinimizerOptions),
597+
options: clonedMinimizerOptions,
618598
},
619599
extractComments: this.options.extractComments,
620600
};

src/utils.js

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
* @typedef {string[]} ExtractedComments
1212
*/
1313

14+
const JS_FILE_RE = /\.[cm]?js(\?.*)?$/i;
15+
const JSON_FILE_RE = /\.json(\?.*)?$/i;
16+
const HTML_FILE_RE = /\.html?(\?.*)?$/i;
17+
const CSS_FILE_RE = /\.css(\?.*)?$/i;
18+
1419
/**
1520
* Map a webpack `output.environment` configuration to the highest
1621
* ECMAScript version that the target is known to support. Returns `5`
@@ -359,7 +364,7 @@ terserMinify.supportsWorkerThreads = () => true;
359364
* @param {string} name asset name
360365
* @returns {boolean} true if `name` looks like a JavaScript file
361366
*/
362-
terserMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name);
367+
terserMinify.filter = (name) => JS_FILE_RE.test(name);
363368

364369
/* istanbul ignore next */
365370
/**
@@ -596,7 +601,7 @@ uglifyJsMinify.supportsWorkerThreads = () => true;
596601
* @param {string} name asset name
597602
* @returns {boolean} true if `name` looks like a JavaScript file
598603
*/
599-
uglifyJsMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name);
604+
uglifyJsMinify.filter = (name) => JS_FILE_RE.test(name);
600605

601606
/* istanbul ignore next */
602607
/**
@@ -833,7 +838,7 @@ swcMinify.supportsWorkerThreads = () => false;
833838
* @param {string} name asset name
834839
* @returns {boolean} true if `name` looks like a JavaScript file
835840
*/
836-
swcMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name);
841+
swcMinify.filter = (name) => JS_FILE_RE.test(name);
837842

838843
/* istanbul ignore next */
839844
/**
@@ -954,7 +959,7 @@ esbuildMinify.supportsWorkerThreads = () => false;
954959
* @param {string} name asset name
955960
* @returns {boolean} true if `name` looks like a JavaScript file
956961
*/
957-
esbuildMinify.filter = (name) => /\.[cm]?js(\?.*)?$/i.test(name);
962+
esbuildMinify.filter = (name) => JS_FILE_RE.test(name);
958963

959964
/* istanbul ignore next */
960965
/**
@@ -986,7 +991,7 @@ jsonMinify.supportsWorkerThreads = () => false;
986991
* @param {string} name asset name
987992
* @returns {boolean} true if `name` looks like a JSON file
988993
*/
989-
jsonMinify.filter = (name) => /\.json(\?.*)?$/i.test(name);
994+
jsonMinify.filter = (name) => JSON_FILE_RE.test(name);
990995

991996
/* istanbul ignore next */
992997
/**
@@ -1058,7 +1063,7 @@ htmlMinifierTerser.supportsWorkerThreads = () => true;
10581063
* @param {string} name asset name
10591064
* @returns {boolean} true if `name` looks like an HTML file
10601065
*/
1061-
htmlMinifierTerser.filter = (name) => /\.html?(\?.*)?$/i.test(name);
1066+
htmlMinifierTerser.filter = (name) => HTML_FILE_RE.test(name);
10621067

10631068
/* istanbul ignore next */
10641069
/**
@@ -1111,7 +1116,7 @@ minifyHtmlNode.supportsWorkerThreads = () => false;
11111116
* @param {string} name asset name
11121117
* @returns {boolean} true if `name` looks like an HTML file
11131118
*/
1114-
minifyHtmlNode.filter = (name) => /\.html?(\?.*)?$/i.test(name);
1119+
minifyHtmlNode.filter = (name) => HTML_FILE_RE.test(name);
11151120

11161121
/* istanbul ignore next */
11171122
/**
@@ -1187,7 +1192,7 @@ swcMinifyHtml.supportsWorkerThreads = () => false;
11871192
* @param {string} name asset name
11881193
* @returns {boolean} true if `name` looks like an HTML file
11891194
*/
1190-
swcMinifyHtml.filter = (name) => /\.html?(\?.*)?$/i.test(name);
1195+
swcMinifyHtml.filter = (name) => HTML_FILE_RE.test(name);
11911196

11921197
/* istanbul ignore next */
11931198
/**
@@ -1247,7 +1252,7 @@ swcMinifyHtmlFragment.supportsWorkerThreads = () => false;
12471252
* @param {string} name asset name
12481253
* @returns {boolean} true if `name` looks like an HTML file
12491254
*/
1250-
swcMinifyHtmlFragment.filter = (name) => /\.html?(\?.*)?$/i.test(name);
1255+
swcMinifyHtmlFragment.filter = (name) => HTML_FILE_RE.test(name);
12511256

12521257
/* istanbul ignore next */
12531258
/**
@@ -1399,7 +1404,7 @@ cssnanoMinify.supportsWorkerThreads = () => true;
13991404
* @param {string} name asset name
14001405
* @returns {boolean} true if `name` looks like a CSS file
14011406
*/
1402-
cssnanoMinify.filter = (name) => /\.css(\?.*)?$/i.test(name);
1407+
cssnanoMinify.filter = (name) => CSS_FILE_RE.test(name);
14031408

14041409
/* istanbul ignore next */
14051410
/**
@@ -1459,7 +1464,7 @@ cssoMinify.supportsWorkerThreads = () => true;
14591464
* @param {string} name asset name
14601465
* @returns {boolean} true if `name` looks like a CSS file
14611466
*/
1462-
cssoMinify.filter = (name) => /\.css(\?.*)?$/i.test(name);
1467+
cssoMinify.filter = (name) => CSS_FILE_RE.test(name);
14631468

14641469
/* istanbul ignore next */
14651470
/**
@@ -1537,7 +1542,7 @@ cleanCssMinify.supportsWorkerThreads = () => true;
15371542
* @param {string} name asset name
15381543
* @returns {boolean} true if `name` looks like a CSS file
15391544
*/
1540-
cleanCssMinify.filter = (name) => /\.css(\?.*)?$/i.test(name);
1545+
cleanCssMinify.filter = (name) => CSS_FILE_RE.test(name);
15411546

15421547
/* istanbul ignore next */
15431548
/**
@@ -1658,7 +1663,7 @@ esbuildMinifyCss.supportsWorkerThreads = () => false;
16581663
* @param {string} name asset name
16591664
* @returns {boolean} true if `name` looks like a CSS file
16601665
*/
1661-
esbuildMinifyCss.filter = (name) => /\.css(\?.*)?$/i.test(name);
1666+
esbuildMinifyCss.filter = (name) => CSS_FILE_RE.test(name);
16621667

16631668
/* istanbul ignore next */
16641669
/**
@@ -1733,7 +1738,7 @@ lightningCssMinify.supportsWorkerThreads = () => false;
17331738
* @param {string} name asset name
17341739
* @returns {boolean} true if `name` looks like a CSS file
17351740
*/
1736-
lightningCssMinify.filter = (name) => /\.css(\?.*)?$/i.test(name);
1741+
lightningCssMinify.filter = (name) => CSS_FILE_RE.test(name);
17371742

17381743
/* istanbul ignore next */
17391744
/**
@@ -1823,7 +1828,7 @@ swcMinifyCss.supportsWorkerThreads = () => false;
18231828
* @param {string} name asset name
18241829
* @returns {boolean} true if `name` looks like a CSS file
18251830
*/
1826-
swcMinifyCss.filter = (name) => /\.css(\?.*)?$/i.test(name);
1831+
swcMinifyCss.filter = (name) => CSS_FILE_RE.test(name);
18271832

18281833
/**
18291834
* @template T

types/utils.d.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
export type Task<T> = () => Promise<T>;
2+
export type FunctionReturning<T> = () => T;
13
export type ExtractCommentsOptions =
24
import("./index.js").ExtractCommentsOptions;
35
export type ExtractCommentsFunction =
@@ -10,8 +12,6 @@ export type CustomOptions = import("./index.js").CustomOptions;
1012
export type RawSourceMap = import("./index.js").RawSourceMap;
1113
export type EXPECTED_OBJECT = import("./index.js").EXPECTED_OBJECT;
1214
export type ExtractedComments = string[];
13-
export type Task<T> = () => Promise<T>;
14-
export type FunctionReturning<T> = () => T;
1515
/**
1616
* Minify CSS using `clean-css`.
1717
* @param {Input} input input
@@ -146,17 +146,6 @@ export namespace esbuildMinifyCss {
146146
*/
147147
function filter(name: string): boolean;
148148
}
149-
/** @typedef {import("./index.js").ExtractCommentsOptions} ExtractCommentsOptions */
150-
/** @typedef {import("./index.js").ExtractCommentsFunction} ExtractCommentsFunction */
151-
/** @typedef {import("./index.js").ExtractCommentsCondition} ExtractCommentsCondition */
152-
/** @typedef {import("./index.js").Input} Input */
153-
/** @typedef {import("./index.js").MinimizedResult} MinimizedResult */
154-
/** @typedef {import("./index.js").CustomOptions} CustomOptions */
155-
/** @typedef {import("./index.js").RawSourceMap} RawSourceMap */
156-
/** @typedef {import("./index.js").EXPECTED_OBJECT} EXPECTED_OBJECT */
157-
/**
158-
* @typedef {string[]} ExtractedComments
159-
*/
160149
/**
161150
* Map a webpack `output.environment` configuration to the highest
162151
* ECMAScript version that the target is known to support. Returns `5`

0 commit comments

Comments
 (0)