Projekt

Obecné

Profil

Stáhnout (22.6 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 util = require("util");
8
const SortableSet = require("./util/SortableSet");
9
const intersect = require("./util/SetHelpers").intersect;
10
const GraphHelpers = require("./GraphHelpers");
11
const Entrypoint = require("./Entrypoint");
12
let debugId = 1000;
13
const ERR_CHUNK_ENTRY = "Chunk.entry was removed. Use hasRuntime()";
14
const ERR_CHUNK_INITIAL =
15
	"Chunk.initial was removed. Use canBeInitial/isOnlyInitial()";
16

    
17
/** @typedef {import("./Module")} Module */
18
/** @typedef {import("./ChunkGroup")} ChunkGroup */
19
/** @typedef {import("./ModuleReason")} ModuleReason */
20
/** @typedef {import("webpack-sources").Source} Source */
21
/** @typedef {import("./util/createHash").Hash} Hash */
22

    
23
/**
24
 *  @typedef {Object} WithId an object who has an id property *
25
 *  @property {string | number} id the id of the object
26
 */
27

    
28
/**
29
 * Compare two Modules based on their ids for sorting
30
 * @param {Module} a module
31
 * @param {Module} b module
32
 * @returns {-1|0|1} sort value
33
 */
34

    
35
// TODO use @callback
36
/** @typedef {(a: Module, b: Module) => -1|0|1} ModuleSortPredicate */
37
/** @typedef {(m: Module) => boolean} ModuleFilterPredicate */
38
/** @typedef {(c: Chunk) => boolean} ChunkFilterPredicate */
39

    
40
const sortModuleById = (a, b) => {
41
	if (a.id < b.id) return -1;
42
	if (b.id < a.id) return 1;
43
	return 0;
44
};
45

    
46
/**
47
 * Compare two ChunkGroups based on their ids for sorting
48
 * @param {ChunkGroup} a chunk group
49
 * @param {ChunkGroup} b chunk group
50
 * @returns {-1|0|1} sort value
51
 */
52
const sortChunkGroupById = (a, b) => {
53
	if (a.id < b.id) return -1;
54
	if (b.id < a.id) return 1;
55
	return 0;
56
};
57

    
58
/**
59
 * Compare two Identifiables , based on their ids for sorting
60
 * @param {Module} a first object with ident fn
61
 * @param {Module} b second object with ident fn
62
 * @returns {-1|0|1} The order number of the sort
63
 */
64
const sortByIdentifier = (a, b) => {
65
	if (a.identifier() > b.identifier()) return 1;
66
	if (a.identifier() < b.identifier()) return -1;
67
	return 0;
68
};
69

    
70
/**
71
 * @returns {string} a concatenation of module identifiers sorted
72
 * @param {SortableSet} set to pull module identifiers from
73
 */
74
const getModulesIdent = set => {
75
	set.sort();
76
	let str = "";
77
	for (const m of set) {
78
		str += m.identifier() + "#";
79
	}
80
	return str;
81
};
82

    
83
/**
84
 * @template T
85
 * @param {SortableSet<T>} set the sortable set to convert to array
86
 * @returns {Array<T>} the array returned from Array.from(set)
87
 */
88
const getArray = set => Array.from(set);
89

    
90
/**
91
 * @param {SortableSet<Module>} set the sortable Set to get the count/size of
92
 * @returns {number} the size of the modules
93
 */
94
const getModulesSize = set => {
95
	let size = 0;
96
	for (const module of set) {
97
		size += module.size();
98
	}
99
	return size;
100
};
101

    
102
/**
103
 * A Chunk is a unit of encapsulation for Modules.
104
 * Chunks are "rendered" into bundles that get emitted when the build completes.
105
 */
106
class Chunk {
107
	/**
108
	 * @param {string=} name of chunk being created, is optional (for subclasses)
109
	 */
110
	constructor(name) {
111
		/** @type {number | null} */
112
		this.id = null;
113
		/** @type {number[] | null} */
114
		this.ids = null;
115
		/** @type {number} */
116
		this.debugId = debugId++;
117
		/** @type {string} */
118
		this.name = name;
119
		/** @type {boolean} */
120
		this.preventIntegration = false;
121
		/** @type {Module=} */
122
		this.entryModule = undefined;
123
		/** @private @type {SortableSet<Module>} */
124
		this._modules = new SortableSet(undefined, sortByIdentifier);
125
		/** @type {string?} */
126
		this.filenameTemplate = undefined;
127
		/** @private @type {SortableSet<ChunkGroup>} */
128
		this._groups = new SortableSet(undefined, sortChunkGroupById);
129
		/** @type {string[]} */
130
		this.files = [];
131
		/** @type {boolean} */
132
		this.rendered = false;
133
		/** @type {string=} */
134
		this.hash = undefined;
135
		/** @type {Object} */
136
		this.contentHash = Object.create(null);
137
		/** @type {string=} */
138
		this.renderedHash = undefined;
139
		/** @type {string=} */
140
		this.chunkReason = undefined;
141
		/** @type {boolean} */
142
		this.extraAsync = false;
143
		this.removedModules = undefined;
144
	}
145

    
146
	/**
147
	 * @deprecated Chunk.entry has been deprecated. Please use .hasRuntime() instead
148
	 * @returns {never} Throws an error trying to access this property
149
	 */
150
	get entry() {
151
		throw new Error(ERR_CHUNK_ENTRY);
152
	}
153

    
154
	/**
155
	 * @deprecated .entry has been deprecated. Please use .hasRuntime() instead
156
	 * @param {never} data The data that was attempting to be set
157
	 * @returns {never} Throws an error trying to access this property
158
	 */
159
	set entry(data) {
160
		throw new Error(ERR_CHUNK_ENTRY);
161
	}
162

    
163
	/**
164
	 * @deprecated Chunk.initial was removed. Use canBeInitial/isOnlyInitial()
165
	 * @returns {never} Throws an error trying to access this property
166
	 */
167
	get initial() {
168
		throw new Error(ERR_CHUNK_INITIAL);
169
	}
170

    
171
	/**
172
	 * @deprecated Chunk.initial was removed. Use canBeInitial/isOnlyInitial()
173
	 * @param {never} data The data attempting to be set
174
	 * @returns {never} Throws an error trying to access this property
175
	 */
176
	set initial(data) {
177
		throw new Error(ERR_CHUNK_INITIAL);
178
	}
179

    
180
	/**
181
	 * @returns {boolean} whether or not the Chunk will have a runtime
182
	 */
183
	hasRuntime() {
184
		for (const chunkGroup of this._groups) {
185
			if (
186
				chunkGroup.isInitial() &&
187
				chunkGroup instanceof Entrypoint &&
188
				chunkGroup.getRuntimeChunk() === this
189
			) {
190
				return true;
191
			}
192
		}
193
		return false;
194
	}
195

    
196
	/**
197
	 * @returns {boolean} whether or not this chunk can be an initial chunk
198
	 */
199
	canBeInitial() {
200
		for (const chunkGroup of this._groups) {
201
			if (chunkGroup.isInitial()) return true;
202
		}
203
		return false;
204
	}
205

    
206
	/**
207
	 * @returns {boolean} whether this chunk can only be an initial chunk
208
	 */
209
	isOnlyInitial() {
210
		if (this._groups.size <= 0) return false;
211
		for (const chunkGroup of this._groups) {
212
			if (!chunkGroup.isInitial()) return false;
213
		}
214
		return true;
215
	}
216

    
217
	/**
218
	 * @returns {boolean} if this chunk contains the entry module
219
	 */
220
	hasEntryModule() {
221
		return !!this.entryModule;
222
	}
223

    
224
	/**
225
	 * @param {Module} module the module that will be added to this chunk.
226
	 * @returns {boolean} returns true if the chunk doesn't have the module and it was added
227
	 */
228
	addModule(module) {
229
		if (!this._modules.has(module)) {
230
			this._modules.add(module);
231
			return true;
232
		}
233
		return false;
234
	}
235

    
236
	/**
237
	 * @param {Module} module the module that will be removed from this chunk
238
	 * @returns {boolean} returns true if chunk exists and is successfully deleted
239
	 */
240
	removeModule(module) {
241
		if (this._modules.delete(module)) {
242
			module.removeChunk(this);
243
			return true;
244
		}
245
		return false;
246
	}
247

    
248
	/**
249
	 * @param {Module[]} modules the new modules to be set
250
	 * @returns {void} set new modules to this chunk and return nothing
251
	 */
252
	setModules(modules) {
253
		this._modules = new SortableSet(modules, sortByIdentifier);
254
	}
255

    
256
	/**
257
	 * @returns {number} the amount of modules in chunk
258
	 */
259
	getNumberOfModules() {
260
		return this._modules.size;
261
	}
262

    
263
	/**
264
	 * @returns {SortableSet} return the modules SortableSet for this chunk
265
	 */
266
	get modulesIterable() {
267
		return this._modules;
268
	}
269

    
270
	/**
271
	 * @param {ChunkGroup} chunkGroup the chunkGroup the chunk is being added
272
	 * @returns {boolean} returns true if chunk is not apart of chunkGroup and is added successfully
273
	 */
274
	addGroup(chunkGroup) {
275
		if (this._groups.has(chunkGroup)) return false;
276
		this._groups.add(chunkGroup);
277
		return true;
278
	}
279

    
280
	/**
281
	 * @param {ChunkGroup} chunkGroup the chunkGroup the chunk is being removed from
282
	 * @returns {boolean} returns true if chunk does exist in chunkGroup and is removed
283
	 */
284
	removeGroup(chunkGroup) {
285
		if (!this._groups.has(chunkGroup)) return false;
286
		this._groups.delete(chunkGroup);
287
		return true;
288
	}
289

    
290
	/**
291
	 * @param {ChunkGroup} chunkGroup the chunkGroup to check
292
	 * @returns {boolean} returns true if chunk has chunkGroup reference and exists in chunkGroup
293
	 */
294
	isInGroup(chunkGroup) {
295
		return this._groups.has(chunkGroup);
296
	}
297

    
298
	/**
299
	 * @returns {number} the amount of groups said chunk is in
300
	 */
301
	getNumberOfGroups() {
302
		return this._groups.size;
303
	}
304

    
305
	/**
306
	 * @returns {SortableSet<ChunkGroup>} the chunkGroups that said chunk is referenced in
307
	 */
308
	get groupsIterable() {
309
		return this._groups;
310
	}
311

    
312
	/**
313
	 * @param {Chunk} otherChunk the chunk to compare itself with
314
	 * @returns {-1|0|1} this is a comparitor function like sort and returns -1, 0, or 1 based on sort order
315
	 */
316
	compareTo(otherChunk) {
317
		if (this.name && !otherChunk.name) return -1;
318
		if (!this.name && otherChunk.name) return 1;
319
		if (this.name < otherChunk.name) return -1;
320
		if (this.name > otherChunk.name) return 1;
321
		if (this._modules.size > otherChunk._modules.size) return -1;
322
		if (this._modules.size < otherChunk._modules.size) return 1;
323
		this._modules.sort();
324
		otherChunk._modules.sort();
325
		const a = this._modules[Symbol.iterator]();
326
		const b = otherChunk._modules[Symbol.iterator]();
327
		// eslint-disable-next-line no-constant-condition
328
		while (true) {
329
			const aItem = a.next();
330
			if (aItem.done) return 0;
331
			const bItem = b.next();
332
			const aModuleIdentifier = aItem.value.identifier();
333
			const bModuleIdentifier = bItem.value.identifier();
334
			if (aModuleIdentifier < bModuleIdentifier) return -1;
335
			if (aModuleIdentifier > bModuleIdentifier) return 1;
336
		}
337
	}
338

    
339
	/**
340
	 * @param {Module} module Module to check
341
	 * @returns {boolean} returns true if module does exist in this chunk
342
	 */
343
	containsModule(module) {
344
		return this._modules.has(module);
345
	}
346

    
347
	/**
348
	 * @returns {Module[]} an array of modules (do not modify)
349
	 */
350
	getModules() {
351
		return this._modules.getFromCache(getArray);
352
	}
353

    
354
	getModulesIdent() {
355
		return this._modules.getFromUnorderedCache(getModulesIdent);
356
	}
357

    
358
	/**
359
	 * @param {string=} reason reason why chunk is removed
360
	 * @returns {void}
361
	 */
362
	remove(reason) {
363
		// cleanup modules
364
		// Array.from is used here to create a clone, because removeChunk modifies this._modules
365
		for (const module of Array.from(this._modules)) {
366
			module.removeChunk(this);
367
		}
368
		for (const chunkGroup of this._groups) {
369
			chunkGroup.removeChunk(this);
370
		}
371
	}
372

    
373
	/**
374
	 *
375
	 * @param {Module} module module to move
376
	 * @param {Chunk} otherChunk other chunk to move it to
377
	 * @returns {void}
378
	 */
379
	moveModule(module, otherChunk) {
380
		GraphHelpers.disconnectChunkAndModule(this, module);
381
		GraphHelpers.connectChunkAndModule(otherChunk, module);
382
		module.rewriteChunkInReasons(this, [otherChunk]);
383
	}
384

    
385
	/**
386
	 *
387
	 * @param {Chunk} otherChunk the chunk to integrate with
388
	 * @param {string} reason reason why the module is being integrated
389
	 * @returns {boolean} returns true or false if integration succeeds or fails
390
	 */
391
	integrate(otherChunk, reason) {
392
		if (!this.canBeIntegrated(otherChunk)) {
393
			return false;
394
		}
395

    
396
		// Pick a new name for the integrated chunk
397
		if (this.name && otherChunk.name) {
398
			if (this.hasEntryModule() === otherChunk.hasEntryModule()) {
399
				// When both chunks have entry modules or none have one, use
400
				// shortest name
401
				if (this.name.length !== otherChunk.name.length) {
402
					this.name =
403
						this.name.length < otherChunk.name.length
404
							? this.name
405
							: otherChunk.name;
406
				} else {
407
					this.name = this.name < otherChunk.name ? this.name : otherChunk.name;
408
				}
409
			} else if (otherChunk.hasEntryModule()) {
410
				// Pick the name of the chunk with the entry module
411
				this.name = otherChunk.name;
412
			}
413
		} else if (otherChunk.name) {
414
			this.name = otherChunk.name;
415
		}
416

    
417
		// Array.from is used here to create a clone, because moveModule modifies otherChunk._modules
418
		for (const module of Array.from(otherChunk._modules)) {
419
			otherChunk.moveModule(module, this);
420
		}
421
		otherChunk._modules.clear();
422

    
423
		if (otherChunk.entryModule) {
424
			this.entryModule = otherChunk.entryModule;
425
		}
426

    
427
		for (const chunkGroup of otherChunk._groups) {
428
			chunkGroup.replaceChunk(otherChunk, this);
429
			this.addGroup(chunkGroup);
430
		}
431
		otherChunk._groups.clear();
432

    
433
		return true;
434
	}
435

    
436
	/**
437
	 * @param {Chunk} newChunk the new chunk that will be split out of the current chunk
438
	 * @returns {void}
439
	 */
440
	split(newChunk) {
441
		for (const chunkGroup of this._groups) {
442
			chunkGroup.insertChunk(newChunk, this);
443
			newChunk.addGroup(chunkGroup);
444
		}
445
	}
446

    
447
	isEmpty() {
448
		return this._modules.size === 0;
449
	}
450

    
451
	updateHash(hash) {
452
		hash.update(`${this.id} `);
453
		hash.update(this.ids ? this.ids.join(",") : "");
454
		hash.update(`${this.name || ""} `);
455
		for (const m of this._modules) {
456
			hash.update(m.hash);
457
		}
458
	}
459

    
460
	canBeIntegrated(otherChunk) {
461
		if (this.preventIntegration || otherChunk.preventIntegration) {
462
			return false;
463
		}
464

    
465
		/**
466
		 * @param {Chunk} a chunk
467
		 * @param {Chunk} b chunk
468
		 * @returns {boolean} true, if a is always available when b is reached
469
		 */
470
		const isAvailable = (a, b) => {
471
			const queue = new Set(b.groupsIterable);
472
			for (const chunkGroup of queue) {
473
				if (a.isInGroup(chunkGroup)) continue;
474
				if (chunkGroup.isInitial()) return false;
475
				for (const parent of chunkGroup.parentsIterable) {
476
					queue.add(parent);
477
				}
478
			}
479
			return true;
480
		};
481

    
482
		const selfHasRuntime = this.hasRuntime();
483
		const otherChunkHasRuntime = otherChunk.hasRuntime();
484

    
485
		if (selfHasRuntime !== otherChunkHasRuntime) {
486
			if (selfHasRuntime) {
487
				return isAvailable(this, otherChunk);
488
			} else if (otherChunkHasRuntime) {
489
				return isAvailable(otherChunk, this);
490
			} else {
491
				return false;
492
			}
493
		}
494

    
495
		if (this.hasEntryModule() || otherChunk.hasEntryModule()) {
496
			return false;
497
		}
498

    
499
		return true;
500
	}
501

    
502
	/**
503
	 *
504
	 * @param {number} size the size
505
	 * @param {Object} options the options passed in
506
	 * @returns {number} the multiplier returned
507
	 */
508
	addMultiplierAndOverhead(size, options) {
509
		const overhead =
510
			typeof options.chunkOverhead === "number" ? options.chunkOverhead : 10000;
511
		const multiplicator = this.canBeInitial()
512
			? options.entryChunkMultiplicator || 10
513
			: 1;
514

    
515
		return size * multiplicator + overhead;
516
	}
517

    
518
	/**
519
	 * @returns {number} the size of all modules
520
	 */
521
	modulesSize() {
522
		return this._modules.getFromUnorderedCache(getModulesSize);
523
	}
524

    
525
	/**
526
	 * @param {Object} options the size display options
527
	 * @returns {number} the chunk size
528
	 */
529
	size(options = {}) {
530
		return this.addMultiplierAndOverhead(this.modulesSize(), options);
531
	}
532

    
533
	/**
534
	 * @param {Chunk} otherChunk the other chunk
535
	 * @param {TODO} options the options for this function
536
	 * @returns {number | false} the size, or false if it can't be integrated
537
	 */
538
	integratedSize(otherChunk, options) {
539
		// Chunk if it's possible to integrate this chunk
540
		if (!this.canBeIntegrated(otherChunk)) {
541
			return false;
542
		}
543

    
544
		let integratedModulesSize = this.modulesSize();
545
		// only count modules that do not exist in this chunk!
546
		for (const otherModule of otherChunk._modules) {
547
			if (!this._modules.has(otherModule)) {
548
				integratedModulesSize += otherModule.size();
549
			}
550
		}
551

    
552
		return this.addMultiplierAndOverhead(integratedModulesSize, options);
553
	}
554

    
555
	/**
556
	 * @param {function(Module, Module): -1|0|1=} sortByFn a predicate function used to sort modules
557
	 * @returns {void}
558
	 */
559
	sortModules(sortByFn) {
560
		this._modules.sortWith(sortByFn || sortModuleById);
561
	}
562

    
563
	sortItems() {
564
		this.sortModules();
565
	}
566

    
567
	/**
568
	 * @returns {Set<Chunk>} a set of all the async chunks
569
	 */
570
	getAllAsyncChunks() {
571
		const queue = new Set();
572
		const chunks = new Set();
573

    
574
		const initialChunks = intersect(
575
			Array.from(this.groupsIterable, g => new Set(g.chunks))
576
		);
577

    
578
		for (const chunkGroup of this.groupsIterable) {
579
			for (const child of chunkGroup.childrenIterable) {
580
				queue.add(child);
581
			}
582
		}
583

    
584
		for (const chunkGroup of queue) {
585
			for (const chunk of chunkGroup.chunks) {
586
				if (!initialChunks.has(chunk)) {
587
					chunks.add(chunk);
588
				}
589
			}
590
			for (const child of chunkGroup.childrenIterable) {
591
				queue.add(child);
592
			}
593
		}
594

    
595
		return chunks;
596
	}
597

    
598
	/**
599
	 * @typedef {Object} ChunkMaps
600
	 * @property {Record<string|number, string>} hash
601
	 * @property {Record<string|number, Record<string, string>>} contentHash
602
	 * @property {Record<string|number, string>} name
603
	 */
604

    
605
	/**
606
	 * @param {boolean} realHash should the full hash or the rendered hash be used
607
	 * @returns {ChunkMaps} the chunk map information
608
	 */
609
	getChunkMaps(realHash) {
610
		/** @type {Record<string|number, string>} */
611
		const chunkHashMap = Object.create(null);
612
		/** @type {Record<string|number, Record<string, string>>} */
613
		const chunkContentHashMap = Object.create(null);
614
		/** @type {Record<string|number, string>} */
615
		const chunkNameMap = Object.create(null);
616

    
617
		for (const chunk of this.getAllAsyncChunks()) {
618
			chunkHashMap[chunk.id] = realHash ? chunk.hash : chunk.renderedHash;
619
			for (const key of Object.keys(chunk.contentHash)) {
620
				if (!chunkContentHashMap[key]) {
621
					chunkContentHashMap[key] = Object.create(null);
622
				}
623
				chunkContentHashMap[key][chunk.id] = chunk.contentHash[key];
624
			}
625
			if (chunk.name) {
626
				chunkNameMap[chunk.id] = chunk.name;
627
			}
628
		}
629

    
630
		return {
631
			hash: chunkHashMap,
632
			contentHash: chunkContentHashMap,
633
			name: chunkNameMap
634
		};
635
	}
636

    
637
	/**
638
	 * @returns {Record<string, Set<TODO>[]>} a record object of names to lists of child ids(?)
639
	 */
640
	getChildIdsByOrders() {
641
		const lists = new Map();
642
		for (const group of this.groupsIterable) {
643
			if (group.chunks[group.chunks.length - 1] === this) {
644
				for (const childGroup of group.childrenIterable) {
645
					// TODO webpack 5 remove this check for options
646
					if (typeof childGroup.options === "object") {
647
						for (const key of Object.keys(childGroup.options)) {
648
							if (key.endsWith("Order")) {
649
								const name = key.substr(0, key.length - "Order".length);
650
								let list = lists.get(name);
651
								if (list === undefined) lists.set(name, (list = []));
652
								list.push({
653
									order: childGroup.options[key],
654
									group: childGroup
655
								});
656
							}
657
						}
658
					}
659
				}
660
			}
661
		}
662
		const result = Object.create(null);
663
		for (const [name, list] of lists) {
664
			list.sort((a, b) => {
665
				const cmp = b.order - a.order;
666
				if (cmp !== 0) return cmp;
667
				// TODO webpack 5 remove this check of compareTo
668
				if (a.group.compareTo) {
669
					return a.group.compareTo(b.group);
670
				}
671
				return 0;
672
			});
673
			result[name] = Array.from(
674
				list.reduce((set, item) => {
675
					for (const chunk of item.group.chunks) {
676
						set.add(chunk.id);
677
					}
678
					return set;
679
				}, new Set())
680
			);
681
		}
682
		return result;
683
	}
684

    
685
	getChildIdsByOrdersMap(includeDirectChildren) {
686
		const chunkMaps = Object.create(null);
687

    
688
		const addChildIdsByOrdersToMap = chunk => {
689
			const data = chunk.getChildIdsByOrders();
690
			for (const key of Object.keys(data)) {
691
				let chunkMap = chunkMaps[key];
692
				if (chunkMap === undefined) {
693
					chunkMaps[key] = chunkMap = Object.create(null);
694
				}
695
				chunkMap[chunk.id] = data[key];
696
			}
697
		};
698

    
699
		if (includeDirectChildren) {
700
			const chunks = new Set();
701
			for (const chunkGroup of this.groupsIterable) {
702
				for (const chunk of chunkGroup.chunks) {
703
					chunks.add(chunk);
704
				}
705
			}
706
			for (const chunk of chunks) {
707
				addChildIdsByOrdersToMap(chunk);
708
			}
709
		}
710

    
711
		for (const chunk of this.getAllAsyncChunks()) {
712
			addChildIdsByOrdersToMap(chunk);
713
		}
714

    
715
		return chunkMaps;
716
	}
717

    
718
	/**
719
	 * @typedef {Object} ChunkModuleMaps
720
	 * @property {Record<string|number, (string|number)[]>} id
721
	 * @property {Record<string|number, string>} hash
722
	 */
723

    
724
	/**
725
	 * @param {ModuleFilterPredicate} filterFn function used to filter modules
726
	 * @returns {ChunkModuleMaps} module map information
727
	 */
728
	getChunkModuleMaps(filterFn) {
729
		/** @type {Record<string|number, (string|number)[]>} */
730
		const chunkModuleIdMap = Object.create(null);
731
		/** @type {Record<string|number, string>} */
732
		const chunkModuleHashMap = Object.create(null);
733

    
734
		for (const chunk of this.getAllAsyncChunks()) {
735
			/** @type {(string|number)[]} */
736
			let array;
737
			for (const module of chunk.modulesIterable) {
738
				if (filterFn(module)) {
739
					if (array === undefined) {
740
						array = [];
741
						chunkModuleIdMap[chunk.id] = array;
742
					}
743
					array.push(module.id);
744
					chunkModuleHashMap[module.id] = module.renderedHash;
745
				}
746
			}
747
			if (array !== undefined) {
748
				array.sort();
749
			}
750
		}
751

    
752
		return {
753
			id: chunkModuleIdMap,
754
			hash: chunkModuleHashMap
755
		};
756
	}
757

    
758
	/**
759
	 *
760
	 * @param {function(Module): boolean} filterFn predicate function used to filter modules
761
	 * @param {function(Chunk): boolean} filterChunkFn predicate function used to filter chunks
762
	 * @returns {boolean} return true if module exists in graph
763
	 */
764
	hasModuleInGraph(filterFn, filterChunkFn) {
765
		const queue = new Set(this.groupsIterable);
766
		const chunksProcessed = new Set();
767

    
768
		for (const chunkGroup of queue) {
769
			for (const chunk of chunkGroup.chunks) {
770
				if (!chunksProcessed.has(chunk)) {
771
					chunksProcessed.add(chunk);
772
					if (!filterChunkFn || filterChunkFn(chunk)) {
773
						for (const module of chunk.modulesIterable) {
774
							if (filterFn(module)) {
775
								return true;
776
							}
777
						}
778
					}
779
				}
780
			}
781
			for (const child of chunkGroup.childrenIterable) {
782
				queue.add(child);
783
			}
784
		}
785
		return false;
786
	}
787

    
788
	toString() {
789
		return `Chunk[${Array.from(this._modules).join()}]`;
790
	}
791
}
792

    
793
// TODO remove in webpack 5
794
Object.defineProperty(Chunk.prototype, "forEachModule", {
795
	configurable: false,
796
	value: util.deprecate(
797
		/**
798
		 * @deprecated
799
		 * @this {Chunk}
800
		 * @typedef {function(any, any, Set<any>): void} ForEachModuleCallback
801
		 * @param {ForEachModuleCallback} fn Callback function
802
		 * @returns {void}
803
		 */
804
		function(fn) {
805
			this._modules.forEach(fn);
806
		},
807
		"Chunk.forEachModule: Use for(const module of chunk.modulesIterable) instead"
808
	)
809
});
810

    
811
// TODO remove in webpack 5
812
Object.defineProperty(Chunk.prototype, "mapModules", {
813
	configurable: false,
814
	value: util.deprecate(
815
		/**
816
		 * @deprecated
817
		 * @this {Chunk}
818
		 * @typedef {function(any, number): any} MapModulesCallback
819
		 * @param {MapModulesCallback} fn Callback function
820
		 * @returns {TODO[]} result of mapped modules
821
		 */
822
		function(fn) {
823
			return Array.from(this._modules, fn);
824
		},
825
		"Chunk.mapModules: Use Array.from(chunk.modulesIterable, fn) instead"
826
	)
827
});
828

    
829
// TODO remove in webpack 5
830
Object.defineProperty(Chunk.prototype, "chunks", {
831
	configurable: false,
832
	get() {
833
		throw new Error("Chunk.chunks: Use ChunkGroup.getChildren() instead");
834
	},
835
	set() {
836
		throw new Error("Chunk.chunks: Use ChunkGroup.add/removeChild() instead");
837
	}
838
});
839

    
840
// TODO remove in webpack 5
841
Object.defineProperty(Chunk.prototype, "parents", {
842
	configurable: false,
843
	get() {
844
		throw new Error("Chunk.parents: Use ChunkGroup.getParents() instead");
845
	},
846
	set() {
847
		throw new Error("Chunk.parents: Use ChunkGroup.add/removeParent() instead");
848
	}
849
});
850

    
851
// TODO remove in webpack 5
852
Object.defineProperty(Chunk.prototype, "blocks", {
853
	configurable: false,
854
	get() {
855
		throw new Error("Chunk.blocks: Use ChunkGroup.getBlocks() instead");
856
	},
857
	set() {
858
		throw new Error("Chunk.blocks: Use ChunkGroup.add/removeBlock() instead");
859
	}
860
});
861

    
862
// TODO remove in webpack 5
863
Object.defineProperty(Chunk.prototype, "entrypoints", {
864
	configurable: false,
865
	get() {
866
		throw new Error(
867
			"Chunk.entrypoints: Use Chunks.groupsIterable and filter by instanceof Entrypoint instead"
868
		);
869
	},
870
	set() {
871
		throw new Error("Chunk.entrypoints: Use Chunks.addGroup instead");
872
	}
873
});
874

    
875
module.exports = Chunk;
(10-10/144)