Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

buildChunkGraph.js 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  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} ChunkGroupDep
  36. * @property {AsyncDependenciesBlock} block referencing block
  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<ChunkGroup, ChunkGroupDep[]>} chunkDependencies dependencies for chunk groups
  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. chunkDependencies,
  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 {DependenciesBlock} */
  205. let block;
  206. /** @type {Set<Module>} */
  207. let minAvailableModules;
  208. /** @type {QueueItem[]} */
  209. let skippedItems;
  210. // For each async Block in graph
  211. /**
  212. * @param {AsyncDependenciesBlock} b iterating over each Async DepBlock
  213. * @returns {void}
  214. */
  215. const iteratorBlock = b => {
  216. // 1. We create a chunk for this Block
  217. // but only once (blockChunkGroups map)
  218. let c = blockChunkGroups.get(b);
  219. if (c === undefined) {
  220. c = namedChunkGroups.get(b.chunkName);
  221. if (c && c.isInitial()) {
  222. compilation.errors.push(
  223. new AsyncDependencyToInitialChunkError(b.chunkName, module, b.loc)
  224. );
  225. c = chunkGroup;
  226. } else {
  227. c = compilation.addChunkInGroup(
  228. b.groupOptions || b.chunkName,
  229. module,
  230. b.loc,
  231. b.request
  232. );
  233. chunkGroupCounters.set(c, { index: 0, index2: 0 });
  234. blockChunkGroups.set(b, c);
  235. allCreatedChunkGroups.add(c);
  236. }
  237. } else {
  238. // TODO webpack 5 remove addOptions check
  239. if (c.addOptions) c.addOptions(b.groupOptions);
  240. c.addOrigin(module, b.loc, b.request);
  241. }
  242. // 2. We store the Block+Chunk mapping as dependency for the chunk
  243. let deps = chunkDependencies.get(chunkGroup);
  244. if (!deps) chunkDependencies.set(chunkGroup, (deps = []));
  245. deps.push({
  246. block: b,
  247. chunkGroup: c
  248. });
  249. // 3. We create/update the chunk group info
  250. let connectList = queueConnect.get(chunkGroup);
  251. if (connectList === undefined) {
  252. connectList = new Set();
  253. queueConnect.set(chunkGroup, connectList);
  254. }
  255. connectList.add(c);
  256. // 4. We enqueue the DependenciesBlock for traversal
  257. queueDelayed.push({
  258. action: PROCESS_BLOCK,
  259. block: b,
  260. module: module,
  261. chunk: c.chunks[0],
  262. chunkGroup: c
  263. });
  264. };
  265. // Iterative traversal of the Module graph
  266. // Recursive would be simpler to write but could result in Stack Overflows
  267. while (queue.length) {
  268. logger.time("visiting");
  269. while (queue.length) {
  270. const queueItem = queue.pop();
  271. module = queueItem.module;
  272. block = queueItem.block;
  273. chunk = queueItem.chunk;
  274. if (chunkGroup !== queueItem.chunkGroup) {
  275. chunkGroup = queueItem.chunkGroup;
  276. const chunkGroupInfo = chunkGroupInfoMap.get(chunkGroup);
  277. minAvailableModules = chunkGroupInfo.minAvailableModules;
  278. skippedItems = chunkGroupInfo.skippedItems;
  279. }
  280. switch (queueItem.action) {
  281. case ADD_AND_ENTER_MODULE: {
  282. if (minAvailableModules.has(module)) {
  283. // already in parent chunks
  284. // skip it for now, but enqueue for rechecking when minAvailableModules shrinks
  285. skippedItems.push(queueItem);
  286. break;
  287. }
  288. // We connect Module and Chunk when not already done
  289. if (chunk.addModule(module)) {
  290. module.addChunk(chunk);
  291. } else {
  292. // already connected, skip it
  293. break;
  294. }
  295. }
  296. // fallthrough
  297. case ENTER_MODULE: {
  298. if (chunkGroup !== undefined) {
  299. const index = chunkGroup.getModuleIndex(module);
  300. if (index === undefined) {
  301. chunkGroup.setModuleIndex(
  302. module,
  303. chunkGroupCounters.get(chunkGroup).index++
  304. );
  305. }
  306. }
  307. if (module.index === null) {
  308. module.index = nextFreeModuleIndex++;
  309. }
  310. queue.push({
  311. action: LEAVE_MODULE,
  312. block,
  313. module,
  314. chunk,
  315. chunkGroup
  316. });
  317. }
  318. // fallthrough
  319. case PROCESS_BLOCK: {
  320. // get prepared block info
  321. const blockInfo = blockInfoMap.get(block);
  322. // Buffer items because order need to be reverse to get indicies correct
  323. const skipBuffer = [];
  324. const queueBuffer = [];
  325. // Traverse all referenced modules
  326. for (const refModule of blockInfo.modules) {
  327. if (chunk.containsModule(refModule)) {
  328. // skip early if already connected
  329. continue;
  330. }
  331. if (minAvailableModules.has(refModule)) {
  332. // already in parent chunks, skip it for now
  333. skipBuffer.push({
  334. action: ADD_AND_ENTER_MODULE,
  335. block: refModule,
  336. module: refModule,
  337. chunk,
  338. chunkGroup
  339. });
  340. continue;
  341. }
  342. // enqueue the add and enter to enter in the correct order
  343. // this is relevant with circular dependencies
  344. queueBuffer.push({
  345. action: ADD_AND_ENTER_MODULE,
  346. block: refModule,
  347. module: refModule,
  348. chunk,
  349. chunkGroup
  350. });
  351. }
  352. // Add buffered items in reversed order
  353. for (let i = skipBuffer.length - 1; i >= 0; i--) {
  354. skippedItems.push(skipBuffer[i]);
  355. }
  356. for (let i = queueBuffer.length - 1; i >= 0; i--) {
  357. queue.push(queueBuffer[i]);
  358. }
  359. // Traverse all Blocks
  360. for (const block of blockInfo.blocks) iteratorBlock(block);
  361. if (blockInfo.blocks.length > 0 && module !== block) {
  362. blocksWithNestedBlocks.add(block);
  363. }
  364. break;
  365. }
  366. case LEAVE_MODULE: {
  367. if (chunkGroup !== undefined) {
  368. const index = chunkGroup.getModuleIndex2(module);
  369. if (index === undefined) {
  370. chunkGroup.setModuleIndex2(
  371. module,
  372. chunkGroupCounters.get(chunkGroup).index2++
  373. );
  374. }
  375. }
  376. if (module.index2 === null) {
  377. module.index2 = nextFreeModuleIndex2++;
  378. }
  379. break;
  380. }
  381. }
  382. }
  383. logger.timeEnd("visiting");
  384. while (queueConnect.size > 0) {
  385. logger.time("calculating available modules");
  386. // Figure out new parents for chunk groups
  387. // to get new available modules for these children
  388. for (const [chunkGroup, targets] of queueConnect) {
  389. const info = chunkGroupInfoMap.get(chunkGroup);
  390. let minAvailableModules = info.minAvailableModules;
  391. // 1. Create a new Set of available modules at this points
  392. const resultingAvailableModules = new Set(minAvailableModules);
  393. for (const chunk of chunkGroup.chunks) {
  394. for (const m of chunk.modulesIterable) {
  395. resultingAvailableModules.add(m);
  396. }
  397. }
  398. info.resultingAvailableModules = resultingAvailableModules;
  399. if (info.children === undefined) {
  400. info.children = targets;
  401. } else {
  402. for (const target of targets) {
  403. info.children.add(target);
  404. }
  405. }
  406. // 2. Update chunk group info
  407. for (const target of targets) {
  408. let chunkGroupInfo = chunkGroupInfoMap.get(target);
  409. if (chunkGroupInfo === undefined) {
  410. chunkGroupInfo = {
  411. chunkGroup: target,
  412. minAvailableModules: undefined,
  413. minAvailableModulesOwned: undefined,
  414. availableModulesToBeMerged: [],
  415. skippedItems: [],
  416. resultingAvailableModules: undefined,
  417. children: undefined
  418. };
  419. chunkGroupInfoMap.set(target, chunkGroupInfo);
  420. }
  421. chunkGroupInfo.availableModulesToBeMerged.push(
  422. resultingAvailableModules
  423. );
  424. outdatedChunkGroupInfo.add(chunkGroupInfo);
  425. }
  426. }
  427. queueConnect.clear();
  428. logger.timeEnd("calculating available modules");
  429. if (outdatedChunkGroupInfo.size > 0) {
  430. logger.time("merging available modules");
  431. // Execute the merge
  432. for (const info of outdatedChunkGroupInfo) {
  433. const availableModulesToBeMerged = info.availableModulesToBeMerged;
  434. let cachedMinAvailableModules = info.minAvailableModules;
  435. // 1. Get minimal available modules
  436. // It doesn't make sense to traverse a chunk again with more available modules.
  437. // This step calculates the minimal available modules and skips traversal when
  438. // the list didn't shrink.
  439. if (availableModulesToBeMerged.length > 1) {
  440. availableModulesToBeMerged.sort(bySetSize);
  441. }
  442. let changed = false;
  443. for (const availableModules of availableModulesToBeMerged) {
  444. if (cachedMinAvailableModules === undefined) {
  445. cachedMinAvailableModules = availableModules;
  446. info.minAvailableModules = cachedMinAvailableModules;
  447. info.minAvailableModulesOwned = false;
  448. changed = true;
  449. } else {
  450. if (info.minAvailableModulesOwned) {
  451. // We own it and can modify it
  452. for (const m of cachedMinAvailableModules) {
  453. if (!availableModules.has(m)) {
  454. cachedMinAvailableModules.delete(m);
  455. changed = true;
  456. }
  457. }
  458. } else {
  459. for (const m of cachedMinAvailableModules) {
  460. if (!availableModules.has(m)) {
  461. // cachedMinAvailableModules need to be modified
  462. // but we don't own it
  463. // construct a new Set as intersection of cachedMinAvailableModules and availableModules
  464. /** @type {Set<Module>} */
  465. const newSet = new Set();
  466. const iterator = cachedMinAvailableModules[
  467. Symbol.iterator
  468. ]();
  469. /** @type {IteratorResult<Module>} */
  470. let it;
  471. while (!(it = iterator.next()).done) {
  472. const module = it.value;
  473. if (module === m) break;
  474. newSet.add(module);
  475. }
  476. while (!(it = iterator.next()).done) {
  477. const module = it.value;
  478. if (availableModules.has(module)) {
  479. newSet.add(module);
  480. }
  481. }
  482. cachedMinAvailableModules = newSet;
  483. info.minAvailableModulesOwned = true;
  484. info.minAvailableModules = newSet;
  485. // Update the cache from the first queue
  486. // if the chunkGroup is currently cached
  487. if (chunkGroup === info.chunkGroup) {
  488. minAvailableModules = cachedMinAvailableModules;
  489. }
  490. changed = true;
  491. break;
  492. }
  493. }
  494. }
  495. }
  496. }
  497. availableModulesToBeMerged.length = 0;
  498. if (!changed) continue;
  499. // 2. Reconsider skipped items
  500. for (const queueItem of info.skippedItems) {
  501. queue.push(queueItem);
  502. }
  503. info.skippedItems.length = 0;
  504. // 3. Reconsider children chunk groups
  505. if (info.children !== undefined) {
  506. const chunkGroup = info.chunkGroup;
  507. for (const c of info.children) {
  508. let connectList = queueConnect.get(chunkGroup);
  509. if (connectList === undefined) {
  510. connectList = new Set();
  511. queueConnect.set(chunkGroup, connectList);
  512. }
  513. connectList.add(c);
  514. }
  515. }
  516. }
  517. outdatedChunkGroupInfo.clear();
  518. logger.timeEnd("merging available modules");
  519. }
  520. }
  521. // Run queueDelayed when all items of the queue are processed
  522. // This is important to get the global indicing correct
  523. // Async blocks should be processed after all sync blocks are processed
  524. if (queue.length === 0) {
  525. const tempQueue = queue;
  526. queue = queueDelayed.reverse();
  527. queueDelayed = tempQueue;
  528. }
  529. }
  530. };
  531. /**
  532. *
  533. * @param {Set<DependenciesBlock>} blocksWithNestedBlocks flag for blocks that have nested blocks
  534. * @param {Map<ChunkGroup, ChunkGroupDep[]>} chunkDependencies dependencies for chunk groups
  535. * @param {Map<ChunkGroup, ChunkGroupInfo>} chunkGroupInfoMap mapping from chunk group to available modules
  536. */
  537. const connectChunkGroups = (
  538. blocksWithNestedBlocks,
  539. chunkDependencies,
  540. chunkGroupInfoMap
  541. ) => {
  542. /** @type {Set<Module>} */
  543. let resultingAvailableModules;
  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. /**
  561. * @param {ChunkGroupDep} dep the dependency used for filtering
  562. * @returns {boolean} used to filter "edges" (aka Dependencies) that were pointing
  563. * to modules that are already available. Also filters circular dependencies in the chunks graph
  564. */
  565. const filterFn = dep => {
  566. const depChunkGroup = dep.chunkGroup;
  567. // TODO is this needed?
  568. if (blocksWithNestedBlocks.has(dep.block)) return true;
  569. if (areModulesAvailable(depChunkGroup, resultingAvailableModules)) {
  570. return false; // break all modules are already available
  571. }
  572. return true;
  573. };
  574. // For all deps, check if chunk groups need to be connected
  575. for (const [chunkGroup, deps] of chunkDependencies) {
  576. if (deps.length === 0) continue;
  577. // 1. Get info from chunk group info map
  578. const info = chunkGroupInfoMap.get(chunkGroup);
  579. resultingAvailableModules = info.resultingAvailableModules;
  580. // 2. Foreach edge
  581. for (let i = 0; i < deps.length; i++) {
  582. const dep = deps[i];
  583. // Filter inline, rather than creating a new array from `.filter()`
  584. // TODO check if inlining filterFn makes sense here
  585. if (!filterFn(dep)) {
  586. continue;
  587. }
  588. const depChunkGroup = dep.chunkGroup;
  589. const depBlock = dep.block;
  590. // 5. Connect block with chunk
  591. GraphHelpers.connectDependenciesBlockAndChunkGroup(
  592. depBlock,
  593. depChunkGroup
  594. );
  595. // 6. Connect chunk with parent
  596. GraphHelpers.connectChunkGroupParentAndChild(chunkGroup, depChunkGroup);
  597. }
  598. }
  599. };
  600. /**
  601. * Remove all unconnected chunk groups
  602. * @param {Compilation} compilation the compilation
  603. * @param {Iterable<ChunkGroup>} allCreatedChunkGroups all chunk groups that where created before
  604. */
  605. const cleanupUnconnectedGroups = (compilation, allCreatedChunkGroups) => {
  606. for (const chunkGroup of allCreatedChunkGroups) {
  607. if (chunkGroup.getNumberOfParents() === 0) {
  608. for (const chunk of chunkGroup.chunks) {
  609. const idx = compilation.chunks.indexOf(chunk);
  610. if (idx >= 0) compilation.chunks.splice(idx, 1);
  611. chunk.remove("unconnected");
  612. }
  613. chunkGroup.remove("unconnected");
  614. }
  615. }
  616. };
  617. /**
  618. * This method creates the Chunk graph from the Module graph
  619. * @param {Compilation} compilation the compilation
  620. * @param {Entrypoint[]} inputChunkGroups chunk groups which are processed
  621. * @returns {void}
  622. */
  623. const buildChunkGraph = (compilation, inputChunkGroups) => {
  624. // SHARED STATE
  625. /** @type {Map<ChunkGroup, ChunkGroupDep[]>} */
  626. const chunkDependencies = new Map();
  627. /** @type {Set<ChunkGroup>} */
  628. const allCreatedChunkGroups = new Set();
  629. /** @type {Map<ChunkGroup, ChunkGroupInfo>} */
  630. const chunkGroupInfoMap = new Map();
  631. /** @type {Set<DependenciesBlock>} */
  632. const blocksWithNestedBlocks = new Set();
  633. // PART ONE
  634. visitModules(
  635. compilation,
  636. inputChunkGroups,
  637. chunkGroupInfoMap,
  638. chunkDependencies,
  639. blocksWithNestedBlocks,
  640. allCreatedChunkGroups
  641. );
  642. // PART TWO
  643. connectChunkGroups(
  644. blocksWithNestedBlocks,
  645. chunkDependencies,
  646. chunkGroupInfoMap
  647. );
  648. // Cleaup work
  649. cleanupUnconnectedGroups(compilation, allCreatedChunkGroups);
  650. };
  651. module.exports = buildChunkGraph;