perf: fix regression in module concatenation

This commit is contained in:
lizhihua 2025-07-25 23:14:00 +08:00 committed by GitHub
parent 4065bddc9d
commit aeab4c5ac1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 63 additions and 38 deletions

View File

@ -11,6 +11,7 @@ const ModuleGraphConnection = require("./ModuleGraphConnection");
const SortableSet = require("./util/SortableSet");
const WeakTupleMap = require("./util/WeakTupleMap");
const { sortWithSourceOrder } = require("./util/comparators");
const memoize = require("./util/memoize");
/** @typedef {import("./Compilation").ModuleMemCaches} ModuleMemCaches */
/** @typedef {import("./DependenciesBlock")} DependenciesBlock */
@ -24,6 +25,10 @@ const { sortWithSourceOrder } = require("./util/comparators");
/** @typedef {import("./dependencies/HarmonyImportSpecifierDependency")} HarmonyImportSpecifierDependency */
/** @typedef {import("./util/comparators").DependencySourceOrder} DependencySourceOrder */
const getCommonJsSelfReferenceDependency = memoize(() =>
require("./dependencies/CommonJsSelfReferenceDependency")
);
/**
* @callback OptimizationBailoutFunction
* @param {RequestShortener} requestShortener
@ -840,8 +845,7 @@ class ModuleGraph {
for (const connection of connections) {
if (
!connection.dependency ||
connection.dependency instanceof
require("./dependencies/CommonJsSelfReferenceDependency")
connection.dependency instanceof getCommonJsSelfReferenceDependency()
) {
continue;
}

View File

@ -384,6 +384,7 @@ const applyExperimentsDefaults = (
D(experiments, "lazyCompilation", undefined);
D(experiments, "buildHttp", undefined);
D(experiments, "cacheUnaffected", experiments.futureDefaults);
D(experiments, "deferImport", false);
F(experiments, "css", () => (experiments.futureDefaults ? true : undefined));
// TODO webpack 6: remove this. topLevelAwait should be enabled by default

View File

@ -77,7 +77,9 @@ module.exports = class HarmonyExportDependencyParserPlugin {
clearDep.loc = /** @type {DependencyLocation} */ (statement.loc);
clearDep.loc.index = -1;
parser.state.module.addPresentationalDependency(clearDep);
const { defer } = getImportMode(parser, statement);
let defer = false;
if (this.deferImport) {
({ defer } = getImportMode(parser, statement));
if (defer) {
const error = new WebpackError(
"Deferred re-export (`export defer * as namespace from '...'`) is not a part of the Import Defer proposal.\nUse the following code instead:\n import defer * as namespace from '...';\n export { namespace };"
@ -85,6 +87,7 @@ module.exports = class HarmonyExportDependencyParserPlugin {
error.loc = statement.loc || undefined;
parser.state.current.addError(error);
}
}
const sideEffectDep = new HarmonyImportSideEffectDependency(
/** @type {string} */ (source),
parser.state.lastHarmonyImportOrder,
@ -202,7 +205,9 @@ module.exports = class HarmonyExportDependencyParserPlugin {
parser.state.harmonyStarExports || new HarmonyStarExportsList();
}
const attributes = getImportAttributes(statement);
const { defer } = getImportMode(parser, statement);
const defer = this.deferImport
? getImportMode(parser, statement).defer
: false;
const dep = new HarmonyExportImportedSpecifierDependency(
/** @type {string} */
(source),

View File

@ -124,7 +124,9 @@ module.exports = class HarmonyImportDependencyParserPlugin {
parser.state.module.addPresentationalDependency(clearDep);
parser.unsetAsiPosition(/** @type {Range} */ (statement.range)[1]);
const attributes = getImportAttributes(statement);
const { defer } = getImportMode(parser, statement);
let defer = false;
if (this.deferImport) {
({ defer } = getImportMode(parser, statement));
if (
defer &&
(statement.specifiers.length !== 1 ||
@ -136,6 +138,7 @@ module.exports = class HarmonyImportDependencyParserPlugin {
error.loc = statement.loc || undefined;
parser.state.current.addError(error);
}
}
const sideEffectDep = new HarmonyImportSideEffectDependency(
/** @type {string} */ (source),
parser.state.lastHarmonyImportOrder,
@ -150,7 +153,9 @@ module.exports = class HarmonyImportDependencyParserPlugin {
PLUGIN_NAME,
(statement, source, id, name) => {
const ids = id === null ? [] : [id];
const { defer } = getImportMode(parser, statement);
const defer = this.deferImport
? getImportMode(parser, statement).defer
: false;
parser.tagVariable(name, harmonySpecifierTag, {
name,
source,
@ -391,7 +396,7 @@ module.exports = class HarmonyImportDependencyParserPlugin {
/**
* @param {JavascriptParser} parser parser
* @param {ExportNamedDeclaration | ExportAllDeclaration | ImportDeclaration} node node
* @returns {{defer: boolean}} import attributes
* @returns {{ defer: boolean }} import attributes
*/
function getImportMode(parser, node) {
const result = { defer: "phase" in node && node.phase === "defer" };

View File

@ -945,7 +945,8 @@ class ConcatenatedModule extends Module {
/** @type {Map<Module, RuntimeSpec | true>} */
const existingEntries = new Map();
const deferEnabled =
this.compilation && this.compilation.options.experiments.deferImport;
/** @type {Compilation} */
(this.compilation).options.experiments.deferImport;
/**
* @param {Module} module a module
@ -1712,7 +1713,8 @@ ${defineGetters}`
/** @type {InitFragment<ChunkRenderContext>[]} */
const chunkInitFragments = [];
const deferEnabled =
this.compilation && this.compilation.options.experiments.deferImport;
/** @type {Compilation} */
(this.compilation).options.experiments.deferImport;
// evaluate modules in order
for (const rawInfo of modulesWithInfo) {

View File

@ -149,6 +149,7 @@ class ModuleConcatenationPlugin {
chunkGraph,
moduleGraph
};
const deferEnabled = compilation.options.experiments.deferImport;
logger.time("select relevant modules");
for (const module of modules) {
let canBeRoot = true;
@ -223,7 +224,7 @@ class ModuleConcatenationPlugin {
canBeInner = false;
}
if (moduleGraph.isDeferred(module)) {
if (deferEnabled && moduleGraph.isDeferred(module)) {
setInnerBailoutReason(module, "Module is deferred");
canBeInner = false;
}

View File

@ -97,6 +97,7 @@ describe("snapshots", () => {
"buildHttp": undefined,
"cacheUnaffected": false,
"css": undefined,
"deferImport": false,
"futureDefaults": false,
"layers": false,
"lazyCompilation": undefined,
@ -2025,9 +2026,10 @@ describe("snapshots", () => {
@@ ... @@
- "cacheUnaffected": false,
- "css": undefined,
- "futureDefaults": false,
+ "cacheUnaffected": true,
+ "css": true,
@@ ... @@
- "futureDefaults": false,
+ "futureDefaults": true,
@@ ... @@
+ },
@ -2491,9 +2493,10 @@ describe("snapshots", () => {
@@ ... @@
- "cacheUnaffected": false,
- "css": undefined,
- "futureDefaults": false,
+ "cacheUnaffected": true,
+ "css": true,
@@ ... @@
- "futureDefaults": false,
+ "futureDefaults": true,
@@ ... @@
+ },
@ -2637,9 +2640,10 @@ describe("snapshots", () => {
@@ ... @@
- "cacheUnaffected": false,
- "css": undefined,
- "futureDefaults": false,
+ "cacheUnaffected": true,
+ "css": false,
@@ ... @@
- "futureDefaults": false,
+ "futureDefaults": true,
@@ ... @@
+ },

View File

@ -2,6 +2,9 @@
module.exports = {
output: {
filename: "[name].js"
filename: "[name].js",
environment: {
nodePrefixForCoreModules: false
}
}
};