buildChunkGraph.js 47 KB

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