diff --git a/lib/ModuleGraph.js b/lib/ModuleGraph.js index 2ebcebd61..20bf162c1 100644 --- a/lib/ModuleGraph.js +++ b/lib/ModuleGraph.js @@ -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; } diff --git a/lib/config/defaults.js b/lib/config/defaults.js index 0c07d8742..05cda8c4f 100644 --- a/lib/config/defaults.js +++ b/lib/config/defaults.js @@ -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 diff --git a/lib/dependencies/HarmonyExportDependencyParserPlugin.js b/lib/dependencies/HarmonyExportDependencyParserPlugin.js index dfb9f5463..eb6162d36 100644 --- a/lib/dependencies/HarmonyExportDependencyParserPlugin.js +++ b/lib/dependencies/HarmonyExportDependencyParserPlugin.js @@ -77,13 +77,16 @@ module.exports = class HarmonyExportDependencyParserPlugin { clearDep.loc = /** @type {DependencyLocation} */ (statement.loc); clearDep.loc.index = -1; parser.state.module.addPresentationalDependency(clearDep); - const { 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 };" - ); - error.loc = statement.loc || undefined; - parser.state.current.addError(error); + 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 };" + ); + error.loc = statement.loc || undefined; + parser.state.current.addError(error); + } } const sideEffectDep = new HarmonyImportSideEffectDependency( /** @type {string} */ (source), @@ -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), diff --git a/lib/dependencies/HarmonyImportDependencyParserPlugin.js b/lib/dependencies/HarmonyImportDependencyParserPlugin.js index 4e131dda3..45a230c23 100644 --- a/lib/dependencies/HarmonyImportDependencyParserPlugin.js +++ b/lib/dependencies/HarmonyImportDependencyParserPlugin.js @@ -124,17 +124,20 @@ 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); - if ( - defer && - (statement.specifiers.length !== 1 || - statement.specifiers[0].type !== "ImportNamespaceSpecifier") - ) { - const error = new WebpackError( - "Deferred import can only be used with `import * as namespace from '...'` syntax." - ); - error.loc = statement.loc || undefined; - parser.state.current.addError(error); + let defer = false; + if (this.deferImport) { + ({ defer } = getImportMode(parser, statement)); + if ( + defer && + (statement.specifiers.length !== 1 || + statement.specifiers[0].type !== "ImportNamespaceSpecifier") + ) { + const error = new WebpackError( + "Deferred import can only be used with `import * as namespace from '...'` syntax." + ); + error.loc = statement.loc || undefined; + parser.state.current.addError(error); + } } const sideEffectDep = new HarmonyImportSideEffectDependency( /** @type {string} */ (source), @@ -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" }; diff --git a/lib/optimize/ConcatenatedModule.js b/lib/optimize/ConcatenatedModule.js index 417f025a9..c551695c5 100644 --- a/lib/optimize/ConcatenatedModule.js +++ b/lib/optimize/ConcatenatedModule.js @@ -945,7 +945,8 @@ class ConcatenatedModule extends Module { /** @type {Map} */ 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[]} */ 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) { diff --git a/lib/optimize/ModuleConcatenationPlugin.js b/lib/optimize/ModuleConcatenationPlugin.js index 559b0d32a..8d9618df0 100644 --- a/lib/optimize/ModuleConcatenationPlugin.js +++ b/lib/optimize/ModuleConcatenationPlugin.js @@ -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; } diff --git a/test/Defaults.unittest.js b/test/Defaults.unittest.js index d92a9c7b1..f9c3f9c3c 100644 --- a/test/Defaults.unittest.js +++ b/test/Defaults.unittest.js @@ -97,6 +97,7 @@ describe("snapshots", () => { "buildHttp": undefined, "cacheUnaffected": false, "css": undefined, + "deferImport": false, "futureDefaults": false, "layers": false, "lazyCompilation": undefined, @@ -1804,15 +1805,15 @@ describe("snapshots", () => { { optimization: { runtimeChunk: "single" } }, (e) => e.toMatchInlineSnapshot(` - - Expected - + Received + - Expected + + Received - @@ ... @@ - - "runtimeChunk": false, - + "runtimeChunk": Object { - + "name": [Function name], - + }, - `) + @@ ... @@ + - "runtimeChunk": false, + + "runtimeChunk": Object { + + "name": [Function name], + + }, + `) ); test( @@ -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, @@ ... @@ + }, diff --git a/test/configCases/split-chunks/asnyc-entries/webpack.config.js b/test/configCases/split-chunks/asnyc-entries/webpack.config.js index 668c6ba5e..d45fc7ec2 100644 --- a/test/configCases/split-chunks/asnyc-entries/webpack.config.js +++ b/test/configCases/split-chunks/asnyc-entries/webpack.config.js @@ -2,6 +2,9 @@ module.exports = { output: { - filename: "[name].js" + filename: "[name].js", + environment: { + nodePrefixForCoreModules: false + } } };