source: frontend/node_modules/webpack/lib/buildChunkGraph.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 11 days ago

Fix frontend appearance

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