Merge pull request #7926 from webpack/refactor/module-graph

Refactor/module graph
This commit is contained in:
Tobias Koppers 2018-08-21 19:12:02 +02:00 committed by GitHub
commit 8fff814298
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 966 additions and 237 deletions

View File

@ -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<string, Set<TODO>[]>} 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<string|number, Record<string, Set<TODO>[]>>} 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) {

View File

@ -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<Module, ChunkGraphModule>} */
this._modules = new WeakMap();
/** @private @type {WeakMap<Chunk, ChunkGraphChunk>} */
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<string|number, (string|number)[]>} */
const chunkModuleIdMap = Object.create(null);
/** @type {Record<string|number, string>} */
@ -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<Module, ChunkGraph>} */
const chunkGraphForModuleMap = new WeakMap();
// TODO remove in webpack 6
/** @type {WeakMap<Chunk, ChunkGraph>} */
const chunkGraphForChunkMap = new WeakMap();
// TODO remove in webpack 6
/** @type {Map<string, (module: Module) => ChunkGraph>} */
const deprecateGetChunkGraphForModuleMap = new Map();
// TODO remove in webpack 6
/** @type {Map<string, (chunk: Chunk) => ChunkGraph>} */
const deprecateGetChunkGraphForChunkMap = new Map();
module.exports = ChunkGraph;

View File

@ -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<string, ChunkGroup[]>} mapping from children type to ordered list of ChunkGroups
*/
getChildrenByOrders(moduleGraph, chunkGraph) {
/** @type {Map<string, {order: number, group: ChunkGroup}[]>} */
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<string, ChunkGroup[]>} */
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);
}

View File

@ -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<ChunkGroup, {block: AsyncDependenciesBlock, chunkGroup: ChunkGroup}[]>} */
const chunkDependencies = new Map();
/** @type {Set<ChunkGroup>} */
@ -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<DependenciesBlock, ChunkGroup>} */
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;

View File

@ -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][]} */

View File

@ -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);
}
}

View File

@ -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) {

View File

@ -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
);

View File

@ -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);

View File

@ -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 (

View File

@ -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<string, any>} 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<string> | null} the used exports
* false: module is not used at all.
* true: the module namespace/object export is used.
* SortableSet<string>: these export names are used.
* empty SortableSet<string>: 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<string>} 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)"
);
}
});

View File

@ -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")<T>} SortableSet<T> */
/** @typedef {(requestShortener: RequestShortener) => string} OptimizationBailoutFunction */
class ModuleGraphModule {
constructor() {
/** @type {Set<ModuleGraphConnection>} */
this.incomingConnections = new Set();
/** @type {Set<ModuleGraphConnection>} */
this.outgoingConnections = new Set();
/** @type {Module | null} */
this.issuer = null;
/** @type {(string | OptimizationBailoutFunction)[]} */
this.optimizationBailout = [];
/** @type {false | true | SortableSet<string> | 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<Dependency, ModuleGraphConnection>} */
/** @type {Map<Dependency, ModuleGraphDependency>} */
this._dependencyMap = new Map();
/** @type {Map<Module, Set<ModuleGraphConnection>>} */
/** @type {Map<Module, ModuleGraphModule>} */
this._moduleMap = new Map();
/** @type {Map<Module, Set<ModuleGraphConnection>>} */
this._originMap = new Map();
/** @type {Map<any, Object>} */
this._metaMap = new Map();
/** @type {Map<Dependency, {module: Module, block: DependenciesBlock}>} */
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<string> | null} the used exports
* false: module is not used at all.
* true: the module namespace/object export is used.
* SortableSet<string>: these export names are used.
* empty SortableSet<string>: 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<string>} 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<Module, ModuleGraph>} */
const moduleGraphForModuleMap = new WeakMap();
// TODO remove in webpack 6
/** @type {Map<string, (module: Module) => ModuleGraph>} */
const deprecateMap = new Map();
module.exports = ModuleGraph;
module.exports.ModuleGraphConnection = ModuleGraphConnection;

View File

@ -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<string, {name: string, chunks: (string|number)[], assets: string[]}[]>} */ 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<string>} */ (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) {

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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]();

View File

@ -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(

View File

@ -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({

View File

@ -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) &&

View File

@ -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) {

View File

@ -123,7 +123,7 @@ class SideEffectsFlagPlugin {
dep,
"(skipped side-effect-free modules)"
);
moduleGraph.getMeta(dep).id = mapping.exportName;
dep.setId(moduleGraph, mapping.exportName);
continue;
}
}

View File

@ -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<T>} ParamizedComparator */
/**
* @template T
* @param {RawParamizedComparator<any, T>} fn comparator with argument
* @returns {ParamizedComparator<any, T>} comparator
*/
const createCachedParamizedComparator = fn => {
/** @type {WeakMap<object, Comparator<T>>} */
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<ModuleGraph, Module>} */
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<ModuleGraph, Module>} */
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<ModuleGraph, Module>} */
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<ModuleGraph, Module>} */
exports.compareModulesByIndexOrIdentifier = createCachedParamizedComparator(
compareModulesByIndexOrIdentifier
);
/**
* @param {string|number} a first id

View File

@ -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
);

View File

@ -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)

View File

@ -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)

View File

@ -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 =

View File

@ -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
) || ""
);
}
);

View File

@ -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 &&

View File

@ -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
)}`
)

View File

@ -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, "_");