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