UNPKG

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