buildChunkGraph.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const AsyncDependencyToInitialChunkError = require("./AsyncDependencyToInitialChunkError");
  7. const GraphHelpers = require("./GraphHelpers");
  8. /** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */
  9. /** @typedef {import("./Chunk")} Chunk */
  10. /** @typedef {import("./ChunkGroup")} ChunkGroup */
  11. /** @typedef {import("./Compilation")} Compilation */
  12. /** @typedef {import("./DependenciesBlock")} DependenciesBlock */
  13. /** @typedef {import("./Dependency")} Dependency */
  14. /** @typedef {import("./Entrypoint")} Entrypoint */
  15. /** @typedef {import("./Module")} Module */
  16. /**
  17. * @typedef {Object} QueueItem
  18. * @property {number} action
  19. * @property {DependenciesBlock} block
  20. * @property {Module} module
  21. * @property {Chunk} chunk
  22. * @property {ChunkGroup} chunkGroup
  23. */
  24. /**
  25. * @typedef {Object} ChunkGroupInfo
  26. * @property {ChunkGroup} chunkGroup the chunk group
  27. * @property {Set<Module>} minAvailableModules current minimal set of modules available at this point
  28. * @property {boolean} minAvailableModulesOwned true, if minAvailableModules is owned and can be modified
  29. * @property {Set<Module>[]} availableModulesToBeMerged enqueued updates to the minimal set of available modules
  30. * @property {QueueItem[]} skippedItems queue items that were skipped because module is already available in parent chunks (need to reconsider when minAvailableModules is shrinking)
  31. * @property {Set<Module>} resultingAvailableModules set of modules available including modules from this chunk group
  32. * @property {Set<ChunkGroup>} children set of children chunk groups, that will be revisited when availableModules shrink
  33. */
  34. /**
  35. * @typedef {Object} BlockChunkGroupConnection
  36. * @property {ChunkGroupInfo} originChunkGroupInfo origin chunk group
  37. * @property {ChunkGroup} chunkGroup referenced chunk group
  38. */
  39. /**
  40. * @template T
  41. * @param {Set<T>} a first set
  42. * @param {Set<T>} b second set
  43. * @returns {number} cmp
  44. */
  45. const bySetSize = (a, b) => {
  46. return b.size - a.size;
  47. };
  48. /**
  49. * Extracts simplified info from the modules and their dependencies
  50. * @param {Compilation} compilation the compilation
  51. * @returns {Map<DependenciesBlock, { modules: Iterable<Module>, blocks: AsyncDependenciesBlock[]}>} the mapping block to modules and inner blocks
  52. */
  53. const extraceBlockInfoMap = compilation => {
  54. /** @type {Map<DependenciesBlock, { modules: Iterable<Module>, blocks: AsyncDependenciesBlock[]}>} */
  55. const blockInfoMap = new Map();
  56. /**
  57. * @param {Dependency} d dependency to iterate over
  58. * @returns {void}
  59. */
  60. const iteratorDependency = d => {
  61. // We skip Dependencies without Reference
  62. const ref = compilation.getDependencyReference(currentModule, d);
  63. if (!ref) {
  64. return;
  65. }
  66. // We skip Dependencies without Module pointer
  67. const refModule = ref.module;
  68. if (!refModule) {
  69. return;
  70. }
  71. // We skip weak Dependencies
  72. if (ref.weak) {
  73. return;
  74. }
  75. blockInfoModules.add(refModule);
  76. };
  77. /**
  78. * @param {AsyncDependenciesBlock} b blocks to prepare
  79. * @returns {void}
  80. */
  81. const iteratorBlockPrepare = b => {
  82. blockInfoBlocks.push(b);
  83. blockQueue.push(b);
  84. };
  85. /** @type {Module} */
  86. let currentModule;
  87. /** @type {DependenciesBlock} */
  88. let block;
  89. /** @type {DependenciesBlock[]} */
  90. let blockQueue;
  91. /** @type {Set<Module>} */
  92. let blockInfoModules;
  93. /** @type {AsyncDependenciesBlock[]} */
  94. let blockInfoBlocks;
  95. for (const module of compilation.modules) {
  96. blockQueue = [module];
  97. currentModule = module;
  98. while (blockQueue.length > 0) {
  99. block = blockQueue.pop();
  100. blockInfoModules = new Set();
  101. blockInfoBlocks = [];
  102. if (block.variables) {
  103. for (const variable of block.variables) {
  104. for (const dep of variable.dependencies) iteratorDependency(dep);
  105. }
  106. }
  107. if (block.dependencies) {
  108. for (const dep of block.dependencies) iteratorDependency(dep);
  109. }
  110. if (block.blocks) {
  111. for (const b of block.blocks) iteratorBlockPrepare(b);
  112. }
  113. const blockInfo = {
  114. modules: blockInfoModules,
  115. blocks: blockInfoBlocks
  116. };
  117. blockInfoMap.set(block, blockInfo);
  118. }
  119. }
  120. return blockInfoMap;
  121. };
  122. /**
  123. *
  124. * @param {Compilation} compilation the compilation
  125. * @param {Entrypoint[]} inputChunkGroups input groups
  126. * @param {Map<ChunkGroup, ChunkGroupInfo>} chunkGroupInfoMap mapping from chunk group to available modules
  127. * @param {Map<AsyncDependenciesBlock, BlockChunkGroupConnection[]>} blockConnections connection for blocks
  128. * @param {Set<DependenciesBlock>} blocksWithNestedBlocks flag for blocks that have nested blocks
  129. * @param {Set<ChunkGroup>} allCreatedChunkGroups filled with all chunk groups that are created here
  130. */
  131. const visitModules = (
  132. compilation,
  133. inputChunkGroups,
  134. chunkGroupInfoMap,
  135. blockConnections,
  136. blocksWithNestedBlocks,
  137. allCreatedChunkGroups
  138. ) => {
  139. const logger = compilation.getLogger("webpack.buildChunkGraph.visitModules");
  140. const { namedChunkGroups } = compilation;
  141. logger.time("prepare");
  142. const blockInfoMap = extraceBlockInfoMap(compilation);
  143. /** @type {Map<ChunkGroup, { index: number, index2: number }>} */
  144. const chunkGroupCounters = new Map();
  145. for (const chunkGroup of inputChunkGroups) {
  146. chunkGroupCounters.set(chunkGroup, {
  147. index: 0,
  148. index2: 0
  149. });
  150. }
  151. let nextFreeModuleIndex = 0;
  152. let nextFreeModuleIndex2 = 0;
  153. /** @type {Map<DependenciesBlock, ChunkGroup>} */
  154. const blockChunkGroups = new Map();
  155. const ADD_AND_ENTER_MODULE = 0;
  156. const ENTER_MODULE = 1;
  157. const PROCESS_BLOCK = 2;
  158. const LEAVE_MODULE = 3;
  159. /**
  160. * @param {QueueItem[]} queue the queue array (will be mutated)
  161. * @param {ChunkGroup} chunkGroup chunk group
  162. * @returns {QueueItem[]} the queue array again
  163. */
  164. const reduceChunkGroupToQueueItem = (queue, chunkGroup) => {
  165. for (const chunk of chunkGroup.chunks) {
  166. const module = chunk.entryModule;
  167. queue.push({
  168. action: ENTER_MODULE,
  169. block: module,
  170. module,
  171. chunk,
  172. chunkGroup
  173. });
  174. }
  175. chunkGroupInfoMap.set(chunkGroup, {
  176. chunkGroup,
  177. minAvailableModules: new Set(),
  178. minAvailableModulesOwned: true,
  179. availableModulesToBeMerged: [],
  180. skippedItems: [],
  181. resultingAvailableModules: undefined,
  182. children: undefined
  183. });
  184. return queue;
  185. };
  186. // Start with the provided modules/chunks
  187. /** @type {QueueItem[]} */
  188. let queue = inputChunkGroups
  189. .reduce(reduceChunkGroupToQueueItem, [])
  190. .reverse();
  191. /** @type {Map<ChunkGroup, Set<ChunkGroup>>} */
  192. const queueConnect = new Map();
  193. /** @type {Set<ChunkGroupInfo>} */
  194. const outdatedChunkGroupInfo = new Set();
  195. /** @type {QueueItem[]} */
  196. let queueDelayed = [];
  197. logger.timeEnd("prepare");
  198. /** @type {Module} */
  199. let module;
  200. /** @type {Chunk} */
  201. let chunk;
  202. /** @type {ChunkGroup} */
  203. let chunkGroup;
  204. /** @type {ChunkGroupInfo} */
  205. let chunkGroupInfo;
  206. /** @type {DependenciesBlock} */
  207. let block;
  208. /** @type {Set<Module>} */
  209. let minAvailableModules;
  210. /** @type {QueueItem[]} */
  211. let skippedItems;
  212. // For each async Block in graph
  213. /**
  214. * @param {AsyncDependenciesBlock} b iterating over each Async DepBlock
  215. * @returns {void}
  216. */
  217. const iteratorBlock = b => {
  218. // 1. We create a chunk for this Block
  219. // but only once (blockChunkGroups map)
  220. let c = blockChunkGroups.get(b);
  221. if (c === undefined) {
  222. c = namedChunkGroups.get(b.chunkName);
  223. if (c && c.isInitial()) {
  224. compilation.errors.push(
  225. new AsyncDependencyToInitialChunkError(b.chunkName, module, b.loc)
  226. );
  227. c = chunkGroup;
  228. } else {
  229. c = compilation.addChunkInGroup(
  230. b.groupOptions || b.chunkName,
  231. module,
  232. b.loc,
  233. b.request
  234. );
  235. chunkGroupCounters.set(c, { index: 0, index2: 0 });
  236. blockChunkGroups.set(b, c);
  237. allCreatedChunkGroups.add(c);
  238. }
  239. blockConnections.set(b, []);
  240. } else {
  241. // TODO webpack 5 remove addOptions check
  242. if (c.addOptions) c.addOptions(b.groupOptions);
  243. c.addOrigin(module, b.loc, b.request);
  244. }
  245. // 2. We store the connection for the block
  246. // to connect it later if needed
  247. blockConnections.get(b).push({
  248. originChunkGroupInfo: chunkGroupInfo,
  249. chunkGroup: c
  250. });
  251. // 3. We create/update the chunk group info
  252. let connectList = queueConnect.get(chunkGroup);
  253. if (connectList === undefined) {
  254. connectList = new Set();
  255. queueConnect.set(chunkGroup, connectList);
  256. }
  257. connectList.add(c);
  258. // 4. We enqueue the DependenciesBlock for traversal
  259. queueDelayed.push({
  260. action: PROCESS_BLOCK,
  261. block: b,
  262. module: module,
  263. chunk: c.chunks[0],
  264. chunkGroup: c
  265. });
  266. };
  267. // Iterative traversal of the Module graph
  268. // Recursive would be simpler to write but could result in Stack Overflows
  269. while (queue.length) {
  270. logger.time("visiting");
  271. while (queue.length) {
  272. const queueItem = queue.pop();
  273. module = queueItem.module;
  274. block = queueItem.block;
  275. chunk = queueItem.chunk;
  276. if (chunkGroup !== queueItem.chunkGroup) {
  277. chunkGroup = queueItem.chunkGroup;
  278. chunkGroupInfo = chunkGroupInfoMap.get(chunkGroup);
  279. minAvailableModules = chunkGroupInfo.minAvailableModules;
  280. skippedItems = chunkGroupInfo.skippedItems;
  281. }
  282. switch (queueItem.action) {
  283. case ADD_AND_ENTER_MODULE: {
  284. if (minAvailableModules.has(module)) {
  285. // already in parent chunks
  286. // skip it for now, but enqueue for rechecking when minAvailableModules shrinks
  287. skippedItems.push(queueItem);
  288. break;
  289. }
  290. // We connect Module and Chunk when not already done
  291. if (chunk.addModule(module)) {
  292. module.addChunk(chunk);
  293. } else {
  294. // already connected, skip it
  295. break;
  296. }
  297. }
  298. // fallthrough
  299. case ENTER_MODULE: {
  300. if (chunkGroup !== undefined) {
  301. const index = chunkGroup.getModuleIndex(module);
  302. if (index === undefined) {
  303. chunkGroup.setModuleIndex(
  304. module,
  305. chunkGroupCounters.get(chunkGroup).index++
  306. );
  307. }
  308. }
  309. if (module.index === null) {
  310. module.index = nextFreeModuleIndex++;
  311. }
  312. queue.push({
  313. action: LEAVE_MODULE,
  314. block,
  315. module,
  316. chunk,
  317. chunkGroup
  318. });
  319. }
  320. // fallthrough
  321. case PROCESS_BLOCK: {
  322. // get prepared block info
  323. const blockInfo = blockInfoMap.get(block);
  324. // Buffer items because order need to be reverse to get indicies correct
  325. const skipBuffer = [];
  326. const queueBuffer = [];
  327. // Traverse all referenced modules
  328. for (const refModule of blockInfo.modules) {
  329. if (chunk.containsModule(refModule)) {
  330. // skip early if already connected
  331. continue;
  332. }
  333. if (minAvailableModules.has(refModule)) {
  334. // already in parent chunks, skip it for now
  335. skipBuffer.push({
  336. action: ADD_AND_ENTER_MODULE,
  337. block: refModule,
  338. module: refModule,
  339. chunk,
  340. chunkGroup
  341. });
  342. continue;
  343. }
  344. // enqueue the add and enter to enter in the correct order
  345. // this is relevant with circular dependencies
  346. queueBuffer.push({
  347. action: ADD_AND_ENTER_MODULE,
  348. block: refModule,
  349. module: refModule,
  350. chunk,
  351. chunkGroup
  352. });
  353. }
  354. // Add buffered items in reversed order
  355. for (let i = skipBuffer.length - 1; i >= 0; i--) {
  356. skippedItems.push(skipBuffer[i]);
  357. }
  358. for (let i = queueBuffer.length - 1; i >= 0; i--) {
  359. queue.push(queueBuffer[i]);
  360. }
  361. // Traverse all Blocks
  362. for (const block of blockInfo.blocks) iteratorBlock(block);
  363. if (blockInfo.blocks.length > 0 && module !== block) {
  364. blocksWithNestedBlocks.add(block);
  365. }
  366. break;
  367. }
  368. case LEAVE_MODULE: {
  369. if (chunkGroup !== undefined) {
  370. const index = chunkGroup.getModuleIndex2(module);
  371. if (index === undefined) {
  372. chunkGroup.setModuleIndex2(
  373. module,
  374. chunkGroupCounters.get(chunkGroup).index2++
  375. );
  376. }
  377. }
  378. if (module.index2 === null) {
  379. module.index2 = nextFreeModuleIndex2++;
  380. }
  381. break;
  382. }
  383. }
  384. }
  385. logger.timeEnd("visiting");
  386. while (queueConnect.size > 0) {
  387. logger.time("calculating available modules");
  388. // Figure out new parents for chunk groups
  389. // to get new available modules for these children
  390. for (const [chunkGroup, targets] of queueConnect) {
  391. const info = chunkGroupInfoMap.get(chunkGroup);
  392. let minAvailableModules = info.minAvailableModules;
  393. // 1. Create a new Set of available modules at this points
  394. const resultingAvailableModules = new Set(minAvailableModules);
  395. for (const chunk of chunkGroup.chunks) {
  396. for (const m of chunk.modulesIterable) {
  397. resultingAvailableModules.add(m);
  398. }
  399. }
  400. info.resultingAvailableModules = resultingAvailableModules;
  401. if (info.children === undefined) {
  402. info.children = targets;
  403. } else {
  404. for (const target of targets) {
  405. info.children.add(target);
  406. }
  407. }
  408. // 2. Update chunk group info
  409. for (const target of targets) {
  410. let chunkGroupInfo = chunkGroupInfoMap.get(target);
  411. if (chunkGroupInfo === undefined) {
  412. chunkGroupInfo = {
  413. chunkGroup: target,
  414. minAvailableModules: undefined,
  415. minAvailableModulesOwned: undefined,
  416. availableModulesToBeMerged: [],
  417. skippedItems: [],
  418. resultingAvailableModules: undefined,
  419. children: undefined
  420. };
  421. chunkGroupInfoMap.set(target, chunkGroupInfo);
  422. }
  423. chunkGroupInfo.availableModulesToBeMerged.push(
  424. resultingAvailableModules
  425. );
  426. outdatedChunkGroupInfo.add(chunkGroupInfo);
  427. }
  428. }
  429. queueConnect.clear();
  430. logger.timeEnd("calculating available modules");
  431. if (outdatedChunkGroupInfo.size > 0) {
  432. logger.time("merging available modules");
  433. // Execute the merge
  434. for (const info of outdatedChunkGroupInfo) {
  435. const availableModulesToBeMerged = info.availableModulesToBeMerged;
  436. let cachedMinAvailableModules = info.minAvailableModules;
  437. // 1. Get minimal available modules
  438. // It doesn't make sense to traverse a chunk again with more available modules.
  439. // This step calculates the minimal available modules and skips traversal when
  440. // the list didn't shrink.
  441. if (availableModulesToBeMerged.length > 1) {
  442. availableModulesToBeMerged.sort(bySetSize);
  443. }
  444. let changed = false;
  445. for (const availableModules of availableModulesToBeMerged) {
  446. if (cachedMinAvailableModules === undefined) {
  447. cachedMinAvailableModules = availableModules;
  448. info.minAvailableModules = cachedMinAvailableModules;
  449. info.minAvailableModulesOwned = false;
  450. changed = true;
  451. } else {
  452. if (info.minAvailableModulesOwned) {
  453. // We own it and can modify it
  454. for (const m of cachedMinAvailableModules) {
  455. if (!availableModules.has(m)) {
  456. cachedMinAvailableModules.delete(m);
  457. changed = true;
  458. }
  459. }
  460. } else {
  461. for (const m of cachedMinAvailableModules) {
  462. if (!availableModules.has(m)) {
  463. // cachedMinAvailableModules need to be modified
  464. // but we don't own it
  465. // construct a new Set as intersection of cachedMinAvailableModules and availableModules
  466. /** @type {Set<Module>} */
  467. const newSet = new Set();
  468. const iterator = cachedMinAvailableModules[
  469. Symbol.iterator
  470. ]();
  471. /** @type {IteratorResult<Module>} */
  472. let it;
  473. while (!(it = iterator.next()).done) {
  474. const module = it.value;
  475. if (module === m) break;
  476. newSet.add(module);
  477. }
  478. while (!(it = iterator.next()).done) {
  479. const module = it.value;
  480. if (availableModules.has(module)) {
  481. newSet.add(module);
  482. }
  483. }
  484. cachedMinAvailableModules = newSet;
  485. info.minAvailableModulesOwned = true;
  486. info.minAvailableModules = newSet;
  487. // Update the cache from the first queue
  488. // if the chunkGroup is currently cached
  489. if (chunkGroup === info.chunkGroup) {
  490. minAvailableModules = cachedMinAvailableModules;
  491. }
  492. changed = true;
  493. break;
  494. }
  495. }
  496. }
  497. }
  498. }
  499. availableModulesToBeMerged.length = 0;
  500. if (!changed) continue;
  501. // 2. Reconsider skipped items
  502. for (const queueItem of info.skippedItems) {
  503. queue.push(queueItem);
  504. }
  505. info.skippedItems.length = 0;
  506. // 3. Reconsider children chunk groups
  507. if (info.children !== undefined) {
  508. const chunkGroup = info.chunkGroup;
  509. for (const c of info.children) {
  510. let connectList = queueConnect.get(chunkGroup);
  511. if (connectList === undefined) {
  512. connectList = new Set();
  513. queueConnect.set(chunkGroup, connectList);
  514. }
  515. connectList.add(c);
  516. }
  517. }
  518. }
  519. outdatedChunkGroupInfo.clear();
  520. logger.timeEnd("merging available modules");
  521. }
  522. }
  523. // Run queueDelayed when all items of the queue are processed
  524. // This is important to get the global indicing correct
  525. // Async blocks should be processed after all sync blocks are processed
  526. if (queue.length === 0) {
  527. const tempQueue = queue;
  528. queue = queueDelayed.reverse();
  529. queueDelayed = tempQueue;
  530. }
  531. }
  532. };
  533. /**
  534. *
  535. * @param {Set<DependenciesBlock>} blocksWithNestedBlocks flag for blocks that have nested blocks
  536. * @param {Map<AsyncDependenciesBlock, BlockChunkGroupConnection[]>} blockConnections connection for blocks
  537. * @param {Map<ChunkGroup, ChunkGroupInfo>} chunkGroupInfoMap mapping from chunk group to available modules
  538. */
  539. const connectChunkGroups = (
  540. blocksWithNestedBlocks,
  541. blockConnections,
  542. chunkGroupInfoMap
  543. ) => {
  544. /**
  545. * Helper function to check if all modules of a chunk are available
  546. *
  547. * @param {ChunkGroup} chunkGroup the chunkGroup to scan
  548. * @param {Set<Module>} availableModules the comparitor set
  549. * @returns {boolean} return true if all modules of a chunk are available
  550. */
  551. const areModulesAvailable = (chunkGroup, availableModules) => {
  552. for (const chunk of chunkGroup.chunks) {
  553. for (const module of chunk.modulesIterable) {
  554. if (!availableModules.has(module)) return false;
  555. }
  556. }
  557. return true;
  558. };
  559. // For each edge in the basic chunk graph
  560. for (const [block, connections] of blockConnections) {
  561. // 1. Check if connection is needed
  562. // When none of the dependencies need to be connected
  563. // we can skip all of them
  564. // It's not possible to filter each item so it doesn't create inconsistent
  565. // connections and modules can only create one version
  566. // TODO maybe decide this per runtime
  567. if (
  568. // TODO is this needed?
  569. !blocksWithNestedBlocks.has(block) &&
  570. connections.every(({ chunkGroup, originChunkGroupInfo }) =>
  571. areModulesAvailable(
  572. chunkGroup,
  573. originChunkGroupInfo.resultingAvailableModules
  574. )
  575. )
  576. ) {
  577. continue;
  578. }
  579. // 2. Foreach edge
  580. for (let i = 0; i < connections.length; i++) {
  581. const { chunkGroup, originChunkGroupInfo } = connections[i];
  582. // 3. Connect block with chunk
  583. GraphHelpers.connectDependenciesBlockAndChunkGroup(block, chunkGroup);
  584. // 4. Connect chunk with parent
  585. GraphHelpers.connectChunkGroupParentAndChild(
  586. originChunkGroupInfo.chunkGroup,
  587. chunkGroup
  588. );
  589. }
  590. }
  591. };
  592. /**
  593. * Remove all unconnected chunk groups
  594. * @param {Compilation} compilation the compilation
  595. * @param {Iterable<ChunkGroup>} allCreatedChunkGroups all chunk groups that where created before
  596. */
  597. const cleanupUnconnectedGroups = (compilation, allCreatedChunkGroups) => {
  598. for (const chunkGroup of allCreatedChunkGroups) {
  599. if (chunkGroup.getNumberOfParents() === 0) {
  600. for (const chunk of chunkGroup.chunks) {
  601. const idx = compilation.chunks.indexOf(chunk);
  602. if (idx >= 0) compilation.chunks.splice(idx, 1);
  603. chunk.remove("unconnected");
  604. }
  605. chunkGroup.remove("unconnected");
  606. }
  607. }
  608. };
  609. /**
  610. * This method creates the Chunk graph from the Module graph
  611. * @param {Compilation} compilation the compilation
  612. * @param {Entrypoint[]} inputChunkGroups chunk groups which are processed
  613. * @returns {void}
  614. */
  615. const buildChunkGraph = (compilation, inputChunkGroups) => {
  616. // SHARED STATE
  617. /** @type {Map<AsyncDependenciesBlock, BlockChunkGroupConnection[]>} */
  618. const blockConnections = new Map();
  619. /** @type {Set<ChunkGroup>} */
  620. const allCreatedChunkGroups = new Set();
  621. /** @type {Map<ChunkGroup, ChunkGroupInfo>} */
  622. const chunkGroupInfoMap = new Map();
  623. /** @type {Set<DependenciesBlock>} */
  624. const blocksWithNestedBlocks = new Set();
  625. // PART ONE
  626. visitModules(
  627. compilation,
  628. inputChunkGroups,
  629. chunkGroupInfoMap,
  630. blockConnections,
  631. blocksWithNestedBlocks,
  632. allCreatedChunkGroups
  633. );
  634. // PART TWO
  635. connectChunkGroups(
  636. blocksWithNestedBlocks,
  637. blockConnections,
  638. chunkGroupInfoMap
  639. );
  640. // Cleaup work
  641. cleanupUnconnectedGroups(compilation, allCreatedChunkGroups);
  642. };
  643. module.exports = buildChunkGraph;