aboutsummaryrefslogtreecommitdiff
path: root/node_modules/webpack/lib/Compilation.js
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2018-09-20 02:56:13 +0200
committerFlorian Dold <florian.dold@gmail.com>2018-09-20 02:56:13 +0200
commitbbff7403fbf46f9ad92240ac213df8d30ef31b64 (patch)
treec58400ec5124da1c7d56b01aea83309f80a56c3b /node_modules/webpack/lib/Compilation.js
parent003fb34971cf63466184351b4db5f7c67df4f444 (diff)
update packages
Diffstat (limited to 'node_modules/webpack/lib/Compilation.js')
-rw-r--r--node_modules/webpack/lib/Compilation.js3968
1 files changed, 2513 insertions, 1455 deletions
diff --git a/node_modules/webpack/lib/Compilation.js b/node_modules/webpack/lib/Compilation.js
index 05f5be60d..a420da696 100644
--- a/node_modules/webpack/lib/Compilation.js
+++ b/node_modules/webpack/lib/Compilation.js
@@ -1,1455 +1,2513 @@
-/*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Tobias Koppers @sokra
- */
-"use strict";
-
-const asyncLib = require("async");
-const crypto = require("crypto");
-const Tapable = require("tapable");
-const EntryModuleNotFoundError = require("./EntryModuleNotFoundError");
-const ModuleNotFoundError = require("./ModuleNotFoundError");
-const ModuleDependencyWarning = require("./ModuleDependencyWarning");
-const ModuleDependencyError = require("./ModuleDependencyError");
-const Module = require("./Module");
-const Chunk = require("./Chunk");
-const Entrypoint = require("./Entrypoint");
-const MainTemplate = require("./MainTemplate");
-const ChunkTemplate = require("./ChunkTemplate");
-const HotUpdateChunkTemplate = require("./HotUpdateChunkTemplate");
-const ModuleTemplate = require("./ModuleTemplate");
-const Dependency = require("./Dependency");
-const ChunkRenderError = require("./ChunkRenderError");
-const AsyncDependencyToInitialChunkWarning = require("./AsyncDependencyToInitialChunkWarning");
-const CachedSource = require("webpack-sources").CachedSource;
-const Stats = require("./Stats");
-const Semaphore = require("./util/Semaphore");
-const Queue = require("./util/Queue");
-
-function byId(a, b) {
- if(a.id < b.id) return -1;
- if(a.id > b.id) return 1;
- return 0;
-}
-
-function iterationBlockVariable(variables, fn) {
- for(let indexVariable = 0; indexVariable < variables.length; indexVariable++) {
- let varDep = variables[indexVariable].dependencies;
- for(let indexVDep = 0; indexVDep < varDep.length; indexVDep++) {
- fn(varDep[indexVDep]);
- }
- }
-}
-
-function iterationOfArrayCallback(arr, fn) {
- for(let index = 0; index < arr.length; index++) {
- fn(arr[index]);
- }
-}
-
-class Compilation extends Tapable {
- constructor(compiler) {
- super();
- this.compiler = compiler;
- this.resolvers = compiler.resolvers;
- this.inputFileSystem = compiler.inputFileSystem;
-
- const options = this.options = compiler.options;
- this.outputOptions = options && options.output;
- this.bail = options && options.bail;
- this.profile = options && options.profile;
- this.performance = options && options.performance;
-
- this.mainTemplate = new MainTemplate(this.outputOptions);
- this.chunkTemplate = new ChunkTemplate(this.outputOptions);
- this.hotUpdateChunkTemplate = new HotUpdateChunkTemplate(this.outputOptions);
- this.moduleTemplate = new ModuleTemplate(this.outputOptions);
-
- this.semaphore = new Semaphore(options.parallelism || 100);
-
- this.entries = [];
- this.preparedChunks = [];
- this.entrypoints = {};
- this.chunks = [];
- this.namedChunks = {};
- this.modules = [];
- this._modules = {};
- this.cache = null;
- this.records = null;
- this.nextFreeModuleIndex = undefined;
- this.nextFreeModuleIndex2 = undefined;
- this.additionalChunkAssets = [];
- this.assets = {};
- this.errors = [];
- this.warnings = [];
- this.children = [];
- this.dependencyFactories = new Map();
- this.dependencyTemplates = new Map();
- this.dependencyTemplates.set("hash", "");
- this.childrenCounters = {};
- }
-
- getStats() {
- return new Stats(this);
- }
-
- templatesPlugin(name, fn) {
- this.mainTemplate.plugin(name, fn);
- this.chunkTemplate.plugin(name, fn);
- }
-
- addModule(module, cacheGroup) {
- const identifier = module.identifier();
- if(this._modules[identifier]) {
- return false;
- }
- const cacheName = (cacheGroup || "m") + identifier;
- if(this.cache && this.cache[cacheName]) {
- const cacheModule = this.cache[cacheName];
-
- let rebuild = true;
- if(!cacheModule.error && cacheModule.cacheable && this.fileTimestamps && this.contextTimestamps) {
- rebuild = cacheModule.needRebuild(this.fileTimestamps, this.contextTimestamps);
- }
-
- if(!rebuild) {
- cacheModule.disconnect();
- this._modules[identifier] = cacheModule;
- this.modules.push(cacheModule);
- cacheModule.errors.forEach(err => this.errors.push(err), this);
- cacheModule.warnings.forEach(err => this.warnings.push(err), this);
- return cacheModule;
- }
- }
- module.unbuild();
- this._modules[identifier] = module;
- if(this.cache) {
- this.cache[cacheName] = module;
- }
- this.modules.push(module);
- return true;
- }
-
- getModule(module) {
- const identifier = module.identifier();
- return this._modules[identifier];
- }
-
- findModule(identifier) {
- return this._modules[identifier];
- }
-
- buildModule(module, optional, origin, dependencies, thisCallback) {
- this.applyPlugins1("build-module", module);
- if(module.building) return module.building.push(thisCallback);
- const building = module.building = [thisCallback];
-
- function callback(err) {
- module.building = undefined;
- building.forEach(cb => cb(err));
- }
- module.build(this.options, this, this.resolvers.normal, this.inputFileSystem, (error) => {
- const errors = module.errors;
- for(let indexError = 0; indexError < errors.length; indexError++) {
- const err = errors[indexError];
- err.origin = origin;
- err.dependencies = dependencies;
- if(optional)
- this.warnings.push(err);
- else
- this.errors.push(err);
- }
-
- const warnings = module.warnings;
- for(let indexWarning = 0; indexWarning < warnings.length; indexWarning++) {
- const war = warnings[indexWarning];
- war.origin = origin;
- war.dependencies = dependencies;
- this.warnings.push(war);
- }
- module.dependencies.sort(Dependency.compare);
- if(error) {
- this.applyPlugins2("failed-module", module, error);
- return callback(error);
- }
- this.applyPlugins1("succeed-module", module);
- return callback();
- });
- }
-
- processModuleDependencies(module, callback) {
- const dependencies = [];
-
- function addDependency(dep) {
- for(let i = 0; i < dependencies.length; i++) {
- if(dep.isEqualResource(dependencies[i][0])) {
- return dependencies[i].push(dep);
- }
- }
- dependencies.push([dep]);
- }
-
- function addDependenciesBlock(block) {
- if(block.dependencies) {
- iterationOfArrayCallback(block.dependencies, addDependency);
- }
- if(block.blocks) {
- iterationOfArrayCallback(block.blocks, addDependenciesBlock);
- }
- if(block.variables) {
- iterationBlockVariable(block.variables, addDependency);
- }
- }
- addDependenciesBlock(module);
- this.addModuleDependencies(module, dependencies, this.bail, null, true, callback);
- }
-
- addModuleDependencies(module, dependencies, bail, cacheGroup, recursive, callback) {
- let _this = this;
- const start = _this.profile && Date.now();
-
- const factories = [];
- for(let i = 0; i < dependencies.length; i++) {
- const factory = _this.dependencyFactories.get(dependencies[i][0].constructor);
- if(!factory) {
- return callback(new Error(`No module factory available for dependency type: ${dependencies[i][0].constructor.name}`));
- }
- factories[i] = [factory, dependencies[i]];
- }
- asyncLib.forEach(factories, function iteratorFactory(item, callback) {
- const dependencies = item[1];
-
- const errorAndCallback = function errorAndCallback(err) {
- err.origin = module;
- _this.errors.push(err);
- if(bail) {
- callback(err);
- } else {
- callback();
- }
- };
- const warningAndCallback = function warningAndCallback(err) {
- err.origin = module;
- _this.warnings.push(err);
- callback();
- };
-
- const semaphore = _this.semaphore;
- semaphore.acquire(() => {
- if(_this === null) return semaphore.release();
-
- const factory = item[0];
- factory.create({
- contextInfo: {
- issuer: module.nameForCondition && module.nameForCondition(),
- compiler: _this.compiler.name
- },
- context: module.context,
- dependencies: dependencies
- }, function factoryCallback(err, dependentModule) {
- if(_this === null) return semaphore.release();
-
- let afterFactory;
-
- function isOptional() {
- return dependencies.filter(d => !d.optional).length === 0;
- }
-
- function errorOrWarningAndCallback(err) {
- if(isOptional()) {
- return warningAndCallback(err);
- } else {
- return errorAndCallback(err);
- }
- }
-
- function iterationDependencies(depend) {
- for(let index = 0; index < depend.length; index++) {
- const dep = depend[index];
- dep.module = dependentModule;
- dependentModule.addReason(module, dep);
- }
- }
-
- if(err) {
- semaphore.release();
- return errorOrWarningAndCallback(new ModuleNotFoundError(module, err, dependencies));
- }
- if(!dependentModule) {
- semaphore.release();
- return process.nextTick(callback);
- }
- if(_this.profile) {
- if(!dependentModule.profile) {
- dependentModule.profile = {};
- }
- afterFactory = Date.now();
- dependentModule.profile.factory = afterFactory - start;
- }
-
- dependentModule.issuer = module;
- const newModule = _this.addModule(dependentModule, cacheGroup);
-
- if(!newModule) { // from cache
- dependentModule = _this.getModule(dependentModule);
-
- if(dependentModule.optional) {
- dependentModule.optional = isOptional();
- }
-
- iterationDependencies(dependencies);
-
- if(_this.profile) {
- if(!module.profile) {
- module.profile = {};
- }
- const time = Date.now() - start;
- if(!module.profile.dependencies || time > module.profile.dependencies) {
- module.profile.dependencies = time;
- }
- }
-
- semaphore.release();
- return process.nextTick(callback);
- }
-
- if(newModule instanceof Module) {
- if(_this.profile) {
- newModule.profile = dependentModule.profile;
- }
-
- newModule.optional = isOptional();
- newModule.issuer = dependentModule.issuer;
- dependentModule = newModule;
-
- iterationDependencies(dependencies);
-
- if(_this.profile) {
- const afterBuilding = Date.now();
- module.profile.building = afterBuilding - afterFactory;
- }
-
- semaphore.release();
- if(recursive) {
- return process.nextTick(_this.processModuleDependencies.bind(_this, dependentModule, callback));
- } else {
- return process.nextTick(callback);
- }
- }
-
- dependentModule.optional = isOptional();
-
- iterationDependencies(dependencies);
-
- _this.buildModule(dependentModule, isOptional(), module, dependencies, err => {
- if(_this === null) return semaphore.release();
-
- if(err) {
- semaphore.release();
- return errorOrWarningAndCallback(err);
- }
-
- if(_this.profile) {
- const afterBuilding = Date.now();
- dependentModule.profile.building = afterBuilding - afterFactory;
- }
-
- semaphore.release();
- if(recursive) {
- _this.processModuleDependencies(dependentModule, callback);
- } else {
- return callback();
- }
- });
-
- });
- });
- }, function finalCallbackAddModuleDependencies(err) {
- // In V8, the Error objects keep a reference to the functions on the stack. These warnings &
- // errors are created inside closures that keep a reference to the Compilation, so errors are
- // leaking the Compilation object. Setting _this to null workarounds the following issue in V8.
- // https://bugs.chromium.org/p/chromium/issues/detail?id=612191
- _this = null;
-
- if(err) {
- return callback(err);
- }
-
- return process.nextTick(callback);
- });
- }
-
- _addModuleChain(context, dependency, onModule, callback) {
- const start = this.profile && Date.now();
-
- const errorAndCallback = this.bail ? (err) => {
- callback(err);
- } : (err) => {
- err.dependencies = [dependency];
- this.errors.push(err);
- callback();
- };
-
- if(typeof dependency !== "object" || dependency === null || !dependency.constructor) {
- throw new Error("Parameter 'dependency' must be a Dependency");
- }
-
- const moduleFactory = this.dependencyFactories.get(dependency.constructor);
- if(!moduleFactory) {
- throw new Error(`No dependency factory available for this dependency type: ${dependency.constructor.name}`);
- }
-
- this.semaphore.acquire(() => {
- moduleFactory.create({
- contextInfo: {
- issuer: "",
- compiler: this.compiler.name
- },
- context: context,
- dependencies: [dependency]
- }, (err, module) => {
- if(err) {
- this.semaphore.release();
- return errorAndCallback(new EntryModuleNotFoundError(err));
- }
-
- let afterFactory;
-
- if(this.profile) {
- if(!module.profile) {
- module.profile = {};
- }
- afterFactory = Date.now();
- module.profile.factory = afterFactory - start;
- }
-
- const result = this.addModule(module);
- if(!result) {
- module = this.getModule(module);
-
- onModule(module);
-
- if(this.profile) {
- const afterBuilding = Date.now();
- module.profile.building = afterBuilding - afterFactory;
- }
-
- this.semaphore.release();
- return callback(null, module);
- }
-
- if(result instanceof Module) {
- if(this.profile) {
- result.profile = module.profile;
- }
-
- module = result;
-
- onModule(module);
-
- moduleReady.call(this);
- return;
- }
-
- onModule(module);
-
- this.buildModule(module, false, null, null, (err) => {
- if(err) {
- this.semaphore.release();
- return errorAndCallback(err);
- }
-
- if(this.profile) {
- const afterBuilding = Date.now();
- module.profile.building = afterBuilding - afterFactory;
- }
-
- moduleReady.call(this);
- });
-
- function moduleReady() {
- this.semaphore.release();
- this.processModuleDependencies(module, err => {
- if(err) {
- return callback(err);
- }
-
- return callback(null, module);
- });
- }
- });
- });
- }
-
- addEntry(context, entry, name, callback) {
- const slot = {
- name: name,
- module: null
- };
- this.preparedChunks.push(slot);
- this._addModuleChain(context, entry, (module) => {
-
- entry.module = module;
- this.entries.push(module);
- module.issuer = null;
-
- }, (err, module) => {
- if(err) {
- return callback(err);
- }
-
- if(module) {
- slot.module = module;
- } else {
- const idx = this.preparedChunks.indexOf(slot);
- this.preparedChunks.splice(idx, 1);
- }
- return callback(null, module);
- });
- }
-
- prefetch(context, dependency, callback) {
- this._addModuleChain(context, dependency, module => {
-
- module.prefetched = true;
- module.issuer = null;
-
- }, callback);
- }
-
- rebuildModule(module, thisCallback) {
- if(module.variables.length || module.blocks.length)
- throw new Error("Cannot rebuild a complex module with variables or blocks");
- if(module.rebuilding) {
- return module.rebuilding.push(thisCallback);
- }
- const rebuilding = module.rebuilding = [thisCallback];
-
- function callback(err) {
- module.rebuilding = undefined;
- rebuilding.forEach(cb => cb(err));
- }
- const deps = module.dependencies.slice();
- this.buildModule(module, false, module, null, (err) => {
- if(err) return callback(err);
-
- this.processModuleDependencies(module, (err) => {
- if(err) return callback(err);
- deps.forEach(d => {
- if(d.module && d.module.removeReason(module, d)) {
- module.forEachChunk(chunk => {
- if(!d.module.hasReasonForChunk(chunk)) {
- if(d.module.removeChunk(chunk)) {
- this.removeChunkFromDependencies(d.module, chunk);
- }
- }
- });
- }
- });
- callback();
- });
-
- });
- }
-
- finish() {
- const modules = this.modules;
- this.applyPlugins1("finish-modules", modules);
-
- for(let index = 0; index < modules.length; index++) {
- const module = modules[index];
- this.reportDependencyErrorsAndWarnings(module, [module]);
- }
- }
-
- unseal() {
- this.applyPlugins0("unseal");
- this.chunks.length = 0;
- this.namedChunks = {};
- this.additionalChunkAssets.length = 0;
- this.assets = {};
- this.modules.forEach(module => module.unseal());
- }
-
- seal(callback) {
- const self = this;
- self.applyPlugins0("seal");
- self.nextFreeModuleIndex = 0;
- self.nextFreeModuleIndex2 = 0;
- self.preparedChunks.forEach(preparedChunk => {
- const module = preparedChunk.module;
- const chunk = self.addChunk(preparedChunk.name, module);
- const entrypoint = self.entrypoints[chunk.name] = new Entrypoint(chunk.name);
- entrypoint.unshiftChunk(chunk);
-
- chunk.addModule(module);
- module.addChunk(chunk);
- chunk.entryModule = module;
- self.assignIndex(module);
- self.assignDepth(module);
- });
- self.processDependenciesBlocksForChunks(self.chunks.slice());
- self.sortModules(self.modules);
- self.applyPlugins0("optimize");
-
- while(self.applyPluginsBailResult1("optimize-modules-basic", self.modules) ||
- self.applyPluginsBailResult1("optimize-modules", self.modules) ||
- self.applyPluginsBailResult1("optimize-modules-advanced", self.modules)) { /* empty */ }
- self.applyPlugins1("after-optimize-modules", self.modules);
-
- while(self.applyPluginsBailResult1("optimize-chunks-basic", self.chunks) ||
- self.applyPluginsBailResult1("optimize-chunks", self.chunks) ||
- self.applyPluginsBailResult1("optimize-chunks-advanced", self.chunks)) { /* empty */ }
- self.applyPlugins1("after-optimize-chunks", self.chunks);
-
- self.applyPluginsAsyncSeries("optimize-tree", self.chunks, self.modules, function sealPart2(err) {
- if(err) {
- return callback(err);
- }
-
- self.applyPlugins2("after-optimize-tree", self.chunks, self.modules);
-
- while(self.applyPluginsBailResult("optimize-chunk-modules-basic", self.chunks, self.modules) ||
- self.applyPluginsBailResult("optimize-chunk-modules", self.chunks, self.modules) ||
- self.applyPluginsBailResult("optimize-chunk-modules-advanced", self.chunks, self.modules)) { /* empty */ }
- self.applyPlugins2("after-optimize-chunk-modules", self.chunks, self.modules);
-
- const shouldRecord = self.applyPluginsBailResult("should-record") !== false;
-
- self.applyPlugins2("revive-modules", self.modules, self.records);
- self.applyPlugins1("optimize-module-order", self.modules);
- self.applyPlugins1("advanced-optimize-module-order", self.modules);
- self.applyPlugins1("before-module-ids", self.modules);
- self.applyPlugins1("module-ids", self.modules);
- self.applyModuleIds();
- self.applyPlugins1("optimize-module-ids", self.modules);
- self.applyPlugins1("after-optimize-module-ids", self.modules);
-
- self.sortItemsWithModuleIds();
-
- self.applyPlugins2("revive-chunks", self.chunks, self.records);
- self.applyPlugins1("optimize-chunk-order", self.chunks);
- self.applyPlugins1("before-chunk-ids", self.chunks);
- self.applyChunkIds();
- self.applyPlugins1("optimize-chunk-ids", self.chunks);
- self.applyPlugins1("after-optimize-chunk-ids", self.chunks);
-
- self.sortItemsWithChunkIds();
-
- if(shouldRecord)
- self.applyPlugins2("record-modules", self.modules, self.records);
- if(shouldRecord)
- self.applyPlugins2("record-chunks", self.chunks, self.records);
-
- self.applyPlugins0("before-hash");
- self.createHash();
- self.applyPlugins0("after-hash");
-
- if(shouldRecord)
- self.applyPlugins1("record-hash", self.records);
-
- self.applyPlugins0("before-module-assets");
- self.createModuleAssets();
- if(self.applyPluginsBailResult("should-generate-chunk-assets") !== false) {
- self.applyPlugins0("before-chunk-assets");
- self.createChunkAssets();
- }
- self.applyPlugins1("additional-chunk-assets", self.chunks);
- self.summarizeDependencies();
- if(shouldRecord)
- self.applyPlugins2("record", self, self.records);
-
- self.applyPluginsAsync("additional-assets", err => {
- if(err) {
- return callback(err);
- }
- self.applyPluginsAsync("optimize-chunk-assets", self.chunks, err => {
- if(err) {
- return callback(err);
- }
- self.applyPlugins1("after-optimize-chunk-assets", self.chunks);
- self.applyPluginsAsync("optimize-assets", self.assets, err => {
- if(err) {
- return callback(err);
- }
- self.applyPlugins1("after-optimize-assets", self.assets);
- if(self.applyPluginsBailResult("need-additional-seal")) {
- self.unseal();
- return self.seal(callback);
- }
- return self.applyPluginsAsync("after-seal", callback);
- });
- });
- });
- });
- }
-
- sortModules(modules) {
- modules.sort((a, b) => {
- if(a.index < b.index) return -1;
- if(a.index > b.index) return 1;
- return 0;
- });
- }
-
- reportDependencyErrorsAndWarnings(module, blocks) {
- for(let indexBlock = 0; indexBlock < blocks.length; indexBlock++) {
- const block = blocks[indexBlock];
- const dependencies = block.dependencies;
-
- for(let indexDep = 0; indexDep < dependencies.length; indexDep++) {
- const d = dependencies[indexDep];
-
- const warnings = d.getWarnings();
- if(warnings) {
- for(let indexWar = 0; indexWar < warnings.length; indexWar++) {
- const w = warnings[indexWar];
-
- const warning = new ModuleDependencyWarning(module, w, d.loc);
- this.warnings.push(warning);
- }
- }
- const errors = d.getErrors();
- if(errors) {
- for(let indexErr = 0; indexErr < errors.length; indexErr++) {
- const e = errors[indexErr];
-
- const error = new ModuleDependencyError(module, e, d.loc);
- this.errors.push(error);
- }
- }
- }
-
- this.reportDependencyErrorsAndWarnings(module, block.blocks);
- }
- }
-
- addChunk(name, module, loc) {
- if(name) {
- if(Object.prototype.hasOwnProperty.call(this.namedChunks, name)) {
- const chunk = this.namedChunks[name];
- if(module) {
- chunk.addOrigin(module, loc);
- }
- return chunk;
- }
- }
- const chunk = new Chunk(name, module, loc);
- this.chunks.push(chunk);
- if(name) {
- this.namedChunks[name] = chunk;
- }
- return chunk;
- }
-
- assignIndex(module) {
- const _this = this;
-
- const queue = [() => {
- assignIndexToModule(module);
- }];
-
- const iteratorAllDependencies = d => {
- queue.push(() => assignIndexToDependency(d));
- };
-
- function assignIndexToModule(module) {
- // enter module
- if(typeof module.index !== "number") {
- module.index = _this.nextFreeModuleIndex++;
-
- // leave module
- queue.push(() => module.index2 = _this.nextFreeModuleIndex2++);
-
- // enter it as block
- assignIndexToDependencyBlock(module);
- }
- }
-
- function assignIndexToDependency(dependency) {
- if(dependency.module) {
- queue.push(() => assignIndexToModule(dependency.module));
- }
- }
-
- function assignIndexToDependencyBlock(block) {
- let allDependencies = [];
-
- function iteratorDependency(d) {
- allDependencies.push(d);
- }
-
- function iteratorBlock(b) {
- queue.push(() => assignIndexToDependencyBlock(b));
- }
-
- if(block.variables) {
- iterationBlockVariable(block.variables, iteratorDependency);
- }
-
- if(block.dependencies) {
- iterationOfArrayCallback(block.dependencies, iteratorDependency);
- }
- if(block.blocks) {
- const blocks = block.blocks;
- let indexBlock = blocks.length;
- while(indexBlock--) {
- iteratorBlock(blocks[indexBlock]);
- }
- }
-
- let indexAll = allDependencies.length;
- while(indexAll--) {
- iteratorAllDependencies(allDependencies[indexAll]);
- }
- }
-
- while(queue.length) {
- queue.pop()();
- }
- }
-
- assignDepth(module) {
- function assignDepthToModule(module, depth) {
- // enter module
- if(typeof module.depth === "number" && module.depth <= depth) return;
- module.depth = depth;
-
- // enter it as block
- assignDepthToDependencyBlock(module, depth + 1);
- }
-
- function assignDepthToDependency(dependency, depth) {
- if(dependency.module) {
- queue.push(() => assignDepthToModule(dependency.module, depth));
- }
- }
-
- function assignDepthToDependencyBlock(block, depth) {
- function iteratorDependency(d) {
- assignDepthToDependency(d, depth);
- }
-
- function iteratorBlock(b) {
- assignDepthToDependencyBlock(b, depth);
- }
-
- if(block.variables) {
- iterationBlockVariable(block.variables, iteratorDependency);
- }
-
- if(block.dependencies) {
- iterationOfArrayCallback(block.dependencies, iteratorDependency);
- }
-
- if(block.blocks) {
- iterationOfArrayCallback(block.blocks, iteratorBlock);
- }
- }
-
- const queue = [() => {
- assignDepthToModule(module, 0);
- }];
- while(queue.length) {
- queue.pop()();
- }
- }
-
- // This method creates the Chunk graph from the Module graph
- processDependenciesBlocksForChunks(inputChunks) {
- // Process is splitting into two parts:
- // Part one traverse the module graph and builds a very basic chunks graph
- // in chunkDependencies.
- // Part two traverse every possible way through the basic chunk graph and
- // tracks the available modules. While traversing it connects chunks with
- // 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 chunkDependencies = new Map(); // Map<Chunk, Array<{Module, Chunk}>>
- const allCreatedChunks = new Set();
-
- // PART ONE
-
- const blockChunks = new Map();
-
- // Start with the provided modules/chunks
- const queue = inputChunks.map(chunk => ({
- block: chunk.entryModule,
- chunk: chunk
- }));
-
- let block, chunk;
-
- // For each async Block in graph
- const iteratorBlock = b => {
- // 1. We create a chunk for this Block
- // but only once (blockChunks map)
- let c = blockChunks.get(b);
- if(c === undefined) {
- c = this.namedChunks[b.chunkName];
- if(c && c.isInitial()) {
- // TODO webpack 4: convert this to an error
- this.warnings.push(new AsyncDependencyToInitialChunkWarning(b.chunkName, b.module, b.loc));
- c = chunk;
- } else {
- c = this.addChunk(b.chunkName, b.module, b.loc);
- blockChunks.set(b, c);
- allCreatedChunks.add(c);
- // We initialize the chunks property
- // this is later filled with the chunk when needed
- b.chunks = [];
- }
- }
-
- // 2. We store the Block+Chunk mapping as dependency for the chunk
- let deps = chunkDependencies.get(chunk);
- if(!deps) chunkDependencies.set(chunk, deps = []);
- deps.push({
- block: b,
- chunk: c
- });
-
- // 3. We enqueue the DependenciesBlock for traversal
- queue.push({
- block: b,
- chunk: c
- });
- };
-
- // For each Dependency in the graph
- const iteratorDependency = d => {
- // We skip Dependencies without Module pointer
- if(!d.module) {
- return;
- }
- // We skip weak Dependencies
- if(d.weak) {
- return;
- }
- // We connect Module and Chunk when not already done
- if(chunk.addModule(d.module)) {
- d.module.addChunk(chunk);
-
- // And enqueue the Module for traversal
- queue.push({
- block: d.module,
- chunk
- });
- }
- };
-
- // Iterative traversal of the Module graph
- // Recursive would be simpler to write but could result in Stack Overflows
- while(queue.length) {
- const queueItem = queue.pop();
- block = queueItem.block;
- chunk = queueItem.chunk;
-
- // Traverse all variables, Dependencies and Blocks
- if(block.variables) {
- iterationBlockVariable(block.variables, iteratorDependency);
- }
-
- if(block.dependencies) {
- iterationOfArrayCallback(block.dependencies, iteratorDependency);
- }
-
- if(block.blocks) {
- iterationOfArrayCallback(block.blocks, iteratorBlock);
- }
- }
-
- // PART TWO
-
- let availableModules;
- let newAvailableModules;
- const queue2 = new Queue(inputChunks.map(chunk => ({
- chunk,
- availableModules: new Set()
- })));
-
- // Helper function to check if all modules of a chunk are available
- const areModulesAvailable = (chunk, availableModules) => {
- for(const module of chunk.modulesIterable) {
- if(!availableModules.has(module))
- return false;
- }
- return true;
- };
-
- // For each edge in the basic chunk graph
- const filterFn = dep => {
- // Filter egdes that are not needed because all modules are already available
- // This also filters circular dependencies in the chunks graph
- const depChunk = dep.chunk;
- if(areModulesAvailable(depChunk, newAvailableModules))
- return false; // break all modules are already available
- return true;
- };
-
- const minAvailableModulesMap = new Map();
-
- // Iterative traversing of the basic chunk graph
- while(queue2.length) {
- const queueItem = queue2.dequeue();
- chunk = queueItem.chunk;
- availableModules = queueItem.availableModules;
-
- // 1. Get minimal available modules
- // It doesn't make sense to traverse a chunk again with more available modules.
- // This step calculates the minimal available modules and skips traversal when
- // the list didn't shrink.
- let minAvailableModules = minAvailableModulesMap.get(chunk);
- if(minAvailableModules === undefined) {
- minAvailableModulesMap.set(chunk, new Set(availableModules));
- } else {
- let deletedModules = false;
- for(const m of minAvailableModules) {
- if(!availableModules.has(m)) {
- minAvailableModules.delete(m);
- deletedModules = true;
- }
- }
- if(!deletedModules)
- continue;
- availableModules = minAvailableModules;
- }
-
- // 2. Get the edges at this point of the graph
- const deps = chunkDependencies.get(chunk);
- if(!deps) continue;
- if(deps.length === 0) continue;
-
- // 3. Create a new Set of available modules at this points
- newAvailableModules = new Set(availableModules);
- for(const m of chunk.modulesIterable)
- newAvailableModules.add(m);
-
- // 4. Filter edges with available modules
- const filteredDeps = deps.filter(filterFn);
-
- // 5. Foreach remaining edge
- const nextChunks = new Set();
- for(let i = 0; i < filteredDeps.length; i++) {
- const dep = filteredDeps[i];
- const depChunk = dep.chunk;
- const depBlock = dep.block;
-
- // 6. Connnect block with chunk
- if(depChunk.addBlock(depBlock)) {
- depBlock.chunks.push(depChunk);
- }
-
- // 7. Connect chunk with parent
- if(chunk.addChunk(depChunk)) {
- depChunk.addParent(chunk);
- }
-
- nextChunks.add(depChunk);
- }
-
- // 8. Enqueue further traversal
- for(const nextChunk of nextChunks) {
- queue2.enqueue({
- chunk: nextChunk,
- availableModules: newAvailableModules
- });
- }
- }
-
- // Remove all unconnected chunks
- for(const chunk of allCreatedChunks) {
- if(chunk.parents.length === 0)
- chunk.remove("unconnected");
- }
- }
-
- removeChunkFromDependencies(block, chunk) {
- const iteratorDependency = d => {
- if(!d.module) {
- return;
- }
- if(!d.module.hasReasonForChunk(chunk)) {
- if(d.module.removeChunk(chunk)) {
- this.removeChunkFromDependencies(d.module, chunk);
- }
- }
- };
-
- const blocks = block.blocks;
- for(let indexBlock = 0; indexBlock < blocks.length; indexBlock++) {
- const chunks = blocks[indexBlock].chunks;
- for(let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
- const blockChunk = chunks[indexChunk];
- chunk.removeChunk(blockChunk);
- blockChunk.removeParent(chunk);
- this.removeChunkFromDependencies(chunks, blockChunk);
- }
- }
-
- if(block.dependencies) {
- iterationOfArrayCallback(block.dependencies, iteratorDependency);
- }
-
- if(block.variables) {
- iterationBlockVariable(block.variables, iteratorDependency);
- }
- }
-
- applyModuleIds() {
- let unusedIds = [];
- let nextFreeModuleId = 0;
- let usedIds = [];
- // TODO consider Map when performance has improved https://gist.github.com/sokra/234c077e1299b7369461f1708519c392
- const usedIdMap = Object.create(null);
- if(this.usedModuleIds) {
- Object.keys(this.usedModuleIds).forEach(key => {
- const id = this.usedModuleIds[key];
- if(!usedIdMap[id]) {
- usedIds.push(id);
- usedIdMap[id] = true;
- }
- });
- }
-
- const modules1 = this.modules;
- for(let indexModule1 = 0; indexModule1 < modules1.length; indexModule1++) {
- const module1 = modules1[indexModule1];
- if(module1.id && !usedIdMap[module1.id]) {
- usedIds.push(module1.id);
- usedIdMap[module1.id] = true;
- }
- }
-
- if(usedIds.length > 0) {
- let usedIdMax = -1;
- for(let index = 0; index < usedIds.length; index++) {
- const usedIdKey = usedIds[index];
-
- if(typeof usedIdKey !== "number") {
- continue;
- }
-
- usedIdMax = Math.max(usedIdMax, usedIdKey);
- }
-
- let lengthFreeModules = nextFreeModuleId = usedIdMax + 1;
-
- while(lengthFreeModules--) {
- if(!usedIdMap[lengthFreeModules]) {
- unusedIds.push(lengthFreeModules);
- }
- }
- }
-
- const modules2 = this.modules;
- for(let indexModule2 = 0; indexModule2 < modules2.length; indexModule2++) {
- const module2 = modules2[indexModule2];
- if(module2.id === null) {
- if(unusedIds.length > 0)
- module2.id = unusedIds.pop();
- else
- module2.id = nextFreeModuleId++;
- }
- }
- }
-
- applyChunkIds() {
- const unusedIds = [];
- let nextFreeChunkId = 0;
-
- function getNextFreeChunkId(usedChunkIds) {
- const keyChunks = Object.keys(usedChunkIds);
- let result = -1;
-
- for(let index = 0; index < keyChunks.length; index++) {
- const usedIdKey = keyChunks[index];
- const usedIdValue = usedChunkIds[usedIdKey];
-
- if(typeof usedIdValue !== "number") {
- continue;
- }
-
- result = Math.max(result, usedIdValue);
- }
-
- return result;
- }
-
- if(this.usedChunkIds) {
- nextFreeChunkId = getNextFreeChunkId(this.usedChunkIds) + 1;
- let index = nextFreeChunkId;
- while(index--) {
- if(this.usedChunkIds[index] !== index) {
- unusedIds.push(index);
- }
- }
- }
-
- const chunks = this.chunks;
- for(let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
- const chunk = chunks[indexChunk];
- if(chunk.id === null) {
- if(unusedIds.length > 0)
- chunk.id = unusedIds.pop();
- else
- chunk.id = nextFreeChunkId++;
- }
- if(!chunk.ids) {
- chunk.ids = [chunk.id];
- }
- }
- }
-
- sortItemsWithModuleIds() {
- this.modules.sort(byId);
-
- const modules = this.modules;
- for(let indexModule = 0; indexModule < modules.length; indexModule++) {
- modules[indexModule].sortItems(false);
- }
-
- const chunks = this.chunks;
- for(let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
- chunks[indexChunk].sortItems();
- }
- }
-
- sortItemsWithChunkIds() {
- this.chunks.sort(byId);
-
- const modules = this.modules;
- for(let indexModule = 0; indexModule < modules.length; indexModule++) {
- modules[indexModule].sortItems(true);
- }
-
- const chunks = this.chunks;
- for(let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
- chunks[indexChunk].sortItems();
- }
-
- const byMessage = (a, b) => {
- const ma = `${a.message}`;
- const mb = `${b.message}`;
- if(ma < mb) return -1;
- if(mb < ma) return 1;
- return 0;
- };
-
- this.errors.sort(byMessage);
- this.warnings.sort(byMessage);
- }
-
- summarizeDependencies() {
- function filterDups(array) {
- const newArray = [];
- for(let i = 0; i < array.length; i++) {
- if(i === 0 || array[i - 1] !== array[i])
- newArray.push(array[i]);
- }
- return newArray;
- }
- this.fileDependencies = (this.compilationDependencies || []).slice();
- this.contextDependencies = [];
- this.missingDependencies = [];
-
- const children = this.children;
- for(let indexChildren = 0; indexChildren < children.length; indexChildren++) {
- const child = children[indexChildren];
-
- this.fileDependencies = this.fileDependencies.concat(child.fileDependencies);
- this.contextDependencies = this.contextDependencies.concat(child.contextDependencies);
- this.missingDependencies = this.missingDependencies.concat(child.missingDependencies);
- }
-
- const modules = this.modules;
- for(let indexModule = 0; indexModule < modules.length; indexModule++) {
- const module = modules[indexModule];
-
- if(module.fileDependencies) {
- const fileDependencies = module.fileDependencies;
- for(let indexFileDep = 0; indexFileDep < fileDependencies.length; indexFileDep++) {
- this.fileDependencies.push(fileDependencies[indexFileDep]);
- }
- }
- if(module.contextDependencies) {
- const contextDependencies = module.contextDependencies;
- for(let indexContextDep = 0; indexContextDep < contextDependencies.length; indexContextDep++) {
- this.contextDependencies.push(contextDependencies[indexContextDep]);
- }
- }
- }
- this.errors.forEach(error => {
- if(Array.isArray(error.missing)) {
- error.missing.forEach(item => this.missingDependencies.push(item));
- }
- });
- this.fileDependencies.sort();
- this.fileDependencies = filterDups(this.fileDependencies);
- this.contextDependencies.sort();
- this.contextDependencies = filterDups(this.contextDependencies);
- this.missingDependencies.sort();
- this.missingDependencies = filterDups(this.missingDependencies);
- }
-
- createHash() {
- const outputOptions = this.outputOptions;
- const hashFunction = outputOptions.hashFunction;
- const hashDigest = outputOptions.hashDigest;
- const hashDigestLength = outputOptions.hashDigestLength;
- const hash = crypto.createHash(hashFunction);
- if(outputOptions.hashSalt)
- hash.update(outputOptions.hashSalt);
- this.mainTemplate.updateHash(hash);
- this.chunkTemplate.updateHash(hash);
- this.moduleTemplate.updateHash(hash);
- this.children.forEach(function(child) {
- hash.update(child.hash);
- });
- this.warnings.forEach(function(warning) {
- hash.update(`${warning.message}`);
- });
- this.errors.forEach(function(error) {
- hash.update(`${error.message}`);
- });
- // clone needed as sort below is inplace mutation
- const chunks = this.chunks.slice();
- /**
- * sort here will bring all "falsy" values to the beginning
- * this is needed as the "hasRuntime()" chunks are dependent on the
- * hashes of the non-runtime chunks.
- */
- chunks.sort((a, b) => {
- const aEntry = a.hasRuntime();
- const bEntry = b.hasRuntime();
- if(aEntry && !bEntry) return 1;
- if(!aEntry && bEntry) return -1;
- return 0;
- });
- for(let i = 0; i < chunks.length; i++) {
- const chunk = chunks[i];
- const chunkHash = crypto.createHash(hashFunction);
- if(outputOptions.hashSalt)
- chunkHash.update(outputOptions.hashSalt);
- chunk.updateHash(chunkHash);
- if(chunk.hasRuntime()) {
- this.mainTemplate.updateHashForChunk(chunkHash, chunk);
- } else {
- this.chunkTemplate.updateHashForChunk(chunkHash, chunk);
- }
- this.applyPlugins2("chunk-hash", chunk, chunkHash);
- chunk.hash = chunkHash.digest(hashDigest);
- hash.update(chunk.hash);
- chunk.renderedHash = chunk.hash.substr(0, hashDigestLength);
- }
- this.fullHash = hash.digest(hashDigest);
- this.hash = this.fullHash.substr(0, hashDigestLength);
- }
-
- modifyHash(update) {
- const outputOptions = this.outputOptions;
- const hashFunction = outputOptions.hashFunction;
- const hashDigest = outputOptions.hashDigest;
- const hashDigestLength = outputOptions.hashDigestLength;
- const hash = crypto.createHash(hashFunction);
- hash.update(this.fullHash);
- hash.update(update);
- this.fullHash = hash.digest(hashDigest);
- this.hash = this.fullHash.substr(0, hashDigestLength);
- }
-
- createModuleAssets() {
- for(let i = 0; i < this.modules.length; i++) {
- const module = this.modules[i];
- if(module.assets) {
- Object.keys(module.assets).forEach((assetName) => {
- const fileName = this.getPath(assetName);
- this.assets[fileName] = module.assets[assetName];
- this.applyPlugins2("module-asset", module, fileName);
- });
- }
- }
- }
-
- createChunkAssets() {
- const outputOptions = this.outputOptions;
- const filename = outputOptions.filename;
- const chunkFilename = outputOptions.chunkFilename;
- for(let i = 0; i < this.chunks.length; i++) {
- const chunk = this.chunks[i];
- chunk.files = [];
- const chunkHash = chunk.hash;
- let source;
- let file;
- const filenameTemplate = chunk.filenameTemplate ? chunk.filenameTemplate :
- chunk.isInitial() ? filename :
- chunkFilename;
- try {
- const useChunkHash = !chunk.hasRuntime() || (this.mainTemplate.useChunkHash && this.mainTemplate.useChunkHash(chunk));
- const usedHash = useChunkHash ? chunkHash : this.fullHash;
- const cacheName = "c" + chunk.id;
- if(this.cache && this.cache[cacheName] && this.cache[cacheName].hash === usedHash) {
- source = this.cache[cacheName].source;
- } else {
- if(chunk.hasRuntime()) {
- source = this.mainTemplate.render(this.hash, chunk, this.moduleTemplate, this.dependencyTemplates);
- } else {
- source = this.chunkTemplate.render(chunk, this.moduleTemplate, this.dependencyTemplates);
- }
- if(this.cache) {
- this.cache[cacheName] = {
- hash: usedHash,
- source: source = (source instanceof CachedSource ? source : new CachedSource(source))
- };
- }
- }
- file = this.getPath(filenameTemplate, {
- noChunkHash: !useChunkHash,
- chunk
- });
- if(this.assets[file])
- throw new Error(`Conflict: Multiple assets emit to the same filename ${file}`);
- this.assets[file] = source;
- chunk.files.push(file);
- this.applyPlugins2("chunk-asset", chunk, file);
- } catch(err) {
- this.errors.push(new ChunkRenderError(chunk, file || filenameTemplate, err));
- }
- }
- }
-
- getPath(filename, data) {
- data = data || {};
- data.hash = data.hash || this.hash;
- return this.mainTemplate.applyPluginsWaterfall("asset-path", filename, data);
- }
-
- createChildCompiler(name, outputOptions, plugins) {
- const idx = (this.childrenCounters[name] || 0);
- this.childrenCounters[name] = idx + 1;
- return this.compiler.createChildCompiler(this, name, idx, outputOptions, plugins);
- }
-
- checkConstraints() {
- const usedIds = {};
-
- const modules = this.modules;
- for(let indexModule = 0; indexModule < modules.length; indexModule++) {
- const moduleId = modules[indexModule].id;
-
- if(usedIds[moduleId])
- throw new Error(`checkConstraints: duplicate module id ${moduleId}`);
- }
-
- const chunks = this.chunks;
- for(let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
- const chunk = chunks[indexChunk];
-
- if(chunks.indexOf(chunk) !== indexChunk)
- throw new Error(`checkConstraints: duplicate chunk in compilation ${chunk.debugId}`);
- chunk.checkConstraints();
- }
- }
-}
-
-module.exports = Compilation;
+/*
+ MIT License http://www.opensource.org/licenses/mit-license.php
+ Author Tobias Koppers @sokra
+ */
+"use strict";
+
+const asyncLib = require("neo-async");
+const util = require("util");
+const { CachedSource } = require("webpack-sources");
+const {
+ Tapable,
+ SyncHook,
+ SyncBailHook,
+ SyncWaterfallHook,
+ AsyncSeriesHook
+} = require("tapable");
+const EntryModuleNotFoundError = require("./EntryModuleNotFoundError");
+const ModuleNotFoundError = require("./ModuleNotFoundError");
+const ModuleDependencyWarning = require("./ModuleDependencyWarning");
+const ModuleDependencyError = require("./ModuleDependencyError");
+const ChunkGroup = require("./ChunkGroup");
+const Chunk = require("./Chunk");
+const Entrypoint = require("./Entrypoint");
+const MainTemplate = require("./MainTemplate");
+const ChunkTemplate = require("./ChunkTemplate");
+const HotUpdateChunkTemplate = require("./HotUpdateChunkTemplate");
+const ModuleTemplate = require("./ModuleTemplate");
+const RuntimeTemplate = require("./RuntimeTemplate");
+const ChunkRenderError = require("./ChunkRenderError");
+const AsyncDependencyToInitialChunkError = require("./AsyncDependencyToInitialChunkError");
+const Stats = require("./Stats");
+const Semaphore = require("./util/Semaphore");
+const createHash = require("./util/createHash");
+const Queue = require("./util/Queue");
+const SortableSet = require("./util/SortableSet");
+const GraphHelpers = require("./GraphHelpers");
+const ModuleDependency = require("./dependencies/ModuleDependency");
+const compareLocations = require("./compareLocations");
+
+/** @typedef {import("./Module")} Module */
+/** @typedef {import("./Compiler")} Compiler */
+/** @typedef {import("webpack-sources").Source} Source */
+/** @typedef {import("./WebpackError")} WebpackError */
+/** @typedef {import("./DependenciesBlockVariable")} DependenciesBlockVariable */
+/** @typedef {import("./dependencies/SingleEntryDependency")} SingleEntryDependency */
+/** @typedef {import("./dependencies/MultiEntryDependency")} MultiEntryDependency */
+/** @typedef {import("./dependencies/DllEntryDependency")} DllEntryDependency */
+/** @typedef {import("./dependencies/DependencyReference")} DependencyReference */
+/** @typedef {import("./DependenciesBlock")} DependenciesBlock */
+/** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */
+/** @typedef {import("./Dependency")} Dependency */
+/** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */
+/** @typedef {import("./Dependency").DependencyTemplate} DependencyTemplate */
+/** @typedef {import("./util/createHash").Hash} Hash */
+
+// TODO use @callback
+/** @typedef {{[assetName: string]: Source}} CompilationAssets */
+/** @typedef {(err: Error|null, result?: Module) => void } ModuleCallback */
+/** @typedef {(err?: Error|null, result?: Module) => void } ModuleChainCallback */
+/** @typedef {(module: Module) => void} OnModuleCallback */
+/** @typedef {(err?: Error|null) => void} Callback */
+/** @typedef {(d: Dependency) => any} DepBlockVarDependenciesCallback */
+/** @typedef {new (...args: any[]) => Dependency} DepConstructor */
+/** @typedef {{apply: () => void}} Plugin */
+
+/**
+ * @typedef {Object} ModuleFactoryCreateDataContextInfo
+ * @property {string} issuer
+ * @property {string} compiler
+ */
+
+/**
+ * @typedef {Object} ModuleFactoryCreateData
+ * @property {ModuleFactoryCreateDataContextInfo} contextInfo
+ * @property {any=} resolveOptions
+ * @property {string} context
+ * @property {Dependency[]} dependencies
+ */
+
+/**
+ * @typedef {Object} ModuleFactory
+ * @property {(data: ModuleFactoryCreateData, callback: ModuleCallback) => any} create
+ */
+
+/**
+ * @typedef {Object} SortedDependency
+ * @property {ModuleFactory} factory
+ * @property {Dependency[]} dependencies
+ */
+
+/**
+ * @typedef {Object} AvailableModulesChunkGroupMapping
+ * @property {ChunkGroup} chunkGroup
+ * @property {Set<Module>} availableModules
+ */
+
+/**
+ * @typedef {Object} DependenciesBlockLike
+ * @property {Dependency[]} dependencies
+ * @property {AsyncDependenciesBlock[]} blocks
+ * @property {DependenciesBlockVariable[]} variables
+ */
+
+/**
+ * @param {Chunk} a first chunk to sort by id
+ * @param {Chunk} b second chunk to sort by id
+ * @returns {-1|0|1} sort value
+ */
+const byId = (a, b) => {
+ if (a.id !== null && b.id !== null) {
+ if (a.id < b.id) return -1;
+ if (a.id > b.id) return 1;
+ }
+ 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 byIdOrIdentifier = (a, b) => {
+ if (a.id < b.id) return -1;
+ if (a.id > b.id) return 1;
+ const identA = a.identifier();
+ const identB = b.identifier();
+ if (identA < identB) return -1;
+ if (identA > identB) return 1;
+ 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
+ * @returns {-1|0|1} sort value
+ */
+const byNameOrHash = (a, b) => {
+ if (a.name < b.name) return -1;
+ if (a.name > b.name) return 1;
+ if (a.fullHash < b.fullHash) return -1;
+ if (a.fullHash > b.fullHash) return 1;
+ return 0;
+};
+
+/**
+ * @param {DependenciesBlockVariable[]} variables DepBlock Variables to iterate over
+ * @param {DepBlockVarDependenciesCallback} fn callback to apply on iterated elements
+ * @returns {void}
+ */
+const iterationBlockVariable = (variables, fn) => {
+ for (
+ let indexVariable = 0;
+ indexVariable < variables.length;
+ indexVariable++
+ ) {
+ const varDep = variables[indexVariable].dependencies;
+ for (let indexVDep = 0; indexVDep < varDep.length; indexVDep++) {
+ fn(varDep[indexVDep]);
+ }
+ }
+};
+
+/**
+ * @template T
+ * @param {T[]} arr array of elements to iterate over
+ * @param {function(T): void} fn callback applied to each element
+ * @returns {void}
+ */
+const iterationOfArrayCallback = (arr, fn) => {
+ for (let index = 0; index < arr.length; index++) {
+ fn(arr[index]);
+ }
+};
+
+/**
+ * @template T
+ * @param {Set<T>} set set to add items to
+ * @param {Set<T>} otherSet set to add items from
+ * @returns {void}
+ */
+const addAllToSet = (set, otherSet) => {
+ for (const item of otherSet) {
+ set.add(item);
+ }
+};
+
+class Compilation extends Tapable {
+ /**
+ * Creates an instance of Compilation.
+ * @param {Compiler} compiler the compiler which created the compilation
+ */
+ constructor(compiler) {
+ super();
+ this.hooks = {
+ /** @type {SyncHook<Module>} */
+ buildModule: new SyncHook(["module"]),
+ /** @type {SyncHook<Module>} */
+ rebuildModule: new SyncHook(["module"]),
+ /** @type {SyncHook<Module, Error>} */
+ failedModule: new SyncHook(["module", "error"]),
+ /** @type {SyncHook<Module>} */
+ succeedModule: new SyncHook(["module"]),
+
+ /** @type {SyncWaterfallHook<DependencyReference, Dependency, Module>} */
+ dependencyReference: new SyncWaterfallHook([
+ "dependencyReference",
+ "dependency",
+ "module"
+ ]),
+
+ /** @type {SyncHook<Module[]>} */
+ finishModules: new SyncHook(["modules"]),
+ /** @type {SyncHook<Module>} */
+ finishRebuildingModule: new SyncHook(["module"]),
+ /** @type {SyncHook} */
+ unseal: new SyncHook([]),
+ /** @type {SyncHook} */
+ seal: new SyncHook([]),
+
+ /** @type {SyncHook} */
+ beforeChunks: new SyncHook([]),
+ /** @type {SyncHook<Chunk[]>} */
+ afterChunks: new SyncHook(["chunks"]),
+
+ /** @type {SyncBailHook<Module[]>} */
+ optimizeDependenciesBasic: new SyncBailHook(["modules"]),
+ /** @type {SyncBailHook<Module[]>} */
+ optimizeDependencies: new SyncBailHook(["modules"]),
+ /** @type {SyncBailHook<Module[]>} */
+ optimizeDependenciesAdvanced: new SyncBailHook(["modules"]),
+ /** @type {SyncBailHook<Module[]>} */
+ afterOptimizeDependencies: new SyncHook(["modules"]),
+
+ /** @type {SyncHook} */
+ optimize: new SyncHook([]),
+ /** @type {SyncBailHook<Module[]>} */
+ optimizeModulesBasic: new SyncBailHook(["modules"]),
+ /** @type {SyncBailHook<Module[]>} */
+ optimizeModules: new SyncBailHook(["modules"]),
+ /** @type {SyncBailHook<Module[]>} */
+ optimizeModulesAdvanced: new SyncBailHook(["modules"]),
+ /** @type {SyncHook<Module[]>} */
+ afterOptimizeModules: new SyncHook(["modules"]),
+
+ /** @type {SyncBailHook<Chunk[], ChunkGroup[]>} */
+ optimizeChunksBasic: new SyncBailHook(["chunks", "chunkGroups"]),
+ /** @type {SyncBailHook<Chunk[], ChunkGroup[]>} */
+ optimizeChunks: new SyncBailHook(["chunks", "chunkGroups"]),
+ /** @type {SyncBailHook<Chunk[], ChunkGroup[]>} */
+ optimizeChunksAdvanced: new SyncBailHook(["chunks", "chunkGroups"]),
+ /** @type {SyncHook<Chunk[], ChunkGroup[]>} */
+ afterOptimizeChunks: new SyncHook(["chunks", "chunkGroups"]),
+
+ /** @type {AsyncSeriesHook<Chunk[], Module[]>} */
+ optimizeTree: new AsyncSeriesHook(["chunks", "modules"]),
+ /** @type {SyncHook<Chunk[], Module[]>} */
+ afterOptimizeTree: new SyncHook(["chunks", "modules"]),
+
+ /** @type {SyncBailHook<Chunk[], Module[]>} */
+ optimizeChunkModulesBasic: new SyncBailHook(["chunks", "modules"]),
+ /** @type {SyncBailHook<Chunk[], Module[]>} */
+ optimizeChunkModules: new SyncBailHook(["chunks", "modules"]),
+ /** @type {SyncBailHook<Chunk[], Module[]>} */
+ optimizeChunkModulesAdvanced: new SyncBailHook(["chunks", "modules"]),
+ /** @type {SyncHook<Chunk[], Module[]>} */
+ afterOptimizeChunkModules: new SyncHook(["chunks", "modules"]),
+ /** @type {SyncBailHook} */
+ shouldRecord: new SyncBailHook([]),
+
+ /** @type {SyncHook<Module[], any>} */
+ reviveModules: new SyncHook(["modules", "records"]),
+ /** @type {SyncHook<Module[]>} */
+ optimizeModuleOrder: new SyncHook(["modules"]),
+ /** @type {SyncHook<Module[]>} */
+ advancedOptimizeModuleOrder: new SyncHook(["modules"]),
+ /** @type {SyncHook<Module[]>} */
+ beforeModuleIds: new SyncHook(["modules"]),
+ /** @type {SyncHook<Module[]>} */
+ moduleIds: new SyncHook(["modules"]),
+ /** @type {SyncHook<Module[]>} */
+ optimizeModuleIds: new SyncHook(["modules"]),
+ /** @type {SyncHook<Module[]>} */
+ afterOptimizeModuleIds: new SyncHook(["modules"]),
+
+ /** @type {SyncHook<Chunk[], any>} */
+ reviveChunks: new SyncHook(["chunks", "records"]),
+ /** @type {SyncHook<Chunk[]>} */
+ optimizeChunkOrder: new SyncHook(["chunks"]),
+ /** @type {SyncHook<Chunk[]>} */
+ beforeChunkIds: new SyncHook(["chunks"]),
+ /** @type {SyncHook<Chunk[]>} */
+ optimizeChunkIds: new SyncHook(["chunks"]),
+ /** @type {SyncHook<Chunk[]>} */
+ afterOptimizeChunkIds: new SyncHook(["chunks"]),
+
+ /** @type {SyncHook<Module[], any>} */
+ recordModules: new SyncHook(["modules", "records"]),
+ /** @type {SyncHook<Chunk[], any>} */
+ recordChunks: new SyncHook(["chunks", "records"]),
+
+ /** @type {SyncHook} */
+ beforeHash: new SyncHook([]),
+ /** @type {SyncHook<Chunk>} */
+ contentHash: new SyncHook(["chunk"]),
+ /** @type {SyncHook} */
+ afterHash: new SyncHook([]),
+ /** @type {SyncHook<any>} */
+ recordHash: new SyncHook(["records"]),
+ /** @type {SyncHook<Compilation, any>} */
+ record: new SyncHook(["compilation", "records"]),
+
+ /** @type {SyncHook} */
+ beforeModuleAssets: new SyncHook([]),
+ /** @type {SyncBailHook} */
+ shouldGenerateChunkAssets: new SyncBailHook([]),
+ /** @type {SyncHook} */
+ beforeChunkAssets: new SyncHook([]),
+ /** @type {SyncHook<Chunk[]>} */
+ additionalChunkAssets: new SyncHook(["chunks"]),
+
+ /** @type {AsyncSeriesHook} */
+ additionalAssets: new AsyncSeriesHook([]),
+ /** @type {AsyncSeriesHook<Chunk[]>} */
+ optimizeChunkAssets: new AsyncSeriesHook(["chunks"]),
+ /** @type {SyncHook<Chunk[]>} */
+ afterOptimizeChunkAssets: new SyncHook(["chunks"]),
+ /** @type {AsyncSeriesHook<CompilationAssets>} */
+ optimizeAssets: new AsyncSeriesHook(["assets"]),
+ /** @type {SyncHook<CompilationAssets>} */
+ afterOptimizeAssets: new SyncHook(["assets"]),
+
+ /** @type {SyncBailHook} */
+ needAdditionalSeal: new SyncBailHook([]),
+ /** @type {AsyncSeriesHook} */
+ afterSeal: new AsyncSeriesHook([]),
+
+ /** @type {SyncHook<Chunk, Hash>} */
+ chunkHash: new SyncHook(["chunk", "chunkHash"]),
+ /** @type {SyncHook<Module, string>} */
+ moduleAsset: new SyncHook(["module", "filename"]),
+ /** @type {SyncHook<Chunk, string>} */
+ chunkAsset: new SyncHook(["chunk", "filename"]),
+
+ /** @type {SyncWaterfallHook<string, TODO>} */
+ assetPath: new SyncWaterfallHook(["filename", "data"]), // TODO MainTemplate
+
+ /** @type {SyncBailHook} */
+ needAdditionalPass: new SyncBailHook([]),
+
+ /** @type {SyncHook<Compiler, string, number>} */
+ childCompiler: new SyncHook([
+ "childCompiler",
+ "compilerName",
+ "compilerIndex"
+ ]),
+
+ // TODO the following hooks are weirdly located here
+ // TODO move them for webpack 5
+ /** @type {SyncHook<object, Module>} */
+ normalModuleLoader: new SyncHook(["loaderContext", "module"]),
+
+ /** @type {SyncBailHook<Chunk[]>} */
+ optimizeExtractedChunksBasic: new SyncBailHook(["chunks"]),
+ /** @type {SyncBailHook<Chunk[]>} */
+ optimizeExtractedChunks: new SyncBailHook(["chunks"]),
+ /** @type {SyncBailHook<Chunk[]>} */
+ optimizeExtractedChunksAdvanced: new SyncBailHook(["chunks"]),
+ /** @type {SyncHook<Chunk[]>} */
+ afterOptimizeExtractedChunks: new SyncHook(["chunks"])
+ };
+ this._pluginCompat.tap("Compilation", options => {
+ switch (options.name) {
+ case "optimize-tree":
+ case "additional-assets":
+ case "optimize-chunk-assets":
+ case "optimize-assets":
+ case "after-seal":
+ options.async = true;
+ break;
+ }
+ });
+ /** @type {string=} */
+ this.name = undefined;
+ /** @type {Compiler} */
+ this.compiler = compiler;
+ this.resolverFactory = compiler.resolverFactory;
+ this.inputFileSystem = compiler.inputFileSystem;
+ this.requestShortener = compiler.requestShortener;
+
+ const options = (this.options = compiler.options);
+ this.outputOptions = options && options.output;
+ /** @type {boolean=} */
+ this.bail = options && options.bail;
+ this.profile = options && options.profile;
+ this.performance = options && options.performance;
+
+ this.mainTemplate = new MainTemplate(this.outputOptions);
+ this.chunkTemplate = new ChunkTemplate(this.outputOptions);
+ this.hotUpdateChunkTemplate = new HotUpdateChunkTemplate(
+ this.outputOptions
+ );
+ this.runtimeTemplate = new RuntimeTemplate(
+ this.outputOptions,
+ this.requestShortener
+ );
+ this.moduleTemplates = {
+ javascript: new ModuleTemplate(this.runtimeTemplate, "javascript"),
+ webassembly: new ModuleTemplate(this.runtimeTemplate, "webassembly")
+ };
+
+ this.semaphore = new Semaphore(options.parallelism || 100);
+
+ this.entries = [];
+ /** @private @type {{name: string, request: string, module: Module}[]} */
+ this._preparedEntrypoints = [];
+ this.entrypoints = new Map();
+ /** @type {Chunk[]} */
+ this.chunks = [];
+ /** @type {ChunkGroup[]} */
+ this.chunkGroups = [];
+ /** @type {Map<string, ChunkGroup>} */
+ this.namedChunkGroups = new Map();
+ /** @type {Map<string, Chunk>} */
+ this.namedChunks = new Map();
+ /** @type {Module[]} */
+ this.modules = [];
+ /** @private @type {Map<string, Module>} */
+ this._modules = new Map();
+ this.cache = null;
+ this.records = null;
+ /** @type {string[]} */
+ this.additionalChunkAssets = [];
+ /** @type {CompilationAssets} */
+ this.assets = {};
+ /** @type {WebpackError[]} */
+ this.errors = [];
+ /** @type {WebpackError[]} */
+ this.warnings = [];
+ /** @type {Compilation[]} */
+ this.children = [];
+ /** @type {Map<DepConstructor, ModuleFactory>} */
+ this.dependencyFactories = new Map();
+ /** @type {Map<DepConstructor, DependencyTemplate>} */
+ this.dependencyTemplates = new Map();
+ // TODO refactor this in webpack 5 to a custom DependencyTemplates class with a hash property
+ // @ts-ignore
+ this.dependencyTemplates.set("hash", "");
+ this.childrenCounters = {};
+ /** @type {Set<number|string>} */
+ this.usedChunkIds = null;
+ /** @type {Set<number>} */
+ this.usedModuleIds = null;
+ /** @type {Map<string, number>=} */
+ this.fileTimestamps = undefined;
+ /** @type {Map<string, number>=} */
+ this.contextTimestamps = undefined;
+ /** @type {Set<string>=} */
+ this.compilationDependencies = undefined;
+ /** @private @type {Map<Module, Callback[]>} */
+ this._buildingModules = new Map();
+ /** @private @type {Map<Module, Callback[]>} */
+ this._rebuildingModules = new Map();
+ }
+
+ getStats() {
+ return new Stats(this);
+ }
+
+ /**
+ * @typedef {Object} AddModuleResult
+ * @property {Module} module the added or existing module
+ * @property {boolean} issuer was this the first request for this module
+ * @property {boolean} build should the module be build
+ * @property {boolean} dependencies should dependencies be walked
+ */
+
+ /**
+ * @param {Module} module module to be added that was created
+ * @param {any=} cacheGroup cacheGroup it is apart of
+ * @returns {AddModuleResult} returns meta about whether or not the module had built
+ * had an issuer, or any dependnecies
+ */
+ addModule(module, cacheGroup) {
+ const identifier = module.identifier();
+ const alreadyAddedModule = this._modules.get(identifier);
+ if (alreadyAddedModule) {
+ return {
+ module: alreadyAddedModule,
+ issuer: false,
+ build: false,
+ dependencies: false
+ };
+ }
+ const cacheName = (cacheGroup || "m") + identifier;
+ if (this.cache && this.cache[cacheName]) {
+ const cacheModule = this.cache[cacheName];
+
+ if (typeof cacheModule.updateCacheModule === "function") {
+ cacheModule.updateCacheModule(module);
+ }
+
+ let rebuild = true;
+ if (this.fileTimestamps && this.contextTimestamps) {
+ rebuild = cacheModule.needRebuild(
+ this.fileTimestamps,
+ this.contextTimestamps
+ );
+ }
+
+ if (!rebuild) {
+ cacheModule.disconnect();
+ this._modules.set(identifier, cacheModule);
+ this.modules.push(cacheModule);
+ for (const err of cacheModule.errors) {
+ this.errors.push(err);
+ }
+ for (const err of cacheModule.warnings) {
+ this.warnings.push(err);
+ }
+ return {
+ module: cacheModule,
+ issuer: true,
+ build: false,
+ dependencies: true
+ };
+ }
+ cacheModule.unbuild();
+ module = cacheModule;
+ }
+ this._modules.set(identifier, module);
+ if (this.cache) {
+ this.cache[cacheName] = module;
+ }
+ this.modules.push(module);
+ return {
+ module: module,
+ issuer: true,
+ build: true,
+ dependencies: true
+ };
+ }
+
+ /**
+ * Fetches a module from a compilation by its identifier
+ * @param {Module} module the module provided
+ * @returns {Module} the module requested
+ */
+ getModule(module) {
+ const identifier = module.identifier();
+ return this._modules.get(identifier);
+ }
+
+ /**
+ * Attempts to search for a module by its identifier
+ * @param {string} identifier identifier (usually path) for module
+ * @returns {Module|undefined} attempt to search for module and return it, else undefined
+ */
+ findModule(identifier) {
+ return this._modules.get(identifier);
+ }
+
+ /**
+ * @param {Module} module module with its callback list
+ * @param {Callback} callback the callback function
+ * @returns {void}
+ */
+ waitForBuildingFinished(module, callback) {
+ let callbackList = this._buildingModules.get(module);
+ if (callbackList) {
+ callbackList.push(() => callback());
+ } else {
+ process.nextTick(callback);
+ }
+ }
+
+ /**
+ * Builds the module object
+ *
+ * @param {Module} module module to be built
+ * @param {boolean} optional optional flag
+ * @param {Module=} origin origin module this module build was requested from
+ * @param {Dependency[]=} dependencies optional dependencies from the module to be built
+ * @param {TODO} thisCallback the callback
+ * @returns {TODO} returns the callback function with results
+ */
+ buildModule(module, optional, origin, dependencies, thisCallback) {
+ let callbackList = this._buildingModules.get(module);
+ if (callbackList) {
+ callbackList.push(thisCallback);
+ return;
+ }
+ this._buildingModules.set(module, (callbackList = [thisCallback]));
+
+ const callback = err => {
+ this._buildingModules.delete(module);
+ for (const cb of callbackList) {
+ cb(err);
+ }
+ };
+
+ this.hooks.buildModule.call(module);
+ module.build(
+ this.options,
+ this,
+ this.resolverFactory.get("normal", module.resolveOptions),
+ this.inputFileSystem,
+ error => {
+ const errors = module.errors;
+ for (let indexError = 0; indexError < errors.length; indexError++) {
+ const err = errors[indexError];
+ err.origin = origin;
+ err.dependencies = dependencies;
+ if (optional) {
+ this.warnings.push(err);
+ } else {
+ this.errors.push(err);
+ }
+ }
+
+ const warnings = module.warnings;
+ for (
+ let indexWarning = 0;
+ indexWarning < warnings.length;
+ indexWarning++
+ ) {
+ const war = warnings[indexWarning];
+ war.origin = origin;
+ war.dependencies = dependencies;
+ this.warnings.push(war);
+ }
+ module.dependencies.sort((a, b) => compareLocations(a.loc, b.loc));
+ if (error) {
+ this.hooks.failedModule.call(module, error);
+ return callback(error);
+ }
+ this.hooks.succeedModule.call(module);
+ return callback();
+ }
+ );
+ }
+
+ /**
+ * @param {Module} module to be processed for deps
+ * @param {ModuleCallback} callback callback to be triggered
+ * @returns {void}
+ */
+ processModuleDependencies(module, callback) {
+ const dependencies = new Map();
+
+ const addDependency = dep => {
+ const resourceIdent = dep.getResourceIdentifier();
+ if (resourceIdent) {
+ const factory = this.dependencyFactories.get(dep.constructor);
+ if (factory === undefined) {
+ throw new Error(
+ `No module factory available for dependency type: ${
+ dep.constructor.name
+ }`
+ );
+ }
+ let innerMap = dependencies.get(factory);
+ if (innerMap === undefined) {
+ dependencies.set(factory, (innerMap = new Map()));
+ }
+ let list = innerMap.get(resourceIdent);
+ if (list === undefined) innerMap.set(resourceIdent, (list = []));
+ list.push(dep);
+ }
+ };
+
+ const addDependenciesBlock = block => {
+ if (block.dependencies) {
+ iterationOfArrayCallback(block.dependencies, addDependency);
+ }
+ if (block.blocks) {
+ iterationOfArrayCallback(block.blocks, addDependenciesBlock);
+ }
+ if (block.variables) {
+ iterationBlockVariable(block.variables, addDependency);
+ }
+ };
+
+ try {
+ addDependenciesBlock(module);
+ } catch (e) {
+ callback(e);
+ }
+
+ const sortedDependencies = [];
+
+ for (const pair1 of dependencies) {
+ for (const pair2 of pair1[1]) {
+ sortedDependencies.push({
+ factory: pair1[0],
+ dependencies: pair2[1]
+ });
+ }
+ }
+
+ this.addModuleDependencies(
+ module,
+ sortedDependencies,
+ this.bail,
+ null,
+ true,
+ callback
+ );
+ }
+
+ /**
+ * @param {Module} module module to add deps to
+ * @param {SortedDependency[]} dependencies set of sorted dependencies to iterate through
+ * @param {(boolean|null)=} bail whether to bail or not
+ * @param {TODO} cacheGroup optional cacheGroup
+ * @param {boolean} recursive whether it is recursive traversal
+ * @param {function} callback callback for when dependencies are finished being added
+ * @returns {void}
+ */
+ addModuleDependencies(
+ module,
+ dependencies,
+ bail,
+ cacheGroup,
+ recursive,
+ callback
+ ) {
+ const start = this.profile && Date.now();
+ const currentProfile = this.profile && {};
+
+ asyncLib.forEach(
+ dependencies,
+ (item, callback) => {
+ const dependencies = item.dependencies;
+
+ const errorAndCallback = err => {
+ err.origin = module;
+ err.dependencies = dependencies;
+ this.errors.push(err);
+ if (bail) {
+ callback(err);
+ } else {
+ callback();
+ }
+ };
+ const warningAndCallback = err => {
+ err.origin = module;
+ this.warnings.push(err);
+ callback();
+ };
+
+ const semaphore = this.semaphore;
+ semaphore.acquire(() => {
+ const factory = item.factory;
+ factory.create(
+ {
+ contextInfo: {
+ issuer: module.nameForCondition && module.nameForCondition(),
+ compiler: this.compiler.name
+ },
+ resolveOptions: module.resolveOptions,
+ context: module.context,
+ dependencies: dependencies
+ },
+ (err, dependentModule) => {
+ let afterFactory;
+
+ const isOptional = () => {
+ return dependencies.every(d => d.optional);
+ };
+
+ const errorOrWarningAndCallback = err => {
+ if (isOptional()) {
+ return warningAndCallback(err);
+ } else {
+ return errorAndCallback(err);
+ }
+ };
+
+ if (err) {
+ semaphore.release();
+ return errorOrWarningAndCallback(
+ new ModuleNotFoundError(module, err)
+ );
+ }
+ if (!dependentModule) {
+ semaphore.release();
+ return process.nextTick(callback);
+ }
+ if (currentProfile) {
+ afterFactory = Date.now();
+ currentProfile.factory = afterFactory - start;
+ }
+
+ const iterationDependencies = depend => {
+ for (let index = 0; index < depend.length; index++) {
+ const dep = depend[index];
+ dep.module = dependentModule;
+ dependentModule.addReason(module, dep);
+ }
+ };
+
+ const addModuleResult = this.addModule(
+ dependentModule,
+ cacheGroup
+ );
+ dependentModule = addModuleResult.module;
+ iterationDependencies(dependencies);
+
+ const afterBuild = () => {
+ if (currentProfile) {
+ const afterBuilding = Date.now();
+ currentProfile.building = afterBuilding - afterFactory;
+ }
+
+ if (recursive && addModuleResult.dependencies) {
+ this.processModuleDependencies(dependentModule, callback);
+ } else {
+ return callback();
+ }
+ };
+
+ if (addModuleResult.issuer) {
+ if (currentProfile) {
+ dependentModule.profile = currentProfile;
+ }
+
+ dependentModule.issuer = module;
+ } else {
+ if (this.profile) {
+ if (module.profile) {
+ const time = Date.now() - start;
+ if (
+ !module.profile.dependencies ||
+ time > module.profile.dependencies
+ ) {
+ module.profile.dependencies = time;
+ }
+ }
+ }
+ }
+
+ if (addModuleResult.build) {
+ this.buildModule(
+ dependentModule,
+ isOptional(),
+ module,
+ dependencies,
+ err => {
+ if (err) {
+ semaphore.release();
+ return errorOrWarningAndCallback(err);
+ }
+
+ if (currentProfile) {
+ const afterBuilding = Date.now();
+ currentProfile.building = afterBuilding - afterFactory;
+ }
+
+ semaphore.release();
+ afterBuild();
+ }
+ );
+ } else {
+ semaphore.release();
+ this.waitForBuildingFinished(dependentModule, afterBuild);
+ }
+ }
+ );
+ });
+ },
+ err => {
+ // In V8, the Error objects keep a reference to the functions on the stack. These warnings &
+ // errors are created inside closures that keep a reference to the Compilation, so errors are
+ // leaking the Compilation object.
+
+ if (err) {
+ // eslint-disable-next-line no-self-assign
+ err.stack = err.stack;
+ return callback(err);
+ }
+
+ return process.nextTick(callback);
+ }
+ );
+ }
+
+ /**
+ *
+ * @param {string} context context string path
+ * @param {Dependency} dependency dependency used to create Module chain
+ * @param {OnModuleCallback} onModule function invoked on modules creation
+ * @param {ModuleChainCallback} callback callback for when module chain is complete
+ * @returns {void} will throw if dependency instance is not a valid Dependency
+ */
+ _addModuleChain(context, dependency, onModule, callback) {
+ const start = this.profile && Date.now();
+ const currentProfile = this.profile && {};
+
+ const errorAndCallback = this.bail
+ ? err => {
+ callback(err);
+ }
+ : err => {
+ err.dependencies = [dependency];
+ this.errors.push(err);
+ callback();
+ };
+
+ if (
+ typeof dependency !== "object" ||
+ dependency === null ||
+ !dependency.constructor
+ ) {
+ throw new Error("Parameter 'dependency' must be a Dependency");
+ }
+ const Dep = /** @type {DepConstructor} */ (dependency.constructor);
+ const moduleFactory = this.dependencyFactories.get(Dep);
+ if (!moduleFactory) {
+ throw new Error(
+ `No dependency factory available for this dependency type: ${
+ dependency.constructor.name
+ }`
+ );
+ }
+
+ this.semaphore.acquire(() => {
+ moduleFactory.create(
+ {
+ contextInfo: {
+ issuer: "",
+ compiler: this.compiler.name
+ },
+ context: context,
+ dependencies: [dependency]
+ },
+ (err, module) => {
+ if (err) {
+ this.semaphore.release();
+ return errorAndCallback(new EntryModuleNotFoundError(err));
+ }
+
+ let afterFactory;
+
+ if (currentProfile) {
+ afterFactory = Date.now();
+ currentProfile.factory = afterFactory - start;
+ }
+
+ const addModuleResult = this.addModule(module);
+ module = addModuleResult.module;
+
+ onModule(module);
+
+ dependency.module = module;
+ module.addReason(null, dependency);
+
+ const afterBuild = () => {
+ if (currentProfile) {
+ const afterBuilding = Date.now();
+ currentProfile.building = afterBuilding - afterFactory;
+ }
+
+ if (addModuleResult.dependencies) {
+ this.processModuleDependencies(module, err => {
+ if (err) return callback(err);
+ callback(null, module);
+ });
+ } else {
+ return callback(null, module);
+ }
+ };
+
+ if (addModuleResult.issuer) {
+ if (currentProfile) {
+ module.profile = currentProfile;
+ }
+ }
+
+ if (addModuleResult.build) {
+ this.buildModule(module, false, null, null, err => {
+ if (err) {
+ this.semaphore.release();
+ return errorAndCallback(err);
+ }
+
+ if (currentProfile) {
+ const afterBuilding = Date.now();
+ currentProfile.building = afterBuilding - afterFactory;
+ }
+
+ this.semaphore.release();
+ afterBuild();
+ });
+ } else {
+ this.semaphore.release();
+ this.waitForBuildingFinished(module, afterBuild);
+ }
+ }
+ );
+ });
+ }
+
+ /**
+ *
+ * @param {string} context context path for entry
+ * @param {Dependency} entry entry dependency being created
+ * @param {string} name name of entry
+ * @param {ModuleCallback} callback callback function
+ * @returns {void} returns
+ */
+ addEntry(context, entry, name, callback) {
+ const slot = {
+ name: name,
+ // TODO webpack 5 remove `request`
+ request: null,
+ module: null
+ };
+
+ if (entry instanceof ModuleDependency) {
+ slot.request = entry.request;
+ }
+
+ // TODO webpack 5: merge modules instead when multiple entry modules are supported
+ const idx = this._preparedEntrypoints.findIndex(slot => slot.name === name);
+ if (idx >= 0) {
+ // Overwrite existing entrypoint
+ this._preparedEntrypoints[idx] = slot;
+ } else {
+ this._preparedEntrypoints.push(slot);
+ }
+ this._addModuleChain(
+ context,
+ entry,
+ module => {
+ this.entries.push(module);
+ },
+ (err, module) => {
+ if (err) {
+ return callback(err);
+ }
+
+ if (module) {
+ slot.module = module;
+ } else {
+ const idx = this._preparedEntrypoints.indexOf(slot);
+ if (idx >= 0) {
+ this._preparedEntrypoints.splice(idx, 1);
+ }
+ }
+ return callback(null, module);
+ }
+ );
+ }
+
+ /**
+ * @param {string} context context path string
+ * @param {Dependency} dependency dep used to create module
+ * @param {ModuleCallback} callback module callback sending module up a level
+ * @returns {void}
+ */
+ prefetch(context, dependency, callback) {
+ this._addModuleChain(
+ context,
+ dependency,
+ module => {
+ module.prefetched = true;
+ },
+ callback
+ );
+ }
+
+ /**
+ * @param {Module} module module to be rebuilt
+ * @param {Callback} thisCallback callback when module finishes rebuilding
+ * @returns {void}
+ */
+ rebuildModule(module, thisCallback) {
+ let callbackList = this._rebuildingModules.get(module);
+ if (callbackList) {
+ callbackList.push(thisCallback);
+ return;
+ }
+ this._rebuildingModules.set(module, (callbackList = [thisCallback]));
+
+ const callback = err => {
+ this._rebuildingModules.delete(module);
+ for (const cb of callbackList) {
+ cb(err);
+ }
+ };
+
+ this.hooks.rebuildModule.call(module);
+ const oldDependencies = module.dependencies.slice();
+ const oldVariables = module.variables.slice();
+ const oldBlocks = module.blocks.slice();
+ module.unbuild();
+ this.buildModule(module, false, module, null, err => {
+ if (err) {
+ this.hooks.finishRebuildingModule.call(module);
+ return callback(err);
+ }
+
+ this.processModuleDependencies(module, err => {
+ if (err) return callback(err);
+ this.removeReasonsOfDependencyBlock(module, {
+ dependencies: oldDependencies,
+ variables: oldVariables,
+ blocks: oldBlocks
+ });
+ this.hooks.finishRebuildingModule.call(module);
+ callback();
+ });
+ });
+ }
+
+ finish() {
+ const modules = this.modules;
+ this.hooks.finishModules.call(modules);
+
+ for (let index = 0; index < modules.length; index++) {
+ const module = modules[index];
+ this.reportDependencyErrorsAndWarnings(module, [module]);
+ }
+ }
+
+ unseal() {
+ this.hooks.unseal.call();
+ this.chunks.length = 0;
+ this.chunkGroups.length = 0;
+ this.namedChunks.clear();
+ this.namedChunkGroups.clear();
+ this.additionalChunkAssets.length = 0;
+ this.assets = {};
+ for (const module of this.modules) {
+ module.unseal();
+ }
+ }
+
+ /**
+ * @param {Callback} callback signals when the seal method is finishes
+ * @returns {void}
+ */
+ seal(callback) {
+ this.hooks.seal.call();
+
+ while (
+ this.hooks.optimizeDependenciesBasic.call(this.modules) ||
+ this.hooks.optimizeDependencies.call(this.modules) ||
+ this.hooks.optimizeDependenciesAdvanced.call(this.modules)
+ ) {
+ /* empty */
+ }
+ this.hooks.afterOptimizeDependencies.call(this.modules);
+
+ this.hooks.beforeChunks.call();
+ for (const preparedEntrypoint of this._preparedEntrypoints) {
+ const module = preparedEntrypoint.module;
+ const name = preparedEntrypoint.name;
+ const chunk = this.addChunk(name);
+ const entrypoint = new Entrypoint(name);
+ entrypoint.setRuntimeChunk(chunk);
+ entrypoint.addOrigin(null, name, preparedEntrypoint.request);
+ this.namedChunkGroups.set(name, entrypoint);
+ this.entrypoints.set(name, entrypoint);
+ this.chunkGroups.push(entrypoint);
+
+ GraphHelpers.connectChunkGroupAndChunk(entrypoint, chunk);
+ GraphHelpers.connectChunkAndModule(chunk, module);
+
+ chunk.entryModule = module;
+ chunk.name = name;
+
+ this.assignDepth(module);
+ }
+ this.processDependenciesBlocksForChunkGroups(this.chunkGroups.slice());
+ this.sortModules(this.modules);
+ this.hooks.afterChunks.call(this.chunks);
+
+ this.hooks.optimize.call();
+
+ while (
+ this.hooks.optimizeModulesBasic.call(this.modules) ||
+ this.hooks.optimizeModules.call(this.modules) ||
+ this.hooks.optimizeModulesAdvanced.call(this.modules)
+ ) {
+ /* empty */
+ }
+ this.hooks.afterOptimizeModules.call(this.modules);
+
+ while (
+ this.hooks.optimizeChunksBasic.call(this.chunks, this.chunkGroups) ||
+ this.hooks.optimizeChunks.call(this.chunks, this.chunkGroups) ||
+ this.hooks.optimizeChunksAdvanced.call(this.chunks, this.chunkGroups)
+ ) {
+ /* empty */
+ }
+ this.hooks.afterOptimizeChunks.call(this.chunks, this.chunkGroups);
+
+ this.hooks.optimizeTree.callAsync(this.chunks, this.modules, err => {
+ if (err) {
+ return callback(err);
+ }
+
+ this.hooks.afterOptimizeTree.call(this.chunks, this.modules);
+
+ while (
+ this.hooks.optimizeChunkModulesBasic.call(this.chunks, this.modules) ||
+ this.hooks.optimizeChunkModules.call(this.chunks, this.modules) ||
+ this.hooks.optimizeChunkModulesAdvanced.call(this.chunks, this.modules)
+ ) {
+ /* empty */
+ }
+ this.hooks.afterOptimizeChunkModules.call(this.chunks, this.modules);
+
+ const shouldRecord = this.hooks.shouldRecord.call() !== false;
+
+ this.hooks.reviveModules.call(this.modules, this.records);
+ this.hooks.optimizeModuleOrder.call(this.modules);
+ this.hooks.advancedOptimizeModuleOrder.call(this.modules);
+ this.hooks.beforeModuleIds.call(this.modules);
+ this.hooks.moduleIds.call(this.modules);
+ this.applyModuleIds();
+ this.hooks.optimizeModuleIds.call(this.modules);
+ this.hooks.afterOptimizeModuleIds.call(this.modules);
+
+ this.sortItemsWithModuleIds();
+
+ this.hooks.reviveChunks.call(this.chunks, this.records);
+ this.hooks.optimizeChunkOrder.call(this.chunks);
+ this.hooks.beforeChunkIds.call(this.chunks);
+ this.applyChunkIds();
+ this.hooks.optimizeChunkIds.call(this.chunks);
+ this.hooks.afterOptimizeChunkIds.call(this.chunks);
+
+ this.sortItemsWithChunkIds();
+
+ if (shouldRecord) {
+ this.hooks.recordModules.call(this.modules, this.records);
+ this.hooks.recordChunks.call(this.chunks, this.records);
+ }
+
+ this.hooks.beforeHash.call();
+ this.createHash();
+ this.hooks.afterHash.call();
+
+ if (shouldRecord) {
+ this.hooks.recordHash.call(this.records);
+ }
+
+ this.hooks.beforeModuleAssets.call();
+ this.createModuleAssets();
+ if (this.hooks.shouldGenerateChunkAssets.call() !== false) {
+ this.hooks.beforeChunkAssets.call();
+ this.createChunkAssets();
+ }
+ this.hooks.additionalChunkAssets.call(this.chunks);
+ this.summarizeDependencies();
+ if (shouldRecord) {
+ this.hooks.record.call(this, this.records);
+ }
+
+ this.hooks.additionalAssets.callAsync(err => {
+ if (err) {
+ return callback(err);
+ }
+ this.hooks.optimizeChunkAssets.callAsync(this.chunks, err => {
+ if (err) {
+ return callback(err);
+ }
+ this.hooks.afterOptimizeChunkAssets.call(this.chunks);
+ this.hooks.optimizeAssets.callAsync(this.assets, err => {
+ if (err) {
+ return callback(err);
+ }
+ this.hooks.afterOptimizeAssets.call(this.assets);
+ if (this.hooks.needAdditionalSeal.call()) {
+ this.unseal();
+ return this.seal(callback);
+ }
+ return this.hooks.afterSeal.callAsync(callback);
+ });
+ });
+ });
+ });
+ }
+
+ /**
+ * @param {Module[]} modules the modules array on compilation to perform the sort for
+ * @returns {void}
+ */
+ sortModules(modules) {
+ // 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);
+ }
+
+ /**
+ * @param {Module} module moulde to report from
+ * @param {DependenciesBlock[]} blocks blocks to report from
+ * @returns {void}
+ */
+ reportDependencyErrorsAndWarnings(module, blocks) {
+ for (let indexBlock = 0; indexBlock < blocks.length; indexBlock++) {
+ const block = blocks[indexBlock];
+ const dependencies = block.dependencies;
+
+ for (let indexDep = 0; indexDep < dependencies.length; indexDep++) {
+ const d = dependencies[indexDep];
+
+ const warnings = d.getWarnings();
+ if (warnings) {
+ for (let indexWar = 0; indexWar < warnings.length; indexWar++) {
+ const w = warnings[indexWar];
+
+ const warning = new ModuleDependencyWarning(module, w, d.loc);
+ this.warnings.push(warning);
+ }
+ }
+ const errors = d.getErrors();
+ if (errors) {
+ for (let indexErr = 0; indexErr < errors.length; indexErr++) {
+ const e = errors[indexErr];
+
+ const error = new ModuleDependencyError(module, e, d.loc);
+ this.errors.push(error);
+ }
+ }
+ }
+
+ this.reportDependencyErrorsAndWarnings(module, block.blocks);
+ }
+ }
+
+ /**
+ * @param {TODO} groupOptions options for the chunk group
+ * @param {Module} module the module the references the chunk group
+ * @param {DependencyLocation} loc the location from with the chunk group is referenced (inside of module)
+ * @param {string} request the request from which the the chunk group is referenced
+ * @returns {ChunkGroup} the new or existing chunk group
+ */
+ addChunkInGroup(groupOptions, module, loc, request) {
+ if (typeof groupOptions === "string") {
+ groupOptions = { name: groupOptions };
+ }
+ const name = groupOptions.name;
+ if (name) {
+ const chunkGroup = this.namedChunkGroups.get(name);
+ if (chunkGroup !== undefined) {
+ chunkGroup.addOptions(groupOptions);
+ if (module) {
+ chunkGroup.addOrigin(module, loc, request);
+ }
+ return chunkGroup;
+ }
+ }
+ const chunkGroup = new ChunkGroup(groupOptions);
+ if (module) chunkGroup.addOrigin(module, loc, request);
+ const chunk = this.addChunk(name);
+
+ GraphHelpers.connectChunkGroupAndChunk(chunkGroup, chunk);
+
+ this.chunkGroups.push(chunkGroup);
+ if (name) {
+ this.namedChunkGroups.set(name, chunkGroup);
+ }
+ return chunkGroup;
+ }
+
+ /**
+ * This method first looks to see if a name is provided for a new chunk,
+ * and first looks to see if any named chunks already exist and reuse that chunk instead.
+ *
+ * @param {string=} name optional chunk name to be provided
+ * @returns {Chunk} create a chunk (invoked during seal event)
+ */
+ addChunk(name) {
+ if (name) {
+ const chunk = this.namedChunks.get(name);
+ if (chunk !== undefined) {
+ return chunk;
+ }
+ }
+ const chunk = new Chunk(name);
+ this.chunks.push(chunk);
+ if (name) {
+ this.namedChunks.set(name, chunk);
+ }
+ return chunk;
+ }
+
+ /**
+ * @param {Module} module module to assign depth
+ * @returns {void}
+ */
+ assignDepth(module) {
+ const queue = new Set([module]);
+ let depth;
+
+ module.depth = 0;
+
+ /**
+ * @param {Module} module module for processeing
+ * @returns {void}
+ */
+ const enqueueJob = module => {
+ const d = module.depth;
+ if (typeof d === "number" && d <= depth) return;
+ queue.add(module);
+ module.depth = depth;
+ };
+
+ /**
+ * @param {Dependency} dependency dependency to assign depth to
+ * @returns {void}
+ */
+ const assignDepthToDependency = dependency => {
+ if (dependency.module) {
+ enqueueJob(dependency.module);
+ }
+ };
+
+ /**
+ * @param {DependenciesBlock} block block to assign depth to
+ * @returns {void}
+ */
+ const assignDepthToDependencyBlock = block => {
+ if (block.variables) {
+ iterationBlockVariable(block.variables, assignDepthToDependency);
+ }
+
+ if (block.dependencies) {
+ iterationOfArrayCallback(block.dependencies, assignDepthToDependency);
+ }
+
+ if (block.blocks) {
+ iterationOfArrayCallback(block.blocks, assignDepthToDependencyBlock);
+ }
+ };
+
+ for (module of queue) {
+ queue.delete(module);
+ depth = module.depth;
+
+ depth++;
+ assignDepthToDependencyBlock(module);
+ }
+ }
+
+ /**
+ * @param {Module} module the module containing the dependency
+ * @param {Dependency} dependency the dependency
+ * @returns {DependencyReference} a reference for the dependency
+ */
+ getDependencyReference(module, dependency) {
+ // TODO remove dep.getReference existence check in webpack 5
+ if (typeof dependency.getReference !== "function") return null;
+ const ref = dependency.getReference();
+ if (!ref) return null;
+ return this.hooks.dependencyReference.call(ref, dependency, module);
+ }
+
+ /**
+ * This method creates the Chunk graph from the Module graph
+ * @private
+ * @param {TODO[]} inputChunkGroups chunk groups which are processed
+ * @returns {void}
+ */
+ processDependenciesBlocksForChunkGroups(inputChunkGroups) {
+ // Process is splitting into two parts:
+ // Part one traverse the module graph and builds a very basic chunks graph
+ // in chunkDependencies.
+ // Part two traverse every possible way through the basic chunk graph and
+ // tracks the available modules. While traversing it connects chunks with
+ // eachother and Blocks with Chunks. It stops traversing when all modules
+ // for a chunk are already available. So it doesn't connect unneeded chunks.
+
+ /** @type {Map<ChunkGroup, {block: AsyncDependenciesBlock, chunkGroup: ChunkGroup}[]>} */
+ const chunkDependencies = new Map();
+ const allCreatedChunkGroups = new Set();
+
+ // PREPARE
+ /** @type {Map<DependenciesBlock, { modules: Module[], blocks: AsyncDependenciesBlock[]}>} */
+ const blockInfoMap = new Map();
+
+ /**
+ * @param {Dependency} d dependency to iterate over
+ * @returns {void}
+ */
+ const iteratorDependency = d => {
+ // We skip Dependencies without Reference
+ const ref = this.getDependencyReference(currentModule, d);
+ if (!ref) {
+ return;
+ }
+ // We skip Dependencies without Module pointer
+ const refModule = ref.module;
+ if (!refModule) {
+ return;
+ }
+ // We skip weak Dependencies
+ if (ref.weak) {
+ return;
+ }
+
+ blockInfoModules.add(refModule);
+ };
+
+ /**
+ * @param {AsyncDependenciesBlock} b blocks to prepare
+ * @returns {void}
+ */
+ const iteratorBlockPrepare = b => {
+ blockInfoBlocks.push(b);
+ blockQueue.push(b);
+ };
+
+ /** @type {Module} */
+ let currentModule;
+ /** @type {DependenciesBlock} */
+ let block;
+ /** @type {DependenciesBlock[]} */
+ let blockQueue;
+ /** @type {Set<Module>} */
+ let blockInfoModules;
+ /** @type {AsyncDependenciesBlock[]} */
+ let blockInfoBlocks;
+
+ for (const module of this.modules) {
+ blockQueue = [module];
+ currentModule = module;
+ while (blockQueue.length > 0) {
+ block = blockQueue.pop();
+ blockInfoModules = new Set();
+ blockInfoBlocks = [];
+
+ if (block.variables) {
+ iterationBlockVariable(block.variables, iteratorDependency);
+ }
+
+ if (block.dependencies) {
+ iterationOfArrayCallback(block.dependencies, iteratorDependency);
+ }
+
+ if (block.blocks) {
+ iterationOfArrayCallback(block.blocks, iteratorBlockPrepare);
+ }
+
+ const blockInfo = {
+ modules: Array.from(blockInfoModules),
+ blocks: blockInfoBlocks
+ };
+ blockInfoMap.set(block, blockInfo);
+ }
+ }
+
+ // PART ONE
+
+ /** @type {Map<ChunkGroup, { index: number, index2: number }>} */
+ const chunkGroupCounters = new Map();
+ for (const chunkGroup of inputChunkGroups) {
+ chunkGroupCounters.set(chunkGroup, { index: 0, index2: 0 });
+ }
+
+ let nextFreeModuleIndex = 0;
+ let nextFreeModuleIndex2 = 0;
+
+ /** @type {Map<DependenciesBlock, ChunkGroup>} */
+ const blockChunkGroups = new Map();
+
+ /** @type {Set<DependenciesBlock>} */
+ const blocksWithNestedBlocks = new Set();
+
+ const ADD_AND_ENTER_MODULE = 0;
+ const ENTER_MODULE = 1;
+ const PROCESS_BLOCK = 2;
+ const LEAVE_MODULE = 3;
+
+ /**
+ * @typedef {Object} QueueItem
+ * @property {number} action
+ * @property {DependenciesBlock} block
+ * @property {Module} module
+ * @property {Chunk} chunk
+ * @property {ChunkGroup} chunkGroup
+ */
+
+ /**
+ * @param {ChunkGroup} chunkGroup chunk group
+ * @returns {QueueItem} queue item
+ */
+ const chunkGroupToQueueItem = chunkGroup => ({
+ action: ENTER_MODULE,
+ block: chunkGroup.chunks[0].entryModule,
+ module: chunkGroup.chunks[0].entryModule,
+ chunk: chunkGroup.chunks[0],
+ chunkGroup
+ });
+
+ // Start with the provided modules/chunks
+ /** @type {QueueItem[]} */
+ let queue = inputChunkGroups.map(chunkGroupToQueueItem).reverse();
+ /** @type {QueueItem[]} */
+ let queueDelayed = [];
+
+ /** @type {Module} */
+ let module;
+ /** @type {Chunk} */
+ let chunk;
+ /** @type {ChunkGroup} */
+ let chunkGroup;
+
+ // For each async Block in graph
+ /**
+ * @param {AsyncDependenciesBlock} b iterating over each Async DepBlock
+ * @returns {void}
+ */
+ const iteratorBlock = b => {
+ // 1. We create a chunk for this Block
+ // but only once (blockChunkGroups map)
+ let c = blockChunkGroups.get(b);
+ if (c === undefined) {
+ c = this.namedChunkGroups.get(b.chunkName);
+ if (c && c.isInitial()) {
+ this.errors.push(
+ new AsyncDependencyToInitialChunkError(b.chunkName, module, b.loc)
+ );
+ c = chunkGroup;
+ } else {
+ c = this.addChunkInGroup(
+ b.groupOptions || b.chunkName,
+ module,
+ b.loc,
+ b.request
+ );
+ chunkGroupCounters.set(c, { index: 0, index2: 0 });
+ blockChunkGroups.set(b, c);
+ allCreatedChunkGroups.add(c);
+ }
+ } else {
+ // TODO webpack 5 remove addOptions check
+ if (c.addOptions) c.addOptions(b.groupOptions);
+ c.addOrigin(module, b.loc, b.request);
+ }
+
+ // 2. We store the Block+Chunk mapping as dependency for the chunk
+ let deps = chunkDependencies.get(chunkGroup);
+ if (!deps) chunkDependencies.set(chunkGroup, (deps = []));
+ deps.push({
+ block: b,
+ chunkGroup: c
+ });
+
+ // 3. We enqueue the DependenciesBlock for traversal
+ queueDelayed.push({
+ action: PROCESS_BLOCK,
+ block: b,
+ module: module,
+ chunk: c.chunks[0],
+ chunkGroup: c
+ });
+ };
+
+ // Iterative traversal of the Module graph
+ // Recursive would be simpler to write but could result in Stack Overflows
+ while (queue.length) {
+ while (queue.length) {
+ const queueItem = queue.pop();
+ module = queueItem.module;
+ block = queueItem.block;
+ chunk = queueItem.chunk;
+ chunkGroup = queueItem.chunkGroup;
+
+ switch (queueItem.action) {
+ case ADD_AND_ENTER_MODULE: {
+ // We connect Module and Chunk when not already done
+ if (chunk.addModule(module)) {
+ module.addChunk(chunk);
+ } else {
+ // already connected, skip it
+ break;
+ }
+ }
+ // fallthrough
+ case ENTER_MODULE: {
+ if (chunkGroup !== undefined) {
+ const index = chunkGroup.getModuleIndex(module);
+ if (index === undefined) {
+ chunkGroup.setModuleIndex(
+ module,
+ chunkGroupCounters.get(chunkGroup).index++
+ );
+ }
+ }
+
+ if (module.index === null) {
+ module.index = nextFreeModuleIndex++;
+ }
+
+ queue.push({
+ action: LEAVE_MODULE,
+ block,
+ module,
+ chunk,
+ chunkGroup
+ });
+ }
+ // fallthrough
+ case PROCESS_BLOCK: {
+ // get prepared block info
+ const blockInfo = blockInfoMap.get(block);
+
+ // Traverse all referenced modules
+ for (let i = blockInfo.modules.length - 1; i >= 0; i--) {
+ const refModule = blockInfo.modules[i];
+ if (chunk.containsModule(refModule)) {
+ // skip early if already connected
+ continue;
+ }
+ // enqueue the add and enter to enter in the correct order
+ // this is relevant with circular dependencies
+ queue.push({
+ action: ADD_AND_ENTER_MODULE,
+ block: refModule,
+ module: refModule,
+ chunk,
+ chunkGroup
+ });
+ }
+
+ // Traverse all Blocks
+ iterationOfArrayCallback(blockInfo.blocks, iteratorBlock);
+
+ if (blockInfo.blocks.length > 0 && module !== block) {
+ blocksWithNestedBlocks.add(block);
+ }
+ break;
+ }
+ case LEAVE_MODULE: {
+ if (chunkGroup !== undefined) {
+ const index = chunkGroup.getModuleIndex2(module);
+ if (index === undefined) {
+ chunkGroup.setModuleIndex2(
+ module,
+ chunkGroupCounters.get(chunkGroup).index2++
+ );
+ }
+ }
+
+ if (module.index2 === null) {
+ module.index2 = nextFreeModuleIndex2++;
+ }
+ break;
+ }
+ }
+ }
+ const tempQueue = queue;
+ queue = queueDelayed.reverse();
+ queueDelayed = tempQueue;
+ }
+
+ // PART TWO
+ /** @type {Set<Module>} */
+ let availableModules;
+ let newAvailableModules;
+ /** @type {Queue<AvailableModulesChunkGroupMapping>} */
+ const queue2 = new Queue(
+ inputChunkGroups.map(chunkGroup => ({
+ chunkGroup,
+ availableModules: new Set()
+ }))
+ );
+
+ /**
+ * Helper function to check if all modules of a chunk are available
+ *
+ * @param {ChunkGroup} chunkGroup the chunkGroup to scan
+ * @param {Set<Module>} availableModules the comparitor set
+ * @returns {boolean} return true if all modules of a chunk are available
+ */
+ const areModulesAvailable = (chunkGroup, availableModules) => {
+ for (const chunk of chunkGroup.chunks) {
+ for (const module of chunk.modulesIterable) {
+ if (!availableModules.has(module)) return false;
+ }
+ }
+ return true;
+ };
+
+ // For each edge in the basic chunk graph
+ /**
+ * @param {TODO} dep the dependency used for filtering
+ * @returns {boolean} used to filter "edges" (aka Dependencies) that were pointing
+ * to modules that are already available. Also filters circular dependencies in the chunks graph
+ */
+ const filterFn = dep => {
+ const depChunkGroup = dep.chunkGroup;
+ if (blocksWithNestedBlocks.has(dep.block)) return true;
+ if (areModulesAvailable(depChunkGroup, newAvailableModules)) return false; // break all modules are already available
+ return true;
+ };
+
+ /** @type {Map<ChunkGroup, Set<Module>>} */
+ const minAvailableModulesMap = new Map();
+
+ // Iterative traversing of the basic chunk graph
+ while (queue2.length) {
+ const queueItem = queue2.dequeue();
+ chunkGroup = queueItem.chunkGroup;
+ availableModules = queueItem.availableModules;
+
+ // 1. Get minimal available modules
+ // It doesn't make sense to traverse a chunk again with more available modules.
+ // This step calculates the minimal available modules and skips traversal when
+ // the list didn't shrink.
+ let minAvailableModules = minAvailableModulesMap.get(chunkGroup);
+ if (minAvailableModules === undefined) {
+ minAvailableModulesMap.set(chunkGroup, new Set(availableModules));
+ } else {
+ let deletedModules = false;
+ for (const m of minAvailableModules) {
+ if (!availableModules.has(m)) {
+ minAvailableModules.delete(m);
+ deletedModules = true;
+ }
+ }
+ if (!deletedModules) continue;
+ availableModules = minAvailableModules;
+ }
+
+ // 2. Get the edges at this point of the graph
+ const deps = chunkDependencies.get(chunkGroup);
+ if (!deps) continue;
+ if (deps.length === 0) continue;
+
+ // 3. Create a new Set of available modules at this points
+ newAvailableModules = new Set(availableModules);
+ for (const chunk of chunkGroup.chunks) {
+ for (const m of chunk.modulesIterable) {
+ newAvailableModules.add(m);
+ }
+ }
+
+ // 4. Filter edges with available modules
+ const filteredDeps = deps.filter(filterFn);
+
+ // 5. Foreach remaining edge
+ const nextChunkGroups = new Set();
+ for (let i = 0; i < filteredDeps.length; i++) {
+ const dep = filteredDeps[i];
+ const depChunkGroup = dep.chunkGroup;
+ const depBlock = dep.block;
+
+ // 6. Connect block with chunk
+ GraphHelpers.connectDependenciesBlockAndChunkGroup(
+ depBlock,
+ depChunkGroup
+ );
+
+ // 7. Connect chunk with parent
+ GraphHelpers.connectChunkGroupParentAndChild(chunkGroup, depChunkGroup);
+
+ nextChunkGroups.add(depChunkGroup);
+ }
+
+ // 8. Enqueue further traversal
+ for (const nextChunkGroup of nextChunkGroups) {
+ queue2.enqueue({
+ chunkGroup: nextChunkGroup,
+ availableModules: newAvailableModules
+ });
+ }
+ }
+
+ // Remove all unconnected chunk groups
+ for (const chunkGroup of allCreatedChunkGroups) {
+ if (chunkGroup.getNumberOfParents() === 0) {
+ for (const chunk of chunkGroup.chunks) {
+ const idx = this.chunks.indexOf(chunk);
+ if (idx >= 0) this.chunks.splice(idx, 1);
+ chunk.remove("unconnected");
+ }
+ chunkGroup.remove("unconnected");
+ }
+ }
+ }
+
+ /**
+ *
+ * @param {Module} module module relationship for removal
+ * @param {DependenciesBlockLike} block //TODO: good description
+ * @returns {void}
+ */
+ removeReasonsOfDependencyBlock(module, block) {
+ const iteratorDependency = d => {
+ if (!d.module) {
+ return;
+ }
+ if (d.module.removeReason(module, d)) {
+ for (const chunk of d.module.chunksIterable) {
+ this.patchChunksAfterReasonRemoval(d.module, chunk);
+ }
+ }
+ };
+
+ if (block.blocks) {
+ iterationOfArrayCallback(block.blocks, block =>
+ this.removeReasonsOfDependencyBlock(module, block)
+ );
+ }
+
+ if (block.dependencies) {
+ iterationOfArrayCallback(block.dependencies, iteratorDependency);
+ }
+
+ if (block.variables) {
+ iterationBlockVariable(block.variables, iteratorDependency);
+ }
+ }
+
+ /**
+ * @param {Module} module module to patch tie
+ * @param {Chunk} chunk chunk to patch tie
+ * @returns {void}
+ */
+ patchChunksAfterReasonRemoval(module, chunk) {
+ if (!module.hasReasons()) {
+ this.removeReasonsOfDependencyBlock(module, module);
+ }
+ if (!module.hasReasonForChunk(chunk)) {
+ if (module.removeChunk(chunk)) {
+ this.removeChunkFromDependencies(module, chunk);
+ }
+ }
+ }
+
+ /**
+ *
+ * @param {DependenciesBlock} block block tie for Chunk
+ * @param {Chunk} chunk chunk to remove from dep
+ * @returns {void}
+ */
+ removeChunkFromDependencies(block, chunk) {
+ const iteratorDependency = d => {
+ if (!d.module) {
+ return;
+ }
+ this.patchChunksAfterReasonRemoval(d.module, chunk);
+ };
+
+ const blocks = block.blocks;
+ for (let indexBlock = 0; indexBlock < blocks.length; indexBlock++) {
+ const asyncBlock = blocks[indexBlock];
+ // Grab all chunks from the first Block's AsyncDepBlock
+ const chunks = asyncBlock.chunkGroup.chunks;
+ // For each chunk in chunkGroup
+ for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
+ const iteratedChunk = chunks[indexChunk];
+ asyncBlock.chunkGroup.removeChunk(iteratedChunk);
+ asyncBlock.chunkGroup.removeParent(iteratedChunk);
+ // Recurse
+ this.removeChunkFromDependencies(block, iteratedChunk);
+ }
+ }
+
+ if (block.dependencies) {
+ iterationOfArrayCallback(block.dependencies, iteratorDependency);
+ }
+
+ if (block.variables) {
+ iterationBlockVariable(block.variables, iteratorDependency);
+ }
+ }
+
+ applyModuleIds() {
+ const unusedIds = [];
+ let nextFreeModuleId = 0;
+ const usedIds = new Set();
+ if (this.usedModuleIds) {
+ for (const id of this.usedModuleIds) {
+ usedIds.add(id);
+ }
+ }
+
+ const modules1 = this.modules;
+ for (let indexModule1 = 0; indexModule1 < modules1.length; indexModule1++) {
+ const module1 = modules1[indexModule1];
+ if (module1.id !== null) {
+ usedIds.add(module1.id);
+ }
+ }
+
+ if (usedIds.size > 0) {
+ let usedIdMax = -1;
+ for (const usedIdKey of usedIds) {
+ if (typeof usedIdKey !== "number") {
+ continue;
+ }
+
+ usedIdMax = Math.max(usedIdMax, usedIdKey);
+ }
+
+ let lengthFreeModules = (nextFreeModuleId = usedIdMax + 1);
+
+ while (lengthFreeModules--) {
+ if (!usedIds.has(lengthFreeModules)) {
+ unusedIds.push(lengthFreeModules);
+ }
+ }
+ }
+
+ const modules2 = this.modules;
+ for (let indexModule2 = 0; indexModule2 < modules2.length; indexModule2++) {
+ const module2 = modules2[indexModule2];
+ if (module2.id === null) {
+ if (unusedIds.length > 0) {
+ module2.id = unusedIds.pop();
+ } else {
+ module2.id = nextFreeModuleId++;
+ }
+ }
+ }
+ }
+
+ applyChunkIds() {
+ /** @type {Set<number>} */
+ const usedIds = new Set();
+
+ // Get used ids from usedChunkIds property (i. e. from records)
+ if (this.usedChunkIds) {
+ for (const id of this.usedChunkIds) {
+ if (typeof id !== "number") {
+ continue;
+ }
+
+ usedIds.add(id);
+ }
+ }
+
+ // Get used ids from existing chunks
+ const chunks = this.chunks;
+ for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
+ const chunk = chunks[indexChunk];
+ const usedIdValue = chunk.id;
+
+ if (typeof usedIdValue !== "number") {
+ continue;
+ }
+
+ usedIds.add(usedIdValue);
+ }
+
+ // Calculate maximum assigned chunk id
+ let nextFreeChunkId = -1;
+ for (const id of usedIds) {
+ nextFreeChunkId = Math.max(nextFreeChunkId, id);
+ }
+ nextFreeChunkId++;
+
+ // Determine free chunk ids from 0 to maximum
+ /** @type {number[]} */
+ const unusedIds = [];
+ if (nextFreeChunkId > 0) {
+ let index = nextFreeChunkId;
+ while (index--) {
+ if (!usedIds.has(index)) {
+ unusedIds.push(index);
+ }
+ }
+ }
+
+ // Assign ids to chunk which has no id
+ for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
+ const chunk = chunks[indexChunk];
+ if (chunk.id === null) {
+ if (unusedIds.length > 0) {
+ chunk.id = unusedIds.pop();
+ } else {
+ chunk.id = nextFreeChunkId++;
+ }
+ }
+ if (!chunk.ids) {
+ chunk.ids = [chunk.id];
+ }
+ }
+ }
+
+ sortItemsWithModuleIds() {
+ this.modules.sort(byIdOrIdentifier);
+
+ const modules = this.modules;
+ for (let indexModule = 0; indexModule < modules.length; indexModule++) {
+ modules[indexModule].sortItems(false);
+ }
+
+ const chunks = this.chunks;
+ for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
+ chunks[indexChunk].sortItems();
+ }
+ }
+
+ sortItemsWithChunkIds() {
+ for (const chunkGroup of this.chunkGroups) {
+ chunkGroup.sortItems();
+ }
+
+ this.chunks.sort(byId);
+
+ for (
+ let indexModule = 0;
+ indexModule < this.modules.length;
+ indexModule++
+ ) {
+ this.modules[indexModule].sortItems(true);
+ }
+
+ const chunks = this.chunks;
+ for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
+ chunks[indexChunk].sortItems();
+ }
+
+ /**
+ * Used to sort errors and warnings in compilation. this.warnings, and
+ * this.errors contribute to the compilation hash and therefore should be
+ * updated whenever other references (having a chunk id) are sorted. This preserves the hash
+ * integrity
+ *
+ * @param {WebpackError} a first WebpackError instance (including subclasses)
+ * @param {WebpackError} b second WebpackError instance (including subclasses)
+ * @returns {-1|0|1} sort order index
+ */
+ const byMessage = (a, b) => {
+ const ma = `${a.message}`;
+ const mb = `${b.message}`;
+ if (ma < mb) return -1;
+ if (mb < ma) return 1;
+ return 0;
+ };
+
+ this.errors.sort(byMessage);
+ this.warnings.sort(byMessage);
+ this.children.sort(byNameOrHash);
+ }
+
+ summarizeDependencies() {
+ this.fileDependencies = new SortableSet(this.compilationDependencies);
+ this.contextDependencies = new SortableSet();
+ this.missingDependencies = new SortableSet();
+
+ for (
+ let indexChildren = 0;
+ indexChildren < this.children.length;
+ indexChildren++
+ ) {
+ const child = this.children[indexChildren];
+
+ addAllToSet(this.fileDependencies, child.fileDependencies);
+ addAllToSet(this.contextDependencies, child.contextDependencies);
+ addAllToSet(this.missingDependencies, child.missingDependencies);
+ }
+
+ for (
+ let indexModule = 0;
+ indexModule < this.modules.length;
+ indexModule++
+ ) {
+ const module = this.modules[indexModule];
+
+ if (module.buildInfo.fileDependencies) {
+ addAllToSet(this.fileDependencies, module.buildInfo.fileDependencies);
+ }
+ if (module.buildInfo.contextDependencies) {
+ addAllToSet(
+ this.contextDependencies,
+ module.buildInfo.contextDependencies
+ );
+ }
+ }
+ for (const error of this.errors) {
+ if (
+ typeof error.missing === "object" &&
+ error.missing &&
+ error.missing[Symbol.iterator]
+ ) {
+ addAllToSet(this.missingDependencies, error.missing);
+ }
+ }
+ this.fileDependencies.sort();
+ this.contextDependencies.sort();
+ this.missingDependencies.sort();
+ }
+
+ createHash() {
+ const outputOptions = this.outputOptions;
+ const hashFunction = outputOptions.hashFunction;
+ const hashDigest = outputOptions.hashDigest;
+ const hashDigestLength = outputOptions.hashDigestLength;
+ const hash = createHash(hashFunction);
+ if (outputOptions.hashSalt) {
+ hash.update(outputOptions.hashSalt);
+ }
+ this.mainTemplate.updateHash(hash);
+ this.chunkTemplate.updateHash(hash);
+ for (const key of Object.keys(this.moduleTemplates).sort()) {
+ this.moduleTemplates[key].updateHash(hash);
+ }
+ for (const child of this.children) {
+ hash.update(child.hash);
+ }
+ for (const warning of this.warnings) {
+ hash.update(`${warning.message}`);
+ }
+ for (const error of this.errors) {
+ hash.update(`${error.message}`);
+ }
+ const modules = this.modules;
+ for (let i = 0; i < modules.length; i++) {
+ const module = modules[i];
+ const moduleHash = createHash(hashFunction);
+ module.updateHash(moduleHash);
+ module.hash = moduleHash.digest(hashDigest);
+ module.renderedHash = module.hash.substr(0, hashDigestLength);
+ }
+ // clone needed as sort below is inplace mutation
+ const chunks = this.chunks.slice();
+ /**
+ * sort here will bring all "falsy" values to the beginning
+ * this is needed as the "hasRuntime()" chunks are dependent on the
+ * hashes of the non-runtime chunks.
+ */
+ chunks.sort((a, b) => {
+ const aEntry = a.hasRuntime();
+ const bEntry = b.hasRuntime();
+ if (aEntry && !bEntry) return 1;
+ if (!aEntry && bEntry) return -1;
+ return byId(a, b);
+ });
+ for (let i = 0; i < chunks.length; i++) {
+ const chunk = chunks[i];
+ const chunkHash = createHash(hashFunction);
+ if (outputOptions.hashSalt) {
+ chunkHash.update(outputOptions.hashSalt);
+ }
+ chunk.updateHash(chunkHash);
+ const template = chunk.hasRuntime()
+ ? this.mainTemplate
+ : this.chunkTemplate;
+ template.updateHashForChunk(
+ chunkHash,
+ chunk,
+ this.moduleTemplates.javascript,
+ this.dependencyTemplates
+ );
+ this.hooks.chunkHash.call(chunk, chunkHash);
+ chunk.hash = chunkHash.digest(hashDigest);
+ hash.update(chunk.hash);
+ chunk.renderedHash = chunk.hash.substr(0, hashDigestLength);
+ this.hooks.contentHash.call(chunk);
+ }
+ this.fullHash = hash.digest(hashDigest);
+ this.hash = this.fullHash.substr(0, hashDigestLength);
+ }
+
+ /**
+ * @param {string} update extra information
+ * @returns {void}
+ */
+ modifyHash(update) {
+ const outputOptions = this.outputOptions;
+ const hashFunction = outputOptions.hashFunction;
+ const hashDigest = outputOptions.hashDigest;
+ const hashDigestLength = outputOptions.hashDigestLength;
+ const hash = createHash(hashFunction);
+ hash.update(this.fullHash);
+ hash.update(update);
+ this.fullHash = hash.digest(hashDigest);
+ this.hash = this.fullHash.substr(0, hashDigestLength);
+ }
+
+ createModuleAssets() {
+ for (let i = 0; i < this.modules.length; i++) {
+ const module = this.modules[i];
+ if (module.buildInfo.assets) {
+ for (const assetName of Object.keys(module.buildInfo.assets)) {
+ const fileName = this.getPath(assetName);
+ this.assets[fileName] = module.buildInfo.assets[assetName];
+ this.hooks.moduleAsset.call(module, fileName);
+ }
+ }
+ }
+ }
+
+ createChunkAssets() {
+ const outputOptions = this.outputOptions;
+ const cachedSourceMap = new Map();
+ /** @type {Map<string, {hash: string, source: Source, chunk: Chunk}>} */
+ const alreadyWrittenFiles = new Map();
+ for (let i = 0; i < this.chunks.length; i++) {
+ const chunk = this.chunks[i];
+ chunk.files = [];
+ let source;
+ let file;
+ let filenameTemplate;
+ try {
+ const template = chunk.hasRuntime()
+ ? this.mainTemplate
+ : this.chunkTemplate;
+ const manifest = template.getRenderManifest({
+ chunk,
+ hash: this.hash,
+ fullHash: this.fullHash,
+ outputOptions,
+ moduleTemplates: this.moduleTemplates,
+ dependencyTemplates: this.dependencyTemplates
+ }); // [{ render(), filenameTemplate, pathOptions, identifier, hash }]
+ for (const fileManifest of manifest) {
+ const cacheName = fileManifest.identifier;
+ const usedHash = fileManifest.hash;
+ filenameTemplate = fileManifest.filenameTemplate;
+ file = this.getPath(filenameTemplate, fileManifest.pathOptions);
+
+ // check if the same filename was already written by another chunk
+ const alreadyWritten = alreadyWrittenFiles.get(file);
+ if (alreadyWritten !== undefined) {
+ if (alreadyWritten.hash === usedHash) {
+ if (this.cache) {
+ this.cache[cacheName] = {
+ hash: usedHash,
+ source: alreadyWritten.source
+ };
+ }
+ chunk.files.push(file);
+ this.hooks.chunkAsset.call(chunk, file);
+ continue;
+ } else {
+ throw new Error(
+ `Conflict: Multiple chunks emit assets to the same filename ${file}` +
+ ` (chunks ${alreadyWritten.chunk.id} and ${chunk.id})`
+ );
+ }
+ }
+ if (
+ this.cache &&
+ this.cache[cacheName] &&
+ this.cache[cacheName].hash === usedHash
+ ) {
+ source = this.cache[cacheName].source;
+ } else {
+ source = fileManifest.render();
+ // Ensure that source is a cached source to avoid additional cost because of repeated access
+ if (!(source instanceof CachedSource)) {
+ const cacheEntry = cachedSourceMap.get(source);
+ if (cacheEntry) {
+ source = cacheEntry;
+ } else {
+ const cachedSource = new CachedSource(source);
+ cachedSourceMap.set(source, cachedSource);
+ source = cachedSource;
+ }
+ }
+ if (this.cache) {
+ this.cache[cacheName] = {
+ hash: usedHash,
+ source
+ };
+ }
+ }
+ if (this.assets[file] && this.assets[file] !== source) {
+ throw new Error(
+ `Conflict: Multiple assets emit to the same filename ${file}`
+ );
+ }
+ this.assets[file] = source;
+ chunk.files.push(file);
+ this.hooks.chunkAsset.call(chunk, file);
+ alreadyWrittenFiles.set(file, {
+ hash: usedHash,
+ source,
+ chunk
+ });
+ }
+ } catch (err) {
+ this.errors.push(
+ new ChunkRenderError(chunk, file || filenameTemplate, err)
+ );
+ }
+ }
+ }
+
+ /**
+ * @param {string} filename used to get asset path with hash
+ * @param {TODO=} data // TODO: figure out this param type
+ * @returns {string} interpolated path
+ */
+ getPath(filename, data) {
+ data = data || {};
+ data.hash = data.hash || this.hash;
+ return this.mainTemplate.getAssetPath(filename, data);
+ }
+
+ /**
+ * This function allows you to run another instance of webpack inside of webpack however as
+ * a child with different settings and configurations (if desired) applied. It copies all hooks, plugins
+ * from parent (or top level compiler) and creates a child Compilation
+ *
+ * @param {string} name name of the child compiler
+ * @param {TODO} outputOptions // Need to convert config schema to types for this
+ * @param {Plugin[]} plugins webpack plugins that will be applied
+ * @returns {Compiler} creates a child Compiler instance
+ */
+ createChildCompiler(name, outputOptions, plugins) {
+ const idx = this.childrenCounters[name] || 0;
+ this.childrenCounters[name] = idx + 1;
+ return this.compiler.createChildCompiler(
+ this,
+ name,
+ idx,
+ outputOptions,
+ plugins
+ );
+ }
+
+ checkConstraints() {
+ /** @type {Set<number|string>} */
+ const usedIds = new Set();
+
+ const modules = this.modules;
+ for (let indexModule = 0; indexModule < modules.length; indexModule++) {
+ const moduleId = modules[indexModule].id;
+ if (moduleId === null) continue;
+ if (usedIds.has(moduleId)) {
+ throw new Error(`checkConstraints: duplicate module id ${moduleId}`);
+ }
+ usedIds.add(moduleId);
+ }
+
+ const chunks = this.chunks;
+ for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
+ const chunk = chunks[indexChunk];
+ if (chunks.indexOf(chunk) !== indexChunk) {
+ throw new Error(
+ `checkConstraints: duplicate chunk in compilation ${chunk.debugId}`
+ );
+ }
+ }
+
+ for (const chunkGroup of this.chunkGroups) {
+ chunkGroup.checkConstraints();
+ }
+ }
+}
+
+// TODO remove in webpack 5
+Compilation.prototype.applyPlugins = util.deprecate(
+ /**
+ * @deprecated
+ * @param {string} name Name
+ * @param {any[]} args Other arguments
+ * @returns {void}
+ * @this {Compilation}
+ */
+ function(name, ...args) {
+ this.hooks[
+ name.replace(/[- ]([a-z])/g, match => match[1].toUpperCase())
+ ].call(...args);
+ },
+ "Compilation.applyPlugins is deprecated. Use new API on `.hooks` instead"
+);
+
+// TODO remove in webpack 5
+Object.defineProperty(Compilation.prototype, "moduleTemplate", {
+ configurable: false,
+ get: util.deprecate(
+ /**
+ * @deprecated
+ * @this {Compilation}
+ * @returns {TODO} module template
+ */
+ function() {
+ return this.moduleTemplates.javascript;
+ },
+ "Compilation.moduleTemplate: Use Compilation.moduleTemplates.javascript instead"
+ ),
+ set: util.deprecate(
+ /**
+ * @deprecated
+ * @param {ModuleTemplate} value Template value
+ * @this {Compilation}
+ * @returns {void}
+ */
+ function(value) {
+ this.moduleTemplates.javascript = value;
+ },
+ "Compilation.moduleTemplate: Use Compilation.moduleTemplates.javascript instead."
+ )
+});
+
+module.exports = Compilation;