Projekt

Obecné

Profil

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

    
6
"use strict";
7

    
8
const AsyncDependencyToInitialChunkError = require("./AsyncDependencyToInitialChunkError");
9
const GraphHelpers = require("./GraphHelpers");
10

    
11
/** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */
12
/** @typedef {import("./Chunk")} Chunk */
13
/** @typedef {import("./ChunkGroup")} ChunkGroup */
14
/** @typedef {import("./Compilation")} Compilation */
15
/** @typedef {import("./DependenciesBlock")} DependenciesBlock */
16
/** @typedef {import("./Dependency")} Dependency */
17
/** @typedef {import("./Entrypoint")} Entrypoint */
18
/** @typedef {import("./Module")} Module */
19

    
20
/**
21
 * @typedef {Object} QueueItem
22
 * @property {number} action
23
 * @property {DependenciesBlock} block
24
 * @property {Module} module
25
 * @property {Chunk} chunk
26
 * @property {ChunkGroup} chunkGroup
27
 */
28

    
29
/**
30
 * @typedef {Object} ChunkGroupInfo
31
 * @property {ChunkGroup} chunkGroup the chunk group
32
 * @property {Set<Module>} minAvailableModules current minimal set of modules available at this point
33
 * @property {boolean} minAvailableModulesOwned true, if minAvailableModules is owned and can be modified
34
 * @property {Set<Module>[]} availableModulesToBeMerged enqueued updates to the minimal set of available modules
35
 * @property {QueueItem[]} skippedItems queue items that were skipped because module is already available in parent chunks (need to reconsider when minAvailableModules is shrinking)
36
 * @property {Set<Module>} resultingAvailableModules set of modules available including modules from this chunk group
37
 * @property {Set<ChunkGroup>} children set of children chunk groups, that will be revisited when availableModules shrink
38
 */
39

    
40
/**
41
 * @typedef {Object} ChunkGroupDep
42
 * @property {AsyncDependenciesBlock} block referencing block
43
 * @property {ChunkGroup} chunkGroup referenced chunk group
44
 */
45

    
46
/**
47
 * @template T
48
 * @param {Set<T>} a first set
49
 * @param {Set<T>} b second set
50
 * @returns {number} cmp
51
 */
52
const bySetSize = (a, b) => {
53
	return b.size - a.size;
54
};
55

    
56
/**
57
 * Extracts simplified info from the modules and their dependencies
58
 * @param {Compilation} compilation the compilation
59
 * @returns {Map<DependenciesBlock, { modules: Iterable<Module>, blocks: AsyncDependenciesBlock[]}>} the mapping block to modules and inner blocks
60
 */
61
const extraceBlockInfoMap = compilation => {
62
	/** @type {Map<DependenciesBlock, { modules: Iterable<Module>, blocks: AsyncDependenciesBlock[]}>} */
63
	const blockInfoMap = new Map();
64

    
65
	/**
66
	 * @param {Dependency} d dependency to iterate over
67
	 * @returns {void}
68
	 */
69
	const iteratorDependency = d => {
70
		// We skip Dependencies without Reference
71
		const ref = compilation.getDependencyReference(currentModule, d);
72
		if (!ref) {
73
			return;
74
		}
75
		// We skip Dependencies without Module pointer
76
		const refModule = ref.module;
77
		if (!refModule) {
78
			return;
79
		}
80
		// We skip weak Dependencies
81
		if (ref.weak) {
82
			return;
83
		}
84

    
85
		blockInfoModules.add(refModule);
86
	};
87

    
88
	/**
89
	 * @param {AsyncDependenciesBlock} b blocks to prepare
90
	 * @returns {void}
91
	 */
92
	const iteratorBlockPrepare = b => {
93
		blockInfoBlocks.push(b);
94
		blockQueue.push(b);
95
	};
96

    
97
	/** @type {Module} */
98
	let currentModule;
99
	/** @type {DependenciesBlock} */
100
	let block;
101
	/** @type {DependenciesBlock[]} */
102
	let blockQueue;
103
	/** @type {Set<Module>} */
104
	let blockInfoModules;
105
	/** @type {AsyncDependenciesBlock[]} */
106
	let blockInfoBlocks;
107

    
108
	for (const module of compilation.modules) {
109
		blockQueue = [module];
110
		currentModule = module;
111
		while (blockQueue.length > 0) {
112
			block = blockQueue.pop();
113
			blockInfoModules = new Set();
114
			blockInfoBlocks = [];
115

    
116
			if (block.variables) {
117
				for (const variable of block.variables) {
118
					for (const dep of variable.dependencies) iteratorDependency(dep);
119
				}
120
			}
121

    
122
			if (block.dependencies) {
123
				for (const dep of block.dependencies) iteratorDependency(dep);
124
			}
125

    
126
			if (block.blocks) {
127
				for (const b of block.blocks) iteratorBlockPrepare(b);
128
			}
129

    
130
			const blockInfo = {
131
				modules: blockInfoModules,
132
				blocks: blockInfoBlocks
133
			};
134
			blockInfoMap.set(block, blockInfo);
135
		}
136
	}
137

    
138
	return blockInfoMap;
139
};
140

    
141
/**
142
 *
143
 * @param {Compilation} compilation the compilation
144
 * @param {Entrypoint[]} inputChunkGroups input groups
145
 * @param {Map<ChunkGroup, ChunkGroupInfo>} chunkGroupInfoMap mapping from chunk group to available modules
146
 * @param {Map<ChunkGroup, ChunkGroupDep[]>} chunkDependencies dependencies for chunk groups
147
 * @param {Set<DependenciesBlock>} blocksWithNestedBlocks flag for blocks that have nested blocks
148
 * @param {Set<ChunkGroup>} allCreatedChunkGroups filled with all chunk groups that are created here
149
 */
150
const visitModules = (
151
	compilation,
152
	inputChunkGroups,
153
	chunkGroupInfoMap,
154
	chunkDependencies,
155
	blocksWithNestedBlocks,
156
	allCreatedChunkGroups
157
) => {
158
	const logger = compilation.getLogger("webpack.buildChunkGraph.visitModules");
159
	const { namedChunkGroups } = compilation;
160

    
161
	logger.time("prepare");
162
	const blockInfoMap = extraceBlockInfoMap(compilation);
163

    
164
	/** @type {Map<ChunkGroup, { index: number, index2: number }>} */
165
	const chunkGroupCounters = new Map();
166
	for (const chunkGroup of inputChunkGroups) {
167
		chunkGroupCounters.set(chunkGroup, {
168
			index: 0,
169
			index2: 0
170
		});
171
	}
172

    
173
	let nextFreeModuleIndex = 0;
174
	let nextFreeModuleIndex2 = 0;
175

    
176
	/** @type {Map<DependenciesBlock, ChunkGroup>} */
177
	const blockChunkGroups = new Map();
178

    
179
	const ADD_AND_ENTER_MODULE = 0;
180
	const ENTER_MODULE = 1;
181
	const PROCESS_BLOCK = 2;
182
	const LEAVE_MODULE = 3;
183

    
184
	/**
185
	 * @param {QueueItem[]} queue the queue array (will be mutated)
186
	 * @param {ChunkGroup} chunkGroup chunk group
187
	 * @returns {QueueItem[]} the queue array again
188
	 */
189
	const reduceChunkGroupToQueueItem = (queue, chunkGroup) => {
190
		for (const chunk of chunkGroup.chunks) {
191
			const module = chunk.entryModule;
192
			queue.push({
193
				action: ENTER_MODULE,
194
				block: module,
195
				module,
196
				chunk,
197
				chunkGroup
198
			});
199
		}
200
		chunkGroupInfoMap.set(chunkGroup, {
201
			chunkGroup,
202
			minAvailableModules: new Set(),
203
			minAvailableModulesOwned: true,
204
			availableModulesToBeMerged: [],
205
			skippedItems: [],
206
			resultingAvailableModules: undefined,
207
			children: undefined
208
		});
209
		return queue;
210
	};
211

    
212
	// Start with the provided modules/chunks
213
	/** @type {QueueItem[]} */
214
	let queue = inputChunkGroups
215
		.reduce(reduceChunkGroupToQueueItem, [])
216
		.reverse();
217
	/** @type {Map<ChunkGroup, Set<ChunkGroup>>} */
218
	const queueConnect = new Map();
219
	/** @type {Set<ChunkGroupInfo>} */
220
	const outdatedChunkGroupInfo = new Set();
221
	/** @type {QueueItem[]} */
222
	let queueDelayed = [];
223

    
224
	logger.timeEnd("prepare");
225

    
226
	/** @type {Module} */
227
	let module;
228
	/** @type {Chunk} */
229
	let chunk;
230
	/** @type {ChunkGroup} */
231
	let chunkGroup;
232
	/** @type {DependenciesBlock} */
233
	let block;
234
	/** @type {Set<Module>} */
235
	let minAvailableModules;
236
	/** @type {QueueItem[]} */
237
	let skippedItems;
238

    
239
	// For each async Block in graph
240
	/**
241
	 * @param {AsyncDependenciesBlock} b iterating over each Async DepBlock
242
	 * @returns {void}
243
	 */
244
	const iteratorBlock = b => {
245
		// 1. We create a chunk for this Block
246
		// but only once (blockChunkGroups map)
247
		let c = blockChunkGroups.get(b);
248
		if (c === undefined) {
249
			c = namedChunkGroups.get(b.chunkName);
250
			if (c && c.isInitial()) {
251
				compilation.errors.push(
252
					new AsyncDependencyToInitialChunkError(b.chunkName, module, b.loc)
253
				);
254
				c = chunkGroup;
255
			} else {
256
				c = compilation.addChunkInGroup(
257
					b.groupOptions || b.chunkName,
258
					module,
259
					b.loc,
260
					b.request
261
				);
262
				chunkGroupCounters.set(c, { index: 0, index2: 0 });
263
				blockChunkGroups.set(b, c);
264
				allCreatedChunkGroups.add(c);
265
			}
266
		} else {
267
			// TODO webpack 5 remove addOptions check
268
			if (c.addOptions) c.addOptions(b.groupOptions);
269
			c.addOrigin(module, b.loc, b.request);
270
		}
271

    
272
		// 2. We store the Block+Chunk mapping as dependency for the chunk
273
		let deps = chunkDependencies.get(chunkGroup);
274
		if (!deps) chunkDependencies.set(chunkGroup, (deps = []));
275
		deps.push({
276
			block: b,
277
			chunkGroup: c
278
		});
279

    
280
		// 3. We create/update the chunk group info
281
		let connectList = queueConnect.get(chunkGroup);
282
		if (connectList === undefined) {
283
			connectList = new Set();
284
			queueConnect.set(chunkGroup, connectList);
285
		}
286
		connectList.add(c);
287

    
288
		// 4. We enqueue the DependenciesBlock for traversal
289
		queueDelayed.push({
290
			action: PROCESS_BLOCK,
291
			block: b,
292
			module: module,
293
			chunk: c.chunks[0],
294
			chunkGroup: c
295
		});
296
	};
297

    
298
	// Iterative traversal of the Module graph
299
	// Recursive would be simpler to write but could result in Stack Overflows
300
	while (queue.length) {
301
		logger.time("visiting");
302
		while (queue.length) {
303
			const queueItem = queue.pop();
304
			module = queueItem.module;
305
			block = queueItem.block;
306
			chunk = queueItem.chunk;
307
			if (chunkGroup !== queueItem.chunkGroup) {
308
				chunkGroup = queueItem.chunkGroup;
309
				const chunkGroupInfo = chunkGroupInfoMap.get(chunkGroup);
310
				minAvailableModules = chunkGroupInfo.minAvailableModules;
311
				skippedItems = chunkGroupInfo.skippedItems;
312
			}
313

    
314
			switch (queueItem.action) {
315
				case ADD_AND_ENTER_MODULE: {
316
					if (minAvailableModules.has(module)) {
317
						// already in parent chunks
318
						// skip it for now, but enqueue for rechecking when minAvailableModules shrinks
319
						skippedItems.push(queueItem);
320
						break;
321
					}
322
					// We connect Module and Chunk when not already done
323
					if (chunk.addModule(module)) {
324
						module.addChunk(chunk);
325
					} else {
326
						// already connected, skip it
327
						break;
328
					}
329
				}
330
				// fallthrough
331
				case ENTER_MODULE: {
332
					if (chunkGroup !== undefined) {
333
						const index = chunkGroup.getModuleIndex(module);
334
						if (index === undefined) {
335
							chunkGroup.setModuleIndex(
336
								module,
337
								chunkGroupCounters.get(chunkGroup).index++
338
							);
339
						}
340
					}
341

    
342
					if (module.index === null) {
343
						module.index = nextFreeModuleIndex++;
344
					}
345

    
346
					queue.push({
347
						action: LEAVE_MODULE,
348
						block,
349
						module,
350
						chunk,
351
						chunkGroup
352
					});
353
				}
354
				// fallthrough
355
				case PROCESS_BLOCK: {
356
					// get prepared block info
357
					const blockInfo = blockInfoMap.get(block);
358

    
359
					// Buffer items because order need to be reverse to get indicies correct
360
					const skipBuffer = [];
361
					const queueBuffer = [];
362
					// Traverse all referenced modules
363
					for (const refModule of blockInfo.modules) {
364
						if (chunk.containsModule(refModule)) {
365
							// skip early if already connected
366
							continue;
367
						}
368
						if (minAvailableModules.has(refModule)) {
369
							// already in parent chunks, skip it for now
370
							skipBuffer.push({
371
								action: ADD_AND_ENTER_MODULE,
372
								block: refModule,
373
								module: refModule,
374
								chunk,
375
								chunkGroup
376
							});
377
							continue;
378
						}
379
						// enqueue the add and enter to enter in the correct order
380
						// this is relevant with circular dependencies
381
						queueBuffer.push({
382
							action: ADD_AND_ENTER_MODULE,
383
							block: refModule,
384
							module: refModule,
385
							chunk,
386
							chunkGroup
387
						});
388
					}
389
					// Add buffered items in reversed order
390
					for (let i = skipBuffer.length - 1; i >= 0; i--) {
391
						skippedItems.push(skipBuffer[i]);
392
					}
393
					for (let i = queueBuffer.length - 1; i >= 0; i--) {
394
						queue.push(queueBuffer[i]);
395
					}
396

    
397
					// Traverse all Blocks
398
					for (const block of blockInfo.blocks) iteratorBlock(block);
399

    
400
					if (blockInfo.blocks.length > 0 && module !== block) {
401
						blocksWithNestedBlocks.add(block);
402
					}
403
					break;
404
				}
405
				case LEAVE_MODULE: {
406
					if (chunkGroup !== undefined) {
407
						const index = chunkGroup.getModuleIndex2(module);
408
						if (index === undefined) {
409
							chunkGroup.setModuleIndex2(
410
								module,
411
								chunkGroupCounters.get(chunkGroup).index2++
412
							);
413
						}
414
					}
415

    
416
					if (module.index2 === null) {
417
						module.index2 = nextFreeModuleIndex2++;
418
					}
419
					break;
420
				}
421
			}
422
		}
423
		logger.timeEnd("visiting");
424

    
425
		while (queueConnect.size > 0) {
426
			logger.time("calculating available modules");
427

    
428
			// Figure out new parents for chunk groups
429
			// to get new available modules for these children
430
			for (const [chunkGroup, targets] of queueConnect) {
431
				const info = chunkGroupInfoMap.get(chunkGroup);
432
				let minAvailableModules = info.minAvailableModules;
433

    
434
				// 1. Create a new Set of available modules at this points
435
				const resultingAvailableModules = new Set(minAvailableModules);
436
				for (const chunk of chunkGroup.chunks) {
437
					for (const m of chunk.modulesIterable) {
438
						resultingAvailableModules.add(m);
439
					}
440
				}
441
				info.resultingAvailableModules = resultingAvailableModules;
442
				if (info.children === undefined) {
443
					info.children = targets;
444
				} else {
445
					for (const target of targets) {
446
						info.children.add(target);
447
					}
448
				}
449

    
450
				// 2. Update chunk group info
451
				for (const target of targets) {
452
					let chunkGroupInfo = chunkGroupInfoMap.get(target);
453
					if (chunkGroupInfo === undefined) {
454
						chunkGroupInfo = {
455
							chunkGroup: target,
456
							minAvailableModules: undefined,
457
							minAvailableModulesOwned: undefined,
458
							availableModulesToBeMerged: [],
459
							skippedItems: [],
460
							resultingAvailableModules: undefined,
461
							children: undefined
462
						};
463
						chunkGroupInfoMap.set(target, chunkGroupInfo);
464
					}
465
					chunkGroupInfo.availableModulesToBeMerged.push(
466
						resultingAvailableModules
467
					);
468
					outdatedChunkGroupInfo.add(chunkGroupInfo);
469
				}
470
			}
471
			queueConnect.clear();
472
			logger.timeEnd("calculating available modules");
473

    
474
			if (outdatedChunkGroupInfo.size > 0) {
475
				logger.time("merging available modules");
476
				// Execute the merge
477
				for (const info of outdatedChunkGroupInfo) {
478
					const availableModulesToBeMerged = info.availableModulesToBeMerged;
479
					let cachedMinAvailableModules = info.minAvailableModules;
480

    
481
					// 1. Get minimal available modules
482
					// It doesn't make sense to traverse a chunk again with more available modules.
483
					// This step calculates the minimal available modules and skips traversal when
484
					// the list didn't shrink.
485
					if (availableModulesToBeMerged.length > 1) {
486
						availableModulesToBeMerged.sort(bySetSize);
487
					}
488
					let changed = false;
489
					for (const availableModules of availableModulesToBeMerged) {
490
						if (cachedMinAvailableModules === undefined) {
491
							cachedMinAvailableModules = availableModules;
492
							info.minAvailableModules = cachedMinAvailableModules;
493
							info.minAvailableModulesOwned = false;
494
							changed = true;
495
						} else {
496
							if (info.minAvailableModulesOwned) {
497
								// We own it and can modify it
498
								for (const m of cachedMinAvailableModules) {
499
									if (!availableModules.has(m)) {
500
										cachedMinAvailableModules.delete(m);
501
										changed = true;
502
									}
503
								}
504
							} else {
505
								for (const m of cachedMinAvailableModules) {
506
									if (!availableModules.has(m)) {
507
										// cachedMinAvailableModules need to be modified
508
										// but we don't own it
509
										// construct a new Set as intersection of cachedMinAvailableModules and availableModules
510
										/** @type {Set<Module>} */
511
										const newSet = new Set();
512
										const iterator = cachedMinAvailableModules[
513
											Symbol.iterator
514
										]();
515
										/** @type {IteratorResult<Module>} */
516
										let it;
517
										while (!(it = iterator.next()).done) {
518
											const module = it.value;
519
											if (module === m) break;
520
											newSet.add(module);
521
										}
522
										while (!(it = iterator.next()).done) {
523
											const module = it.value;
524
											if (availableModules.has(module)) {
525
												newSet.add(module);
526
											}
527
										}
528
										cachedMinAvailableModules = newSet;
529
										info.minAvailableModulesOwned = true;
530
										info.minAvailableModules = newSet;
531

    
532
										// Update the cache from the first queue
533
										// if the chunkGroup is currently cached
534
										if (chunkGroup === info.chunkGroup) {
535
											minAvailableModules = cachedMinAvailableModules;
536
										}
537

    
538
										changed = true;
539
										break;
540
									}
541
								}
542
							}
543
						}
544
					}
545
					availableModulesToBeMerged.length = 0;
546
					if (!changed) continue;
547

    
548
					// 2. Reconsider skipped items
549
					for (const queueItem of info.skippedItems) {
550
						queue.push(queueItem);
551
					}
552
					info.skippedItems.length = 0;
553

    
554
					// 3. Reconsider children chunk groups
555
					if (info.children !== undefined) {
556
						const chunkGroup = info.chunkGroup;
557
						for (const c of info.children) {
558
							let connectList = queueConnect.get(chunkGroup);
559
							if (connectList === undefined) {
560
								connectList = new Set();
561
								queueConnect.set(chunkGroup, connectList);
562
							}
563
							connectList.add(c);
564
						}
565
					}
566
				}
567
				outdatedChunkGroupInfo.clear();
568
				logger.timeEnd("merging available modules");
569
			}
570
		}
571

    
572
		// Run queueDelayed when all items of the queue are processed
573
		// This is important to get the global indicing correct
574
		// Async blocks should be processed after all sync blocks are processed
575
		if (queue.length === 0) {
576
			const tempQueue = queue;
577
			queue = queueDelayed.reverse();
578
			queueDelayed = tempQueue;
579
		}
580
	}
581
};
582

    
583
/**
584
 *
585
 * @param {Set<DependenciesBlock>} blocksWithNestedBlocks flag for blocks that have nested blocks
586
 * @param {Map<ChunkGroup, ChunkGroupDep[]>} chunkDependencies dependencies for chunk groups
587
 * @param {Map<ChunkGroup, ChunkGroupInfo>} chunkGroupInfoMap mapping from chunk group to available modules
588
 */
589
const connectChunkGroups = (
590
	blocksWithNestedBlocks,
591
	chunkDependencies,
592
	chunkGroupInfoMap
593
) => {
594
	/** @type {Set<Module>} */
595
	let resultingAvailableModules;
596

    
597
	/**
598
	 * Helper function to check if all modules of a chunk are available
599
	 *
600
	 * @param {ChunkGroup} chunkGroup the chunkGroup to scan
601
	 * @param {Set<Module>} availableModules the comparitor set
602
	 * @returns {boolean} return true if all modules of a chunk are available
603
	 */
604
	const areModulesAvailable = (chunkGroup, availableModules) => {
605
		for (const chunk of chunkGroup.chunks) {
606
			for (const module of chunk.modulesIterable) {
607
				if (!availableModules.has(module)) return false;
608
			}
609
		}
610
		return true;
611
	};
612

    
613
	// For each edge in the basic chunk graph
614
	/**
615
	 * @param {ChunkGroupDep} dep the dependency used for filtering
616
	 * @returns {boolean} used to filter "edges" (aka Dependencies) that were pointing
617
	 * to modules that are already available. Also filters circular dependencies in the chunks graph
618
	 */
619
	const filterFn = dep => {
620
		const depChunkGroup = dep.chunkGroup;
621
		// TODO is this needed?
622
		if (blocksWithNestedBlocks.has(dep.block)) return true;
623
		if (areModulesAvailable(depChunkGroup, resultingAvailableModules)) {
624
			return false; // break all modules are already available
625
		}
626
		return true;
627
	};
628

    
629
	// For all deps, check if chunk groups need to be connected
630
	for (const [chunkGroup, deps] of chunkDependencies) {
631
		if (deps.length === 0) continue;
632

    
633
		// 1. Get info from chunk group info map
634
		const info = chunkGroupInfoMap.get(chunkGroup);
635
		resultingAvailableModules = info.resultingAvailableModules;
636

    
637
		// 2. Foreach edge
638
		for (let i = 0; i < deps.length; i++) {
639
			const dep = deps[i];
640

    
641
			// Filter inline, rather than creating a new array from `.filter()`
642
			// TODO check if inlining filterFn makes sense here
643
			if (!filterFn(dep)) {
644
				continue;
645
			}
646
			const depChunkGroup = dep.chunkGroup;
647
			const depBlock = dep.block;
648

    
649
			// 5. Connect block with chunk
650
			GraphHelpers.connectDependenciesBlockAndChunkGroup(
651
				depBlock,
652
				depChunkGroup
653
			);
654

    
655
			// 6. Connect chunk with parent
656
			GraphHelpers.connectChunkGroupParentAndChild(chunkGroup, depChunkGroup);
657
		}
658
	}
659
};
660

    
661
/**
662
 * Remove all unconnected chunk groups
663
 * @param {Compilation} compilation the compilation
664
 * @param {Iterable<ChunkGroup>} allCreatedChunkGroups all chunk groups that where created before
665
 */
666
const cleanupUnconnectedGroups = (compilation, allCreatedChunkGroups) => {
667
	for (const chunkGroup of allCreatedChunkGroups) {
668
		if (chunkGroup.getNumberOfParents() === 0) {
669
			for (const chunk of chunkGroup.chunks) {
670
				const idx = compilation.chunks.indexOf(chunk);
671
				if (idx >= 0) compilation.chunks.splice(idx, 1);
672
				chunk.remove("unconnected");
673
			}
674
			chunkGroup.remove("unconnected");
675
		}
676
	}
677
};
678

    
679
/**
680
 * This method creates the Chunk graph from the Module graph
681
 * @param {Compilation} compilation the compilation
682
 * @param {Entrypoint[]} inputChunkGroups chunk groups which are processed
683
 * @returns {void}
684
 */
685
const buildChunkGraph = (compilation, inputChunkGroups) => {
686
	// SHARED STATE
687

    
688
	/** @type {Map<ChunkGroup, ChunkGroupDep[]>} */
689
	const chunkDependencies = new Map();
690

    
691
	/** @type {Set<ChunkGroup>} */
692
	const allCreatedChunkGroups = new Set();
693

    
694
	/** @type {Map<ChunkGroup, ChunkGroupInfo>} */
695
	const chunkGroupInfoMap = new Map();
696

    
697
	/** @type {Set<DependenciesBlock>} */
698
	const blocksWithNestedBlocks = new Set();
699

    
700
	// PART ONE
701

    
702
	visitModules(
703
		compilation,
704
		inputChunkGroups,
705
		chunkGroupInfoMap,
706
		chunkDependencies,
707
		blocksWithNestedBlocks,
708
		allCreatedChunkGroups
709
	);
710

    
711
	// PART TWO
712

    
713
	connectChunkGroups(
714
		blocksWithNestedBlocks,
715
		chunkDependencies,
716
		chunkGroupInfoMap
717
	);
718

    
719
	// Cleaup work
720

    
721
	cleanupUnconnectedGroups(compilation, allCreatedChunkGroups);
722
};
723

    
724
module.exports = buildChunkGraph;
(139-139/144)