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

View File

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

View File

@ -77,13 +77,16 @@ module.exports = class HarmonyExportDependencyParserPlugin {
clearDep.loc = /** @type {DependencyLocation} */ (statement.loc); clearDep.loc = /** @type {DependencyLocation} */ (statement.loc);
clearDep.loc.index = -1; clearDep.loc.index = -1;
parser.state.module.addPresentationalDependency(clearDep); parser.state.module.addPresentationalDependency(clearDep);
const { defer } = getImportMode(parser, statement); let defer = false;
if (defer) { if (this.deferImport) {
const error = new WebpackError( ({ defer } = getImportMode(parser, statement));
"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 };" if (defer) {
); const error = new WebpackError(
error.loc = statement.loc || undefined; "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 };"
parser.state.current.addError(error); );
error.loc = statement.loc || undefined;
parser.state.current.addError(error);
}
} }
const sideEffectDep = new HarmonyImportSideEffectDependency( const sideEffectDep = new HarmonyImportSideEffectDependency(
/** @type {string} */ (source), /** @type {string} */ (source),
@ -202,7 +205,9 @@ module.exports = class HarmonyExportDependencyParserPlugin {
parser.state.harmonyStarExports || new HarmonyStarExportsList(); parser.state.harmonyStarExports || new HarmonyStarExportsList();
} }
const attributes = getImportAttributes(statement); const attributes = getImportAttributes(statement);
const { defer } = getImportMode(parser, statement); const defer = this.deferImport
? getImportMode(parser, statement).defer
: false;
const dep = new HarmonyExportImportedSpecifierDependency( const dep = new HarmonyExportImportedSpecifierDependency(
/** @type {string} */ /** @type {string} */
(source), (source),

View File

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

View File

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

View File

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

View File

@ -97,6 +97,7 @@ describe("snapshots", () => {
"buildHttp": undefined, "buildHttp": undefined,
"cacheUnaffected": false, "cacheUnaffected": false,
"css": undefined, "css": undefined,
"deferImport": false,
"futureDefaults": false, "futureDefaults": false,
"layers": false, "layers": false,
"lazyCompilation": undefined, "lazyCompilation": undefined,
@ -1804,15 +1805,15 @@ describe("snapshots", () => {
{ optimization: { runtimeChunk: "single" } }, { optimization: { runtimeChunk: "single" } },
(e) => (e) =>
e.toMatchInlineSnapshot(` e.toMatchInlineSnapshot(`
- Expected - Expected
+ Received + Received
@@ ... @@ @@ ... @@
- "runtimeChunk": false, - "runtimeChunk": false,
+ "runtimeChunk": Object { + "runtimeChunk": Object {
+ "name": [Function name], + "name": [Function name],
+ }, + },
`) `)
); );
test( test(
@ -2025,9 +2026,10 @@ describe("snapshots", () => {
@@ ... @@ @@ ... @@
- "cacheUnaffected": false, - "cacheUnaffected": false,
- "css": undefined, - "css": undefined,
- "futureDefaults": false,
+ "cacheUnaffected": true, + "cacheUnaffected": true,
+ "css": true, + "css": true,
@@ ... @@
- "futureDefaults": false,
+ "futureDefaults": true, + "futureDefaults": true,
@@ ... @@ @@ ... @@
+ }, + },
@ -2491,9 +2493,10 @@ describe("snapshots", () => {
@@ ... @@ @@ ... @@
- "cacheUnaffected": false, - "cacheUnaffected": false,
- "css": undefined, - "css": undefined,
- "futureDefaults": false,
+ "cacheUnaffected": true, + "cacheUnaffected": true,
+ "css": true, + "css": true,
@@ ... @@
- "futureDefaults": false,
+ "futureDefaults": true, + "futureDefaults": true,
@@ ... @@ @@ ... @@
+ }, + },
@ -2637,9 +2640,10 @@ describe("snapshots", () => {
@@ ... @@ @@ ... @@
- "cacheUnaffected": false, - "cacheUnaffected": false,
- "css": undefined, - "css": undefined,
- "futureDefaults": false,
+ "cacheUnaffected": true, + "cacheUnaffected": true,
+ "css": false, + "css": false,
@@ ... @@
- "futureDefaults": false,
+ "futureDefaults": true, + "futureDefaults": true,
@@ ... @@ @@ ... @@
+ }, + },

View File

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