Skip to content

Commit 111f820

Browse files
committed
feat(config): use module paths for default minimizers
1 parent 05a1865 commit 111f820

4 files changed

Lines changed: 124 additions & 6 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"webpack": patch
3+
---
4+
5+
Use module paths for webpack's built-in minimizers so parallel minification can load them directly in workers instead of serializing their function sources.

‎lib/config/defaults.js‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2573,12 +2573,12 @@ const applyOptimizationDefaults = (
25732573
if (minifyJs && !minifyCss && !minifyHtml && !minifyJson) {
25742574
new TerserPlugin({
25752575
test: assetNameTest(["[cm]?js"]),
2576-
minify: require("../javascript/jsMinify"),
2576+
minify: require.resolve("../javascript/jsMinify"),
25772577
minimizerOptions: terserOptions
25782578
}).apply(/** @type {EXPECTED_ANY} */ (compiler));
25792579
return;
25802580
}
2581-
/** @type {(typeof import("../javascript/jsMinify") | typeof import("../css/cssMinify") | typeof import("../html/htmlMinify") | typeof import("../json/jsonMinify"))[]} */
2581+
/** @type {string[]} */
25822582
const minify = [];
25832583
/** @type {EXPECTED_OBJECT[]} */
25842584
const minimizerOptions = [];
@@ -2610,20 +2610,20 @@ const applyOptimizationDefaults = (
26102610
? minimizeByType.html
26112611
: undefined;
26122612
if (minifyJs) {
2613-
minify.push(require("../javascript/jsMinify"));
2613+
minify.push(require.resolve("../javascript/jsMinify"));
26142614
minimizerOptions.push(terserOptions);
26152615
extensions.push("[cm]?js");
26162616
}
26172617
if (minifyCss) {
2618-
minify.push(require("../css/cssMinify"));
2618+
minify.push(require.resolve("../css/cssMinify"));
26192619
minimizerOptions.push({
26202620
environment: cssEnvironment,
26212621
...cssOptions
26222622
});
26232623
extensions.push("css");
26242624
}
26252625
if (minifyHtml) {
2626-
minify.push(require("../html/htmlMinify"));
2626+
minify.push(require.resolve("../html/htmlMinify"));
26272627
// `minimize.css` rides under its own key rather than spread beside the
26282628
// HTML options: the two name some of the same switches, so one flat
26292629
// object would hand each language the other's answer.
@@ -2635,7 +2635,7 @@ const applyOptimizationDefaults = (
26352635
extensions.push("html");
26362636
}
26372637
if (minifyJson) {
2638-
minify.push(require("../json/jsonMinify"));
2638+
minify.push(require.resolve("../json/jsonMinify"));
26392639
minimizerOptions.push({});
26402640
extensions.push("json");
26412641
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { createRequire } from "module";
2+
3+
const require = createRequire(import.meta.url);
4+
5+
const { minify, transform } = require("minimizer-webpack-plugin/dist/minify");
6+
const serialize = require("minimizer-webpack-plugin/dist/serialize-javascript");
7+
const { terserMinify } = require("minimizer-webpack-plugin/dist/utils");
8+
9+
const implementationPath = {
10+
path: require.resolve("minimizer-webpack-plugin/dist/utils"),
11+
export: "terserMinify"
12+
};
13+
const input = "function add(a, b) { return a + b; } console.log(add(1, 2));";
14+
const options = {
15+
name: "bundle.js",
16+
input,
17+
minimizer: {
18+
implementation: terserMinify,
19+
options: { compress: true, mangle: true }
20+
},
21+
extractComments: false
22+
};
23+
const pathOptions = {
24+
...options,
25+
minimizer: {
26+
...options.minimizer,
27+
implementation: implementationPath
28+
}
29+
};
30+
31+
/** @param {import("tinybench").Bench} bench benchmark suite */
32+
export default (bench) => {
33+
bench.add(
34+
'unit benchmark "minimizer-module-path-unit", minify entry by module path',
35+
async () => {
36+
await minify(pathOptions);
37+
}
38+
);
39+
bench.add(
40+
'unit benchmark "minimizer-module-path-unit", transform entry by function source',
41+
async () => {
42+
await transform(serialize(options));
43+
}
44+
);
45+
};
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
"use strict";
2+
3+
const path = require("path");
4+
const MinimizerPlugin = require("minimizer-webpack-plugin");
5+
const { canMinifyByPath } = /** @type {EXPECTED_ANY} */ (
6+
// @ts-expect-error -- the plugin does not publish declarations for this test hook
7+
require("minimizer-webpack-plugin/dist/implementation")
8+
);
9+
const webpack = require("../..");
10+
11+
describe("default minimizer module paths", () => {
12+
/**
13+
* @param {import("../..").Configuration} config configuration
14+
* @returns {EXPECTED_ANY} raw minimizer options
15+
*/
16+
const getRawOptions = (config) => {
17+
const originalApply = MinimizerPlugin.prototype.apply;
18+
/** @type {EXPECTED_ANY} */
19+
let rawOptions;
20+
MinimizerPlugin.prototype.apply = function () {
21+
rawOptions = /** @type {EXPECTED_ANY} */ (this).rawOptions;
22+
};
23+
try {
24+
webpack({ ...config, mode: "production" });
25+
} finally {
26+
MinimizerPlugin.prototype.apply = originalApply;
27+
}
28+
return rawOptions;
29+
};
30+
31+
const expectModulePathDispatch = (/** @type {EXPECTED_ANY} */ rawOptions) => {
32+
expect(rawOptions).toBeDefined();
33+
const implementations = Array.isArray(rawOptions.minify)
34+
? rawOptions.minify
35+
: [rawOptions.minify];
36+
expect(
37+
implementations.every(
38+
(/** @type {EXPECTED_ANY} */ implementation) =>
39+
typeof implementation === "string"
40+
)
41+
).toBe(true);
42+
// This is the plugin's exact guard for worker.minify. A function,
43+
// function-valued option, or mixed implementation would make it false.
44+
expect(canMinifyByPath({
45+
minimizer: {
46+
implementation: rawOptions.minify,
47+
options: rawOptions.minimizerOptions || {}
48+
}
49+
})).toBe(true);
50+
};
51+
52+
it("keeps the JavaScript task on the module-path dispatch", () => {
53+
expectModulePathDispatch(
54+
getRawOptions({
55+
entry: path.resolve(__dirname, "../../package.json")
56+
})
57+
);
58+
});
59+
60+
it("keeps the multi-language task on the module-path dispatch", () => {
61+
expectModulePathDispatch(
62+
getRawOptions({
63+
entry: path.resolve(__dirname, "../../package.json"),
64+
experiments: { css: true, html: true }
65+
})
66+
);
67+
});
68+
});

0 commit comments

Comments
 (0)