Skip to content

Commit 0dfbbcc

Browse files
committed
refactor: stop cloning minimizer options on every asset
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.
1 parent 12e271d commit 0dfbbcc

2 files changed

Lines changed: 24 additions & 28 deletions

File tree

src/index.js

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -379,21 +379,13 @@ class TerserPlugin {
379379
const cache = compilation.getCache("TerserWebpackPlugin");
380380
let numberOfAssets = 0;
381381

382-
// Normalize the configured minimizer(s) and their options into parallel
383-
// arrays. The user can pass either a single `(impl, options)` pair or an
384-
// array of pairs; for dispatch and per-asset slicing we treat both the
385-
// same. The original `this.options.minimizer.implementation` shape is
386-
// preserved for chunk hashing further down.
382+
// Normalize the implementation list to an array so dispatch and the
383+
// worker-pool capability checks below can iterate uniformly. The
384+
// original shape on `this.options.minimizer.implementation` is preserved
385+
// for chunk hashing.
387386
const implementations = Array.isArray(this.options.minimizer.implementation)
388387
? this.options.minimizer.implementation
389388
: [this.options.minimizer.implementation];
390-
const minimizerOptionsList =
391-
/** @type {MinimizerOptions<T>[]} */
392-
(
393-
Array.isArray(this.options.minimizer.options)
394-
? this.options.minimizer.options
395-
: implementations.map(() => this.options.minimizer.options)
396-
);
397389

398390
/**
399391
* Collect the indices of minimizers whose `filter` accepts `name`.
@@ -578,16 +570,19 @@ class TerserPlugin {
578570
// Dispatch to only the minimizers whose `filter` accepted this
579571
// asset (computed once when collecting `assetsForMinify`).
580572
// `minify.js` already normalizes a single implementation into a
581-
// one-element array, so we always hand it the matching subset here.
573+
// one-element array, so we always hand it the matching subset.
574+
// Options are sliced as references — `minify.js` overlays
575+
// `module`/`ecma` without mutating the caller's object.
582576
const assetImplementation =
583577
/** @type {MinimizerImplementation<T>} */
584578
(matched.map((i) => implementations[i]));
585-
const clonedMinimizerOptions =
579+
const sourceOptions = this.options.minimizer.options;
580+
const assetMinimizerOptions =
586581
/** @type {MinimizerOptions<T>} */
587582
(
588-
matched.map((i) => ({
589-
.../** @type {T} */ (minimizerOptionsList[i]),
590-
}))
583+
Array.isArray(sourceOptions)
584+
? matched.map((i) => sourceOptions[i] || {})
585+
: sourceOptions
591586
);
592587

593588
/**
@@ -599,7 +594,7 @@ class TerserPlugin {
599594
inputSourceMap,
600595
minimizer: {
601596
implementation: assetImplementation,
602-
options: clonedMinimizerOptions,
597+
options: assetMinimizerOptions,
603598
},
604599
extractComments: this.options.extractComments,
605600
};

src/minify.js

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -323,8 +323,8 @@ async function minify(options) {
323323
const currentImplementation =
324324
/** @type {import("./index.js").BasicMinimizerImplementation<T> & import("./index.js").MinimizeFunctionHelpers} */
325325
(implementations[i]);
326-
const currentOptions =
327-
/** @type {import("./index.js").MinimizerOptions<T>} */
326+
const baseOptions =
327+
/** @type {import("./index.js").MinimizerOptions<T> & { module?: boolean, ecma?: number | string }} */
328328
(
329329
Array.isArray(minimizerOptions)
330330
? minimizerOptions[i] || {}
@@ -333,14 +333,15 @@ async function minify(options) {
333333
const currentInput = typeof lastCode === "string" ? lastCode : input;
334334
const currentMap = typeof lastCode === "string" ? lastMap : inputSourceMap;
335335

336-
/** @type {MinimizerOptions<T & { module?: boolean }>} */
337-
(currentOptions).module =
338-
/** @type {MinimizerOptions<T & { module?: boolean }>} */
339-
(currentOptions).module || module;
340-
/** @type {MinimizerOptions<T & { ecma?: number | string }>} */
341-
(currentOptions).ecma =
342-
/** @type {MinimizerOptions<T & { ecma?: number | string }>} */
343-
(currentOptions).ecma || ecma;
336+
// Overlay `module` and `ecma` without mutating the caller's options so
337+
// a single options object can be reused safely across assets.
338+
const currentOptions =
339+
/** @type {import("./index.js").MinimizerOptions<T>} */
340+
({
341+
...baseOptions,
342+
module: baseOptions.module || module,
343+
ecma: baseOptions.ecma || ecma,
344+
});
344345

345346
const result = await currentImplementation(
346347
{ [name]: currentInput },

0 commit comments

Comments
 (0)