diff --git a/lib/Chunk.js b/lib/Chunk.js index dc17206dd..80d5a77d9 100644 --- a/lib/Chunk.js +++ b/lib/Chunk.js @@ -5,15 +5,17 @@ "use strict"; +const ChunkGraph = require("./ChunkGraph"); const Entrypoint = require("./Entrypoint"); const { intersect } = require("./util/SetHelpers"); const SortableSet = require("./util/SortableSet"); const { compareModulesById } = require("./util/comparators"); /** @typedef {import("webpack-sources").Source} Source */ -/** @typedef {import("./ChunkGraph")} ChunkGraph */ /** @typedef {import("./ChunkGroup")} ChunkGroup */ +/** @typedef {import("./Compilation")} Compilation */ /** @typedef {import("./Module")} Module */ +/** @typedef {import("./ModuleGraph")} ModuleGraph */ /** @typedef {import("./ModuleReason")} ModuleReason */ /** @typedef {import("./util/createHash").Hash} Hash */ @@ -87,6 +89,169 @@ class Chunk { this.removedModules = undefined; } + // TODO remove in webpack 6 + // BACKWARD-COMPAT START + get entryModule() { + const entryModules = Array.from( + ChunkGraph.getChunkGraphForChunk( + this, + "Chunk.entryModule" + ).getChunkEntryModulesIterable(this) + ); + if (entryModules.length === 0) { + return undefined; + } else if (entryModules.length === 1) { + return entryModules[0]; + } else { + throw new Error( + "Module.entryModule: Multiple entry modules are not supported by the deprecated API (Use the new ChunkGroup API)" + ); + } + } + + hasEntryModule() { + return ( + ChunkGraph.getChunkGraphForChunk( + this, + "Chunk.hasEntryModule" + ).getNumberOfEntryModules(this) > 0 + ); + } + + addModule(module) { + return ChunkGraph.getChunkGraphForChunk( + this, + "Chunk.addModule" + ).connectChunkAndModule(this, module); + } + + removeModule(module) { + return ChunkGraph.getChunkGraphForChunk( + this, + "Chunk.removeModule" + ).disconnectChunkAndModule(this, module); + } + + getNumberOfModules() { + return ChunkGraph.getChunkGraphForChunk( + this, + "Chunk.getNumberOfModules" + ).getNumberOfChunkModules(this); + } + + get modulesIterable() { + const chunkGraph = ChunkGraph.getChunkGraphForChunk( + this, + "Chunk.modulesIterable" + ); + return chunkGraph.getOrderedChunkModulesIterable( + this, + compareModulesById(chunkGraph._moduleGraph) + ); + } + + compareTo(otherChunk) { + const chunkGraph = ChunkGraph.getChunkGraphForChunk( + this, + "Chunk.compareTo" + ); + return chunkGraph.compareChunks(chunkGraph._moduleGraph, this, otherChunk); + } + + containsModule(module) { + return ChunkGraph.getChunkGraphForChunk( + this, + "Chunk.containsModule" + ).isModuleInChunk(module, this); + } + + getModules() { + return ChunkGraph.getChunkGraphForChunk( + this, + "Chunk.getModules" + ).getChunkModules(this); + } + + remove() { + const chunkGraph = ChunkGraph.getChunkGraphForChunk(this, "Chunk.remove"); + chunkGraph.disconnectChunk(this); + this.disconnectFromGroups(); + } + + moveModule(module, otherChunk) { + const chunkGraph = ChunkGraph.getChunkGraphForChunk( + this, + "Chunk.moveModule" + ); + chunkGraph.disconnectChunkAndModule(this, module); + chunkGraph.connectChunkAndModule(otherChunk, module); + } + + integrate(otherChunk) { + const chunkGraph = ChunkGraph.getChunkGraphForChunk( + this, + "Chunk.integrate" + ); + if (!chunkGraph.canChunksBeIntegrated(this, otherChunk)) return false; + chunkGraph.integrateChunks(this, otherChunk); + return true; + } + + canBeIntegrated(otherChunk) { + const chunkGraph = ChunkGraph.getChunkGraphForChunk( + this, + "Chunk.canBeIntegrated" + ); + return chunkGraph.canChunksBeIntegrated(this, otherChunk); + } + + isEmpty() { + const chunkGraph = ChunkGraph.getChunkGraphForChunk(this, "Chunk.isEmpty"); + return chunkGraph.getNumberOfChunkModules(this) === 0; + } + + modulesSize() { + const chunkGraph = ChunkGraph.getChunkGraphForChunk( + this, + "Chunk.modulesSize" + ); + return chunkGraph.getChunkModulesSize(this); + } + + size(options) { + const chunkGraph = ChunkGraph.getChunkGraphForChunk(this, "Chunk.size"); + return chunkGraph.getChunkSize(this, options); + } + + integratedSize(otherChunk, options) { + const chunkGraph = ChunkGraph.getChunkGraphForChunk( + this, + "Chunk.integratedSize" + ); + return chunkGraph.getIntegratedChunksSize(this, otherChunk, options); + } + + getChunkModuleMaps(filterFn) { + const chunkGraph = ChunkGraph.getChunkGraphForChunk( + this, + "Chunk.getChunkModuleMaps" + ); + return chunkGraph.getChunkModuleMaps( + chunkGraph._moduleGraph, + this, + filterFn + ); + } + + hasModuleInGraph(filterFn, filterChunkFn) { + const chunkGraph = ChunkGraph.getChunkGraphForChunk( + this, + "Chunk.hasModuleInGraph" + ); + return chunkGraph.hasModuleInGraph(this, filterFn, filterChunkFn); + } + // BACKWARD-COMPAT END + /** * @returns {boolean} whether or not the Chunk will have a runtime */ @@ -187,20 +352,20 @@ class Chunk { /** * @param {Hash} hash hash (will be modified) - * @param {ChunkGraph} chunkGraph the chunk graph + * @param {Compilation} compilation the compilation * @returns {void} */ - updateHash(hash, chunkGraph) { + updateHash(hash, compilation) { hash.update(`${this.id} `); hash.update(this.ids ? this.ids.join(",") : ""); hash.update(`${this.name || ""} `); - for (const m of chunkGraph.getOrderedChunkModulesIterable( + for (const m of compilation.chunkGraph.getOrderedChunkModulesIterable( this, - compareModulesById + compareModulesById(compilation.moduleGraph) )) { hash.update(m.hash); } - const entryModules = chunkGraph.getChunkEntryModulesWithChunkGroupIterable( + const entryModules = compilation.chunkGraph.getChunkEntryModulesWithChunkGroupIterable( this ); for (const [m, chunkGroup] of entryModules) { @@ -281,10 +446,11 @@ class Chunk { } /** + * @param {ModuleGraph} moduleGraph the module graph * @param {ChunkGraph} chunkGraph the chunk graph * @returns {Record[]>} a record object of names to lists of child ids(?) */ - getChildIdsByOrders(chunkGraph) { + getChildIdsByOrders(moduleGraph, chunkGraph) { const lists = new Map(); for (const group of this.groupsIterable) { if (group.chunks[group.chunks.length - 1] === this) { @@ -308,7 +474,7 @@ class Chunk { list.sort((a, b) => { const cmp = b.order - a.order; if (cmp !== 0) return cmp; - return a.group.compareTo(chunkGraph, b.group); + return a.group.compareTo(moduleGraph, chunkGraph, b.group); }); result[name] = Array.from( list.reduce((set, item) => { @@ -322,11 +488,17 @@ class Chunk { return result; } - getChildIdsByOrdersMap(chunkGraph, includeDirectChildren) { + /** + * @param {ModuleGraph} moduleGraph the module graph + * @param {ChunkGraph} chunkGraph the chunk graph + * @param {boolean=} includeDirectChildren include direct children (by default only children of async children are included) + * @returns {Record[]>>} a record object of names to lists of child ids(?) by chunk id + */ + getChildIdsByOrdersMap(moduleGraph, chunkGraph, includeDirectChildren) { const chunkMaps = Object.create(null); const addChildIdsByOrdersToMap = chunk => { - const data = chunk.getChildIdsByOrders(chunkGraph); + const data = chunk.getChildIdsByOrders(moduleGraph, chunkGraph); for (const key of Object.keys(data)) { let chunkMap = chunkMaps[key]; if (chunkMap === undefined) { diff --git a/lib/ChunkGraph.js b/lib/ChunkGraph.js index c521fa651..3996a6078 100644 --- a/lib/ChunkGraph.js +++ b/lib/ChunkGraph.js @@ -5,12 +5,14 @@ "use strict"; +const util = require("util"); const SortableSet = require("./util/SortableSet"); const { compareModulesById } = require("./util/comparators"); /** @typedef {import("./Chunk")} Chunk */ /** @typedef {import("./ChunkGroup")} ChunkGroup */ /** @typedef {import("./Module")} Module */ +/** @typedef {import("./ModuleGraph")} ModuleGraph */ /** @typedef {(m: Module) => boolean} ModuleFilterPredicate */ @@ -85,11 +87,16 @@ class ChunkGraphChunk { } class ChunkGraph { - constructor() { + /** + * @param {ModuleGraph} moduleGraph the module graph + */ + constructor(moduleGraph) { /** @private @type {WeakMap} */ this._modules = new WeakMap(); /** @private @type {WeakMap} */ this._chunks = new WeakMap(); + /** @private @type {ModuleGraph} */ + this._moduleGraph = moduleGraph; } /** @@ -354,11 +361,12 @@ class ChunkGraph { */ /** + * @param {ModuleGraph} moduleGraph the module graph * @param {Chunk} chunk the chunk * @param {ModuleFilterPredicate} filterFn function used to filter modules * @returns {ChunkModuleMaps} module map information */ - getChunkModuleMaps(chunk, filterFn) { + getChunkModuleMaps(moduleGraph, chunk, filterFn) { /** @type {Record} */ const chunkModuleIdMap = Object.create(null); /** @type {Record} */ @@ -369,7 +377,7 @@ class ChunkGraph { let array; for (const module of this.getOrderedChunkModulesIterable( asyncChunk, - compareModulesById + compareModulesById(moduleGraph) )) { if (filterFn(module)) { if (array === undefined) { @@ -422,17 +430,19 @@ class ChunkGraph { } /** + * @param {ModuleGraph} moduleGraph the module graph * @param {Chunk} chunkA first chunk * @param {Chunk} chunkB second chunk * @returns {-1|0|1} this is a comparitor function like sort and returns -1, 0, or 1 based on sort order */ - compareChunks(chunkA, chunkB) { + compareChunks(moduleGraph, chunkA, chunkB) { const cgcA = this._getChunkGraphChunk(chunkA); const cgcB = this._getChunkGraphChunk(chunkB); if (cgcA.modules.size > cgcB.modules.size) return -1; if (cgcA.modules.size < cgcB.modules.size) return 1; - cgcA.modules.sortWith(compareModulesById); - cgcB.modules.sortWith(compareModulesById); + const cmpFn = compareModulesById(moduleGraph); + cgcA.modules.sortWith(cmpFn); + cgcB.modules.sortWith(cmpFn); const a = cgcA.modules[Symbol.iterator](); const b = cgcB.modules[Symbol.iterator](); // eslint-disable-next-line no-constant-condition @@ -665,6 +675,100 @@ class ChunkGraph { const cgc = this._getChunkGraphChunk(chunk); return cgc.entryModules; } + + // TODO remove in webpack 6 + /** + * @param {Module} module the module + * @param {string} deprecateMessage message for the deprecation message + * @returns {ChunkGraph} the chunk graph + */ + static getChunkGraphForModule(module, deprecateMessage) { + const fn = deprecateGetChunkGraphForModuleMap.get(deprecateMessage); + if (fn) return fn(module); + const newFn = util.deprecate( + /** + * @param {Module} module the module + * @returns {ChunkGraph} the chunk graph + */ + module => { + const chunkGraph = chunkGraphForModuleMap.get(module); + if (!chunkGraph) + throw new Error( + deprecateMessage + + ": There was no ChunkGraph assigned to the Module for backward-compat (Use the new API)" + ); + return chunkGraph; + }, + deprecateMessage + ": Use new ChunkGraph API" + ); + deprecateGetChunkGraphForModuleMap.set(deprecateMessage, newFn); + return newFn(module); + } + + // TODO remove in webpack 6 + /** + * @param {Module} module the module + * @param {ChunkGraph} chunkGraph the chunk graph + * @returns {void} + */ + static setChunkGraphForModule(module, chunkGraph) { + chunkGraphForModuleMap.set(module, chunkGraph); + } + + // TODO remove in webpack 6 + /** + * @param {Chunk} chunk the chunk + * @param {string} deprecateMessage message for the deprecation message + * @returns {ChunkGraph} the chunk graph + */ + static getChunkGraphForChunk(chunk, deprecateMessage) { + const fn = deprecateGetChunkGraphForChunkMap.get(deprecateMessage); + if (fn) return fn(chunk); + const newFn = util.deprecate( + /** + * @param {Chunk} chunk the chunk + * @returns {ChunkGraph} the chunk graph + */ + chunk => { + const chunkGraph = chunkGraphForChunkMap.get(chunk); + if (!chunkGraph) + throw new Error( + deprecateMessage + + "There was no ChunkGraph assigned to the Chunk for backward-compat (Use the new API)" + ); + return chunkGraph; + }, + deprecateMessage + ": Use new ChunkGraph API" + ); + deprecateGetChunkGraphForChunkMap.set(deprecateMessage, newFn); + return newFn(chunk); + } + + // TODO remove in webpack 6 + /** + * @param {Chunk} chunk the chunk + * @param {ChunkGraph} chunkGraph the chunk graph + * @returns {void} + */ + static setChunkGraphForChunk(chunk, chunkGraph) { + chunkGraphForChunkMap.set(chunk, chunkGraph); + } } +// TODO remove in webpack 6 +/** @type {WeakMap} */ +const chunkGraphForModuleMap = new WeakMap(); + +// TODO remove in webpack 6 +/** @type {WeakMap} */ +const chunkGraphForChunkMap = new WeakMap(); + +// TODO remove in webpack 6 +/** @type {Map ChunkGraph>} */ +const deprecateGetChunkGraphForModuleMap = new Map(); + +// TODO remove in webpack 6 +/** @type {Map ChunkGraph>} */ +const deprecateGetChunkGraphForChunkMap = new Map(); + module.exports = ChunkGraph; diff --git a/lib/ChunkGroup.js b/lib/ChunkGroup.js index 4c7f4a081..ca5af4e19 100644 --- a/lib/ChunkGroup.js +++ b/lib/ChunkGroup.js @@ -12,6 +12,7 @@ const SortableSet = require("./util/SortableSet"); /** @typedef {import("./ChunkGraph")} ChunkGraph */ /** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */ /** @typedef {import("./Module")} Module */ +/** @typedef {import("./ModuleGraph")} ModuleGraph */ /** @typedef {import("./ModuleReason")} ModuleReason */ /** @typedef {{id: number}} HasId */ @@ -401,11 +402,12 @@ class ChunkGroup { * Sorting predicate which allows current ChunkGroup to be compared against another. * Sorting values are based off of number of chunks in ChunkGroup. * + * @param {ModuleGraph} moduleGraph the module graph * @param {ChunkGraph} chunkGraph the chunk graph * @param {ChunkGroup} otherGroup the chunkGroup to compare this against * @returns {-1|0|1} sort position for comparison */ - compareTo(chunkGraph, otherGroup) { + compareTo(moduleGraph, chunkGraph, otherGroup) { if (this.chunks.length > otherGroup.chunks.length) return -1; if (this.chunks.length < otherGroup.chunks.length) return 1; const a = this.chunks[Symbol.iterator](); @@ -415,12 +417,22 @@ class ChunkGroup { const aItem = a.next(); const bItem = b.next(); if (aItem.done) return 0; - const cmp = chunkGraph.compareChunks(aItem.value, bItem.value); + const cmp = chunkGraph.compareChunks( + moduleGraph, + aItem.value, + bItem.value + ); if (cmp !== 0) return cmp; } } - getChildrenByOrders(chunkGraph) { + /** + * @param {ModuleGraph} moduleGraph the module graph + * @param {ChunkGraph} chunkGraph the chunk graph + * @returns {Record} mapping from children type to ordered list of ChunkGroups + */ + getChildrenByOrders(moduleGraph, chunkGraph) { + /** @type {Map} */ const lists = new Map(); for (const childGroup of this._children) { for (const key of Object.keys(childGroup.options)) { @@ -437,12 +449,13 @@ class ChunkGroup { } } } + /** @type {Record} */ const result = Object.create(null); for (const [name, list] of lists) { list.sort((a, b) => { const cmp = b.order - a.order; if (cmp !== 0) return cmp; - return a.group.compareTo(chunkGraph, b.group); + return a.group.compareTo(moduleGraph, chunkGraph, b.group); }); result[name] = list.map(i => i.group); } diff --git a/lib/Compilation.js b/lib/Compilation.js index c88ec29c9..ead808e39 100644 --- a/lib/Compilation.js +++ b/lib/Compilation.js @@ -40,6 +40,7 @@ const compareLocations = require("./compareLocations"); const Queue = require("./util/Queue"); const Semaphore = require("./util/Semaphore"); const SortableSet = require("./util/SortableSet"); +const { compareModulesByIndexOrIdentifier } = require("./util/comparators"); const createHash = require("./util/createHash"); /** @typedef {import("webpack-sources").Source} Source */ @@ -131,21 +132,6 @@ const byIdOrIdentifier = (a, b) => { return 0; }; -/** - * @param {Module} a first module to sort by - * @param {Module} b second module to sort by - * @returns {-1|0|1} sort value - */ -const byIndexOrIdentifier = (a, b) => { - if (a.index < b.index) return -1; - if (a.index > b.index) return 1; - const identA = a.identifier(); - const identB = b.identifier(); - if (identA < identB) return -1; - if (identA > identB) return 1; - return 0; -}; - /** * @param {Compilation} a first compilation to sort by * @param {Compilation} b second compilation to sort by @@ -791,7 +777,7 @@ class Compilation { dependentModule.profile = currentProfile; } - dependentModule.setIssuer(this.moduleGraph, module); + this.moduleGraph.setIssuer(dependentModule, module); } else { if (this.profile) { if (module.profile) { @@ -1070,6 +1056,7 @@ class Compilation { for (const module of this.modules) { module.unseal(); } + this.moduleGraph.removeAllModuleAttributes(); } /** @@ -1077,9 +1064,13 @@ class Compilation { * @returns {void} */ seal(callback) { - const chunkGraph = new ChunkGraph(); + const chunkGraph = new ChunkGraph(this.moduleGraph); this.chunkGraph = chunkGraph; + for (const module of this.modules) { + ChunkGraph.setChunkGraphForModule(module, chunkGraph); + } + this.hooks.seal.call(); while (this.hooks.optimizeDependencies.call(this.modules)) { @@ -1217,7 +1208,7 @@ class Compilation { // TODO webpack 5: this should only be enabled when `moduleIds: "natural"` // TODO move it into a plugin (NaturalModuleIdsPlugin) and use this in WebpackOptionsApply // TODO remove this method - modules.sort(byIndexOrIdentifier); + modules.sort(compareModulesByIndexOrIdentifier(this.moduleGraph)); } /** @@ -1308,6 +1299,7 @@ class Compilation { } const chunk = new Chunk(name); this.chunks.push(chunk); + ChunkGraph.setChunkGraphForChunk(chunk, this.chunkGraph); if (name) { this.namedChunks.set(name, chunk); } @@ -1395,6 +1387,8 @@ class Compilation { // eachother and Blocks with Chunks. It stops traversing when all modules // for a chunk are already available. So it doesn't connect unneeded chunks. + const moduleGraph = this.moduleGraph; + /** @type {Map} */ const chunkDependencies = new Map(); /** @type {Set} */ @@ -1481,8 +1475,8 @@ class Compilation { chunkGroupCounters.set(chunkGroup, { index: 0, index2: 0 }); } - let nextFreeModuleIndex = 0; - let nextFreeModuleIndex2 = 0; + let nextFreeModulePreOrderIndex = 0; + let nextFreeModulePostOrderIndex = 0; /** @type {Map} */ const blockChunkGroups = new Map(); @@ -1619,8 +1613,13 @@ class Compilation { } } - if (module.index === null) { - module.index = nextFreeModuleIndex++; + if ( + moduleGraph.setPreOrderIndexIfUnset( + module, + nextFreeModulePreOrderIndex + ) + ) { + nextFreeModulePreOrderIndex++; } queue.push({ @@ -1673,8 +1672,13 @@ class Compilation { } } - if (module.index2 === null) { - module.index2 = nextFreeModuleIndex2++; + if ( + moduleGraph.setPostOrderIndexIfUnset( + module, + nextFreeModulePostOrderIndex + ) + ) { + nextFreeModulePostOrderIndex++; } break; } @@ -2139,7 +2143,7 @@ class Compilation { if (outputOptions.hashSalt) { chunkHash.update(outputOptions.hashSalt); } - chunk.updateHash(chunkHash, this.chunkGraph); + chunk.updateHash(chunkHash, this); const template = chunk.hasRuntime() ? this.mainTemplate : this.chunkTemplate; diff --git a/lib/FlagDependencyUsagePlugin.js b/lib/FlagDependencyUsagePlugin.js index 69efb7905..8420956ac 100644 --- a/lib/FlagDependencyUsagePlugin.js +++ b/lib/FlagDependencyUsagePlugin.js @@ -51,14 +51,14 @@ class FlagDependencyUsagePlugin { * @returns {void} */ const processModule = (module, usedExports) => { - let ue = module.getUsedExports(moduleGraph); + let ue = moduleGraph.getUsedExports(module); if (ue === true) return; if (usedExports === true) { - module.setUsedExports(moduleGraph, (ue = true)); + moduleGraph.setUsedExports(module, (ue = true)); } else if (Array.isArray(usedExports)) { if (!ue) { - module.setUsedExports( - moduleGraph, + moduleGraph.setUsedExports( + module, (ue = new SortableSet(usedExports)) ); } else { @@ -72,7 +72,7 @@ class FlagDependencyUsagePlugin { } } else { if (ue !== false) return; - module.setUsedExports(moduleGraph, (ue = new SortableSet())); + moduleGraph.setUsedExports(module, (ue = new SortableSet())); } // for a module without side effects we stop tracking usage here when no export is used @@ -109,7 +109,7 @@ class FlagDependencyUsagePlugin { if (!reference) return; const referenceModule = reference.module; const importedNames = reference.importedNames; - const oldUsedExports = referenceModule.getUsedExports(moduleGraph); + const oldUsedExports = moduleGraph.getUsedExports(referenceModule); if ( !oldUsedExports || !isContained(oldUsedExports, importedNames) @@ -119,7 +119,7 @@ class FlagDependencyUsagePlugin { }; for (const module of modules) { - module.setUsedExports(moduleGraph, false); + moduleGraph.setUsedExports(module, false); } /** @type {[Module, DependenciesBlock, UsedExports][]} */ diff --git a/lib/FlagInitialModulesAsUsedPlugin.js b/lib/FlagInitialModulesAsUsedPlugin.js index dca0f105d..c2c659617 100644 --- a/lib/FlagInitialModulesAsUsedPlugin.js +++ b/lib/FlagInitialModulesAsUsedPlugin.js @@ -30,7 +30,7 @@ class FlagInitialModulesAsUsedPlugin { return; } for (const module of chunkGraph.getChunkModulesIterable(chunk)) { - module.setUsedExports(moduleGraph, true); + moduleGraph.setUsedExports(module, true); moduleGraph.addExtraReason(module, this.explanation); } } diff --git a/lib/FunctionModuleTemplatePlugin.js b/lib/FunctionModuleTemplatePlugin.js index 23976edba..66585a33b 100644 --- a/lib/FunctionModuleTemplatePlugin.js +++ b/lib/FunctionModuleTemplatePlugin.js @@ -75,7 +75,7 @@ class FunctionModuleTemplatePlugin { } else if (module.buildMeta.providedExports) { source.add(Template.toComment("no static exports found") + "\n"); } - const usedExports = module.getUsedExports(moduleGraph); + const usedExports = moduleGraph.getUsedExports(module); if (usedExports === true) { source.add(Template.toComment("all exports used") + "\n"); } else if (usedExports === false) { @@ -91,8 +91,8 @@ class FunctionModuleTemplatePlugin { ); } } - const optimizationBailout = module.getOptimizationBailout( - this.moduleGraph + const optimizationBailout = this.moduleGraph.getOptimizationBailout( + module ); if (optimizationBailout) { for (const text of optimizationBailout) { diff --git a/lib/HotModuleReplacementPlugin.js b/lib/HotModuleReplacementPlugin.js index b8d411334..d0171d167 100644 --- a/lib/HotModuleReplacementPlugin.js +++ b/lib/HotModuleReplacementPlugin.js @@ -175,6 +175,7 @@ module.exports = class HotModuleReplacementPlugin { compiler.hooks.compilation.tap( "HotModuleReplacementPlugin", (compilation, { normalModuleFactory }) => { + const moduleGraph = compilation.moduleGraph; const hotUpdateChunkTemplate = compilation.hotUpdateChunkTemplate; if (!hotUpdateChunkTemplate) return; @@ -222,7 +223,7 @@ module.exports = class HotModuleReplacementPlugin { records.chunkModuleIds[chunk.id] = Array.from( chunkGraph.getOrderedChunkModulesIterable( chunk, - compareModulesById + compareModulesById(moduleGraph) ), m => m.id ); diff --git a/lib/JavascriptModulesPlugin.js b/lib/JavascriptModulesPlugin.js index 76230a964..a93ce8a94 100644 --- a/lib/JavascriptModulesPlugin.js +++ b/lib/JavascriptModulesPlugin.js @@ -48,6 +48,7 @@ class JavascriptModulesPlugin { compiler.hooks.compilation.tap( "JavascriptModulesPlugin", (compilation, { normalModuleFactory }) => { + const moduleGraph = compilation.moduleGraph; const hooks = JavascriptModulesPlugin.getHooks(compilation); hooks.shouldRender.tap("JavascriptModulesPlugin", module => { if (module.type === "javascript/auto") return true; @@ -194,7 +195,7 @@ class JavascriptModulesPlugin { template.updateHashForChunk(hash, chunk); for (const m of chunkGraph.getOrderedChunkModulesIterable( chunk, - compareModulesById + compareModulesById(moduleGraph) )) { if (typeof m.source === "function") { hash.update(m.hash); diff --git a/lib/LibManifestPlugin.js b/lib/LibManifestPlugin.js index 9018ade2c..fd899c6b8 100644 --- a/lib/LibManifestPlugin.js +++ b/lib/LibManifestPlugin.js @@ -19,6 +19,7 @@ class LibManifestPlugin { compiler.hooks.emit.tapAsync( "LibManifestPlugin", (compilation, callback) => { + const moduleGraph = compilation.moduleGraph; asyncLib.forEach( compilation.chunks, (chunk, callback) => { @@ -43,7 +44,7 @@ class LibManifestPlugin { content: Array.from( chunkGraph.getOrderedChunkModulesIterable( chunk, - compareModulesById + compareModulesById(moduleGraph) ), module => { if ( diff --git a/lib/Module.js b/lib/Module.js index fed565d43..25dca9600 100644 --- a/lib/Module.js +++ b/lib/Module.js @@ -5,17 +5,18 @@ "use strict"; +const ChunkGraph = require("./ChunkGraph"); const DependenciesBlock = require("./DependenciesBlock"); +const ModuleGraph = require("./ModuleGraph"); const Template = require("./Template"); +const { compareChunksById } = require("./util/comparators"); /** @typedef {import("webpack-sources").Source} Source */ /** @typedef {import("./Chunk")} Chunk */ -/** @typedef {import("./ChunkGraph")} ChunkGraph */ /** @typedef {import("./ChunkGroup")} ChunkGroup */ /** @typedef {import("./Compilation")} Compilation */ /** @typedef {import("./Dependency")} Dependency */ /** @typedef {import("./DependencyTemplates")} DependencyTemplates */ -/** @typedef {import("./ModuleGraph")} ModuleGraph */ /** @typedef {import("./RequestShortener")} RequestShortener */ /** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */ /** @typedef {import("./WebpackError")} WebpackError */ @@ -45,9 +46,6 @@ const Template = require("./Template"); /** @typedef {KnownBuildMeta & Record} BuildMeta */ const EMPTY_RESOLVE_OPTIONS = {}; -const optimizationBailoutSymbol = Symbol("optimization bailout"); -const usedExportsSymbol = Symbol("used exports"); -const issuerSymbol = Symbol("issuer"); let debugId = 1000; @@ -106,10 +104,6 @@ class Module extends DependenciesBlock { /** @type {number|string} */ this.id = null; /** @type {number} */ - this.index = null; - /** @type {number} */ - this.index2 = null; - /** @type {number} */ this.depth = null; /** @type {undefined | object} */ this.profile = undefined; @@ -122,6 +116,126 @@ class Module extends DependenciesBlock { this.useSourceMap = false; } + // TODO remove in webpack 6 + // BACKWARD-COMPAT START + get index() { + return ModuleGraph.getModuleGraphForModule( + this, + "Module.index" + ).getPreOrderIndex(this); + } + + set index(value) { + ModuleGraph.getModuleGraphForModule(this, "Module.index").setPreOrderIndex( + this, + value + ); + } + + get index2() { + return ModuleGraph.getModuleGraphForModule( + this, + "Module.index2" + ).getPostOrderIndex(this); + } + + set index2(value) { + ModuleGraph.getModuleGraphForModule( + this, + "Module.index2" + ).setPostOrderIndex(this, value); + } + + get issuer() { + return ModuleGraph.getModuleGraphForModule(this, "Module.issuer").getIssuer( + this + ); + } + + set issuer(value) { + ModuleGraph.getModuleGraphForModule(this, "Module.issuer").setIssuer( + this, + value + ); + } + + get usedExports() { + return ModuleGraph.getModuleGraphForModule( + this, + "Module.usedExports" + ).getUsedExports(this); + } + + set usedExports(value) { + ModuleGraph.getModuleGraphForModule( + this, + "Module.usedExports" + ).setUsedExports(this, value); + } + + get optimizationBailout() { + return ModuleGraph.getModuleGraphForModule( + this, + "Module.optimizationBailout" + ).getOptimizationBailout(this); + } + + get optional() { + return this.isOptional( + ModuleGraph.getModuleGraphForModule(this, "Module.optional") + ); + } + + addChunk(chunk) { + return ChunkGraph.getChunkGraphForModule( + this, + "Module.addChunk" + ).connectChunkAndModule(chunk, this); + } + + removeChunk(chunk) { + return ChunkGraph.getChunkGraphForModule( + this, + "Module.removeChunk" + ).disconnectChunkAndModule(chunk, this); + } + + isInChunk(chunk) { + return ChunkGraph.getChunkGraphForModule( + this, + "Module.isInChunk" + ).isModuleInChunk(this, chunk); + } + + isEntryModule() { + return ChunkGraph.getChunkGraphForModule( + this, + "Module.isEntryModule" + ).isEntryModule(this); + } + + getChunks() { + return ChunkGraph.getChunkGraphForModule( + this, + "Module.getChunks" + ).getModuleChunks(this); + } + + getNumberOfChunks() { + return ChunkGraph.getChunkGraphForModule( + this, + "Module.getNumberOfChunks" + ).getNumberOfModuleChunks(this); + } + + get chunksIterable() { + return ChunkGraph.getChunkGraphForModule( + this, + "Module.chunksIterable" + ).getOrderedModuleChunksIterable(this, compareChunksById); + } + // BACKWARD-COMPAT END + /** * @deprecated moved to .buildInfo.exportsArgument * @returns {string} name of the exports argument @@ -138,61 +252,6 @@ class Module extends DependenciesBlock { return (this.buildInfo && this.buildInfo.moduleArgument) || "module"; } - /** - * @param {ModuleGraph} moduleGraph the module graph - * @returns {Module | null} the issuer module - */ - getIssuer(moduleGraph) { - const meta = moduleGraph.getMeta(this); - return meta[issuerSymbol]; - } - - /** - * @param {ModuleGraph} moduleGraph the module graph - * @param {Module | null} issuer the issuer module - * @returns {void} - */ - setIssuer(moduleGraph, issuer) { - const meta = moduleGraph.getMeta(this); - meta[issuerSymbol] = issuer; - } - - /** - * @param {ModuleGraph} moduleGraph the module graph - * @returns {(string | OptimizationBailoutFunction)[]} optimization bailouts - */ - getOptimizationBailout(moduleGraph) { - const meta = moduleGraph.getMeta(this); - const list = meta[optimizationBailoutSymbol]; - if (list === undefined) { - return (meta[optimizationBailoutSymbol] = []); - } - return list; - } - - /** - * @param {ModuleGraph} moduleGraph the module graph - * @returns {false | true | SortableSet | null} the used exports - * false: module is not used at all. - * true: the module namespace/object export is used. - * SortableSet: these export names are used. - * empty SortableSet: module is used but no export. - * null: unknown, worst case should be assumed. - */ - getUsedExports(moduleGraph) { - const value = moduleGraph.getMeta(this)[usedExportsSymbol]; - return value === undefined ? null : value; - } - - /** - * @param {ModuleGraph} moduleGraph the module graph - * @param {false | true | SortableSet} usedExports the used exports - * @returns {void} - */ - setUsedExports(moduleGraph, usedExports) { - moduleGraph.getMeta(this)[usedExportsSymbol] = usedExports; - } - /** * disconnect the module from the graph * @returns {void} @@ -202,8 +261,6 @@ class Module extends DependenciesBlock { this.renderedHash = undefined; this.id = null; - this.index = null; - this.index2 = null; this.depth = null; this.profile = undefined; this.prefetched = false; @@ -217,8 +274,6 @@ class Module extends DependenciesBlock { */ unseal() { this.id = null; - this.index = null; - this.index2 = null; this.depth = null; super.unseal(); } @@ -309,7 +364,7 @@ class Module extends DependenciesBlock { * @returns {boolean} true, if the module is used */ isModuleUsed(moduleGraph) { - return this.getUsedExports(moduleGraph) !== false; + return moduleGraph.getUsedExports(this) !== false; } /** @@ -318,7 +373,7 @@ class Module extends DependenciesBlock { * @returns {boolean} true, if the export is used */ isExportUsed(moduleGraph, exportName) { - const usedExports = this.getUsedExports(moduleGraph); + const usedExports = moduleGraph.getUsedExports(this); if (usedExports === null || usedExports === true) return true; if (usedExports === false) return false; return usedExports.has(exportName); @@ -331,7 +386,7 @@ class Module extends DependenciesBlock { * string, the mangled export name when used. */ getUsedName(moduleGraph, exportName) { - const usedExports = this.getUsedExports(moduleGraph); + const usedExports = moduleGraph.getUsedExports(this); if (usedExports === null || usedExports === true) return exportName; if (usedExports === false) return false; if (!usedExports.has(exportName)) return false; @@ -391,7 +446,7 @@ class Module extends DependenciesBlock { */ updateHash(hash, compilation) { hash.update(`${this.id}`); - const usedExports = this.getUsedExports(compilation.moduleGraph); + const usedExports = compilation.moduleGraph.getUsedExports(this); if (typeof usedExports === "boolean") { hash.update(JSON.stringify(usedExports)); } else if (!usedExports) { @@ -522,22 +577,26 @@ Object.defineProperty(Module.prototype, "isUsed", { Object.defineProperty(Module.prototype, "used", { get() { - throw new Error("Module.used was refactored (use getUsedExports instead)"); + throw new Error( + "Module.used was refactored (use ModuleGraph.getUsedExports instead)" + ); }, set(value) { - throw new Error("Module.used was refactored (use setUsedExports instead)"); + throw new Error( + "Module.used was refactored (use ModuleGraph.setUsedExports instead)" + ); } }); Object.defineProperty(Module.prototype, "usedExports", { get() { throw new Error( - "Module.usedExports was refactored (use getUsedExports instead)" + "Module.usedExports was refactored (use ModuleGraph.getUsedExports instead)" ); }, set(value) { throw new Error( - "Module.usedExports was refactored (use setUsedExports instead)" + "Module.usedExports was refactored (use ModuleGraph.setUsedExports instead)" ); } }); diff --git a/lib/ModuleGraph.js b/lib/ModuleGraph.js index 7a94b7657..63d757184 100644 --- a/lib/ModuleGraph.js +++ b/lib/ModuleGraph.js @@ -5,42 +5,83 @@ "use strict"; +const util = require("util"); const ModuleGraphConnection = require("./ModuleGraphConnection"); /** @typedef {import("./DependenciesBlock")} DependenciesBlock */ /** @typedef {import("./Dependency")} Dependency */ /** @typedef {import("./Module")} Module */ +/** @typedef {import("./RequestShortener")} RequestShortener */ +/** @template T @typedef {import("./util/SortableSet")} SortableSet */ + +/** @typedef {(requestShortener: RequestShortener) => string} OptimizationBailoutFunction */ + +class ModuleGraphModule { + constructor() { + /** @type {Set} */ + this.incomingConnections = new Set(); + /** @type {Set} */ + this.outgoingConnections = new Set(); + /** @type {Module | null} */ + this.issuer = null; + /** @type {(string | OptimizationBailoutFunction)[]} */ + this.optimizationBailout = []; + /** @type {false | true | SortableSet | null} */ + this.usedExports = null; + /** @type {number} */ + this.preOrderIndex = null; + /** @type {number} */ + this.postOrderIndex = null; + } +} + +class ModuleGraphDependency { + constructor() { + /** @type {ModuleGraphConnection} */ + this.connection = undefined; + /** @type {Module} */ + this.parentModule = undefined; + /** @type {DependenciesBlock} */ + this.parentBlock = undefined; + } +} class ModuleGraph { constructor() { - /** @type {Map} */ + /** @type {Map} */ this._dependencyMap = new Map(); - /** @type {Map>} */ + /** @type {Map} */ this._moduleMap = new Map(); /** @type {Map>} */ this._originMap = new Map(); /** @type {Map} */ this._metaMap = new Map(); - /** @type {Map} */ - this._parentsMap = new Map(); } - _getModuleSet(module) { - let connections = this._moduleMap.get(module); - if (connections === undefined) { - connections = new Set(); - this._moduleMap.set(module, connections); + /** + * @param {Module} module the module + * @returns {ModuleGraphModule} the internal module + */ + _getModuleGraphModule(module) { + let mgm = this._moduleMap.get(module); + if (mgm === undefined) { + mgm = new ModuleGraphModule(); + this._moduleMap.set(module, mgm); } - return connections; + return mgm; } - _getOriginSet(module) { - let connections = this._originMap.get(module); - if (connections === undefined) { - connections = new Set(); - this._originMap.set(module, connections); + /** + * @param {Dependency} dependency the dependency + * @returns {ModuleGraphDependency} the internal dependency + */ + _getModuleGraphDependency(dependency) { + let mgd = this._dependencyMap.get(dependency); + if (mgd === undefined) { + mgd = new ModuleGraphDependency(); + this._dependencyMap.set(dependency, mgd); } - return connections; + return mgd; } /** @@ -50,7 +91,9 @@ class ModuleGraph { * @returns {void} */ setParents(dependency, block, module) { - this._parentsMap.set(dependency, { module, block }); + const mgd = this._getModuleGraphDependency(dependency); + mgd.parentBlock = block; + mgd.parentModule = module; } /** @@ -58,8 +101,8 @@ class ModuleGraph { * @returns {Module} parent module */ getParentModule(dependency) { - const entry = this._parentsMap.get(dependency); - return entry !== undefined ? entry.module : null; + const mgd = this._getModuleGraphDependency(dependency); + return mgd.parentModule; } /** @@ -67,8 +110,8 @@ class ModuleGraph { * @returns {DependenciesBlock} parent block */ getParentBlock(dependency) { - const entry = this._parentsMap.get(dependency); - return entry !== undefined ? entry.block : null; + const mgd = this._getModuleGraphDependency(dependency); + return mgd.parentBlock; } /** @@ -83,10 +126,12 @@ class ModuleGraph { dependency, module ); - this._dependencyMap.set(dependency, connection); - const connections = this._getModuleSet(module); + const mgd = this._getModuleGraphDependency(dependency); + mgd.connection = connection; + const connections = this._getModuleGraphModule(module).incomingConnections; connections.add(connection); - const originConnections = this._getOriginSet(originModule); + const originConnections = this._getModuleGraphModule(originModule) + .outgoingConnections; originConnections.add(connection); } @@ -96,13 +141,13 @@ class ModuleGraph { * @returns {void} */ updateModule(dependency, module) { - const connection = this._dependencyMap.get(dependency); + const { connection } = this._getModuleGraphDependency(dependency); if (connection.module === module) return; - const oldSet = this._moduleMap.get(connection.module); - oldSet.delete(connection); + const oldMgm = this._getModuleGraphModule(connection.module); + oldMgm.incomingConnections.delete(connection); connection.module = module; - const newSet = this._moduleMap.get(module); - newSet.add(connection); + const newMgm = this._getModuleGraphModule(module); + newMgm.incomingConnections.add(connection); } /** @@ -111,20 +156,57 @@ class ModuleGraph { * @returns {void} */ addExplanation(dependency, explanation) { - const connection = this._dependencyMap.get(dependency); + const { connection } = this._getModuleGraphDependency(dependency); connection.addExplanation(explanation); } + /** + * @param {Module} oldModule the old module + * @param {Module} newModule the new module + * @returns {void} + */ + moveModuleAttributes(oldModule, newModule) { + const oldMgm = this._getModuleGraphModule(oldModule); + const newMgm = this._getModuleGraphModule(newModule); + newMgm.postOrderIndex = oldMgm.postOrderIndex; + newMgm.preOrderIndex = oldMgm.preOrderIndex; + oldMgm.postOrderIndex = null; + oldMgm.preOrderIndex = null; + } + + /** + * @param {Module} module the module + * @returns {void} + */ + removeModuleAttributes(module) { + const mgm = this._getModuleGraphModule(module); + mgm.postOrderIndex = null; + mgm.preOrderIndex = null; + } + + /** + * @returns {void} + */ + removeAllModuleAttributes() { + for (const mgm of this._moduleMap.values()) { + mgm.postOrderIndex = null; + mgm.preOrderIndex = null; + } + } + /** * @param {Module} oldModule the old referencing module * @param {Module} newModule the new referencing module * @param {function(ModuleGraphConnection): boolean} filterConnection filter predicate for replacement * @returns {void} */ - replaceModule(oldModule, newModule, filterConnection) { + moveModuleConnections(oldModule, newModule, filterConnection) { if (oldModule === newModule) return; - const oldConnections = this._getOriginSet(oldModule); - const newConnections = this._getOriginSet(newModule); + const oldMgm = this._getModuleGraphModule(oldModule); + const newMgm = this._getModuleGraphModule(newModule); + // Outgoing connections + const oldConnections = oldMgm.outgoingConnections; + const newConnections = newMgm.outgoingConnections; for (const connection of oldConnections) { if (filterConnection(connection)) { connection.originModule = newModule; @@ -132,8 +214,9 @@ class ModuleGraph { oldConnections.delete(connection); } } - const oldConnections2 = this._getModuleSet(oldModule); - const newConnections2 = this._getModuleSet(newModule); + // Incoming connections + const oldConnections2 = oldMgm.incomingConnections; + const newConnections2 = newMgm.incomingConnections; for (const connection of oldConnections2) { if (filterConnection(connection)) { connection.module = newModule; @@ -149,7 +232,7 @@ class ModuleGraph { * @returns {void} */ addExtraReason(module, explanation) { - const connections = this._getModuleSet(module); + const connections = this._getModuleGraphModule(module).incomingConnections; connections.add(new ModuleGraphConnection(null, null, module, explanation)); } @@ -158,7 +241,7 @@ class ModuleGraph { * @returns {Module} the referenced module */ getResolvedModule(dependency) { - const connection = this._dependencyMap.get(dependency); + const { connection } = this._getModuleGraphDependency(dependency); return connection !== undefined ? connection.resolvedModule : null; } @@ -167,7 +250,7 @@ class ModuleGraph { * @returns {ModuleGraphConnection | undefined} the connection */ getConnection(dependency) { - const connection = this._dependencyMap.get(dependency); + const { connection } = this._getModuleGraphDependency(dependency); return connection; } @@ -176,7 +259,7 @@ class ModuleGraph { * @returns {Module} the referenced module */ getModule(dependency) { - const connection = this._dependencyMap.get(dependency); + const { connection } = this._getModuleGraphDependency(dependency); return connection !== undefined ? connection.module : null; } @@ -185,7 +268,7 @@ class ModuleGraph { * @returns {Module} the referencing module */ getOrigin(dependency) { - const connection = this._dependencyMap.get(dependency); + const { connection } = this._getModuleGraphDependency(dependency); return connection !== undefined ? connection.originModule : null; } @@ -194,7 +277,7 @@ class ModuleGraph { * @returns {Module} the original referencing module */ getResolvedOrigin(dependency) { - const connection = this._dependencyMap.get(dependency); + const { connection } = this._getModuleGraphDependency(dependency); return connection !== undefined ? connection.resolvedOriginModule : null; } @@ -203,7 +286,7 @@ class ModuleGraph { * @returns {ModuleGraphConnection[]} reasons why a module is included */ getIncomingConnections(module) { - const connections = this._getModuleSet(module); + const connections = this._getModuleGraphModule(module).incomingConnections; return Array.from(connections); } @@ -212,10 +295,128 @@ class ModuleGraph { * @returns {ModuleGraphConnection[]} list of outgoing connections */ getOutgoingConnection(module) { - const connections = this._getOriginSet(module); + const connections = this._getModuleGraphModule(module).outgoingConnections; return Array.from(connections); } + /** + * @param {Module} module the module + * @returns {Module | null} the issuer module + */ + getIssuer(module) { + const mgm = this._getModuleGraphModule(module); + return mgm.issuer; + } + + /** + * @param {Module} module the module + * @param {Module | null} issuer the issuer module + * @returns {void} + */ + setIssuer(module, issuer) { + const mgm = this._getModuleGraphModule(module); + mgm.issuer = issuer; + } + + /** + * @param {Module} module the module + * @returns {(string | OptimizationBailoutFunction)[]} optimization bailouts + */ + getOptimizationBailout(module) { + const mgm = this._getModuleGraphModule(module); + return mgm.optimizationBailout; + } + + /** + * @param {Module} module the module + * @returns {false | true | SortableSet | null} the used exports + * false: module is not used at all. + * true: the module namespace/object export is used. + * SortableSet: these export names are used. + * empty SortableSet: module is used but no export. + * null: unknown, worst case should be assumed. + */ + getUsedExports(module) { + const mgm = this._getModuleGraphModule(module); + return mgm.usedExports; + } + + /** + * @param {Module} module the module + * @param {false | true | SortableSet} usedExports the used exports + * @returns {void} + */ + setUsedExports(module, usedExports) { + const mgm = this._getModuleGraphModule(module); + mgm.usedExports = usedExports; + } + + /** + * @param {Module} module the module + * @returns {number} the index of the module + */ + getPreOrderIndex(module) { + const mgm = this._getModuleGraphModule(module); + return mgm.preOrderIndex; + } + + /** + * @param {Module} module the module + * @returns {number} the index of the module + */ + getPostOrderIndex(module) { + const mgm = this._getModuleGraphModule(module); + return mgm.postOrderIndex; + } + + /** + * @param {Module} module the module + * @param {number} index the index of the module + * @returns {void} + */ + setPreOrderIndex(module, index) { + const mgm = this._getModuleGraphModule(module); + mgm.preOrderIndex = index; + } + + /** + * @param {Module} module the module + * @param {number} index the index of the module + * @returns {boolean} true, if the index was set + */ + setPreOrderIndexIfUnset(module, index) { + const mgm = this._getModuleGraphModule(module); + if (mgm.preOrderIndex === null) { + mgm.preOrderIndex = index; + return true; + } + return false; + } + + /** + * @param {Module} module the module + * @param {number} index the index of the module + * @returns {void} + */ + setPostOrderIndex(module, index) { + const mgm = this._getModuleGraphModule(module); + mgm.postOrderIndex = index; + } + + /** + * @param {Module} module the module + * @param {number} index the index of the module + * @returns {boolean} true, if the index was set + */ + setPostOrderIndexIfUnset(module, index) { + const mgm = this._getModuleGraphModule(module); + if (mgm.postOrderIndex === null) { + mgm.postOrderIndex = index; + return true; + } + return false; + } + /** * @param {any} thing any thing * @returns {Object} metadata @@ -228,7 +429,54 @@ class ModuleGraph { } return meta; } + + // TODO remove in webpack 6 + /** + * @param {Module} module the module + * @param {string} deprecateMessage message for the deprecation message + * @returns {ModuleGraph} the module graph + */ + static getModuleGraphForModule(module, deprecateMessage) { + const fn = deprecateMap.get(deprecateMessage); + if (fn) return fn(module); + const newFn = util.deprecate( + /** + * @param {Module} module the module + * @returns {ModuleGraph} the module graph + */ + module => { + const moduleGraph = moduleGraphForModuleMap.get(module); + if (!moduleGraph) + throw new Error( + deprecateMessage + + "There was no ModuleGraph assigned to the Module for backward-compat (Use the new API)" + ); + return moduleGraph; + }, + deprecateMessage + ": Use new ModuleGraph API" + ); + deprecateMap.set(deprecateMessage, newFn); + return newFn(module); + } + + // TODO remove in webpack 6 + /** + * @param {Module} module the module + * @param {ModuleGraph} moduleGraph the module graph + * @returns {void} + */ + static setModuleGraphForModule(module, moduleGraph) { + moduleGraphForModuleMap.set(module, moduleGraph); + } } +// TODO remove in webpack 6 +/** @type {WeakMap} */ +const moduleGraphForModuleMap = new WeakMap(); + +// TODO remove in webpack 6 +/** @type {Map ModuleGraph>} */ +const deprecateMap = new Map(); + module.exports = ModuleGraph; module.exports.ModuleGraphConnection = ModuleGraphConnection; diff --git a/lib/Stats.js b/lib/Stats.js index 976278944..4aa7d8bde 100644 --- a/lib/Stats.js +++ b/lib/Stats.js @@ -324,7 +324,7 @@ class Stats { if (showErrorDetails && e.missing) { text += e.missing.map(item => `\n[${item}]`).join(""); } - const origin = e.origin || (e.module && e.module.getIssuer(moduleGraph)); + const origin = e.origin || (e.module && moduleGraph.getIssuer(e.module)); if (showModuleTrace && origin) { text += `\n @ ${this.formatFilePath( origin.readableIdentifier(requestShortener) @@ -342,10 +342,10 @@ class Stats { text += ` ${locInfo}`; } } - let current = origin.getIssuer(moduleGraph); + let current = moduleGraph.getIssuer(origin); while (current) { text += `\n @ ${current.readableIdentifier(requestShortener)}`; - current = current.getIssuer(moduleGraph); + current = moduleGraph.getIssuer(current); } } return text; @@ -455,12 +455,12 @@ class Stats { for (const keyValuePair of groupMap) { const name = keyValuePair[0]; const cg = keyValuePair[1]; - const children = cg.getChildrenByOrders(chunkGraph); + const children = cg.getChildrenByOrders(moduleGraph, chunkGraph); obj[name] = { chunks: cg.chunks.map(c => c.id), assets: cg.chunks.reduce( (array, c) => array.concat(c.files || []), - [] + /** @type {string[]} */ ([]) ), children: Object.keys(children).reduce((obj, key) => { const groups = children[key]; @@ -469,11 +469,11 @@ class Stats { chunks: group.chunks.map(c => c.id), assets: group.chunks.reduce( (array, c) => array.concat(c.files || []), - [] + /** @type {string[]} */ ([]) ) })); return obj; - }, Object.create(null)), + }, /** @type {Record} */ Object.create(null)), childAssets: Object.keys(children).reduce((obj, key) => { const groups = children[key]; obj[key] = Array.from( @@ -484,7 +484,7 @@ class Stats { } } return set; - }, new Set()) + }, /** @type {Set} */ (new Set())) ); return obj; }, Object.create(null)) @@ -507,19 +507,21 @@ class Stats { const fnModule = (module, nested) => { const path = []; - const issuer = module.getIssuer(moduleGraph); + const issuer = moduleGraph.getIssuer(module); let current = issuer; while (current) { path.push(current); - current = current.getIssuer(moduleGraph); + current = moduleGraph.getIssuer(current); } path.reverse(); const obj = { id: module.id, identifier: module.identifier(), name: module.readableIdentifier(requestShortener), - index: module.index, - index2: module.index2, + index: moduleGraph.getPreOrderIndex(module), + preOrderIndex: moduleGraph.getPreOrderIndex(module), + index2: moduleGraph.getPostOrderIndex(module), + postOrderIndex: moduleGraph.getPostOrderIndex(module), size: module.size(), cacheable: module.buildInfo.cacheable, built: !!module.built, @@ -601,7 +603,7 @@ class Stats { }); } if (showUsedExports) { - const usedExports = module.getUsedExports(moduleGraph); + const usedExports = moduleGraph.getUsedExports(module); if (usedExports === null) { obj.usedExports = null; } else if (typeof usedExports === "boolean") { @@ -616,8 +618,8 @@ class Stats { : null; } if (showOptimizationBailout) { - obj.optimizationBailout = module - .getOptimizationBailout(moduleGraph) + obj.optimizationBailout = moduleGraph + .getOptimizationBailout(module) .map(item => { if (typeof item === "function") return item(requestShortener); return item; @@ -647,7 +649,10 @@ class Stats { const parents = new Set(); const children = new Set(); const siblings = new Set(); - const childIdByOrder = chunk.getChildIdsByOrders(chunkGraph); + const childIdByOrder = chunk.getChildIdsByOrders( + moduleGraph, + chunkGraph + ); for (const chunkGroup of chunk.groupsIterable) { for (const parentGroup of chunkGroup.parentsIterable) { for (const chunk of parentGroup.chunks) { diff --git a/lib/dependencies/HarmonyCompatibilityDependency.js b/lib/dependencies/HarmonyCompatibilityDependency.js index 80ba0846d..00567a647 100644 --- a/lib/dependencies/HarmonyCompatibilityDependency.js +++ b/lib/dependencies/HarmonyCompatibilityDependency.js @@ -36,7 +36,7 @@ HarmonyCompatibilityDependency.Template = class HarmonyExportDependencyTemplate * @returns {InitFragment[]|null} the init fragments */ getInitFragments(dependency, { module, runtimeTemplate, moduleGraph }) { - const usedExports = module.getUsedExports(moduleGraph); + const usedExports = moduleGraph.getUsedExports(module); if (usedExports === true || usedExports === null) { const content = runtimeTemplate.defineEsModuleFlagStatement({ exportsArgument: module.exportsArgument diff --git a/lib/dependencies/HarmonyExportImportedSpecifierDependency.js b/lib/dependencies/HarmonyExportImportedSpecifierDependency.js index 100a4c2f0..f1b876b23 100644 --- a/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +++ b/lib/dependencies/HarmonyExportImportedSpecifierDependency.js @@ -35,7 +35,7 @@ const getHashValue = (moduleGraph, importedModule) => { return ""; } - const usedExports = importedModule.getUsedExports(moduleGraph); + const usedExports = moduleGraph.getUsedExports(importedModule); const stringifiedUsedExports = JSON.stringify(usedExports); const stringifiedProvidedExports = JSON.stringify( importedModule.buildMeta.providedExports @@ -96,7 +96,7 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency { const id = this.id; const parentModule = moduleGraph.getParentModule(this); const used = parentModule.getUsedName(moduleGraph, name); - const usedExports = parentModule.getUsedExports(moduleGraph); + const usedExports = moduleGraph.getUsedExports(parentModule); const importedModule = moduleGraph.getModule(this); if (!importedModule) { @@ -495,7 +495,7 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS const activeFromOtherStarExports = dep._discoverActiveExportsFromOtherStartExports( moduleGraph ); - const usedExports = module.getUsedExports(moduleGraph); + const usedExports = moduleGraph.getUsedExports(module); if (usedExports && usedExports !== true) { // we know which exports are used diff --git a/lib/dependencies/HarmonyImportSpecifierDependency.js b/lib/dependencies/HarmonyImportSpecifierDependency.js index 50881cc5a..a43d6d876 100644 --- a/lib/dependencies/HarmonyImportSpecifierDependency.js +++ b/lib/dependencies/HarmonyImportSpecifierDependency.js @@ -17,6 +17,8 @@ const HarmonyImportDependency = require("./HarmonyImportDependency"); /** @typedef {import("../WebpackError")} WebpackError */ /** @typedef {import("../util/createHash").Hash} Hash */ +const idSymbol = Symbol("HarmonyImportSpecifierDependency.id"); + class HarmonyImportSpecifierDependency extends HarmonyImportDependency { constructor( request, @@ -48,7 +50,16 @@ class HarmonyImportSpecifierDependency extends HarmonyImportDependency { * @returns {string} the imported id */ getId(moduleGraph) { - return moduleGraph.getMeta(this).id || this.id; + return moduleGraph.getMeta(this)[idSymbol] || this.id; + } + + /** + * @param {ModuleGraph} moduleGraph the module graph + * @param {string} id the imported id + * @returns {void} + */ + setId(moduleGraph, id) { + moduleGraph.getMeta(this)[idSymbol] = id; } /** @@ -173,7 +184,7 @@ class HarmonyImportSpecifierDependency extends HarmonyImportDependency { "" ); if (importedModule) { - const usedExports = importedModule.getUsedExports(moduleGraph); + const usedExports = moduleGraph.getUsedExports(importedModule); const stringifyUsedExports = JSON.stringify(usedExports); hash.update(stringifyUsedExports); } diff --git a/lib/optimize/AggressiveSplittingPlugin.js b/lib/optimize/AggressiveSplittingPlugin.js index 171986ce6..e9e72c618 100644 --- a/lib/optimize/AggressiveSplittingPlugin.js +++ b/lib/optimize/AggressiveSplittingPlugin.js @@ -73,6 +73,7 @@ class AggressiveSplittingPlugin { compiler.hooks.thisCompilation.tap( "AggressiveSplittingPlugin", compilation => { + const moduleGraph = compilation.moduleGraph; let needAdditionalSeal = false; let newSplits; let fromAggressiveSplittingSet; @@ -191,6 +192,7 @@ class AggressiveSplittingPlugin { // for any chunk which isn't splitted yet, split it and create a new entry // start with the biggest chunk + const cmpFn = compareModulesById(moduleGraph); const sortedChunks = chunks.slice().sort((a, b) => { const diff1 = chunkGraph.getChunkModulesSize(b) - @@ -201,10 +203,10 @@ class AggressiveSplittingPlugin { chunkGraph.getNumberOfChunkModules(b); if (diff2) return diff2; const modulesA = Array.from( - chunkGraph.getOrderedChunkModulesIterable(a, compareModulesById) + chunkGraph.getOrderedChunkModulesIterable(a, cmpFn) ); const modulesB = Array.from( - chunkGraph.getOrderedChunkModulesIterable(b, compareModulesById) + chunkGraph.getOrderedChunkModulesIterable(b, cmpFn) ); const aI = modulesA[Symbol.iterator](); const bI = modulesB[Symbol.iterator](); diff --git a/lib/optimize/ChunkModuleIdRangePlugin.js b/lib/optimize/ChunkModuleIdRangePlugin.js index fd3c956b3..88b57f429 100644 --- a/lib/optimize/ChunkModuleIdRangePlugin.js +++ b/lib/optimize/ChunkModuleIdRangePlugin.js @@ -24,6 +24,7 @@ class ChunkModuleIdRangePlugin { apply(compiler) { const options = this.options; compiler.hooks.compilation.tap("ChunkModuleIdRangePlugin", compilation => { + const moduleGraph = compilation.moduleGraph; compilation.hooks.moduleIds.tap("ChunkModuleIdRangePlugin", modules => { const chunkGraph = compilation.chunkGraph; const chunk = compilation.chunks.find( @@ -42,10 +43,10 @@ class ChunkModuleIdRangePlugin { let cmpFn; switch (options.order) { case "index": - cmpFn = compareModulesByIndex; + cmpFn = compareModulesByIndex(moduleGraph); break; case "index2": - cmpFn = compareModulesByIndex2; + cmpFn = compareModulesByIndex2(moduleGraph); break; default: throw new Error( diff --git a/lib/optimize/ConcatenatedModule.js b/lib/optimize/ConcatenatedModule.js index afe5f4fab..0997d98e3 100644 --- a/lib/optimize/ConcatenatedModule.js +++ b/lib/optimize/ConcatenatedModule.js @@ -321,12 +321,10 @@ class ConcatenatedModule extends Module { this.factoryMeta = rootModule.factoryMeta; // Info from Compilation - this.index = rootModule.index; - this.index2 = rootModule.index2; this.depth = rootModule.depth; // Info from Optimization - this.setUsedExports(moduleGraph, rootModule.getUsedExports(moduleGraph)); + moduleGraph.setUsedExports(this, moduleGraph.getUsedExports(rootModule)); const modulesArray = Array.from(modules); @@ -1145,7 +1143,7 @@ class ConcatenatedModule extends Module { const result = new ConcatSource(); // add harmony compatibility flag (must be first because of possible circular dependencies) - const usedExports = this.rootModule.getUsedExports(moduleGraph); + const usedExports = moduleGraph.getUsedExports(this.rootModule); if (usedExports === true) { result.add( runtimeTemplate.defineEsModuleFlagStatement({ diff --git a/lib/optimize/ModuleConcatenationPlugin.js b/lib/optimize/ModuleConcatenationPlugin.js index f0bd9dd15..e597d05f9 100644 --- a/lib/optimize/ModuleConcatenationPlugin.js +++ b/lib/optimize/ModuleConcatenationPlugin.js @@ -5,6 +5,8 @@ "use strict"; +const ChunkGraph = require("../ChunkGraph"); +const ModuleGraph = require("../ModuleGraph"); const { STAGE_DEFAULT } = require("../OptimizationStages"); const HarmonyCompatibilityDependency = require("../dependencies/HarmonyCompatibilityDependency"); const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency"); @@ -58,8 +60,8 @@ class ModuleConcatenationPlugin { const setBailoutReason = (module, reason) => { setInnerBailoutReason(module, reason); - module - .getOptimizationBailout(moduleGraph) + moduleGraph + .getOptimizationBailout(module) .push( typeof reason === "function" ? rs => formatBailoutReason(reason(rs)) @@ -308,8 +310,8 @@ class ModuleConcatenationPlugin { } } else { for (const warning of currentConfiguration.getWarningsSorted()) { - currentRoot - .getOptimizationBailout(moduleGraph) + moduleGraph + .getOptimizationBailout(currentRoot) .push(formatBailoutWarning(warning[0], warning[1])); } } @@ -331,17 +333,22 @@ class ModuleConcatenationPlugin { modules, compilation ); + ChunkGraph.setChunkGraphForModule(newModule, chunkGraph); + ModuleGraph.setModuleGraphForModule(newModule, moduleGraph); for (const warning of concatConfiguration.getWarningsSorted()) { - newModule - .getOptimizationBailout(moduleGraph) + moduleGraph + .getOptimizationBailout(newModule) .push(formatBailoutWarning(warning[0], warning[1])); } + moduleGraph.moveModuleAttributes(rootModule, newModule); for (const m of modules) { usedModules.add(m); + // remove attributes from module + moduleGraph.removeModuleAttributes(m); // remove module from chunk chunkGraph.replaceModule(m, newModule); // replace module references with the concatenated module - moduleGraph.replaceModule(m, newModule, c => { + moduleGraph.moveModuleConnections(m, newModule, c => { return !( c.dependency instanceof HarmonyImportDependency && modules.has(c.originModule) && diff --git a/lib/optimize/NaturalChunkOrderPlugin.js b/lib/optimize/NaturalChunkOrderPlugin.js index 34fbf8d5d..8fc324ff5 100644 --- a/lib/optimize/NaturalChunkOrderPlugin.js +++ b/lib/optimize/NaturalChunkOrderPlugin.js @@ -16,16 +16,18 @@ class NaturalChunkOrderPlugin { */ apply(compiler) { compiler.hooks.compilation.tap("NaturalChunkOrderPlugin", compilation => { + const moduleGraph = compilation.moduleGraph; compilation.hooks.optimizeChunkOrder.tap( "NaturalChunkOrderPlugin", chunks => { const chunkGraph = compilation.chunkGraph; chunks.sort((chunkA, chunkB) => { + const cmpFn = compareModulesById(moduleGraph); const a = chunkGraph - .getOrderedChunkModulesIterable(chunkA, compareModulesById) + .getOrderedChunkModulesIterable(chunkA, cmpFn) [Symbol.iterator](); const b = chunkGraph - .getOrderedChunkModulesIterable(chunkB, compareModulesById) + .getOrderedChunkModulesIterable(chunkB, cmpFn) [Symbol.iterator](); // eslint-disable-next-line no-constant-condition while (true) { diff --git a/lib/optimize/SideEffectsFlagPlugin.js b/lib/optimize/SideEffectsFlagPlugin.js index 0547602fe..d29a593f3 100644 --- a/lib/optimize/SideEffectsFlagPlugin.js +++ b/lib/optimize/SideEffectsFlagPlugin.js @@ -123,7 +123,7 @@ class SideEffectsFlagPlugin { dep, "(skipped side-effect-free modules)" ); - moduleGraph.getMeta(dep).id = mapping.exportName; + dep.setId(moduleGraph, mapping.exportName); continue; } } diff --git a/lib/util/comparators.js b/lib/util/comparators.js index b0e10098b..23590724b 100644 --- a/lib/util/comparators.js +++ b/lib/util/comparators.js @@ -7,6 +7,35 @@ /** @typedef {import("../Chunk")} Chunk */ /** @typedef {import("../Module")} Module */ +/** @typedef {import("../ModuleGraph")} ModuleGraph */ + +/** @template T @typedef {function(T, T): -1|0|1} Comparator */ +/** @template TArg @template T @typedef {function(TArg, T, T): -1|0|1} RawParamizedComparator */ +/** @template TArg @template T @typedef {function(TArg): Comparator} ParamizedComparator */ + +/** + * @template T + * @param {RawParamizedComparator} fn comparator with argument + * @returns {ParamizedComparator} comparator + */ +const createCachedParamizedComparator = fn => { + /** @type {WeakMap>} */ + const map = new WeakMap(); + return arg => { + const cachedResult = map.get(arg); + if (cachedResult !== undefined) return cachedResult; + /** + * @param {T} a first item + * @param {T} b second item + * @returns {-1|0|1} compare result + */ + const result = (a, b) => { + return fn(arg, a, b); + }; + map.set(arg, result); + return result; + }; +}; /** * @param {Chunk} a chunk @@ -18,13 +47,18 @@ exports.compareChunksById = (a, b) => { }; /** + * @param {ModuleGraph} moduleGraph the module graph * @param {Module} a module * @param {Module} b module * @returns {-1|0|1} compare result */ -exports.compareModulesById = (a, b) => { +const compareModulesById = (moduleGraph, a, b) => { return compareIds(a.id, b.id); }; +/** @type {ParamizedComparator} */ +exports.compareModulesById = createCachedParamizedComparator( + compareModulesById +); /** * @param {number} a number @@ -41,22 +75,58 @@ const compareNumbers = (a, b) => { }; /** + * @param {ModuleGraph} moduleGraph the module graph * @param {Module} a module * @param {Module} b module * @returns {-1|0|1} compare result */ -exports.compareModulesByIndex = (a, b) => { - return compareNumbers(a.index, b.index); +const compareModulesByIndex = (moduleGraph, a, b) => { + return compareNumbers( + moduleGraph.getPreOrderIndex(a), + moduleGraph.getPreOrderIndex(b) + ); }; +/** @type {ParamizedComparator} */ +exports.compareModulesByIndex = createCachedParamizedComparator( + compareModulesByIndex +); /** + * @param {ModuleGraph} moduleGraph the module graph * @param {Module} a module * @param {Module} b module * @returns {-1|0|1} compare result */ -exports.compareModulesByIndex2 = (a, b) => { - return compareNumbers(a.index2, b.index2); +const compareModulesByIndex2 = (moduleGraph, a, b) => { + return compareNumbers( + moduleGraph.getPostOrderIndex(a), + moduleGraph.getPostOrderIndex(b) + ); }; +/** @type {ParamizedComparator} */ +exports.compareModulesByIndex2 = createCachedParamizedComparator( + compareModulesByIndex2 +); + +/** + * @param {ModuleGraph} moduleGraph the module graph + * @param {Module} a module + * @param {Module} b module + * @returns {-1|0|1} compare result + */ +const compareModulesByIndexOrIdentifier = (moduleGraph, a, b) => { + const cmp1 = compareNumbers( + moduleGraph.getPreOrderIndex(a), + moduleGraph.getPreOrderIndex(b) + ); + if (cmp1 !== 0) return cmp1; + const cmp2 = compareIds(a.identifier(), b.identifier()); + return cmp2; +}; +/** @type {ParamizedComparator} */ +exports.compareModulesByIndexOrIdentifier = createCachedParamizedComparator( + compareModulesByIndexOrIdentifier +); /** * @param {string|number} a first id diff --git a/lib/wasm/WasmMainTemplatePlugin.js b/lib/wasm/WasmMainTemplatePlugin.js index a3c68feb9..87caa73db 100644 --- a/lib/wasm/WasmMainTemplatePlugin.js +++ b/lib/wasm/WasmMainTemplatePlugin.js @@ -16,13 +16,13 @@ const WebAssemblyUtils = require("./WebAssemblyUtils"); /** @typedef {import("../ModuleGraph")} ModuleGraph */ // Get all wasm modules -const getAllWasmModules = (chunkGraph, chunk) => { +const getAllWasmModules = (moduleGraph, chunkGraph, chunk) => { const wasmModules = chunk.getAllAsyncChunks(); const array = []; for (const chunk of wasmModules) { for (const m of chunkGraph.getOrderedChunkModulesIterable( chunk, - compareModulesById + compareModulesById(moduleGraph) )) { if (m.type.startsWith("webassembly")) { array.push(m); @@ -188,10 +188,12 @@ class WasmMainTemplatePlugin { * @returns {void} */ apply(mainTemplate) { + const moduleGraph = this.compilation.moduleGraph; mainTemplate.hooks.localVars.tap( "WasmMainTemplatePlugin", (source, chunk) => { const wasmModules = getAllWasmModules( + this.compilation.moduleGraph, this.compilation.chunkGraph, chunk ); @@ -228,6 +230,7 @@ class WasmMainTemplatePlugin { mainTemplate.outputOptions.webassemblyModuleFilename; const chunkModuleMaps = this.compilation.chunkGraph.getChunkModuleMaps( + moduleGraph, chunk, m => m.type.startsWith("webassembly") ); @@ -378,11 +381,13 @@ class WasmMainTemplatePlugin { "WasmMainTemplatePlugin", (hash, chunk) => { const chunkModuleMaps = this.compilation.chunkGraph.getChunkModuleMaps( + moduleGraph, chunk, m => m.type.startsWith("webassembly") ); hash.update(JSON.stringify(chunkModuleMaps.id)); const wasmModules = getAllWasmModules( + this.compilation.moduleGraph, this.compilation.chunkGraph, chunk ); diff --git a/lib/wasm/WebAssemblyGenerator.js b/lib/wasm/WebAssemblyGenerator.js index cb23bd109..1066ae637 100644 --- a/lib/wasm/WebAssemblyGenerator.js +++ b/lib/wasm/WebAssemblyGenerator.js @@ -400,7 +400,7 @@ class WebAssemblyGenerator extends Generator { let bin = /** @type {ArrayBuffer} */ (sourceAsAny); bin = preprocess(bin); - const usedExports = module.getUsedExports(moduleGraph); + const usedExports = moduleGraph.getUsedExports(module); const initFuncId = t.identifier( usedExports && usedExports !== true ? Template.numberToIdentifer(usedExports.size) diff --git a/lib/wasm/WebAssemblyJavascriptGenerator.js b/lib/wasm/WebAssemblyJavascriptGenerator.js index 1e22d11b6..00b02b036 100644 --- a/lib/wasm/WebAssemblyJavascriptGenerator.js +++ b/lib/wasm/WebAssemblyJavascriptGenerator.js @@ -24,7 +24,7 @@ class WebAssemblyJavascriptGenerator extends Generator { * @returns {Source} generated code */ generate(module, { runtimeTemplate, moduleGraph }) { - const usedExports = module.getUsedExports(moduleGraph); + const usedExports = moduleGraph.getUsedExports(module); const initIdentifer = usedExports && usedExports !== true ? Template.numberToIdentifer(usedExports.size) diff --git a/lib/wasm/WebAssemblyModulesPlugin.js b/lib/wasm/WebAssemblyModulesPlugin.js index 841c639b4..2aeb2aadf 100644 --- a/lib/wasm/WebAssemblyModulesPlugin.js +++ b/lib/wasm/WebAssemblyModulesPlugin.js @@ -34,6 +34,7 @@ class WebAssemblyModulesPlugin { compiler.hooks.compilation.tap( "WebAssemblyModulesPlugin", (compilation, { normalModuleFactory }) => { + const moduleGraph = compilation.moduleGraph; compilation.dependencyFactories.set( WebAssemblyImportDependency, normalModuleFactory @@ -75,7 +76,7 @@ class WebAssemblyModulesPlugin { for (const module of chunkGraph.getOrderedChunkModulesIterable( chunk, - compareModulesById + compareModulesById(moduleGraph) )) { if (module.type && module.type.startsWith("webassembly")) { const filenameTemplate = diff --git a/lib/web/JsonpChunkTemplatePlugin.js b/lib/web/JsonpChunkTemplatePlugin.js index c80b4d487..d37880d0b 100644 --- a/lib/web/JsonpChunkTemplatePlugin.js +++ b/lib/web/JsonpChunkTemplatePlugin.js @@ -25,13 +25,17 @@ class JsonpChunkTemplatePlugin { * @returns {void} */ apply(chunkTemplate) { + const moduleGraph = this.compilation.moduleGraph; chunkTemplate.hooks.render.tap( "JsonpChunkTemplatePlugin", (modules, moduleTemplate, { chunk, chunkGraph }) => { const jsonpFunction = chunkTemplate.outputOptions.jsonpFunction; const globalObject = chunkTemplate.outputOptions.globalObject; const source = new ConcatSource(); - const prefetchChunks = chunk.getChildIdsByOrders(chunkGraph).prefetch; + const prefetchChunks = chunk.getChildIdsByOrders( + moduleGraph, + chunkGraph + ).prefetch; source.add( `(${globalObject}[${JSON.stringify( jsonpFunction @@ -66,7 +70,9 @@ class JsonpChunkTemplatePlugin { const chunkGraph = this.compilation.chunkGraph; hash.update(JSON.stringify(getEntryInfo(chunkGraph, chunk))); hash.update( - JSON.stringify(chunk.getChildIdsByOrders(chunkGraph).prefetch) || "" + JSON.stringify( + chunk.getChildIdsByOrders(moduleGraph, chunkGraph).prefetch + ) || "" ); } ); diff --git a/lib/web/JsonpMainTemplatePlugin.js b/lib/web/JsonpMainTemplatePlugin.js index 35df13ff7..7d357018e 100644 --- a/lib/web/JsonpMainTemplatePlugin.js +++ b/lib/web/JsonpMainTemplatePlugin.js @@ -46,6 +46,7 @@ class JsonpMainTemplatePlugin { * @returns {void} */ apply(mainTemplate) { + const moduleGraph = this.compilation.moduleGraph; const needChunkOnDemandLoadingCode = chunk => { for (const chunkGroup of chunk.groupsIterable) { if (chunkGroup.getNumberOfChildren() > 0) return true; @@ -67,6 +68,7 @@ class JsonpMainTemplatePlugin { }; const needPrefetchingCode = chunk => { const allPrefetchChunks = chunk.getChildIdsByOrdersMap( + moduleGraph, this.compilation.chunkGraph, true ).prefetch; @@ -331,6 +333,7 @@ class JsonpMainTemplatePlugin { }), (source, chunkIdExpression, { chunk, hash }) => { const chunkMap = chunk.getChildIdsByOrdersMap( + moduleGraph, this.compilation.chunkGraph ).preload; if (!chunkMap || Object.keys(chunkMap).length === 0) return source; @@ -514,7 +517,10 @@ class JsonpMainTemplatePlugin { "JsonpMainTemplatePlugin", (source, chunk, hash) => { const chunkGraph = this.compilation.chunkGraph; - const prefetchChunks = chunk.getChildIdsByOrders(chunkGraph).prefetch; + const prefetchChunks = chunk.getChildIdsByOrders( + moduleGraph, + chunkGraph + ).prefetch; if ( needChunkLoadingCode(chunk) && prefetchChunks && diff --git a/test/configCases/chunk-index/order-multiple-entries/webpack.config.js b/test/configCases/chunk-index/order-multiple-entries/webpack.config.js index fbac66a06..e3a3a5109 100644 --- a/test/configCases/chunk-index/order-multiple-entries/webpack.config.js +++ b/test/configCases/chunk-index/order-multiple-entries/webpack.config.js @@ -16,6 +16,7 @@ module.exports = { * @returns {void} */ const handler = compilation => { + const moduleGraph = compilation.moduleGraph; compilation.hooks.afterSeal.tap("testcase", () => { const data = {}; for (const [name, group] of compilation.namedChunkGroups) { @@ -69,20 +70,28 @@ module.exports = { }); const indicies = compilation.modules .slice() - .sort((a, b) => a.index - b.index) + .sort( + (a, b) => + moduleGraph.getPreOrderIndex(a) - + moduleGraph.getPreOrderIndex(b) + ) .map( m => - `${m.index}: ${m.readableIdentifier( + `${moduleGraph.getPreOrderIndex(m)}: ${m.readableIdentifier( compilation.requestShortener )}` ) .join(", "); const indicies2 = compilation.modules .slice() - .sort((a, b) => a.index2 - b.index2) + .sort( + (a, b) => + moduleGraph.getPostOrderIndex(a) - + moduleGraph.getPostOrderIndex(b) + ) .map( m => - `${m.index2}: ${m.readableIdentifier( + `${moduleGraph.getPostOrderIndex(m)}: ${m.readableIdentifier( compilation.requestShortener )}` ) diff --git a/test/statsCases/named-chunks-plugin-async/webpack.config.js b/test/statsCases/named-chunks-plugin-async/webpack.config.js index b3d2dc442..7799b1243 100644 --- a/test/statsCases/named-chunks-plugin-async/webpack.config.js +++ b/test/statsCases/named-chunks-plugin-async/webpack.config.js @@ -11,13 +11,16 @@ module.exports = { entry: "./entry" }, plugins: [ - new NamedChunksPlugin(function(chunk, { chunkGraph }) { + new NamedChunksPlugin(function(chunk, { chunkGraph, moduleGraph }) { if (chunk.name) { return chunk.name; } const chunkModulesToName = chunk => Array.from( - chunkGraph.getOrderedChunkModulesIterable(chunk, compareModulesById), + chunkGraph.getOrderedChunkModulesIterable( + chunk, + compareModulesById(moduleGraph) + ), mod => { const rs = new RequestShortener(mod.context); return rs.shorten(mod.request).replace(/[./\\]/g, "_");