UNPKG

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