UNPKG

122 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const asyncLib = require("neo-async");
9const {
10 HookMap,
11 SyncHook,
12 SyncBailHook,
13 SyncWaterfallHook,
14 AsyncSeriesHook,
15 AsyncSeriesBailHook
16} = require("tapable");
17const util = require("util");
18const { CachedSource } = require("webpack-sources");
19const { MultiItemCache } = require("./CacheFacade");
20const Chunk = require("./Chunk");
21const ChunkGraph = require("./ChunkGraph");
22const ChunkGroup = require("./ChunkGroup");
23const ChunkRenderError = require("./ChunkRenderError");
24const ChunkTemplate = require("./ChunkTemplate");
25const CodeGenerationError = require("./CodeGenerationError");
26const CodeGenerationResults = require("./CodeGenerationResults");
27const DependencyTemplates = require("./DependencyTemplates");
28const Entrypoint = require("./Entrypoint");
29const ErrorHelpers = require("./ErrorHelpers");
30const FileSystemInfo = require("./FileSystemInfo");
31const {
32 connectChunkGroupAndChunk,
33 connectChunkGroupParentAndChild
34} = require("./GraphHelpers");
35const { makeWebpackError } = require("./HookWebpackError");
36const MainTemplate = require("./MainTemplate");
37const Module = require("./Module");
38const ModuleDependencyError = require("./ModuleDependencyError");
39const ModuleDependencyWarning = require("./ModuleDependencyWarning");
40const ModuleGraph = require("./ModuleGraph");
41const ModuleNotFoundError = require("./ModuleNotFoundError");
42const ModuleProfile = require("./ModuleProfile");
43const ModuleRestoreError = require("./ModuleRestoreError");
44const ModuleStoreError = require("./ModuleStoreError");
45const ModuleTemplate = require("./ModuleTemplate");
46const RuntimeGlobals = require("./RuntimeGlobals");
47const RuntimeTemplate = require("./RuntimeTemplate");
48const Stats = require("./Stats");
49const WebpackError = require("./WebpackError");
50const buildChunkGraph = require("./buildChunkGraph");
51const BuildCycleError = require("./errors/BuildCycleError");
52const { Logger, LogType } = require("./logging/Logger");
53const StatsFactory = require("./stats/StatsFactory");
54const StatsPrinter = require("./stats/StatsPrinter");
55const { equals: arrayEquals } = require("./util/ArrayHelpers");
56const AsyncQueue = require("./util/AsyncQueue");
57const LazySet = require("./util/LazySet");
58const { provide } = require("./util/MapHelpers");
59const { cachedCleverMerge } = require("./util/cleverMerge");
60const {
61 compareLocations,
62 concatComparators,
63 compareSelect,
64 compareIds,
65 compareStringsNumeric,
66 compareModulesByIdentifier
67} = require("./util/comparators");
68const createHash = require("./util/createHash");
69const {
70 arrayToSetDeprecation,
71 soonFrozenObjectDeprecation,
72 createFakeHook
73} = require("./util/deprecation");
74const { getRuntimeKey } = require("./util/runtime");
75const { isSourceEqual } = require("./util/source");
76
77/** @template T @typedef {import("tapable").AsArray<T>} AsArray<T> */
78/** @typedef {import("webpack-sources").Source} Source */
79/** @typedef {import("../declarations/WebpackOptions").EntryDescriptionNormalized} EntryDescription */
80/** @typedef {import("../declarations/WebpackOptions").OutputNormalized} OutputOptions */
81/** @typedef {import("../declarations/WebpackOptions").StatsOptions} StatsOptions */
82/** @typedef {import("../declarations/WebpackOptions").WebpackPluginFunction} WebpackPluginFunction */
83/** @typedef {import("../declarations/WebpackOptions").WebpackPluginInstance} WebpackPluginInstance */
84/** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */
85/** @typedef {import("./Cache")} Cache */
86/** @typedef {import("./CacheFacade")} CacheFacade */
87/** @typedef {import("./ChunkGroup").ChunkGroupOptions} ChunkGroupOptions */
88/** @typedef {import("./Compiler")} Compiler */
89/** @typedef {import("./DependenciesBlock")} DependenciesBlock */
90/** @typedef {import("./Dependency")} Dependency */
91/** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */
92/** @typedef {import("./Dependency").ReferencedExport} ReferencedExport */
93/** @typedef {import("./DependencyTemplate")} DependencyTemplate */
94/** @typedef {import("./Entrypoint").EntryOptions} EntryOptions */
95/** @typedef {import("./Module").CodeGenerationResult} CodeGenerationResult */
96/** @typedef {import("./ModuleFactory")} ModuleFactory */
97/** @typedef {import("./ModuleFactory").ModuleFactoryCreateDataContextInfo} ModuleFactoryCreateDataContextInfo */
98/** @typedef {import("./RequestShortener")} RequestShortener */
99/** @typedef {import("./RuntimeModule")} RuntimeModule */
100/** @typedef {import("./Template").RenderManifestEntry} RenderManifestEntry */
101/** @typedef {import("./Template").RenderManifestOptions} RenderManifestOptions */
102/** @typedef {import("./stats/DefaultStatsFactoryPlugin").StatsAsset} StatsAsset */
103/** @typedef {import("./stats/DefaultStatsFactoryPlugin").StatsError} StatsError */
104/** @typedef {import("./stats/DefaultStatsFactoryPlugin").StatsModule} StatsModule */
105/** @typedef {import("./util/Hash")} Hash */
106/** @template T @typedef {import("./util/deprecation").FakeHook<T>} FakeHook<T> */
107/** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */
108
109/**
110 * @callback Callback
111 * @param {WebpackError=} err
112 * @returns {void}
113 */
114
115/**
116 * @callback ModuleCallback
117 * @param {WebpackError=} err
118 * @param {Module=} result
119 * @returns {void}
120 */
121
122/**
123 * @callback DepBlockVarDependenciesCallback
124 * @param {Dependency} dependency
125 * @returns {any}
126 */
127
128/** @typedef {new (...args: any[]) => Dependency} DepConstructor */
129/** @typedef {Record<string, Source>} CompilationAssets */
130
131/**
132 * @typedef {Object} AvailableModulesChunkGroupMapping
133 * @property {ChunkGroup} chunkGroup
134 * @property {Set<Module>} availableModules
135 * @property {boolean} needCopy
136 */
137
138/**
139 * @typedef {Object} DependenciesBlockLike
140 * @property {Dependency[]} dependencies
141 * @property {AsyncDependenciesBlock[]} blocks
142 */
143
144/**
145 * @typedef {Object} ChunkPathData
146 * @property {string|number} id
147 * @property {string=} name
148 * @property {string} hash
149 * @property {function(number): string=} hashWithLength
150 * @property {(Record<string, string>)=} contentHash
151 * @property {(Record<string, (length: number) => string>)=} contentHashWithLength
152 */
153
154/**
155 * @typedef {Object} ChunkHashContext
156 * @property {RuntimeTemplate} runtimeTemplate the runtime template
157 * @property {ModuleGraph} moduleGraph the module graph
158 * @property {ChunkGraph} chunkGraph the chunk graph
159 */
160
161/**
162 * @typedef {Object} EntryData
163 * @property {Dependency[]} dependencies dependencies of the entrypoint that should be evaluated at startup
164 * @property {Dependency[]} includeDependencies dependencies of the entrypoint that should be included but not evaluated
165 * @property {EntryOptions} options options of the entrypoint
166 */
167
168/**
169 * @typedef {Object} LogEntry
170 * @property {string} type
171 * @property {any[]} args
172 * @property {number} time
173 * @property {string[]=} trace
174 */
175
176/**
177 * @typedef {Object} KnownAssetInfo
178 * @property {boolean=} immutable true, if the asset can be long term cached forever (contains a hash)
179 * @property {boolean=} minimized whether the asset is minimized
180 * @property {string | string[]=} fullhash the value(s) of the full hash used for this asset
181 * @property {string | string[]=} chunkhash the value(s) of the chunk hash used for this asset
182 * @property {string | string[]=} modulehash the value(s) of the module hash used for this asset
183 * @property {string | string[]=} contenthash the value(s) of the content hash used for this asset
184 * @property {string=} sourceFilename when asset was created from a source file (potentially transformed), the original filename relative to compilation context
185 * @property {number=} size size in bytes, only set after asset has been emitted
186 * @property {boolean=} development true, when asset is only used for development and doesn't count towards user-facing assets
187 * @property {boolean=} hotModuleReplacement true, when asset ships data for updating an existing application (HMR)
188 * @property {boolean=} javascriptModule true, when asset is javascript and an ESM
189 * @property {Record<string, string | string[]>=} related object of pointers to other assets, keyed by type of relation (only points from parent to child)
190 */
191
192/** @typedef {KnownAssetInfo & Record<string, any>} AssetInfo */
193
194/**
195 * @typedef {Object} Asset
196 * @property {string} name the filename of the asset
197 * @property {Source} source source of the asset
198 * @property {AssetInfo} info info about the asset
199 */
200
201/**
202 * @typedef {Object} ModulePathData
203 * @property {string|number} id
204 * @property {string} hash
205 * @property {function(number): string=} hashWithLength
206 */
207
208/**
209 * @typedef {Object} PathData
210 * @property {ChunkGraph=} chunkGraph
211 * @property {string=} hash
212 * @property {function(number): string=} hashWithLength
213 * @property {(Chunk|ChunkPathData)=} chunk
214 * @property {(Module|ModulePathData)=} module
215 * @property {RuntimeSpec=} runtime
216 * @property {string=} filename
217 * @property {string=} basename
218 * @property {string=} query
219 * @property {string=} contentHashType
220 * @property {string=} contentHash
221 * @property {function(number): string=} contentHashWithLength
222 * @property {boolean=} noChunkHash
223 * @property {string=} url
224 */
225
226/**
227 * @typedef {Object} KnownNormalizedStatsOptions
228 * @property {string} context
229 * @property {RequestShortener} requestShortener
230 * @property {string} chunksSort
231 * @property {string} modulesSort
232 * @property {string} chunkModulesSort
233 * @property {string} nestedModulesSort
234 * @property {string} assetsSort
235 * @property {boolean} ids
236 * @property {boolean} cachedAssets
237 * @property {boolean} groupAssetsByEmitStatus
238 * @property {boolean} groupAssetsByPath
239 * @property {boolean} groupAssetsByExtension
240 * @property {number} assetsSpace
241 * @property {((value: string, asset: StatsAsset) => boolean)[]} excludeAssets
242 * @property {((name: string, module: StatsModule, type: "module" | "chunk" | "root-of-chunk" | "nested") => boolean)[]} excludeModules
243 * @property {((warning: StatsError, textValue: string) => boolean)[]} warningsFilter
244 * @property {boolean} cachedModules
245 * @property {boolean} orphanModules
246 * @property {boolean} dependentModules
247 * @property {boolean} runtimeModules
248 * @property {boolean} groupModulesByCacheStatus
249 * @property {boolean} groupModulesByLayer
250 * @property {boolean} groupModulesByAttributes
251 * @property {boolean} groupModulesByPath
252 * @property {boolean} groupModulesByExtension
253 * @property {boolean} groupModulesByType
254 * @property {boolean | "auto"} entrypoints
255 * @property {boolean} chunkGroups
256 * @property {boolean} chunkGroupAuxiliary
257 * @property {boolean} chunkGroupChildren
258 * @property {number} chunkGroupMaxAssets
259 * @property {number} modulesSpace
260 * @property {number} chunkModulesSpace
261 * @property {number} nestedModulesSpace
262 * @property {false|"none"|"error"|"warn"|"info"|"log"|"verbose"} logging
263 * @property {((value: string) => boolean)[]} loggingDebug
264 * @property {boolean} loggingTrace
265 * @property {any} _env
266 */
267
268/** @typedef {KnownNormalizedStatsOptions & Omit<StatsOptions, keyof KnownNormalizedStatsOptions> & Record<string, any>} NormalizedStatsOptions */
269
270/**
271 * @typedef {Object} KnownCreateStatsOptionsContext
272 * @property {boolean=} forToString
273 */
274
275/** @typedef {KnownCreateStatsOptionsContext & Record<string, any>} CreateStatsOptionsContext */
276
277/** @type {AssetInfo} */
278const EMPTY_ASSET_INFO = Object.freeze({});
279
280const esmDependencyCategory = "esm";
281// TODO webpack 6: remove
282const deprecatedNormalModuleLoaderHook = util.deprecate(
283 compilation => {
284 return require("./NormalModule").getCompilationHooks(compilation).loader;
285 },
286 "Compilation.hooks.normalModuleLoader was moved to NormalModule.getCompilationHooks(compilation).loader",
287 "DEP_WEBPACK_COMPILATION_NORMAL_MODULE_LOADER_HOOK"
288);
289
290const byId = compareSelect(
291 /**
292 * @param {Chunk} c chunk
293 * @returns {number | string} id
294 */ c => c.id,
295 compareIds
296);
297
298const byNameOrHash = concatComparators(
299 compareSelect(
300 /**
301 * @param {Compilation} c compilation
302 * @returns {string} name
303 */
304 c => c.name,
305 compareIds
306 ),
307 compareSelect(
308 /**
309 * @param {Compilation} c compilation
310 * @returns {string} hash
311 */ c => c.fullHash,
312 compareIds
313 )
314);
315
316const byMessage = compareSelect(err => `${err.message}`, compareStringsNumeric);
317
318const byModule = compareSelect(
319 err => (err.module && err.module.identifier()) || "",
320 compareStringsNumeric
321);
322
323const byLocation = compareSelect(err => err.loc, compareLocations);
324
325const compareErrors = concatComparators(byModule, byLocation, byMessage);
326
327class Compilation {
328 /**
329 * Creates an instance of Compilation.
330 * @param {Compiler} compiler the compiler which created the compilation
331 */
332 constructor(compiler) {
333 const getNormalModuleLoader = () => deprecatedNormalModuleLoaderHook(this);
334 /** @typedef {{ additionalAssets?: true | Function }} ProcessAssetsAdditionalOptions */
335 /** @type {AsyncSeriesHook<[CompilationAssets], ProcessAssetsAdditionalOptions>} */
336 const processAssetsHook = new AsyncSeriesHook(["assets"]);
337
338 let savedAssets = new Set();
339 const popNewAssets = assets => {
340 let newAssets = undefined;
341 for (const file of Object.keys(assets)) {
342 if (savedAssets.has(file)) continue;
343 if (newAssets === undefined) {
344 newAssets = Object.create(null);
345 }
346 newAssets[file] = assets[file];
347 savedAssets.add(file);
348 }
349 return newAssets;
350 };
351 processAssetsHook.intercept({
352 name: "Compilation",
353 call: () => {
354 savedAssets = new Set(Object.keys(this.assets));
355 },
356 register: tap => {
357 const { type, name } = tap;
358 const { fn, additionalAssets, ...remainingTap } = tap;
359 const additionalAssetsFn =
360 additionalAssets === true ? fn : additionalAssets;
361 let processedAssets = undefined;
362 switch (type) {
363 case "sync":
364 if (additionalAssetsFn) {
365 this.hooks.processAdditionalAssets.tap(name, assets => {
366 if (processedAssets === this.assets) additionalAssetsFn(assets);
367 });
368 }
369 return {
370 ...remainingTap,
371 type: "async",
372 fn: (assets, callback) => {
373 try {
374 fn(assets);
375 } catch (e) {
376 return callback(e);
377 }
378 processedAssets = this.assets;
379 const newAssets = popNewAssets(assets);
380 if (newAssets !== undefined) {
381 this.hooks.processAdditionalAssets.callAsync(
382 newAssets,
383 callback
384 );
385 return;
386 }
387 callback();
388 }
389 };
390 case "async":
391 if (additionalAssetsFn) {
392 this.hooks.processAdditionalAssets.tapAsync(
393 name,
394 (assets, callback) => {
395 if (processedAssets === this.assets)
396 return additionalAssetsFn(assets, callback);
397 callback();
398 }
399 );
400 }
401 return {
402 ...remainingTap,
403 fn: (assets, callback) => {
404 fn(assets, err => {
405 if (err) return callback(err);
406 processedAssets = this.assets;
407 const newAssets = popNewAssets(assets);
408 if (newAssets !== undefined) {
409 this.hooks.processAdditionalAssets.callAsync(
410 newAssets,
411 callback
412 );
413 return;
414 }
415 callback();
416 });
417 }
418 };
419 case "promise":
420 if (additionalAssetsFn) {
421 this.hooks.processAdditionalAssets.tapPromise(name, assets => {
422 if (processedAssets === this.assets)
423 return additionalAssetsFn(assets);
424 return Promise.resolve();
425 });
426 }
427 return {
428 ...remainingTap,
429 fn: assets => {
430 const p = fn(assets);
431 if (!p || !p.then) return p;
432 return p.then(() => {
433 processedAssets = this.assets;
434 const newAssets = popNewAssets(assets);
435 if (newAssets !== undefined) {
436 return this.hooks.processAdditionalAssets.promise(
437 newAssets
438 );
439 }
440 });
441 }
442 };
443 }
444 }
445 });
446
447 /** @type {SyncHook<[CompilationAssets]>} */
448 const afterProcessAssetsHook = new SyncHook(["assets"]);
449
450 /**
451 * @template T
452 * @param {string} name name of the hook
453 * @param {number} stage new stage
454 * @param {function(): AsArray<T>} getArgs get old hook function args
455 * @param {string=} code deprecation code (not deprecated when unset)
456 * @returns {FakeHook<Pick<AsyncSeriesHook<T>, "tap" | "tapAsync" | "tapPromise" | "name">>} fake hook which redirects
457 */
458 const createProcessAssetsHook = (name, stage, getArgs, code) => {
459 const errorMessage = reason => `Can't automatically convert plugin using Compilation.hooks.${name} to Compilation.hooks.processAssets because ${reason}.
460BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a single Compilation.hooks.processAssets hook.`;
461 const getOptions = options => {
462 if (typeof options === "string") options = { name: options };
463 if (options.stage) {
464 throw new Error(errorMessage("it's using the 'stage' option"));
465 }
466 return { ...options, stage: stage };
467 };
468 return createFakeHook(
469 {
470 name,
471 /** @type {AsyncSeriesHook<T>["intercept"]} */
472 intercept(interceptor) {
473 throw new Error(errorMessage("it's using 'intercept'"));
474 },
475 /** @type {AsyncSeriesHook<T>["tap"]} */
476 tap: (options, fn) => {
477 processAssetsHook.tap(getOptions(options), () => fn(...getArgs()));
478 },
479 /** @type {AsyncSeriesHook<T>["tapAsync"]} */
480 tapAsync: (options, fn) => {
481 processAssetsHook.tapAsync(
482 getOptions(options),
483 (assets, callback) =>
484 /** @type {any} */ (fn)(...getArgs(), callback)
485 );
486 },
487 /** @type {AsyncSeriesHook<T>["tapPromise"]} */
488 tapPromise: (options, fn) => {
489 processAssetsHook.tapPromise(getOptions(options), () =>
490 fn(...getArgs())
491 );
492 }
493 },
494 `${name} is deprecated (use Compilation.hooks.processAssets instead and use one of Compilation.PROCESS_ASSETS_STAGE_* as stage option)`,
495 code
496 );
497 };
498 this.hooks = Object.freeze({
499 /** @type {SyncHook<[Module]>} */
500 buildModule: new SyncHook(["module"]),
501 /** @type {SyncHook<[Module]>} */
502 rebuildModule: new SyncHook(["module"]),
503 /** @type {SyncHook<[Module, WebpackError]>} */
504 failedModule: new SyncHook(["module", "error"]),
505 /** @type {SyncHook<[Module]>} */
506 succeedModule: new SyncHook(["module"]),
507 /** @type {SyncHook<[Module]>} */
508 stillValidModule: new SyncHook(["module"]),
509
510 /** @type {SyncHook<[Dependency, EntryOptions]>} */
511 addEntry: new SyncHook(["entry", "options"]),
512 /** @type {SyncHook<[Dependency, EntryOptions, Error]>} */
513 failedEntry: new SyncHook(["entry", "options", "error"]),
514 /** @type {SyncHook<[Dependency, EntryOptions, Module]>} */
515 succeedEntry: new SyncHook(["entry", "options", "module"]),
516
517 /** @type {SyncWaterfallHook<[(string[] | ReferencedExport)[], Dependency, RuntimeSpec]>} */
518 dependencyReferencedExports: new SyncWaterfallHook([
519 "referencedExports",
520 "dependency",
521 "runtime"
522 ]),
523
524 /** @type {AsyncSeriesHook<[Iterable<Module>]>} */
525 finishModules: new AsyncSeriesHook(["modules"]),
526 /** @type {AsyncSeriesHook<[Module]>} */
527 finishRebuildingModule: new AsyncSeriesHook(["module"]),
528 /** @type {SyncHook<[]>} */
529 unseal: new SyncHook([]),
530 /** @type {SyncHook<[]>} */
531 seal: new SyncHook([]),
532
533 /** @type {SyncHook<[]>} */
534 beforeChunks: new SyncHook([]),
535 /** @type {SyncHook<[Iterable<Chunk>]>} */
536 afterChunks: new SyncHook(["chunks"]),
537
538 /** @type {SyncBailHook<[Iterable<Module>]>} */
539 optimizeDependencies: new SyncBailHook(["modules"]),
540 /** @type {SyncHook<[Iterable<Module>]>} */
541 afterOptimizeDependencies: new SyncHook(["modules"]),
542
543 /** @type {SyncHook<[]>} */
544 optimize: new SyncHook([]),
545 /** @type {SyncBailHook<[Iterable<Module>]>} */
546 optimizeModules: new SyncBailHook(["modules"]),
547 /** @type {SyncHook<[Iterable<Module>]>} */
548 afterOptimizeModules: new SyncHook(["modules"]),
549
550 /** @type {SyncBailHook<[Iterable<Chunk>, ChunkGroup[]]>} */
551 optimizeChunks: new SyncBailHook(["chunks", "chunkGroups"]),
552 /** @type {SyncHook<[Iterable<Chunk>, ChunkGroup[]]>} */
553 afterOptimizeChunks: new SyncHook(["chunks", "chunkGroups"]),
554
555 /** @type {AsyncSeriesHook<[Iterable<Chunk>, Iterable<Module>]>} */
556 optimizeTree: new AsyncSeriesHook(["chunks", "modules"]),
557 /** @type {SyncHook<[Iterable<Chunk>, Iterable<Module>]>} */
558 afterOptimizeTree: new SyncHook(["chunks", "modules"]),
559
560 /** @type {AsyncSeriesBailHook<[Iterable<Chunk>, Iterable<Module>]>} */
561 optimizeChunkModules: new AsyncSeriesBailHook(["chunks", "modules"]),
562 /** @type {SyncHook<[Iterable<Chunk>, Iterable<Module>]>} */
563 afterOptimizeChunkModules: new SyncHook(["chunks", "modules"]),
564 /** @type {SyncBailHook<[], boolean>} */
565 shouldRecord: new SyncBailHook([]),
566
567 /** @type {SyncHook<[Chunk, Set<string>]>} */
568 additionalChunkRuntimeRequirements: new SyncHook([
569 "chunk",
570 "runtimeRequirements"
571 ]),
572 /** @type {HookMap<SyncBailHook<[Chunk, Set<string>]>>} */
573 runtimeRequirementInChunk: new HookMap(
574 () => new SyncBailHook(["chunk", "runtimeRequirements"])
575 ),
576 /** @type {SyncHook<[Module, Set<string>]>} */
577 additionalModuleRuntimeRequirements: new SyncHook([
578 "module",
579 "runtimeRequirements"
580 ]),
581 /** @type {HookMap<SyncBailHook<[Module, Set<string>]>>} */
582 runtimeRequirementInModule: new HookMap(
583 () => new SyncBailHook(["module", "runtimeRequirements"])
584 ),
585 /** @type {SyncHook<[Chunk, Set<string>]>} */
586 additionalTreeRuntimeRequirements: new SyncHook([
587 "chunk",
588 "runtimeRequirements"
589 ]),
590 /** @type {HookMap<SyncBailHook<[Chunk, Set<string>]>>} */
591 runtimeRequirementInTree: new HookMap(
592 () => new SyncBailHook(["chunk", "runtimeRequirements"])
593 ),
594
595 /** @type {SyncHook<[RuntimeModule, Chunk]>} */
596 runtimeModule: new SyncHook(["module", "chunk"]),
597
598 /** @type {SyncHook<[Iterable<Module>, any]>} */
599 reviveModules: new SyncHook(["modules", "records"]),
600 /** @type {SyncHook<[Iterable<Module>]>} */
601 beforeModuleIds: new SyncHook(["modules"]),
602 /** @type {SyncHook<[Iterable<Module>]>} */
603 moduleIds: new SyncHook(["modules"]),
604 /** @type {SyncHook<[Iterable<Module>]>} */
605 optimizeModuleIds: new SyncHook(["modules"]),
606 /** @type {SyncHook<[Iterable<Module>]>} */
607 afterOptimizeModuleIds: new SyncHook(["modules"]),
608
609 /** @type {SyncHook<[Iterable<Chunk>, any]>} */
610 reviveChunks: new SyncHook(["chunks", "records"]),
611 /** @type {SyncHook<[Iterable<Chunk>]>} */
612 beforeChunkIds: new SyncHook(["chunks"]),
613 /** @type {SyncHook<[Iterable<Chunk>]>} */
614 chunkIds: new SyncHook(["chunks"]),
615 /** @type {SyncHook<[Iterable<Chunk>]>} */
616 optimizeChunkIds: new SyncHook(["chunks"]),
617 /** @type {SyncHook<[Iterable<Chunk>]>} */
618 afterOptimizeChunkIds: new SyncHook(["chunks"]),
619
620 /** @type {SyncHook<[Iterable<Module>, any]>} */
621 recordModules: new SyncHook(["modules", "records"]),
622 /** @type {SyncHook<[Iterable<Chunk>, any]>} */
623 recordChunks: new SyncHook(["chunks", "records"]),
624
625 /** @type {SyncHook<[Iterable<Module>]>} */
626 optimizeCodeGeneration: new SyncHook(["modules"]),
627
628 /** @type {SyncHook<[]>} */
629 beforeModuleHash: new SyncHook([]),
630 /** @type {SyncHook<[]>} */
631 afterModuleHash: new SyncHook([]),
632
633 /** @type {SyncHook<[]>} */
634 beforeCodeGeneration: new SyncHook([]),
635 /** @type {SyncHook<[]>} */
636 afterCodeGeneration: new SyncHook([]),
637
638 /** @type {SyncHook<[]>} */
639 beforeRuntimeRequirements: new SyncHook([]),
640 /** @type {SyncHook<[]>} */
641 afterRuntimeRequirements: new SyncHook([]),
642
643 /** @type {SyncHook<[]>} */
644 beforeHash: new SyncHook([]),
645 /** @type {SyncHook<[Chunk]>} */
646 contentHash: new SyncHook(["chunk"]),
647 /** @type {SyncHook<[]>} */
648 afterHash: new SyncHook([]),
649 /** @type {SyncHook<[any]>} */
650 recordHash: new SyncHook(["records"]),
651 /** @type {SyncHook<[Compilation, any]>} */
652 record: new SyncHook(["compilation", "records"]),
653
654 /** @type {SyncHook<[]>} */
655 beforeModuleAssets: new SyncHook([]),
656 /** @type {SyncBailHook<[], boolean>} */
657 shouldGenerateChunkAssets: new SyncBailHook([]),
658 /** @type {SyncHook<[]>} */
659 beforeChunkAssets: new SyncHook([]),
660 // TODO webpack 6 remove
661 /** @deprecated */
662 additionalChunkAssets: createProcessAssetsHook(
663 "additionalChunkAssets",
664 Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
665 () => [this.chunks],
666 "DEP_WEBPACK_COMPILATION_ADDITIONAL_CHUNK_ASSETS"
667 ),
668
669 // TODO webpack 6 deprecate
670 /** @deprecated */
671 additionalAssets: createProcessAssetsHook(
672 "additionalAssets",
673 Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
674 () => []
675 ),
676 // TODO webpack 6 remove
677 /** @deprecated */
678 optimizeChunkAssets: createProcessAssetsHook(
679 "optimizeChunkAssets",
680 Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE,
681 () => [this.chunks],
682 "DEP_WEBPACK_COMPILATION_OPTIMIZE_CHUNK_ASSETS"
683 ),
684 // TODO webpack 6 remove
685 /** @deprecated */
686 afterOptimizeChunkAssets: createProcessAssetsHook(
687 "afterOptimizeChunkAssets",
688 Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE + 1,
689 () => [this.chunks],
690 "DEP_WEBPACK_COMPILATION_AFTER_OPTIMIZE_CHUNK_ASSETS"
691 ),
692 // TODO webpack 6 deprecate
693 /** @deprecated */
694 optimizeAssets: processAssetsHook,
695 // TODO webpack 6 deprecate
696 /** @deprecated */
697 afterOptimizeAssets: afterProcessAssetsHook,
698
699 processAssets: processAssetsHook,
700 afterProcessAssets: afterProcessAssetsHook,
701 /** @type {AsyncSeriesHook<[CompilationAssets]>} */
702 processAdditionalAssets: new AsyncSeriesHook(["assets"]),
703
704 /** @type {SyncBailHook<[], boolean>} */
705 needAdditionalSeal: new SyncBailHook([]),
706 /** @type {AsyncSeriesHook<[]>} */
707 afterSeal: new AsyncSeriesHook([]),
708
709 /** @type {SyncWaterfallHook<[RenderManifestEntry[], RenderManifestOptions]>} */
710 renderManifest: new SyncWaterfallHook(["result", "options"]),
711
712 /** @type {SyncHook<[Hash]>} */
713 fullHash: new SyncHook(["hash"]),
714 /** @type {SyncHook<[Chunk, Hash, ChunkHashContext]>} */
715 chunkHash: new SyncHook(["chunk", "chunkHash", "ChunkHashContext"]),
716
717 /** @type {SyncHook<[Module, string]>} */
718 moduleAsset: new SyncHook(["module", "filename"]),
719 /** @type {SyncHook<[Chunk, string]>} */
720 chunkAsset: new SyncHook(["chunk", "filename"]),
721
722 /** @type {SyncWaterfallHook<[string, object, AssetInfo]>} */
723 assetPath: new SyncWaterfallHook(["path", "options", "assetInfo"]),
724
725 /** @type {SyncBailHook<[], boolean>} */
726 needAdditionalPass: new SyncBailHook([]),
727
728 /** @type {SyncHook<[Compiler, string, number]>} */
729 childCompiler: new SyncHook([
730 "childCompiler",
731 "compilerName",
732 "compilerIndex"
733 ]),
734
735 /** @type {SyncBailHook<[string, LogEntry], true>} */
736 log: new SyncBailHook(["origin", "logEntry"]),
737
738 /** @type {SyncWaterfallHook<[WebpackError[]]>} */
739 processWarnings: new SyncWaterfallHook(["warnings"]),
740 /** @type {SyncWaterfallHook<[WebpackError[]]>} */
741 processErrors: new SyncWaterfallHook(["errors"]),
742
743 /** @type {HookMap<SyncHook<[Partial<NormalizedStatsOptions>, CreateStatsOptionsContext]>>} */
744 statsPreset: new HookMap(() => new SyncHook(["options", "context"])),
745 /** @type {SyncHook<[Partial<NormalizedStatsOptions>, CreateStatsOptionsContext]>} */
746 statsNormalize: new SyncHook(["options", "context"]),
747 /** @type {SyncHook<[StatsFactory, NormalizedStatsOptions]>} */
748 statsFactory: new SyncHook(["statsFactory", "options"]),
749 /** @type {SyncHook<[StatsPrinter, NormalizedStatsOptions]>} */
750 statsPrinter: new SyncHook(["statsPrinter", "options"]),
751
752 get normalModuleLoader() {
753 return getNormalModuleLoader();
754 }
755 });
756 /** @type {string=} */
757 this.name = undefined;
758 this.startTime = undefined;
759 this.endTime = undefined;
760 /** @type {Compiler} */
761 this.compiler = compiler;
762 this.resolverFactory = compiler.resolverFactory;
763 this.inputFileSystem = compiler.inputFileSystem;
764 this.fileSystemInfo = new FileSystemInfo(this.inputFileSystem, {
765 managedPaths: compiler.managedPaths,
766 immutablePaths: compiler.immutablePaths,
767 logger: this.getLogger("webpack.FileSystemInfo")
768 });
769 if (compiler.fileTimestamps) {
770 this.fileSystemInfo.addFileTimestamps(compiler.fileTimestamps);
771 }
772 if (compiler.contextTimestamps) {
773 this.fileSystemInfo.addContextTimestamps(compiler.contextTimestamps);
774 }
775 /** @type {Map<string, string>} */
776 this.valueCacheVersions = new Map();
777 this.requestShortener = compiler.requestShortener;
778 this.compilerPath = compiler.compilerPath;
779
780 this.logger = this.getLogger("webpack.Compilation");
781
782 const options = compiler.options;
783 this.options = options;
784 this.outputOptions = options && options.output;
785 /** @type {boolean} */
786 this.bail = (options && options.bail) || false;
787 /** @type {boolean} */
788 this.profile = (options && options.profile) || false;
789
790 this.mainTemplate = new MainTemplate(this.outputOptions, this);
791 this.chunkTemplate = new ChunkTemplate(this.outputOptions, this);
792 this.runtimeTemplate = new RuntimeTemplate(
793 this,
794 this.outputOptions,
795 this.requestShortener
796 );
797 /** @type {{javascript: ModuleTemplate}} */
798 this.moduleTemplates = {
799 javascript: new ModuleTemplate(this.runtimeTemplate, this)
800 };
801 Object.defineProperties(this.moduleTemplates, {
802 asset: {
803 enumerable: false,
804 configurable: false,
805 get() {
806 throw new WebpackError(
807 "Compilation.moduleTemplates.asset has been removed"
808 );
809 }
810 },
811 webassembly: {
812 enumerable: false,
813 configurable: false,
814 get() {
815 throw new WebpackError(
816 "Compilation.moduleTemplates.webassembly has been removed"
817 );
818 }
819 }
820 });
821
822 this.moduleGraph = new ModuleGraph();
823 this.chunkGraph = undefined;
824 /** @type {CodeGenerationResults} */
825 this.codeGenerationResults = undefined;
826
827 /** @type {AsyncQueue<Module, Module, Module>} */
828 this.processDependenciesQueue = new AsyncQueue({
829 name: "processDependencies",
830 parallelism: options.parallelism || 100,
831 processor: this._processModuleDependencies.bind(this)
832 });
833 /** @type {AsyncQueue<Module, string, Module>} */
834 this.addModuleQueue = new AsyncQueue({
835 name: "addModule",
836 parent: this.processDependenciesQueue,
837 getKey: module => module.identifier(),
838 processor: this._addModule.bind(this)
839 });
840 /** @type {AsyncQueue<FactorizeModuleOptions, string, Module>} */
841 this.factorizeQueue = new AsyncQueue({
842 name: "factorize",
843 parent: this.addModuleQueue,
844 processor: this._factorizeModule.bind(this)
845 });
846 /** @type {AsyncQueue<Module, Module, Module>} */
847 this.buildQueue = new AsyncQueue({
848 name: "build",
849 parent: this.factorizeQueue,
850 processor: this._buildModule.bind(this)
851 });
852 /** @type {AsyncQueue<Module, Module, Module>} */
853 this.rebuildQueue = new AsyncQueue({
854 name: "rebuild",
855 parallelism: options.parallelism || 100,
856 processor: this._rebuildModule.bind(this)
857 });
858
859 /**
860 * Modules in value are building during the build of Module in key.
861 * Means value blocking key from finishing.
862 * Needed to detect build cycles.
863 * @type {WeakMap<Module, Set<Module>>}
864 */
865 this.creatingModuleDuringBuild = new WeakMap();
866
867 /** @type {Map<string, EntryData>} */
868 this.entries = new Map();
869 /** @type {EntryData} */
870 this.globalEntry = {
871 dependencies: [],
872 includeDependencies: [],
873 options: {
874 name: undefined
875 }
876 };
877 /** @type {Map<string, Entrypoint>} */
878 this.entrypoints = new Map();
879 /** @type {Entrypoint[]} */
880 this.asyncEntrypoints = [];
881 /** @type {Set<Chunk>} */
882 this.chunks = new Set();
883 arrayToSetDeprecation(this.chunks, "Compilation.chunks");
884 /** @type {ChunkGroup[]} */
885 this.chunkGroups = [];
886 /** @type {Map<string, ChunkGroup>} */
887 this.namedChunkGroups = new Map();
888 /** @type {Map<string, Chunk>} */
889 this.namedChunks = new Map();
890 /** @type {Set<Module>} */
891 this.modules = new Set();
892 arrayToSetDeprecation(this.modules, "Compilation.modules");
893 /** @private @type {Map<string, Module>} */
894 this._modules = new Map();
895 this.records = null;
896 /** @type {string[]} */
897 this.additionalChunkAssets = [];
898 /** @type {CompilationAssets} */
899 this.assets = {};
900 /** @type {Map<string, AssetInfo>} */
901 this.assetsInfo = new Map();
902 /** @type {Map<string, Map<string, Set<string>>>} */
903 this._assetsRelatedIn = new Map();
904 /** @type {WebpackError[]} */
905 this.errors = [];
906 /** @type {WebpackError[]} */
907 this.warnings = [];
908 /** @type {Compilation[]} */
909 this.children = [];
910 /** @type {Map<string, LogEntry[]>} */
911 this.logging = new Map();
912 /** @type {Map<DepConstructor, ModuleFactory>} */
913 this.dependencyFactories = new Map();
914 /** @type {DependencyTemplates} */
915 this.dependencyTemplates = new DependencyTemplates();
916 this.childrenCounters = {};
917 /** @type {Set<number|string>} */
918 this.usedChunkIds = null;
919 /** @type {Set<number>} */
920 this.usedModuleIds = null;
921 /** @type {boolean} */
922 this.needAdditionalPass = false;
923 /** @type {WeakSet<Module>} */
924 this.builtModules = new WeakSet();
925 /** @type {WeakSet<Module>} */
926 this.codeGeneratedModules = new WeakSet();
927 /** @private @type {Map<Module, Callback[]>} */
928 this._rebuildingModules = new Map();
929 /** @type {Set<string>} */
930 this.emittedAssets = new Set();
931 /** @type {Set<string>} */
932 this.comparedForEmitAssets = new Set();
933 /** @type {LazySet<string>} */
934 this.fileDependencies = new LazySet();
935 /** @type {LazySet<string>} */
936 this.contextDependencies = new LazySet();
937 /** @type {LazySet<string>} */
938 this.missingDependencies = new LazySet();
939 /** @type {LazySet<string>} */
940 this.buildDependencies = new LazySet();
941 // TODO webpack 6 remove
942 this.compilationDependencies = {
943 add: util.deprecate(
944 item => this.fileDependencies.add(item),
945 "Compilation.compilationDependencies is deprecated (used Compilation.fileDependencies instead)",
946 "DEP_WEBPACK_COMPILATION_COMPILATION_DEPENDENCIES"
947 )
948 };
949
950 this._modulesCache = this.getCache("Compilation/modules");
951 this._assetsCache = this.getCache("Compilation/assets");
952 this._codeGenerationCache = this.getCache("Compilation/codeGeneration");
953 }
954
955 getStats() {
956 return new Stats(this);
957 }
958
959 /**
960 * @param {StatsOptions | string} optionsOrPreset stats option value
961 * @param {CreateStatsOptionsContext} context context
962 * @returns {NormalizedStatsOptions} normalized options
963 */
964 createStatsOptions(optionsOrPreset, context = {}) {
965 if (
966 typeof optionsOrPreset === "boolean" ||
967 typeof optionsOrPreset === "string"
968 ) {
969 optionsOrPreset = { preset: optionsOrPreset };
970 }
971 if (typeof optionsOrPreset === "object" && optionsOrPreset !== null) {
972 // We use this method of shallow cloning this object to include
973 // properties in the prototype chain
974 /** @type {Partial<NormalizedStatsOptions>} */
975 const options = {};
976 for (const key in optionsOrPreset) {
977 options[key] = optionsOrPreset[key];
978 }
979 if (options.preset !== undefined) {
980 this.hooks.statsPreset.for(options.preset).call(options, context);
981 }
982 this.hooks.statsNormalize.call(options, context);
983 return /** @type {NormalizedStatsOptions} */ (options);
984 } else {
985 /** @type {Partial<NormalizedStatsOptions>} */
986 const options = {};
987 this.hooks.statsNormalize.call(options, context);
988 return /** @type {NormalizedStatsOptions} */ (options);
989 }
990 }
991
992 createStatsFactory(options) {
993 const statsFactory = new StatsFactory();
994 this.hooks.statsFactory.call(statsFactory, options);
995 return statsFactory;
996 }
997
998 createStatsPrinter(options) {
999 const statsPrinter = new StatsPrinter();
1000 this.hooks.statsPrinter.call(statsPrinter, options);
1001 return statsPrinter;
1002 }
1003
1004 /**
1005 * @param {string} name cache name
1006 * @returns {CacheFacade} the cache facade instance
1007 */
1008 getCache(name) {
1009 return this.compiler.getCache(name);
1010 }
1011
1012 /**
1013 * @param {string | (function(): string)} name name of the logger, or function called once to get the logger name
1014 * @returns {Logger} a logger with that name
1015 */
1016 getLogger(name) {
1017 if (!name) {
1018 throw new TypeError("Compilation.getLogger(name) called without a name");
1019 }
1020 /** @type {LogEntry[] | undefined} */
1021 let logEntries;
1022 return new Logger(
1023 (type, args) => {
1024 if (typeof name === "function") {
1025 name = name();
1026 if (!name) {
1027 throw new TypeError(
1028 "Compilation.getLogger(name) called with a function not returning a name"
1029 );
1030 }
1031 }
1032 let trace;
1033 switch (type) {
1034 case LogType.warn:
1035 case LogType.error:
1036 case LogType.trace:
1037 trace = ErrorHelpers.cutOffLoaderExecution(new Error("Trace").stack)
1038 .split("\n")
1039 .slice(3);
1040 break;
1041 }
1042 /** @type {LogEntry} */
1043 const logEntry = {
1044 time: Date.now(),
1045 type,
1046 args,
1047 trace
1048 };
1049 if (this.hooks.log.call(name, logEntry) === undefined) {
1050 if (logEntry.type === LogType.profileEnd) {
1051 // eslint-disable-next-line node/no-unsupported-features/node-builtins
1052 if (typeof console.profileEnd === "function") {
1053 // eslint-disable-next-line node/no-unsupported-features/node-builtins
1054 console.profileEnd(`[${name}] ${logEntry.args[0]}`);
1055 }
1056 }
1057 if (logEntries === undefined) {
1058 logEntries = this.logging.get(name);
1059 if (logEntries === undefined) {
1060 logEntries = [];
1061 this.logging.set(name, logEntries);
1062 }
1063 }
1064 logEntries.push(logEntry);
1065 if (logEntry.type === LogType.profile) {
1066 // eslint-disable-next-line node/no-unsupported-features/node-builtins
1067 if (typeof console.profile === "function") {
1068 // eslint-disable-next-line node/no-unsupported-features/node-builtins
1069 console.profile(`[${name}] ${logEntry.args[0]}`);
1070 }
1071 }
1072 }
1073 },
1074 childName => {
1075 if (typeof name === "function") {
1076 if (typeof childName === "function") {
1077 return this.getLogger(() => {
1078 if (typeof name === "function") {
1079 name = name();
1080 if (!name) {
1081 throw new TypeError(
1082 "Compilation.getLogger(name) called with a function not returning a name"
1083 );
1084 }
1085 }
1086 if (typeof childName === "function") {
1087 childName = childName();
1088 if (!childName) {
1089 throw new TypeError(
1090 "Logger.getChildLogger(name) called with a function not returning a name"
1091 );
1092 }
1093 }
1094 return `${name}/${childName}`;
1095 });
1096 } else {
1097 return this.getLogger(() => {
1098 if (typeof name === "function") {
1099 name = name();
1100 if (!name) {
1101 throw new TypeError(
1102 "Compilation.getLogger(name) called with a function not returning a name"
1103 );
1104 }
1105 }
1106 return `${name}/${childName}`;
1107 });
1108 }
1109 } else {
1110 if (typeof childName === "function") {
1111 return this.getLogger(() => {
1112 if (typeof childName === "function") {
1113 childName = childName();
1114 if (!childName) {
1115 throw new TypeError(
1116 "Logger.getChildLogger(name) called with a function not returning a name"
1117 );
1118 }
1119 }
1120 return `${name}/${childName}`;
1121 });
1122 } else {
1123 return this.getLogger(`${name}/${childName}`);
1124 }
1125 }
1126 }
1127 );
1128 }
1129
1130 /**
1131 * @param {Module} module module to be added that was created
1132 * @param {ModuleCallback} callback returns the module in the compilation,
1133 * it could be the passed one (if new), or an already existing in the compilation
1134 * @returns {void}
1135 */
1136 addModule(module, callback) {
1137 this.addModuleQueue.add(module, callback);
1138 }
1139
1140 /**
1141 * @param {Module} module module to be added that was created
1142 * @param {ModuleCallback} callback returns the module in the compilation,
1143 * it could be the passed one (if new), or an already existing in the compilation
1144 * @returns {void}
1145 */
1146 _addModule(module, callback) {
1147 const identifier = module.identifier();
1148 const alreadyAddedModule = this._modules.get(identifier);
1149 if (alreadyAddedModule) {
1150 return callback(null, alreadyAddedModule);
1151 }
1152
1153 const currentProfile = this.profile
1154 ? this.moduleGraph.getProfile(module)
1155 : undefined;
1156 if (currentProfile !== undefined) {
1157 currentProfile.markRestoringStart();
1158 }
1159
1160 this._modulesCache.get(identifier, null, (err, cacheModule) => {
1161 if (err) return callback(new ModuleRestoreError(module, err));
1162
1163 if (currentProfile !== undefined) {
1164 currentProfile.markRestoringEnd();
1165 currentProfile.markIntegrationStart();
1166 }
1167
1168 if (cacheModule) {
1169 cacheModule.updateCacheModule(module);
1170
1171 module = cacheModule;
1172 }
1173 this._modules.set(identifier, module);
1174 this.modules.add(module);
1175 ModuleGraph.setModuleGraphForModule(module, this.moduleGraph);
1176 if (currentProfile !== undefined) {
1177 currentProfile.markIntegrationEnd();
1178 }
1179 callback(null, module);
1180 });
1181 }
1182
1183 /**
1184 * Fetches a module from a compilation by its identifier
1185 * @param {Module} module the module provided
1186 * @returns {Module} the module requested
1187 */
1188 getModule(module) {
1189 const identifier = module.identifier();
1190 return this._modules.get(identifier);
1191 }
1192
1193 /**
1194 * Attempts to search for a module by its identifier
1195 * @param {string} identifier identifier (usually path) for module
1196 * @returns {Module|undefined} attempt to search for module and return it, else undefined
1197 */
1198 findModule(identifier) {
1199 return this._modules.get(identifier);
1200 }
1201
1202 /**
1203 * Schedules a build of the module object
1204 *
1205 * @param {Module} module module to be built
1206 * @param {ModuleCallback} callback the callback
1207 * @returns {void}
1208 */
1209 buildModule(module, callback) {
1210 this.buildQueue.add(module, callback);
1211 }
1212
1213 /**
1214 * Builds the module object
1215 *
1216 * @param {Module} module module to be built
1217 * @param {ModuleCallback} callback the callback
1218 * @returns {void}
1219 */
1220 _buildModule(module, callback) {
1221 const currentProfile = this.profile
1222 ? this.moduleGraph.getProfile(module)
1223 : undefined;
1224 if (currentProfile !== undefined) {
1225 currentProfile.markBuildingStart();
1226 }
1227
1228 module.needBuild(
1229 {
1230 fileSystemInfo: this.fileSystemInfo,
1231 valueCacheVersions: this.valueCacheVersions
1232 },
1233 (err, needBuild) => {
1234 if (err) return callback(err);
1235
1236 if (!needBuild) {
1237 if (currentProfile !== undefined) {
1238 currentProfile.markBuildingEnd();
1239 }
1240 this.hooks.stillValidModule.call(module);
1241 return callback();
1242 }
1243
1244 this.hooks.buildModule.call(module);
1245 this.builtModules.add(module);
1246 module.build(
1247 this.options,
1248 this,
1249 this.resolverFactory.get("normal", module.resolveOptions),
1250 this.inputFileSystem,
1251 err => {
1252 if (currentProfile !== undefined) {
1253 currentProfile.markBuildingEnd();
1254 }
1255 if (err) {
1256 this.hooks.failedModule.call(module, err);
1257 return callback(err);
1258 }
1259 if (currentProfile !== undefined) {
1260 currentProfile.markStoringStart();
1261 }
1262 this._modulesCache.store(module.identifier(), null, module, err => {
1263 if (currentProfile !== undefined) {
1264 currentProfile.markStoringEnd();
1265 }
1266 if (err) {
1267 this.hooks.failedModule.call(module, err);
1268 return callback(new ModuleStoreError(module, err));
1269 }
1270 this.hooks.succeedModule.call(module);
1271 return callback();
1272 });
1273 }
1274 );
1275 }
1276 );
1277 }
1278
1279 /**
1280 * @param {Module} module to be processed for deps
1281 * @param {ModuleCallback} callback callback to be triggered
1282 * @returns {void}
1283 */
1284 processModuleDependencies(module, callback) {
1285 this.processDependenciesQueue.add(module, callback);
1286 }
1287
1288 /**
1289 * @param {Module} module to be processed for deps
1290 * @returns {void}
1291 */
1292 processModuleDependenciesNonRecursive(module) {
1293 const processDependenciesBlock = block => {
1294 if (block.dependencies) {
1295 for (const dep of block.dependencies) {
1296 this.moduleGraph.setParents(dep, block, module);
1297 }
1298 }
1299 if (block.blocks) {
1300 for (const b of block.blocks) processDependenciesBlock(b);
1301 }
1302 };
1303
1304 processDependenciesBlock(module);
1305 }
1306
1307 /**
1308 * @param {Module} module to be processed for deps
1309 * @param {ModuleCallback} callback callback to be triggered
1310 * @returns {void}
1311 */
1312 _processModuleDependencies(module, callback) {
1313 const dependencies = new Map();
1314
1315 /**
1316 * @type {Array<{factory: ModuleFactory, dependencies: Dependency[], originModule: Module|null}>}
1317 */
1318 const sortedDependencies = [];
1319
1320 let currentBlock = module;
1321
1322 let factoryCacheKey;
1323 let factoryCacheValue;
1324 let factoryCacheValue2;
1325 let listCacheKey;
1326 let listCacheValue;
1327
1328 const processDependency = dep => {
1329 this.moduleGraph.setParents(dep, currentBlock, module);
1330 const resourceIdent = dep.getResourceIdentifier();
1331 if (resourceIdent) {
1332 // Here webpack is using heuristic that assumes
1333 // mostly esm dependencies would be used
1334 // so we don't allocate extra string for them
1335 const category = dep.category;
1336 const cacheKey =
1337 category === esmDependencyCategory
1338 ? resourceIdent
1339 : `${category}${resourceIdent}`;
1340 const constructor = dep.constructor;
1341 let innerMap;
1342 let factory;
1343 if (factoryCacheKey === constructor) {
1344 innerMap = factoryCacheValue;
1345 if (listCacheKey === cacheKey) {
1346 listCacheValue.push(dep);
1347 return;
1348 }
1349 } else {
1350 factory = this.dependencyFactories.get(dep.constructor);
1351 if (factory === undefined) {
1352 throw new Error(
1353 `No module factory available for dependency type: ${dep.constructor.name}`
1354 );
1355 }
1356 innerMap = dependencies.get(factory);
1357 if (innerMap === undefined) {
1358 dependencies.set(factory, (innerMap = new Map()));
1359 }
1360 factoryCacheKey = constructor;
1361 factoryCacheValue = innerMap;
1362 factoryCacheValue2 = factory;
1363 }
1364 let list = innerMap.get(cacheKey);
1365 if (list === undefined) {
1366 innerMap.set(cacheKey, (list = []));
1367 sortedDependencies.push({
1368 factory: factoryCacheValue2,
1369 dependencies: list,
1370 originModule: module
1371 });
1372 }
1373 list.push(dep);
1374 listCacheKey = cacheKey;
1375 listCacheValue = list;
1376 }
1377 };
1378
1379 const processDependenciesBlock = block => {
1380 if (block.dependencies) {
1381 currentBlock = block;
1382 for (const dep of block.dependencies) processDependency(dep);
1383 }
1384 if (block.blocks) {
1385 for (const b of block.blocks) processDependenciesBlock(b);
1386 }
1387 };
1388
1389 try {
1390 processDependenciesBlock(module);
1391 } catch (e) {
1392 return callback(e);
1393 }
1394
1395 if (sortedDependencies.length === 0) {
1396 callback();
1397 return;
1398 }
1399
1400 // This is nested so we need to allow one additional task
1401 this.processDependenciesQueue.increaseParallelism();
1402
1403 asyncLib.forEach(
1404 sortedDependencies,
1405 (item, callback) => {
1406 this.handleModuleCreation(item, err => {
1407 // In V8, the Error objects keep a reference to the functions on the stack. These warnings &
1408 // errors are created inside closures that keep a reference to the Compilation, so errors are
1409 // leaking the Compilation object.
1410 if (err && this.bail) {
1411 // eslint-disable-next-line no-self-assign
1412 err.stack = err.stack;
1413 return callback(err);
1414 }
1415 callback();
1416 });
1417 },
1418 err => {
1419 this.processDependenciesQueue.decreaseParallelism();
1420
1421 return callback(err);
1422 }
1423 );
1424 }
1425
1426 /**
1427 * @typedef {Object} HandleModuleCreationOptions
1428 * @property {ModuleFactory} factory
1429 * @property {Dependency[]} dependencies
1430 * @property {Module | null} originModule
1431 * @property {Partial<ModuleFactoryCreateDataContextInfo>=} contextInfo
1432 * @property {string=} context
1433 * @property {boolean=} recursive recurse into dependencies of the created module
1434 */
1435
1436 /**
1437 * @param {HandleModuleCreationOptions} options options object
1438 * @param {ModuleCallback} callback callback
1439 * @returns {void}
1440 */
1441 handleModuleCreation(
1442 {
1443 factory,
1444 dependencies,
1445 originModule,
1446 contextInfo,
1447 context,
1448 recursive = true
1449 },
1450 callback
1451 ) {
1452 const moduleGraph = this.moduleGraph;
1453
1454 const currentProfile = this.profile ? new ModuleProfile() : undefined;
1455
1456 this.factorizeModule(
1457 {
1458 currentProfile,
1459 factory,
1460 dependencies,
1461 originModule,
1462 contextInfo,
1463 context
1464 },
1465 (err, newModule) => {
1466 if (err) {
1467 if (dependencies.every(d => d.optional)) {
1468 this.warnings.push(err);
1469 } else {
1470 this.errors.push(err);
1471 }
1472 return callback(err);
1473 }
1474
1475 if (!newModule) {
1476 return callback();
1477 }
1478
1479 if (currentProfile !== undefined) {
1480 moduleGraph.setProfile(newModule, currentProfile);
1481 }
1482
1483 this.addModule(newModule, (err, module) => {
1484 if (err) {
1485 if (!err.module) {
1486 err.module = module;
1487 }
1488 this.errors.push(err);
1489
1490 return callback(err);
1491 }
1492
1493 for (let i = 0; i < dependencies.length; i++) {
1494 const dependency = dependencies[i];
1495 moduleGraph.setResolvedModule(
1496 recursive ? originModule : null,
1497 dependency,
1498 module
1499 );
1500 }
1501
1502 moduleGraph.setIssuerIfUnset(
1503 module,
1504 originModule !== undefined ? originModule : null
1505 );
1506 if (module !== newModule) {
1507 if (currentProfile !== undefined) {
1508 const otherProfile = moduleGraph.getProfile(module);
1509 if (otherProfile !== undefined) {
1510 currentProfile.mergeInto(otherProfile);
1511 } else {
1512 moduleGraph.setProfile(module, currentProfile);
1513 }
1514 }
1515 }
1516
1517 // Check for cycles when build is trigger inside another build
1518 let creatingModuleDuringBuildSet = undefined;
1519 if (!recursive && this.buildQueue.isProcessing(originModule)) {
1520 // Track build dependency
1521 creatingModuleDuringBuildSet = this.creatingModuleDuringBuild.get(
1522 originModule
1523 );
1524 if (creatingModuleDuringBuildSet === undefined) {
1525 creatingModuleDuringBuildSet = new Set();
1526 this.creatingModuleDuringBuild.set(
1527 originModule,
1528 creatingModuleDuringBuildSet
1529 );
1530 }
1531 creatingModuleDuringBuildSet.add(originModule);
1532
1533 // When building is blocked by another module
1534 // search for a cycle, cancel the cycle by throwing
1535 // an error (otherwise this would deadlock)
1536 const blockReasons = this.creatingModuleDuringBuild.get(module);
1537 if (blockReasons !== undefined) {
1538 const set = new Set(blockReasons);
1539 for (const item of set) {
1540 const blockReasons = this.creatingModuleDuringBuild.get(item);
1541 if (blockReasons !== undefined) {
1542 for (const m of blockReasons) {
1543 if (m === module) {
1544 return callback(new BuildCycleError(module));
1545 }
1546 set.add(m);
1547 }
1548 }
1549 }
1550 }
1551 }
1552
1553 this.buildModule(module, err => {
1554 if (creatingModuleDuringBuildSet !== undefined) {
1555 creatingModuleDuringBuildSet.delete(module);
1556 }
1557 if (err) {
1558 if (!err.module) {
1559 err.module = module;
1560 }
1561 this.errors.push(err);
1562
1563 return callback(err);
1564 }
1565
1566 if (!recursive) {
1567 this.processModuleDependenciesNonRecursive(module);
1568 callback(null, module);
1569 return;
1570 }
1571
1572 // This avoids deadlocks for circular dependencies
1573 if (this.processDependenciesQueue.isProcessing(module)) {
1574 return callback();
1575 }
1576
1577 this.processModuleDependencies(module, err => {
1578 if (err) {
1579 return callback(err);
1580 }
1581 callback(null, module);
1582 });
1583 });
1584 });
1585 }
1586 );
1587 }
1588
1589 /**
1590 * @typedef {Object} FactorizeModuleOptions
1591 * @property {ModuleProfile} currentProfile
1592 * @property {ModuleFactory} factory
1593 * @property {Dependency[]} dependencies
1594 * @property {Module | null} originModule
1595 * @property {Partial<ModuleFactoryCreateDataContextInfo>=} contextInfo
1596 * @property {string=} context
1597 */
1598
1599 /**
1600 * @param {FactorizeModuleOptions} options options object
1601 * @param {ModuleCallback} callback callback
1602 * @returns {void}
1603 */
1604 factorizeModule(options, callback) {
1605 this.factorizeQueue.add(options, callback);
1606 }
1607
1608 /**
1609 * @param {FactorizeModuleOptions} options options object
1610 * @param {ModuleCallback} callback callback
1611 * @returns {void}
1612 */
1613 _factorizeModule(
1614 {
1615 currentProfile,
1616 factory,
1617 dependencies,
1618 originModule,
1619 contextInfo,
1620 context
1621 },
1622 callback
1623 ) {
1624 if (currentProfile !== undefined) {
1625 currentProfile.markFactoryStart();
1626 }
1627 factory.create(
1628 {
1629 contextInfo: {
1630 issuer: originModule ? originModule.nameForCondition() : "",
1631 issuerLayer: originModule ? originModule.layer : null,
1632 compiler: this.compiler.name,
1633 ...contextInfo
1634 },
1635 resolveOptions: originModule ? originModule.resolveOptions : undefined,
1636 context: context
1637 ? context
1638 : originModule
1639 ? originModule.context
1640 : this.compiler.context,
1641 dependencies: dependencies
1642 },
1643 (err, result) => {
1644 if (result) {
1645 // TODO webpack 6: remove
1646 // For backward-compat
1647 if (result.module === undefined && result instanceof Module) {
1648 result = {
1649 module: result
1650 };
1651 }
1652 const {
1653 fileDependencies,
1654 contextDependencies,
1655 missingDependencies
1656 } = result;
1657 if (fileDependencies) {
1658 this.fileDependencies.addAll(fileDependencies);
1659 }
1660 if (contextDependencies) {
1661 this.contextDependencies.addAll(contextDependencies);
1662 }
1663 if (missingDependencies) {
1664 this.missingDependencies.addAll(missingDependencies);
1665 }
1666 }
1667 if (err) {
1668 const notFoundError = new ModuleNotFoundError(
1669 originModule,
1670 err,
1671 dependencies.map(d => d.loc).filter(Boolean)[0]
1672 );
1673 return callback(notFoundError);
1674 }
1675 if (!result) {
1676 return callback();
1677 }
1678 const newModule = result.module;
1679 if (!newModule) {
1680 return callback();
1681 }
1682 if (currentProfile !== undefined) {
1683 currentProfile.markFactoryEnd();
1684 }
1685
1686 callback(null, newModule);
1687 }
1688 );
1689 }
1690
1691 /**
1692 * @param {string} context context string path
1693 * @param {Dependency} dependency dependency used to create Module chain
1694 * @param {ModuleCallback} callback callback for when module chain is complete
1695 * @returns {void} will throw if dependency instance is not a valid Dependency
1696 */
1697 addModuleChain(context, dependency, callback) {
1698 return this.addModuleTree({ context, dependency }, callback);
1699 }
1700
1701 /**
1702 * @param {Object} options options
1703 * @param {string} options.context context string path
1704 * @param {Dependency} options.dependency dependency used to create Module chain
1705 * @param {Partial<ModuleFactoryCreateDataContextInfo>=} options.contextInfo additional context info for the root module
1706 * @param {ModuleCallback} callback callback for when module chain is complete
1707 * @returns {void} will throw if dependency instance is not a valid Dependency
1708 */
1709 addModuleTree({ context, dependency, contextInfo }, callback) {
1710 if (
1711 typeof dependency !== "object" ||
1712 dependency === null ||
1713 !dependency.constructor
1714 ) {
1715 return callback(
1716 new WebpackError("Parameter 'dependency' must be a Dependency")
1717 );
1718 }
1719 const Dep = /** @type {DepConstructor} */ (dependency.constructor);
1720 const moduleFactory = this.dependencyFactories.get(Dep);
1721 if (!moduleFactory) {
1722 return callback(
1723 new WebpackError(
1724 `No dependency factory available for this dependency type: ${dependency.constructor.name}`
1725 )
1726 );
1727 }
1728
1729 this.handleModuleCreation(
1730 {
1731 factory: moduleFactory,
1732 dependencies: [dependency],
1733 originModule: null,
1734 contextInfo,
1735 context
1736 },
1737 err => {
1738 if (err && this.bail) {
1739 callback(err);
1740 this.buildQueue.stop();
1741 this.rebuildQueue.stop();
1742 this.processDependenciesQueue.stop();
1743 this.factorizeQueue.stop();
1744 } else {
1745 callback();
1746 }
1747 }
1748 );
1749 }
1750
1751 /**
1752 * @param {string} context context path for entry
1753 * @param {Dependency} entry entry dependency that should be followed
1754 * @param {string | EntryOptions} optionsOrName options or deprecated name of entry
1755 * @param {ModuleCallback} callback callback function
1756 * @returns {void} returns
1757 */
1758 addEntry(context, entry, optionsOrName, callback) {
1759 // TODO webpack 6 remove
1760 const options =
1761 typeof optionsOrName === "object"
1762 ? optionsOrName
1763 : { name: optionsOrName };
1764
1765 this._addEntryItem(context, entry, "dependencies", options, callback);
1766 }
1767
1768 /**
1769 * @param {string} context context path for entry
1770 * @param {Dependency} dependency dependency that should be followed
1771 * @param {EntryOptions} options options
1772 * @param {ModuleCallback} callback callback function
1773 * @returns {void} returns
1774 */
1775 addInclude(context, dependency, options, callback) {
1776 this._addEntryItem(
1777 context,
1778 dependency,
1779 "includeDependencies",
1780 options,
1781 callback
1782 );
1783 }
1784
1785 /**
1786 * @param {string} context context path for entry
1787 * @param {Dependency} entry entry dependency that should be followed
1788 * @param {"dependencies" | "includeDependencies"} target type of entry
1789 * @param {EntryOptions} options options
1790 * @param {ModuleCallback} callback callback function
1791 * @returns {void} returns
1792 */
1793 _addEntryItem(context, entry, target, options, callback) {
1794 const { name } = options;
1795 let entryData =
1796 name !== undefined ? this.entries.get(name) : this.globalEntry;
1797 if (entryData === undefined) {
1798 entryData = {
1799 dependencies: [],
1800 includeDependencies: [],
1801 options: {
1802 name: undefined,
1803 ...options
1804 }
1805 };
1806 entryData[target].push(entry);
1807 this.entries.set(name, entryData);
1808 } else {
1809 entryData[target].push(entry);
1810 for (const key of Object.keys(options)) {
1811 if (options[key] === undefined) continue;
1812 if (entryData.options[key] === options[key]) continue;
1813 if (
1814 Array.isArray(entryData.options[key]) &&
1815 Array.isArray(options[key]) &&
1816 arrayEquals(entryData.options[key], options[key])
1817 ) {
1818 continue;
1819 }
1820 if (entryData.options[key] === undefined) {
1821 entryData.options[key] = options[key];
1822 } else {
1823 return callback(
1824 new WebpackError(
1825 `Conflicting entry option ${key} = ${entryData.options[key]} vs ${options[key]}`
1826 )
1827 );
1828 }
1829 }
1830 }
1831
1832 this.hooks.addEntry.call(entry, options);
1833
1834 this.addModuleTree(
1835 {
1836 context,
1837 dependency: entry,
1838 contextInfo: entryData.options.layer
1839 ? { issuerLayer: entryData.options.layer }
1840 : undefined
1841 },
1842 (err, module) => {
1843 if (err) {
1844 this.hooks.failedEntry.call(entry, options, err);
1845 return callback(err);
1846 }
1847 this.hooks.succeedEntry.call(entry, options, module);
1848 return callback(null, module);
1849 }
1850 );
1851 }
1852
1853 /**
1854 * @param {Module} module module to be rebuilt
1855 * @param {ModuleCallback} callback callback when module finishes rebuilding
1856 * @returns {void}
1857 */
1858 rebuildModule(module, callback) {
1859 this.rebuildQueue.add(module, callback);
1860 }
1861
1862 /**
1863 * @param {Module} module module to be rebuilt
1864 * @param {ModuleCallback} callback callback when module finishes rebuilding
1865 * @returns {void}
1866 */
1867 _rebuildModule(module, callback) {
1868 this.hooks.rebuildModule.call(module);
1869 const oldDependencies = module.dependencies.slice();
1870 const oldBlocks = module.blocks.slice();
1871 module.invalidateBuild();
1872 this.buildQueue.invalidate(module);
1873 this.buildModule(module, err => {
1874 if (err) {
1875 return this.hooks.finishRebuildingModule.callAsync(module, err2 => {
1876 if (err2) {
1877 callback(
1878 makeWebpackError(err2, "Compilation.hooks.finishRebuildingModule")
1879 );
1880 return;
1881 }
1882 callback(err);
1883 });
1884 }
1885
1886 this.processDependenciesQueue.invalidate(module);
1887 this.processModuleDependencies(module, err => {
1888 if (err) return callback(err);
1889 this.removeReasonsOfDependencyBlock(module, {
1890 dependencies: oldDependencies,
1891 blocks: oldBlocks
1892 });
1893 this.hooks.finishRebuildingModule.callAsync(module, err2 => {
1894 if (err2) {
1895 callback(
1896 makeWebpackError(err2, "Compilation.hooks.finishRebuildingModule")
1897 );
1898 return;
1899 }
1900 callback(null, module);
1901 });
1902 });
1903 });
1904 }
1905
1906 finish(callback) {
1907 if (this.profile) {
1908 this.logger.time("finish module profiles");
1909 const ParallelismFactorCalculator = require("./util/ParallelismFactorCalculator");
1910 const p = new ParallelismFactorCalculator();
1911 const moduleGraph = this.moduleGraph;
1912 const modulesWithProfiles = new Map();
1913 for (const module of this.modules) {
1914 const profile = moduleGraph.getProfile(module);
1915 if (!profile) continue;
1916 modulesWithProfiles.set(module, profile);
1917 p.range(
1918 profile.buildingStartTime,
1919 profile.buildingEndTime,
1920 f => (profile.buildingParallelismFactor = f)
1921 );
1922 p.range(
1923 profile.factoryStartTime,
1924 profile.factoryEndTime,
1925 f => (profile.factoryParallelismFactor = f)
1926 );
1927 p.range(
1928 profile.integrationStartTime,
1929 profile.integrationEndTime,
1930 f => (profile.integrationParallelismFactor = f)
1931 );
1932 p.range(
1933 profile.storingStartTime,
1934 profile.storingEndTime,
1935 f => (profile.storingParallelismFactor = f)
1936 );
1937 p.range(
1938 profile.restoringStartTime,
1939 profile.restoringEndTime,
1940 f => (profile.restoringParallelismFactor = f)
1941 );
1942 if (profile.additionalFactoryTimes) {
1943 for (const { start, end } of profile.additionalFactoryTimes) {
1944 const influence = (end - start) / profile.additionalFactories;
1945 p.range(
1946 start,
1947 end,
1948 f =>
1949 (profile.additionalFactoriesParallelismFactor += f * influence)
1950 );
1951 }
1952 }
1953 }
1954 p.calculate();
1955
1956 const logger = this.getLogger("webpack.Compilation.ModuleProfile");
1957 const logByValue = (value, msg) => {
1958 if (value > 1000) {
1959 logger.error(msg);
1960 } else if (value > 500) {
1961 logger.warn(msg);
1962 } else if (value > 200) {
1963 logger.info(msg);
1964 } else if (value > 30) {
1965 logger.log(msg);
1966 } else {
1967 logger.debug(msg);
1968 }
1969 };
1970 const logNormalSummary = (category, getDuration, getParallelism) => {
1971 let sum = 0;
1972 let max = 0;
1973 for (const [module, profile] of modulesWithProfiles) {
1974 const p = getParallelism(profile);
1975 const d = getDuration(profile);
1976 if (d === 0 || p === 0) continue;
1977 const t = d / p;
1978 sum += t;
1979 if (t <= 10) continue;
1980 logByValue(
1981 t,
1982 ` | ${Math.round(t)} ms${
1983 p >= 1.1 ? ` (parallelism ${Math.round(p * 10) / 10})` : ""
1984 } ${category} > ${module.readableIdentifier(this.requestShortener)}`
1985 );
1986 max = Math.max(max, t);
1987 }
1988 if (sum <= 10) return;
1989 logByValue(
1990 Math.max(sum / 10, max),
1991 `${Math.round(sum)} ms ${category}`
1992 );
1993 };
1994 const logByLoadersSummary = (category, getDuration, getParallelism) => {
1995 const map = new Map();
1996 for (const [module, profile] of modulesWithProfiles) {
1997 const list = provide(
1998 map,
1999 module.type + "!" + module.identifier().replace(/(!|^)[^!]*$/, ""),
2000 () => []
2001 );
2002 list.push({ module, profile });
2003 }
2004
2005 let sum = 0;
2006 let max = 0;
2007 for (const [key, modules] of map) {
2008 let innerSum = 0;
2009 let innerMax = 0;
2010 for (const { module, profile } of modules) {
2011 const p = getParallelism(profile);
2012 const d = getDuration(profile);
2013 if (d === 0 || p === 0) continue;
2014 const t = d / p;
2015 innerSum += t;
2016 if (t <= 10) continue;
2017 logByValue(
2018 t,
2019 ` | | ${Math.round(t)} ms${
2020 p >= 1.1 ? ` (parallelism ${Math.round(p * 10) / 10})` : ""
2021 } ${category} > ${module.readableIdentifier(
2022 this.requestShortener
2023 )}`
2024 );
2025 innerMax = Math.max(innerMax, t);
2026 }
2027 sum += innerSum;
2028 if (innerSum <= 10) continue;
2029 const idx = key.indexOf("!");
2030 const loaders = key.slice(idx + 1);
2031 const moduleType = key.slice(0, idx);
2032 const t = Math.max(innerSum / 10, innerMax);
2033 logByValue(
2034 t,
2035 ` | ${Math.round(innerSum)} ms ${category} > ${
2036 loaders
2037 ? `${
2038 modules.length
2039 } x ${moduleType} with ${this.requestShortener.shorten(
2040 loaders
2041 )}`
2042 : `${modules.length} x ${moduleType}`
2043 }`
2044 );
2045 max = Math.max(max, t);
2046 }
2047 if (sum <= 10) return;
2048 logByValue(
2049 Math.max(sum / 10, max),
2050 `${Math.round(sum)} ms ${category}`
2051 );
2052 };
2053 logNormalSummary(
2054 "resolve to new modules",
2055 p => p.factory,
2056 p => p.factoryParallelismFactor
2057 );
2058 logNormalSummary(
2059 "resolve to existing modules",
2060 p => p.additionalFactories,
2061 p => p.additionalFactoriesParallelismFactor
2062 );
2063 logNormalSummary(
2064 "integrate modules",
2065 p => p.restoring,
2066 p => p.restoringParallelismFactor
2067 );
2068 logByLoadersSummary(
2069 "build modules",
2070 p => p.building,
2071 p => p.buildingParallelismFactor
2072 );
2073 logNormalSummary(
2074 "store modules",
2075 p => p.storing,
2076 p => p.storingParallelismFactor
2077 );
2078 logNormalSummary(
2079 "restore modules",
2080 p => p.restoring,
2081 p => p.restoringParallelismFactor
2082 );
2083 this.logger.timeEnd("finish module profiles");
2084 }
2085 this.logger.time("finish modules");
2086 const { modules } = this;
2087 this.hooks.finishModules.callAsync(modules, err => {
2088 this.logger.timeEnd("finish modules");
2089 if (err) return callback(err);
2090
2091 // extract warnings and errors from modules
2092 this.logger.time("report dependency errors and warnings");
2093 for (const module of modules) {
2094 this.reportDependencyErrorsAndWarnings(module, [module]);
2095 const errors = module.getErrors();
2096 if (errors !== undefined) {
2097 for (const error of errors) {
2098 if (!error.module) {
2099 error.module = module;
2100 }
2101 this.errors.push(error);
2102 }
2103 }
2104 const warnings = module.getWarnings();
2105 if (warnings !== undefined) {
2106 for (const warning of warnings) {
2107 if (!warning.module) {
2108 warning.module = module;
2109 }
2110 this.warnings.push(warning);
2111 }
2112 }
2113 }
2114 this.logger.timeEnd("report dependency errors and warnings");
2115
2116 callback();
2117 });
2118 }
2119
2120 unseal() {
2121 this.hooks.unseal.call();
2122 this.chunks.clear();
2123 this.chunkGroups.length = 0;
2124 this.namedChunks.clear();
2125 this.namedChunkGroups.clear();
2126 this.entrypoints.clear();
2127 this.additionalChunkAssets.length = 0;
2128 this.assets = {};
2129 this.assetsInfo.clear();
2130 this.moduleGraph.removeAllModuleAttributes();
2131 }
2132
2133 /**
2134 * @param {Callback} callback signals when the call finishes
2135 * @returns {void}
2136 */
2137 seal(callback) {
2138 const chunkGraph = new ChunkGraph(this.moduleGraph);
2139 this.chunkGraph = chunkGraph;
2140
2141 for (const module of this.modules) {
2142 ChunkGraph.setChunkGraphForModule(module, chunkGraph);
2143 }
2144
2145 this.hooks.seal.call();
2146
2147 this.logger.time("optimize dependencies");
2148 while (this.hooks.optimizeDependencies.call(this.modules)) {
2149 /* empty */
2150 }
2151 this.hooks.afterOptimizeDependencies.call(this.modules);
2152 this.logger.timeEnd("optimize dependencies");
2153
2154 this.logger.time("create chunks");
2155 this.hooks.beforeChunks.call();
2156 /** @type {Map<Entrypoint, Module[]>} */
2157 const chunkGraphInit = new Map();
2158 for (const [name, { dependencies, includeDependencies, options }] of this
2159 .entries) {
2160 const chunk = this.addChunk(name);
2161 if (options.filename) {
2162 chunk.filenameTemplate = options.filename;
2163 }
2164 const entrypoint = new Entrypoint(options);
2165 if (!options.dependOn && !options.runtime) {
2166 entrypoint.setRuntimeChunk(chunk);
2167 }
2168 entrypoint.setEntrypointChunk(chunk);
2169 this.namedChunkGroups.set(name, entrypoint);
2170 this.entrypoints.set(name, entrypoint);
2171 this.chunkGroups.push(entrypoint);
2172 connectChunkGroupAndChunk(entrypoint, chunk);
2173
2174 for (const dep of [...this.globalEntry.dependencies, ...dependencies]) {
2175 entrypoint.addOrigin(null, { name }, /** @type {any} */ (dep).request);
2176
2177 const module = this.moduleGraph.getModule(dep);
2178 if (module) {
2179 chunkGraph.connectChunkAndEntryModule(chunk, module, entrypoint);
2180 this.assignDepth(module);
2181 const modulesList = chunkGraphInit.get(entrypoint);
2182 if (modulesList === undefined) {
2183 chunkGraphInit.set(entrypoint, [module]);
2184 } else {
2185 modulesList.push(module);
2186 }
2187 }
2188 }
2189
2190 const mapAndSort = deps =>
2191 deps
2192 .map(dep => this.moduleGraph.getModule(dep))
2193 .filter(Boolean)
2194 .sort(compareModulesByIdentifier);
2195 const includedModules = [
2196 ...mapAndSort(this.globalEntry.includeDependencies),
2197 ...mapAndSort(includeDependencies)
2198 ];
2199
2200 let modulesList = chunkGraphInit.get(entrypoint);
2201 if (modulesList === undefined) {
2202 chunkGraphInit.set(entrypoint, (modulesList = []));
2203 }
2204 for (const module of includedModules) {
2205 this.assignDepth(module);
2206 modulesList.push(module);
2207 }
2208 }
2209 const runtimeChunks = new Set();
2210 outer: for (const [
2211 name,
2212 {
2213 options: { dependOn, runtime }
2214 }
2215 ] of this.entries) {
2216 if (dependOn && runtime) {
2217 const err = new WebpackError(`Entrypoint '${name}' has 'dependOn' and 'runtime' specified. This is not valid.
2218Entrypoints that depend on other entrypoints do not have their own runtime.
2219They will use the runtime(s) from referenced entrypoints instead.
2220Remove the 'runtime' option from the entrypoint.`);
2221 const entry = this.entrypoints.get(name);
2222 err.chunk = entry.getEntrypointChunk();
2223 this.errors.push(err);
2224 }
2225 if (dependOn) {
2226 const entry = this.entrypoints.get(name);
2227 const referencedChunks = entry
2228 .getEntrypointChunk()
2229 .getAllReferencedChunks();
2230 const dependOnEntries = [];
2231 for (const dep of dependOn) {
2232 const dependency = this.entrypoints.get(dep);
2233 if (!dependency) {
2234 throw new Error(
2235 `Entry ${name} depends on ${dep}, but this entry was not found`
2236 );
2237 }
2238 if (referencedChunks.has(dependency.getEntrypointChunk())) {
2239 const err = new WebpackError(
2240 `Entrypoints '${name}' and '${dep}' use 'dependOn' to depend on each other in a circular way.`
2241 );
2242 const entryChunk = entry.getEntrypointChunk();
2243 err.chunk = entryChunk;
2244 this.errors.push(err);
2245 entry.setRuntimeChunk(entryChunk);
2246 continue outer;
2247 }
2248 dependOnEntries.push(dependency);
2249 }
2250 for (const dependency of dependOnEntries) {
2251 connectChunkGroupParentAndChild(dependency, entry);
2252 }
2253 } else if (runtime) {
2254 const entry = this.entrypoints.get(name);
2255 let chunk = this.namedChunks.get(runtime);
2256 if (chunk) {
2257 if (!runtimeChunks.has(chunk)) {
2258 const err = new WebpackError(`Entrypoint '${name}' has a 'runtime' option which points to another entrypoint named '${runtime}'.
2259It's not valid to use other entrypoints as runtime chunk.
2260Did you mean to use 'dependOn: ${JSON.stringify(
2261 runtime
2262 )}' instead to allow using entrypoint '${name}' within the runtime of entrypoint '${runtime}'? For this '${runtime}' must always be loaded when '${name}' is used.
2263Or do you want to use the entrypoints '${name}' and '${runtime}' independently on the same page with a shared runtime? In this case give them both the same value for the 'runtime' option. It must be a name not already used by an entrypoint.`);
2264 const entryChunk = entry.getEntrypointChunk();
2265 err.chunk = entryChunk;
2266 this.errors.push(err);
2267 entry.setRuntimeChunk(entryChunk);
2268 continue;
2269 }
2270 } else {
2271 chunk = this.addChunk(runtime);
2272 chunk.preventIntegration = true;
2273 runtimeChunks.add(chunk);
2274 }
2275 entry.unshiftChunk(chunk);
2276 chunk.addGroup(entry);
2277 entry.setRuntimeChunk(chunk);
2278 }
2279 }
2280 buildChunkGraph(this, chunkGraphInit);
2281 this.hooks.afterChunks.call(this.chunks);
2282 this.logger.timeEnd("create chunks");
2283
2284 this.logger.time("optimize");
2285 this.hooks.optimize.call();
2286
2287 while (this.hooks.optimizeModules.call(this.modules)) {
2288 /* empty */
2289 }
2290 this.hooks.afterOptimizeModules.call(this.modules);
2291
2292 while (this.hooks.optimizeChunks.call(this.chunks, this.chunkGroups)) {
2293 /* empty */
2294 }
2295 this.hooks.afterOptimizeChunks.call(this.chunks, this.chunkGroups);
2296
2297 this.hooks.optimizeTree.callAsync(this.chunks, this.modules, err => {
2298 if (err) {
2299 return callback(
2300 makeWebpackError(err, "Compilation.hooks.optimizeTree")
2301 );
2302 }
2303
2304 this.hooks.afterOptimizeTree.call(this.chunks, this.modules);
2305
2306 this.hooks.optimizeChunkModules.callAsync(
2307 this.chunks,
2308 this.modules,
2309 err => {
2310 if (err) {
2311 return callback(
2312 makeWebpackError(err, "Compilation.hooks.optimizeChunkModules")
2313 );
2314 }
2315
2316 this.hooks.afterOptimizeChunkModules.call(this.chunks, this.modules);
2317
2318 const shouldRecord = this.hooks.shouldRecord.call() !== false;
2319
2320 this.hooks.reviveModules.call(this.modules, this.records);
2321 this.hooks.beforeModuleIds.call(this.modules);
2322 this.hooks.moduleIds.call(this.modules);
2323 this.hooks.optimizeModuleIds.call(this.modules);
2324 this.hooks.afterOptimizeModuleIds.call(this.modules);
2325
2326 this.hooks.reviveChunks.call(this.chunks, this.records);
2327 this.hooks.beforeChunkIds.call(this.chunks);
2328 this.hooks.chunkIds.call(this.chunks);
2329 this.hooks.optimizeChunkIds.call(this.chunks);
2330 this.hooks.afterOptimizeChunkIds.call(this.chunks);
2331
2332 this.assignRuntimeIds();
2333
2334 this.sortItemsWithChunkIds();
2335
2336 if (shouldRecord) {
2337 this.hooks.recordModules.call(this.modules, this.records);
2338 this.hooks.recordChunks.call(this.chunks, this.records);
2339 }
2340
2341 this.hooks.optimizeCodeGeneration.call(this.modules);
2342 this.logger.timeEnd("optimize");
2343
2344 this.logger.time("module hashing");
2345 this.hooks.beforeModuleHash.call();
2346 this.createModuleHashes();
2347 this.hooks.afterModuleHash.call();
2348 this.logger.timeEnd("module hashing");
2349
2350 this.logger.time("code generation");
2351 this.hooks.beforeCodeGeneration.call();
2352 this.codeGeneration(err => {
2353 if (err) {
2354 return callback(err);
2355 }
2356 this.hooks.afterCodeGeneration.call();
2357 this.logger.timeEnd("code generation");
2358
2359 this.logger.time("runtime requirements");
2360 this.hooks.beforeRuntimeRequirements.call();
2361 this.processRuntimeRequirements();
2362 this.hooks.afterRuntimeRequirements.call();
2363 this.logger.timeEnd("runtime requirements");
2364
2365 this.logger.time("hashing");
2366 this.hooks.beforeHash.call();
2367 const codeGenerationJobs = this.createHash();
2368 this.hooks.afterHash.call();
2369 this.logger.timeEnd("hashing");
2370
2371 this._runCodeGenerationJobs(codeGenerationJobs, err => {
2372 if (err) {
2373 return callback(err);
2374 }
2375
2376 if (shouldRecord) {
2377 this.logger.time("record hash");
2378 this.hooks.recordHash.call(this.records);
2379 this.logger.timeEnd("record hash");
2380 }
2381
2382 this.logger.time("module assets");
2383 this.clearAssets();
2384
2385 this.hooks.beforeModuleAssets.call();
2386 this.createModuleAssets();
2387 this.logger.timeEnd("module assets");
2388
2389 const cont = () => {
2390 this.logger.time("process assets");
2391 this.hooks.processAssets.callAsync(this.assets, err => {
2392 if (err) {
2393 return callback(
2394 makeWebpackError(err, "Compilation.hooks.processAssets")
2395 );
2396 }
2397 this.hooks.afterProcessAssets.call(this.assets);
2398 this.logger.timeEnd("process assets");
2399 this.assets = soonFrozenObjectDeprecation(
2400 this.assets,
2401 "Compilation.assets",
2402 "DEP_WEBPACK_COMPILATION_ASSETS",
2403 `BREAKING CHANGE: No more changes should happen to Compilation.assets after sealing the Compilation.
2404 Do changes to assets earlier, e. g. in Compilation.hooks.processAssets.
2405 Make sure to select an appropriate stage from Compilation.PROCESS_ASSETS_STAGE_*.`
2406 );
2407
2408 this.summarizeDependencies();
2409 if (shouldRecord) {
2410 this.hooks.record.call(this, this.records);
2411 }
2412
2413 if (this.hooks.needAdditionalSeal.call()) {
2414 this.unseal();
2415 return this.seal(callback);
2416 }
2417 return this.hooks.afterSeal.callAsync(err => {
2418 if (err) {
2419 return callback(
2420 makeWebpackError(err, "Compilation.hooks.afterSeal")
2421 );
2422 }
2423 this.fileSystemInfo.logStatistics();
2424 callback();
2425 });
2426 });
2427 };
2428
2429 this.logger.time("create chunk assets");
2430 if (this.hooks.shouldGenerateChunkAssets.call() !== false) {
2431 this.hooks.beforeChunkAssets.call();
2432 this.createChunkAssets(err => {
2433 this.logger.timeEnd("create chunk assets");
2434 if (err) {
2435 return callback(err);
2436 }
2437 cont();
2438 });
2439 } else {
2440 this.logger.timeEnd("create chunk assets");
2441 cont();
2442 }
2443 });
2444 });
2445 }
2446 );
2447 });
2448 }
2449
2450 /**
2451 * @param {Module} module module to report from
2452 * @param {DependenciesBlock[]} blocks blocks to report from
2453 * @returns {void}
2454 */
2455 reportDependencyErrorsAndWarnings(module, blocks) {
2456 for (let indexBlock = 0; indexBlock < blocks.length; indexBlock++) {
2457 const block = blocks[indexBlock];
2458 const dependencies = block.dependencies;
2459
2460 for (let indexDep = 0; indexDep < dependencies.length; indexDep++) {
2461 const d = dependencies[indexDep];
2462
2463 const warnings = d.getWarnings(this.moduleGraph);
2464 if (warnings) {
2465 for (let indexWar = 0; indexWar < warnings.length; indexWar++) {
2466 const w = warnings[indexWar];
2467
2468 const warning = new ModuleDependencyWarning(module, w, d.loc);
2469 this.warnings.push(warning);
2470 }
2471 }
2472 const errors = d.getErrors(this.moduleGraph);
2473 if (errors) {
2474 for (let indexErr = 0; indexErr < errors.length; indexErr++) {
2475 const e = errors[indexErr];
2476
2477 const error = new ModuleDependencyError(module, e, d.loc);
2478 this.errors.push(error);
2479 }
2480 }
2481 }
2482
2483 this.reportDependencyErrorsAndWarnings(module, block.blocks);
2484 }
2485 }
2486
2487 codeGeneration(callback) {
2488 const { chunkGraph } = this;
2489 this.codeGenerationResults = new CodeGenerationResults();
2490 /** @type {{module: Module, hash: string, runtime: RuntimeSpec, runtimes: RuntimeSpec[]}[]} */
2491 const jobs = [];
2492 for (const module of this.modules) {
2493 const runtimes = chunkGraph.getModuleRuntimes(module);
2494 if (runtimes.size === 1) {
2495 for (const runtime of runtimes) {
2496 const hash = chunkGraph.getModuleHash(module, runtime);
2497 jobs.push({ module, hash, runtime, runtimes: [runtime] });
2498 }
2499 } else if (runtimes.size > 1) {
2500 /** @type {Map<string, { runtimes: RuntimeSpec[] }>} */
2501 const map = new Map();
2502 for (const runtime of runtimes) {
2503 const hash = chunkGraph.getModuleHash(module, runtime);
2504 const job = map.get(hash);
2505 if (job === undefined) {
2506 const newJob = { module, hash, runtime, runtimes: [runtime] };
2507 jobs.push(newJob);
2508 map.set(hash, newJob);
2509 } else {
2510 job.runtimes.push(runtime);
2511 }
2512 }
2513 }
2514 }
2515
2516 this._runCodeGenerationJobs(jobs, callback);
2517 }
2518
2519 _runCodeGenerationJobs(jobs, callback) {
2520 let statModulesFromCache = 0;
2521 let statModulesGenerated = 0;
2522 const {
2523 chunkGraph,
2524 moduleGraph,
2525 dependencyTemplates,
2526 runtimeTemplate
2527 } = this;
2528 const results = this.codeGenerationResults;
2529 const errors = [];
2530 asyncLib.eachLimit(
2531 jobs,
2532 this.options.parallelism,
2533 ({ module, hash, runtime, runtimes }, callback) => {
2534 this._codeGenerationModule(
2535 module,
2536 runtime,
2537 runtimes,
2538 hash,
2539 dependencyTemplates,
2540 chunkGraph,
2541 moduleGraph,
2542 runtimeTemplate,
2543 errors,
2544 results,
2545 (err, codeGenerated) => {
2546 if (codeGenerated) statModulesGenerated++;
2547 else statModulesFromCache++;
2548 callback(err);
2549 }
2550 );
2551 },
2552 err => {
2553 if (err) return callback(err);
2554 if (errors.length > 0) {
2555 errors.sort(
2556 compareSelect(err => err.module, compareModulesByIdentifier)
2557 );
2558 for (const error of errors) {
2559 this.errors.push(error);
2560 }
2561 }
2562 this.logger.log(
2563 `${Math.round(
2564 (100 * statModulesGenerated) /
2565 (statModulesGenerated + statModulesFromCache)
2566 )}% code generated (${statModulesGenerated} generated, ${statModulesFromCache} from cache)`
2567 );
2568 callback();
2569 }
2570 );
2571 }
2572
2573 /**
2574 * @param {Module} module module
2575 * @param {RuntimeSpec} runtime runtime
2576 * @param {RuntimeSpec[]} runtimes runtimes
2577 * @param {string} hash hash
2578 * @param {DependencyTemplates} dependencyTemplates dependencyTemplates
2579 * @param {ChunkGraph} chunkGraph chunkGraph
2580 * @param {ModuleGraph} moduleGraph moduleGraph
2581 * @param {RuntimeTemplate} runtimeTemplate runtimeTemplate
2582 * @param {WebpackError[]} errors errors
2583 * @param {CodeGenerationResults} results results
2584 * @param {function(WebpackError=, boolean=): void} callback callback
2585 */
2586 _codeGenerationModule(
2587 module,
2588 runtime,
2589 runtimes,
2590 hash,
2591 dependencyTemplates,
2592 chunkGraph,
2593 moduleGraph,
2594 runtimeTemplate,
2595 errors,
2596 results,
2597 callback
2598 ) {
2599 let codeGenerated = false;
2600 const cache = new MultiItemCache(
2601 runtimes.map(runtime =>
2602 this._codeGenerationCache.getItemCache(
2603 `${module.identifier()}|${getRuntimeKey(runtime)}`,
2604 `${hash}|${dependencyTemplates.getHash()}`
2605 )
2606 )
2607 );
2608 cache.get((err, cachedResult) => {
2609 if (err) return callback(err);
2610 let result;
2611 if (!cachedResult) {
2612 try {
2613 codeGenerated = true;
2614 this.codeGeneratedModules.add(module);
2615 result = module.codeGeneration({
2616 chunkGraph,
2617 moduleGraph,
2618 dependencyTemplates,
2619 runtimeTemplate,
2620 runtime
2621 });
2622 } catch (err) {
2623 errors.push(new CodeGenerationError(module, err));
2624 result = cachedResult = {
2625 sources: new Map(),
2626 runtimeRequirements: null
2627 };
2628 }
2629 } else {
2630 result = cachedResult;
2631 }
2632 for (const runtime of runtimes) {
2633 results.add(module, runtime, result);
2634 }
2635 if (!cachedResult) {
2636 cache.store(result, err => callback(err, codeGenerated));
2637 } else {
2638 callback(null, codeGenerated);
2639 }
2640 });
2641 }
2642
2643 /**
2644 * @returns {void}
2645 */
2646 processRuntimeRequirements() {
2647 const { chunkGraph } = this;
2648
2649 const additionalModuleRuntimeRequirements = this.hooks
2650 .additionalModuleRuntimeRequirements;
2651 const runtimeRequirementInModule = this.hooks.runtimeRequirementInModule;
2652 for (const module of this.modules) {
2653 if (chunkGraph.getNumberOfModuleChunks(module) > 0) {
2654 for (const runtime of chunkGraph.getModuleRuntimes(module)) {
2655 let set;
2656 const runtimeRequirements = this.codeGenerationResults.getRuntimeRequirements(
2657 module,
2658 runtime
2659 );
2660 if (runtimeRequirements && runtimeRequirements.size > 0) {
2661 set = new Set(runtimeRequirements);
2662 } else if (additionalModuleRuntimeRequirements.isUsed()) {
2663 set = new Set();
2664 } else {
2665 continue;
2666 }
2667 additionalModuleRuntimeRequirements.call(module, set);
2668
2669 for (const r of set) {
2670 const hook = runtimeRequirementInModule.get(r);
2671 if (hook !== undefined) hook.call(module, set);
2672 }
2673 chunkGraph.addModuleRuntimeRequirements(module, runtime, set);
2674 }
2675 }
2676 }
2677
2678 for (const chunk of this.chunks) {
2679 const set = new Set();
2680 for (const module of chunkGraph.getChunkModulesIterable(chunk)) {
2681 const runtimeRequirements = chunkGraph.getModuleRuntimeRequirements(
2682 module,
2683 chunk.runtime
2684 );
2685 for (const r of runtimeRequirements) set.add(r);
2686 }
2687 this.hooks.additionalChunkRuntimeRequirements.call(chunk, set);
2688
2689 for (const r of set) {
2690 this.hooks.runtimeRequirementInChunk.for(r).call(chunk, set);
2691 }
2692
2693 chunkGraph.addChunkRuntimeRequirements(chunk, set);
2694 }
2695
2696 /** @type {Set<Chunk>} */
2697 const treeEntries = new Set();
2698 for (const ep of this.entrypoints.values()) {
2699 const chunk = ep.getRuntimeChunk();
2700 if (chunk) treeEntries.add(chunk);
2701 }
2702 for (const ep of this.asyncEntrypoints) {
2703 const chunk = ep.getRuntimeChunk();
2704 if (chunk) treeEntries.add(chunk);
2705 }
2706
2707 for (const treeEntry of treeEntries) {
2708 const set = new Set();
2709 for (const chunk of treeEntry.getAllReferencedChunks()) {
2710 const runtimeRequirements = chunkGraph.getChunkRuntimeRequirements(
2711 chunk
2712 );
2713 for (const r of runtimeRequirements) set.add(r);
2714 }
2715
2716 this.hooks.additionalTreeRuntimeRequirements.call(treeEntry, set);
2717
2718 for (const r of set) {
2719 this.hooks.runtimeRequirementInTree.for(r).call(treeEntry, set);
2720 }
2721
2722 chunkGraph.addTreeRuntimeRequirements(treeEntry, set);
2723 }
2724 }
2725
2726 /**
2727 * @param {Chunk} chunk target chunk
2728 * @param {RuntimeModule} module runtime module
2729 * @returns {void}
2730 */
2731 addRuntimeModule(chunk, module) {
2732 // Deprecated ModuleGraph association
2733 ModuleGraph.setModuleGraphForModule(module, this.moduleGraph);
2734
2735 // add it to the list
2736 this.modules.add(module);
2737 this._modules.set(module.identifier(), module);
2738
2739 // connect to the chunk graph
2740 this.chunkGraph.connectChunkAndModule(chunk, module);
2741 this.chunkGraph.connectChunkAndRuntimeModule(chunk, module);
2742 if (module.fullHash) {
2743 this.chunkGraph.addFullHashModuleToChunk(chunk, module);
2744 }
2745
2746 // attach runtime module
2747 module.attach(this, chunk);
2748
2749 // Setup internals
2750 const exportsInfo = this.moduleGraph.getExportsInfo(module);
2751 exportsInfo.setHasProvideInfo();
2752 if (typeof chunk.runtime === "string") {
2753 exportsInfo.setUsedForSideEffectsOnly(chunk.runtime);
2754 } else if (chunk.runtime === undefined) {
2755 exportsInfo.setUsedForSideEffectsOnly(undefined);
2756 } else {
2757 for (const runtime of chunk.runtime) {
2758 exportsInfo.setUsedForSideEffectsOnly(runtime);
2759 }
2760 }
2761 this.chunkGraph.addModuleRuntimeRequirements(
2762 module,
2763 chunk.runtime,
2764 new Set([RuntimeGlobals.requireScope])
2765 );
2766
2767 // runtime modules don't need ids
2768 this.chunkGraph.setModuleId(module, "");
2769
2770 // Call hook
2771 this.hooks.runtimeModule.call(module, chunk);
2772 }
2773
2774 /**
2775 * @param {string | ChunkGroupOptions} groupOptions options for the chunk group
2776 * @param {Module} module the module the references the chunk group
2777 * @param {DependencyLocation} loc the location from with the chunk group is referenced (inside of module)
2778 * @param {string} request the request from which the the chunk group is referenced
2779 * @returns {ChunkGroup} the new or existing chunk group
2780 */
2781 addChunkInGroup(groupOptions, module, loc, request) {
2782 if (typeof groupOptions === "string") {
2783 groupOptions = { name: groupOptions };
2784 }
2785 const name = groupOptions.name;
2786
2787 if (name) {
2788 const chunkGroup = this.namedChunkGroups.get(name);
2789 if (chunkGroup !== undefined) {
2790 chunkGroup.addOptions(groupOptions);
2791 if (module) {
2792 chunkGroup.addOrigin(module, loc, request);
2793 }
2794 return chunkGroup;
2795 }
2796 }
2797 const chunkGroup = new ChunkGroup(groupOptions);
2798 if (module) chunkGroup.addOrigin(module, loc, request);
2799 const chunk = this.addChunk(name);
2800
2801 connectChunkGroupAndChunk(chunkGroup, chunk);
2802
2803 this.chunkGroups.push(chunkGroup);
2804 if (name) {
2805 this.namedChunkGroups.set(name, chunkGroup);
2806 }
2807 return chunkGroup;
2808 }
2809
2810 /**
2811 * @param {EntryOptions} options options for the entrypoint
2812 * @param {Module} module the module the references the chunk group
2813 * @param {DependencyLocation} loc the location from with the chunk group is referenced (inside of module)
2814 * @param {string} request the request from which the the chunk group is referenced
2815 * @returns {Entrypoint} the new or existing entrypoint
2816 */
2817 addAsyncEntrypoint(options, module, loc, request) {
2818 const name = options.name;
2819 if (name) {
2820 const entrypoint = this.namedChunkGroups.get(name);
2821 if (entrypoint instanceof Entrypoint) {
2822 if (entrypoint !== undefined) {
2823 if (module) {
2824 entrypoint.addOrigin(module, loc, request);
2825 }
2826 return entrypoint;
2827 }
2828 } else if (entrypoint) {
2829 throw new Error(
2830 `Cannot add an async entrypoint with the name '${name}', because there is already an chunk group with this name`
2831 );
2832 }
2833 }
2834 const chunk = this.addChunk(name);
2835 if (options.filename) {
2836 chunk.filenameTemplate = options.filename;
2837 }
2838 const entrypoint = new Entrypoint(options, false);
2839 entrypoint.setRuntimeChunk(chunk);
2840 entrypoint.setEntrypointChunk(chunk);
2841 if (name) {
2842 this.namedChunkGroups.set(name, entrypoint);
2843 }
2844 this.chunkGroups.push(entrypoint);
2845 this.asyncEntrypoints.push(entrypoint);
2846 connectChunkGroupAndChunk(entrypoint, chunk);
2847 if (module) {
2848 entrypoint.addOrigin(module, loc, request);
2849 }
2850 return entrypoint;
2851 }
2852
2853 /**
2854 * This method first looks to see if a name is provided for a new chunk,
2855 * and first looks to see if any named chunks already exist and reuse that chunk instead.
2856 *
2857 * @param {string=} name optional chunk name to be provided
2858 * @returns {Chunk} create a chunk (invoked during seal event)
2859 */
2860 addChunk(name) {
2861 if (name) {
2862 const chunk = this.namedChunks.get(name);
2863 if (chunk !== undefined) {
2864 return chunk;
2865 }
2866 }
2867 const chunk = new Chunk(name);
2868 this.chunks.add(chunk);
2869 ChunkGraph.setChunkGraphForChunk(chunk, this.chunkGraph);
2870 if (name) {
2871 this.namedChunks.set(name, chunk);
2872 }
2873 return chunk;
2874 }
2875
2876 /**
2877 * @param {Module} module module to assign depth
2878 * @returns {void}
2879 */
2880 assignDepth(module) {
2881 const moduleGraph = this.moduleGraph;
2882
2883 const queue = new Set([module]);
2884 let depth;
2885
2886 moduleGraph.setDepth(module, 0);
2887
2888 /**
2889 * @param {Module} module module for processing
2890 * @returns {void}
2891 */
2892 const processModule = module => {
2893 if (!moduleGraph.setDepthIfLower(module, depth)) return;
2894 queue.add(module);
2895 };
2896
2897 for (module of queue) {
2898 queue.delete(module);
2899 depth = moduleGraph.getDepth(module) + 1;
2900
2901 for (const connection of moduleGraph.getOutgoingConnections(module)) {
2902 const refModule = connection.module;
2903 if (refModule) {
2904 processModule(refModule);
2905 }
2906 }
2907 }
2908 }
2909
2910 /**
2911 * @param {Dependency} dependency the dependency
2912 * @param {RuntimeSpec} runtime the runtime
2913 * @returns {(string[] | ReferencedExport)[]} referenced exports
2914 */
2915 getDependencyReferencedExports(dependency, runtime) {
2916 const referencedExports = dependency.getReferencedExports(
2917 this.moduleGraph,
2918 runtime
2919 );
2920 return this.hooks.dependencyReferencedExports.call(
2921 referencedExports,
2922 dependency,
2923 runtime
2924 );
2925 }
2926
2927 /**
2928 *
2929 * @param {Module} module module relationship for removal
2930 * @param {DependenciesBlockLike} block //TODO: good description
2931 * @returns {void}
2932 */
2933 removeReasonsOfDependencyBlock(module, block) {
2934 if (block.blocks) {
2935 for (const b of block.blocks) {
2936 this.removeReasonsOfDependencyBlock(module, b);
2937 }
2938 }
2939
2940 if (block.dependencies) {
2941 for (const dep of block.dependencies) {
2942 const originalModule = this.moduleGraph.getModule(dep);
2943 if (originalModule) {
2944 this.moduleGraph.removeConnection(dep);
2945
2946 if (this.chunkGraph) {
2947 for (const chunk of this.chunkGraph.getModuleChunks(
2948 originalModule
2949 )) {
2950 this.patchChunksAfterReasonRemoval(originalModule, chunk);
2951 }
2952 }
2953 }
2954 }
2955 }
2956 }
2957
2958 /**
2959 * @param {Module} module module to patch tie
2960 * @param {Chunk} chunk chunk to patch tie
2961 * @returns {void}
2962 */
2963 patchChunksAfterReasonRemoval(module, chunk) {
2964 if (!module.hasReasons(this.moduleGraph, chunk.runtime)) {
2965 this.removeReasonsOfDependencyBlock(module, module);
2966 }
2967 if (!module.hasReasonForChunk(chunk, this.moduleGraph, this.chunkGraph)) {
2968 if (this.chunkGraph.isModuleInChunk(module, chunk)) {
2969 this.chunkGraph.disconnectChunkAndModule(chunk, module);
2970 this.removeChunkFromDependencies(module, chunk);
2971 }
2972 }
2973 }
2974
2975 /**
2976 *
2977 * @param {DependenciesBlock} block block tie for Chunk
2978 * @param {Chunk} chunk chunk to remove from dep
2979 * @returns {void}
2980 */
2981 removeChunkFromDependencies(block, chunk) {
2982 /**
2983 * @param {Dependency} d dependency to (maybe) patch up
2984 */
2985 const iteratorDependency = d => {
2986 const depModule = this.moduleGraph.getModule(d);
2987 if (!depModule) {
2988 return;
2989 }
2990 this.patchChunksAfterReasonRemoval(depModule, chunk);
2991 };
2992
2993 const blocks = block.blocks;
2994 for (let indexBlock = 0; indexBlock < blocks.length; indexBlock++) {
2995 const asyncBlock = blocks[indexBlock];
2996 const chunkGroup = this.chunkGraph.getBlockChunkGroup(asyncBlock);
2997 // Grab all chunks from the first Block's AsyncDepBlock
2998 const chunks = chunkGroup.chunks;
2999 // For each chunk in chunkGroup
3000 for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
3001 const iteratedChunk = chunks[indexChunk];
3002 chunkGroup.removeChunk(iteratedChunk);
3003 // Recurse
3004 this.removeChunkFromDependencies(block, iteratedChunk);
3005 }
3006 }
3007
3008 if (block.dependencies) {
3009 for (const dep of block.dependencies) iteratorDependency(dep);
3010 }
3011 }
3012
3013 assignRuntimeIds() {
3014 const { chunkGraph } = this;
3015 const processEntrypoint = ep => {
3016 const runtime = ep.options.runtime || ep.name;
3017 const chunk = ep.getRuntimeChunk();
3018 chunkGraph.setRuntimeId(runtime, chunk.id);
3019 };
3020 for (const ep of this.entrypoints.values()) {
3021 processEntrypoint(ep);
3022 }
3023 for (const ep of this.asyncEntrypoints) {
3024 processEntrypoint(ep);
3025 }
3026 }
3027
3028 sortItemsWithChunkIds() {
3029 for (const chunkGroup of this.chunkGroups) {
3030 chunkGroup.sortItems();
3031 }
3032
3033 this.errors.sort(compareErrors);
3034 this.warnings.sort(compareErrors);
3035 this.children.sort(byNameOrHash);
3036 }
3037
3038 summarizeDependencies() {
3039 for (
3040 let indexChildren = 0;
3041 indexChildren < this.children.length;
3042 indexChildren++
3043 ) {
3044 const child = this.children[indexChildren];
3045
3046 this.fileDependencies.addAll(child.fileDependencies);
3047 this.contextDependencies.addAll(child.contextDependencies);
3048 this.missingDependencies.addAll(child.missingDependencies);
3049 this.buildDependencies.addAll(child.buildDependencies);
3050 }
3051
3052 for (const module of this.modules) {
3053 module.addCacheDependencies(
3054 this.fileDependencies,
3055 this.contextDependencies,
3056 this.missingDependencies,
3057 this.buildDependencies
3058 );
3059 }
3060 }
3061
3062 createModuleHashes() {
3063 let statModulesHashed = 0;
3064 const { chunkGraph, runtimeTemplate } = this;
3065 const { hashFunction, hashDigest, hashDigestLength } = this.outputOptions;
3066 for (const module of this.modules) {
3067 for (const runtime of chunkGraph.getModuleRuntimes(module)) {
3068 statModulesHashed++;
3069 this._createModuleHash(
3070 module,
3071 chunkGraph,
3072 runtime,
3073 hashFunction,
3074 runtimeTemplate,
3075 hashDigest,
3076 hashDigestLength
3077 );
3078 }
3079 }
3080 this.logger.log(
3081 `${statModulesHashed} modules hashed (${
3082 Math.round((100 * statModulesHashed) / this.modules.size) / 100
3083 } variants per module in average)`
3084 );
3085 }
3086
3087 _createModuleHash(
3088 module,
3089 chunkGraph,
3090 runtime,
3091 hashFunction,
3092 runtimeTemplate,
3093 hashDigest,
3094 hashDigestLength
3095 ) {
3096 const moduleHash = createHash(hashFunction);
3097 module.updateHash(moduleHash, {
3098 chunkGraph,
3099 runtime,
3100 runtimeTemplate
3101 });
3102 const moduleHashDigest = /** @type {string} */ (moduleHash.digest(
3103 hashDigest
3104 ));
3105 chunkGraph.setModuleHashes(
3106 module,
3107 runtime,
3108 moduleHashDigest,
3109 moduleHashDigest.substr(0, hashDigestLength)
3110 );
3111 return moduleHashDigest;
3112 }
3113
3114 createHash() {
3115 this.logger.time("hashing: initialize hash");
3116 const chunkGraph = this.chunkGraph;
3117 const runtimeTemplate = this.runtimeTemplate;
3118 const outputOptions = this.outputOptions;
3119 const hashFunction = outputOptions.hashFunction;
3120 const hashDigest = outputOptions.hashDigest;
3121 const hashDigestLength = outputOptions.hashDigestLength;
3122 const hash = createHash(hashFunction);
3123 if (outputOptions.hashSalt) {
3124 hash.update(outputOptions.hashSalt);
3125 }
3126 this.logger.timeEnd("hashing: initialize hash");
3127 if (this.children.length > 0) {
3128 this.logger.time("hashing: hash child compilations");
3129 for (const child of this.children) {
3130 hash.update(child.hash);
3131 }
3132 this.logger.timeEnd("hashing: hash child compilations");
3133 }
3134 if (this.warnings.length > 0) {
3135 this.logger.time("hashing: hash warnings");
3136 for (const warning of this.warnings) {
3137 hash.update(`${warning.message}`);
3138 }
3139 this.logger.timeEnd("hashing: hash warnings");
3140 }
3141 if (this.errors.length > 0) {
3142 this.logger.time("hashing: hash errors");
3143 for (const error of this.errors) {
3144 hash.update(`${error.message}`);
3145 }
3146 this.logger.timeEnd("hashing: hash errors");
3147 }
3148
3149 this.logger.time("hashing: sort chunks");
3150 /*
3151 * all non-runtime chunks need to be hashes first,
3152 * since runtime chunk might use their hashes.
3153 * runtime chunks need to be hashed in the correct order
3154 * since they may depend on each other (for async entrypoints).
3155 * So we put all non-runtime chunks first and hash them in any order.
3156 * And order runtime chunks according to referenced between each other.
3157 * Chunks need to be in deterministic order since we add hashes to full chunk
3158 * during these hashing.
3159 */
3160 /** @type {Chunk[]} */
3161 const unorderedRuntimeChunks = [];
3162 /** @type {Chunk[]} */
3163 const otherChunks = [];
3164 for (const c of this.chunks) {
3165 if (c.hasRuntime()) {
3166 unorderedRuntimeChunks.push(c);
3167 } else {
3168 otherChunks.push(c);
3169 }
3170 }
3171 unorderedRuntimeChunks.sort(byId);
3172 otherChunks.sort(byId);
3173
3174 /** @typedef {{ chunk: Chunk, referencedBy: RuntimeChunkInfo[], remaining: number }} RuntimeChunkInfo */
3175 /** @type {Map<Chunk, RuntimeChunkInfo>} */
3176 const runtimeChunksMap = new Map();
3177 for (const chunk of unorderedRuntimeChunks) {
3178 runtimeChunksMap.set(chunk, {
3179 chunk,
3180 referencedBy: [],
3181 remaining: 0
3182 });
3183 }
3184 let remaining = 0;
3185 for (const info of runtimeChunksMap.values()) {
3186 for (const other of new Set(
3187 Array.from(info.chunk.getAllReferencedAsyncEntrypoints()).map(
3188 e => e.chunks[e.chunks.length - 1]
3189 )
3190 )) {
3191 const otherInfo = runtimeChunksMap.get(other);
3192 otherInfo.referencedBy.push(info);
3193 info.remaining++;
3194 remaining++;
3195 }
3196 }
3197 /** @type {Chunk[]} */
3198 const runtimeChunks = [];
3199 for (const info of runtimeChunksMap.values()) {
3200 if (info.remaining === 0) {
3201 runtimeChunks.push(info.chunk);
3202 }
3203 }
3204 // If there are any references between chunks
3205 // make sure to follow these chains
3206 if (remaining > 0) {
3207 const readyChunks = [];
3208 for (const chunk of runtimeChunks) {
3209 const info = runtimeChunksMap.get(chunk);
3210 for (const otherInfo of info.referencedBy) {
3211 remaining--;
3212 if (--otherInfo.remaining === 0) {
3213 readyChunks.push(otherInfo.chunk);
3214 }
3215 }
3216 if (readyChunks.length > 0) {
3217 // This ensures deterministic ordering, since referencedBy is non-deterministic
3218 readyChunks.sort(byId);
3219 for (const c of readyChunks) runtimeChunks.push(c);
3220 readyChunks.length = 0;
3221 }
3222 }
3223 }
3224 // If there are still remaining references we have cycles and want to create a warning
3225 if (remaining > 0) {
3226 let circularRuntimeChunkInfo = [];
3227 for (const info of runtimeChunksMap.values()) {
3228 if (info.remaining !== 0) {
3229 circularRuntimeChunkInfo.push(info);
3230 }
3231 }
3232 circularRuntimeChunkInfo.sort(compareSelect(i => i.chunk, byId));
3233 const err = new WebpackError(`Circular dependency between chunks with runtime (${Array.from(
3234 circularRuntimeChunkInfo,
3235 c => c.chunk.name || c.chunk.id
3236 ).join(", ")})
3237This prevents using hashes of each other and should be avoided.`);
3238 err.chunk = circularRuntimeChunkInfo[0].chunk;
3239 this.warnings.push(err);
3240 for (const i of circularRuntimeChunkInfo) runtimeChunks.push(i.chunk);
3241 }
3242 this.logger.timeEnd("hashing: sort chunks");
3243
3244 const fullHashChunks = new Set();
3245 /** @type {{module: Module, hash: string, runtime: RuntimeSpec, runtimes: RuntimeSpec[]}[]} */
3246 const codeGenerationJobs = [];
3247 /** @type {Map<string, Map<Module, {module: Module, hash: string, runtime: RuntimeSpec, runtimes: RuntimeSpec[]}>>} */
3248 const codeGenerationJobsMap = new Map();
3249
3250 const processChunk = chunk => {
3251 // Last minute module hash generation for modules that depend on chunk hashes
3252 this.logger.time("hashing: hash runtime modules");
3253 const runtime = chunk.runtime;
3254 for (const module of chunkGraph.getChunkModulesIterable(chunk)) {
3255 if (!chunkGraph.hasModuleHashes(module, runtime)) {
3256 const hash = this._createModuleHash(
3257 module,
3258 chunkGraph,
3259 runtime,
3260 hashFunction,
3261 runtimeTemplate,
3262 hashDigest,
3263 hashDigestLength
3264 );
3265 let hashMap = codeGenerationJobsMap.get(hash);
3266 if (hashMap) {
3267 const moduleJob = hashMap.get(module);
3268 if (moduleJob) {
3269 moduleJob.runtimes.push(runtime);
3270 continue;
3271 }
3272 } else {
3273 hashMap = new Map();
3274 codeGenerationJobsMap.set(hash, hashMap);
3275 }
3276 const job = {
3277 module,
3278 hash,
3279 runtime,
3280 runtimes: [runtime]
3281 };
3282 hashMap.set(module, job);
3283 codeGenerationJobs.push(job);
3284 }
3285 }
3286 this.logger.timeAggregate("hashing: hash runtime modules");
3287 this.logger.time("hashing: hash chunks");
3288 const chunkHash = createHash(hashFunction);
3289 try {
3290 if (outputOptions.hashSalt) {
3291 chunkHash.update(outputOptions.hashSalt);
3292 }
3293 chunk.updateHash(chunkHash, chunkGraph);
3294 this.hooks.chunkHash.call(chunk, chunkHash, {
3295 chunkGraph,
3296 moduleGraph: this.moduleGraph,
3297 runtimeTemplate: this.runtimeTemplate
3298 });
3299 const chunkHashDigest = /** @type {string} */ (chunkHash.digest(
3300 hashDigest
3301 ));
3302 hash.update(chunkHashDigest);
3303 chunk.hash = chunkHashDigest;
3304 chunk.renderedHash = chunk.hash.substr(0, hashDigestLength);
3305 const fullHashModules = chunkGraph.getChunkFullHashModulesIterable(
3306 chunk
3307 );
3308 if (fullHashModules) {
3309 fullHashChunks.add(chunk);
3310 } else {
3311 this.hooks.contentHash.call(chunk);
3312 }
3313 } catch (err) {
3314 this.errors.push(new ChunkRenderError(chunk, "", err));
3315 }
3316 this.logger.timeAggregate("hashing: hash chunks");
3317 };
3318 otherChunks.forEach(processChunk);
3319 for (const chunk of runtimeChunks) processChunk(chunk);
3320
3321 this.logger.timeAggregateEnd("hashing: hash runtime modules");
3322 this.logger.timeAggregateEnd("hashing: hash chunks");
3323 this.logger.time("hashing: hash digest");
3324 this.hooks.fullHash.call(hash);
3325 this.fullHash = /** @type {string} */ (hash.digest(hashDigest));
3326 this.hash = this.fullHash.substr(0, hashDigestLength);
3327 this.logger.timeEnd("hashing: hash digest");
3328
3329 this.logger.time("hashing: process full hash modules");
3330 for (const chunk of fullHashChunks) {
3331 for (const module of chunkGraph.getChunkFullHashModulesIterable(chunk)) {
3332 const moduleHash = createHash(hashFunction);
3333 module.updateHash(moduleHash, {
3334 chunkGraph,
3335 runtime: chunk.runtime,
3336 runtimeTemplate
3337 });
3338 const moduleHashDigest = /** @type {string} */ (moduleHash.digest(
3339 hashDigest
3340 ));
3341 const oldHash = chunkGraph.getModuleHash(module, chunk.runtime);
3342 chunkGraph.setModuleHashes(
3343 module,
3344 chunk.runtime,
3345 moduleHashDigest,
3346 moduleHashDigest.substr(0, hashDigestLength)
3347 );
3348 codeGenerationJobsMap.get(oldHash).get(module).hash = moduleHashDigest;
3349 }
3350 const chunkHash = createHash(hashFunction);
3351 chunkHash.update(chunk.hash);
3352 chunkHash.update(this.hash);
3353 const chunkHashDigest = /** @type {string} */ (chunkHash.digest(
3354 hashDigest
3355 ));
3356 chunk.hash = chunkHashDigest;
3357 chunk.renderedHash = chunk.hash.substr(0, hashDigestLength);
3358 this.hooks.contentHash.call(chunk);
3359 }
3360 this.logger.timeEnd("hashing: process full hash modules");
3361 return codeGenerationJobs;
3362 }
3363
3364 /**
3365 * @param {string} file file name
3366 * @param {Source} source asset source
3367 * @param {AssetInfo} assetInfo extra asset information
3368 * @returns {void}
3369 */
3370 emitAsset(file, source, assetInfo = {}) {
3371 if (this.assets[file]) {
3372 if (!isSourceEqual(this.assets[file], source)) {
3373 this.errors.push(
3374 new WebpackError(
3375 `Conflict: Multiple assets emit different content to the same filename ${file}`
3376 )
3377 );
3378 this.assets[file] = source;
3379 this._setAssetInfo(file, assetInfo);
3380 return;
3381 }
3382 const oldInfo = this.assetsInfo.get(file);
3383 const newInfo = Object.assign({}, oldInfo, assetInfo);
3384 this._setAssetInfo(file, newInfo, oldInfo);
3385 return;
3386 }
3387 this.assets[file] = source;
3388 this._setAssetInfo(file, assetInfo, undefined);
3389 }
3390
3391 _setAssetInfo(file, newInfo, oldInfo = this.assetsInfo.get(file)) {
3392 if (newInfo === undefined) {
3393 this.assetsInfo.delete(file);
3394 } else {
3395 this.assetsInfo.set(file, newInfo);
3396 }
3397 const oldRelated = oldInfo && oldInfo.related;
3398 const newRelated = newInfo && newInfo.related;
3399 if (oldRelated) {
3400 for (const key of Object.keys(oldRelated)) {
3401 const remove = name => {
3402 const relatedIn = this._assetsRelatedIn.get(name);
3403 if (relatedIn === undefined) return;
3404 const entry = relatedIn.get(key);
3405 if (entry === undefined) return;
3406 entry.delete(file);
3407 if (entry.size !== 0) return;
3408 relatedIn.delete(key);
3409 if (relatedIn.size === 0) this._assetsRelatedIn.delete(name);
3410 };
3411 const entry = oldRelated[key];
3412 if (Array.isArray(entry)) {
3413 entry.forEach(remove);
3414 } else if (entry) {
3415 remove(entry);
3416 }
3417 }
3418 }
3419 if (newRelated) {
3420 for (const key of Object.keys(newRelated)) {
3421 const add = name => {
3422 let relatedIn = this._assetsRelatedIn.get(name);
3423 if (relatedIn === undefined) {
3424 this._assetsRelatedIn.set(name, (relatedIn = new Map()));
3425 }
3426 let entry = relatedIn.get(key);
3427 if (entry === undefined) {
3428 relatedIn.set(key, (entry = new Set()));
3429 }
3430 entry.add(file);
3431 };
3432 const entry = newRelated[key];
3433 if (Array.isArray(entry)) {
3434 entry.forEach(add);
3435 } else if (entry) {
3436 add(entry);
3437 }
3438 }
3439 }
3440 }
3441
3442 /**
3443 * @param {string} file file name
3444 * @param {Source | function(Source): Source} newSourceOrFunction new asset source or function converting old to new
3445 * @param {AssetInfo | function(AssetInfo | undefined): AssetInfo} assetInfoUpdateOrFunction new asset info or function converting old to new
3446 */
3447 updateAsset(
3448 file,
3449 newSourceOrFunction,
3450 assetInfoUpdateOrFunction = undefined
3451 ) {
3452 if (!this.assets[file]) {
3453 throw new Error(
3454 `Called Compilation.updateAsset for not existing filename ${file}`
3455 );
3456 }
3457 if (typeof newSourceOrFunction === "function") {
3458 this.assets[file] = newSourceOrFunction(this.assets[file]);
3459 } else {
3460 this.assets[file] = newSourceOrFunction;
3461 }
3462 if (assetInfoUpdateOrFunction !== undefined) {
3463 const oldInfo = this.assetsInfo.get(file) || EMPTY_ASSET_INFO;
3464 if (typeof assetInfoUpdateOrFunction === "function") {
3465 this._setAssetInfo(file, assetInfoUpdateOrFunction(oldInfo), oldInfo);
3466 } else {
3467 this._setAssetInfo(
3468 file,
3469 cachedCleverMerge(oldInfo, assetInfoUpdateOrFunction),
3470 oldInfo
3471 );
3472 }
3473 }
3474 }
3475
3476 renameAsset(file, newFile) {
3477 const source = this.assets[file];
3478 if (!source) {
3479 throw new Error(
3480 `Called Compilation.renameAsset for not existing filename ${file}`
3481 );
3482 }
3483 if (this.assets[newFile]) {
3484 if (!isSourceEqual(this.assets[file], source)) {
3485 this.errors.push(
3486 new WebpackError(
3487 `Conflict: Called Compilation.renameAsset for already existing filename ${newFile} with different content`
3488 )
3489 );
3490 }
3491 }
3492 const assetInfo = this.assetsInfo.get(file);
3493 // Update related in all other assets
3494 const relatedInInfo = this._assetsRelatedIn.get(file);
3495 if (relatedInInfo) {
3496 for (const [key, assets] of relatedInInfo) {
3497 for (const name of assets) {
3498 const info = this.assetsInfo.get(name);
3499 if (!info) continue;
3500 const related = info.related;
3501 if (!related) continue;
3502 const entry = related[key];
3503 let newEntry;
3504 if (Array.isArray(entry)) {
3505 newEntry = entry.map(x => (x === file ? newFile : x));
3506 } else if (entry === file) {
3507 newEntry = newFile;
3508 } else continue;
3509 this.assetsInfo.set(name, {
3510 ...info,
3511 related: {
3512 ...related,
3513 [key]: newEntry
3514 }
3515 });
3516 }
3517 }
3518 }
3519 this._setAssetInfo(file, undefined, assetInfo);
3520 this._setAssetInfo(newFile, assetInfo);
3521 delete this.assets[file];
3522 this.assets[newFile] = source;
3523 for (const chunk of this.chunks) {
3524 {
3525 const size = chunk.files.size;
3526 chunk.files.delete(file);
3527 if (size !== chunk.files.size) {
3528 chunk.files.add(newFile);
3529 }
3530 }
3531 {
3532 const size = chunk.auxiliaryFiles.size;
3533 chunk.auxiliaryFiles.delete(file);
3534 if (size !== chunk.auxiliaryFiles.size) {
3535 chunk.auxiliaryFiles.add(newFile);
3536 }
3537 }
3538 }
3539 }
3540
3541 /**
3542 * @param {string} file file name
3543 */
3544 deleteAsset(file) {
3545 if (!this.assets[file]) {
3546 return;
3547 }
3548 delete this.assets[file];
3549 const assetInfo = this.assetsInfo.get(file);
3550 this._setAssetInfo(file, undefined, assetInfo);
3551 const related = assetInfo && assetInfo.related;
3552 if (related) {
3553 for (const key of Object.keys(related)) {
3554 const checkUsedAndDelete = file => {
3555 if (!this._assetsRelatedIn.has(file)) {
3556 this.deleteAsset(file);
3557 }
3558 };
3559 const items = related[key];
3560 if (Array.isArray(items)) {
3561 items.forEach(checkUsedAndDelete);
3562 } else if (items) {
3563 checkUsedAndDelete(items);
3564 }
3565 }
3566 }
3567 // TODO If this becomes a performance problem
3568 // store a reverse mapping from asset to chunk
3569 for (const chunk of this.chunks) {
3570 chunk.files.delete(file);
3571 chunk.auxiliaryFiles.delete(file);
3572 }
3573 }
3574
3575 getAssets() {
3576 /** @type {Readonly<Asset>[]} */
3577 const array = [];
3578 for (const assetName of Object.keys(this.assets)) {
3579 if (Object.prototype.hasOwnProperty.call(this.assets, assetName)) {
3580 array.push({
3581 name: assetName,
3582 source: this.assets[assetName],
3583 info: this.assetsInfo.get(assetName) || EMPTY_ASSET_INFO
3584 });
3585 }
3586 }
3587 return array;
3588 }
3589
3590 /**
3591 * @param {string} name the name of the asset
3592 * @returns {Readonly<Asset> | undefined} the asset or undefined when not found
3593 */
3594 getAsset(name) {
3595 if (!Object.prototype.hasOwnProperty.call(this.assets, name))
3596 return undefined;
3597 return {
3598 name,
3599 source: this.assets[name],
3600 info: this.assetsInfo.get(name) || EMPTY_ASSET_INFO
3601 };
3602 }
3603
3604 clearAssets() {
3605 for (const chunk of this.chunks) {
3606 chunk.files.clear();
3607 chunk.auxiliaryFiles.clear();
3608 }
3609 }
3610
3611 createModuleAssets() {
3612 const { chunkGraph } = this;
3613 for (const module of this.modules) {
3614 if (module.buildInfo.assets) {
3615 const assetsInfo = module.buildInfo.assetsInfo;
3616 for (const assetName of Object.keys(module.buildInfo.assets)) {
3617 const fileName = this.getPath(assetName, {
3618 chunkGraph: this.chunkGraph,
3619 module
3620 });
3621 for (const chunk of chunkGraph.getModuleChunksIterable(module)) {
3622 chunk.auxiliaryFiles.add(fileName);
3623 }
3624 this.emitAsset(
3625 fileName,
3626 module.buildInfo.assets[assetName],
3627 assetsInfo ? assetsInfo.get(assetName) : undefined
3628 );
3629 this.hooks.moduleAsset.call(module, fileName);
3630 }
3631 }
3632 }
3633 }
3634
3635 /**
3636 * @param {RenderManifestOptions} options options object
3637 * @returns {RenderManifestEntry[]} manifest entries
3638 */
3639 getRenderManifest(options) {
3640 return this.hooks.renderManifest.call([], options);
3641 }
3642
3643 /**
3644 * @param {Callback} callback signals when the call finishes
3645 * @returns {void}
3646 */
3647 createChunkAssets(callback) {
3648 const outputOptions = this.outputOptions;
3649 const cachedSourceMap = new WeakMap();
3650 /** @type {Map<string, {hash: string, source: Source, chunk: Chunk}>} */
3651 const alreadyWrittenFiles = new Map();
3652
3653 asyncLib.forEach(
3654 this.chunks,
3655 (chunk, callback) => {
3656 /** @type {RenderManifestEntry[]} */
3657 let manifest;
3658 try {
3659 manifest = this.getRenderManifest({
3660 chunk,
3661 hash: this.hash,
3662 fullHash: this.fullHash,
3663 outputOptions,
3664 codeGenerationResults: this.codeGenerationResults,
3665 moduleTemplates: this.moduleTemplates,
3666 dependencyTemplates: this.dependencyTemplates,
3667 chunkGraph: this.chunkGraph,
3668 moduleGraph: this.moduleGraph,
3669 runtimeTemplate: this.runtimeTemplate
3670 });
3671 } catch (err) {
3672 this.errors.push(new ChunkRenderError(chunk, "", err));
3673 return callback();
3674 }
3675 asyncLib.forEach(
3676 manifest,
3677 (fileManifest, callback) => {
3678 const ident = fileManifest.identifier;
3679 const usedHash = fileManifest.hash;
3680
3681 const assetCacheItem = this._assetsCache.getItemCache(
3682 ident,
3683 usedHash
3684 );
3685
3686 assetCacheItem.get((err, sourceFromCache) => {
3687 /** @type {string | function(PathData, AssetInfo=): string} */
3688 let filenameTemplate;
3689 /** @type {string} */
3690 let file;
3691 /** @type {AssetInfo} */
3692 let assetInfo;
3693
3694 let inTry = true;
3695 const errorAndCallback = err => {
3696 const filename =
3697 file ||
3698 (typeof file === "string"
3699 ? file
3700 : typeof filenameTemplate === "string"
3701 ? filenameTemplate
3702 : "");
3703
3704 this.errors.push(new ChunkRenderError(chunk, filename, err));
3705 inTry = false;
3706 return callback();
3707 };
3708
3709 try {
3710 if ("filename" in fileManifest) {
3711 file = fileManifest.filename;
3712 assetInfo = fileManifest.info;
3713 } else {
3714 filenameTemplate = fileManifest.filenameTemplate;
3715 const pathAndInfo = this.getPathWithInfo(
3716 filenameTemplate,
3717 fileManifest.pathOptions
3718 );
3719 file = pathAndInfo.path;
3720 assetInfo = fileManifest.info
3721 ? {
3722 ...pathAndInfo.info,
3723 ...fileManifest.info
3724 }
3725 : pathAndInfo.info;
3726 }
3727
3728 if (err) {
3729 return errorAndCallback(err);
3730 }
3731
3732 let source = sourceFromCache;
3733
3734 // check if the same filename was already written by another chunk
3735 const alreadyWritten = alreadyWrittenFiles.get(file);
3736 if (alreadyWritten !== undefined) {
3737 if (alreadyWritten.hash !== usedHash) {
3738 inTry = false;
3739 return callback(
3740 new WebpackError(
3741 `Conflict: Multiple chunks emit assets to the same filename ${file}` +
3742 ` (chunks ${alreadyWritten.chunk.id} and ${chunk.id})`
3743 )
3744 );
3745 } else {
3746 source = alreadyWritten.source;
3747 }
3748 } else if (!source) {
3749 // render the asset
3750 source = fileManifest.render();
3751
3752 // Ensure that source is a cached source to avoid additional cost because of repeated access
3753 if (!(source instanceof CachedSource)) {
3754 const cacheEntry = cachedSourceMap.get(source);
3755 if (cacheEntry) {
3756 source = cacheEntry;
3757 } else {
3758 const cachedSource = new CachedSource(source);
3759 cachedSourceMap.set(source, cachedSource);
3760 source = cachedSource;
3761 }
3762 }
3763 }
3764 this.emitAsset(file, source, assetInfo);
3765 if (fileManifest.auxiliary) {
3766 chunk.auxiliaryFiles.add(file);
3767 } else {
3768 chunk.files.add(file);
3769 }
3770 this.hooks.chunkAsset.call(chunk, file);
3771 alreadyWrittenFiles.set(file, {
3772 hash: usedHash,
3773 source,
3774 chunk
3775 });
3776 if (source !== sourceFromCache) {
3777 assetCacheItem.store(source, err => {
3778 if (err) return errorAndCallback(err);
3779 inTry = false;
3780 return callback();
3781 });
3782 } else {
3783 inTry = false;
3784 callback();
3785 }
3786 } catch (err) {
3787 if (!inTry) throw err;
3788 errorAndCallback(err);
3789 }
3790 });
3791 },
3792 callback
3793 );
3794 },
3795 callback
3796 );
3797 }
3798
3799 /**
3800 * @param {string | function(PathData, AssetInfo=): string} filename used to get asset path with hash
3801 * @param {PathData} data context data
3802 * @returns {string} interpolated path
3803 */
3804 getPath(filename, data = {}) {
3805 if (!data.hash) {
3806 data = {
3807 hash: this.hash,
3808 ...data
3809 };
3810 }
3811 return this.getAssetPath(filename, data);
3812 }
3813
3814 /**
3815 * @param {string | function(PathData, AssetInfo=): string} filename used to get asset path with hash
3816 * @param {PathData} data context data
3817 * @returns {{ path: string, info: AssetInfo }} interpolated path and asset info
3818 */
3819 getPathWithInfo(filename, data = {}) {
3820 if (!data.hash) {
3821 data = {
3822 hash: this.hash,
3823 ...data
3824 };
3825 }
3826 return this.getAssetPathWithInfo(filename, data);
3827 }
3828
3829 /**
3830 * @param {string | function(PathData, AssetInfo=): string} filename used to get asset path with hash
3831 * @param {PathData} data context data
3832 * @returns {string} interpolated path
3833 */
3834 getAssetPath(filename, data) {
3835 return this.hooks.assetPath.call(
3836 typeof filename === "function" ? filename(data) : filename,
3837 data,
3838 undefined
3839 );
3840 }
3841
3842 /**
3843 * @param {string | function(PathData, AssetInfo=): string} filename used to get asset path with hash
3844 * @param {PathData} data context data
3845 * @returns {{ path: string, info: AssetInfo }} interpolated path and asset info
3846 */
3847 getAssetPathWithInfo(filename, data) {
3848 const assetInfo = {};
3849 // TODO webpack 5: refactor assetPath hook to receive { path, info } object
3850 const newPath = this.hooks.assetPath.call(
3851 typeof filename === "function" ? filename(data, assetInfo) : filename,
3852 data,
3853 assetInfo
3854 );
3855 return { path: newPath, info: assetInfo };
3856 }
3857
3858 getWarnings() {
3859 return this.hooks.processWarnings.call(this.warnings);
3860 }
3861
3862 getErrors() {
3863 return this.hooks.processErrors.call(this.errors);
3864 }
3865
3866 /**
3867 * This function allows you to run another instance of webpack inside of webpack however as
3868 * a child with different settings and configurations (if desired) applied. It copies all hooks, plugins
3869 * from parent (or top level compiler) and creates a child Compilation
3870 *
3871 * @param {string} name name of the child compiler
3872 * @param {OutputOptions} outputOptions // Need to convert config schema to types for this
3873 * @param {Array<WebpackPluginInstance | WebpackPluginFunction>} plugins webpack plugins that will be applied
3874 * @returns {Compiler} creates a child Compiler instance
3875 */
3876 createChildCompiler(name, outputOptions, plugins) {
3877 const idx = this.childrenCounters[name] || 0;
3878 this.childrenCounters[name] = idx + 1;
3879 return this.compiler.createChildCompiler(
3880 this,
3881 name,
3882 idx,
3883 outputOptions,
3884 plugins
3885 );
3886 }
3887
3888 checkConstraints() {
3889 const chunkGraph = this.chunkGraph;
3890
3891 /** @type {Set<number|string>} */
3892 const usedIds = new Set();
3893
3894 for (const module of this.modules) {
3895 if (module.type === "runtime") continue;
3896 const moduleId = chunkGraph.getModuleId(module);
3897 if (moduleId === null) continue;
3898 if (usedIds.has(moduleId)) {
3899 throw new Error(`checkConstraints: duplicate module id ${moduleId}`);
3900 }
3901 usedIds.add(moduleId);
3902 }
3903
3904 for (const chunk of this.chunks) {
3905 for (const module of chunkGraph.getChunkModulesIterable(chunk)) {
3906 if (!this.modules.has(module)) {
3907 throw new Error(
3908 "checkConstraints: module in chunk but not in compilation " +
3909 ` ${chunk.debugId} ${module.debugId}`
3910 );
3911 }
3912 }
3913 for (const module of chunkGraph.getChunkEntryModulesIterable(chunk)) {
3914 if (!this.modules.has(module)) {
3915 throw new Error(
3916 "checkConstraints: entry module in chunk but not in compilation " +
3917 ` ${chunk.debugId} ${module.debugId}`
3918 );
3919 }
3920 }
3921 }
3922
3923 for (const chunkGroup of this.chunkGroups) {
3924 chunkGroup.checkConstraints();
3925 }
3926 }
3927}
3928
3929// Hide from typescript
3930const compilationPrototype = Compilation.prototype;
3931
3932// TODO webpack 6 remove
3933Object.defineProperty(compilationPrototype, "modifyHash", {
3934 writable: false,
3935 enumerable: false,
3936 configurable: false,
3937 value: () => {
3938 throw new Error(
3939 "Compilation.modifyHash was removed in favor of Compilation.hooks.fullHash"
3940 );
3941 }
3942});
3943
3944// TODO webpack 6 remove
3945Object.defineProperty(compilationPrototype, "cache", {
3946 enumerable: false,
3947 configurable: false,
3948 get: util.deprecate(
3949 /**
3950 * @this {Compilation} the compilation
3951 * @returns {Cache} the cache
3952 */
3953 function () {
3954 return this.compiler.cache;
3955 },
3956 "Compilation.cache was removed in favor of Compilation.getCache()",
3957 "DEP_WEBPACK_COMPILATION_CACHE"
3958 ),
3959 set: util.deprecate(
3960 v => {},
3961 "Compilation.cache was removed in favor of Compilation.getCache()",
3962 "DEP_WEBPACK_COMPILATION_CACHE"
3963 )
3964});
3965
3966/**
3967 * Add additional assets to the compilation.
3968 */
3969Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL = -2000;
3970
3971/**
3972 * Basic preprocessing of assets.
3973 */
3974Compilation.PROCESS_ASSETS_STAGE_PRE_PROCESS = -1000;
3975
3976/**
3977 * Derive new assets from existing assets.
3978 * Existing assets should not be treated as complete.
3979 */
3980Compilation.PROCESS_ASSETS_STAGE_DERIVED = -200;
3981
3982/**
3983 * Add additional sections to existing assets, like a banner or initialization code.
3984 */
3985Compilation.PROCESS_ASSETS_STAGE_ADDITIONS = -100;
3986
3987/**
3988 * Optimize existing assets in a general way.
3989 */
3990Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE = 100;
3991
3992/**
3993 * Optimize the count of existing assets, e. g. by merging them.
3994 * Only assets of the same type should be merged.
3995 * For assets of different types see PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE.
3996 */
3997Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_COUNT = 200;
3998
3999/**
4000 * Optimize the compatibility of existing assets, e. g. add polyfills or vendor-prefixes.
4001 */
4002Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_COMPATIBILITY = 300;
4003
4004/**
4005 * Optimize the size of existing assets, e. g. by minimizing or omitting whitespace.
4006 */
4007Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE = 400;
4008
4009/**
4010 * Add development tooling to assets, e. g. by extracting a SourceMap.
4011 */
4012Compilation.PROCESS_ASSETS_STAGE_DEV_TOOLING = 500;
4013
4014/**
4015 * Optimize the count of existing assets, e. g. by inlining assets of into other assets.
4016 * Only assets of different types should be inlined.
4017 * For assets of the same type see PROCESS_ASSETS_STAGE_OPTIMIZE_COUNT.
4018 */
4019Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE = 700;
4020
4021/**
4022 * Summarize the list of existing assets
4023 * e. g. creating an assets manifest of Service Workers.
4024 */
4025Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE = 1000;
4026
4027/**
4028 * Optimize the hashes of the assets, e. g. by generating real hashes of the asset content.
4029 */
4030Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_HASH = 2500;
4031
4032/**
4033 * Optimize the transfer of existing assets, e. g. by preparing a compressed (gzip) file as separate asset.
4034 */
4035Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER = 3000;
4036
4037/**
4038 * Analyse existing assets.
4039 */
4040Compilation.PROCESS_ASSETS_STAGE_ANALYSE = 4000;
4041
4042/**
4043 * Creating assets for reporting purposes.
4044 */
4045Compilation.PROCESS_ASSETS_STAGE_REPORT = 5000;
4046
4047module.exports = Compilation;
4048
\No newline at end of file