Projekt

Obecné

Profil

Stáhnout (66.1 KB) Statistiky
| Větev: | Revize:
1
/*
2
	MIT License http://www.opensource.org/licenses/mit-license.php
3
	Author Tobias Koppers @sokra
4
	*/
5
"use strict";
6

    
7
const asyncLib = require("neo-async");
8
const util = require("util");
9
const { CachedSource } = require("webpack-sources");
10
const {
11
	Tapable,
12
	SyncHook,
13
	SyncBailHook,
14
	SyncWaterfallHook,
15
	AsyncSeriesHook
16
} = require("tapable");
17
const EntryModuleNotFoundError = require("./EntryModuleNotFoundError");
18
const ModuleNotFoundError = require("./ModuleNotFoundError");
19
const ModuleDependencyWarning = require("./ModuleDependencyWarning");
20
const ModuleDependencyError = require("./ModuleDependencyError");
21
const ChunkGroup = require("./ChunkGroup");
22
const Chunk = require("./Chunk");
23
const Entrypoint = require("./Entrypoint");
24
const MainTemplate = require("./MainTemplate");
25
const ChunkTemplate = require("./ChunkTemplate");
26
const HotUpdateChunkTemplate = require("./HotUpdateChunkTemplate");
27
const ModuleTemplate = require("./ModuleTemplate");
28
const RuntimeTemplate = require("./RuntimeTemplate");
29
const ChunkRenderError = require("./ChunkRenderError");
30
const Stats = require("./Stats");
31
const Semaphore = require("./util/Semaphore");
32
const createHash = require("./util/createHash");
33
const SortableSet = require("./util/SortableSet");
34
const GraphHelpers = require("./GraphHelpers");
35
const ModuleDependency = require("./dependencies/ModuleDependency");
36
const compareLocations = require("./compareLocations");
37
const { Logger, LogType } = require("./logging/Logger");
38
const ErrorHelpers = require("./ErrorHelpers");
39
const buildChunkGraph = require("./buildChunkGraph");
40
const WebpackError = require("./WebpackError");
41

    
42
/** @typedef {import("./Module")} Module */
43
/** @typedef {import("./Compiler")} Compiler */
44
/** @typedef {import("webpack-sources").Source} Source */
45
/** @typedef {import("./DependenciesBlockVariable")} DependenciesBlockVariable */
46
/** @typedef {import("./dependencies/SingleEntryDependency")} SingleEntryDependency */
47
/** @typedef {import("./dependencies/MultiEntryDependency")} MultiEntryDependency */
48
/** @typedef {import("./dependencies/DllEntryDependency")} DllEntryDependency */
49
/** @typedef {import("./dependencies/DependencyReference")} DependencyReference */
50
/** @typedef {import("./DependenciesBlock")} DependenciesBlock */
51
/** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */
52
/** @typedef {import("./Dependency")} Dependency */
53
/** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */
54
/** @typedef {import("./Dependency").DependencyTemplate} DependencyTemplate */
55
/** @typedef {import("./util/createHash").Hash} Hash */
56

    
57
// TODO use @callback
58
/** @typedef {{[assetName: string]: Source}} CompilationAssets */
59
/** @typedef {(err: Error|null, result?: Module) => void } ModuleCallback */
60
/** @typedef {(err?: Error|null, result?: Module) => void } ModuleChainCallback */
61
/** @typedef {(module: Module) => void} OnModuleCallback */
62
/** @typedef {(err?: Error|null) => void} Callback */
63
/** @typedef {(d: Dependency) => any} DepBlockVarDependenciesCallback */
64
/** @typedef {new (...args: any[]) => Dependency} DepConstructor */
65
/** @typedef {{apply: () => void}} Plugin */
66

    
67
/**
68
 * @typedef {Object} ModuleFactoryCreateDataContextInfo
69
 * @property {string} issuer
70
 * @property {string} compiler
71
 */
72

    
73
/**
74
 * @typedef {Object} ModuleFactoryCreateData
75
 * @property {ModuleFactoryCreateDataContextInfo} contextInfo
76
 * @property {any=} resolveOptions
77
 * @property {string} context
78
 * @property {Dependency[]} dependencies
79
 */
80

    
81
/**
82
 * @typedef {Object} ModuleFactory
83
 * @property {(data: ModuleFactoryCreateData, callback: ModuleCallback) => any} create
84
 */
85

    
86
/**
87
 * @typedef {Object} SortedDependency
88
 * @property {ModuleFactory} factory
89
 * @property {Dependency[]} dependencies
90
 */
91

    
92
/**
93
 * @typedef {Object} DependenciesBlockLike
94
 * @property {Dependency[]} dependencies
95
 * @property {AsyncDependenciesBlock[]} blocks
96
 * @property {DependenciesBlockVariable[]} variables
97
 */
98

    
99
/**
100
 * @typedef {Object} LogEntry
101
 * @property {string} type
102
 * @property {any[]} args
103
 * @property {number} time
104
 * @property {string[]=} trace
105
 */
106

    
107
/**
108
 * @typedef {Object} AssetInfo
109
 * @property {boolean=} immutable true, if the asset can be long term cached forever (contains a hash)
110
 * @property {number=} size size in bytes, only set after asset has been emitted
111
 * @property {boolean=} development true, when asset is only used for development and doesn't count towards user-facing assets
112
 * @property {boolean=} hotModuleReplacement true, when asset ships data for updating an existing application (HMR)
113
 */
114

    
115
/**
116
 * @typedef {Object} Asset
117
 * @property {string} name the filename of the asset
118
 * @property {Source} source source of the asset
119
 * @property {AssetInfo} info info about the asset
120
 */
121

    
122
/**
123
 * @param {Chunk} a first chunk to sort by id
124
 * @param {Chunk} b second chunk to sort by id
125
 * @returns {-1|0|1} sort value
126
 */
127
const byId = (a, b) => {
128
	if (typeof a.id !== typeof b.id) {
129
		return typeof a.id < typeof b.id ? -1 : 1;
130
	}
131
	if (a.id < b.id) return -1;
132
	if (a.id > b.id) return 1;
133
	return 0;
134
};
135

    
136
/**
137
 * @param {Module} a first module to sort by
138
 * @param {Module} b second module to sort by
139
 * @returns {-1|0|1} sort value
140
 */
141
const byIdOrIdentifier = (a, b) => {
142
	if (typeof a.id !== typeof b.id) {
143
		return typeof a.id < typeof b.id ? -1 : 1;
144
	}
145
	if (a.id < b.id) return -1;
146
	if (a.id > b.id) return 1;
147
	const identA = a.identifier();
148
	const identB = b.identifier();
149
	if (identA < identB) return -1;
150
	if (identA > identB) return 1;
151
	return 0;
152
};
153

    
154
/**
155
 * @param {Module} a first module to sort by
156
 * @param {Module} b second module to sort by
157
 * @returns {-1|0|1} sort value
158
 */
159
const byIndexOrIdentifier = (a, b) => {
160
	if (a.index < b.index) return -1;
161
	if (a.index > b.index) return 1;
162
	const identA = a.identifier();
163
	const identB = b.identifier();
164
	if (identA < identB) return -1;
165
	if (identA > identB) return 1;
166
	return 0;
167
};
168

    
169
/**
170
 * @param {Compilation} a first compilation to sort by
171
 * @param {Compilation} b second compilation to sort by
172
 * @returns {-1|0|1} sort value
173
 */
174
const byNameOrHash = (a, b) => {
175
	if (a.name < b.name) return -1;
176
	if (a.name > b.name) return 1;
177
	if (a.fullHash < b.fullHash) return -1;
178
	if (a.fullHash > b.fullHash) return 1;
179
	return 0;
180
};
181

    
182
/**
183
 * @param {DependenciesBlockVariable[]} variables DepBlock Variables to iterate over
184
 * @param {DepBlockVarDependenciesCallback} fn callback to apply on iterated elements
185
 * @returns {void}
186
 */
187
const iterationBlockVariable = (variables, fn) => {
188
	for (
189
		let indexVariable = 0;
190
		indexVariable < variables.length;
191
		indexVariable++
192
	) {
193
		const varDep = variables[indexVariable].dependencies;
194
		for (let indexVDep = 0; indexVDep < varDep.length; indexVDep++) {
195
			fn(varDep[indexVDep]);
196
		}
197
	}
198
};
199

    
200
/**
201
 * @template T
202
 * @param {T[]} arr array of elements to iterate over
203
 * @param {function(T): void} fn callback applied to each element
204
 * @returns {void}
205
 */
206
const iterationOfArrayCallback = (arr, fn) => {
207
	for (let index = 0; index < arr.length; index++) {
208
		fn(arr[index]);
209
	}
210
};
211

    
212
/**
213
 * @template T
214
 * @param {Set<T>} set set to add items to
215
 * @param {Set<T>} otherSet set to add items from
216
 * @returns {void}
217
 */
218
const addAllToSet = (set, otherSet) => {
219
	for (const item of otherSet) {
220
		set.add(item);
221
	}
222
};
223

    
224
/**
225
 * @param {Source} a a source
226
 * @param {Source} b another source
227
 * @returns {boolean} true, when both sources are equal
228
 */
229
const isSourceEqual = (a, b) => {
230
	if (a === b) return true;
231
	// TODO webpack 5: check .buffer() instead, it's called anyway during emit
232
	/** @type {Buffer|string} */
233
	let aSource = a.source();
234
	/** @type {Buffer|string} */
235
	let bSource = b.source();
236
	if (aSource === bSource) return true;
237
	if (typeof aSource === "string" && typeof bSource === "string") return false;
238
	if (!Buffer.isBuffer(aSource)) aSource = Buffer.from(aSource, "utf-8");
239
	if (!Buffer.isBuffer(bSource)) bSource = Buffer.from(bSource, "utf-8");
240
	return aSource.equals(bSource);
241
};
242

    
243
class Compilation extends Tapable {
244
	/**
245
	 * Creates an instance of Compilation.
246
	 * @param {Compiler} compiler the compiler which created the compilation
247
	 */
248
	constructor(compiler) {
249
		super();
250
		this.hooks = {
251
			/** @type {SyncHook<Module>} */
252
			buildModule: new SyncHook(["module"]),
253
			/** @type {SyncHook<Module>} */
254
			rebuildModule: new SyncHook(["module"]),
255
			/** @type {SyncHook<Module, Error>} */
256
			failedModule: new SyncHook(["module", "error"]),
257
			/** @type {SyncHook<Module>} */
258
			succeedModule: new SyncHook(["module"]),
259

    
260
			/** @type {SyncHook<Dependency, string>} */
261
			addEntry: new SyncHook(["entry", "name"]),
262
			/** @type {SyncHook<Dependency, string, Error>} */
263
			failedEntry: new SyncHook(["entry", "name", "error"]),
264
			/** @type {SyncHook<Dependency, string, Module>} */
265
			succeedEntry: new SyncHook(["entry", "name", "module"]),
266

    
267
			/** @type {SyncWaterfallHook<DependencyReference, Dependency, Module>} */
268
			dependencyReference: new SyncWaterfallHook([
269
				"dependencyReference",
270
				"dependency",
271
				"module"
272
			]),
273

    
274
			/** @type {AsyncSeriesHook<Module[]>} */
275
			finishModules: new AsyncSeriesHook(["modules"]),
276
			/** @type {SyncHook<Module>} */
277
			finishRebuildingModule: new SyncHook(["module"]),
278
			/** @type {SyncHook} */
279
			unseal: new SyncHook([]),
280
			/** @type {SyncHook} */
281
			seal: new SyncHook([]),
282

    
283
			/** @type {SyncHook} */
284
			beforeChunks: new SyncHook([]),
285
			/** @type {SyncHook<Chunk[]>} */
286
			afterChunks: new SyncHook(["chunks"]),
287

    
288
			/** @type {SyncBailHook<Module[]>} */
289
			optimizeDependenciesBasic: new SyncBailHook(["modules"]),
290
			/** @type {SyncBailHook<Module[]>} */
291
			optimizeDependencies: new SyncBailHook(["modules"]),
292
			/** @type {SyncBailHook<Module[]>} */
293
			optimizeDependenciesAdvanced: new SyncBailHook(["modules"]),
294
			/** @type {SyncBailHook<Module[]>} */
295
			afterOptimizeDependencies: new SyncHook(["modules"]),
296

    
297
			/** @type {SyncHook} */
298
			optimize: new SyncHook([]),
299
			/** @type {SyncBailHook<Module[]>} */
300
			optimizeModulesBasic: new SyncBailHook(["modules"]),
301
			/** @type {SyncBailHook<Module[]>} */
302
			optimizeModules: new SyncBailHook(["modules"]),
303
			/** @type {SyncBailHook<Module[]>} */
304
			optimizeModulesAdvanced: new SyncBailHook(["modules"]),
305
			/** @type {SyncHook<Module[]>} */
306
			afterOptimizeModules: new SyncHook(["modules"]),
307

    
308
			/** @type {SyncBailHook<Chunk[], ChunkGroup[]>} */
309
			optimizeChunksBasic: new SyncBailHook(["chunks", "chunkGroups"]),
310
			/** @type {SyncBailHook<Chunk[], ChunkGroup[]>} */
311
			optimizeChunks: new SyncBailHook(["chunks", "chunkGroups"]),
312
			/** @type {SyncBailHook<Chunk[], ChunkGroup[]>} */
313
			optimizeChunksAdvanced: new SyncBailHook(["chunks", "chunkGroups"]),
314
			/** @type {SyncHook<Chunk[], ChunkGroup[]>} */
315
			afterOptimizeChunks: new SyncHook(["chunks", "chunkGroups"]),
316

    
317
			/** @type {AsyncSeriesHook<Chunk[], Module[]>} */
318
			optimizeTree: new AsyncSeriesHook(["chunks", "modules"]),
319
			/** @type {SyncHook<Chunk[], Module[]>} */
320
			afterOptimizeTree: new SyncHook(["chunks", "modules"]),
321

    
322
			/** @type {SyncBailHook<Chunk[], Module[]>} */
323
			optimizeChunkModulesBasic: new SyncBailHook(["chunks", "modules"]),
324
			/** @type {SyncBailHook<Chunk[], Module[]>} */
325
			optimizeChunkModules: new SyncBailHook(["chunks", "modules"]),
326
			/** @type {SyncBailHook<Chunk[], Module[]>} */
327
			optimizeChunkModulesAdvanced: new SyncBailHook(["chunks", "modules"]),
328
			/** @type {SyncHook<Chunk[], Module[]>} */
329
			afterOptimizeChunkModules: new SyncHook(["chunks", "modules"]),
330
			/** @type {SyncBailHook} */
331
			shouldRecord: new SyncBailHook([]),
332

    
333
			/** @type {SyncHook<Module[], any>} */
334
			reviveModules: new SyncHook(["modules", "records"]),
335
			/** @type {SyncHook<Module[]>} */
336
			optimizeModuleOrder: new SyncHook(["modules"]),
337
			/** @type {SyncHook<Module[]>} */
338
			advancedOptimizeModuleOrder: new SyncHook(["modules"]),
339
			/** @type {SyncHook<Module[]>} */
340
			beforeModuleIds: new SyncHook(["modules"]),
341
			/** @type {SyncHook<Module[]>} */
342
			moduleIds: new SyncHook(["modules"]),
343
			/** @type {SyncHook<Module[]>} */
344
			optimizeModuleIds: new SyncHook(["modules"]),
345
			/** @type {SyncHook<Module[]>} */
346
			afterOptimizeModuleIds: new SyncHook(["modules"]),
347

    
348
			/** @type {SyncHook<Chunk[], any>} */
349
			reviveChunks: new SyncHook(["chunks", "records"]),
350
			/** @type {SyncHook<Chunk[]>} */
351
			optimizeChunkOrder: new SyncHook(["chunks"]),
352
			/** @type {SyncHook<Chunk[]>} */
353
			beforeChunkIds: new SyncHook(["chunks"]),
354
			/** @type {SyncHook<Chunk[]>} */
355
			optimizeChunkIds: new SyncHook(["chunks"]),
356
			/** @type {SyncHook<Chunk[]>} */
357
			afterOptimizeChunkIds: new SyncHook(["chunks"]),
358

    
359
			/** @type {SyncHook<Module[], any>} */
360
			recordModules: new SyncHook(["modules", "records"]),
361
			/** @type {SyncHook<Chunk[], any>} */
362
			recordChunks: new SyncHook(["chunks", "records"]),
363

    
364
			/** @type {SyncHook} */
365
			beforeHash: new SyncHook([]),
366
			/** @type {SyncHook<Chunk>} */
367
			contentHash: new SyncHook(["chunk"]),
368
			/** @type {SyncHook} */
369
			afterHash: new SyncHook([]),
370
			/** @type {SyncHook<any>} */
371
			recordHash: new SyncHook(["records"]),
372
			/** @type {SyncHook<Compilation, any>} */
373
			record: new SyncHook(["compilation", "records"]),
374

    
375
			/** @type {SyncHook} */
376
			beforeModuleAssets: new SyncHook([]),
377
			/** @type {SyncBailHook} */
378
			shouldGenerateChunkAssets: new SyncBailHook([]),
379
			/** @type {SyncHook} */
380
			beforeChunkAssets: new SyncHook([]),
381
			/** @type {SyncHook<Chunk[]>} */
382
			additionalChunkAssets: new SyncHook(["chunks"]),
383

    
384
			/** @type {AsyncSeriesHook} */
385
			additionalAssets: new AsyncSeriesHook([]),
386
			/** @type {AsyncSeriesHook<Chunk[]>} */
387
			optimizeChunkAssets: new AsyncSeriesHook(["chunks"]),
388
			/** @type {SyncHook<Chunk[]>} */
389
			afterOptimizeChunkAssets: new SyncHook(["chunks"]),
390
			/** @type {AsyncSeriesHook<CompilationAssets>} */
391
			optimizeAssets: new AsyncSeriesHook(["assets"]),
392
			/** @type {SyncHook<CompilationAssets>} */
393
			afterOptimizeAssets: new SyncHook(["assets"]),
394

    
395
			/** @type {SyncBailHook} */
396
			needAdditionalSeal: new SyncBailHook([]),
397
			/** @type {AsyncSeriesHook} */
398
			afterSeal: new AsyncSeriesHook([]),
399

    
400
			/** @type {SyncHook<Chunk, Hash>} */
401
			chunkHash: new SyncHook(["chunk", "chunkHash"]),
402
			/** @type {SyncHook<Module, string>} */
403
			moduleAsset: new SyncHook(["module", "filename"]),
404
			/** @type {SyncHook<Chunk, string>} */
405
			chunkAsset: new SyncHook(["chunk", "filename"]),
406

    
407
			/** @type {SyncWaterfallHook<string, TODO>} */
408
			assetPath: new SyncWaterfallHook(["filename", "data"]), // TODO MainTemplate
409

    
410
			/** @type {SyncBailHook} */
411
			needAdditionalPass: new SyncBailHook([]),
412

    
413
			/** @type {SyncHook<Compiler, string, number>} */
414
			childCompiler: new SyncHook([
415
				"childCompiler",
416
				"compilerName",
417
				"compilerIndex"
418
			]),
419

    
420
			/** @type {SyncBailHook<string, LogEntry>} */
421
			log: new SyncBailHook(["origin", "logEntry"]),
422

    
423
			// TODO the following hooks are weirdly located here
424
			// TODO move them for webpack 5
425
			/** @type {SyncHook<object, Module>} */
426
			normalModuleLoader: new SyncHook(["loaderContext", "module"]),
427

    
428
			/** @type {SyncBailHook<Chunk[]>} */
429
			optimizeExtractedChunksBasic: new SyncBailHook(["chunks"]),
430
			/** @type {SyncBailHook<Chunk[]>} */
431
			optimizeExtractedChunks: new SyncBailHook(["chunks"]),
432
			/** @type {SyncBailHook<Chunk[]>} */
433
			optimizeExtractedChunksAdvanced: new SyncBailHook(["chunks"]),
434
			/** @type {SyncHook<Chunk[]>} */
435
			afterOptimizeExtractedChunks: new SyncHook(["chunks"])
436
		};
437
		this._pluginCompat.tap("Compilation", options => {
438
			switch (options.name) {
439
				case "optimize-tree":
440
				case "additional-assets":
441
				case "optimize-chunk-assets":
442
				case "optimize-assets":
443
				case "after-seal":
444
					options.async = true;
445
					break;
446
			}
447
		});
448
		/** @type {string=} */
449
		this.name = undefined;
450
		/** @type {Compiler} */
451
		this.compiler = compiler;
452
		this.resolverFactory = compiler.resolverFactory;
453
		this.inputFileSystem = compiler.inputFileSystem;
454
		this.requestShortener = compiler.requestShortener;
455

    
456
		const options = compiler.options;
457
		this.options = options;
458
		this.outputOptions = options && options.output;
459
		/** @type {boolean=} */
460
		this.bail = options && options.bail;
461
		this.profile = options && options.profile;
462
		this.performance = options && options.performance;
463

    
464
		this.mainTemplate = new MainTemplate(this.outputOptions);
465
		this.chunkTemplate = new ChunkTemplate(this.outputOptions);
466
		this.hotUpdateChunkTemplate = new HotUpdateChunkTemplate(
467
			this.outputOptions
468
		);
469
		this.runtimeTemplate = new RuntimeTemplate(
470
			this.outputOptions,
471
			this.requestShortener
472
		);
473
		this.moduleTemplates = {
474
			javascript: new ModuleTemplate(this.runtimeTemplate, "javascript"),
475
			webassembly: new ModuleTemplate(this.runtimeTemplate, "webassembly")
476
		};
477

    
478
		this.semaphore = new Semaphore(options.parallelism || 100);
479

    
480
		this.entries = [];
481
		/** @private @type {{name: string, request: string, module: Module}[]} */
482
		this._preparedEntrypoints = [];
483
		/** @type {Map<string, Entrypoint>} */
484
		this.entrypoints = new Map();
485
		/** @type {Chunk[]} */
486
		this.chunks = [];
487
		/** @type {ChunkGroup[]} */
488
		this.chunkGroups = [];
489
		/** @type {Map<string, ChunkGroup>} */
490
		this.namedChunkGroups = new Map();
491
		/** @type {Map<string, Chunk>} */
492
		this.namedChunks = new Map();
493
		/** @type {Module[]} */
494
		this.modules = [];
495
		/** @private @type {Map<string, Module>} */
496
		this._modules = new Map();
497
		this.cache = null;
498
		this.records = null;
499
		/** @type {string[]} */
500
		this.additionalChunkAssets = [];
501
		/** @type {CompilationAssets} */
502
		this.assets = {};
503
		/** @type {Map<string, AssetInfo>} */
504
		this.assetsInfo = new Map();
505
		/** @type {WebpackError[]} */
506
		this.errors = [];
507
		/** @type {WebpackError[]} */
508
		this.warnings = [];
509
		/** @type {Compilation[]} */
510
		this.children = [];
511
		/** @type {Map<string, LogEntry[]>} */
512
		this.logging = new Map();
513
		/** @type {Map<DepConstructor, ModuleFactory>} */
514
		this.dependencyFactories = new Map();
515
		/** @type {Map<DepConstructor, DependencyTemplate>} */
516
		this.dependencyTemplates = new Map();
517
		// TODO refactor this in webpack 5 to a custom DependencyTemplates class with a hash property
518
		// @ts-ignore
519
		this.dependencyTemplates.set("hash", "");
520
		this.childrenCounters = {};
521
		/** @type {Set<number|string>} */
522
		this.usedChunkIds = null;
523
		/** @type {Set<number>} */
524
		this.usedModuleIds = null;
525
		/** @type {Map<string, number>=} */
526
		this.fileTimestamps = undefined;
527
		/** @type {Map<string, number>=} */
528
		this.contextTimestamps = undefined;
529
		/** @type {Set<string>=} */
530
		this.compilationDependencies = undefined;
531
		/** @private @type {Map<Module, Callback[]>} */
532
		this._buildingModules = new Map();
533
		/** @private @type {Map<Module, Callback[]>} */
534
		this._rebuildingModules = new Map();
535
		/** @type {Set<string>} */
536
		this.emittedAssets = new Set();
537
	}
538

    
539
	getStats() {
540
		return new Stats(this);
541
	}
542

    
543
	/**
544
	 * @param {string | (function(): string)} name name of the logger, or function called once to get the logger name
545
	 * @returns {Logger} a logger with that name
546
	 */
547
	getLogger(name) {
548
		if (!name) {
549
			throw new TypeError("Compilation.getLogger(name) called without a name");
550
		}
551
		/** @type {LogEntry[] | undefined} */
552
		let logEntries;
553
		return new Logger((type, args) => {
554
			if (typeof name === "function") {
555
				name = name();
556
				if (!name) {
557
					throw new TypeError(
558
						"Compilation.getLogger(name) called with a function not returning a name"
559
					);
560
				}
561
			}
562
			let trace;
563
			switch (type) {
564
				case LogType.warn:
565
				case LogType.error:
566
				case LogType.trace:
567
					trace = ErrorHelpers.cutOffLoaderExecution(new Error("Trace").stack)
568
						.split("\n")
569
						.slice(3);
570
					break;
571
			}
572
			/** @type {LogEntry} */
573
			const logEntry = {
574
				time: Date.now(),
575
				type,
576
				args,
577
				trace
578
			};
579
			if (this.hooks.log.call(name, logEntry) === undefined) {
580
				if (logEntry.type === LogType.profileEnd) {
581
					// eslint-disable-next-line node/no-unsupported-features/node-builtins
582
					if (typeof console.profileEnd === "function") {
583
						// eslint-disable-next-line node/no-unsupported-features/node-builtins
584
						console.profileEnd(`[${name}] ${logEntry.args[0]}`);
585
					}
586
				}
587
				if (logEntries === undefined) {
588
					logEntries = this.logging.get(name);
589
					if (logEntries === undefined) {
590
						logEntries = [];
591
						this.logging.set(name, logEntries);
592
					}
593
				}
594
				logEntries.push(logEntry);
595
				if (logEntry.type === LogType.profile) {
596
					// eslint-disable-next-line node/no-unsupported-features/node-builtins
597
					if (typeof console.profile === "function") {
598
						// eslint-disable-next-line node/no-unsupported-features/node-builtins
599
						console.profile(`[${name}] ${logEntry.args[0]}`);
600
					}
601
				}
602
			}
603
		});
604
	}
605

    
606
	/**
607
	 * @typedef {Object} AddModuleResult
608
	 * @property {Module} module the added or existing module
609
	 * @property {boolean} issuer was this the first request for this module
610
	 * @property {boolean} build should the module be build
611
	 * @property {boolean} dependencies should dependencies be walked
612
	 */
613

    
614
	/**
615
	 * @param {Module} module module to be added that was created
616
	 * @param {any=} cacheGroup cacheGroup it is apart of
617
	 * @returns {AddModuleResult} returns meta about whether or not the module had built
618
	 * had an issuer, or any dependnecies
619
	 */
620
	addModule(module, cacheGroup) {
621
		const identifier = module.identifier();
622
		const alreadyAddedModule = this._modules.get(identifier);
623
		if (alreadyAddedModule) {
624
			return {
625
				module: alreadyAddedModule,
626
				issuer: false,
627
				build: false,
628
				dependencies: false
629
			};
630
		}
631
		const cacheName = (cacheGroup || "m") + identifier;
632
		if (this.cache && this.cache[cacheName]) {
633
			const cacheModule = this.cache[cacheName];
634

    
635
			if (typeof cacheModule.updateCacheModule === "function") {
636
				cacheModule.updateCacheModule(module);
637
			}
638

    
639
			let rebuild = true;
640
			if (this.fileTimestamps && this.contextTimestamps) {
641
				rebuild = cacheModule.needRebuild(
642
					this.fileTimestamps,
643
					this.contextTimestamps
644
				);
645
			}
646

    
647
			if (!rebuild) {
648
				cacheModule.disconnect();
649
				this._modules.set(identifier, cacheModule);
650
				this.modules.push(cacheModule);
651
				for (const err of cacheModule.errors) {
652
					this.errors.push(err);
653
				}
654
				for (const err of cacheModule.warnings) {
655
					this.warnings.push(err);
656
				}
657
				return {
658
					module: cacheModule,
659
					issuer: true,
660
					build: false,
661
					dependencies: true
662
				};
663
			}
664
			cacheModule.unbuild();
665
			module = cacheModule;
666
		}
667
		this._modules.set(identifier, module);
668
		if (this.cache) {
669
			this.cache[cacheName] = module;
670
		}
671
		this.modules.push(module);
672
		return {
673
			module: module,
674
			issuer: true,
675
			build: true,
676
			dependencies: true
677
		};
678
	}
679

    
680
	/**
681
	 * Fetches a module from a compilation by its identifier
682
	 * @param {Module} module the module provided
683
	 * @returns {Module} the module requested
684
	 */
685
	getModule(module) {
686
		const identifier = module.identifier();
687
		return this._modules.get(identifier);
688
	}
689

    
690
	/**
691
	 * Attempts to search for a module by its identifier
692
	 * @param {string} identifier identifier (usually path) for module
693
	 * @returns {Module|undefined} attempt to search for module and return it, else undefined
694
	 */
695
	findModule(identifier) {
696
		return this._modules.get(identifier);
697
	}
698

    
699
	/**
700
	 * @param {Module} module module with its callback list
701
	 * @param {Callback} callback the callback function
702
	 * @returns {void}
703
	 */
704
	waitForBuildingFinished(module, callback) {
705
		let callbackList = this._buildingModules.get(module);
706
		if (callbackList) {
707
			callbackList.push(() => callback());
708
		} else {
709
			process.nextTick(callback);
710
		}
711
	}
712

    
713
	/**
714
	 * Builds the module object
715
	 *
716
	 * @param {Module} module module to be built
717
	 * @param {boolean} optional optional flag
718
	 * @param {Module=} origin origin module this module build was requested from
719
	 * @param {Dependency[]=} dependencies optional dependencies from the module to be built
720
	 * @param {TODO} thisCallback the callback
721
	 * @returns {TODO} returns the callback function with results
722
	 */
723
	buildModule(module, optional, origin, dependencies, thisCallback) {
724
		let callbackList = this._buildingModules.get(module);
725
		if (callbackList) {
726
			callbackList.push(thisCallback);
727
			return;
728
		}
729
		this._buildingModules.set(module, (callbackList = [thisCallback]));
730

    
731
		const callback = err => {
732
			this._buildingModules.delete(module);
733
			for (const cb of callbackList) {
734
				cb(err);
735
			}
736
		};
737

    
738
		this.hooks.buildModule.call(module);
739
		module.build(
740
			this.options,
741
			this,
742
			this.resolverFactory.get("normal", module.resolveOptions),
743
			this.inputFileSystem,
744
			error => {
745
				const errors = module.errors;
746
				for (let indexError = 0; indexError < errors.length; indexError++) {
747
					const err = errors[indexError];
748
					err.origin = origin;
749
					err.dependencies = dependencies;
750
					if (optional) {
751
						this.warnings.push(err);
752
					} else {
753
						this.errors.push(err);
754
					}
755
				}
756

    
757
				const warnings = module.warnings;
758
				for (
759
					let indexWarning = 0;
760
					indexWarning < warnings.length;
761
					indexWarning++
762
				) {
763
					const war = warnings[indexWarning];
764
					war.origin = origin;
765
					war.dependencies = dependencies;
766
					this.warnings.push(war);
767
				}
768
				const originalMap = module.dependencies.reduce((map, v, i) => {
769
					map.set(v, i);
770
					return map;
771
				}, new Map());
772
				module.dependencies.sort((a, b) => {
773
					const cmp = compareLocations(a.loc, b.loc);
774
					if (cmp) return cmp;
775
					return originalMap.get(a) - originalMap.get(b);
776
				});
777
				if (error) {
778
					this.hooks.failedModule.call(module, error);
779
					return callback(error);
780
				}
781
				this.hooks.succeedModule.call(module);
782
				return callback();
783
			}
784
		);
785
	}
786

    
787
	/**
788
	 * @param {Module} module to be processed for deps
789
	 * @param {ModuleCallback} callback callback to be triggered
790
	 * @returns {void}
791
	 */
792
	processModuleDependencies(module, callback) {
793
		const dependencies = new Map();
794

    
795
		const addDependency = dep => {
796
			const resourceIdent = dep.getResourceIdentifier();
797
			if (resourceIdent) {
798
				const factory = this.dependencyFactories.get(dep.constructor);
799
				if (factory === undefined) {
800
					throw new Error(
801
						`No module factory available for dependency type: ${dep.constructor.name}`
802
					);
803
				}
804
				let innerMap = dependencies.get(factory);
805
				if (innerMap === undefined) {
806
					dependencies.set(factory, (innerMap = new Map()));
807
				}
808
				let list = innerMap.get(resourceIdent);
809
				if (list === undefined) innerMap.set(resourceIdent, (list = []));
810
				list.push(dep);
811
			}
812
		};
813

    
814
		const addDependenciesBlock = block => {
815
			if (block.dependencies) {
816
				iterationOfArrayCallback(block.dependencies, addDependency);
817
			}
818
			if (block.blocks) {
819
				iterationOfArrayCallback(block.blocks, addDependenciesBlock);
820
			}
821
			if (block.variables) {
822
				iterationBlockVariable(block.variables, addDependency);
823
			}
824
		};
825

    
826
		try {
827
			addDependenciesBlock(module);
828
		} catch (e) {
829
			callback(e);
830
		}
831

    
832
		const sortedDependencies = [];
833

    
834
		for (const pair1 of dependencies) {
835
			for (const pair2 of pair1[1]) {
836
				sortedDependencies.push({
837
					factory: pair1[0],
838
					dependencies: pair2[1]
839
				});
840
			}
841
		}
842

    
843
		this.addModuleDependencies(
844
			module,
845
			sortedDependencies,
846
			this.bail,
847
			null,
848
			true,
849
			callback
850
		);
851
	}
852

    
853
	/**
854
	 * @param {Module} module module to add deps to
855
	 * @param {SortedDependency[]} dependencies set of sorted dependencies to iterate through
856
	 * @param {(boolean|null)=} bail whether to bail or not
857
	 * @param {TODO} cacheGroup optional cacheGroup
858
	 * @param {boolean} recursive whether it is recursive traversal
859
	 * @param {function} callback callback for when dependencies are finished being added
860
	 * @returns {void}
861
	 */
862
	addModuleDependencies(
863
		module,
864
		dependencies,
865
		bail,
866
		cacheGroup,
867
		recursive,
868
		callback
869
	) {
870
		const start = this.profile && Date.now();
871
		const currentProfile = this.profile && {};
872

    
873
		asyncLib.forEach(
874
			dependencies,
875
			(item, callback) => {
876
				const dependencies = item.dependencies;
877

    
878
				const errorAndCallback = err => {
879
					err.origin = module;
880
					err.dependencies = dependencies;
881
					this.errors.push(err);
882
					if (bail) {
883
						callback(err);
884
					} else {
885
						callback();
886
					}
887
				};
888
				const warningAndCallback = err => {
889
					err.origin = module;
890
					this.warnings.push(err);
891
					callback();
892
				};
893

    
894
				const semaphore = this.semaphore;
895
				semaphore.acquire(() => {
896
					const factory = item.factory;
897
					factory.create(
898
						{
899
							contextInfo: {
900
								issuer: module.nameForCondition && module.nameForCondition(),
901
								compiler: this.compiler.name
902
							},
903
							resolveOptions: module.resolveOptions,
904
							context: module.context,
905
							dependencies: dependencies
906
						},
907
						(err, dependentModule) => {
908
							let afterFactory;
909

    
910
							const isOptional = () => {
911
								return dependencies.every(d => d.optional);
912
							};
913

    
914
							const errorOrWarningAndCallback = err => {
915
								if (isOptional()) {
916
									return warningAndCallback(err);
917
								} else {
918
									return errorAndCallback(err);
919
								}
920
							};
921

    
922
							if (err) {
923
								semaphore.release();
924
								return errorOrWarningAndCallback(
925
									new ModuleNotFoundError(module, err)
926
								);
927
							}
928
							if (!dependentModule) {
929
								semaphore.release();
930
								return process.nextTick(callback);
931
							}
932
							if (currentProfile) {
933
								afterFactory = Date.now();
934
								currentProfile.factory = afterFactory - start;
935
							}
936

    
937
							const iterationDependencies = depend => {
938
								for (let index = 0; index < depend.length; index++) {
939
									const dep = depend[index];
940
									dep.module = dependentModule;
941
									dependentModule.addReason(module, dep);
942
								}
943
							};
944

    
945
							const addModuleResult = this.addModule(
946
								dependentModule,
947
								cacheGroup
948
							);
949
							dependentModule = addModuleResult.module;
950
							iterationDependencies(dependencies);
951

    
952
							const afterBuild = () => {
953
								if (recursive && addModuleResult.dependencies) {
954
									this.processModuleDependencies(dependentModule, callback);
955
								} else {
956
									return callback();
957
								}
958
							};
959

    
960
							if (addModuleResult.issuer) {
961
								if (currentProfile) {
962
									dependentModule.profile = currentProfile;
963
								}
964

    
965
								dependentModule.issuer = module;
966
							} else {
967
								if (this.profile) {
968
									if (module.profile) {
969
										const time = Date.now() - start;
970
										if (
971
											!module.profile.dependencies ||
972
											time > module.profile.dependencies
973
										) {
974
											module.profile.dependencies = time;
975
										}
976
									}
977
								}
978
							}
979

    
980
							if (addModuleResult.build) {
981
								this.buildModule(
982
									dependentModule,
983
									isOptional(),
984
									module,
985
									dependencies,
986
									err => {
987
										if (err) {
988
											semaphore.release();
989
											return errorOrWarningAndCallback(err);
990
										}
991

    
992
										if (currentProfile) {
993
											const afterBuilding = Date.now();
994
											currentProfile.building = afterBuilding - afterFactory;
995
										}
996

    
997
										semaphore.release();
998
										afterBuild();
999
									}
1000
								);
1001
							} else {
1002
								semaphore.release();
1003
								this.waitForBuildingFinished(dependentModule, afterBuild);
1004
							}
1005
						}
1006
					);
1007
				});
1008
			},
1009
			err => {
1010
				// In V8, the Error objects keep a reference to the functions on the stack. These warnings &
1011
				// errors are created inside closures that keep a reference to the Compilation, so errors are
1012
				// leaking the Compilation object.
1013

    
1014
				if (err) {
1015
					// eslint-disable-next-line no-self-assign
1016
					err.stack = err.stack;
1017
					return callback(err);
1018
				}
1019

    
1020
				return process.nextTick(callback);
1021
			}
1022
		);
1023
	}
1024

    
1025
	/**
1026
	 *
1027
	 * @param {string} context context string path
1028
	 * @param {Dependency} dependency dependency used to create Module chain
1029
	 * @param {OnModuleCallback} onModule function invoked on modules creation
1030
	 * @param {ModuleChainCallback} callback callback for when module chain is complete
1031
	 * @returns {void} will throw if dependency instance is not a valid Dependency
1032
	 */
1033
	_addModuleChain(context, dependency, onModule, callback) {
1034
		const start = this.profile && Date.now();
1035
		const currentProfile = this.profile && {};
1036

    
1037
		const errorAndCallback = this.bail
1038
			? err => {
1039
					callback(err);
1040
			  }
1041
			: err => {
1042
					err.dependencies = [dependency];
1043
					this.errors.push(err);
1044
					callback();
1045
			  };
1046

    
1047
		if (
1048
			typeof dependency !== "object" ||
1049
			dependency === null ||
1050
			!dependency.constructor
1051
		) {
1052
			throw new Error("Parameter 'dependency' must be a Dependency");
1053
		}
1054
		const Dep = /** @type {DepConstructor} */ (dependency.constructor);
1055
		const moduleFactory = this.dependencyFactories.get(Dep);
1056
		if (!moduleFactory) {
1057
			throw new Error(
1058
				`No dependency factory available for this dependency type: ${dependency.constructor.name}`
1059
			);
1060
		}
1061

    
1062
		this.semaphore.acquire(() => {
1063
			moduleFactory.create(
1064
				{
1065
					contextInfo: {
1066
						issuer: "",
1067
						compiler: this.compiler.name
1068
					},
1069
					context: context,
1070
					dependencies: [dependency]
1071
				},
1072
				(err, module) => {
1073
					if (err) {
1074
						this.semaphore.release();
1075
						return errorAndCallback(new EntryModuleNotFoundError(err));
1076
					}
1077

    
1078
					let afterFactory;
1079

    
1080
					if (currentProfile) {
1081
						afterFactory = Date.now();
1082
						currentProfile.factory = afterFactory - start;
1083
					}
1084

    
1085
					const addModuleResult = this.addModule(module);
1086
					module = addModuleResult.module;
1087

    
1088
					onModule(module);
1089

    
1090
					dependency.module = module;
1091
					module.addReason(null, dependency);
1092

    
1093
					const afterBuild = () => {
1094
						if (addModuleResult.dependencies) {
1095
							this.processModuleDependencies(module, err => {
1096
								if (err) return callback(err);
1097
								callback(null, module);
1098
							});
1099
						} else {
1100
							return callback(null, module);
1101
						}
1102
					};
1103

    
1104
					if (addModuleResult.issuer) {
1105
						if (currentProfile) {
1106
							module.profile = currentProfile;
1107
						}
1108
					}
1109

    
1110
					if (addModuleResult.build) {
1111
						this.buildModule(module, false, null, null, err => {
1112
							if (err) {
1113
								this.semaphore.release();
1114
								return errorAndCallback(err);
1115
							}
1116

    
1117
							if (currentProfile) {
1118
								const afterBuilding = Date.now();
1119
								currentProfile.building = afterBuilding - afterFactory;
1120
							}
1121

    
1122
							this.semaphore.release();
1123
							afterBuild();
1124
						});
1125
					} else {
1126
						this.semaphore.release();
1127
						this.waitForBuildingFinished(module, afterBuild);
1128
					}
1129
				}
1130
			);
1131
		});
1132
	}
1133

    
1134
	/**
1135
	 *
1136
	 * @param {string} context context path for entry
1137
	 * @param {Dependency} entry entry dependency being created
1138
	 * @param {string} name name of entry
1139
	 * @param {ModuleCallback} callback callback function
1140
	 * @returns {void} returns
1141
	 */
1142
	addEntry(context, entry, name, callback) {
1143
		this.hooks.addEntry.call(entry, name);
1144

    
1145
		const slot = {
1146
			name: name,
1147
			// TODO webpack 5 remove `request`
1148
			request: null,
1149
			module: null
1150
		};
1151

    
1152
		if (entry instanceof ModuleDependency) {
1153
			slot.request = entry.request;
1154
		}
1155

    
1156
		// TODO webpack 5: merge modules instead when multiple entry modules are supported
1157
		const idx = this._preparedEntrypoints.findIndex(slot => slot.name === name);
1158
		if (idx >= 0) {
1159
			// Overwrite existing entrypoint
1160
			this._preparedEntrypoints[idx] = slot;
1161
		} else {
1162
			this._preparedEntrypoints.push(slot);
1163
		}
1164
		this._addModuleChain(
1165
			context,
1166
			entry,
1167
			module => {
1168
				this.entries.push(module);
1169
			},
1170
			(err, module) => {
1171
				if (err) {
1172
					this.hooks.failedEntry.call(entry, name, err);
1173
					return callback(err);
1174
				}
1175

    
1176
				if (module) {
1177
					slot.module = module;
1178
				} else {
1179
					const idx = this._preparedEntrypoints.indexOf(slot);
1180
					if (idx >= 0) {
1181
						this._preparedEntrypoints.splice(idx, 1);
1182
					}
1183
				}
1184
				this.hooks.succeedEntry.call(entry, name, module);
1185
				return callback(null, module);
1186
			}
1187
		);
1188
	}
1189

    
1190
	/**
1191
	 * @param {string} context context path string
1192
	 * @param {Dependency} dependency dep used to create module
1193
	 * @param {ModuleCallback} callback module callback sending module up a level
1194
	 * @returns {void}
1195
	 */
1196
	prefetch(context, dependency, callback) {
1197
		this._addModuleChain(
1198
			context,
1199
			dependency,
1200
			module => {
1201
				module.prefetched = true;
1202
			},
1203
			callback
1204
		);
1205
	}
1206

    
1207
	/**
1208
	 * @param {Module} module module to be rebuilt
1209
	 * @param {Callback} thisCallback callback when module finishes rebuilding
1210
	 * @returns {void}
1211
	 */
1212
	rebuildModule(module, thisCallback) {
1213
		let callbackList = this._rebuildingModules.get(module);
1214
		if (callbackList) {
1215
			callbackList.push(thisCallback);
1216
			return;
1217
		}
1218
		this._rebuildingModules.set(module, (callbackList = [thisCallback]));
1219

    
1220
		const callback = err => {
1221
			this._rebuildingModules.delete(module);
1222
			for (const cb of callbackList) {
1223
				cb(err);
1224
			}
1225
		};
1226

    
1227
		this.hooks.rebuildModule.call(module);
1228
		const oldDependencies = module.dependencies.slice();
1229
		const oldVariables = module.variables.slice();
1230
		const oldBlocks = module.blocks.slice();
1231
		module.unbuild();
1232
		this.buildModule(module, false, module, null, err => {
1233
			if (err) {
1234
				this.hooks.finishRebuildingModule.call(module);
1235
				return callback(err);
1236
			}
1237

    
1238
			this.processModuleDependencies(module, err => {
1239
				if (err) return callback(err);
1240
				this.removeReasonsOfDependencyBlock(module, {
1241
					dependencies: oldDependencies,
1242
					variables: oldVariables,
1243
					blocks: oldBlocks
1244
				});
1245
				this.hooks.finishRebuildingModule.call(module);
1246
				callback();
1247
			});
1248
		});
1249
	}
1250

    
1251
	finish(callback) {
1252
		const modules = this.modules;
1253
		this.hooks.finishModules.callAsync(modules, err => {
1254
			if (err) return callback(err);
1255

    
1256
			for (let index = 0; index < modules.length; index++) {
1257
				const module = modules[index];
1258
				this.reportDependencyErrorsAndWarnings(module, [module]);
1259
			}
1260

    
1261
			callback();
1262
		});
1263
	}
1264

    
1265
	unseal() {
1266
		this.hooks.unseal.call();
1267
		this.chunks.length = 0;
1268
		this.chunkGroups.length = 0;
1269
		this.namedChunks.clear();
1270
		this.namedChunkGroups.clear();
1271
		this.additionalChunkAssets.length = 0;
1272
		this.assets = {};
1273
		this.assetsInfo.clear();
1274
		for (const module of this.modules) {
1275
			module.unseal();
1276
		}
1277
	}
1278

    
1279
	/**
1280
	 * @param {Callback} callback signals when the seal method is finishes
1281
	 * @returns {void}
1282
	 */
1283
	seal(callback) {
1284
		this.hooks.seal.call();
1285

    
1286
		while (
1287
			this.hooks.optimizeDependenciesBasic.call(this.modules) ||
1288
			this.hooks.optimizeDependencies.call(this.modules) ||
1289
			this.hooks.optimizeDependenciesAdvanced.call(this.modules)
1290
		) {
1291
			/* empty */
1292
		}
1293
		this.hooks.afterOptimizeDependencies.call(this.modules);
1294

    
1295
		this.hooks.beforeChunks.call();
1296
		for (const preparedEntrypoint of this._preparedEntrypoints) {
1297
			const module = preparedEntrypoint.module;
1298
			const name = preparedEntrypoint.name;
1299
			const chunk = this.addChunk(name);
1300
			const entrypoint = new Entrypoint(name);
1301
			entrypoint.setRuntimeChunk(chunk);
1302
			entrypoint.addOrigin(null, name, preparedEntrypoint.request);
1303
			this.namedChunkGroups.set(name, entrypoint);
1304
			this.entrypoints.set(name, entrypoint);
1305
			this.chunkGroups.push(entrypoint);
1306

    
1307
			GraphHelpers.connectChunkGroupAndChunk(entrypoint, chunk);
1308
			GraphHelpers.connectChunkAndModule(chunk, module);
1309

    
1310
			chunk.entryModule = module;
1311
			chunk.name = name;
1312

    
1313
			this.assignDepth(module);
1314
		}
1315
		buildChunkGraph(
1316
			this,
1317
			/** @type {Entrypoint[]} */ (this.chunkGroups.slice())
1318
		);
1319
		this.sortModules(this.modules);
1320
		this.hooks.afterChunks.call(this.chunks);
1321

    
1322
		this.hooks.optimize.call();
1323

    
1324
		while (
1325
			this.hooks.optimizeModulesBasic.call(this.modules) ||
1326
			this.hooks.optimizeModules.call(this.modules) ||
1327
			this.hooks.optimizeModulesAdvanced.call(this.modules)
1328
		) {
1329
			/* empty */
1330
		}
1331
		this.hooks.afterOptimizeModules.call(this.modules);
1332

    
1333
		while (
1334
			this.hooks.optimizeChunksBasic.call(this.chunks, this.chunkGroups) ||
1335
			this.hooks.optimizeChunks.call(this.chunks, this.chunkGroups) ||
1336
			this.hooks.optimizeChunksAdvanced.call(this.chunks, this.chunkGroups)
1337
		) {
1338
			/* empty */
1339
		}
1340
		this.hooks.afterOptimizeChunks.call(this.chunks, this.chunkGroups);
1341

    
1342
		this.hooks.optimizeTree.callAsync(this.chunks, this.modules, err => {
1343
			if (err) {
1344
				return callback(err);
1345
			}
1346

    
1347
			this.hooks.afterOptimizeTree.call(this.chunks, this.modules);
1348

    
1349
			while (
1350
				this.hooks.optimizeChunkModulesBasic.call(this.chunks, this.modules) ||
1351
				this.hooks.optimizeChunkModules.call(this.chunks, this.modules) ||
1352
				this.hooks.optimizeChunkModulesAdvanced.call(this.chunks, this.modules)
1353
			) {
1354
				/* empty */
1355
			}
1356
			this.hooks.afterOptimizeChunkModules.call(this.chunks, this.modules);
1357

    
1358
			const shouldRecord = this.hooks.shouldRecord.call() !== false;
1359

    
1360
			this.hooks.reviveModules.call(this.modules, this.records);
1361
			this.hooks.optimizeModuleOrder.call(this.modules);
1362
			this.hooks.advancedOptimizeModuleOrder.call(this.modules);
1363
			this.hooks.beforeModuleIds.call(this.modules);
1364
			this.hooks.moduleIds.call(this.modules);
1365
			this.applyModuleIds();
1366
			this.hooks.optimizeModuleIds.call(this.modules);
1367
			this.hooks.afterOptimizeModuleIds.call(this.modules);
1368

    
1369
			this.sortItemsWithModuleIds();
1370

    
1371
			this.hooks.reviveChunks.call(this.chunks, this.records);
1372
			this.hooks.optimizeChunkOrder.call(this.chunks);
1373
			this.hooks.beforeChunkIds.call(this.chunks);
1374
			this.applyChunkIds();
1375
			this.hooks.optimizeChunkIds.call(this.chunks);
1376
			this.hooks.afterOptimizeChunkIds.call(this.chunks);
1377

    
1378
			this.sortItemsWithChunkIds();
1379

    
1380
			if (shouldRecord) {
1381
				this.hooks.recordModules.call(this.modules, this.records);
1382
				this.hooks.recordChunks.call(this.chunks, this.records);
1383
			}
1384

    
1385
			this.hooks.beforeHash.call();
1386
			this.createHash();
1387
			this.hooks.afterHash.call();
1388

    
1389
			if (shouldRecord) {
1390
				this.hooks.recordHash.call(this.records);
1391
			}
1392

    
1393
			this.hooks.beforeModuleAssets.call();
1394
			this.createModuleAssets();
1395
			if (this.hooks.shouldGenerateChunkAssets.call() !== false) {
1396
				this.hooks.beforeChunkAssets.call();
1397
				this.createChunkAssets();
1398
			}
1399
			this.hooks.additionalChunkAssets.call(this.chunks);
1400
			this.summarizeDependencies();
1401
			if (shouldRecord) {
1402
				this.hooks.record.call(this, this.records);
1403
			}
1404

    
1405
			this.hooks.additionalAssets.callAsync(err => {
1406
				if (err) {
1407
					return callback(err);
1408
				}
1409
				this.hooks.optimizeChunkAssets.callAsync(this.chunks, err => {
1410
					if (err) {
1411
						return callback(err);
1412
					}
1413
					this.hooks.afterOptimizeChunkAssets.call(this.chunks);
1414
					this.hooks.optimizeAssets.callAsync(this.assets, err => {
1415
						if (err) {
1416
							return callback(err);
1417
						}
1418
						this.hooks.afterOptimizeAssets.call(this.assets);
1419
						if (this.hooks.needAdditionalSeal.call()) {
1420
							this.unseal();
1421
							return this.seal(callback);
1422
						}
1423
						return this.hooks.afterSeal.callAsync(callback);
1424
					});
1425
				});
1426
			});
1427
		});
1428
	}
1429

    
1430
	/**
1431
	 * @param {Module[]} modules the modules array on compilation to perform the sort for
1432
	 * @returns {void}
1433
	 */
1434
	sortModules(modules) {
1435
		// TODO webpack 5: this should only be enabled when `moduleIds: "natural"`
1436
		// TODO move it into a plugin (NaturalModuleIdsPlugin) and use this in WebpackOptionsApply
1437
		// TODO remove this method
1438
		modules.sort(byIndexOrIdentifier);
1439
	}
1440

    
1441
	/**
1442
	 * @param {Module} module moulde to report from
1443
	 * @param {DependenciesBlock[]} blocks blocks to report from
1444
	 * @returns {void}
1445
	 */
1446
	reportDependencyErrorsAndWarnings(module, blocks) {
1447
		for (let indexBlock = 0; indexBlock < blocks.length; indexBlock++) {
1448
			const block = blocks[indexBlock];
1449
			const dependencies = block.dependencies;
1450

    
1451
			for (let indexDep = 0; indexDep < dependencies.length; indexDep++) {
1452
				const d = dependencies[indexDep];
1453

    
1454
				const warnings = d.getWarnings();
1455
				if (warnings) {
1456
					for (let indexWar = 0; indexWar < warnings.length; indexWar++) {
1457
						const w = warnings[indexWar];
1458

    
1459
						const warning = new ModuleDependencyWarning(module, w, d.loc);
1460
						this.warnings.push(warning);
1461
					}
1462
				}
1463
				const errors = d.getErrors();
1464
				if (errors) {
1465
					for (let indexErr = 0; indexErr < errors.length; indexErr++) {
1466
						const e = errors[indexErr];
1467

    
1468
						const error = new ModuleDependencyError(module, e, d.loc);
1469
						this.errors.push(error);
1470
					}
1471
				}
1472
			}
1473

    
1474
			this.reportDependencyErrorsAndWarnings(module, block.blocks);
1475
		}
1476
	}
1477

    
1478
	/**
1479
	 * @param {TODO} groupOptions options for the chunk group
1480
	 * @param {Module} module the module the references the chunk group
1481
	 * @param {DependencyLocation} loc the location from with the chunk group is referenced (inside of module)
1482
	 * @param {string} request the request from which the the chunk group is referenced
1483
	 * @returns {ChunkGroup} the new or existing chunk group
1484
	 */
1485
	addChunkInGroup(groupOptions, module, loc, request) {
1486
		if (typeof groupOptions === "string") {
1487
			groupOptions = { name: groupOptions };
1488
		}
1489
		const name = groupOptions.name;
1490
		if (name) {
1491
			const chunkGroup = this.namedChunkGroups.get(name);
1492
			if (chunkGroup !== undefined) {
1493
				chunkGroup.addOptions(groupOptions);
1494
				if (module) {
1495
					chunkGroup.addOrigin(module, loc, request);
1496
				}
1497
				return chunkGroup;
1498
			}
1499
		}
1500
		const chunkGroup = new ChunkGroup(groupOptions);
1501
		if (module) chunkGroup.addOrigin(module, loc, request);
1502
		const chunk = this.addChunk(name);
1503

    
1504
		GraphHelpers.connectChunkGroupAndChunk(chunkGroup, chunk);
1505

    
1506
		this.chunkGroups.push(chunkGroup);
1507
		if (name) {
1508
			this.namedChunkGroups.set(name, chunkGroup);
1509
		}
1510
		return chunkGroup;
1511
	}
1512

    
1513
	/**
1514
	 * This method first looks to see if a name is provided for a new chunk,
1515
	 * and first looks to see if any named chunks already exist and reuse that chunk instead.
1516
	 *
1517
	 * @param {string=} name optional chunk name to be provided
1518
	 * @returns {Chunk} create a chunk (invoked during seal event)
1519
	 */
1520
	addChunk(name) {
1521
		if (name) {
1522
			const chunk = this.namedChunks.get(name);
1523
			if (chunk !== undefined) {
1524
				return chunk;
1525
			}
1526
		}
1527
		const chunk = new Chunk(name);
1528
		this.chunks.push(chunk);
1529
		if (name) {
1530
			this.namedChunks.set(name, chunk);
1531
		}
1532
		return chunk;
1533
	}
1534

    
1535
	/**
1536
	 * @param {Module} module module to assign depth
1537
	 * @returns {void}
1538
	 */
1539
	assignDepth(module) {
1540
		const queue = new Set([module]);
1541
		let depth;
1542

    
1543
		module.depth = 0;
1544

    
1545
		/**
1546
		 * @param {Module} module module for processeing
1547
		 * @returns {void}
1548
		 */
1549
		const enqueueJob = module => {
1550
			const d = module.depth;
1551
			if (typeof d === "number" && d <= depth) return;
1552
			queue.add(module);
1553
			module.depth = depth;
1554
		};
1555

    
1556
		/**
1557
		 * @param {Dependency} dependency dependency to assign depth to
1558
		 * @returns {void}
1559
		 */
1560
		const assignDepthToDependency = dependency => {
1561
			if (dependency.module) {
1562
				enqueueJob(dependency.module);
1563
			}
1564
		};
1565

    
1566
		/**
1567
		 * @param {DependenciesBlock} block block to assign depth to
1568
		 * @returns {void}
1569
		 */
1570
		const assignDepthToDependencyBlock = block => {
1571
			if (block.variables) {
1572
				iterationBlockVariable(block.variables, assignDepthToDependency);
1573
			}
1574

    
1575
			if (block.dependencies) {
1576
				iterationOfArrayCallback(block.dependencies, assignDepthToDependency);
1577
			}
1578

    
1579
			if (block.blocks) {
1580
				iterationOfArrayCallback(block.blocks, assignDepthToDependencyBlock);
1581
			}
1582
		};
1583

    
1584
		for (module of queue) {
1585
			queue.delete(module);
1586
			depth = module.depth;
1587

    
1588
			depth++;
1589
			assignDepthToDependencyBlock(module);
1590
		}
1591
	}
1592

    
1593
	/**
1594
	 * @param {Module} module the module containing the dependency
1595
	 * @param {Dependency} dependency the dependency
1596
	 * @returns {DependencyReference} a reference for the dependency
1597
	 */
1598
	getDependencyReference(module, dependency) {
1599
		// TODO remove dep.getReference existence check in webpack 5
1600
		if (typeof dependency.getReference !== "function") return null;
1601
		const ref = dependency.getReference();
1602
		if (!ref) return null;
1603
		return this.hooks.dependencyReference.call(ref, dependency, module);
1604
	}
1605

    
1606
	/**
1607
	 *
1608
	 * @param {Module} module module relationship for removal
1609
	 * @param {DependenciesBlockLike} block //TODO: good description
1610
	 * @returns {void}
1611
	 */
1612
	removeReasonsOfDependencyBlock(module, block) {
1613
		const iteratorDependency = d => {
1614
			if (!d.module) {
1615
				return;
1616
			}
1617
			if (d.module.removeReason(module, d)) {
1618
				for (const chunk of d.module.chunksIterable) {
1619
					this.patchChunksAfterReasonRemoval(d.module, chunk);
1620
				}
1621
			}
1622
		};
1623

    
1624
		if (block.blocks) {
1625
			iterationOfArrayCallback(block.blocks, block =>
1626
				this.removeReasonsOfDependencyBlock(module, block)
1627
			);
1628
		}
1629

    
1630
		if (block.dependencies) {
1631
			iterationOfArrayCallback(block.dependencies, iteratorDependency);
1632
		}
1633

    
1634
		if (block.variables) {
1635
			iterationBlockVariable(block.variables, iteratorDependency);
1636
		}
1637
	}
1638

    
1639
	/**
1640
	 * @param {Module} module module to patch tie
1641
	 * @param {Chunk} chunk chunk to patch tie
1642
	 * @returns {void}
1643
	 */
1644
	patchChunksAfterReasonRemoval(module, chunk) {
1645
		if (!module.hasReasons()) {
1646
			this.removeReasonsOfDependencyBlock(module, module);
1647
		}
1648
		if (!module.hasReasonForChunk(chunk)) {
1649
			if (module.removeChunk(chunk)) {
1650
				this.removeChunkFromDependencies(module, chunk);
1651
			}
1652
		}
1653
	}
1654

    
1655
	/**
1656
	 *
1657
	 * @param {DependenciesBlock} block block tie for Chunk
1658
	 * @param {Chunk} chunk chunk to remove from dep
1659
	 * @returns {void}
1660
	 */
1661
	removeChunkFromDependencies(block, chunk) {
1662
		const iteratorDependency = d => {
1663
			if (!d.module) {
1664
				return;
1665
			}
1666
			this.patchChunksAfterReasonRemoval(d.module, chunk);
1667
		};
1668

    
1669
		const blocks = block.blocks;
1670
		for (let indexBlock = 0; indexBlock < blocks.length; indexBlock++) {
1671
			const asyncBlock = blocks[indexBlock];
1672
			// Grab all chunks from the first Block's AsyncDepBlock
1673
			const chunks = asyncBlock.chunkGroup.chunks;
1674
			// For each chunk in chunkGroup
1675
			for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
1676
				const iteratedChunk = chunks[indexChunk];
1677
				asyncBlock.chunkGroup.removeChunk(iteratedChunk);
1678
				asyncBlock.chunkGroup.removeParent(iteratedChunk);
1679
				// Recurse
1680
				this.removeChunkFromDependencies(block, iteratedChunk);
1681
			}
1682
		}
1683

    
1684
		if (block.dependencies) {
1685
			iterationOfArrayCallback(block.dependencies, iteratorDependency);
1686
		}
1687

    
1688
		if (block.variables) {
1689
			iterationBlockVariable(block.variables, iteratorDependency);
1690
		}
1691
	}
1692

    
1693
	applyModuleIds() {
1694
		const unusedIds = [];
1695
		let nextFreeModuleId = 0;
1696
		const usedIds = new Set();
1697
		if (this.usedModuleIds) {
1698
			for (const id of this.usedModuleIds) {
1699
				usedIds.add(id);
1700
			}
1701
		}
1702

    
1703
		const modules1 = this.modules;
1704
		for (let indexModule1 = 0; indexModule1 < modules1.length; indexModule1++) {
1705
			const module1 = modules1[indexModule1];
1706
			if (module1.id !== null) {
1707
				usedIds.add(module1.id);
1708
			}
1709
		}
1710

    
1711
		if (usedIds.size > 0) {
1712
			let usedIdMax = -1;
1713
			for (const usedIdKey of usedIds) {
1714
				if (typeof usedIdKey !== "number") {
1715
					continue;
1716
				}
1717

    
1718
				usedIdMax = Math.max(usedIdMax, usedIdKey);
1719
			}
1720

    
1721
			let lengthFreeModules = (nextFreeModuleId = usedIdMax + 1);
1722

    
1723
			while (lengthFreeModules--) {
1724
				if (!usedIds.has(lengthFreeModules)) {
1725
					unusedIds.push(lengthFreeModules);
1726
				}
1727
			}
1728
		}
1729

    
1730
		const modules2 = this.modules;
1731
		for (let indexModule2 = 0; indexModule2 < modules2.length; indexModule2++) {
1732
			const module2 = modules2[indexModule2];
1733
			if (module2.id === null) {
1734
				if (unusedIds.length > 0) {
1735
					module2.id = unusedIds.pop();
1736
				} else {
1737
					module2.id = nextFreeModuleId++;
1738
				}
1739
			}
1740
		}
1741
	}
1742

    
1743
	applyChunkIds() {
1744
		/** @type {Set<number>} */
1745
		const usedIds = new Set();
1746

    
1747
		// Get used ids from usedChunkIds property (i. e. from records)
1748
		if (this.usedChunkIds) {
1749
			for (const id of this.usedChunkIds) {
1750
				if (typeof id !== "number") {
1751
					continue;
1752
				}
1753

    
1754
				usedIds.add(id);
1755
			}
1756
		}
1757

    
1758
		// Get used ids from existing chunks
1759
		const chunks = this.chunks;
1760
		for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
1761
			const chunk = chunks[indexChunk];
1762
			const usedIdValue = chunk.id;
1763

    
1764
			if (typeof usedIdValue !== "number") {
1765
				continue;
1766
			}
1767

    
1768
			usedIds.add(usedIdValue);
1769
		}
1770

    
1771
		// Calculate maximum assigned chunk id
1772
		let nextFreeChunkId = -1;
1773
		for (const id of usedIds) {
1774
			nextFreeChunkId = Math.max(nextFreeChunkId, id);
1775
		}
1776
		nextFreeChunkId++;
1777

    
1778
		// Determine free chunk ids from 0 to maximum
1779
		/** @type {number[]} */
1780
		const unusedIds = [];
1781
		if (nextFreeChunkId > 0) {
1782
			let index = nextFreeChunkId;
1783
			while (index--) {
1784
				if (!usedIds.has(index)) {
1785
					unusedIds.push(index);
1786
				}
1787
			}
1788
		}
1789

    
1790
		// Assign ids to chunk which has no id
1791
		for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
1792
			const chunk = chunks[indexChunk];
1793
			if (chunk.id === null) {
1794
				if (unusedIds.length > 0) {
1795
					chunk.id = unusedIds.pop();
1796
				} else {
1797
					chunk.id = nextFreeChunkId++;
1798
				}
1799
			}
1800
			if (!chunk.ids) {
1801
				chunk.ids = [chunk.id];
1802
			}
1803
		}
1804
	}
1805

    
1806
	sortItemsWithModuleIds() {
1807
		this.modules.sort(byIdOrIdentifier);
1808

    
1809
		const modules = this.modules;
1810
		for (let indexModule = 0; indexModule < modules.length; indexModule++) {
1811
			modules[indexModule].sortItems(false);
1812
		}
1813

    
1814
		const chunks = this.chunks;
1815
		for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
1816
			chunks[indexChunk].sortItems();
1817
		}
1818

    
1819
		chunks.sort((a, b) => a.compareTo(b));
1820
	}
1821

    
1822
	sortItemsWithChunkIds() {
1823
		for (const chunkGroup of this.chunkGroups) {
1824
			chunkGroup.sortItems();
1825
		}
1826

    
1827
		this.chunks.sort(byId);
1828

    
1829
		for (
1830
			let indexModule = 0;
1831
			indexModule < this.modules.length;
1832
			indexModule++
1833
		) {
1834
			this.modules[indexModule].sortItems(true);
1835
		}
1836

    
1837
		const chunks = this.chunks;
1838
		for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
1839
			chunks[indexChunk].sortItems();
1840
		}
1841

    
1842
		/**
1843
		 * Used to sort errors and warnings in compilation. this.warnings, and
1844
		 * this.errors contribute to the compilation hash and therefore should be
1845
		 * updated whenever other references (having a chunk id) are sorted. This preserves the hash
1846
		 * integrity
1847
		 *
1848
		 * @param {WebpackError} a first WebpackError instance (including subclasses)
1849
		 * @param {WebpackError} b second WebpackError instance (including subclasses)
1850
		 * @returns {-1|0|1} sort order index
1851
		 */
1852
		const byMessage = (a, b) => {
1853
			const ma = `${a.message}`;
1854
			const mb = `${b.message}`;
1855
			if (ma < mb) return -1;
1856
			if (mb < ma) return 1;
1857
			return 0;
1858
		};
1859

    
1860
		this.errors.sort(byMessage);
1861
		this.warnings.sort(byMessage);
1862
		this.children.sort(byNameOrHash);
1863
	}
1864

    
1865
	summarizeDependencies() {
1866
		this.fileDependencies = new SortableSet(this.compilationDependencies);
1867
		this.contextDependencies = new SortableSet();
1868
		this.missingDependencies = new SortableSet();
1869

    
1870
		for (
1871
			let indexChildren = 0;
1872
			indexChildren < this.children.length;
1873
			indexChildren++
1874
		) {
1875
			const child = this.children[indexChildren];
1876

    
1877
			addAllToSet(this.fileDependencies, child.fileDependencies);
1878
			addAllToSet(this.contextDependencies, child.contextDependencies);
1879
			addAllToSet(this.missingDependencies, child.missingDependencies);
1880
		}
1881

    
1882
		for (
1883
			let indexModule = 0;
1884
			indexModule < this.modules.length;
1885
			indexModule++
1886
		) {
1887
			const module = this.modules[indexModule];
1888

    
1889
			if (module.buildInfo.fileDependencies) {
1890
				addAllToSet(this.fileDependencies, module.buildInfo.fileDependencies);
1891
			}
1892
			if (module.buildInfo.contextDependencies) {
1893
				addAllToSet(
1894
					this.contextDependencies,
1895
					module.buildInfo.contextDependencies
1896
				);
1897
			}
1898
		}
1899
		for (const error of this.errors) {
1900
			if (
1901
				typeof error.missing === "object" &&
1902
				error.missing &&
1903
				error.missing[Symbol.iterator]
1904
			) {
1905
				addAllToSet(this.missingDependencies, error.missing);
1906
			}
1907
		}
1908
		this.fileDependencies.sort();
1909
		this.contextDependencies.sort();
1910
		this.missingDependencies.sort();
1911
	}
1912

    
1913
	createHash() {
1914
		const outputOptions = this.outputOptions;
1915
		const hashFunction = outputOptions.hashFunction;
1916
		const hashDigest = outputOptions.hashDigest;
1917
		const hashDigestLength = outputOptions.hashDigestLength;
1918
		const hash = createHash(hashFunction);
1919
		if (outputOptions.hashSalt) {
1920
			hash.update(outputOptions.hashSalt);
1921
		}
1922
		this.mainTemplate.updateHash(hash);
1923
		this.chunkTemplate.updateHash(hash);
1924
		for (const key of Object.keys(this.moduleTemplates).sort()) {
1925
			this.moduleTemplates[key].updateHash(hash);
1926
		}
1927
		for (const child of this.children) {
1928
			hash.update(child.hash);
1929
		}
1930
		for (const warning of this.warnings) {
1931
			hash.update(`${warning.message}`);
1932
		}
1933
		for (const error of this.errors) {
1934
			hash.update(`${error.message}`);
1935
		}
1936
		const modules = this.modules;
1937
		for (let i = 0; i < modules.length; i++) {
1938
			const module = modules[i];
1939
			const moduleHash = createHash(hashFunction);
1940
			module.updateHash(moduleHash);
1941
			module.hash = /** @type {string} */ (moduleHash.digest(hashDigest));
1942
			module.renderedHash = module.hash.substr(0, hashDigestLength);
1943
		}
1944
		// clone needed as sort below is inplace mutation
1945
		const chunks = this.chunks.slice();
1946
		/**
1947
		 * sort here will bring all "falsy" values to the beginning
1948
		 * this is needed as the "hasRuntime()" chunks are dependent on the
1949
		 * hashes of the non-runtime chunks.
1950
		 */
1951
		chunks.sort((a, b) => {
1952
			const aEntry = a.hasRuntime();
1953
			const bEntry = b.hasRuntime();
1954
			if (aEntry && !bEntry) return 1;
1955
			if (!aEntry && bEntry) return -1;
1956
			return byId(a, b);
1957
		});
1958
		for (let i = 0; i < chunks.length; i++) {
1959
			const chunk = chunks[i];
1960
			const chunkHash = createHash(hashFunction);
1961
			try {
1962
				if (outputOptions.hashSalt) {
1963
					chunkHash.update(outputOptions.hashSalt);
1964
				}
1965
				chunk.updateHash(chunkHash);
1966
				const template = chunk.hasRuntime()
1967
					? this.mainTemplate
1968
					: this.chunkTemplate;
1969
				template.updateHashForChunk(
1970
					chunkHash,
1971
					chunk,
1972
					this.moduleTemplates.javascript,
1973
					this.dependencyTemplates
1974
				);
1975
				this.hooks.chunkHash.call(chunk, chunkHash);
1976
				chunk.hash = /** @type {string} */ (chunkHash.digest(hashDigest));
1977
				hash.update(chunk.hash);
1978
				chunk.renderedHash = chunk.hash.substr(0, hashDigestLength);
1979
				this.hooks.contentHash.call(chunk);
1980
			} catch (err) {
1981
				this.errors.push(new ChunkRenderError(chunk, "", err));
1982
			}
1983
		}
1984
		this.fullHash = /** @type {string} */ (hash.digest(hashDigest));
1985
		this.hash = this.fullHash.substr(0, hashDigestLength);
1986
	}
1987

    
1988
	/**
1989
	 * @param {string} update extra information
1990
	 * @returns {void}
1991
	 */
1992
	modifyHash(update) {
1993
		const outputOptions = this.outputOptions;
1994
		const hashFunction = outputOptions.hashFunction;
1995
		const hashDigest = outputOptions.hashDigest;
1996
		const hashDigestLength = outputOptions.hashDigestLength;
1997
		const hash = createHash(hashFunction);
1998
		hash.update(this.fullHash);
1999
		hash.update(update);
2000
		this.fullHash = /** @type {string} */ (hash.digest(hashDigest));
2001
		this.hash = this.fullHash.substr(0, hashDigestLength);
2002
	}
2003

    
2004
	/**
2005
	 * @param {string} file file name
2006
	 * @param {Source} source asset source
2007
	 * @param {AssetInfo} assetInfo extra asset information
2008
	 * @returns {void}
2009
	 */
2010
	emitAsset(file, source, assetInfo = {}) {
2011
		if (this.assets[file]) {
2012
			if (!isSourceEqual(this.assets[file], source)) {
2013
				// TODO webpack 5: make this an error instead
2014
				this.warnings.push(
2015
					new WebpackError(
2016
						`Conflict: Multiple assets emit different content to the same filename ${file}`
2017
					)
2018
				);
2019
				this.assets[file] = source;
2020
				this.assetsInfo.set(file, assetInfo);
2021
				return;
2022
			}
2023
			const oldInfo = this.assetsInfo.get(file);
2024
			this.assetsInfo.set(file, Object.assign({}, oldInfo, assetInfo));
2025
			return;
2026
		}
2027
		this.assets[file] = source;
2028
		this.assetsInfo.set(file, assetInfo);
2029
	}
2030

    
2031
	/**
2032
	 * @param {string} file file name
2033
	 * @param {Source | function(Source): Source} newSourceOrFunction new asset source or function converting old to new
2034
	 * @param {AssetInfo | function(AssetInfo | undefined): AssetInfo} assetInfoUpdateOrFunction new asset info or function converting old to new
2035
	 */
2036
	updateAsset(
2037
		file,
2038
		newSourceOrFunction,
2039
		assetInfoUpdateOrFunction = undefined
2040
	) {
2041
		if (!this.assets[file]) {
2042
			throw new Error(
2043
				`Called Compilation.updateAsset for not existing filename ${file}`
2044
			);
2045
		}
2046
		if (typeof newSourceOrFunction === "function") {
2047
			this.assets[file] = newSourceOrFunction(this.assets[file]);
2048
		} else {
2049
			this.assets[file] = newSourceOrFunction;
2050
		}
2051
		if (assetInfoUpdateOrFunction !== undefined) {
2052
			const oldInfo = this.assetsInfo.get(file);
2053
			if (typeof assetInfoUpdateOrFunction === "function") {
2054
				this.assetsInfo.set(file, assetInfoUpdateOrFunction(oldInfo || {}));
2055
			} else {
2056
				this.assetsInfo.set(
2057
					file,
2058
					Object.assign({}, oldInfo, assetInfoUpdateOrFunction)
2059
				);
2060
			}
2061
		}
2062
	}
2063

    
2064
	getAssets() {
2065
		/** @type {Asset[]} */
2066
		const array = [];
2067
		for (const assetName of Object.keys(this.assets)) {
2068
			if (Object.prototype.hasOwnProperty.call(this.assets, assetName)) {
2069
				array.push({
2070
					name: assetName,
2071
					source: this.assets[assetName],
2072
					info: this.assetsInfo.get(assetName) || {}
2073
				});
2074
			}
2075
		}
2076
		return array;
2077
	}
2078

    
2079
	/**
2080
	 * @param {string} name the name of the asset
2081
	 * @returns {Asset | undefined} the asset or undefined when not found
2082
	 */
2083
	getAsset(name) {
2084
		if (!Object.prototype.hasOwnProperty.call(this.assets, name))
2085
			return undefined;
2086
		return {
2087
			name,
2088
			source: this.assets[name],
2089
			info: this.assetsInfo.get(name) || {}
2090
		};
2091
	}
2092

    
2093
	createModuleAssets() {
2094
		for (let i = 0; i < this.modules.length; i++) {
2095
			const module = this.modules[i];
2096
			if (module.buildInfo.assets) {
2097
				const assetsInfo = module.buildInfo.assetsInfo;
2098
				for (const assetName of Object.keys(module.buildInfo.assets)) {
2099
					const fileName = this.getPath(assetName);
2100
					this.emitAsset(
2101
						fileName,
2102
						module.buildInfo.assets[assetName],
2103
						assetsInfo ? assetsInfo.get(assetName) : undefined
2104
					);
2105
					this.hooks.moduleAsset.call(module, fileName);
2106
				}
2107
			}
2108
		}
2109
	}
2110

    
2111
	createChunkAssets() {
2112
		const outputOptions = this.outputOptions;
2113
		const cachedSourceMap = new Map();
2114
		/** @type {Map<string, {hash: string, source: Source, chunk: Chunk}>} */
2115
		const alreadyWrittenFiles = new Map();
2116
		for (let i = 0; i < this.chunks.length; i++) {
2117
			const chunk = this.chunks[i];
2118
			chunk.files = [];
2119
			let source;
2120
			let file;
2121
			let filenameTemplate;
2122
			try {
2123
				const template = chunk.hasRuntime()
2124
					? this.mainTemplate
2125
					: this.chunkTemplate;
2126
				const manifest = template.getRenderManifest({
2127
					chunk,
2128
					hash: this.hash,
2129
					fullHash: this.fullHash,
2130
					outputOptions,
2131
					moduleTemplates: this.moduleTemplates,
2132
					dependencyTemplates: this.dependencyTemplates
2133
				}); // [{ render(), filenameTemplate, pathOptions, identifier, hash }]
2134
				for (const fileManifest of manifest) {
2135
					const cacheName = fileManifest.identifier;
2136
					const usedHash = fileManifest.hash;
2137
					filenameTemplate = fileManifest.filenameTemplate;
2138
					const pathAndInfo = this.getPathWithInfo(
2139
						filenameTemplate,
2140
						fileManifest.pathOptions
2141
					);
2142
					file = pathAndInfo.path;
2143
					const assetInfo = pathAndInfo.info;
2144

    
2145
					// check if the same filename was already written by another chunk
2146
					const alreadyWritten = alreadyWrittenFiles.get(file);
2147
					if (alreadyWritten !== undefined) {
2148
						if (alreadyWritten.hash === usedHash) {
2149
							if (this.cache) {
2150
								this.cache[cacheName] = {
2151
									hash: usedHash,
2152
									source: alreadyWritten.source
2153
								};
2154
							}
2155
							chunk.files.push(file);
2156
							this.hooks.chunkAsset.call(chunk, file);
2157
							continue;
2158
						} else {
2159
							throw new Error(
2160
								`Conflict: Multiple chunks emit assets to the same filename ${file}` +
2161
									` (chunks ${alreadyWritten.chunk.id} and ${chunk.id})`
2162
							);
2163
						}
2164
					}
2165
					if (
2166
						this.cache &&
2167
						this.cache[cacheName] &&
2168
						this.cache[cacheName].hash === usedHash
2169
					) {
2170
						source = this.cache[cacheName].source;
2171
					} else {
2172
						source = fileManifest.render();
2173
						// Ensure that source is a cached source to avoid additional cost because of repeated access
2174
						if (!(source instanceof CachedSource)) {
2175
							const cacheEntry = cachedSourceMap.get(source);
2176
							if (cacheEntry) {
2177
								source = cacheEntry;
2178
							} else {
2179
								const cachedSource = new CachedSource(source);
2180
								cachedSourceMap.set(source, cachedSource);
2181
								source = cachedSource;
2182
							}
2183
						}
2184
						if (this.cache) {
2185
							this.cache[cacheName] = {
2186
								hash: usedHash,
2187
								source
2188
							};
2189
						}
2190
					}
2191
					this.emitAsset(file, source, assetInfo);
2192
					chunk.files.push(file);
2193
					this.hooks.chunkAsset.call(chunk, file);
2194
					alreadyWrittenFiles.set(file, {
2195
						hash: usedHash,
2196
						source,
2197
						chunk
2198
					});
2199
				}
2200
			} catch (err) {
2201
				this.errors.push(
2202
					new ChunkRenderError(chunk, file || filenameTemplate, err)
2203
				);
2204
			}
2205
		}
2206
	}
2207

    
2208
	/**
2209
	 * @param {string} filename used to get asset path with hash
2210
	 * @param {TODO=} data // TODO: figure out this param type
2211
	 * @returns {string} interpolated path
2212
	 */
2213
	getPath(filename, data) {
2214
		data = data || {};
2215
		data.hash = data.hash || this.hash;
2216
		return this.mainTemplate.getAssetPath(filename, data);
2217
	}
2218

    
2219
	/**
2220
	 * @param {string} filename used to get asset path with hash
2221
	 * @param {TODO=} data // TODO: figure out this param type
2222
	 * @returns {{ path: string, info: AssetInfo }} interpolated path and asset info
2223
	 */
2224
	getPathWithInfo(filename, data) {
2225
		data = data || {};
2226
		data.hash = data.hash || this.hash;
2227
		return this.mainTemplate.getAssetPathWithInfo(filename, data);
2228
	}
2229

    
2230
	/**
2231
	 * This function allows you to run another instance of webpack inside of webpack however as
2232
	 * a child with different settings and configurations (if desired) applied. It copies all hooks, plugins
2233
	 * from parent (or top level compiler) and creates a child Compilation
2234
	 *
2235
	 * @param {string} name name of the child compiler
2236
	 * @param {TODO} outputOptions // Need to convert config schema to types for this
2237
	 * @param {Plugin[]} plugins webpack plugins that will be applied
2238
	 * @returns {Compiler} creates a child Compiler instance
2239
	 */
2240
	createChildCompiler(name, outputOptions, plugins) {
2241
		const idx = this.childrenCounters[name] || 0;
2242
		this.childrenCounters[name] = idx + 1;
2243
		return this.compiler.createChildCompiler(
2244
			this,
2245
			name,
2246
			idx,
2247
			outputOptions,
2248
			plugins
2249
		);
2250
	}
2251

    
2252
	checkConstraints() {
2253
		/** @type {Set<number|string>} */
2254
		const usedIds = new Set();
2255

    
2256
		const modules = this.modules;
2257
		for (let indexModule = 0; indexModule < modules.length; indexModule++) {
2258
			const moduleId = modules[indexModule].id;
2259
			if (moduleId === null) continue;
2260
			if (usedIds.has(moduleId)) {
2261
				throw new Error(`checkConstraints: duplicate module id ${moduleId}`);
2262
			}
2263
			usedIds.add(moduleId);
2264
		}
2265

    
2266
		const chunks = this.chunks;
2267
		for (let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
2268
			const chunk = chunks[indexChunk];
2269
			if (chunks.indexOf(chunk) !== indexChunk) {
2270
				throw new Error(
2271
					`checkConstraints: duplicate chunk in compilation ${chunk.debugId}`
2272
				);
2273
			}
2274
		}
2275

    
2276
		for (const chunkGroup of this.chunkGroups) {
2277
			chunkGroup.checkConstraints();
2278
		}
2279
	}
2280
}
2281

    
2282
// TODO remove in webpack 5
2283
Compilation.prototype.applyPlugins = util.deprecate(
2284
	/**
2285
	 * @deprecated
2286
	 * @param {string} name Name
2287
	 * @param {any[]} args Other arguments
2288
	 * @returns {void}
2289
	 * @this {Compilation}
2290
	 */
2291
	function(name, ...args) {
2292
		this.hooks[
2293
			name.replace(/[- ]([a-z])/g, match => match[1].toUpperCase())
2294
		].call(...args);
2295
	},
2296
	"Compilation.applyPlugins is deprecated. Use new API on `.hooks` instead"
2297
);
2298

    
2299
// TODO remove in webpack 5
2300
Object.defineProperty(Compilation.prototype, "moduleTemplate", {
2301
	configurable: false,
2302
	get: util.deprecate(
2303
		/**
2304
		 * @deprecated
2305
		 * @this {Compilation}
2306
		 * @returns {TODO} module template
2307
		 */
2308
		function() {
2309
			return this.moduleTemplates.javascript;
2310
		},
2311
		"Compilation.moduleTemplate: Use Compilation.moduleTemplates.javascript instead"
2312
	),
2313
	set: util.deprecate(
2314
		/**
2315
		 * @deprecated
2316
		 * @param {ModuleTemplate} value Template value
2317
		 * @this {Compilation}
2318
		 * @returns {void}
2319
		 */
2320
		function(value) {
2321
			this.moduleTemplates.javascript = value;
2322
		},
2323
		"Compilation.moduleTemplate: Use Compilation.moduleTemplates.javascript instead."
2324
	)
2325
});
2326

    
2327
module.exports = Compilation;
(17-17/144)