UNPKG

74.3 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4 */
5"use strict";
6
7const asyncLib = require("neo-async");
8const util = require("util");
9const { CachedSource } = require("webpack-sources");
10const {
11 Tapable,
12 SyncHook,
13 SyncBailHook,
14 SyncWaterfallHook,
15 AsyncSeriesHook
16} = require("tapable");
17const EntryModuleNotFoundError = require("./EntryModuleNotFoundError");
18const ModuleNotFoundError = require("./ModuleNotFoundError");
19const ModuleDependencyWarning = require("./ModuleDependencyWarning");
20const ModuleDependencyError = require("./ModuleDependencyError");
21const ChunkGroup = require("./ChunkGroup");
22const Chunk = require("./Chunk");
23const Entrypoint = require("./Entrypoint");
24const MainTemplate = require("./MainTemplate");
25const ChunkTemplate = require("./ChunkTemplate");
26const HotUpdateChunkTemplate = require("./HotUpdateChunkTemplate");
27const ModuleTemplate = require("./ModuleTemplate");
28const RuntimeTemplate = require("./RuntimeTemplate");
29const ChunkRenderError = require("./ChunkRenderError");
30const AsyncDependencyToInitialChunkError = require("./AsyncDependencyToInitialChunkError");
31const Stats = require("./Stats");
32const Semaphore = require("./util/Semaphore");
33const createHash = require("./util/createHash");
34const Queue = require("./util/Queue");
35const SortableSet = require("./util/SortableSet");
36const GraphHelpers = require("./GraphHelpers");
37const ModuleDependency = require("./dependencies/ModuleDependency");
38const compareLocations = require("./compareLocations");
39
40/** @typedef {import("./Module")} Module */
41/** @typedef {import("./Compiler")} Compiler */
42/** @typedef {import("webpack-sources").Source} Source */
43/** @typedef {import("./WebpackError")} WebpackError */
44/** @typedef {import("./DependenciesBlockVariable")} DependenciesBlockVariable */
45/** @typedef {import("./dependencies/SingleEntryDependency")} SingleEntryDependency */
46/** @typedef {import("./dependencies/MultiEntryDependency")} MultiEntryDependency */
47/** @typedef {import("./dependencies/DllEntryDependency")} DllEntryDependency */
48/** @typedef {import("./dependencies/DependencyReference")} DependencyReference */
49/** @typedef {import("./DependenciesBlock")} DependenciesBlock */
50/** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */
51/** @typedef {import("./Dependency")} Dependency */
52/** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */
53/** @typedef {import("./Dependency").DependencyTemplate} DependencyTemplate */
54/** @typedef {import("./util/createHash").Hash} Hash */
55
56// TODO use @callback
57/** @typedef {{[assetName: string]: Source}} CompilationAssets */
58/** @typedef {(err: Error|null, result?: Module) => void } ModuleCallback */
59/** @typedef {(err?: Error|null, result?: Module) => void } ModuleChainCallback */
60/** @typedef {(module: Module) => void} OnModuleCallback */
61/** @typedef {(err?: Error|null) => void} Callback */
62/** @typedef {(d: Dependency) => any} DepBlockVarDependenciesCallback */
63/** @typedef {new (...args: any[]) => Dependency} DepConstructor */
64/** @typedef {{apply: () => void}} Plugin */
65
66/**
67 * @typedef {Object} ModuleFactoryCreateDataContextInfo
68 * @property {string} issuer
69 * @property {string} compiler
70 */
71
72/**
73 * @typedef {Object} ModuleFactoryCreateData
74 * @property {ModuleFactoryCreateDataContextInfo} contextInfo
75 * @property {any=} resolveOptions
76 * @property {string} context
77 * @property {Dependency[]} dependencies
78 */
79
80/**
81 * @typedef {Object} ModuleFactory
82 * @property {(data: ModuleFactoryCreateData, callback: ModuleCallback) => any} create
83 */
84
85/**
86 * @typedef {Object} SortedDependency
87 * @property {ModuleFactory} factory
88 * @property {Dependency[]} dependencies
89 */
90
91/**
92 * @typedef {Object} DependenciesBlockLike
93 * @property {Dependency[]} dependencies
94 * @property {AsyncDependenciesBlock[]} blocks
95 * @property {DependenciesBlockVariable[]} variables
96 */
97
98/**
99 * @param {Chunk} a first chunk to sort by id
100 * @param {Chunk} b second chunk to sort by id
101 * @returns {-1|0|1} sort value
102 */
103const byId = (a, b) => {
104 if (typeof a.id !== typeof b.id) {
105 return typeof a.id < typeof b.id ? -1 : 1;
106 }
107 if (a.id < b.id) return -1;
108 if (a.id > b.id) return 1;
109 return 0;
110};
111
112/**
113 * @param {Module} a first module to sort by
114 * @param {Module} b second module to sort by
115 * @returns {-1|0|1} sort value
116 */
117const byIdOrIdentifier = (a, b) => {
118 if (typeof a.id !== typeof b.id) {
119 return typeof a.id < typeof b.id ? -1 : 1;
120 }
121 if (a.id < b.id) return -1;
122 if (a.id > b.id) return 1;
123 const identA = a.identifier();
124 const identB = b.identifier();
125 if (identA < identB) return -1;
126 if (identA > identB) return 1;
127 return 0;
128};
129
130/**
131 * @param {Module} a first module to sort by
132 * @param {Module} b second module to sort by
133 * @returns {-1|0|1} sort value
134 */
135const byIndexOrIdentifier = (a, b) => {
136 if (a.index < b.index) return -1;
137 if (a.index > b.index) return 1;
138 const identA = a.identifier();
139 const identB = b.identifier();
140 if (identA < identB) return -1;
141 if (identA > identB) return 1;
142 return 0;
143};
144
145/**
146 * @param {Compilation} a first compilation to sort by
147 * @param {Compilation} b second compilation to sort by
148 * @returns {-1|0|1} sort value
149 */
150const byNameOrHash = (a, b) => {
151 if (a.name < b.name) return -1;
152 if (a.name > b.name) return 1;
153 if (a.fullHash < b.fullHash) return -1;
154 if (a.fullHash > b.fullHash) return 1;
155 return 0;
156};
157
158/**
159 * @template T
160 * @param {Set<T>} a first set
161 * @param {Set<T>} b second set
162 * @returns {number} cmp
163 */
164const bySetSize = (a, b) => {
165 return a.size - b.size;
166};
167
168/**
169 * @param {DependenciesBlockVariable[]} variables DepBlock Variables to iterate over
170 * @param {DepBlockVarDependenciesCallback} fn callback to apply on iterated elements
171 * @returns {void}
172 */
173const iterationBlockVariable = (variables, fn) => {
174 for (
175 let indexVariable = 0;
176 indexVariable < variables.length;
177 indexVariable++
178 ) {
179 const varDep = variables[indexVariable].dependencies;
180 for (let indexVDep = 0; indexVDep < varDep.length; indexVDep++) {
181 fn(varDep[indexVDep]);
182 }
183 }
184};
185
186/**
187 * @template T
188 * @param {T[]} arr array of elements to iterate over
189 * @param {function(T): void} fn callback applied to each element
190 * @returns {void}
191 */
192const iterationOfArrayCallback = (arr, fn) => {
193 for (let index = 0; index < arr.length; index++) {
194 fn(arr[index]);
195 }
196};
197
198/**
199 * @template T
200 * @param {Set<T>} set set to add items to
201 * @param {Set<T>} otherSet set to add items from
202 * @returns {void}
203 */
204const addAllToSet = (set, otherSet) => {
205 for (const item of otherSet) {
206 set.add(item);
207 }
208};
209
210class Compilation extends Tapable {
211 /**
212 * Creates an instance of Compilation.
213 * @param {Compiler} compiler the compiler which created the compilation
214 */
215 constructor(compiler) {
216 super();
217 this.hooks = {
218 /** @type {SyncHook<Module>} */
219 buildModule: new SyncHook(["module"]),
220 /** @type {SyncHook<Module>} */
221 rebuildModule: new SyncHook(["module"]),
222 /** @type {SyncHook<Module, Error>} */
223 failedModule: new SyncHook(["module", "error"]),
224 /** @type {SyncHook<Module>} */
225 succeedModule: new SyncHook(["module"]),
226
227 /** @type {SyncHook<Dependency, string>} */
228 addEntry: new SyncHook(["entry", "name"]),
229 /** @type {SyncHook<Dependency, string, Error>} */
230 failedEntry: new SyncHook(["entry", "name", "error"]),
231 /** @type {SyncHook<Dependency, string, Module>} */
232 succeedEntry: new SyncHook(["entry", "name", "module"]),
233
234 /** @type {SyncWaterfallHook<DependencyReference, Dependency, Module>} */
235 dependencyReference: new SyncWaterfallHook([
236 "dependencyReference",
237 "dependency",
238 "module"
239 ]),
240
241 /** @type {SyncHook<Module[]>} */
242 finishModules: new SyncHook(["modules"]),
243 /** @type {SyncHook<Module>} */
244 finishRebuildingModule: new SyncHook(["module"]),
245 /** @type {SyncHook} */
246 unseal: new SyncHook([]),
247 /** @type {SyncHook} */
248 seal: new SyncHook([]),
249
250 /** @type {SyncHook} */
251 beforeChunks: new SyncHook([]),
252 /** @type {SyncHook<Chunk[]>} */
253 afterChunks: new SyncHook(["chunks"]),
254
255 /** @type {SyncBailHook<Module[]>} */
256 optimizeDependenciesBasic: new SyncBailHook(["modules"]),
257 /** @type {SyncBailHook<Module[]>} */
258 optimizeDependencies: new SyncBailHook(["modules"]),
259 /** @type {SyncBailHook<Module[]>} */
260 optimizeDependenciesAdvanced: new SyncBailHook(["modules"]),
261 /** @type {SyncBailHook<Module[]>} */
262 afterOptimizeDependencies: new SyncHook(["modules"]),
263
264 /** @type {SyncHook} */
265 optimize: new SyncHook([]),
266 /** @type {SyncBailHook<Module[]>} */
267 optimizeModulesBasic: new SyncBailHook(["modules"]),
268 /** @type {SyncBailHook<Module[]>} */
269 optimizeModules: new SyncBailHook(["modules"]),
270 /** @type {SyncBailHook<Module[]>} */
271 optimizeModulesAdvanced: new SyncBailHook(["modules"]),
272 /** @type {SyncHook<Module[]>} */
273 afterOptimizeModules: new SyncHook(["modules"]),
274
275 /** @type {SyncBailHook<Chunk[], ChunkGroup[]>} */
276 optimizeChunksBasic: new SyncBailHook(["chunks", "chunkGroups"]),
277 /** @type {SyncBailHook<Chunk[], ChunkGroup[]>} */
278 optimizeChunks: new SyncBailHook(["chunks", "chunkGroups"]),
279 /** @type {SyncBailHook<Chunk[], ChunkGroup[]>} */
280 optimizeChunksAdvanced: new SyncBailHook(["chunks", "chunkGroups"]),
281 /** @type {SyncHook<Chunk[], ChunkGroup[]>} */
282 afterOptimizeChunks: new SyncHook(["chunks", "chunkGroups"]),
283
284 /** @type {AsyncSeriesHook<Chunk[], Module[]>} */
285 optimizeTree: new AsyncSeriesHook(["chunks", "modules"]),
286 /** @type {SyncHook<Chunk[], Module[]>} */
287 afterOptimizeTree: new SyncHook(["chunks", "modules"]),
288
289 /** @type {SyncBailHook<Chunk[], Module[]>} */
290 optimizeChunkModulesBasic: new SyncBailHook(["chunks", "modules"]),
291 /** @type {SyncBailHook<Chunk[], Module[]>} */
292 optimizeChunkModules: new SyncBailHook(["chunks", "modules"]),
293 /** @type {SyncBailHook<Chunk[], Module[]>} */
294 optimizeChunkModulesAdvanced: new SyncBailHook(["chunks", "modules"]),
295 /** @type {SyncHook<Chunk[], Module[]>} */
296 afterOptimizeChunkModules: new SyncHook(["chunks", "modules"]),
297 /** @type {SyncBailHook} */
298 shouldRecord: new SyncBailHook([]),
299
300 /** @type {SyncHook<Module[], any>} */
301 reviveModules: new SyncHook(["modules", "records"]),
302 /** @type {SyncHook<Module[]>} */
303 optimizeModuleOrder: new SyncHook(["modules"]),
304 /** @type {SyncHook<Module[]>} */
305 advancedOptimizeModuleOrder: new SyncHook(["modules"]),
306 /** @type {SyncHook<Module[]>} */
307 beforeModuleIds: new SyncHook(["modules"]),
308 /** @type {SyncHook<Module[]>} */
309 moduleIds: new SyncHook(["modules"]),
310 /** @type {SyncHook<Module[]>} */
311 optimizeModuleIds: new SyncHook(["modules"]),
312 /** @type {SyncHook<Module[]>} */
313 afterOptimizeModuleIds: new SyncHook(["modules"]),
314
315 /** @type {SyncHook<Chunk[], any>} */
316 reviveChunks: new SyncHook(["chunks", "records"]),
317 /** @type {SyncHook<Chunk[]>} */
318 optimizeChunkOrder: new SyncHook(["chunks"]),
319 /** @type {SyncHook<Chunk[]>} */
320 beforeChunkIds: new SyncHook(["chunks"]),
321 /** @type {SyncHook<Chunk[]>} */
322 optimizeChunkIds: new SyncHook(["chunks"]),
323 /** @type {SyncHook<Chunk[]>} */
324 afterOptimizeChunkIds: new SyncHook(["chunks"]),
325
326 /** @type {SyncHook<Module[], any>} */
327 recordModules: new SyncHook(["modules", "records"]),
328 /** @type {SyncHook<Chunk[], any>} */
329 recordChunks: new SyncHook(["chunks", "records"]),
330
331 /** @type {SyncHook} */
332 beforeHash: new SyncHook([]),
333 /** @type {SyncHook<Chunk>} */
334 contentHash: new SyncHook(["chunk"]),
335 /** @type {SyncHook} */
336 afterHash: new SyncHook([]),
337 /** @type {SyncHook<any>} */
338 recordHash: new SyncHook(["records"]),
339 /** @type {SyncHook<Compilation, any>} */
340 record: new SyncHook(["compilation", "records"]),
341
342 /** @type {SyncHook} */
343 beforeModuleAssets: new SyncHook([]),
344 /** @type {SyncBailHook} */
345 shouldGenerateChunkAssets: new SyncBailHook([]),
346 /** @type {SyncHook} */
347 beforeChunkAssets: new SyncHook([]),
348 /** @type {SyncHook<Chunk[]>} */
349 additionalChunkAssets: new SyncHook(["chunks"]),
350
351 /** @type {AsyncSeriesHook} */
352 additionalAssets: new AsyncSeriesHook([]),
353 /** @type {AsyncSeriesHook<Chunk[]>} */
354 optimizeChunkAssets: new AsyncSeriesHook(["chunks"]),
355 /** @type {SyncHook<Chunk[]>} */
356 afterOptimizeChunkAssets: new SyncHook(["chunks"]),
357 /** @type {AsyncSeriesHook<CompilationAssets>} */
358 optimizeAssets: new AsyncSeriesHook(["assets"]),
359 /** @type {SyncHook<CompilationAssets>} */
360 afterOptimizeAssets: new SyncHook(["assets"]),
361
362 /** @type {SyncBailHook} */
363 needAdditionalSeal: new SyncBailHook([]),
364 /** @type {AsyncSeriesHook} */
365 afterSeal: new AsyncSeriesHook([]),
366
367 /** @type {SyncHook<Chunk, Hash>} */
368 chunkHash: new SyncHook(["chunk", "chunkHash"]),
369 /** @type {SyncHook<Module, string>} */
370 moduleAsset: new SyncHook(["module", "filename"]),
371 /** @type {SyncHook<Chunk, string>} */
372 chunkAsset: new SyncHook(["chunk", "filename"]),
373
374 /** @type {SyncWaterfallHook<string, TODO>} */
375 assetPath: new SyncWaterfallHook(["filename", "data"]), // TODO MainTemplate
376
377 /** @type {SyncBailHook} */
378 needAdditionalPass: new SyncBailHook([]),
379
380 /** @type {SyncHook<Compiler, string, number>} */
381 childCompiler: new SyncHook([
382 "childCompiler",
383 "compilerName",
384 "compilerIndex"
385 ]),
386
387 // TODO the following hooks are weirdly located here
388 // TODO move them for webpack 5
389 /** @type {SyncHook<object, Module>} */
390 normalModuleLoader: new SyncHook(["loaderContext", "module"]),
391
392 /** @type {SyncBailHook<Chunk[]>} */
393 optimizeExtractedChunksBasic: new SyncBailHook(["chunks"]),
394 /** @type {SyncBailHook<Chunk[]>} */
395 optimizeExtractedChunks: new SyncBailHook(["chunks"]),
396 /** @type {SyncBailHook<Chunk[]>} */
397 optimizeExtractedChunksAdvanced: new SyncBailHook(["chunks"]),
398 /** @type {SyncHook<Chunk[]>} */
399 afterOptimizeExtractedChunks: new SyncHook(["chunks"])
400 };
401 this._pluginCompat.tap("Compilation", options => {
402 switch (options.name) {
403 case "optimize-tree":
404 case "additional-assets":
405 case "optimize-chunk-assets":
406 case "optimize-assets":
407 case "after-seal":
408 options.async = true;
409 break;
410 }
411 });
412 /** @type {string=} */
413 this.name = undefined;
414 /** @type {Compiler} */
415 this.compiler = compiler;
416 this.resolverFactory = compiler.resolverFactory;
417 this.inputFileSystem = compiler.inputFileSystem;
418 this.requestShortener = compiler.requestShortener;
419
420 const options = compiler.options;
421 this.options = options;
422 this.outputOptions = options && options.output;
423 /** @type {boolean=} */
424 this.bail = options && options.bail;
425 this.profile = options && options.profile;
426 this.performance = options && options.performance;
427
428 this.mainTemplate = new MainTemplate(this.outputOptions);
429 this.chunkTemplate = new ChunkTemplate(this.outputOptions);
430 this.hotUpdateChunkTemplate = new HotUpdateChunkTemplate(
431 this.outputOptions
432 );
433 this.runtimeTemplate = new RuntimeTemplate(
434 this.outputOptions,
435 this.requestShortener
436 );
437 this.moduleTemplates = {
438 javascript: new ModuleTemplate(this.runtimeTemplate, "javascript"),
439 webassembly: new ModuleTemplate(this.runtimeTemplate, "webassembly")
440 };
441
442 this.semaphore = new Semaphore(options.parallelism || 100);
443
444 this.entries = [];
445 /** @private @type {{name: string, request: string, module: Module}[]} */
446 this._preparedEntrypoints = [];
447 this.entrypoints = new Map();
448 /** @type {Chunk[]} */
449 this.chunks = [];
450 /** @type {ChunkGroup[]} */
451 this.chunkGroups = [];
452 /** @type {Map<string, ChunkGroup>} */
453 this.namedChunkGroups = new Map();
454 /** @type {Map<string, Chunk>} */
455 this.namedChunks = new Map();
456 /** @type {Module[]} */
457 this.modules = [];
458 /** @private @type {Map<string, Module>} */
459 this._modules = new Map();
460 this.cache = null;
461 this.records = null;
462 /** @type {string[]} */
463 this.additionalChunkAssets = [];
464 /** @type {CompilationAssets} */
465 this.assets = {};
466 /** @type {WebpackError[]} */
467 this.errors = [];
468 /** @type {WebpackError[]} */
469 this.warnings = [];
470 /** @type {Compilation[]} */
471 this.children = [];
472 /** @type {Map<DepConstructor, ModuleFactory>} */
473 this.dependencyFactories = new Map();
474 /** @type {Map<DepConstructor, DependencyTemplate>} */
475 this.dependencyTemplates = new Map();
476 // TODO refactor this in webpack 5 to a custom DependencyTemplates class with a hash property
477 // @ts-ignore
478 this.dependencyTemplates.set("hash", "");
479 this.childrenCounters = {};
480 /** @type {Set<number|string>} */
481 this.usedChunkIds = null;
482 /** @type {Set<number>} */
483 this.usedModuleIds = null;
484 /** @type {Map<string, number>=} */
485 this.fileTimestamps = undefined;
486 /** @type {Map<string, number>=} */
487 this.contextTimestamps = undefined;
488 /** @type {Set<string>=} */
489 this.compilationDependencies = undefined;
490 /** @private @type {Map<Module, Callback[]>} */
491 this._buildingModules = new Map();
492 /** @private @type {Map<Module, Callback[]>} */
493 this._rebuildingModules = new Map();
494 }
495
496 getStats() {
497 return new Stats(this);
498 }
499
500 /**
501 * @typedef {Object} AddModuleResult
502 * @property {Module} module the added or existing module
503 * @property {boolean} issuer was this the first request for this module
504 * @property {boolean} build should the module be build
505 * @property {boolean} dependencies should dependencies be walked
506 */
507
508 /**
509 * @param {Module} module module to be added that was created
510 * @param {any=} cacheGroup cacheGroup it is apart of
511 * @returns {AddModuleResult} returns meta about whether or not the module had built
512 * had an issuer, or any dependnecies
513 */
514 addModule(module, cacheGroup) {
515 const identifier = module.identifier();
516 const alreadyAddedModule = this._modules.get(identifier);
517 if (alreadyAddedModule) {
518 return {
519 module: alreadyAddedModule,
520 issuer: false,
521 build: false,
522 dependencies: false
523 };
524 }
525 const cacheName = (cacheGroup || "m") + identifier;
526 if (this.cache && this.cache[cacheName]) {
527 const cacheModule = this.cache[cacheName];
528
529 if (typeof cacheModule.updateCacheModule === "function") {
530 cacheModule.updateCacheModule(module);
531 }
532
533 let rebuild = true;
534 if (this.fileTimestamps && this.contextTimestamps) {
535 rebuild = cacheModule.needRebuild(
536 this.fileTimestamps,
537 this.contextTimestamps
538 );
539 }
540
541 if (!rebuild) {
542 cacheModule.disconnect();
543 this._modules.set(identifier, cacheModule);
544 this.modules.push(cacheModule);
545 for (const err of cacheModule.errors) {
546 this.errors.push(err);
547 }
548 for (const err of cacheModule.warnings) {
549 this.warnings.push(err);
550 }
551 return {
552 module: cacheModule,
553 issuer: true,
554 build: false,
555 dependencies: true
556 };
557 }
558 cacheModule.unbuild();
559 module = cacheModule;
560 }
561 this._modules.set(identifier, module);
562 if (this.cache) {
563 this.cache[cacheName] = module;
564 }
565 this.modules.push(module);
566 return {
567 module: module,
568 issuer: true,
569 build: true,
570 dependencies: true
571 };
572 }
573
574 /**
575 * Fetches a module from a compilation by its identifier
576 * @param {Module} module the module provided
577 * @returns {Module} the module requested
578 */
579 getModule(module) {
580 const identifier = module.identifier();
581 return this._modules.get(identifier);
582 }
583
584 /**
585 * Attempts to search for a module by its identifier
586 * @param {string} identifier identifier (usually path) for module
587 * @returns {Module|undefined} attempt to search for module and return it, else undefined
588 */
589 findModule(identifier) {
590 return this._modules.get(identifier);
591 }
592
593 /**
594 * @param {Module} module module with its callback list
595 * @param {Callback} callback the callback function
596 * @returns {void}
597 */
598 waitForBuildingFinished(module, callback) {
599 let callbackList = this._buildingModules.get(module);
600 if (callbackList) {
601 callbackList.push(() => callback());
602 } else {
603 process.nextTick(callback);
604 }
605 }
606
607 /**
608 * Builds the module object
609 *
610 * @param {Module} module module to be built
611 * @param {boolean} optional optional flag
612 * @param {Module=} origin origin module this module build was requested from
613 * @param {Dependency[]=} dependencies optional dependencies from the module to be built
614 * @param {TODO} thisCallback the callback
615 * @returns {TODO} returns the callback function with results
616 */
617 buildModule(module, optional, origin, dependencies, thisCallback) {
618 let callbackList = this._buildingModules.get(module);
619 if (callbackList) {
620 callbackList.push(thisCallback);
621 return;
622 }
623 this._buildingModules.set(module, (callbackList = [thisCallback]));
624
625 const callback = err => {
626 this._buildingModules.delete(module);
627 for (const cb of callbackList) {
628 cb(err);
629 }
630 };
631
632 this.hooks.buildModule.call(module);
633 module.build(
634 this.options,
635 this,
636 this.resolverFactory.get("normal", module.resolveOptions),
637 this.inputFileSystem,
638 error => {
639 const errors = module.errors;
640 for (let indexError = 0; indexError < errors.length; indexError++) {
641 const err = errors[indexError];
642 err.origin = origin;
643 err.dependencies = dependencies;
644 if (optional) {
645 this.warnings.push(err);
646 } else {
647 this.errors.push(err);
648 }
649 }
650
651 const warnings = module.warnings;
652 for (
653 let indexWarning = 0;
654 indexWarning < warnings.length;
655 indexWarning++
656 ) {
657 const war = warnings[indexWarning];
658 war.origin = origin;
659 war.dependencies = dependencies;
660 this.warnings.push(war);
661 }
662 const originalMap = module.dependencies.reduce((map, v, i) => {
663 map.set(v, i);
664 return map;
665 }, new Map());
666 module.dependencies.sort((a, b) => {
667 const cmp = compareLocations(a.loc, b.loc);
668 if (cmp) return cmp;
669 return originalMap.get(a) - originalMap.get(b);
670 });
671 if (error) {
672 this.hooks.failedModule.call(module, error);
673 return callback(error);
674 }
675 this.hooks.succeedModule.call(module);
676 return callback();
677 }
678 );
679 }
680
681 /**
682 * @param {Module} module to be processed for deps
683 * @param {ModuleCallback} callback callback to be triggered
684 * @returns {void}
685 */
686 processModuleDependencies(module, callback) {
687 const dependencies = new Map();
688
689 const addDependency = dep => {
690 const resourceIdent = dep.getResourceIdentifier();
691 if (resourceIdent) {
692 const factory = this.dependencyFactories.get(dep.constructor);
693 if (factory === undefined) {
694 throw new Error(
695 `No module factory available for dependency type: ${
696 dep.constructor.name
697 }`
698 );
699 }
700 let innerMap = dependencies.get(factory);
701 if (innerMap === undefined) {
702 dependencies.set(factory, (innerMap = new Map()));
703 }
704 let list = innerMap.get(resourceIdent);
705 if (list === undefined) innerMap.set(resourceIdent, (list = []));
706 list.push(dep);
707 }
708 };
709
710 const addDependenciesBlock = block => {
711 if (block.dependencies) {
712 iterationOfArrayCallback(block.dependencies, addDependency);
713 }
714 if (block.blocks) {
715 iterationOfArrayCallback(block.blocks, addDependenciesBlock);
716 }
717 if (block.variables) {
718 iterationBlockVariable(block.variables, addDependency);
719 }
720 };
721
722 try {
723 addDependenciesBlock(module);
724 } catch (e) {
725 callback(e);
726 }
727
728 const sortedDependencies = [];
729
730 for (const pair1 of dependencies) {
731 for (const pair2 of pair1[1]) {
732 sortedDependencies.push({
733 factory: pair1[0],
734 dependencies: pair2[1]
735 });
736 }
737 }
738
739 this.addModuleDependencies(
740 module,
741 sortedDependencies,
742 this.bail,
743 null,
744 true,
745 callback
746 );
747 }
748
749 /**
750 * @param {Module} module module to add deps to
751 * @param {SortedDependency[]} dependencies set of sorted dependencies to iterate through
752 * @param {(boolean|null)=} bail whether to bail or not
753 * @param {TODO} cacheGroup optional cacheGroup
754 * @param {boolean} recursive whether it is recursive traversal
755 * @param {function} callback callback for when dependencies are finished being added
756 * @returns {void}
757 */
758 addModuleDependencies(
759 module,
760 dependencies,
761 bail,
762 cacheGroup,
763 recursive,
764 callback
765 ) {
766 const start = this.profile && Date.now();
767 const currentProfile = this.profile && {};
768
769 asyncLib.forEach(
770 dependencies,
771 (item, callback) => {
772 const dependencies = item.dependencies;
773
774 const errorAndCallback = err => {
775 err.origin = module;
776 err.dependencies = dependencies;
777 this.errors.push(err);
778 if (bail) {
779 callback(err);
780 } else {
781 callback();
782 }
783 };
784 const warningAndCallback = err => {
785 err.origin = module;
786 this.warnings.push(err);
787 callback();
788 };
789
790 const semaphore = this.semaphore;
791 semaphore.acquire(() => {
792 const factory = item.factory;
793 factory.create(
794 {
795 contextInfo: {
796 issuer: module.nameForCondition && module.nameForCondition(),
797 compiler: this.compiler.name
798 },
799 resolveOptions: module.resolveOptions,
800 context: module.context,
801 dependencies: dependencies
802 },
803 (err, dependentModule) => {
804 let afterFactory;
805
806 const isOptional = () => {
807 return dependencies.every(d => d.optional);
808 };
809
810 const errorOrWarningAndCallback = err => {
811 if (isOptional()) {
812 return warningAndCallback(err);
813 } else {
814 return errorAndCallback(err);
815 }
816 };
817
818 if (err) {
819 semaphore.release();
820 return errorOrWarningAndCallback(
821 new ModuleNotFoundError(module, err)
822 );
823 }
824 if (!dependentModule) {
825 semaphore.release();
826 return process.nextTick(callback);
827 }
828 if (currentProfile) {
829 afterFactory = Date.now();
830 currentProfile.factory = afterFactory - start;
831 }
832
833 const iterationDependencies = depend => {
834 for (let index = 0; index < depend.length; index++) {
835 const dep = depend[index];
836 dep.module = dependentModule;
837 dependentModule.addReason(module, dep);
838 }
839 };
840
841 const addModuleResult = this.addModule(
842 dependentModule,
843 cacheGroup
844 );
845 dependentModule = addModuleResult.module;
846 iterationDependencies(dependencies);
847
848 const afterBuild = () => {
849 if (currentProfile) {
850 const afterBuilding = Date.now();
851 currentProfile.building = afterBuilding - afterFactory;
852 }
853
854 if (recursive && addModuleResult.dependencies) {
855 this.processModuleDependencies(dependentModule, callback);
856 } else {
857 return callback();
858 }
859 };
860
861 if (addModuleResult.issuer) {
862 if (currentProfile) {
863 dependentModule.profile = currentProfile;
864 }
865
866 dependentModule.issuer = module;
867 } else {
868 if (this.profile) {
869 if (module.profile) {
870 const time = Date.now() - start;
871 if (
872 !module.profile.dependencies ||
873 time > module.profile.dependencies
874 ) {
875 module.profile.dependencies = time;
876 }
877 }
878 }
879 }
880
881 if (addModuleResult.build) {
882 this.buildModule(
883 dependentModule,
884 isOptional(),
885 module,
886 dependencies,
887 err => {
888 if (err) {
889 semaphore.release();
890 return errorOrWarningAndCallback(err);
891 }
892
893 if (currentProfile) {
894 const afterBuilding = Date.now();
895 currentProfile.building = afterBuilding - afterFactory;
896 }
897
898 semaphore.release();
899 afterBuild();
900 }
901 );
902 } else {
903 semaphore.release();
904 this.waitForBuildingFinished(dependentModule, afterBuild);
905 }
906 }
907 );
908 });
909 },
910 err => {
911 // In V8, the Error objects keep a reference to the functions on the stack. These warnings &
912 // errors are created inside closures that keep a reference to the Compilation, so errors are
913 // leaking the Compilation object.
914
915 if (err) {
916 // eslint-disable-next-line no-self-assign
917 err.stack = err.stack;
918 return callback(err);
919 }
920
921 return process.nextTick(callback);
922 }
923 );
924 }
925
926 /**
927 *
928 * @param {string} context context string path
929 * @param {Dependency} dependency dependency used to create Module chain
930 * @param {OnModuleCallback} onModule function invoked on modules creation
931 * @param {ModuleChainCallback} callback callback for when module chain is complete
932 * @returns {void} will throw if dependency instance is not a valid Dependency
933 */
934 _addModuleChain(context, dependency, onModule, callback) {
935 const start = this.profile && Date.now();
936 const currentProfile = this.profile && {};
937
938 const errorAndCallback = this.bail
939 ? err => {
940 callback(err);
941 }
942 : err => {
943 err.dependencies = [dependency];
944 this.errors.push(err);
945 callback();
946 };
947
948 if (
949 typeof dependency !== "object" ||
950 dependency === null ||
951 !dependency.constructor
952 ) {
953 throw new Error("Parameter 'dependency' must be a Dependency");
954 }
955 const Dep = /** @type {DepConstructor} */ (dependency.constructor);
956 const moduleFactory = this.dependencyFactories.get(Dep);
957 if (!moduleFactory) {
958 throw new Error(
959 `No dependency factory available for this dependency type: ${
960 dependency.constructor.name
961 }`
962 );
963 }
964
965 this.semaphore.acquire(() => {
966 moduleFactory.create(
967 {
968 contextInfo: {
969 issuer: "",
970 compiler: this.compiler.name
971 },
972 context: context,
973 dependencies: [dependency]
974 },
975 (err, module) => {
976 if (err) {
977 this.semaphore.release();
978 return errorAndCallback(new EntryModuleNotFoundError(err));
979 }
980
981 let afterFactory;
982
983 if (currentProfile) {
984 afterFactory = Date.now();
985 currentProfile.factory = afterFactory - start;
986 }
987
988 const addModuleResult = this.addModule(module);
989 module = addModuleResult.module;
990
991 onModule(module);
992
993 dependency.module = module;
994 module.addReason(null, dependency);
995
996 const afterBuild = () => {
997 if (currentProfile) {
998 const afterBuilding = Date.now();
999 currentProfile.building = afterBuilding - afterFactory;
1000 }
1001
1002 if (addModuleResult.dependencies) {
1003 this.processModuleDependencies(module, err => {
1004 if (err) return callback(err);
1005 callback(null, module);
1006 });
1007 } else {
1008 return callback(null, module);
1009 }
1010 };
1011
1012 if (addModuleResult.issuer) {
1013 if (currentProfile) {
1014 module.profile = currentProfile;
1015 }
1016 }
1017
1018 if (addModuleResult.build) {
1019 this.buildModule(module, false, null, null, err => {
1020 if (err) {
1021 this.semaphore.release();
1022 return errorAndCallback(err);
1023 }
1024
1025 if (currentProfile) {
1026 const afterBuilding = Date.now();
1027 currentProfile.building = afterBuilding - afterFactory;
1028 }
1029
1030 this.semaphore.release();
1031 afterBuild();
1032 });
1033 } else {
1034 this.semaphore.release();
1035 this.waitForBuildingFinished(module, afterBuild);
1036 }
1037 }
1038 );
1039 });
1040 }
1041
1042 /**
1043 *
1044 * @param {string} context context path for entry
1045 * @param {Dependency} entry entry dependency being created
1046 * @param {string} name name of entry
1047 * @param {ModuleCallback} callback callback function
1048 * @returns {void} returns
1049 */
1050 addEntry(context, entry, name, callback) {
1051 this.hooks.addEntry.call(entry, name);
1052
1053 const slot = {
1054 name: name,
1055 // TODO webpack 5 remove `request`
1056 request: null,
1057 module: null
1058 };
1059
1060 if (entry instanceof ModuleDependency) {
1061 slot.request = entry.request;
1062 }
1063
1064 // TODO webpack 5: merge modules instead when multiple entry modules are supported
1065 const idx = this._preparedEntrypoints.findIndex(slot => slot.name === name);
1066 if (idx >= 0) {
1067 // Overwrite existing entrypoint
1068 this._preparedEntrypoints[idx] = slot;
1069 } else {
1070 this._preparedEntrypoints.push(slot);
1071 }
1072 this._addModuleChain(
1073 context,
1074 entry,
1075 module => {
1076 this.entries.push(module);
1077 },
1078 (err, module) => {
1079 if (err) {
1080 this.hooks.failedEntry.call(entry, name, err);
1081 return callback(err);
1082 }
1083
1084 if (module) {
1085 slot.module = module;
1086 } else {
1087 const idx = this._preparedEntrypoints.indexOf(slot);
1088 if (idx >= 0) {
1089 this._preparedEntrypoints.splice(idx, 1);
1090 }
1091 }
1092 this.hooks.succeedEntry.call(entry, name, module);
1093 return callback(null, module);
1094 }
1095 );
1096 }
1097
1098 /**
1099 * @param {string} context context path string
1100 * @param {Dependency} dependency dep used to create module
1101 * @param {ModuleCallback} callback module callback sending module up a level
1102 * @returns {void}
1103 */
1104 prefetch(context, dependency, callback) {
1105 this._addModuleChain(
1106 context,
1107 dependency,
1108 module => {
1109 module.prefetched = true;
1110 },
1111 callback
1112 );
1113 }
1114
1115 /**
1116 * @param {Module} module module to be rebuilt
1117 * @param {Callback} thisCallback callback when module finishes rebuilding
1118 * @returns {void}
1119 */
1120 rebuildModule(module, thisCallback) {
1121 let callbackList = this._rebuildingModules.get(module);
1122 if (callbackList) {
1123 callbackList.push(thisCallback);
1124 return;
1125 }
1126 this._rebuildingModules.set(module, (callbackList = [thisCallback]));
1127
1128 const callback = err => {
1129 this._rebuildingModules.delete(module);
1130 for (const cb of callbackList) {
1131 cb(err);
1132 }
1133 };
1134
1135 this.hooks.rebuildModule.call(module);
1136 const oldDependencies = module.dependencies.slice();
1137 const oldVariables = module.variables.slice();
1138 const oldBlocks = module.blocks.slice();
1139 module.unbuild();
1140 this.buildModule(module, false, module, null, err => {
1141 if (err) {
1142 this.hooks.finishRebuildingModule.call(module);
1143 return callback(err);
1144 }
1145
1146 this.processModuleDependencies(module, err => {
1147 if (err) return callback(err);
1148 this.removeReasonsOfDependencyBlock(module, {
1149 dependencies: oldDependencies,
1150 variables: oldVariables,
1151 blocks: oldBlocks
1152 });
1153 this.hooks.finishRebuildingModule.call(module);
1154 callback();
1155 });
1156 });
1157 }
1158
1159 finish() {
1160 const modules = this.modules;
1161 this.hooks.finishModules.call(modules);
1162
1163 for (let index = 0; index < modules.length; index++) {
1164 const module = modules[index];
1165 this.reportDependencyErrorsAndWarnings(module, [module]);
1166 }
1167 }
1168
1169 unseal() {
1170 this.hooks.unseal.call();
1171 this.chunks.length = 0;
1172 this.chunkGroups.length = 0;
1173 this.namedChunks.clear();
1174 this.namedChunkGroups.clear();
1175 this.additionalChunkAssets.length = 0;
1176 this.assets = {};
1177 for (const module of this.modules) {
1178 module.unseal();
1179 }
1180 }
1181
1182 /**
1183 * @param {Callback} callback signals when the seal method is finishes
1184 * @returns {void}
1185 */
1186 seal(callback) {
1187 this.hooks.seal.call();
1188
1189 while (
1190 this.hooks.optimizeDependenciesBasic.call(this.modules) ||
1191 this.hooks.optimizeDependencies.call(this.modules) ||
1192 this.hooks.optimizeDependenciesAdvanced.call(this.modules)
1193 ) {
1194 /* empty */
1195 }
1196 this.hooks.afterOptimizeDependencies.call(this.modules);
1197
1198 this.hooks.beforeChunks.call();
1199 for (const preparedEntrypoint of this._preparedEntrypoints) {
1200 const module = preparedEntrypoint.module;
1201 const name = preparedEntrypoint.name;
1202 const chunk = this.addChunk(name);
1203 const entrypoint = new Entrypoint(name);
1204 entrypoint.setRuntimeChunk(chunk);
1205 entrypoint.addOrigin(null, name, preparedEntrypoint.request);
1206 this.namedChunkGroups.set(name, entrypoint);
1207 this.entrypoints.set(name, entrypoint);
1208 this.chunkGroups.push(entrypoint);
1209
1210 GraphHelpers.connectChunkGroupAndChunk(entrypoint, chunk);
1211 GraphHelpers.connectChunkAndModule(chunk, module);
1212
1213 chunk.entryModule = module;
1214 chunk.name = name;
1215
1216 this.assignDepth(module);
1217 }
1218 this.processDependenciesBlocksForChunkGroups(this.chunkGroups.slice());
1219 this.sortModules(this.modules);
1220 this.hooks.afterChunks.call(this.chunks);
1221
1222 this.hooks.optimize.call();
1223
1224 while (
1225 this.hooks.optimizeModulesBasic.call(this.modules) ||
1226 this.hooks.optimizeModules.call(this.modules) ||
1227 this.hooks.optimizeModulesAdvanced.call(this.modules)
1228 ) {
1229 /* empty */
1230 }
1231 this.hooks.afterOptimizeModules.call(this.modules);
1232
1233 while (
1234 this.hooks.optimizeChunksBasic.call(this.chunks, this.chunkGroups) ||
1235 this.hooks.optimizeChunks.call(this.chunks, this.chunkGroups) ||
1236 this.hooks.optimizeChunksAdvanced.call(this.chunks, this.chunkGroups)
1237 ) {
1238 /* empty */
1239 }
1240 this.hooks.afterOptimizeChunks.call(this.chunks, this.chunkGroups);
1241
1242 this.hooks.optimizeTree.callAsync(this.chunks, this.modules, err => {
1243 if (err) {
1244 return callback(err);
1245 }
1246
1247 this.hooks.afterOptimizeTree.call(this.chunks, this.modules);
1248
1249 while (
1250 this.hooks.optimizeChunkModulesBasic.call(this.chunks, this.modules) ||
1251 this.hooks.optimizeChunkModules.call(this.chunks, this.modules) ||
1252 this.hooks.optimizeChunkModulesAdvanced.call(this.chunks, this.modules)
1253 ) {
1254 /* empty */
1255 }
1256 this.hooks.afterOptimizeChunkModules.call(this.chunks, this.modules);
1257
1258 const shouldRecord = this.hooks.shouldRecord.call() !== false;
1259
1260 this.hooks.reviveModules.call(this.modules, this.records);
1261 this.hooks.optimizeModuleOrder.call(this.modules);
1262 this.hooks.advancedOptimizeModuleOrder.call(this.modules);
1263 this.hooks.beforeModuleIds.call(this.modules);
1264 this.hooks.moduleIds.call(this.modules);
1265 this.applyModuleIds();
1266 this.hooks.optimizeModuleIds.call(this.modules);
1267 this.hooks.afterOptimizeModuleIds.call(this.modules);
1268
1269 this.sortItemsWithModuleIds();
1270
1271 this.hooks.reviveChunks.call(this.chunks, this.records);
1272 this.hooks.optimizeChunkOrder.call(this.chunks);
1273 this.hooks.beforeChunkIds.call(this.chunks);
1274 this.applyChunkIds();
1275 this.hooks.optimizeChunkIds.call(this.chunks);
1276 this.hooks.afterOptimizeChunkIds.call(this.chunks);
1277
1278 this.sortItemsWithChunkIds();
1279
1280 if (shouldRecord) {
1281 this.hooks.recordModules.call(this.modules, this.records);
1282 this.hooks.recordChunks.call(this.chunks, this.records);
1283 }
1284
1285 this.hooks.beforeHash.call();
1286 this.createHash();
1287 this.hooks.afterHash.call();
1288
1289 if (shouldRecord) {
1290 this.hooks.recordHash.call(this.records);
1291 }
1292
1293 this.hooks.beforeModuleAssets.call();
1294 this.createModuleAssets();
1295 if (this.hooks.shouldGenerateChunkAssets.call() !== false) {
1296 this.hooks.beforeChunkAssets.call();
1297 this.createChunkAssets();
1298 }
1299 this.hooks.additionalChunkAssets.call(this.chunks);
1300 this.summarizeDependencies();
1301 if (shouldRecord) {
1302 this.hooks.record.call(this, this.records);
1303 }
1304
1305 this.hooks.additionalAssets.callAsync(err => {
1306 if (err) {
1307 return callback(err);
1308 }
1309 this.hooks.optimizeChunkAssets.callAsync(this.chunks, err => {
1310 if (err) {
1311 return callback(err);
1312 }
1313 this.hooks.afterOptimizeChunkAssets.call(this.chunks);
1314 this.hooks.optimizeAssets.callAsync(this.assets, err => {
1315 if (err) {
1316 return callback(err);
1317 }
1318 this.hooks.afterOptimizeAssets.call(this.assets);
1319 if (this.hooks.needAdditionalSeal.call()) {
1320 this.unseal();
1321 return this.seal(callback);
1322 }
1323 return this.hooks.afterSeal.callAsync(callback);
1324 });
1325 });
1326 });
1327 });
1328 }
1329
1330 /**
1331 * @param {Module[]} modules the modules array on compilation to perform the sort for
1332 * @returns {void}
1333 */
1334 sortModules(modules) {
1335 // TODO webpack 5: this should only be enabled when `moduleIds: "natural"`
1336 // TODO move it into a plugin (NaturalModuleIdsPlugin) and use this in WebpackOptionsApply
1337 // TODO remove this method
1338 modules.sort(byIndexOrIdentifier);
1339 }
1340
1341 /**
1342 * @param {Module} module moulde to report from
1343 * @param {DependenciesBlock[]} blocks blocks to report from
1344 * @returns {void}
1345 */
1346 reportDependencyErrorsAndWarnings(module, blocks) {
1347 for (let indexBlock = 0; indexBlock < blocks.length; indexBlock++) {
1348 const block = blocks[indexBlock];
1349 const dependencies = block.dependencies;
1350
1351 for (let indexDep = 0; indexDep < dependencies.length; indexDep++) {
1352 const d = dependencies[indexDep];
1353
1354 const warnings = d.getWarnings();
1355 if (warnings) {
1356 for (let indexWar = 0; indexWar < warnings.length; indexWar++) {
1357 const w = warnings[indexWar];
1358
1359 const warning = new ModuleDependencyWarning(module, w, d.loc);
1360 this.warnings.push(warning);
1361 }
1362 }
1363 const errors = d.getErrors();
1364 if (errors) {
1365 for (let indexErr = 0; indexErr < errors.length; indexErr++) {
1366 const e = errors[indexErr];
1367
1368 const error = new ModuleDependencyError(module, e, d.loc);
1369 this.errors.push(error);
1370 }
1371 }
1372 }
1373
1374 this.reportDependencyErrorsAndWarnings(module, block.blocks);
1375 }
1376 }
1377
1378 /**
1379 * @param {TODO} groupOptions options for the chunk group
1380 * @param {Module} module the module the references the chunk group
1381 * @param {DependencyLocation} loc the location from with the chunk group is referenced (inside of module)
1382 * @param {string} request the request from which the the chunk group is referenced
1383 * @returns {ChunkGroup} the new or existing chunk group
1384 */
1385 addChunkInGroup(groupOptions, module, loc, request) {
1386 if (typeof groupOptions === "string") {
1387 groupOptions = { name: groupOptions };
1388 }
1389 const name = groupOptions.name;
1390 if (name) {
1391 const chunkGroup = this.namedChunkGroups.get(name);
1392 if (chunkGroup !== undefined) {
1393 chunkGroup.addOptions(groupOptions);
1394 if (module) {
1395 chunkGroup.addOrigin(module, loc, request);
1396 }
1397 return chunkGroup;
1398 }
1399 }
1400 const chunkGroup = new ChunkGroup(groupOptions);
1401 if (module) chunkGroup.addOrigin(module, loc, request);
1402 const chunk = this.addChunk(name);
1403
1404 GraphHelpers.connectChunkGroupAndChunk(chunkGroup, chunk);
1405
1406 this.chunkGroups.push(chunkGroup);
1407 if (name) {
1408 this.namedChunkGroups.set(name, chunkGroup);
1409 }
1410 return chunkGroup;
1411 }
1412
1413 /**
1414 * This method first looks to see if a name is provided for a new chunk,
1415 * and first looks to see if any named chunks already exist and reuse that chunk instead.
1416 *
1417 * @param {string=} name optional chunk name to be provided
1418 * @returns {Chunk} create a chunk (invoked during seal event)
1419 */
1420 addChunk(name) {
1421 if (name) {
1422 const chunk = this.namedChunks.get(name);
1423 if (chunk !== undefined) {
1424 return chunk;
1425 }
1426 }
1427 const chunk = new Chunk(name);
1428 this.chunks.push(chunk);
1429 if (name) {
1430 this.namedChunks.set(name, chunk);
1431 }
1432 return chunk;
1433 }
1434
1435 /**
1436 * @param {Module} module module to assign depth
1437 * @returns {void}
1438 */
1439 assignDepth(module) {
1440 const queue = new Set([module]);
1441 let depth;
1442
1443 module.depth = 0;
1444
1445 /**
1446 * @param {Module} module module for processeing
1447 * @returns {void}
1448 */
1449 const enqueueJob = module => {
1450 const d = module.depth;
1451 if (typeof d === "number" && d <= depth) return;
1452 queue.add(module);
1453 module.depth = depth;
1454 };
1455
1456 /**
1457 * @param {Dependency} dependency dependency to assign depth to
1458 * @returns {void}
1459 */
1460 const assignDepthToDependency = dependency => {
1461 if (dependency.module) {
1462 enqueueJob(dependency.module);
1463 }
1464 };
1465
1466 /**
1467 * @param {DependenciesBlock} block block to assign depth to
1468 * @returns {void}
1469 */
1470 const assignDepthToDependencyBlock = block => {
1471 if (block.variables) {
1472 iterationBlockVariable(block.variables, assignDepthToDependency);
1473 }
1474
1475 if (block.dependencies) {
1476 iterationOfArrayCallback(block.dependencies, assignDepthToDependency);
1477 }
1478
1479 if (block.blocks) {
1480 iterationOfArrayCallback(block.blocks, assignDepthToDependencyBlock);
1481 }
1482 };
1483
1484 for (module of queue) {
1485 queue.delete(module);
1486 depth = module.depth;
1487
1488 depth++;
1489 assignDepthToDependencyBlock(module);
1490 }
1491 }
1492
1493 /**
1494 * @param {Module} module the module containing the dependency
1495 * @param {Dependency} dependency the dependency
1496 * @returns {DependencyReference} a reference for the dependency
1497 */
1498 getDependencyReference(module, dependency) {
1499 // TODO remove dep.getReference existence check in webpack 5
1500 if (typeof dependency.getReference !== "function") return null;
1501 const ref = dependency.getReference();
1502 if (!ref) return null;
1503 return this.hooks.dependencyReference.call(ref, dependency, module);
1504 }
1505
1506 /**
1507 * This method creates the Chunk graph from the Module graph
1508 * @private
1509 * @param {TODO[]} inputChunkGroups chunk groups which are processed
1510 * @returns {void}
1511 */
1512 processDependenciesBlocksForChunkGroups(inputChunkGroups) {
1513 // Process is splitting into two parts:
1514 // Part one traverse the module graph and builds a very basic chunks graph
1515 // in chunkDependencies.
1516 // Part two traverse every possible way through the basic chunk graph and
1517 // tracks the available modules. While traversing it connects chunks with
1518 // eachother and Blocks with Chunks. It stops traversing when all modules
1519 // for a chunk are already available. So it doesn't connect unneeded chunks.
1520
1521 /** @type {Map<ChunkGroup, {block: AsyncDependenciesBlock, chunkGroup: ChunkGroup, couldBeFiltered: boolean}[]>} */
1522 const chunkDependencies = new Map();
1523 const allCreatedChunkGroups = new Set();
1524
1525 // PREPARE
1526 /** @type {Map<DependenciesBlock, { modules: Module[], blocks: AsyncDependenciesBlock[]}>} */
1527 const blockInfoMap = new Map();
1528
1529 /**
1530 * @param {Dependency} d dependency to iterate over
1531 * @returns {void}
1532 */
1533 const iteratorDependency = d => {
1534 // We skip Dependencies without Reference
1535 const ref = this.getDependencyReference(currentModule, d);
1536 if (!ref) {
1537 return;
1538 }
1539 // We skip Dependencies without Module pointer
1540 const refModule = ref.module;
1541 if (!refModule) {
1542 return;
1543 }
1544 // We skip weak Dependencies
1545 if (ref.weak) {
1546 return;
1547 }
1548
1549 blockInfoModules.add(refModule);
1550 };
1551
1552 /**
1553 * @param {AsyncDependenciesBlock} b blocks to prepare
1554 * @returns {void}
1555 */
1556 const iteratorBlockPrepare = b => {
1557 blockInfoBlocks.push(b);
1558 blockQueue.push(b);
1559 };
1560
1561 /** @type {Module} */
1562 let currentModule;
1563 /** @type {DependenciesBlock} */
1564 let block;
1565 /** @type {DependenciesBlock[]} */
1566 let blockQueue;
1567 /** @type {Set<Module>} */
1568 let blockInfoModules;
1569 /** @type {AsyncDependenciesBlock[]} */
1570 let blockInfoBlocks;
1571
1572 for (const module of this.modules) {
1573 blockQueue = [module];
1574 currentModule = module;
1575 while (blockQueue.length > 0) {
1576 block = blockQueue.pop();
1577 blockInfoModules = new Set();
1578 blockInfoBlocks = [];
1579
1580 if (block.variables) {
1581 iterationBlockVariable(block.variables, iteratorDependency);
1582 }
1583
1584 if (block.dependencies) {
1585 iterationOfArrayCallback(block.dependencies, iteratorDependency);
1586 }
1587
1588 if (block.blocks) {
1589 iterationOfArrayCallback(block.blocks, iteratorBlockPrepare);
1590 }
1591
1592 const blockInfo = {
1593 modules: Array.from(blockInfoModules),
1594 blocks: blockInfoBlocks
1595 };
1596 blockInfoMap.set(block, blockInfo);
1597 }
1598 }
1599
1600 // PART ONE
1601
1602 /** @type {Map<ChunkGroup, { index: number, index2: number }>} */
1603 const chunkGroupCounters = new Map();
1604 for (const chunkGroup of inputChunkGroups) {
1605 chunkGroupCounters.set(chunkGroup, { index: 0, index2: 0 });
1606 }
1607
1608 let nextFreeModuleIndex = 0;
1609 let nextFreeModuleIndex2 = 0;
1610
1611 /** @type {Map<DependenciesBlock, ChunkGroup>} */
1612 const blockChunkGroups = new Map();
1613
1614 /** @type {Set<DependenciesBlock>} */
1615 const blocksWithNestedBlocks = new Set();
1616
1617 const ADD_AND_ENTER_MODULE = 0;
1618 const ENTER_MODULE = 1;
1619 const PROCESS_BLOCK = 2;
1620 const LEAVE_MODULE = 3;
1621
1622 /**
1623 * @typedef {Object} QueueItem
1624 * @property {number} action
1625 * @property {DependenciesBlock} block
1626 * @property {Module} module
1627 * @property {Chunk} chunk
1628 * @property {ChunkGroup} chunkGroup
1629 */
1630
1631 /**
1632 * @param {ChunkGroup} chunkGroup chunk group
1633 * @returns {QueueItem} queue item
1634 */
1635 const chunkGroupToQueueItem = chunkGroup => ({
1636 action: ENTER_MODULE,
1637 block: chunkGroup.chunks[0].entryModule,
1638 module: chunkGroup.chunks[0].entryModule,
1639 chunk: chunkGroup.chunks[0],
1640 chunkGroup
1641 });
1642
1643 // Start with the provided modules/chunks
1644 /** @type {QueueItem[]} */
1645 let queue = inputChunkGroups.map(chunkGroupToQueueItem).reverse();
1646 /** @type {QueueItem[]} */
1647 let queueDelayed = [];
1648
1649 /** @type {Module} */
1650 let module;
1651 /** @type {Chunk} */
1652 let chunk;
1653 /** @type {ChunkGroup} */
1654 let chunkGroup;
1655
1656 // For each async Block in graph
1657 /**
1658 * @param {AsyncDependenciesBlock} b iterating over each Async DepBlock
1659 * @returns {void}
1660 */
1661 const iteratorBlock = b => {
1662 // 1. We create a chunk for this Block
1663 // but only once (blockChunkGroups map)
1664 let c = blockChunkGroups.get(b);
1665 if (c === undefined) {
1666 c = this.namedChunkGroups.get(b.chunkName);
1667 if (c && c.isInitial()) {
1668 this.errors.push(
1669 new AsyncDependencyToInitialChunkError(b.chunkName, module, b.loc)
1670 );
1671 c = chunkGroup;
1672 } else {
1673 c = this.addChunkInGroup(
1674 b.groupOptions || b.chunkName,
1675 module,
1676 b.loc,
1677 b.request
1678 );
1679 chunkGroupCounters.set(c, { index: 0, index2: 0 });
1680 blockChunkGroups.set(b, c);
1681 allCreatedChunkGroups.add(c);
1682 }
1683 } else {
1684 // TODO webpack 5 remove addOptions check
1685 if (c.addOptions) c.addOptions(b.groupOptions);
1686 c.addOrigin(module, b.loc, b.request);
1687 }
1688
1689 // 2. We store the Block+Chunk mapping as dependency for the chunk
1690 let deps = chunkDependencies.get(chunkGroup);
1691 if (!deps) chunkDependencies.set(chunkGroup, (deps = []));
1692 deps.push({
1693 block: b,
1694 chunkGroup: c,
1695 couldBeFiltered: true
1696 });
1697
1698 // 3. We enqueue the DependenciesBlock for traversal
1699 queueDelayed.push({
1700 action: PROCESS_BLOCK,
1701 block: b,
1702 module: module,
1703 chunk: c.chunks[0],
1704 chunkGroup: c
1705 });
1706 };
1707
1708 // Iterative traversal of the Module graph
1709 // Recursive would be simpler to write but could result in Stack Overflows
1710 while (queue.length) {
1711 while (queue.length) {
1712 const queueItem = queue.pop();
1713 module = queueItem.module;
1714 block = queueItem.block;
1715 chunk = queueItem.chunk;
1716 chunkGroup = queueItem.chunkGroup;
1717
1718 switch (queueItem.action) {
1719 case ADD_AND_ENTER_MODULE: {
1720 // We connect Module and Chunk when not already done
1721 if (chunk.addModule(module)) {
1722 module.addChunk(chunk);
1723 } else {
1724 // already connected, skip it
1725 break;
1726 }
1727 }
1728 // fallthrough
1729 case ENTER_MODULE: {
1730 if (chunkGroup !== undefined) {
1731 const index = chunkGroup.getModuleIndex(module);
1732 if (index === undefined) {
1733 chunkGroup.setModuleIndex(
1734 module,
1735 chunkGroupCounters.get(chunkGroup).index++
1736 );
1737 }
1738 }
1739
1740 if (module.index === null) {
1741 module.index = nextFreeModuleIndex++;
1742 }
1743
1744 queue.push({
1745 action: LEAVE_MODULE,
1746 block,
1747 module,
1748 chunk,
1749 chunkGroup
1750 });
1751 }
1752 // fallthrough
1753 case PROCESS_BLOCK: {
1754 // get prepared block info
1755 const blockInfo = blockInfoMap.get(block);
1756
1757 // Traverse all referenced modules
1758 for (let i = blockInfo.modules.length - 1; i >= 0; i--) {
1759 const refModule = blockInfo.modules[i];
1760 if (chunk.containsModule(refModule)) {
1761 // skip early if already connected
1762 continue;
1763 }
1764 // enqueue the add and enter to enter in the correct order
1765 // this is relevant with circular dependencies
1766 queue.push({
1767 action: ADD_AND_ENTER_MODULE,
1768 block: refModule,
1769 module: refModule,
1770 chunk,
1771 chunkGroup
1772 });
1773 }
1774
1775 // Traverse all Blocks
1776 iterationOfArrayCallback(blockInfo.blocks, iteratorBlock);
1777
1778 if (blockInfo.blocks.length > 0 && module !== block) {
1779 blocksWithNestedBlocks.add(block);
1780 }
1781 break;
1782 }
1783 case LEAVE_MODULE: {
1784 if (chunkGroup !== undefined) {
1785 const index = chunkGroup.getModuleIndex2(module);
1786 if (index === undefined) {
1787 chunkGroup.setModuleIndex2(
1788 module,
1789 chunkGroupCounters.get(chunkGroup).index2++
1790 );
1791 }
1792 }
1793
1794 if (module.index2 === null) {
1795 module.index2 = nextFreeModuleIndex2++;
1796 }
1797 break;
1798 }
1799 }
1800 }
1801 const tempQueue = queue;
1802 queue = queueDelayed.reverse();
1803 queueDelayed = tempQueue;
1804 }
1805
1806 // PART TWO
1807 /** @type {Set<Module>} */
1808 let newAvailableModules;
1809
1810 /**
1811 * @typedef {Object} ChunkGroupInfo
1812 * @property {Set<Module>} minAvailableModules current minimal set of modules available at this point
1813 * @property {Set<Module>[]} availableModulesToBeMerged enqueued updates to the minimal set of available modules
1814 */
1815
1816 /** @type {Map<ChunkGroup, ChunkGroupInfo>} */
1817 const chunkGroupInfoMap = new Map();
1818
1819 /** @type {Queue<ChunkGroup>} */
1820 const queue2 = new Queue(inputChunkGroups);
1821
1822 for (const chunkGroup of inputChunkGroups) {
1823 chunkGroupInfoMap.set(chunkGroup, {
1824 minAvailableModules: undefined,
1825 availableModulesToBeMerged: [new Set()]
1826 });
1827 }
1828
1829 /**
1830 * Helper function to check if all modules of a chunk are available
1831 *
1832 * @param {ChunkGroup} chunkGroup the chunkGroup to scan
1833 * @param {Set<Module>} availableModules the comparitor set
1834 * @returns {boolean} return true if all modules of a chunk are available
1835 */
1836 const areModulesAvailable = (chunkGroup, availableModules) => {
1837 for (const chunk of chunkGroup.chunks) {
1838 for (const module of chunk.modulesIterable) {
1839 if (!availableModules.has(module)) return false;
1840 }
1841 }
1842 return true;
1843 };
1844
1845 // For each edge in the basic chunk graph
1846 /**
1847 * @param {TODO} dep the dependency used for filtering
1848 * @returns {boolean} used to filter "edges" (aka Dependencies) that were pointing
1849 * to modules that are already available. Also filters circular dependencies in the chunks graph
1850 */
1851 const filterFn = dep => {
1852 const depChunkGroup = dep.chunkGroup;
1853 if (!dep.couldBeFiltered) return true;
1854 if (blocksWithNestedBlocks.has(dep.block)) return true;
1855 if (areModulesAvailable(depChunkGroup, newAvailableModules)) {
1856 return false; // break all modules are already available
1857 }
1858 dep.couldBeFiltered = false;
1859 return true;
1860 };
1861
1862 // Iterative traversing of the basic chunk graph
1863 while (queue2.length) {
1864 chunkGroup = queue2.dequeue();
1865 const info = chunkGroupInfoMap.get(chunkGroup);
1866 const availableModulesToBeMerged = info.availableModulesToBeMerged;
1867 let minAvailableModules = info.minAvailableModules;
1868
1869 // 1. Get minimal available modules
1870 // It doesn't make sense to traverse a chunk again with more available modules.
1871 // This step calculates the minimal available modules and skips traversal when
1872 // the list didn't shrink.
1873 availableModulesToBeMerged.sort(bySetSize);
1874 let changed = false;
1875 for (const availableModules of availableModulesToBeMerged) {
1876 if (minAvailableModules === undefined) {
1877 minAvailableModules = new Set(availableModules);
1878 info.minAvailableModules = minAvailableModules;
1879 changed = true;
1880 } else {
1881 for (const m of minAvailableModules) {
1882 if (!availableModules.has(m)) {
1883 minAvailableModules.delete(m);
1884 changed = true;
1885 }
1886 }
1887 }
1888 }
1889 availableModulesToBeMerged.length = 0;
1890 if (!changed) continue;
1891
1892 // 2. Get the edges at this point of the graph
1893 const deps = chunkDependencies.get(chunkGroup);
1894 if (!deps) continue;
1895 if (deps.length === 0) continue;
1896
1897 // 3. Create a new Set of available modules at this points
1898 newAvailableModules = new Set(minAvailableModules);
1899 for (const chunk of chunkGroup.chunks) {
1900 for (const m of chunk.modulesIterable) {
1901 newAvailableModules.add(m);
1902 }
1903 }
1904
1905 // 4. Foreach remaining edge
1906 const nextChunkGroups = new Set();
1907 for (let i = 0; i < deps.length; i++) {
1908 const dep = deps[i];
1909
1910 // Filter inline, rather than creating a new array from `.filter()`
1911 if (!filterFn(dep)) {
1912 continue;
1913 }
1914 const depChunkGroup = dep.chunkGroup;
1915 const depBlock = dep.block;
1916
1917 // 5. Connect block with chunk
1918 GraphHelpers.connectDependenciesBlockAndChunkGroup(
1919 depBlock,
1920 depChunkGroup
1921 );
1922
1923 // 6. Connect chunk with parent
1924 GraphHelpers.connectChunkGroupParentAndChild(chunkGroup, depChunkGroup);
1925
1926 nextChunkGroups.add(depChunkGroup);
1927 }
1928
1929 // 7. Enqueue further traversal
1930 for (const nextChunkGroup of nextChunkGroups) {
1931 let nextInfo = chunkGroupInfoMap.get(nextChunkGroup);
1932 if (nextInfo === undefined) {
1933 nextInfo = {
1934 minAvailableModules: undefined,
1935 availableModulesToBeMerged: []
1936 };
1937 chunkGroupInfoMap.set(nextChunkGroup, nextInfo);
1938 }
1939 nextInfo.availableModulesToBeMerged.push(newAvailableModules);
1940
1941 // As queue deduplicates enqueued items this makes sure that a ChunkGroup
1942 // is not enqueued twice
1943 queue2.enqueue(nextChunkGroup);
1944 }
1945 }
1946
1947 // Remove all unconnected chunk groups
1948 for (const chunkGroup of allCreatedChunkGroups) {
1949 if (chunkGroup.getNumberOfParents() === 0) {
1950 for (const chunk of chunkGroup.chunks) {
1951 const idx = this.chunks.indexOf(chunk);
1952 if (idx >= 0) this.chunks.splice(idx, 1);
1953 chunk.remove("unconnected");
1954 }
1955 chunkGroup.remove("unconnected");
1956 }
1957 }
1958 }
1959
1960 /**
1961 *
1962 * @param {Module} module module relationship for removal
1963 * @param {DependenciesBlockLike} block //TODO: good description
1964 * @returns {void}
1965 */
1966 removeReasonsOfDependencyBlock(module, block) {
1967 const iteratorDependency = d => {
1968 if (!d.module) {
1969 return;
1970 }
1971 if (d.module.removeReason(module, d)) {
1972 for (const chunk of d.module.chunksIterable) {
1973 this.patchChunksAfterReasonRemoval(d.module, chunk);
1974 }
1975 }
1976 };
1977
1978 if (block.blocks) {
1979 iterationOfArrayCallback(block.blocks, block =>
1980 this.removeReasonsOfDependencyBlock(module, block)
1981 );
1982 }
1983
1984 if (block.dependencies) {
1985 iterationOfArrayCallback(block.dependencies, iteratorDependency);
1986 }
1987
1988 if (block.variables) {
1989 iterationBlockVariable(block.variables, iteratorDependency);
1990 }
1991 }
1992
1993 /**
1994 * @param {Module} module module to patch tie
1995 * @param {Chunk} chunk chunk to patch tie
1996 * @returns {void}
1997 */
1998 patchChunksAfterReasonRemoval(module, chunk) {
1999 if (!module.hasReasons()) {
2000 this.removeReasonsOfDependencyBlock(module, module);
2001 }
2002 if (!module.hasReasonForChunk(chunk)) {
2003 if (module.removeChunk(chunk)) {
2004 this.removeChunkFromDependencies(module, chunk);
2005 }
2006 }
2007 }
2008
2009 /**
2010 *
2011 * @param {DependenciesBlock} block block tie for Chunk
2012 * @param {Chunk} chunk chunk to remove from dep
2013 * @returns {void}
2014 */
2015 removeChunkFromDependencies(block, chunk) {
2016 const iteratorDependency = d => {
2017 if (!d.module) {
2018 return;
2019 }
2020 this.patchChunksAfterReasonRemoval(d.module, chunk);
2021 };
2022
2023 const blocks = block.blocks;
2024 for (let indexBlock = 0; indexBlock < blocks.length; indexBlock++) {
2025 const asyncBlock = blocks[indexBlock];
2026 // Grab all chunks from the first Block's AsyncDepBlock
2027 const chunks = asyncBlock.chunkGroup.chunks;
2028 // For each chunk in chunkGroup
2029 for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
2030 const iteratedChunk = chunks[indexChunk];
2031 asyncBlock.chunkGroup.removeChunk(iteratedChunk);
2032 asyncBlock.chunkGroup.removeParent(iteratedChunk);
2033 // Recurse
2034 this.removeChunkFromDependencies(block, iteratedChunk);
2035 }
2036 }
2037
2038 if (block.dependencies) {
2039 iterationOfArrayCallback(block.dependencies, iteratorDependency);
2040 }
2041
2042 if (block.variables) {
2043 iterationBlockVariable(block.variables, iteratorDependency);
2044 }
2045 }
2046
2047 applyModuleIds() {
2048 const unusedIds = [];
2049 let nextFreeModuleId = 0;
2050 const usedIds = new Set();
2051 if (this.usedModuleIds) {
2052 for (const id of this.usedModuleIds) {
2053 usedIds.add(id);
2054 }
2055 }
2056
2057 const modules1 = this.modules;
2058 for (let indexModule1 = 0; indexModule1 < modules1.length; indexModule1++) {
2059 const module1 = modules1[indexModule1];
2060 if (module1.id !== null) {
2061 usedIds.add(module1.id);
2062 }
2063 }
2064
2065 if (usedIds.size > 0) {
2066 let usedIdMax = -1;
2067 for (const usedIdKey of usedIds) {
2068 if (typeof usedIdKey !== "number") {
2069 continue;
2070 }
2071
2072 usedIdMax = Math.max(usedIdMax, usedIdKey);
2073 }
2074
2075 let lengthFreeModules = (nextFreeModuleId = usedIdMax + 1);
2076
2077 while (lengthFreeModules--) {
2078 if (!usedIds.has(lengthFreeModules)) {
2079 unusedIds.push(lengthFreeModules);
2080 }
2081 }
2082 }
2083
2084 const modules2 = this.modules;
2085 for (let indexModule2 = 0; indexModule2 < modules2.length; indexModule2++) {
2086 const module2 = modules2[indexModule2];
2087 if (module2.id === null) {
2088 if (unusedIds.length > 0) {
2089 module2.id = unusedIds.pop();
2090 } else {
2091 module2.id = nextFreeModuleId++;
2092 }
2093 }
2094 }
2095 }
2096
2097 applyChunkIds() {
2098 /** @type {Set<number>} */
2099 const usedIds = new Set();
2100
2101 // Get used ids from usedChunkIds property (i. e. from records)
2102 if (this.usedChunkIds) {
2103 for (const id of this.usedChunkIds) {
2104 if (typeof id !== "number") {
2105 continue;
2106 }
2107
2108 usedIds.add(id);
2109 }
2110 }
2111
2112 // Get used ids from existing chunks
2113 const chunks = this.chunks;
2114 for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
2115 const chunk = chunks[indexChunk];
2116 const usedIdValue = chunk.id;
2117
2118 if (typeof usedIdValue !== "number") {
2119 continue;
2120 }
2121
2122 usedIds.add(usedIdValue);
2123 }
2124
2125 // Calculate maximum assigned chunk id
2126 let nextFreeChunkId = -1;
2127 for (const id of usedIds) {
2128 nextFreeChunkId = Math.max(nextFreeChunkId, id);
2129 }
2130 nextFreeChunkId++;
2131
2132 // Determine free chunk ids from 0 to maximum
2133 /** @type {number[]} */
2134 const unusedIds = [];
2135 if (nextFreeChunkId > 0) {
2136 let index = nextFreeChunkId;
2137 while (index--) {
2138 if (!usedIds.has(index)) {
2139 unusedIds.push(index);
2140 }
2141 }
2142 }
2143
2144 // Assign ids to chunk which has no id
2145 for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
2146 const chunk = chunks[indexChunk];
2147 if (chunk.id === null) {
2148 if (unusedIds.length > 0) {
2149 chunk.id = unusedIds.pop();
2150 } else {
2151 chunk.id = nextFreeChunkId++;
2152 }
2153 }
2154 if (!chunk.ids) {
2155 chunk.ids = [chunk.id];
2156 }
2157 }
2158 }
2159
2160 sortItemsWithModuleIds() {
2161 this.modules.sort(byIdOrIdentifier);
2162
2163 const modules = this.modules;
2164 for (let indexModule = 0; indexModule < modules.length; indexModule++) {
2165 modules[indexModule].sortItems(false);
2166 }
2167
2168 const chunks = this.chunks;
2169 for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
2170 chunks[indexChunk].sortItems();
2171 }
2172
2173 chunks.sort((a, b) => a.compareTo(b));
2174 }
2175
2176 sortItemsWithChunkIds() {
2177 for (const chunkGroup of this.chunkGroups) {
2178 chunkGroup.sortItems();
2179 }
2180
2181 this.chunks.sort(byId);
2182
2183 for (
2184 let indexModule = 0;
2185 indexModule < this.modules.length;
2186 indexModule++
2187 ) {
2188 this.modules[indexModule].sortItems(true);
2189 }
2190
2191 const chunks = this.chunks;
2192 for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
2193 chunks[indexChunk].sortItems();
2194 }
2195
2196 /**
2197 * Used to sort errors and warnings in compilation. this.warnings, and
2198 * this.errors contribute to the compilation hash and therefore should be
2199 * updated whenever other references (having a chunk id) are sorted. This preserves the hash
2200 * integrity
2201 *
2202 * @param {WebpackError} a first WebpackError instance (including subclasses)
2203 * @param {WebpackError} b second WebpackError instance (including subclasses)
2204 * @returns {-1|0|1} sort order index
2205 */
2206 const byMessage = (a, b) => {
2207 const ma = `${a.message}`;
2208 const mb = `${b.message}`;
2209 if (ma < mb) return -1;
2210 if (mb < ma) return 1;
2211 return 0;
2212 };
2213
2214 this.errors.sort(byMessage);
2215 this.warnings.sort(byMessage);
2216 this.children.sort(byNameOrHash);
2217 }
2218
2219 summarizeDependencies() {
2220 this.fileDependencies = new SortableSet(this.compilationDependencies);
2221 this.contextDependencies = new SortableSet();
2222 this.missingDependencies = new SortableSet();
2223
2224 for (
2225 let indexChildren = 0;
2226 indexChildren < this.children.length;
2227 indexChildren++
2228 ) {
2229 const child = this.children[indexChildren];
2230
2231 addAllToSet(this.fileDependencies, child.fileDependencies);
2232 addAllToSet(this.contextDependencies, child.contextDependencies);
2233 addAllToSet(this.missingDependencies, child.missingDependencies);
2234 }
2235
2236 for (
2237 let indexModule = 0;
2238 indexModule < this.modules.length;
2239 indexModule++
2240 ) {
2241 const module = this.modules[indexModule];
2242
2243 if (module.buildInfo.fileDependencies) {
2244 addAllToSet(this.fileDependencies, module.buildInfo.fileDependencies);
2245 }
2246 if (module.buildInfo.contextDependencies) {
2247 addAllToSet(
2248 this.contextDependencies,
2249 module.buildInfo.contextDependencies
2250 );
2251 }
2252 }
2253 for (const error of this.errors) {
2254 if (
2255 typeof error.missing === "object" &&
2256 error.missing &&
2257 error.missing[Symbol.iterator]
2258 ) {
2259 addAllToSet(this.missingDependencies, error.missing);
2260 }
2261 }
2262 this.fileDependencies.sort();
2263 this.contextDependencies.sort();
2264 this.missingDependencies.sort();
2265 }
2266
2267 createHash() {
2268 const outputOptions = this.outputOptions;
2269 const hashFunction = outputOptions.hashFunction;
2270 const hashDigest = outputOptions.hashDigest;
2271 const hashDigestLength = outputOptions.hashDigestLength;
2272 const hash = createHash(hashFunction);
2273 if (outputOptions.hashSalt) {
2274 hash.update(outputOptions.hashSalt);
2275 }
2276 this.mainTemplate.updateHash(hash);
2277 this.chunkTemplate.updateHash(hash);
2278 for (const key of Object.keys(this.moduleTemplates).sort()) {
2279 this.moduleTemplates[key].updateHash(hash);
2280 }
2281 for (const child of this.children) {
2282 hash.update(child.hash);
2283 }
2284 for (const warning of this.warnings) {
2285 hash.update(`${warning.message}`);
2286 }
2287 for (const error of this.errors) {
2288 hash.update(`${error.message}`);
2289 }
2290 const modules = this.modules;
2291 for (let i = 0; i < modules.length; i++) {
2292 const module = modules[i];
2293 const moduleHash = createHash(hashFunction);
2294 module.updateHash(moduleHash);
2295 module.hash = moduleHash.digest(hashDigest);
2296 module.renderedHash = module.hash.substr(0, hashDigestLength);
2297 }
2298 // clone needed as sort below is inplace mutation
2299 const chunks = this.chunks.slice();
2300 /**
2301 * sort here will bring all "falsy" values to the beginning
2302 * this is needed as the "hasRuntime()" chunks are dependent on the
2303 * hashes of the non-runtime chunks.
2304 */
2305 chunks.sort((a, b) => {
2306 const aEntry = a.hasRuntime();
2307 const bEntry = b.hasRuntime();
2308 if (aEntry && !bEntry) return 1;
2309 if (!aEntry && bEntry) return -1;
2310 return byId(a, b);
2311 });
2312 for (let i = 0; i < chunks.length; i++) {
2313 const chunk = chunks[i];
2314 const chunkHash = createHash(hashFunction);
2315 try {
2316 if (outputOptions.hashSalt) {
2317 chunkHash.update(outputOptions.hashSalt);
2318 }
2319 chunk.updateHash(chunkHash);
2320 const template = chunk.hasRuntime()
2321 ? this.mainTemplate
2322 : this.chunkTemplate;
2323 template.updateHashForChunk(
2324 chunkHash,
2325 chunk,
2326 this.moduleTemplates.javascript,
2327 this.dependencyTemplates
2328 );
2329 this.hooks.chunkHash.call(chunk, chunkHash);
2330 chunk.hash = chunkHash.digest(hashDigest);
2331 hash.update(chunk.hash);
2332 chunk.renderedHash = chunk.hash.substr(0, hashDigestLength);
2333 this.hooks.contentHash.call(chunk);
2334 } catch (err) {
2335 this.errors.push(new ChunkRenderError(chunk, "", err));
2336 }
2337 }
2338 this.fullHash = hash.digest(hashDigest);
2339 this.hash = this.fullHash.substr(0, hashDigestLength);
2340 }
2341
2342 /**
2343 * @param {string} update extra information
2344 * @returns {void}
2345 */
2346 modifyHash(update) {
2347 const outputOptions = this.outputOptions;
2348 const hashFunction = outputOptions.hashFunction;
2349 const hashDigest = outputOptions.hashDigest;
2350 const hashDigestLength = outputOptions.hashDigestLength;
2351 const hash = createHash(hashFunction);
2352 hash.update(this.fullHash);
2353 hash.update(update);
2354 this.fullHash = hash.digest(hashDigest);
2355 this.hash = this.fullHash.substr(0, hashDigestLength);
2356 }
2357
2358 createModuleAssets() {
2359 for (let i = 0; i < this.modules.length; i++) {
2360 const module = this.modules[i];
2361 if (module.buildInfo.assets) {
2362 for (const assetName of Object.keys(module.buildInfo.assets)) {
2363 const fileName = this.getPath(assetName);
2364 this.assets[fileName] = module.buildInfo.assets[assetName];
2365 this.hooks.moduleAsset.call(module, fileName);
2366 }
2367 }
2368 }
2369 }
2370
2371 createChunkAssets() {
2372 const outputOptions = this.outputOptions;
2373 const cachedSourceMap = new Map();
2374 /** @type {Map<string, {hash: string, source: Source, chunk: Chunk}>} */
2375 const alreadyWrittenFiles = new Map();
2376 for (let i = 0; i < this.chunks.length; i++) {
2377 const chunk = this.chunks[i];
2378 chunk.files = [];
2379 let source;
2380 let file;
2381 let filenameTemplate;
2382 try {
2383 const template = chunk.hasRuntime()
2384 ? this.mainTemplate
2385 : this.chunkTemplate;
2386 const manifest = template.getRenderManifest({
2387 chunk,
2388 hash: this.hash,
2389 fullHash: this.fullHash,
2390 outputOptions,
2391 moduleTemplates: this.moduleTemplates,
2392 dependencyTemplates: this.dependencyTemplates
2393 }); // [{ render(), filenameTemplate, pathOptions, identifier, hash }]
2394 for (const fileManifest of manifest) {
2395 const cacheName = fileManifest.identifier;
2396 const usedHash = fileManifest.hash;
2397 filenameTemplate = fileManifest.filenameTemplate;
2398 file = this.getPath(filenameTemplate, fileManifest.pathOptions);
2399
2400 // check if the same filename was already written by another chunk
2401 const alreadyWritten = alreadyWrittenFiles.get(file);
2402 if (alreadyWritten !== undefined) {
2403 if (alreadyWritten.hash === usedHash) {
2404 if (this.cache) {
2405 this.cache[cacheName] = {
2406 hash: usedHash,
2407 source: alreadyWritten.source
2408 };
2409 }
2410 chunk.files.push(file);
2411 this.hooks.chunkAsset.call(chunk, file);
2412 continue;
2413 } else {
2414 throw new Error(
2415 `Conflict: Multiple chunks emit assets to the same filename ${file}` +
2416 ` (chunks ${alreadyWritten.chunk.id} and ${chunk.id})`
2417 );
2418 }
2419 }
2420 if (
2421 this.cache &&
2422 this.cache[cacheName] &&
2423 this.cache[cacheName].hash === usedHash
2424 ) {
2425 source = this.cache[cacheName].source;
2426 } else {
2427 source = fileManifest.render();
2428 // Ensure that source is a cached source to avoid additional cost because of repeated access
2429 if (!(source instanceof CachedSource)) {
2430 const cacheEntry = cachedSourceMap.get(source);
2431 if (cacheEntry) {
2432 source = cacheEntry;
2433 } else {
2434 const cachedSource = new CachedSource(source);
2435 cachedSourceMap.set(source, cachedSource);
2436 source = cachedSource;
2437 }
2438 }
2439 if (this.cache) {
2440 this.cache[cacheName] = {
2441 hash: usedHash,
2442 source
2443 };
2444 }
2445 }
2446 if (this.assets[file] && this.assets[file] !== source) {
2447 throw new Error(
2448 `Conflict: Multiple assets emit to the same filename ${file}`
2449 );
2450 }
2451 this.assets[file] = source;
2452 chunk.files.push(file);
2453 this.hooks.chunkAsset.call(chunk, file);
2454 alreadyWrittenFiles.set(file, {
2455 hash: usedHash,
2456 source,
2457 chunk
2458 });
2459 }
2460 } catch (err) {
2461 this.errors.push(
2462 new ChunkRenderError(chunk, file || filenameTemplate, err)
2463 );
2464 }
2465 }
2466 }
2467
2468 /**
2469 * @param {string} filename used to get asset path with hash
2470 * @param {TODO=} data // TODO: figure out this param type
2471 * @returns {string} interpolated path
2472 */
2473 getPath(filename, data) {
2474 data = data || {};
2475 data.hash = data.hash || this.hash;
2476 return this.mainTemplate.getAssetPath(filename, data);
2477 }
2478
2479 /**
2480 * This function allows you to run another instance of webpack inside of webpack however as
2481 * a child with different settings and configurations (if desired) applied. It copies all hooks, plugins
2482 * from parent (or top level compiler) and creates a child Compilation
2483 *
2484 * @param {string} name name of the child compiler
2485 * @param {TODO} outputOptions // Need to convert config schema to types for this
2486 * @param {Plugin[]} plugins webpack plugins that will be applied
2487 * @returns {Compiler} creates a child Compiler instance
2488 */
2489 createChildCompiler(name, outputOptions, plugins) {
2490 const idx = this.childrenCounters[name] || 0;
2491 this.childrenCounters[name] = idx + 1;
2492 return this.compiler.createChildCompiler(
2493 this,
2494 name,
2495 idx,
2496 outputOptions,
2497 plugins
2498 );
2499 }
2500
2501 checkConstraints() {
2502 /** @type {Set<number|string>} */
2503 const usedIds = new Set();
2504
2505 const modules = this.modules;
2506 for (let indexModule = 0; indexModule < modules.length; indexModule++) {
2507 const moduleId = modules[indexModule].id;
2508 if (moduleId === null) continue;
2509 if (usedIds.has(moduleId)) {
2510 throw new Error(`checkConstraints: duplicate module id ${moduleId}`);
2511 }
2512 usedIds.add(moduleId);
2513 }
2514
2515 const chunks = this.chunks;
2516 for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
2517 const chunk = chunks[indexChunk];
2518 if (chunks.indexOf(chunk) !== indexChunk) {
2519 throw new Error(
2520 `checkConstraints: duplicate chunk in compilation ${chunk.debugId}`
2521 );
2522 }
2523 }
2524
2525 for (const chunkGroup of this.chunkGroups) {
2526 chunkGroup.checkConstraints();
2527 }
2528 }
2529}
2530
2531// TODO remove in webpack 5
2532Compilation.prototype.applyPlugins = util.deprecate(
2533 /**
2534 * @deprecated
2535 * @param {string} name Name
2536 * @param {any[]} args Other arguments
2537 * @returns {void}
2538 * @this {Compilation}
2539 */
2540 function(name, ...args) {
2541 this.hooks[
2542 name.replace(/[- ]([a-z])/g, match => match[1].toUpperCase())
2543 ].call(...args);
2544 },
2545 "Compilation.applyPlugins is deprecated. Use new API on `.hooks` instead"
2546);
2547
2548// TODO remove in webpack 5
2549Object.defineProperty(Compilation.prototype, "moduleTemplate", {
2550 configurable: false,
2551 get: util.deprecate(
2552 /**
2553 * @deprecated
2554 * @this {Compilation}
2555 * @returns {TODO} module template
2556 */
2557 function() {
2558 return this.moduleTemplates.javascript;
2559 },
2560 "Compilation.moduleTemplate: Use Compilation.moduleTemplates.javascript instead"
2561 ),
2562 set: util.deprecate(
2563 /**
2564 * @deprecated
2565 * @param {ModuleTemplate} value Template value
2566 * @this {Compilation}
2567 * @returns {void}
2568 */
2569 function(value) {
2570 this.moduleTemplates.javascript = value;
2571 },
2572 "Compilation.moduleTemplate: Use Compilation.moduleTemplates.javascript instead."
2573 )
2574});
2575
2576module.exports = Compilation;