buildChunkGraph.js 38 KB

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