source: imaps-frontend/node_modules/webpack/lib/buildChunkGraph.js@ 79a0317

main
Last change on this file since 79a0317 was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 3 days ago

F4 Finalna Verzija

  • Property mode set to 100644
File size: 41.4 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const AsyncDependencyToInitialChunkError = require("./AsyncDependencyToInitialChunkError");
9const { connectChunkGroupParentAndChild } = require("./GraphHelpers");
10const ModuleGraphConnection = require("./ModuleGraphConnection");
11const { getEntryRuntime, mergeRuntime } = require("./util/runtime");
12
13/** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */
14/** @typedef {import("./Chunk")} Chunk */
15/** @typedef {import("./ChunkGroup")} ChunkGroup */
16/** @typedef {import("./Compilation")} Compilation */
17/** @typedef {import("./DependenciesBlock")} DependenciesBlock */
18/** @typedef {import("./Dependency")} Dependency */
19/** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */
20/** @typedef {import("./Entrypoint")} Entrypoint */
21/** @typedef {import("./Module")} Module */
22/** @typedef {import("./ModuleGraph")} ModuleGraph */
23/** @typedef {import("./ModuleGraphConnection").ConnectionState} ConnectionState */
24/** @typedef {import("./logging/Logger").Logger} Logger */
25/** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */
26
27/**
28 * @typedef {object} QueueItem
29 * @property {number} action
30 * @property {DependenciesBlock} block
31 * @property {Module} module
32 * @property {Chunk} chunk
33 * @property {ChunkGroup} chunkGroup
34 * @property {ChunkGroupInfo} chunkGroupInfo
35 */
36
37/**
38 * @typedef {object} ChunkGroupInfo
39 * @property {ChunkGroup} chunkGroup the chunk group
40 * @property {RuntimeSpec} runtime the runtimes
41 * @property {boolean} initialized is this chunk group initialized
42 * @property {bigint | undefined} minAvailableModules current minimal set of modules available at this point
43 * @property {bigint[]} availableModulesToBeMerged enqueued updates to the minimal set of available modules
44 * @property {Set<Module>=} skippedItems modules that were skipped because module is already available in parent chunks (need to reconsider when minAvailableModules is shrinking)
45 * @property {Set<[Module, ModuleGraphConnection[]]>=} skippedModuleConnections referenced modules that where skipped because they were not active in this runtime
46 * @property {bigint | undefined} resultingAvailableModules set of modules available including modules from this chunk group
47 * @property {Set<ChunkGroupInfo> | undefined} children set of children chunk groups, that will be revisited when availableModules shrink
48 * @property {Set<ChunkGroupInfo> | undefined} availableSources set of chunk groups that are the source for minAvailableModules
49 * @property {Set<ChunkGroupInfo> | undefined} availableChildren set of chunk groups which depend on the this chunk group as availableSource
50 * @property {number} preOrderIndex next pre order index
51 * @property {number} postOrderIndex next post order index
52 * @property {boolean} chunkLoading has a chunk loading mechanism
53 * @property {boolean} asyncChunks create async chunks
54 */
55
56/**
57 * @typedef {object} BlockChunkGroupConnection
58 * @property {ChunkGroupInfo} originChunkGroupInfo origin chunk group
59 * @property {ChunkGroup} chunkGroup referenced chunk group
60 */
61
62/** @typedef {(Module | ConnectionState | ModuleGraphConnection)[]} BlockModulesInTuples */
63/** @typedef {(Module | ConnectionState | ModuleGraphConnection[])[]} BlockModulesInFlattenTuples */
64/** @typedef {Map<DependenciesBlock, BlockModulesInFlattenTuples>} BlockModulesMap */
65/** @typedef {Map<Chunk, bigint>} MaskByChunk */
66/** @typedef {Set<DependenciesBlock>} BlocksWithNestedBlocks */
67/** @typedef {Map<AsyncDependenciesBlock, BlockChunkGroupConnection[]>} BlockConnections */
68/** @typedef {Map<ChunkGroup, ChunkGroupInfo>} ChunkGroupInfoMap */
69/** @typedef {Set<ChunkGroup>} AllCreatedChunkGroups */
70/** @typedef {Map<Entrypoint, Module[]>} InputEntrypointsAndModules */
71
72const ZERO_BIGINT = BigInt(0);
73const ONE_BIGINT = BigInt(1);
74
75/**
76 * @param {bigint} mask The mask to test
77 * @param {number} ordinal The ordinal of the bit to test
78 * @returns {boolean} If the ordinal-th bit is set in the mask
79 */
80const isOrdinalSetInMask = (mask, ordinal) =>
81 BigInt.asUintN(1, mask >> BigInt(ordinal)) !== ZERO_BIGINT;
82
83/**
84 * @param {ModuleGraphConnection[]} connections list of connections
85 * @param {RuntimeSpec} runtime for which runtime
86 * @returns {ConnectionState} connection state
87 */
88const getActiveStateOfConnections = (connections, runtime) => {
89 let merged = connections[0].getActiveState(runtime);
90 if (merged === true) return true;
91 for (let i = 1; i < connections.length; i++) {
92 const c = connections[i];
93 merged = ModuleGraphConnection.addConnectionStates(
94 merged,
95 c.getActiveState(runtime)
96 );
97 if (merged === true) return true;
98 }
99 return merged;
100};
101
102/**
103 * @param {Module} module module
104 * @param {ModuleGraph} moduleGraph module graph
105 * @param {RuntimeSpec} runtime runtime
106 * @param {BlockModulesMap} blockModulesMap block modules map
107 */
108const extractBlockModules = (module, moduleGraph, runtime, blockModulesMap) => {
109 /** @type {DependenciesBlock | undefined} */
110 let blockCache;
111 /** @type {BlockModulesInTuples | undefined} */
112 let modules;
113
114 /** @type {BlockModulesInTuples[]} */
115 const arrays = [];
116
117 /** @type {DependenciesBlock[]} */
118 const queue = [module];
119 while (queue.length > 0) {
120 const block = /** @type {DependenciesBlock} */ (queue.pop());
121 /** @type {Module[]} */
122 const arr = [];
123 arrays.push(arr);
124 blockModulesMap.set(block, arr);
125 for (const b of block.blocks) {
126 queue.push(b);
127 }
128 }
129
130 for (const connection of moduleGraph.getOutgoingConnections(module)) {
131 const d = connection.dependency;
132 // We skip connections without dependency
133 if (!d) continue;
134 const m = connection.module;
135 // We skip connections without Module pointer
136 if (!m) continue;
137 // We skip weak connections
138 if (connection.weak) continue;
139
140 const block = moduleGraph.getParentBlock(d);
141 let index = moduleGraph.getParentBlockIndex(d);
142
143 // deprecated fallback
144 if (index < 0) {
145 index = /** @type {DependenciesBlock} */ (block).dependencies.indexOf(d);
146 }
147
148 if (blockCache !== block) {
149 modules =
150 /** @type {BlockModulesInTuples} */
151 (
152 blockModulesMap.get(
153 (blockCache = /** @type {DependenciesBlock} */ (block))
154 )
155 );
156 }
157
158 const i = index * 3;
159 /** @type {BlockModulesInTuples} */
160 (modules)[i] = m;
161 /** @type {BlockModulesInTuples} */
162 (modules)[i + 1] = connection.getActiveState(runtime);
163 /** @type {BlockModulesInTuples} */
164 (modules)[i + 2] = connection;
165 }
166
167 for (const modules of arrays) {
168 if (modules.length === 0) continue;
169 let indexMap;
170 let length = 0;
171 outer: for (let j = 0; j < modules.length; j += 3) {
172 const m = modules[j];
173 if (m === undefined) continue;
174 const state = /** @type {ConnectionState} */ (modules[j + 1]);
175 const connection = /** @type {ModuleGraphConnection} */ (modules[j + 2]);
176 if (indexMap === undefined) {
177 let i = 0;
178 for (; i < length; i += 3) {
179 if (modules[i] === m) {
180 const merged = /** @type {ConnectionState} */ (modules[i + 1]);
181 /** @type {ModuleGraphConnection[]} */
182 (/** @type {unknown} */ (modules[i + 2])).push(connection);
183 if (merged === true) continue outer;
184 modules[i + 1] = ModuleGraphConnection.addConnectionStates(
185 merged,
186 state
187 );
188 continue outer;
189 }
190 }
191 modules[length] = m;
192 length++;
193 modules[length] = state;
194 length++;
195 /** @type {ModuleGraphConnection[]} */
196 (/** @type {unknown} */ (modules[length])) = [connection];
197 length++;
198 if (length > 30) {
199 // To avoid worse case performance, we will use an index map for
200 // linear cost access, which allows to maintain O(n) complexity
201 // while keeping allocations down to a minimum
202 indexMap = new Map();
203 for (let i = 0; i < length; i += 3) {
204 indexMap.set(modules[i], i + 1);
205 }
206 }
207 } else {
208 const idx = indexMap.get(m);
209 if (idx !== undefined) {
210 const merged = /** @type {ConnectionState} */ (modules[idx]);
211 /** @type {ModuleGraphConnection[]} */
212 (/** @type {unknown} */ (modules[idx + 1])).push(connection);
213 if (merged === true) continue;
214 modules[idx] = ModuleGraphConnection.addConnectionStates(
215 merged,
216 state
217 );
218 } else {
219 modules[length] = m;
220 length++;
221 modules[length] = state;
222 indexMap.set(m, length);
223 length++;
224 /** @type {ModuleGraphConnection[]} */
225 (
226 /** @type {unknown} */
227 (modules[length])
228 ) = [connection];
229 length++;
230 }
231 }
232 }
233 modules.length = length;
234 }
235};
236
237/**
238 * @param {Logger} logger a logger
239 * @param {Compilation} compilation the compilation
240 * @param {InputEntrypointsAndModules} inputEntrypointsAndModules chunk groups which are processed with the modules
241 * @param {ChunkGroupInfoMap} chunkGroupInfoMap mapping from chunk group to available modules
242 * @param {BlockConnections} blockConnections connection for blocks
243 * @param {BlocksWithNestedBlocks} blocksWithNestedBlocks flag for blocks that have nested blocks
244 * @param {AllCreatedChunkGroups} allCreatedChunkGroups filled with all chunk groups that are created here
245 * @param {MaskByChunk} maskByChunk module content mask by chunk
246 */
247const visitModules = (
248 logger,
249 compilation,
250 inputEntrypointsAndModules,
251 chunkGroupInfoMap,
252 blockConnections,
253 blocksWithNestedBlocks,
254 allCreatedChunkGroups,
255 maskByChunk
256) => {
257 const { moduleGraph, chunkGraph, moduleMemCaches } = compilation;
258
259 const blockModulesRuntimeMap = new Map();
260
261 /** @type {BlockModulesMap | undefined} */
262 let blockModulesMap;
263
264 /** @type {Map<Module, number>} */
265 const ordinalByModule = new Map();
266
267 /**
268 * @param {Module} module The module to look up
269 * @returns {number} The ordinal of the module in masks
270 */
271 const getModuleOrdinal = module => {
272 let ordinal = ordinalByModule.get(module);
273 if (ordinal === undefined) {
274 ordinal = ordinalByModule.size;
275 ordinalByModule.set(module, ordinal);
276 }
277 return ordinal;
278 };
279
280 for (const chunk of compilation.chunks) {
281 let mask = ZERO_BIGINT;
282 for (const m of chunkGraph.getChunkModulesIterable(chunk)) {
283 mask |= ONE_BIGINT << BigInt(getModuleOrdinal(m));
284 }
285 maskByChunk.set(chunk, mask);
286 }
287
288 /**
289 * @param {DependenciesBlock} block block
290 * @param {RuntimeSpec} runtime runtime
291 * @returns {BlockModulesInFlattenTuples} block modules in flatten tuples
292 */
293 const getBlockModules = (block, runtime) => {
294 blockModulesMap = blockModulesRuntimeMap.get(runtime);
295 if (blockModulesMap === undefined) {
296 blockModulesMap = new Map();
297 blockModulesRuntimeMap.set(runtime, blockModulesMap);
298 }
299 let blockModules = blockModulesMap.get(block);
300 if (blockModules !== undefined) return blockModules;
301 const module = /** @type {Module} */ (block.getRootBlock());
302 const memCache = moduleMemCaches && moduleMemCaches.get(module);
303 if (memCache !== undefined) {
304 const map = memCache.provide(
305 "bundleChunkGraph.blockModules",
306 runtime,
307 () => {
308 logger.time("visitModules: prepare");
309 const map = new Map();
310 extractBlockModules(module, moduleGraph, runtime, map);
311 logger.timeAggregate("visitModules: prepare");
312 return map;
313 }
314 );
315 for (const [block, blockModules] of map)
316 blockModulesMap.set(block, blockModules);
317 return map.get(block);
318 }
319 logger.time("visitModules: prepare");
320 extractBlockModules(module, moduleGraph, runtime, blockModulesMap);
321 blockModules =
322 /** @type {BlockModulesInFlattenTuples} */
323 (blockModulesMap.get(block));
324 logger.timeAggregate("visitModules: prepare");
325 return blockModules;
326 };
327
328 let statProcessedQueueItems = 0;
329 let statProcessedBlocks = 0;
330 let statConnectedChunkGroups = 0;
331 let statProcessedChunkGroupsForMerging = 0;
332 let statMergedAvailableModuleSets = 0;
333 const statForkedAvailableModules = 0;
334 const statForkedAvailableModulesCount = 0;
335 const statForkedAvailableModulesCountPlus = 0;
336 const statForkedMergedModulesCount = 0;
337 const statForkedMergedModulesCountPlus = 0;
338 const statForkedResultModulesCount = 0;
339 let statChunkGroupInfoUpdated = 0;
340 let statChildChunkGroupsReconnected = 0;
341
342 let nextChunkGroupIndex = 0;
343 let nextFreeModulePreOrderIndex = 0;
344 let nextFreeModulePostOrderIndex = 0;
345
346 /** @type {Map<DependenciesBlock, ChunkGroupInfo>} */
347 const blockChunkGroups = new Map();
348
349 /** @type {Map<ChunkGroupInfo, Set<DependenciesBlock>>} */
350 const blocksByChunkGroups = new Map();
351
352 /** @type {Map<string, ChunkGroupInfo>} */
353 const namedChunkGroups = new Map();
354
355 /** @type {Map<string, ChunkGroupInfo>} */
356 const namedAsyncEntrypoints = new Map();
357
358 /** @type {Set<ChunkGroupInfo>} */
359 const outdatedOrderIndexChunkGroups = new Set();
360
361 const ADD_AND_ENTER_ENTRY_MODULE = 0;
362 const ADD_AND_ENTER_MODULE = 1;
363 const ENTER_MODULE = 2;
364 const PROCESS_BLOCK = 3;
365 const PROCESS_ENTRY_BLOCK = 4;
366 const LEAVE_MODULE = 5;
367
368 /** @type {QueueItem[]} */
369 let queue = [];
370
371 /** @type {Map<ChunkGroupInfo, Set<[ChunkGroupInfo, QueueItem | null]>>} */
372 const queueConnect = new Map();
373 /** @type {Set<ChunkGroupInfo>} */
374 const chunkGroupsForCombining = new Set();
375
376 // Fill queue with entrypoint modules
377 // Create ChunkGroupInfo for entrypoints
378 for (const [chunkGroup, modules] of inputEntrypointsAndModules) {
379 const runtime = getEntryRuntime(
380 compilation,
381 /** @type {string} */ (chunkGroup.name),
382 chunkGroup.options
383 );
384 /** @type {ChunkGroupInfo} */
385 const chunkGroupInfo = {
386 initialized: false,
387 chunkGroup,
388 runtime,
389 minAvailableModules: undefined,
390 availableModulesToBeMerged: [],
391 skippedItems: undefined,
392 resultingAvailableModules: undefined,
393 children: undefined,
394 availableSources: undefined,
395 availableChildren: undefined,
396 preOrderIndex: 0,
397 postOrderIndex: 0,
398 chunkLoading:
399 chunkGroup.options.chunkLoading !== undefined
400 ? chunkGroup.options.chunkLoading !== false
401 : compilation.outputOptions.chunkLoading !== false,
402 asyncChunks:
403 chunkGroup.options.asyncChunks !== undefined
404 ? chunkGroup.options.asyncChunks
405 : compilation.outputOptions.asyncChunks !== false
406 };
407 chunkGroup.index = nextChunkGroupIndex++;
408 if (chunkGroup.getNumberOfParents() > 0) {
409 // minAvailableModules for child entrypoints are unknown yet, set to undefined.
410 // This means no module is added until other sets are merged into
411 // this minAvailableModules (by the parent entrypoints)
412 const skippedItems = new Set(modules);
413 chunkGroupInfo.skippedItems = skippedItems;
414 chunkGroupsForCombining.add(chunkGroupInfo);
415 } else {
416 // The application may start here: We start with an empty list of available modules
417 chunkGroupInfo.minAvailableModules = ZERO_BIGINT;
418 const chunk = chunkGroup.getEntrypointChunk();
419 for (const module of modules) {
420 queue.push({
421 action: ADD_AND_ENTER_MODULE,
422 block: module,
423 module,
424 chunk,
425 chunkGroup,
426 chunkGroupInfo
427 });
428 }
429 }
430 chunkGroupInfoMap.set(chunkGroup, chunkGroupInfo);
431 if (chunkGroup.name) {
432 namedChunkGroups.set(chunkGroup.name, chunkGroupInfo);
433 }
434 }
435 // Fill availableSources with parent-child dependencies between entrypoints
436 for (const chunkGroupInfo of chunkGroupsForCombining) {
437 const { chunkGroup } = chunkGroupInfo;
438 chunkGroupInfo.availableSources = new Set();
439 for (const parent of chunkGroup.parentsIterable) {
440 const parentChunkGroupInfo =
441 /** @type {ChunkGroupInfo} */
442 (chunkGroupInfoMap.get(parent));
443 chunkGroupInfo.availableSources.add(parentChunkGroupInfo);
444 if (parentChunkGroupInfo.availableChildren === undefined) {
445 parentChunkGroupInfo.availableChildren = new Set();
446 }
447 parentChunkGroupInfo.availableChildren.add(chunkGroupInfo);
448 }
449 }
450 // pop() is used to read from the queue
451 // so it need to be reversed to be iterated in
452 // correct order
453 queue.reverse();
454
455 /** @type {Set<ChunkGroupInfo>} */
456 const outdatedChunkGroupInfo = new Set();
457 /** @type {Set<[ChunkGroupInfo, QueueItem | null]>} */
458 const chunkGroupsForMerging = new Set();
459 /** @type {QueueItem[]} */
460 let queueDelayed = [];
461
462 /** @type {[Module, ModuleGraphConnection[]][]} */
463 const skipConnectionBuffer = [];
464 /** @type {Module[]} */
465 const skipBuffer = [];
466 /** @type {QueueItem[]} */
467 const queueBuffer = [];
468
469 /** @type {Module} */
470 let module;
471 /** @type {Chunk} */
472 let chunk;
473 /** @type {ChunkGroup} */
474 let chunkGroup;
475 /** @type {DependenciesBlock} */
476 let block;
477 /** @type {ChunkGroupInfo} */
478 let chunkGroupInfo;
479
480 // For each async Block in graph
481 /**
482 * @param {AsyncDependenciesBlock} b iterating over each Async DepBlock
483 * @returns {void}
484 */
485 const iteratorBlock = b => {
486 // 1. We create a chunk group with single chunk in it for this Block
487 // but only once (blockChunkGroups map)
488 /** @type {ChunkGroupInfo | undefined} */
489 let cgi = blockChunkGroups.get(b);
490 /** @type {ChunkGroup | undefined} */
491 let c;
492 /** @type {Entrypoint | undefined} */
493 let entrypoint;
494 const entryOptions = b.groupOptions && b.groupOptions.entryOptions;
495 if (cgi === undefined) {
496 const chunkName = (b.groupOptions && b.groupOptions.name) || b.chunkName;
497 if (entryOptions) {
498 cgi = namedAsyncEntrypoints.get(/** @type {string} */ (chunkName));
499 if (!cgi) {
500 entrypoint = compilation.addAsyncEntrypoint(
501 entryOptions,
502 module,
503 /** @type {DependencyLocation} */ (b.loc),
504 /** @type {string} */ (b.request)
505 );
506 maskByChunk.set(entrypoint.chunks[0], ZERO_BIGINT);
507 entrypoint.index = nextChunkGroupIndex++;
508 cgi = {
509 chunkGroup: entrypoint,
510 initialized: false,
511 runtime: entrypoint.options.runtime || entrypoint.name,
512 minAvailableModules: ZERO_BIGINT,
513 availableModulesToBeMerged: [],
514 skippedItems: undefined,
515 resultingAvailableModules: undefined,
516 children: undefined,
517 availableSources: undefined,
518 availableChildren: undefined,
519 preOrderIndex: 0,
520 postOrderIndex: 0,
521 chunkLoading:
522 entryOptions.chunkLoading !== undefined
523 ? entryOptions.chunkLoading !== false
524 : chunkGroupInfo.chunkLoading,
525 asyncChunks:
526 entryOptions.asyncChunks !== undefined
527 ? entryOptions.asyncChunks
528 : chunkGroupInfo.asyncChunks
529 };
530 chunkGroupInfoMap.set(entrypoint, cgi);
531
532 chunkGraph.connectBlockAndChunkGroup(b, entrypoint);
533 if (chunkName) {
534 namedAsyncEntrypoints.set(chunkName, cgi);
535 }
536 } else {
537 entrypoint = /** @type {Entrypoint} */ (cgi.chunkGroup);
538 // TODO merge entryOptions
539 entrypoint.addOrigin(
540 module,
541 /** @type {DependencyLocation} */ (b.loc),
542 /** @type {string} */ (b.request)
543 );
544 chunkGraph.connectBlockAndChunkGroup(b, entrypoint);
545 }
546
547 // 2. We enqueue the DependenciesBlock for traversal
548 queueDelayed.push({
549 action: PROCESS_ENTRY_BLOCK,
550 block: b,
551 module,
552 chunk: entrypoint.chunks[0],
553 chunkGroup: entrypoint,
554 chunkGroupInfo: cgi
555 });
556 } else if (!chunkGroupInfo.asyncChunks || !chunkGroupInfo.chunkLoading) {
557 // Just queue the block into the current chunk group
558 queue.push({
559 action: PROCESS_BLOCK,
560 block: b,
561 module,
562 chunk,
563 chunkGroup,
564 chunkGroupInfo
565 });
566 } else {
567 cgi = chunkName ? namedChunkGroups.get(chunkName) : undefined;
568 if (!cgi) {
569 c = compilation.addChunkInGroup(
570 b.groupOptions || b.chunkName,
571 module,
572 /** @type {DependencyLocation} */ (b.loc),
573 /** @type {string} */ (b.request)
574 );
575 maskByChunk.set(c.chunks[0], ZERO_BIGINT);
576 c.index = nextChunkGroupIndex++;
577 cgi = {
578 initialized: false,
579 chunkGroup: c,
580 runtime: chunkGroupInfo.runtime,
581 minAvailableModules: undefined,
582 availableModulesToBeMerged: [],
583 skippedItems: undefined,
584 resultingAvailableModules: undefined,
585 children: undefined,
586 availableSources: undefined,
587 availableChildren: undefined,
588 preOrderIndex: 0,
589 postOrderIndex: 0,
590 chunkLoading: chunkGroupInfo.chunkLoading,
591 asyncChunks: chunkGroupInfo.asyncChunks
592 };
593 allCreatedChunkGroups.add(c);
594 chunkGroupInfoMap.set(c, cgi);
595 if (chunkName) {
596 namedChunkGroups.set(chunkName, cgi);
597 }
598 } else {
599 c = cgi.chunkGroup;
600 if (c.isInitial()) {
601 compilation.errors.push(
602 new AsyncDependencyToInitialChunkError(
603 /** @type {string} */ (chunkName),
604 module,
605 /** @type {DependencyLocation} */ (b.loc)
606 )
607 );
608 c = chunkGroup;
609 } else {
610 c.addOptions(b.groupOptions);
611 }
612 c.addOrigin(
613 module,
614 /** @type {DependencyLocation} */ (b.loc),
615 /** @type {string} */ (b.request)
616 );
617 }
618 blockConnections.set(b, []);
619 }
620 blockChunkGroups.set(b, /** @type {ChunkGroupInfo} */ (cgi));
621 } else if (entryOptions) {
622 entrypoint = /** @type {Entrypoint} */ (cgi.chunkGroup);
623 } else {
624 c = cgi.chunkGroup;
625 }
626
627 if (c !== undefined) {
628 // 2. We store the connection for the block
629 // to connect it later if needed
630 /** @type {BlockChunkGroupConnection[]} */
631 (blockConnections.get(b)).push({
632 originChunkGroupInfo: chunkGroupInfo,
633 chunkGroup: c
634 });
635
636 // 3. We enqueue the chunk group info creation/updating
637 let connectList = queueConnect.get(chunkGroupInfo);
638 if (connectList === undefined) {
639 connectList = new Set();
640 queueConnect.set(chunkGroupInfo, connectList);
641 }
642 connectList.add([
643 /** @type {ChunkGroupInfo} */ (cgi),
644 {
645 action: PROCESS_BLOCK,
646 block: b,
647 module,
648 chunk: c.chunks[0],
649 chunkGroup: c,
650 chunkGroupInfo: /** @type {ChunkGroupInfo} */ (cgi)
651 }
652 ]);
653 } else if (entrypoint !== undefined) {
654 chunkGroupInfo.chunkGroup.addAsyncEntrypoint(entrypoint);
655 }
656 };
657
658 /**
659 * @param {DependenciesBlock} block the block
660 * @returns {void}
661 */
662 const processBlock = block => {
663 statProcessedBlocks++;
664 // get prepared block info
665 const blockModules = getBlockModules(block, chunkGroupInfo.runtime);
666
667 if (blockModules !== undefined) {
668 const minAvailableModules =
669 /** @type {bigint} */
670 (chunkGroupInfo.minAvailableModules);
671 // Buffer items because order need to be reversed to get indices correct
672 // Traverse all referenced modules
673 for (let i = 0, len = blockModules.length; i < len; i += 3) {
674 const refModule = /** @type {Module} */ (blockModules[i]);
675 // For single comparisons this might be cheaper
676 const isModuleInChunk = chunkGraph.isModuleInChunk(refModule, chunk);
677
678 if (isModuleInChunk) {
679 // skip early if already connected
680 continue;
681 }
682
683 const refOrdinal = /** @type {number} */ getModuleOrdinal(refModule);
684 const activeState = /** @type {ConnectionState} */ (
685 blockModules[i + 1]
686 );
687 if (activeState !== true) {
688 const connections = /** @type {ModuleGraphConnection[]} */ (
689 blockModules[i + 2]
690 );
691 skipConnectionBuffer.push([refModule, connections]);
692 // We skip inactive connections
693 if (activeState === false) continue;
694 } else if (isOrdinalSetInMask(minAvailableModules, refOrdinal)) {
695 // already in parent chunks, skip it for now
696 skipBuffer.push(refModule);
697 continue;
698 }
699 // enqueue, then add and enter to be in the correct order
700 // this is relevant with circular dependencies
701 queueBuffer.push({
702 action: activeState === true ? ADD_AND_ENTER_MODULE : PROCESS_BLOCK,
703 block: refModule,
704 module: refModule,
705 chunk,
706 chunkGroup,
707 chunkGroupInfo
708 });
709 }
710 // Add buffered items in reverse order
711 if (skipConnectionBuffer.length > 0) {
712 let { skippedModuleConnections } = chunkGroupInfo;
713 if (skippedModuleConnections === undefined) {
714 chunkGroupInfo.skippedModuleConnections = skippedModuleConnections =
715 new Set();
716 }
717 for (let i = skipConnectionBuffer.length - 1; i >= 0; i--) {
718 skippedModuleConnections.add(skipConnectionBuffer[i]);
719 }
720 skipConnectionBuffer.length = 0;
721 }
722 if (skipBuffer.length > 0) {
723 let { skippedItems } = chunkGroupInfo;
724 if (skippedItems === undefined) {
725 chunkGroupInfo.skippedItems = skippedItems = new Set();
726 }
727 for (let i = skipBuffer.length - 1; i >= 0; i--) {
728 skippedItems.add(skipBuffer[i]);
729 }
730 skipBuffer.length = 0;
731 }
732 if (queueBuffer.length > 0) {
733 for (let i = queueBuffer.length - 1; i >= 0; i--) {
734 queue.push(queueBuffer[i]);
735 }
736 queueBuffer.length = 0;
737 }
738 }
739
740 // Traverse all Blocks
741 for (const b of block.blocks) {
742 iteratorBlock(b);
743 }
744
745 if (block.blocks.length > 0 && module !== block) {
746 blocksWithNestedBlocks.add(block);
747 }
748 };
749
750 /**
751 * @param {DependenciesBlock} block the block
752 * @returns {void}
753 */
754 const processEntryBlock = block => {
755 statProcessedBlocks++;
756 // get prepared block info
757 const blockModules = getBlockModules(block, chunkGroupInfo.runtime);
758
759 if (blockModules !== undefined) {
760 // Traverse all referenced modules in reverse order
761 for (let i = blockModules.length - 3; i >= 0; i -= 3) {
762 const refModule = /** @type {Module} */ (blockModules[i]);
763 const activeState = /** @type {ConnectionState} */ (
764 blockModules[i + 1]
765 );
766 // enqueue, then add and enter to be in the correct order
767 // this is relevant with circular dependencies
768 queue.push({
769 action:
770 activeState === true ? ADD_AND_ENTER_ENTRY_MODULE : PROCESS_BLOCK,
771 block: refModule,
772 module: refModule,
773 chunk,
774 chunkGroup,
775 chunkGroupInfo
776 });
777 }
778 }
779
780 // Traverse all Blocks
781 for (const b of block.blocks) {
782 iteratorBlock(b);
783 }
784
785 if (block.blocks.length > 0 && module !== block) {
786 blocksWithNestedBlocks.add(block);
787 }
788 };
789
790 const processQueue = () => {
791 while (queue.length) {
792 statProcessedQueueItems++;
793 const queueItem = /** @type {QueueItem} */ (queue.pop());
794 module = queueItem.module;
795 block = queueItem.block;
796 chunk = queueItem.chunk;
797 chunkGroup = queueItem.chunkGroup;
798 chunkGroupInfo = queueItem.chunkGroupInfo;
799
800 switch (queueItem.action) {
801 case ADD_AND_ENTER_ENTRY_MODULE:
802 chunkGraph.connectChunkAndEntryModule(
803 chunk,
804 module,
805 /** @type {Entrypoint} */ (chunkGroup)
806 );
807 // fallthrough
808 case ADD_AND_ENTER_MODULE: {
809 const isModuleInChunk = chunkGraph.isModuleInChunk(module, chunk);
810
811 if (isModuleInChunk) {
812 // already connected, skip it
813 break;
814 }
815 // We connect Module and Chunk
816 chunkGraph.connectChunkAndModule(chunk, module);
817 const moduleOrdinal = getModuleOrdinal(module);
818 let chunkMask = /** @type {bigint} */ (maskByChunk.get(chunk));
819 chunkMask |= ONE_BIGINT << BigInt(moduleOrdinal);
820 maskByChunk.set(chunk, chunkMask);
821 }
822 // fallthrough
823 case ENTER_MODULE: {
824 const index = chunkGroup.getModulePreOrderIndex(module);
825 if (index === undefined) {
826 chunkGroup.setModulePreOrderIndex(
827 module,
828 chunkGroupInfo.preOrderIndex++
829 );
830 }
831
832 if (
833 moduleGraph.setPreOrderIndexIfUnset(
834 module,
835 nextFreeModulePreOrderIndex
836 )
837 ) {
838 nextFreeModulePreOrderIndex++;
839 }
840
841 // reuse queueItem
842 queueItem.action = LEAVE_MODULE;
843 queue.push(queueItem);
844 }
845 // fallthrough
846 case PROCESS_BLOCK: {
847 processBlock(block);
848 break;
849 }
850 case PROCESS_ENTRY_BLOCK: {
851 processEntryBlock(block);
852 break;
853 }
854 case LEAVE_MODULE: {
855 const index = chunkGroup.getModulePostOrderIndex(module);
856 if (index === undefined) {
857 chunkGroup.setModulePostOrderIndex(
858 module,
859 chunkGroupInfo.postOrderIndex++
860 );
861 }
862
863 if (
864 moduleGraph.setPostOrderIndexIfUnset(
865 module,
866 nextFreeModulePostOrderIndex
867 )
868 ) {
869 nextFreeModulePostOrderIndex++;
870 }
871 break;
872 }
873 }
874 }
875 };
876
877 /**
878 * @param {ChunkGroupInfo} chunkGroupInfo The info object for the chunk group
879 * @returns {bigint} The mask of available modules after the chunk group
880 */
881 const calculateResultingAvailableModules = chunkGroupInfo => {
882 if (chunkGroupInfo.resultingAvailableModules !== undefined)
883 return chunkGroupInfo.resultingAvailableModules;
884
885 let resultingAvailableModules = /** @type {bigint} */ (
886 chunkGroupInfo.minAvailableModules
887 );
888
889 // add the modules from the chunk group to the set
890 for (const chunk of chunkGroupInfo.chunkGroup.chunks) {
891 const mask = /** @type {bigint} */ (maskByChunk.get(chunk));
892 resultingAvailableModules |= mask;
893 }
894
895 return (chunkGroupInfo.resultingAvailableModules =
896 resultingAvailableModules);
897 };
898
899 const processConnectQueue = () => {
900 // Figure out new parents for chunk groups
901 // to get new available modules for these children
902 for (const [chunkGroupInfo, targets] of queueConnect) {
903 // 1. Add new targets to the list of children
904 if (chunkGroupInfo.children === undefined) {
905 chunkGroupInfo.children = new Set();
906 }
907 for (const [target] of targets) {
908 chunkGroupInfo.children.add(target);
909 }
910
911 // 2. Calculate resulting available modules
912 const resultingAvailableModules =
913 calculateResultingAvailableModules(chunkGroupInfo);
914
915 const runtime = chunkGroupInfo.runtime;
916
917 // 3. Update chunk group info
918 for (const [target, processBlock] of targets) {
919 target.availableModulesToBeMerged.push(resultingAvailableModules);
920 chunkGroupsForMerging.add([target, processBlock]);
921 const oldRuntime = target.runtime;
922 const newRuntime = mergeRuntime(oldRuntime, runtime);
923 if (oldRuntime !== newRuntime) {
924 target.runtime = newRuntime;
925 outdatedChunkGroupInfo.add(target);
926 }
927 }
928
929 statConnectedChunkGroups += targets.size;
930 }
931 queueConnect.clear();
932 };
933
934 const processChunkGroupsForMerging = () => {
935 statProcessedChunkGroupsForMerging += chunkGroupsForMerging.size;
936
937 // Execute the merge
938 for (const [info, processBlock] of chunkGroupsForMerging) {
939 const availableModulesToBeMerged = info.availableModulesToBeMerged;
940 const cachedMinAvailableModules = info.minAvailableModules;
941 let minAvailableModules = cachedMinAvailableModules;
942
943 statMergedAvailableModuleSets += availableModulesToBeMerged.length;
944
945 for (const availableModules of availableModulesToBeMerged) {
946 if (minAvailableModules === undefined) {
947 minAvailableModules = availableModules;
948 } else {
949 minAvailableModules &= availableModules;
950 }
951 }
952
953 const changed = minAvailableModules !== cachedMinAvailableModules;
954
955 availableModulesToBeMerged.length = 0;
956 if (changed) {
957 info.minAvailableModules = minAvailableModules;
958 info.resultingAvailableModules = undefined;
959 outdatedChunkGroupInfo.add(info);
960 }
961
962 if (processBlock) {
963 let blocks = blocksByChunkGroups.get(info);
964 if (!blocks) {
965 blocksByChunkGroups.set(info, (blocks = new Set()));
966 }
967
968 // Whether to walk block depends on minAvailableModules and input block.
969 // We can treat creating chunk group as a function with 2 input, entry block and minAvailableModules
970 // If input is the same, we can skip re-walk
971 let needWalkBlock = !info.initialized || changed;
972 if (!blocks.has(processBlock.block)) {
973 needWalkBlock = true;
974 blocks.add(processBlock.block);
975 }
976
977 if (needWalkBlock) {
978 info.initialized = true;
979 queueDelayed.push(processBlock);
980 }
981 }
982 }
983 chunkGroupsForMerging.clear();
984 };
985
986 const processChunkGroupsForCombining = () => {
987 for (const info of chunkGroupsForCombining) {
988 for (const source of /** @type {Set<ChunkGroupInfo>} */ (
989 info.availableSources
990 )) {
991 if (source.minAvailableModules === undefined) {
992 chunkGroupsForCombining.delete(info);
993 break;
994 }
995 }
996 }
997
998 for (const info of chunkGroupsForCombining) {
999 let availableModules = ZERO_BIGINT;
1000 // combine minAvailableModules from all resultingAvailableModules
1001 for (const source of /** @type {Set<ChunkGroupInfo>} */ (
1002 info.availableSources
1003 )) {
1004 const resultingAvailableModules =
1005 calculateResultingAvailableModules(source);
1006 availableModules |= resultingAvailableModules;
1007 }
1008 info.minAvailableModules = availableModules;
1009 info.resultingAvailableModules = undefined;
1010 outdatedChunkGroupInfo.add(info);
1011 }
1012 chunkGroupsForCombining.clear();
1013 };
1014
1015 const processOutdatedChunkGroupInfo = () => {
1016 statChunkGroupInfoUpdated += outdatedChunkGroupInfo.size;
1017 // Revisit skipped elements
1018 for (const info of outdatedChunkGroupInfo) {
1019 // 1. Reconsider skipped items
1020 if (info.skippedItems !== undefined) {
1021 const minAvailableModules =
1022 /** @type {bigint} */
1023 (info.minAvailableModules);
1024 for (const module of info.skippedItems) {
1025 const ordinal = getModuleOrdinal(module);
1026 if (!isOrdinalSetInMask(minAvailableModules, ordinal)) {
1027 queue.push({
1028 action: ADD_AND_ENTER_MODULE,
1029 block: module,
1030 module,
1031 chunk: info.chunkGroup.chunks[0],
1032 chunkGroup: info.chunkGroup,
1033 chunkGroupInfo: info
1034 });
1035 info.skippedItems.delete(module);
1036 }
1037 }
1038 }
1039
1040 // 2. Reconsider skipped connections
1041 if (info.skippedModuleConnections !== undefined) {
1042 const minAvailableModules =
1043 /** @type {bigint} */
1044 (info.minAvailableModules);
1045 for (const entry of info.skippedModuleConnections) {
1046 const [module, connections] = entry;
1047 const activeState = getActiveStateOfConnections(
1048 connections,
1049 info.runtime
1050 );
1051 if (activeState === false) continue;
1052 if (activeState === true) {
1053 const ordinal = getModuleOrdinal(module);
1054 info.skippedModuleConnections.delete(entry);
1055 if (isOrdinalSetInMask(minAvailableModules, ordinal)) {
1056 /** @type {NonNullable<ChunkGroupInfo["skippedItems"]>} */
1057 (info.skippedItems).add(module);
1058 continue;
1059 }
1060 }
1061 queue.push({
1062 action: activeState === true ? ADD_AND_ENTER_MODULE : PROCESS_BLOCK,
1063 block: module,
1064 module,
1065 chunk: info.chunkGroup.chunks[0],
1066 chunkGroup: info.chunkGroup,
1067 chunkGroupInfo: info
1068 });
1069 }
1070 }
1071
1072 // 2. Reconsider children chunk groups
1073 if (info.children !== undefined) {
1074 statChildChunkGroupsReconnected += info.children.size;
1075 for (const cgi of info.children) {
1076 let connectList = queueConnect.get(info);
1077 if (connectList === undefined) {
1078 connectList = new Set();
1079 queueConnect.set(info, connectList);
1080 }
1081 connectList.add([cgi, null]);
1082 }
1083 }
1084
1085 // 3. Reconsider chunk groups for combining
1086 if (info.availableChildren !== undefined) {
1087 for (const cgi of info.availableChildren) {
1088 chunkGroupsForCombining.add(cgi);
1089 }
1090 }
1091 outdatedOrderIndexChunkGroups.add(info);
1092 }
1093 outdatedChunkGroupInfo.clear();
1094 };
1095
1096 // Iterative traversal of the Module graph
1097 // Recursive would be simpler to write but could result in Stack Overflows
1098 while (queue.length || queueConnect.size) {
1099 logger.time("visitModules: visiting");
1100 processQueue();
1101 logger.timeAggregateEnd("visitModules: prepare");
1102 logger.timeEnd("visitModules: visiting");
1103
1104 if (chunkGroupsForCombining.size > 0) {
1105 logger.time("visitModules: combine available modules");
1106 processChunkGroupsForCombining();
1107 logger.timeEnd("visitModules: combine available modules");
1108 }
1109
1110 if (queueConnect.size > 0) {
1111 logger.time("visitModules: calculating available modules");
1112 processConnectQueue();
1113 logger.timeEnd("visitModules: calculating available modules");
1114
1115 if (chunkGroupsForMerging.size > 0) {
1116 logger.time("visitModules: merging available modules");
1117 processChunkGroupsForMerging();
1118 logger.timeEnd("visitModules: merging available modules");
1119 }
1120 }
1121
1122 if (outdatedChunkGroupInfo.size > 0) {
1123 logger.time("visitModules: check modules for revisit");
1124 processOutdatedChunkGroupInfo();
1125 logger.timeEnd("visitModules: check modules for revisit");
1126 }
1127
1128 // Run queueDelayed when all items of the queue are processed
1129 // This is important to get the global indexing correct
1130 // Async blocks should be processed after all sync blocks are processed
1131 if (queue.length === 0) {
1132 const tempQueue = queue;
1133 queue = queueDelayed.reverse();
1134 queueDelayed = tempQueue;
1135 }
1136 }
1137
1138 for (const info of outdatedOrderIndexChunkGroups) {
1139 const { chunkGroup, runtime } = info;
1140
1141 const blocks = blocksByChunkGroups.get(info);
1142
1143 if (!blocks) {
1144 continue;
1145 }
1146
1147 for (const block of blocks) {
1148 let preOrderIndex = 0;
1149 let postOrderIndex = 0;
1150 /**
1151 * @param {DependenciesBlock} current current
1152 * @param {BlocksWithNestedBlocks} visited visited dependencies blocks
1153 */
1154 const process = (current, visited) => {
1155 const blockModules = getBlockModules(current, runtime);
1156 for (let i = 0, len = blockModules.length; i < len; i += 3) {
1157 const activeState = /** @type {ConnectionState} */ (
1158 blockModules[i + 1]
1159 );
1160 if (activeState === false) {
1161 continue;
1162 }
1163 const refModule = /** @type {Module} */ (blockModules[i]);
1164 if (visited.has(refModule)) {
1165 continue;
1166 }
1167
1168 visited.add(refModule);
1169
1170 if (refModule) {
1171 chunkGroup.setModulePreOrderIndex(refModule, preOrderIndex++);
1172 process(refModule, visited);
1173 chunkGroup.setModulePostOrderIndex(refModule, postOrderIndex++);
1174 }
1175 }
1176 };
1177 process(block, new Set());
1178 }
1179 }
1180 outdatedOrderIndexChunkGroups.clear();
1181 ordinalByModule.clear();
1182
1183 logger.log(
1184 `${statProcessedQueueItems} queue items processed (${statProcessedBlocks} blocks)`
1185 );
1186 logger.log(`${statConnectedChunkGroups} chunk groups connected`);
1187 logger.log(
1188 `${statProcessedChunkGroupsForMerging} chunk groups processed for merging (${statMergedAvailableModuleSets} module sets, ${statForkedAvailableModules} forked, ${statForkedAvailableModulesCount} + ${statForkedAvailableModulesCountPlus} modules forked, ${statForkedMergedModulesCount} + ${statForkedMergedModulesCountPlus} modules merged into fork, ${statForkedResultModulesCount} resulting modules)`
1189 );
1190 logger.log(
1191 `${statChunkGroupInfoUpdated} chunk group info updated (${statChildChunkGroupsReconnected} already connected chunk groups reconnected)`
1192 );
1193};
1194
1195/**
1196 * @param {Compilation} compilation the compilation
1197 * @param {BlocksWithNestedBlocks} blocksWithNestedBlocks flag for blocks that have nested blocks
1198 * @param {BlockConnections} blockConnections connection for blocks
1199 * @param {MaskByChunk} maskByChunk mapping from chunk to module mask
1200 */
1201const connectChunkGroups = (
1202 compilation,
1203 blocksWithNestedBlocks,
1204 blockConnections,
1205 maskByChunk
1206) => {
1207 const { chunkGraph } = compilation;
1208
1209 /**
1210 * Helper function to check if all modules of a chunk are available
1211 * @param {ChunkGroup} chunkGroup the chunkGroup to scan
1212 * @param {bigint} availableModules the comparator set
1213 * @returns {boolean} return true if all modules of a chunk are available
1214 */
1215 const areModulesAvailable = (chunkGroup, availableModules) => {
1216 for (const chunk of chunkGroup.chunks) {
1217 const chunkMask = /** @type {bigint} */ (maskByChunk.get(chunk));
1218 if ((chunkMask & availableModules) !== chunkMask) return false;
1219 }
1220 return true;
1221 };
1222
1223 // For each edge in the basic chunk graph
1224 for (const [block, connections] of blockConnections) {
1225 // 1. Check if connection is needed
1226 // When none of the dependencies need to be connected
1227 // we can skip all of them
1228 // It's not possible to filter each item so it doesn't create inconsistent
1229 // connections and modules can only create one version
1230 // TODO maybe decide this per runtime
1231 if (
1232 // TODO is this needed?
1233 !blocksWithNestedBlocks.has(block) &&
1234 connections.every(({ chunkGroup, originChunkGroupInfo }) =>
1235 areModulesAvailable(
1236 chunkGroup,
1237 /** @type {bigint} */ (originChunkGroupInfo.resultingAvailableModules)
1238 )
1239 )
1240 ) {
1241 continue;
1242 }
1243
1244 // 2. Foreach edge
1245 for (let i = 0; i < connections.length; i++) {
1246 const { chunkGroup, originChunkGroupInfo } = connections[i];
1247
1248 // 3. Connect block with chunk
1249 chunkGraph.connectBlockAndChunkGroup(block, chunkGroup);
1250
1251 // 4. Connect chunk with parent
1252 connectChunkGroupParentAndChild(
1253 originChunkGroupInfo.chunkGroup,
1254 chunkGroup
1255 );
1256 }
1257 }
1258};
1259
1260/**
1261 * Remove all unconnected chunk groups
1262 * @param {Compilation} compilation the compilation
1263 * @param {Iterable<ChunkGroup>} allCreatedChunkGroups all chunk groups that where created before
1264 */
1265const cleanupUnconnectedGroups = (compilation, allCreatedChunkGroups) => {
1266 const { chunkGraph } = compilation;
1267
1268 for (const chunkGroup of allCreatedChunkGroups) {
1269 if (chunkGroup.getNumberOfParents() === 0) {
1270 for (const chunk of chunkGroup.chunks) {
1271 compilation.chunks.delete(chunk);
1272 chunkGraph.disconnectChunk(chunk);
1273 }
1274 chunkGraph.disconnectChunkGroup(chunkGroup);
1275 chunkGroup.remove();
1276 }
1277 }
1278};
1279
1280/**
1281 * This method creates the Chunk graph from the Module graph
1282 * @param {Compilation} compilation the compilation
1283 * @param {InputEntrypointsAndModules} inputEntrypointsAndModules chunk groups which are processed with the modules
1284 * @returns {void}
1285 */
1286const buildChunkGraph = (compilation, inputEntrypointsAndModules) => {
1287 const logger = compilation.getLogger("webpack.buildChunkGraph");
1288
1289 // SHARED STATE
1290
1291 /** @type {BlockConnections} */
1292 const blockConnections = new Map();
1293
1294 /** @type {AllCreatedChunkGroups} */
1295 const allCreatedChunkGroups = new Set();
1296
1297 /** @type {ChunkGroupInfoMap} */
1298 const chunkGroupInfoMap = new Map();
1299
1300 /** @type {BlocksWithNestedBlocks} */
1301 const blocksWithNestedBlocks = new Set();
1302
1303 /** @type {MaskByChunk} */
1304 const maskByChunk = new Map();
1305
1306 // PART ONE
1307
1308 logger.time("visitModules");
1309 visitModules(
1310 logger,
1311 compilation,
1312 inputEntrypointsAndModules,
1313 chunkGroupInfoMap,
1314 blockConnections,
1315 blocksWithNestedBlocks,
1316 allCreatedChunkGroups,
1317 maskByChunk
1318 );
1319 logger.timeEnd("visitModules");
1320
1321 // PART TWO
1322
1323 logger.time("connectChunkGroups");
1324 connectChunkGroups(
1325 compilation,
1326 blocksWithNestedBlocks,
1327 blockConnections,
1328 maskByChunk
1329 );
1330 logger.timeEnd("connectChunkGroups");
1331
1332 for (const [chunkGroup, chunkGroupInfo] of chunkGroupInfoMap) {
1333 for (const chunk of chunkGroup.chunks)
1334 chunk.runtime = mergeRuntime(chunk.runtime, chunkGroupInfo.runtime);
1335 }
1336
1337 // Cleanup work
1338
1339 logger.time("cleanup");
1340 cleanupUnconnectedGroups(compilation, allCreatedChunkGroups);
1341 logger.timeEnd("cleanup");
1342};
1343
1344module.exports = buildChunkGraph;
Note: See TracBrowser for help on using the repository browser.