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"; "use strict";
const ChunkGraph = require("./ChunkGraph");
const Entrypoint = require("./Entrypoint"); const Entrypoint = require("./Entrypoint");
const { intersect } = require("./util/SetHelpers"); const { intersect } = require("./util/SetHelpers");
const SortableSet = require("./util/SortableSet"); const SortableSet = require("./util/SortableSet");
const { compareModulesById } = require("./util/comparators"); const { compareModulesById } = require("./util/comparators");
/** @typedef {import("webpack-sources").Source} Source */ /** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("./ChunkGraph")} ChunkGraph */
/** @typedef {import("./ChunkGroup")} ChunkGroup */ /** @typedef {import("./ChunkGroup")} ChunkGroup */
/** @typedef {import("./Compilation")} Compilation */
/** @typedef {import("./Module")} Module */ /** @typedef {import("./Module")} Module */
/** @typedef {import("./ModuleGraph")} ModuleGraph */
/** @typedef {import("./ModuleReason")} ModuleReason */ /** @typedef {import("./ModuleReason")} ModuleReason */
/** @typedef {import("./util/createHash").Hash} Hash */ /** @typedef {import("./util/createHash").Hash} Hash */
@ -87,6 +89,169 @@ class Chunk {
this.removedModules = undefined; 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 * @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 {Hash} hash hash (will be modified)
* @param {ChunkGraph} chunkGraph the chunk graph * @param {Compilation} compilation the compilation
* @returns {void} * @returns {void}
*/ */
updateHash(hash, chunkGraph) { updateHash(hash, compilation) {
hash.update(`${this.id} `); hash.update(`${this.id} `);
hash.update(this.ids ? this.ids.join(",") : ""); hash.update(this.ids ? this.ids.join(",") : "");
hash.update(`${this.name || ""} `); hash.update(`${this.name || ""} `);
for (const m of chunkGraph.getOrderedChunkModulesIterable( for (const m of compilation.chunkGraph.getOrderedChunkModulesIterable(
this, this,
compareModulesById compareModulesById(compilation.moduleGraph)
)) { )) {
hash.update(m.hash); hash.update(m.hash);
} }
const entryModules = chunkGraph.getChunkEntryModulesWithChunkGroupIterable( const entryModules = compilation.chunkGraph.getChunkEntryModulesWithChunkGroupIterable(
this this
); );
for (const [m, chunkGroup] of entryModules) { for (const [m, chunkGroup] of entryModules) {
@ -281,10 +446,11 @@ class Chunk {
} }
/** /**
* @param {ModuleGraph} moduleGraph the module graph
* @param {ChunkGraph} chunkGraph the chunk graph * @param {ChunkGraph} chunkGraph the chunk graph
* @returns {Record<string, Set<TODO>[]>} a record object of names to lists of child ids(?) * @returns {Record<string, Set<TODO>[]>} a record object of names to lists of child ids(?)
*/ */
getChildIdsByOrders(chunkGraph) { getChildIdsByOrders(moduleGraph, chunkGraph) {
const lists = new Map(); const lists = new Map();
for (const group of this.groupsIterable) { for (const group of this.groupsIterable) {
if (group.chunks[group.chunks.length - 1] === this) { if (group.chunks[group.chunks.length - 1] === this) {
@ -308,7 +474,7 @@ class Chunk {
list.sort((a, b) => { list.sort((a, b) => {
const cmp = b.order - a.order; const cmp = b.order - a.order;
if (cmp !== 0) return cmp; if (cmp !== 0) return cmp;
return a.group.compareTo(chunkGraph, b.group); return a.group.compareTo(moduleGraph, chunkGraph, b.group);
}); });
result[name] = Array.from( result[name] = Array.from(
list.reduce((set, item) => { list.reduce((set, item) => {
@ -322,11 +488,17 @@ class Chunk {
return result; 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 chunkMaps = Object.create(null);
const addChildIdsByOrdersToMap = chunk => { const addChildIdsByOrdersToMap = chunk => {
const data = chunk.getChildIdsByOrders(chunkGraph); const data = chunk.getChildIdsByOrders(moduleGraph, chunkGraph);
for (const key of Object.keys(data)) { for (const key of Object.keys(data)) {
let chunkMap = chunkMaps[key]; let chunkMap = chunkMaps[key];
if (chunkMap === undefined) { if (chunkMap === undefined) {

View File

@ -5,12 +5,14 @@
"use strict"; "use strict";
const util = require("util");
const SortableSet = require("./util/SortableSet"); const SortableSet = require("./util/SortableSet");
const { compareModulesById } = require("./util/comparators"); const { compareModulesById } = require("./util/comparators");
/** @typedef {import("./Chunk")} Chunk */ /** @typedef {import("./Chunk")} Chunk */
/** @typedef {import("./ChunkGroup")} ChunkGroup */ /** @typedef {import("./ChunkGroup")} ChunkGroup */
/** @typedef {import("./Module")} Module */ /** @typedef {import("./Module")} Module */
/** @typedef {import("./ModuleGraph")} ModuleGraph */
/** @typedef {(m: Module) => boolean} ModuleFilterPredicate */ /** @typedef {(m: Module) => boolean} ModuleFilterPredicate */
@ -85,11 +87,16 @@ class ChunkGraphChunk {
} }
class ChunkGraph { class ChunkGraph {
constructor() { /**
* @param {ModuleGraph} moduleGraph the module graph
*/
constructor(moduleGraph) {
/** @private @type {WeakMap<Module, ChunkGraphModule>} */ /** @private @type {WeakMap<Module, ChunkGraphModule>} */
this._modules = new WeakMap(); this._modules = new WeakMap();
/** @private @type {WeakMap<Chunk, ChunkGraphChunk>} */ /** @private @type {WeakMap<Chunk, ChunkGraphChunk>} */
this._chunks = new 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 {Chunk} chunk the chunk
* @param {ModuleFilterPredicate} filterFn function used to filter modules * @param {ModuleFilterPredicate} filterFn function used to filter modules
* @returns {ChunkModuleMaps} module map information * @returns {ChunkModuleMaps} module map information
*/ */
getChunkModuleMaps(chunk, filterFn) { getChunkModuleMaps(moduleGraph, chunk, filterFn) {
/** @type {Record<string|number, (string|number)[]>} */ /** @type {Record<string|number, (string|number)[]>} */
const chunkModuleIdMap = Object.create(null); const chunkModuleIdMap = Object.create(null);
/** @type {Record<string|number, string>} */ /** @type {Record<string|number, string>} */
@ -369,7 +377,7 @@ class ChunkGraph {
let array; let array;
for (const module of this.getOrderedChunkModulesIterable( for (const module of this.getOrderedChunkModulesIterable(
asyncChunk, asyncChunk,
compareModulesById compareModulesById(moduleGraph)
)) { )) {
if (filterFn(module)) { if (filterFn(module)) {
if (array === undefined) { if (array === undefined) {
@ -422,17 +430,19 @@ class ChunkGraph {
} }
/** /**
* @param {ModuleGraph} moduleGraph the module graph
* @param {Chunk} chunkA first chunk * @param {Chunk} chunkA first chunk
* @param {Chunk} chunkB second 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 * @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 cgcA = this._getChunkGraphChunk(chunkA);
const cgcB = this._getChunkGraphChunk(chunkB); const cgcB = this._getChunkGraphChunk(chunkB);
if (cgcA.modules.size > cgcB.modules.size) return -1; if (cgcA.modules.size > cgcB.modules.size) return -1;
if (cgcA.modules.size < cgcB.modules.size) return 1; if (cgcA.modules.size < cgcB.modules.size) return 1;
cgcA.modules.sortWith(compareModulesById); const cmpFn = compareModulesById(moduleGraph);
cgcB.modules.sortWith(compareModulesById); cgcA.modules.sortWith(cmpFn);
cgcB.modules.sortWith(cmpFn);
const a = cgcA.modules[Symbol.iterator](); const a = cgcA.modules[Symbol.iterator]();
const b = cgcB.modules[Symbol.iterator](); const b = cgcB.modules[Symbol.iterator]();
// eslint-disable-next-line no-constant-condition // eslint-disable-next-line no-constant-condition
@ -665,6 +675,100 @@ class ChunkGraph {
const cgc = this._getChunkGraphChunk(chunk); const cgc = this._getChunkGraphChunk(chunk);
return cgc.entryModules; 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; module.exports = ChunkGraph;

View File

@ -12,6 +12,7 @@ const SortableSet = require("./util/SortableSet");
/** @typedef {import("./ChunkGraph")} ChunkGraph */ /** @typedef {import("./ChunkGraph")} ChunkGraph */
/** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */ /** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */
/** @typedef {import("./Module")} Module */ /** @typedef {import("./Module")} Module */
/** @typedef {import("./ModuleGraph")} ModuleGraph */
/** @typedef {import("./ModuleReason")} ModuleReason */ /** @typedef {import("./ModuleReason")} ModuleReason */
/** @typedef {{id: number}} HasId */ /** @typedef {{id: number}} HasId */
@ -401,11 +402,12 @@ class ChunkGroup {
* Sorting predicate which allows current ChunkGroup to be compared against another. * Sorting predicate which allows current ChunkGroup to be compared against another.
* Sorting values are based off of number of chunks in ChunkGroup. * Sorting values are based off of number of chunks in ChunkGroup.
* *
* @param {ModuleGraph} moduleGraph the module graph
* @param {ChunkGraph} chunkGraph the chunk graph * @param {ChunkGraph} chunkGraph the chunk graph
* @param {ChunkGroup} otherGroup the chunkGroup to compare this against * @param {ChunkGroup} otherGroup the chunkGroup to compare this against
* @returns {-1|0|1} sort position for comparison * @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;
if (this.chunks.length < otherGroup.chunks.length) return 1; if (this.chunks.length < otherGroup.chunks.length) return 1;
const a = this.chunks[Symbol.iterator](); const a = this.chunks[Symbol.iterator]();
@ -415,12 +417,22 @@ class ChunkGroup {
const aItem = a.next(); const aItem = a.next();
const bItem = b.next(); const bItem = b.next();
if (aItem.done) return 0; 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; 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(); const lists = new Map();
for (const childGroup of this._children) { for (const childGroup of this._children) {
for (const key of Object.keys(childGroup.options)) { for (const key of Object.keys(childGroup.options)) {
@ -437,12 +449,13 @@ class ChunkGroup {
} }
} }
} }
/** @type {Record<string, ChunkGroup[]>} */
const result = Object.create(null); const result = Object.create(null);
for (const [name, list] of lists) { for (const [name, list] of lists) {
list.sort((a, b) => { list.sort((a, b) => {
const cmp = b.order - a.order; const cmp = b.order - a.order;
if (cmp !== 0) return cmp; 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); result[name] = list.map(i => i.group);
} }

View File

@ -40,6 +40,7 @@ const compareLocations = require("./compareLocations");
const Queue = require("./util/Queue"); const Queue = require("./util/Queue");
const Semaphore = require("./util/Semaphore"); const Semaphore = require("./util/Semaphore");
const SortableSet = require("./util/SortableSet"); const SortableSet = require("./util/SortableSet");
const { compareModulesByIndexOrIdentifier } = require("./util/comparators");
const createHash = require("./util/createHash"); const createHash = require("./util/createHash");
/** @typedef {import("webpack-sources").Source} Source */ /** @typedef {import("webpack-sources").Source} Source */
@ -131,21 +132,6 @@ const byIdOrIdentifier = (a, b) => {
return 0; 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} a first compilation to sort by
* @param {Compilation} b second compilation to sort by * @param {Compilation} b second compilation to sort by
@ -791,7 +777,7 @@ class Compilation {
dependentModule.profile = currentProfile; dependentModule.profile = currentProfile;
} }
dependentModule.setIssuer(this.moduleGraph, module); this.moduleGraph.setIssuer(dependentModule, module);
} else { } else {
if (this.profile) { if (this.profile) {
if (module.profile) { if (module.profile) {
@ -1070,6 +1056,7 @@ class Compilation {
for (const module of this.modules) { for (const module of this.modules) {
module.unseal(); module.unseal();
} }
this.moduleGraph.removeAllModuleAttributes();
} }
/** /**
@ -1077,9 +1064,13 @@ class Compilation {
* @returns {void} * @returns {void}
*/ */
seal(callback) { seal(callback) {
const chunkGraph = new ChunkGraph(); const chunkGraph = new ChunkGraph(this.moduleGraph);
this.chunkGraph = chunkGraph; this.chunkGraph = chunkGraph;
for (const module of this.modules) {
ChunkGraph.setChunkGraphForModule(module, chunkGraph);
}
this.hooks.seal.call(); this.hooks.seal.call();
while (this.hooks.optimizeDependencies.call(this.modules)) { 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 webpack 5: this should only be enabled when `moduleIds: "natural"`
// TODO move it into a plugin (NaturalModuleIdsPlugin) and use this in WebpackOptionsApply // TODO move it into a plugin (NaturalModuleIdsPlugin) and use this in WebpackOptionsApply
// TODO remove this method // TODO remove this method
modules.sort(byIndexOrIdentifier); modules.sort(compareModulesByIndexOrIdentifier(this.moduleGraph));
} }
/** /**
@ -1308,6 +1299,7 @@ class Compilation {
} }
const chunk = new Chunk(name); const chunk = new Chunk(name);
this.chunks.push(chunk); this.chunks.push(chunk);
ChunkGraph.setChunkGraphForChunk(chunk, this.chunkGraph);
if (name) { if (name) {
this.namedChunks.set(name, chunk); this.namedChunks.set(name, chunk);
} }
@ -1395,6 +1387,8 @@ class Compilation {
// eachother and Blocks with Chunks. It stops traversing when all modules // eachother and Blocks with Chunks. It stops traversing when all modules
// for a chunk are already available. So it doesn't connect unneeded chunks. // for a chunk are already available. So it doesn't connect unneeded chunks.
const moduleGraph = this.moduleGraph;
/** @type {Map<ChunkGroup, {block: AsyncDependenciesBlock, chunkGroup: ChunkGroup}[]>} */ /** @type {Map<ChunkGroup, {block: AsyncDependenciesBlock, chunkGroup: ChunkGroup}[]>} */
const chunkDependencies = new Map(); const chunkDependencies = new Map();
/** @type {Set<ChunkGroup>} */ /** @type {Set<ChunkGroup>} */
@ -1481,8 +1475,8 @@ class Compilation {
chunkGroupCounters.set(chunkGroup, { index: 0, index2: 0 }); chunkGroupCounters.set(chunkGroup, { index: 0, index2: 0 });
} }
let nextFreeModuleIndex = 0; let nextFreeModulePreOrderIndex = 0;
let nextFreeModuleIndex2 = 0; let nextFreeModulePostOrderIndex = 0;
/** @type {Map<DependenciesBlock, ChunkGroup>} */ /** @type {Map<DependenciesBlock, ChunkGroup>} */
const blockChunkGroups = new Map(); const blockChunkGroups = new Map();
@ -1619,8 +1613,13 @@ class Compilation {
} }
} }
if (module.index === null) { if (
module.index = nextFreeModuleIndex++; moduleGraph.setPreOrderIndexIfUnset(
module,
nextFreeModulePreOrderIndex
)
) {
nextFreeModulePreOrderIndex++;
} }
queue.push({ queue.push({
@ -1673,8 +1672,13 @@ class Compilation {
} }
} }
if (module.index2 === null) { if (
module.index2 = nextFreeModuleIndex2++; moduleGraph.setPostOrderIndexIfUnset(
module,
nextFreeModulePostOrderIndex
)
) {
nextFreeModulePostOrderIndex++;
} }
break; break;
} }
@ -2139,7 +2143,7 @@ class Compilation {
if (outputOptions.hashSalt) { if (outputOptions.hashSalt) {
chunkHash.update(outputOptions.hashSalt); chunkHash.update(outputOptions.hashSalt);
} }
chunk.updateHash(chunkHash, this.chunkGraph); chunk.updateHash(chunkHash, this);
const template = chunk.hasRuntime() const template = chunk.hasRuntime()
? this.mainTemplate ? this.mainTemplate
: this.chunkTemplate; : this.chunkTemplate;

View File

@ -51,14 +51,14 @@ class FlagDependencyUsagePlugin {
* @returns {void} * @returns {void}
*/ */
const processModule = (module, usedExports) => { const processModule = (module, usedExports) => {
let ue = module.getUsedExports(moduleGraph); let ue = moduleGraph.getUsedExports(module);
if (ue === true) return; if (ue === true) return;
if (usedExports === true) { if (usedExports === true) {
module.setUsedExports(moduleGraph, (ue = true)); moduleGraph.setUsedExports(module, (ue = true));
} else if (Array.isArray(usedExports)) { } else if (Array.isArray(usedExports)) {
if (!ue) { if (!ue) {
module.setUsedExports( moduleGraph.setUsedExports(
moduleGraph, module,
(ue = new SortableSet(usedExports)) (ue = new SortableSet(usedExports))
); );
} else { } else {
@ -72,7 +72,7 @@ class FlagDependencyUsagePlugin {
} }
} else { } else {
if (ue !== false) return; 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 // 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; if (!reference) return;
const referenceModule = reference.module; const referenceModule = reference.module;
const importedNames = reference.importedNames; const importedNames = reference.importedNames;
const oldUsedExports = referenceModule.getUsedExports(moduleGraph); const oldUsedExports = moduleGraph.getUsedExports(referenceModule);
if ( if (
!oldUsedExports || !oldUsedExports ||
!isContained(oldUsedExports, importedNames) !isContained(oldUsedExports, importedNames)
@ -119,7 +119,7 @@ class FlagDependencyUsagePlugin {
}; };
for (const module of modules) { for (const module of modules) {
module.setUsedExports(moduleGraph, false); moduleGraph.setUsedExports(module, false);
} }
/** @type {[Module, DependenciesBlock, UsedExports][]} */ /** @type {[Module, DependenciesBlock, UsedExports][]} */

View File

@ -30,7 +30,7 @@ class FlagInitialModulesAsUsedPlugin {
return; return;
} }
for (const module of chunkGraph.getChunkModulesIterable(chunk)) { for (const module of chunkGraph.getChunkModulesIterable(chunk)) {
module.setUsedExports(moduleGraph, true); moduleGraph.setUsedExports(module, true);
moduleGraph.addExtraReason(module, this.explanation); moduleGraph.addExtraReason(module, this.explanation);
} }
} }

View File

@ -75,7 +75,7 @@ class FunctionModuleTemplatePlugin {
} else if (module.buildMeta.providedExports) { } else if (module.buildMeta.providedExports) {
source.add(Template.toComment("no static exports found") + "\n"); source.add(Template.toComment("no static exports found") + "\n");
} }
const usedExports = module.getUsedExports(moduleGraph); const usedExports = moduleGraph.getUsedExports(module);
if (usedExports === true) { if (usedExports === true) {
source.add(Template.toComment("all exports used") + "\n"); source.add(Template.toComment("all exports used") + "\n");
} else if (usedExports === false) { } else if (usedExports === false) {
@ -91,8 +91,8 @@ class FunctionModuleTemplatePlugin {
); );
} }
} }
const optimizationBailout = module.getOptimizationBailout( const optimizationBailout = this.moduleGraph.getOptimizationBailout(
this.moduleGraph module
); );
if (optimizationBailout) { if (optimizationBailout) {
for (const text of optimizationBailout) { for (const text of optimizationBailout) {

View File

@ -175,6 +175,7 @@ module.exports = class HotModuleReplacementPlugin {
compiler.hooks.compilation.tap( compiler.hooks.compilation.tap(
"HotModuleReplacementPlugin", "HotModuleReplacementPlugin",
(compilation, { normalModuleFactory }) => { (compilation, { normalModuleFactory }) => {
const moduleGraph = compilation.moduleGraph;
const hotUpdateChunkTemplate = compilation.hotUpdateChunkTemplate; const hotUpdateChunkTemplate = compilation.hotUpdateChunkTemplate;
if (!hotUpdateChunkTemplate) return; if (!hotUpdateChunkTemplate) return;
@ -222,7 +223,7 @@ module.exports = class HotModuleReplacementPlugin {
records.chunkModuleIds[chunk.id] = Array.from( records.chunkModuleIds[chunk.id] = Array.from(
chunkGraph.getOrderedChunkModulesIterable( chunkGraph.getOrderedChunkModulesIterable(
chunk, chunk,
compareModulesById compareModulesById(moduleGraph)
), ),
m => m.id m => m.id
); );

View File

@ -48,6 +48,7 @@ class JavascriptModulesPlugin {
compiler.hooks.compilation.tap( compiler.hooks.compilation.tap(
"JavascriptModulesPlugin", "JavascriptModulesPlugin",
(compilation, { normalModuleFactory }) => { (compilation, { normalModuleFactory }) => {
const moduleGraph = compilation.moduleGraph;
const hooks = JavascriptModulesPlugin.getHooks(compilation); const hooks = JavascriptModulesPlugin.getHooks(compilation);
hooks.shouldRender.tap("JavascriptModulesPlugin", module => { hooks.shouldRender.tap("JavascriptModulesPlugin", module => {
if (module.type === "javascript/auto") return true; if (module.type === "javascript/auto") return true;
@ -194,7 +195,7 @@ class JavascriptModulesPlugin {
template.updateHashForChunk(hash, chunk); template.updateHashForChunk(hash, chunk);
for (const m of chunkGraph.getOrderedChunkModulesIterable( for (const m of chunkGraph.getOrderedChunkModulesIterable(
chunk, chunk,
compareModulesById compareModulesById(moduleGraph)
)) { )) {
if (typeof m.source === "function") { if (typeof m.source === "function") {
hash.update(m.hash); hash.update(m.hash);

View File

@ -19,6 +19,7 @@ class LibManifestPlugin {
compiler.hooks.emit.tapAsync( compiler.hooks.emit.tapAsync(
"LibManifestPlugin", "LibManifestPlugin",
(compilation, callback) => { (compilation, callback) => {
const moduleGraph = compilation.moduleGraph;
asyncLib.forEach( asyncLib.forEach(
compilation.chunks, compilation.chunks,
(chunk, callback) => { (chunk, callback) => {
@ -43,7 +44,7 @@ class LibManifestPlugin {
content: Array.from( content: Array.from(
chunkGraph.getOrderedChunkModulesIterable( chunkGraph.getOrderedChunkModulesIterable(
chunk, chunk,
compareModulesById compareModulesById(moduleGraph)
), ),
module => { module => {
if ( if (

View File

@ -5,17 +5,18 @@
"use strict"; "use strict";
const ChunkGraph = require("./ChunkGraph");
const DependenciesBlock = require("./DependenciesBlock"); const DependenciesBlock = require("./DependenciesBlock");
const ModuleGraph = require("./ModuleGraph");
const Template = require("./Template"); const Template = require("./Template");
const { compareChunksById } = require("./util/comparators");
/** @typedef {import("webpack-sources").Source} Source */ /** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("./Chunk")} Chunk */ /** @typedef {import("./Chunk")} Chunk */
/** @typedef {import("./ChunkGraph")} ChunkGraph */
/** @typedef {import("./ChunkGroup")} ChunkGroup */ /** @typedef {import("./ChunkGroup")} ChunkGroup */
/** @typedef {import("./Compilation")} Compilation */ /** @typedef {import("./Compilation")} Compilation */
/** @typedef {import("./Dependency")} Dependency */ /** @typedef {import("./Dependency")} Dependency */
/** @typedef {import("./DependencyTemplates")} DependencyTemplates */ /** @typedef {import("./DependencyTemplates")} DependencyTemplates */
/** @typedef {import("./ModuleGraph")} ModuleGraph */
/** @typedef {import("./RequestShortener")} RequestShortener */ /** @typedef {import("./RequestShortener")} RequestShortener */
/** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */ /** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */
/** @typedef {import("./WebpackError")} WebpackError */ /** @typedef {import("./WebpackError")} WebpackError */
@ -45,9 +46,6 @@ const Template = require("./Template");
/** @typedef {KnownBuildMeta & Record<string, any>} BuildMeta */ /** @typedef {KnownBuildMeta & Record<string, any>} BuildMeta */
const EMPTY_RESOLVE_OPTIONS = {}; const EMPTY_RESOLVE_OPTIONS = {};
const optimizationBailoutSymbol = Symbol("optimization bailout");
const usedExportsSymbol = Symbol("used exports");
const issuerSymbol = Symbol("issuer");
let debugId = 1000; let debugId = 1000;
@ -106,10 +104,6 @@ class Module extends DependenciesBlock {
/** @type {number|string} */ /** @type {number|string} */
this.id = null; this.id = null;
/** @type {number} */ /** @type {number} */
this.index = null;
/** @type {number} */
this.index2 = null;
/** @type {number} */
this.depth = null; this.depth = null;
/** @type {undefined | object} */ /** @type {undefined | object} */
this.profile = undefined; this.profile = undefined;
@ -122,6 +116,126 @@ class Module extends DependenciesBlock {
this.useSourceMap = false; 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 * @deprecated moved to .buildInfo.exportsArgument
* @returns {string} name of the exports argument * @returns {string} name of the exports argument
@ -138,61 +252,6 @@ class Module extends DependenciesBlock {
return (this.buildInfo && this.buildInfo.moduleArgument) || "module"; 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 * disconnect the module from the graph
* @returns {void} * @returns {void}
@ -202,8 +261,6 @@ class Module extends DependenciesBlock {
this.renderedHash = undefined; this.renderedHash = undefined;
this.id = null; this.id = null;
this.index = null;
this.index2 = null;
this.depth = null; this.depth = null;
this.profile = undefined; this.profile = undefined;
this.prefetched = false; this.prefetched = false;
@ -217,8 +274,6 @@ class Module extends DependenciesBlock {
*/ */
unseal() { unseal() {
this.id = null; this.id = null;
this.index = null;
this.index2 = null;
this.depth = null; this.depth = null;
super.unseal(); super.unseal();
} }
@ -309,7 +364,7 @@ class Module extends DependenciesBlock {
* @returns {boolean} true, if the module is used * @returns {boolean} true, if the module is used
*/ */
isModuleUsed(moduleGraph) { 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 * @returns {boolean} true, if the export is used
*/ */
isExportUsed(moduleGraph, exportName) { isExportUsed(moduleGraph, exportName) {
const usedExports = this.getUsedExports(moduleGraph); const usedExports = moduleGraph.getUsedExports(this);
if (usedExports === null || usedExports === true) return true; if (usedExports === null || usedExports === true) return true;
if (usedExports === false) return false; if (usedExports === false) return false;
return usedExports.has(exportName); return usedExports.has(exportName);
@ -331,7 +386,7 @@ class Module extends DependenciesBlock {
* string, the mangled export name when used. * string, the mangled export name when used.
*/ */
getUsedName(moduleGraph, exportName) { getUsedName(moduleGraph, exportName) {
const usedExports = this.getUsedExports(moduleGraph); const usedExports = moduleGraph.getUsedExports(this);
if (usedExports === null || usedExports === true) return exportName; if (usedExports === null || usedExports === true) return exportName;
if (usedExports === false) return false; if (usedExports === false) return false;
if (!usedExports.has(exportName)) return false; if (!usedExports.has(exportName)) return false;
@ -391,7 +446,7 @@ class Module extends DependenciesBlock {
*/ */
updateHash(hash, compilation) { updateHash(hash, compilation) {
hash.update(`${this.id}`); hash.update(`${this.id}`);
const usedExports = this.getUsedExports(compilation.moduleGraph); const usedExports = compilation.moduleGraph.getUsedExports(this);
if (typeof usedExports === "boolean") { if (typeof usedExports === "boolean") {
hash.update(JSON.stringify(usedExports)); hash.update(JSON.stringify(usedExports));
} else if (!usedExports) { } else if (!usedExports) {
@ -522,22 +577,26 @@ Object.defineProperty(Module.prototype, "isUsed", {
Object.defineProperty(Module.prototype, "used", { Object.defineProperty(Module.prototype, "used", {
get() { get() {
throw new Error("Module.used was refactored (use getUsedExports instead)"); throw new Error(
"Module.used was refactored (use ModuleGraph.getUsedExports instead)"
);
}, },
set(value) { 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", { Object.defineProperty(Module.prototype, "usedExports", {
get() { get() {
throw new Error( throw new Error(
"Module.usedExports was refactored (use getUsedExports instead)" "Module.usedExports was refactored (use ModuleGraph.getUsedExports instead)"
); );
}, },
set(value) { set(value) {
throw new Error( 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"; "use strict";
const util = require("util");
const ModuleGraphConnection = require("./ModuleGraphConnection"); const ModuleGraphConnection = require("./ModuleGraphConnection");
/** @typedef {import("./DependenciesBlock")} DependenciesBlock */ /** @typedef {import("./DependenciesBlock")} DependenciesBlock */
/** @typedef {import("./Dependency")} Dependency */ /** @typedef {import("./Dependency")} Dependency */
/** @typedef {import("./Module")} Module */ /** @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 { class ModuleGraph {
constructor() { constructor() {
/** @type {Map<Dependency, ModuleGraphConnection>} */ /** @type {Map<Dependency, ModuleGraphDependency>} */
this._dependencyMap = new Map(); this._dependencyMap = new Map();
/** @type {Map<Module, Set<ModuleGraphConnection>>} */ /** @type {Map<Module, ModuleGraphModule>} */
this._moduleMap = new Map(); this._moduleMap = new Map();
/** @type {Map<Module, Set<ModuleGraphConnection>>} */ /** @type {Map<Module, Set<ModuleGraphConnection>>} */
this._originMap = new Map(); this._originMap = new Map();
/** @type {Map<any, Object>} */ /** @type {Map<any, Object>} */
this._metaMap = new Map(); this._metaMap = new Map();
/** @type {Map<Dependency, {module: Module, block: DependenciesBlock}>} */
this._parentsMap = new Map();
} }
_getModuleSet(module) { /**
let connections = this._moduleMap.get(module); * @param {Module} module the module
if (connections === undefined) { * @returns {ModuleGraphModule} the internal module
connections = new Set(); */
this._moduleMap.set(module, connections); _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); * @param {Dependency} dependency the dependency
if (connections === undefined) { * @returns {ModuleGraphDependency} the internal dependency
connections = new Set(); */
this._originMap.set(module, connections); _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} * @returns {void}
*/ */
setParents(dependency, block, module) { 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 * @returns {Module} parent module
*/ */
getParentModule(dependency) { getParentModule(dependency) {
const entry = this._parentsMap.get(dependency); const mgd = this._getModuleGraphDependency(dependency);
return entry !== undefined ? entry.module : null; return mgd.parentModule;
} }
/** /**
@ -67,8 +110,8 @@ class ModuleGraph {
* @returns {DependenciesBlock} parent block * @returns {DependenciesBlock} parent block
*/ */
getParentBlock(dependency) { getParentBlock(dependency) {
const entry = this._parentsMap.get(dependency); const mgd = this._getModuleGraphDependency(dependency);
return entry !== undefined ? entry.block : null; return mgd.parentBlock;
} }
/** /**
@ -83,10 +126,12 @@ class ModuleGraph {
dependency, dependency,
module module
); );
this._dependencyMap.set(dependency, connection); const mgd = this._getModuleGraphDependency(dependency);
const connections = this._getModuleSet(module); mgd.connection = connection;
const connections = this._getModuleGraphModule(module).incomingConnections;
connections.add(connection); connections.add(connection);
const originConnections = this._getOriginSet(originModule); const originConnections = this._getModuleGraphModule(originModule)
.outgoingConnections;
originConnections.add(connection); originConnections.add(connection);
} }
@ -96,13 +141,13 @@ class ModuleGraph {
* @returns {void} * @returns {void}
*/ */
updateModule(dependency, module) { updateModule(dependency, module) {
const connection = this._dependencyMap.get(dependency); const { connection } = this._getModuleGraphDependency(dependency);
if (connection.module === module) return; if (connection.module === module) return;
const oldSet = this._moduleMap.get(connection.module); const oldMgm = this._getModuleGraphModule(connection.module);
oldSet.delete(connection); oldMgm.incomingConnections.delete(connection);
connection.module = module; connection.module = module;
const newSet = this._moduleMap.get(module); const newMgm = this._getModuleGraphModule(module);
newSet.add(connection); newMgm.incomingConnections.add(connection);
} }
/** /**
@ -111,20 +156,57 @@ class ModuleGraph {
* @returns {void} * @returns {void}
*/ */
addExplanation(dependency, explanation) { addExplanation(dependency, explanation) {
const connection = this._dependencyMap.get(dependency); const { connection } = this._getModuleGraphDependency(dependency);
connection.addExplanation(explanation); 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} oldModule the old referencing module
* @param {Module} newModule the new referencing module * @param {Module} newModule the new referencing module
* @param {function(ModuleGraphConnection): boolean} filterConnection filter predicate for replacement * @param {function(ModuleGraphConnection): boolean} filterConnection filter predicate for replacement
* @returns {void} * @returns {void}
*/ */
replaceModule(oldModule, newModule, filterConnection) { moveModuleConnections(oldModule, newModule, filterConnection) {
if (oldModule === newModule) return; if (oldModule === newModule) return;
const oldConnections = this._getOriginSet(oldModule); const oldMgm = this._getModuleGraphModule(oldModule);
const newConnections = this._getOriginSet(newModule); const newMgm = this._getModuleGraphModule(newModule);
// Outgoing connections
const oldConnections = oldMgm.outgoingConnections;
const newConnections = newMgm.outgoingConnections;
for (const connection of oldConnections) { for (const connection of oldConnections) {
if (filterConnection(connection)) { if (filterConnection(connection)) {
connection.originModule = newModule; connection.originModule = newModule;
@ -132,8 +214,9 @@ class ModuleGraph {
oldConnections.delete(connection); oldConnections.delete(connection);
} }
} }
const oldConnections2 = this._getModuleSet(oldModule); // Incoming connections
const newConnections2 = this._getModuleSet(newModule); const oldConnections2 = oldMgm.incomingConnections;
const newConnections2 = newMgm.incomingConnections;
for (const connection of oldConnections2) { for (const connection of oldConnections2) {
if (filterConnection(connection)) { if (filterConnection(connection)) {
connection.module = newModule; connection.module = newModule;
@ -149,7 +232,7 @@ class ModuleGraph {
* @returns {void} * @returns {void}
*/ */
addExtraReason(module, explanation) { addExtraReason(module, explanation) {
const connections = this._getModuleSet(module); const connections = this._getModuleGraphModule(module).incomingConnections;
connections.add(new ModuleGraphConnection(null, null, module, explanation)); connections.add(new ModuleGraphConnection(null, null, module, explanation));
} }
@ -158,7 +241,7 @@ class ModuleGraph {
* @returns {Module} the referenced module * @returns {Module} the referenced module
*/ */
getResolvedModule(dependency) { getResolvedModule(dependency) {
const connection = this._dependencyMap.get(dependency); const { connection } = this._getModuleGraphDependency(dependency);
return connection !== undefined ? connection.resolvedModule : null; return connection !== undefined ? connection.resolvedModule : null;
} }
@ -167,7 +250,7 @@ class ModuleGraph {
* @returns {ModuleGraphConnection | undefined} the connection * @returns {ModuleGraphConnection | undefined} the connection
*/ */
getConnection(dependency) { getConnection(dependency) {
const connection = this._dependencyMap.get(dependency); const { connection } = this._getModuleGraphDependency(dependency);
return connection; return connection;
} }
@ -176,7 +259,7 @@ class ModuleGraph {
* @returns {Module} the referenced module * @returns {Module} the referenced module
*/ */
getModule(dependency) { getModule(dependency) {
const connection = this._dependencyMap.get(dependency); const { connection } = this._getModuleGraphDependency(dependency);
return connection !== undefined ? connection.module : null; return connection !== undefined ? connection.module : null;
} }
@ -185,7 +268,7 @@ class ModuleGraph {
* @returns {Module} the referencing module * @returns {Module} the referencing module
*/ */
getOrigin(dependency) { getOrigin(dependency) {
const connection = this._dependencyMap.get(dependency); const { connection } = this._getModuleGraphDependency(dependency);
return connection !== undefined ? connection.originModule : null; return connection !== undefined ? connection.originModule : null;
} }
@ -194,7 +277,7 @@ class ModuleGraph {
* @returns {Module} the original referencing module * @returns {Module} the original referencing module
*/ */
getResolvedOrigin(dependency) { getResolvedOrigin(dependency) {
const connection = this._dependencyMap.get(dependency); const { connection } = this._getModuleGraphDependency(dependency);
return connection !== undefined ? connection.resolvedOriginModule : null; return connection !== undefined ? connection.resolvedOriginModule : null;
} }
@ -203,7 +286,7 @@ class ModuleGraph {
* @returns {ModuleGraphConnection[]} reasons why a module is included * @returns {ModuleGraphConnection[]} reasons why a module is included
*/ */
getIncomingConnections(module) { getIncomingConnections(module) {
const connections = this._getModuleSet(module); const connections = this._getModuleGraphModule(module).incomingConnections;
return Array.from(connections); return Array.from(connections);
} }
@ -212,10 +295,128 @@ class ModuleGraph {
* @returns {ModuleGraphConnection[]} list of outgoing connections * @returns {ModuleGraphConnection[]} list of outgoing connections
*/ */
getOutgoingConnection(module) { getOutgoingConnection(module) {
const connections = this._getOriginSet(module); const connections = this._getModuleGraphModule(module).outgoingConnections;
return Array.from(connections); 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 * @param {any} thing any thing
* @returns {Object} metadata * @returns {Object} metadata
@ -228,7 +429,54 @@ class ModuleGraph {
} }
return meta; 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 = ModuleGraph;
module.exports.ModuleGraphConnection = ModuleGraphConnection; module.exports.ModuleGraphConnection = ModuleGraphConnection;

View File

@ -324,7 +324,7 @@ class Stats {
if (showErrorDetails && e.missing) { if (showErrorDetails && e.missing) {
text += e.missing.map(item => `\n[${item}]`).join(""); 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) { if (showModuleTrace && origin) {
text += `\n @ ${this.formatFilePath( text += `\n @ ${this.formatFilePath(
origin.readableIdentifier(requestShortener) origin.readableIdentifier(requestShortener)
@ -342,10 +342,10 @@ class Stats {
text += ` ${locInfo}`; text += ` ${locInfo}`;
} }
} }
let current = origin.getIssuer(moduleGraph); let current = moduleGraph.getIssuer(origin);
while (current) { while (current) {
text += `\n @ ${current.readableIdentifier(requestShortener)}`; text += `\n @ ${current.readableIdentifier(requestShortener)}`;
current = current.getIssuer(moduleGraph); current = moduleGraph.getIssuer(current);
} }
} }
return text; return text;
@ -455,12 +455,12 @@ class Stats {
for (const keyValuePair of groupMap) { for (const keyValuePair of groupMap) {
const name = keyValuePair[0]; const name = keyValuePair[0];
const cg = keyValuePair[1]; const cg = keyValuePair[1];
const children = cg.getChildrenByOrders(chunkGraph); const children = cg.getChildrenByOrders(moduleGraph, chunkGraph);
obj[name] = { obj[name] = {
chunks: cg.chunks.map(c => c.id), chunks: cg.chunks.map(c => c.id),
assets: cg.chunks.reduce( assets: cg.chunks.reduce(
(array, c) => array.concat(c.files || []), (array, c) => array.concat(c.files || []),
[] /** @type {string[]} */ ([])
), ),
children: Object.keys(children).reduce((obj, key) => { children: Object.keys(children).reduce((obj, key) => {
const groups = children[key]; const groups = children[key];
@ -469,11 +469,11 @@ class Stats {
chunks: group.chunks.map(c => c.id), chunks: group.chunks.map(c => c.id),
assets: group.chunks.reduce( assets: group.chunks.reduce(
(array, c) => array.concat(c.files || []), (array, c) => array.concat(c.files || []),
[] /** @type {string[]} */ ([])
) )
})); }));
return obj; 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) => { childAssets: Object.keys(children).reduce((obj, key) => {
const groups = children[key]; const groups = children[key];
obj[key] = Array.from( obj[key] = Array.from(
@ -484,7 +484,7 @@ class Stats {
} }
} }
return set; return set;
}, new Set()) }, /** @type {Set<string>} */ (new Set()))
); );
return obj; return obj;
}, Object.create(null)) }, Object.create(null))
@ -507,19 +507,21 @@ class Stats {
const fnModule = (module, nested) => { const fnModule = (module, nested) => {
const path = []; const path = [];
const issuer = module.getIssuer(moduleGraph); const issuer = moduleGraph.getIssuer(module);
let current = issuer; let current = issuer;
while (current) { while (current) {
path.push(current); path.push(current);
current = current.getIssuer(moduleGraph); current = moduleGraph.getIssuer(current);
} }
path.reverse(); path.reverse();
const obj = { const obj = {
id: module.id, id: module.id,
identifier: module.identifier(), identifier: module.identifier(),
name: module.readableIdentifier(requestShortener), name: module.readableIdentifier(requestShortener),
index: module.index, index: moduleGraph.getPreOrderIndex(module),
index2: module.index2, preOrderIndex: moduleGraph.getPreOrderIndex(module),
index2: moduleGraph.getPostOrderIndex(module),
postOrderIndex: moduleGraph.getPostOrderIndex(module),
size: module.size(), size: module.size(),
cacheable: module.buildInfo.cacheable, cacheable: module.buildInfo.cacheable,
built: !!module.built, built: !!module.built,
@ -601,7 +603,7 @@ class Stats {
}); });
} }
if (showUsedExports) { if (showUsedExports) {
const usedExports = module.getUsedExports(moduleGraph); const usedExports = moduleGraph.getUsedExports(module);
if (usedExports === null) { if (usedExports === null) {
obj.usedExports = null; obj.usedExports = null;
} else if (typeof usedExports === "boolean") { } else if (typeof usedExports === "boolean") {
@ -616,8 +618,8 @@ class Stats {
: null; : null;
} }
if (showOptimizationBailout) { if (showOptimizationBailout) {
obj.optimizationBailout = module obj.optimizationBailout = moduleGraph
.getOptimizationBailout(moduleGraph) .getOptimizationBailout(module)
.map(item => { .map(item => {
if (typeof item === "function") return item(requestShortener); if (typeof item === "function") return item(requestShortener);
return item; return item;
@ -647,7 +649,10 @@ class Stats {
const parents = new Set(); const parents = new Set();
const children = new Set(); const children = new Set();
const siblings = 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 chunkGroup of chunk.groupsIterable) {
for (const parentGroup of chunkGroup.parentsIterable) { for (const parentGroup of chunkGroup.parentsIterable) {
for (const chunk of parentGroup.chunks) { for (const chunk of parentGroup.chunks) {

View File

@ -36,7 +36,7 @@ HarmonyCompatibilityDependency.Template = class HarmonyExportDependencyTemplate
* @returns {InitFragment[]|null} the init fragments * @returns {InitFragment[]|null} the init fragments
*/ */
getInitFragments(dependency, { module, runtimeTemplate, moduleGraph }) { getInitFragments(dependency, { module, runtimeTemplate, moduleGraph }) {
const usedExports = module.getUsedExports(moduleGraph); const usedExports = moduleGraph.getUsedExports(module);
if (usedExports === true || usedExports === null) { if (usedExports === true || usedExports === null) {
const content = runtimeTemplate.defineEsModuleFlagStatement({ const content = runtimeTemplate.defineEsModuleFlagStatement({
exportsArgument: module.exportsArgument exportsArgument: module.exportsArgument

View File

@ -35,7 +35,7 @@ const getHashValue = (moduleGraph, importedModule) => {
return ""; return "";
} }
const usedExports = importedModule.getUsedExports(moduleGraph); const usedExports = moduleGraph.getUsedExports(importedModule);
const stringifiedUsedExports = JSON.stringify(usedExports); const stringifiedUsedExports = JSON.stringify(usedExports);
const stringifiedProvidedExports = JSON.stringify( const stringifiedProvidedExports = JSON.stringify(
importedModule.buildMeta.providedExports importedModule.buildMeta.providedExports
@ -96,7 +96,7 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
const id = this.id; const id = this.id;
const parentModule = moduleGraph.getParentModule(this); const parentModule = moduleGraph.getParentModule(this);
const used = parentModule.getUsedName(moduleGraph, name); const used = parentModule.getUsedName(moduleGraph, name);
const usedExports = parentModule.getUsedExports(moduleGraph); const usedExports = moduleGraph.getUsedExports(parentModule);
const importedModule = moduleGraph.getModule(this); const importedModule = moduleGraph.getModule(this);
if (!importedModule) { if (!importedModule) {
@ -495,7 +495,7 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
const activeFromOtherStarExports = dep._discoverActiveExportsFromOtherStartExports( const activeFromOtherStarExports = dep._discoverActiveExportsFromOtherStartExports(
moduleGraph moduleGraph
); );
const usedExports = module.getUsedExports(moduleGraph); const usedExports = moduleGraph.getUsedExports(module);
if (usedExports && usedExports !== true) { if (usedExports && usedExports !== true) {
// we know which exports are used // we know which exports are used

View File

@ -17,6 +17,8 @@ const HarmonyImportDependency = require("./HarmonyImportDependency");
/** @typedef {import("../WebpackError")} WebpackError */ /** @typedef {import("../WebpackError")} WebpackError */
/** @typedef {import("../util/createHash").Hash} Hash */ /** @typedef {import("../util/createHash").Hash} Hash */
const idSymbol = Symbol("HarmonyImportSpecifierDependency.id");
class HarmonyImportSpecifierDependency extends HarmonyImportDependency { class HarmonyImportSpecifierDependency extends HarmonyImportDependency {
constructor( constructor(
request, request,
@ -48,7 +50,16 @@ class HarmonyImportSpecifierDependency extends HarmonyImportDependency {
* @returns {string} the imported id * @returns {string} the imported id
*/ */
getId(moduleGraph) { 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) { if (importedModule) {
const usedExports = importedModule.getUsedExports(moduleGraph); const usedExports = moduleGraph.getUsedExports(importedModule);
const stringifyUsedExports = JSON.stringify(usedExports); const stringifyUsedExports = JSON.stringify(usedExports);
hash.update(stringifyUsedExports); hash.update(stringifyUsedExports);
} }

View File

@ -73,6 +73,7 @@ class AggressiveSplittingPlugin {
compiler.hooks.thisCompilation.tap( compiler.hooks.thisCompilation.tap(
"AggressiveSplittingPlugin", "AggressiveSplittingPlugin",
compilation => { compilation => {
const moduleGraph = compilation.moduleGraph;
let needAdditionalSeal = false; let needAdditionalSeal = false;
let newSplits; let newSplits;
let fromAggressiveSplittingSet; let fromAggressiveSplittingSet;
@ -191,6 +192,7 @@ class AggressiveSplittingPlugin {
// for any chunk which isn't splitted yet, split it and create a new entry // for any chunk which isn't splitted yet, split it and create a new entry
// start with the biggest chunk // start with the biggest chunk
const cmpFn = compareModulesById(moduleGraph);
const sortedChunks = chunks.slice().sort((a, b) => { const sortedChunks = chunks.slice().sort((a, b) => {
const diff1 = const diff1 =
chunkGraph.getChunkModulesSize(b) - chunkGraph.getChunkModulesSize(b) -
@ -201,10 +203,10 @@ class AggressiveSplittingPlugin {
chunkGraph.getNumberOfChunkModules(b); chunkGraph.getNumberOfChunkModules(b);
if (diff2) return diff2; if (diff2) return diff2;
const modulesA = Array.from( const modulesA = Array.from(
chunkGraph.getOrderedChunkModulesIterable(a, compareModulesById) chunkGraph.getOrderedChunkModulesIterable(a, cmpFn)
); );
const modulesB = Array.from( const modulesB = Array.from(
chunkGraph.getOrderedChunkModulesIterable(b, compareModulesById) chunkGraph.getOrderedChunkModulesIterable(b, cmpFn)
); );
const aI = modulesA[Symbol.iterator](); const aI = modulesA[Symbol.iterator]();
const bI = modulesB[Symbol.iterator](); const bI = modulesB[Symbol.iterator]();

View File

@ -24,6 +24,7 @@ class ChunkModuleIdRangePlugin {
apply(compiler) { apply(compiler) {
const options = this.options; const options = this.options;
compiler.hooks.compilation.tap("ChunkModuleIdRangePlugin", compilation => { compiler.hooks.compilation.tap("ChunkModuleIdRangePlugin", compilation => {
const moduleGraph = compilation.moduleGraph;
compilation.hooks.moduleIds.tap("ChunkModuleIdRangePlugin", modules => { compilation.hooks.moduleIds.tap("ChunkModuleIdRangePlugin", modules => {
const chunkGraph = compilation.chunkGraph; const chunkGraph = compilation.chunkGraph;
const chunk = compilation.chunks.find( const chunk = compilation.chunks.find(
@ -42,10 +43,10 @@ class ChunkModuleIdRangePlugin {
let cmpFn; let cmpFn;
switch (options.order) { switch (options.order) {
case "index": case "index":
cmpFn = compareModulesByIndex; cmpFn = compareModulesByIndex(moduleGraph);
break; break;
case "index2": case "index2":
cmpFn = compareModulesByIndex2; cmpFn = compareModulesByIndex2(moduleGraph);
break; break;
default: default:
throw new Error( throw new Error(

View File

@ -321,12 +321,10 @@ class ConcatenatedModule extends Module {
this.factoryMeta = rootModule.factoryMeta; this.factoryMeta = rootModule.factoryMeta;
// Info from Compilation // Info from Compilation
this.index = rootModule.index;
this.index2 = rootModule.index2;
this.depth = rootModule.depth; this.depth = rootModule.depth;
// Info from Optimization // Info from Optimization
this.setUsedExports(moduleGraph, rootModule.getUsedExports(moduleGraph)); moduleGraph.setUsedExports(this, moduleGraph.getUsedExports(rootModule));
const modulesArray = Array.from(modules); const modulesArray = Array.from(modules);
@ -1145,7 +1143,7 @@ class ConcatenatedModule extends Module {
const result = new ConcatSource(); const result = new ConcatSource();
// add harmony compatibility flag (must be first because of possible circular dependencies) // 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) { if (usedExports === true) {
result.add( result.add(
runtimeTemplate.defineEsModuleFlagStatement({ runtimeTemplate.defineEsModuleFlagStatement({

View File

@ -5,6 +5,8 @@
"use strict"; "use strict";
const ChunkGraph = require("../ChunkGraph");
const ModuleGraph = require("../ModuleGraph");
const { STAGE_DEFAULT } = require("../OptimizationStages"); const { STAGE_DEFAULT } = require("../OptimizationStages");
const HarmonyCompatibilityDependency = require("../dependencies/HarmonyCompatibilityDependency"); const HarmonyCompatibilityDependency = require("../dependencies/HarmonyCompatibilityDependency");
const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency"); const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency");
@ -58,8 +60,8 @@ class ModuleConcatenationPlugin {
const setBailoutReason = (module, reason) => { const setBailoutReason = (module, reason) => {
setInnerBailoutReason(module, reason); setInnerBailoutReason(module, reason);
module moduleGraph
.getOptimizationBailout(moduleGraph) .getOptimizationBailout(module)
.push( .push(
typeof reason === "function" typeof reason === "function"
? rs => formatBailoutReason(reason(rs)) ? rs => formatBailoutReason(reason(rs))
@ -308,8 +310,8 @@ class ModuleConcatenationPlugin {
} }
} else { } else {
for (const warning of currentConfiguration.getWarningsSorted()) { for (const warning of currentConfiguration.getWarningsSorted()) {
currentRoot moduleGraph
.getOptimizationBailout(moduleGraph) .getOptimizationBailout(currentRoot)
.push(formatBailoutWarning(warning[0], warning[1])); .push(formatBailoutWarning(warning[0], warning[1]));
} }
} }
@ -331,17 +333,22 @@ class ModuleConcatenationPlugin {
modules, modules,
compilation compilation
); );
ChunkGraph.setChunkGraphForModule(newModule, chunkGraph);
ModuleGraph.setModuleGraphForModule(newModule, moduleGraph);
for (const warning of concatConfiguration.getWarningsSorted()) { for (const warning of concatConfiguration.getWarningsSorted()) {
newModule moduleGraph
.getOptimizationBailout(moduleGraph) .getOptimizationBailout(newModule)
.push(formatBailoutWarning(warning[0], warning[1])); .push(formatBailoutWarning(warning[0], warning[1]));
} }
moduleGraph.moveModuleAttributes(rootModule, newModule);
for (const m of modules) { for (const m of modules) {
usedModules.add(m); usedModules.add(m);
// remove attributes from module
moduleGraph.removeModuleAttributes(m);
// remove module from chunk // remove module from chunk
chunkGraph.replaceModule(m, newModule); chunkGraph.replaceModule(m, newModule);
// replace module references with the concatenated module // replace module references with the concatenated module
moduleGraph.replaceModule(m, newModule, c => { moduleGraph.moveModuleConnections(m, newModule, c => {
return !( return !(
c.dependency instanceof HarmonyImportDependency && c.dependency instanceof HarmonyImportDependency &&
modules.has(c.originModule) && modules.has(c.originModule) &&

View File

@ -16,16 +16,18 @@ class NaturalChunkOrderPlugin {
*/ */
apply(compiler) { apply(compiler) {
compiler.hooks.compilation.tap("NaturalChunkOrderPlugin", compilation => { compiler.hooks.compilation.tap("NaturalChunkOrderPlugin", compilation => {
const moduleGraph = compilation.moduleGraph;
compilation.hooks.optimizeChunkOrder.tap( compilation.hooks.optimizeChunkOrder.tap(
"NaturalChunkOrderPlugin", "NaturalChunkOrderPlugin",
chunks => { chunks => {
const chunkGraph = compilation.chunkGraph; const chunkGraph = compilation.chunkGraph;
chunks.sort((chunkA, chunkB) => { chunks.sort((chunkA, chunkB) => {
const cmpFn = compareModulesById(moduleGraph);
const a = chunkGraph const a = chunkGraph
.getOrderedChunkModulesIterable(chunkA, compareModulesById) .getOrderedChunkModulesIterable(chunkA, cmpFn)
[Symbol.iterator](); [Symbol.iterator]();
const b = chunkGraph const b = chunkGraph
.getOrderedChunkModulesIterable(chunkB, compareModulesById) .getOrderedChunkModulesIterable(chunkB, cmpFn)
[Symbol.iterator](); [Symbol.iterator]();
// eslint-disable-next-line no-constant-condition // eslint-disable-next-line no-constant-condition
while (true) { while (true) {

View File

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

View File

@ -7,6 +7,35 @@
/** @typedef {import("../Chunk")} Chunk */ /** @typedef {import("../Chunk")} Chunk */
/** @typedef {import("../Module")} Module */ /** @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 * @param {Chunk} a chunk
@ -18,13 +47,18 @@ exports.compareChunksById = (a, b) => {
}; };
/** /**
* @param {ModuleGraph} moduleGraph the module graph
* @param {Module} a module * @param {Module} a module
* @param {Module} b module * @param {Module} b module
* @returns {-1|0|1} compare result * @returns {-1|0|1} compare result
*/ */
exports.compareModulesById = (a, b) => { const compareModulesById = (moduleGraph, a, b) => {
return compareIds(a.id, b.id); return compareIds(a.id, b.id);
}; };
/** @type {ParamizedComparator<ModuleGraph, Module>} */
exports.compareModulesById = createCachedParamizedComparator(
compareModulesById
);
/** /**
* @param {number} a number * @param {number} a number
@ -41,22 +75,58 @@ const compareNumbers = (a, b) => {
}; };
/** /**
* @param {ModuleGraph} moduleGraph the module graph
* @param {Module} a module * @param {Module} a module
* @param {Module} b module * @param {Module} b module
* @returns {-1|0|1} compare result * @returns {-1|0|1} compare result
*/ */
exports.compareModulesByIndex = (a, b) => { const compareModulesByIndex = (moduleGraph, a, b) => {
return compareNumbers(a.index, b.index); 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} a module
* @param {Module} b module * @param {Module} b module
* @returns {-1|0|1} compare result * @returns {-1|0|1} compare result
*/ */
exports.compareModulesByIndex2 = (a, b) => { const compareModulesByIndex2 = (moduleGraph, a, b) => {
return compareNumbers(a.index2, b.index2); 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 * @param {string|number} a first id

View File

@ -16,13 +16,13 @@ const WebAssemblyUtils = require("./WebAssemblyUtils");
/** @typedef {import("../ModuleGraph")} ModuleGraph */ /** @typedef {import("../ModuleGraph")} ModuleGraph */
// Get all wasm modules // Get all wasm modules
const getAllWasmModules = (chunkGraph, chunk) => { const getAllWasmModules = (moduleGraph, chunkGraph, chunk) => {
const wasmModules = chunk.getAllAsyncChunks(); const wasmModules = chunk.getAllAsyncChunks();
const array = []; const array = [];
for (const chunk of wasmModules) { for (const chunk of wasmModules) {
for (const m of chunkGraph.getOrderedChunkModulesIterable( for (const m of chunkGraph.getOrderedChunkModulesIterable(
chunk, chunk,
compareModulesById compareModulesById(moduleGraph)
)) { )) {
if (m.type.startsWith("webassembly")) { if (m.type.startsWith("webassembly")) {
array.push(m); array.push(m);
@ -188,10 +188,12 @@ class WasmMainTemplatePlugin {
* @returns {void} * @returns {void}
*/ */
apply(mainTemplate) { apply(mainTemplate) {
const moduleGraph = this.compilation.moduleGraph;
mainTemplate.hooks.localVars.tap( mainTemplate.hooks.localVars.tap(
"WasmMainTemplatePlugin", "WasmMainTemplatePlugin",
(source, chunk) => { (source, chunk) => {
const wasmModules = getAllWasmModules( const wasmModules = getAllWasmModules(
this.compilation.moduleGraph,
this.compilation.chunkGraph, this.compilation.chunkGraph,
chunk chunk
); );
@ -228,6 +230,7 @@ class WasmMainTemplatePlugin {
mainTemplate.outputOptions.webassemblyModuleFilename; mainTemplate.outputOptions.webassemblyModuleFilename;
const chunkModuleMaps = this.compilation.chunkGraph.getChunkModuleMaps( const chunkModuleMaps = this.compilation.chunkGraph.getChunkModuleMaps(
moduleGraph,
chunk, chunk,
m => m.type.startsWith("webassembly") m => m.type.startsWith("webassembly")
); );
@ -378,11 +381,13 @@ class WasmMainTemplatePlugin {
"WasmMainTemplatePlugin", "WasmMainTemplatePlugin",
(hash, chunk) => { (hash, chunk) => {
const chunkModuleMaps = this.compilation.chunkGraph.getChunkModuleMaps( const chunkModuleMaps = this.compilation.chunkGraph.getChunkModuleMaps(
moduleGraph,
chunk, chunk,
m => m.type.startsWith("webassembly") m => m.type.startsWith("webassembly")
); );
hash.update(JSON.stringify(chunkModuleMaps.id)); hash.update(JSON.stringify(chunkModuleMaps.id));
const wasmModules = getAllWasmModules( const wasmModules = getAllWasmModules(
this.compilation.moduleGraph,
this.compilation.chunkGraph, this.compilation.chunkGraph,
chunk chunk
); );

View File

@ -400,7 +400,7 @@ class WebAssemblyGenerator extends Generator {
let bin = /** @type {ArrayBuffer} */ (sourceAsAny); let bin = /** @type {ArrayBuffer} */ (sourceAsAny);
bin = preprocess(bin); bin = preprocess(bin);
const usedExports = module.getUsedExports(moduleGraph); const usedExports = moduleGraph.getUsedExports(module);
const initFuncId = t.identifier( const initFuncId = t.identifier(
usedExports && usedExports !== true usedExports && usedExports !== true
? Template.numberToIdentifer(usedExports.size) ? Template.numberToIdentifer(usedExports.size)

View File

@ -24,7 +24,7 @@ class WebAssemblyJavascriptGenerator extends Generator {
* @returns {Source} generated code * @returns {Source} generated code
*/ */
generate(module, { runtimeTemplate, moduleGraph }) { generate(module, { runtimeTemplate, moduleGraph }) {
const usedExports = module.getUsedExports(moduleGraph); const usedExports = moduleGraph.getUsedExports(module);
const initIdentifer = const initIdentifer =
usedExports && usedExports !== true usedExports && usedExports !== true
? Template.numberToIdentifer(usedExports.size) ? Template.numberToIdentifer(usedExports.size)

View File

@ -34,6 +34,7 @@ class WebAssemblyModulesPlugin {
compiler.hooks.compilation.tap( compiler.hooks.compilation.tap(
"WebAssemblyModulesPlugin", "WebAssemblyModulesPlugin",
(compilation, { normalModuleFactory }) => { (compilation, { normalModuleFactory }) => {
const moduleGraph = compilation.moduleGraph;
compilation.dependencyFactories.set( compilation.dependencyFactories.set(
WebAssemblyImportDependency, WebAssemblyImportDependency,
normalModuleFactory normalModuleFactory
@ -75,7 +76,7 @@ class WebAssemblyModulesPlugin {
for (const module of chunkGraph.getOrderedChunkModulesIterable( for (const module of chunkGraph.getOrderedChunkModulesIterable(
chunk, chunk,
compareModulesById compareModulesById(moduleGraph)
)) { )) {
if (module.type && module.type.startsWith("webassembly")) { if (module.type && module.type.startsWith("webassembly")) {
const filenameTemplate = const filenameTemplate =

View File

@ -25,13 +25,17 @@ class JsonpChunkTemplatePlugin {
* @returns {void} * @returns {void}
*/ */
apply(chunkTemplate) { apply(chunkTemplate) {
const moduleGraph = this.compilation.moduleGraph;
chunkTemplate.hooks.render.tap( chunkTemplate.hooks.render.tap(
"JsonpChunkTemplatePlugin", "JsonpChunkTemplatePlugin",
(modules, moduleTemplate, { chunk, chunkGraph }) => { (modules, moduleTemplate, { chunk, chunkGraph }) => {
const jsonpFunction = chunkTemplate.outputOptions.jsonpFunction; const jsonpFunction = chunkTemplate.outputOptions.jsonpFunction;
const globalObject = chunkTemplate.outputOptions.globalObject; const globalObject = chunkTemplate.outputOptions.globalObject;
const source = new ConcatSource(); const source = new ConcatSource();
const prefetchChunks = chunk.getChildIdsByOrders(chunkGraph).prefetch; const prefetchChunks = chunk.getChildIdsByOrders(
moduleGraph,
chunkGraph
).prefetch;
source.add( source.add(
`(${globalObject}[${JSON.stringify( `(${globalObject}[${JSON.stringify(
jsonpFunction jsonpFunction
@ -66,7 +70,9 @@ class JsonpChunkTemplatePlugin {
const chunkGraph = this.compilation.chunkGraph; const chunkGraph = this.compilation.chunkGraph;
hash.update(JSON.stringify(getEntryInfo(chunkGraph, chunk))); hash.update(JSON.stringify(getEntryInfo(chunkGraph, chunk)));
hash.update( 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} * @returns {void}
*/ */
apply(mainTemplate) { apply(mainTemplate) {
const moduleGraph = this.compilation.moduleGraph;
const needChunkOnDemandLoadingCode = chunk => { const needChunkOnDemandLoadingCode = chunk => {
for (const chunkGroup of chunk.groupsIterable) { for (const chunkGroup of chunk.groupsIterable) {
if (chunkGroup.getNumberOfChildren() > 0) return true; if (chunkGroup.getNumberOfChildren() > 0) return true;
@ -67,6 +68,7 @@ class JsonpMainTemplatePlugin {
}; };
const needPrefetchingCode = chunk => { const needPrefetchingCode = chunk => {
const allPrefetchChunks = chunk.getChildIdsByOrdersMap( const allPrefetchChunks = chunk.getChildIdsByOrdersMap(
moduleGraph,
this.compilation.chunkGraph, this.compilation.chunkGraph,
true true
).prefetch; ).prefetch;
@ -331,6 +333,7 @@ class JsonpMainTemplatePlugin {
}), }),
(source, chunkIdExpression, { chunk, hash }) => { (source, chunkIdExpression, { chunk, hash }) => {
const chunkMap = chunk.getChildIdsByOrdersMap( const chunkMap = chunk.getChildIdsByOrdersMap(
moduleGraph,
this.compilation.chunkGraph this.compilation.chunkGraph
).preload; ).preload;
if (!chunkMap || Object.keys(chunkMap).length === 0) return source; if (!chunkMap || Object.keys(chunkMap).length === 0) return source;
@ -514,7 +517,10 @@ class JsonpMainTemplatePlugin {
"JsonpMainTemplatePlugin", "JsonpMainTemplatePlugin",
(source, chunk, hash) => { (source, chunk, hash) => {
const chunkGraph = this.compilation.chunkGraph; const chunkGraph = this.compilation.chunkGraph;
const prefetchChunks = chunk.getChildIdsByOrders(chunkGraph).prefetch; const prefetchChunks = chunk.getChildIdsByOrders(
moduleGraph,
chunkGraph
).prefetch;
if ( if (
needChunkLoadingCode(chunk) && needChunkLoadingCode(chunk) &&
prefetchChunks && prefetchChunks &&

View File

@ -16,6 +16,7 @@ module.exports = {
* @returns {void} * @returns {void}
*/ */
const handler = compilation => { const handler = compilation => {
const moduleGraph = compilation.moduleGraph;
compilation.hooks.afterSeal.tap("testcase", () => { compilation.hooks.afterSeal.tap("testcase", () => {
const data = {}; const data = {};
for (const [name, group] of compilation.namedChunkGroups) { for (const [name, group] of compilation.namedChunkGroups) {
@ -69,20 +70,28 @@ module.exports = {
}); });
const indicies = compilation.modules const indicies = compilation.modules
.slice() .slice()
.sort((a, b) => a.index - b.index) .sort(
(a, b) =>
moduleGraph.getPreOrderIndex(a) -
moduleGraph.getPreOrderIndex(b)
)
.map( .map(
m => m =>
`${m.index}: ${m.readableIdentifier( `${moduleGraph.getPreOrderIndex(m)}: ${m.readableIdentifier(
compilation.requestShortener compilation.requestShortener
)}` )}`
) )
.join(", "); .join(", ");
const indicies2 = compilation.modules const indicies2 = compilation.modules
.slice() .slice()
.sort((a, b) => a.index2 - b.index2) .sort(
(a, b) =>
moduleGraph.getPostOrderIndex(a) -
moduleGraph.getPostOrderIndex(b)
)
.map( .map(
m => m =>
`${m.index2}: ${m.readableIdentifier( `${moduleGraph.getPostOrderIndex(m)}: ${m.readableIdentifier(
compilation.requestShortener compilation.requestShortener
)}` )}`
) )

View File

@ -11,13 +11,16 @@ module.exports = {
entry: "./entry" entry: "./entry"
}, },
plugins: [ plugins: [
new NamedChunksPlugin(function(chunk, { chunkGraph }) { new NamedChunksPlugin(function(chunk, { chunkGraph, moduleGraph }) {
if (chunk.name) { if (chunk.name) {
return chunk.name; return chunk.name;
} }
const chunkModulesToName = chunk => const chunkModulesToName = chunk =>
Array.from( Array.from(
chunkGraph.getOrderedChunkModulesIterable(chunk, compareModulesById), chunkGraph.getOrderedChunkModulesIterable(
chunk,
compareModulesById(moduleGraph)
),
mod => { mod => {
const rs = new RequestShortener(mod.context); const rs = new RequestShortener(mod.context);
return rs.shorten(mod.request).replace(/[./\\]/g, "_"); return rs.shorten(mod.request).replace(/[./\\]/g, "_");