提交学习笔记专用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

895 lines
23 KiB

  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const ChunkGraph = require("./ChunkGraph");
  7. const Entrypoint = require("./Entrypoint");
  8. const { intersect } = require("./util/SetHelpers");
  9. const SortableSet = require("./util/SortableSet");
  10. const StringXor = require("./util/StringXor");
  11. const {
  12. compareChunkGroupsByIndex,
  13. compareModulesById,
  14. compareModulesByIdentifier
  15. } = require("./util/comparators");
  16. const { createArrayToSetDeprecationSet } = require("./util/deprecation");
  17. const { mergeRuntime } = require("./util/runtime");
  18. /** @typedef {import("./ChunkGraph").ChunkFilterPredicate} ChunkFilterPredicate */
  19. /** @typedef {import("./ChunkGraph").ChunkSizeOptions} ChunkSizeOptions */
  20. /** @typedef {import("./ChunkGraph").ModuleFilterPredicate} ModuleFilterPredicate */
  21. /** @typedef {import("./ChunkGraph").ModuleId} ModuleId */
  22. /** @typedef {import("./ChunkGroup")} ChunkGroup */
  23. /** @typedef {import("./ChunkGroup").ChunkGroupOptions} ChunkGroupOptions */
  24. /** @typedef {import("./Entrypoint").EntryOptions} EntryOptions */
  25. /** @typedef {import("./Module")} Module */
  26. /** @typedef {import("./TemplatedPathPlugin").TemplatePath} TemplatePath */
  27. /** @typedef {import("./util/Hash")} Hash */
  28. /** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */
  29. /** @typedef {string | null} ChunkName */
  30. /** @typedef {string | number} ChunkId */
  31. /** @typedef {SortableSet<string>} IdNameHints */
  32. const ChunkFilesSet = createArrayToSetDeprecationSet("chunk.files");
  33. /**
  34. * @deprecated
  35. * @typedef {object} ChunkMaps
  36. * @property {Record<ChunkId, string>} hash
  37. * @property {Record<ChunkId, Record<string, string>>} contentHash
  38. * @property {Record<ChunkId, string>} name
  39. */
  40. /**
  41. * @deprecated
  42. * @typedef {Record<ChunkId, ChunkId[]>} ChunkModuleIdMap
  43. */
  44. /**
  45. * @deprecated
  46. * @typedef {Record<ModuleId, string>} chunkModuleHashMap
  47. */
  48. /**
  49. * @deprecated
  50. * @typedef {object} ChunkModuleMaps
  51. * @property {ChunkModuleIdMap} id
  52. * @property {chunkModuleHashMap} hash
  53. */
  54. /** @typedef {Set<Chunk>} Chunks */
  55. /** @typedef {Set<Entrypoint>} Entrypoints */
  56. /** @typedef {Set<ChunkGroup>} Queue */
  57. /** @typedef {SortableSet<ChunkGroup>} SortableChunkGroups */
  58. /** @typedef {Record<string, ChunkId[]>} ChunkChildIdsByOrdersMap */
  59. /** @typedef {Record<string, ChunkChildIdsByOrdersMap>} ChunkChildIdsByOrdersMapByData */
  60. let debugId = 1000;
  61. /**
  62. * A Chunk is a unit of encapsulation for Modules.
  63. * Chunks are "rendered" into bundles that get emitted when the build completes.
  64. */
  65. class Chunk {
  66. /**
  67. * @param {ChunkName=} name of chunk being created, is optional (for subclasses)
  68. * @param {boolean} backCompat enable backward-compatibility
  69. */
  70. constructor(name, backCompat = true) {
  71. /** @type {ChunkId | null} */
  72. this.id = null;
  73. /** @type {ChunkId[] | null} */
  74. this.ids = null;
  75. /** @type {number} */
  76. this.debugId = debugId++;
  77. /** @type {ChunkName | undefined} */
  78. this.name = name;
  79. /** @type {IdNameHints} */
  80. this.idNameHints = new SortableSet();
  81. /** @type {boolean} */
  82. this.preventIntegration = false;
  83. /** @type {TemplatePath | undefined} */
  84. this.filenameTemplate = undefined;
  85. /** @type {TemplatePath | undefined} */
  86. this.cssFilenameTemplate = undefined;
  87. /**
  88. * @private
  89. * @type {SortableChunkGroups}
  90. */
  91. this._groups = new SortableSet(undefined, compareChunkGroupsByIndex);
  92. /** @type {RuntimeSpec} */
  93. this.runtime = undefined;
  94. /** @type {Set<string>} */
  95. this.files = backCompat ? new ChunkFilesSet() : new Set();
  96. /** @type {Set<string>} */
  97. this.auxiliaryFiles = new Set();
  98. /** @type {boolean} */
  99. this.rendered = false;
  100. /** @type {string=} */
  101. this.hash = undefined;
  102. /** @type {Record<string, string>} */
  103. this.contentHash = Object.create(null);
  104. /** @type {string=} */
  105. this.renderedHash = undefined;
  106. /** @type {string=} */
  107. this.chunkReason = undefined;
  108. /** @type {boolean} */
  109. this.extraAsync = false;
  110. }
  111. // TODO remove in webpack 6
  112. // BACKWARD-COMPAT START
  113. get entryModule() {
  114. const entryModules = [
  115. ...ChunkGraph.getChunkGraphForChunk(
  116. this,
  117. "Chunk.entryModule",
  118. "DEP_WEBPACK_CHUNK_ENTRY_MODULE"
  119. ).getChunkEntryModulesIterable(this)
  120. ];
  121. if (entryModules.length === 0) {
  122. return undefined;
  123. } else if (entryModules.length === 1) {
  124. return entryModules[0];
  125. }
  126. throw new Error(
  127. "Module.entryModule: Multiple entry modules are not supported by the deprecated API (Use the new ChunkGroup API)"
  128. );
  129. }
  130. /**
  131. * @returns {boolean} true, if the chunk contains an entry module
  132. */
  133. hasEntryModule() {
  134. return (
  135. ChunkGraph.getChunkGraphForChunk(
  136. this,
  137. "Chunk.hasEntryModule",
  138. "DEP_WEBPACK_CHUNK_HAS_ENTRY_MODULE"
  139. ).getNumberOfEntryModules(this) > 0
  140. );
  141. }
  142. /**
  143. * @param {Module} module the module
  144. * @returns {boolean} true, if the chunk could be added
  145. */
  146. addModule(module) {
  147. const chunkGraph = ChunkGraph.getChunkGraphForChunk(
  148. this,
  149. "Chunk.addModule",
  150. "DEP_WEBPACK_CHUNK_ADD_MODULE"
  151. );
  152. if (chunkGraph.isModuleInChunk(module, this)) return false;
  153. chunkGraph.connectChunkAndModule(this, module);
  154. return true;
  155. }
  156. /**
  157. * @param {Module} module the module
  158. * @returns {void}
  159. */
  160. removeModule(module) {
  161. ChunkGraph.getChunkGraphForChunk(
  162. this,
  163. "Chunk.removeModule",
  164. "DEP_WEBPACK_CHUNK_REMOVE_MODULE"
  165. ).disconnectChunkAndModule(this, module);
  166. }
  167. /**
  168. * @returns {number} the number of module which are contained in this chunk
  169. */
  170. getNumberOfModules() {
  171. return ChunkGraph.getChunkGraphForChunk(
  172. this,
  173. "Chunk.getNumberOfModules",
  174. "DEP_WEBPACK_CHUNK_GET_NUMBER_OF_MODULES"
  175. ).getNumberOfChunkModules(this);
  176. }
  177. get modulesIterable() {
  178. const chunkGraph = ChunkGraph.getChunkGraphForChunk(
  179. this,
  180. "Chunk.modulesIterable",
  181. "DEP_WEBPACK_CHUNK_MODULES_ITERABLE"
  182. );
  183. return chunkGraph.getOrderedChunkModulesIterable(
  184. this,
  185. compareModulesByIdentifier
  186. );
  187. }
  188. /**
  189. * @param {Chunk} otherChunk the chunk to compare with
  190. * @returns {-1|0|1} the comparison result
  191. */
  192. compareTo(otherChunk) {
  193. const chunkGraph = ChunkGraph.getChunkGraphForChunk(
  194. this,
  195. "Chunk.compareTo",
  196. "DEP_WEBPACK_CHUNK_COMPARE_TO"
  197. );
  198. return chunkGraph.compareChunks(this, otherChunk);
  199. }
  200. /**
  201. * @param {Module} module the module
  202. * @returns {boolean} true, if the chunk contains the module
  203. */
  204. containsModule(module) {
  205. return ChunkGraph.getChunkGraphForChunk(
  206. this,
  207. "Chunk.containsModule",
  208. "DEP_WEBPACK_CHUNK_CONTAINS_MODULE"
  209. ).isModuleInChunk(module, this);
  210. }
  211. /**
  212. * @returns {Module[]} the modules for this chunk
  213. */
  214. getModules() {
  215. return ChunkGraph.getChunkGraphForChunk(
  216. this,
  217. "Chunk.getModules",
  218. "DEP_WEBPACK_CHUNK_GET_MODULES"
  219. ).getChunkModules(this);
  220. }
  221. /**
  222. * @returns {void}
  223. */
  224. remove() {
  225. const chunkGraph = ChunkGraph.getChunkGraphForChunk(
  226. this,
  227. "Chunk.remove",
  228. "DEP_WEBPACK_CHUNK_REMOVE"
  229. );
  230. chunkGraph.disconnectChunk(this);
  231. this.disconnectFromGroups();
  232. }
  233. /**
  234. * @param {Module} module the module
  235. * @param {Chunk} otherChunk the target chunk
  236. * @returns {void}
  237. */
  238. moveModule(module, otherChunk) {
  239. const chunkGraph = ChunkGraph.getChunkGraphForChunk(
  240. this,
  241. "Chunk.moveModule",
  242. "DEP_WEBPACK_CHUNK_MOVE_MODULE"
  243. );
  244. chunkGraph.disconnectChunkAndModule(this, module);
  245. chunkGraph.connectChunkAndModule(otherChunk, module);
  246. }
  247. /**
  248. * @param {Chunk} otherChunk the other chunk
  249. * @returns {boolean} true, if the specified chunk has been integrated
  250. */
  251. integrate(otherChunk) {
  252. const chunkGraph = ChunkGraph.getChunkGraphForChunk(
  253. this,
  254. "Chunk.integrate",
  255. "DEP_WEBPACK_CHUNK_INTEGRATE"
  256. );
  257. if (chunkGraph.canChunksBeIntegrated(this, otherChunk)) {
  258. chunkGraph.integrateChunks(this, otherChunk);
  259. return true;
  260. }
  261. return false;
  262. }
  263. /**
  264. * @param {Chunk} otherChunk the other chunk
  265. * @returns {boolean} true, if chunks could be integrated
  266. */
  267. canBeIntegrated(otherChunk) {
  268. const chunkGraph = ChunkGraph.getChunkGraphForChunk(
  269. this,
  270. "Chunk.canBeIntegrated",
  271. "DEP_WEBPACK_CHUNK_CAN_BE_INTEGRATED"
  272. );
  273. return chunkGraph.canChunksBeIntegrated(this, otherChunk);
  274. }
  275. /**
  276. * @returns {boolean} true, if this chunk contains no module
  277. */
  278. isEmpty() {
  279. const chunkGraph = ChunkGraph.getChunkGraphForChunk(
  280. this,
  281. "Chunk.isEmpty",
  282. "DEP_WEBPACK_CHUNK_IS_EMPTY"
  283. );
  284. return chunkGraph.getNumberOfChunkModules(this) === 0;
  285. }
  286. /**
  287. * @returns {number} total size of all modules in this chunk
  288. */
  289. modulesSize() {
  290. const chunkGraph = ChunkGraph.getChunkGraphForChunk(
  291. this,
  292. "Chunk.modulesSize",
  293. "DEP_WEBPACK_CHUNK_MODULES_SIZE"
  294. );
  295. return chunkGraph.getChunkModulesSize(this);
  296. }
  297. /**
  298. * @param {ChunkSizeOptions} options options object
  299. * @returns {number} total size of this chunk
  300. */
  301. size(options = {}) {
  302. const chunkGraph = ChunkGraph.getChunkGraphForChunk(
  303. this,
  304. "Chunk.size",
  305. "DEP_WEBPACK_CHUNK_SIZE"
  306. );
  307. return chunkGraph.getChunkSize(this, options);
  308. }
  309. /**
  310. * @param {Chunk} otherChunk the other chunk
  311. * @param {ChunkSizeOptions} options options object
  312. * @returns {number} total size of the chunk or false if the chunk can't be integrated
  313. */
  314. integratedSize(otherChunk, options) {
  315. const chunkGraph = ChunkGraph.getChunkGraphForChunk(
  316. this,
  317. "Chunk.integratedSize",
  318. "DEP_WEBPACK_CHUNK_INTEGRATED_SIZE"
  319. );
  320. return chunkGraph.getIntegratedChunksSize(this, otherChunk, options);
  321. }
  322. /**
  323. * @param {ModuleFilterPredicate} filterFn function used to filter modules
  324. * @returns {ChunkModuleMaps} module map information
  325. */
  326. getChunkModuleMaps(filterFn) {
  327. const chunkGraph = ChunkGraph.getChunkGraphForChunk(
  328. this,
  329. "Chunk.getChunkModuleMaps",
  330. "DEP_WEBPACK_CHUNK_GET_CHUNK_MODULE_MAPS"
  331. );
  332. /** @type {ChunkModuleIdMap} */
  333. const chunkModuleIdMap = Object.create(null);
  334. /** @type {chunkModuleHashMap} */
  335. const chunkModuleHashMap = Object.create(null);
  336. for (const asyncChunk of this.getAllAsyncChunks()) {
  337. /** @type {ChunkId[] | undefined} */
  338. let array;
  339. for (const module of chunkGraph.getOrderedChunkModulesIterable(
  340. asyncChunk,
  341. compareModulesById(chunkGraph)
  342. )) {
  343. if (filterFn(module)) {
  344. if (array === undefined) {
  345. array = [];
  346. chunkModuleIdMap[/** @type {ChunkId} */ (asyncChunk.id)] = array;
  347. }
  348. const moduleId =
  349. /** @type {ModuleId} */
  350. (chunkGraph.getModuleId(module));
  351. array.push(moduleId);
  352. chunkModuleHashMap[moduleId] = chunkGraph.getRenderedModuleHash(
  353. module,
  354. undefined
  355. );
  356. }
  357. }
  358. }
  359. return {
  360. id: chunkModuleIdMap,
  361. hash: chunkModuleHashMap
  362. };
  363. }
  364. /**
  365. * @param {ModuleFilterPredicate} filterFn predicate function used to filter modules
  366. * @param {ChunkFilterPredicate=} filterChunkFn predicate function used to filter chunks
  367. * @returns {boolean} return true if module exists in graph
  368. */
  369. hasModuleInGraph(filterFn, filterChunkFn) {
  370. const chunkGraph = ChunkGraph.getChunkGraphForChunk(
  371. this,
  372. "Chunk.hasModuleInGraph",
  373. "DEP_WEBPACK_CHUNK_HAS_MODULE_IN_GRAPH"
  374. );
  375. return chunkGraph.hasModuleInGraph(this, filterFn, filterChunkFn);
  376. }
  377. /**
  378. * @deprecated
  379. * @param {boolean} realHash whether the full hash or the rendered hash is to be used
  380. * @returns {ChunkMaps} the chunk map information
  381. */
  382. getChunkMaps(realHash) {
  383. /** @type {Record<ChunkId, string>} */
  384. const chunkHashMap = Object.create(null);
  385. /** @type {Record<string, Record<ChunkId, string>>} */
  386. const chunkContentHashMap = Object.create(null);
  387. /** @type {Record<ChunkId, string>} */
  388. const chunkNameMap = Object.create(null);
  389. for (const chunk of this.getAllAsyncChunks()) {
  390. const id = /** @type {ChunkId} */ (chunk.id);
  391. chunkHashMap[id] =
  392. /** @type {string} */
  393. (realHash ? chunk.hash : chunk.renderedHash);
  394. for (const key of Object.keys(chunk.contentHash)) {
  395. if (!chunkContentHashMap[key]) {
  396. chunkContentHashMap[key] = Object.create(null);
  397. }
  398. chunkContentHashMap[key][id] = chunk.contentHash[key];
  399. }
  400. if (chunk.name) {
  401. chunkNameMap[id] = chunk.name;
  402. }
  403. }
  404. return {
  405. hash: chunkHashMap,
  406. contentHash: chunkContentHashMap,
  407. name: chunkNameMap
  408. };
  409. }
  410. // BACKWARD-COMPAT END
  411. /**
  412. * @returns {boolean} whether or not the Chunk will have a runtime
  413. */
  414. hasRuntime() {
  415. for (const chunkGroup of this._groups) {
  416. if (
  417. chunkGroup instanceof Entrypoint &&
  418. chunkGroup.getRuntimeChunk() === this
  419. ) {
  420. return true;
  421. }
  422. }
  423. return false;
  424. }
  425. /**
  426. * @returns {boolean} whether or not this chunk can be an initial chunk
  427. */
  428. canBeInitial() {
  429. for (const chunkGroup of this._groups) {
  430. if (chunkGroup.isInitial()) return true;
  431. }
  432. return false;
  433. }
  434. /**
  435. * @returns {boolean} whether this chunk can only be an initial chunk
  436. */
  437. isOnlyInitial() {
  438. if (this._groups.size <= 0) return false;
  439. for (const chunkGroup of this._groups) {
  440. if (!chunkGroup.isInitial()) return false;
  441. }
  442. return true;
  443. }
  444. /**
  445. * @returns {EntryOptions | undefined} the entry options for this chunk
  446. */
  447. getEntryOptions() {
  448. for (const chunkGroup of this._groups) {
  449. if (chunkGroup instanceof Entrypoint) {
  450. return chunkGroup.options;
  451. }
  452. }
  453. return undefined;
  454. }
  455. /**
  456. * @param {ChunkGroup} chunkGroup the chunkGroup the chunk is being added
  457. * @returns {void}
  458. */
  459. addGroup(chunkGroup) {
  460. this._groups.add(chunkGroup);
  461. }
  462. /**
  463. * @param {ChunkGroup} chunkGroup the chunkGroup the chunk is being removed from
  464. * @returns {void}
  465. */
  466. removeGroup(chunkGroup) {
  467. this._groups.delete(chunkGroup);
  468. }
  469. /**
  470. * @param {ChunkGroup} chunkGroup the chunkGroup to check
  471. * @returns {boolean} returns true if chunk has chunkGroup reference and exists in chunkGroup
  472. */
  473. isInGroup(chunkGroup) {
  474. return this._groups.has(chunkGroup);
  475. }
  476. /**
  477. * @returns {number} the amount of groups that the said chunk is in
  478. */
  479. getNumberOfGroups() {
  480. return this._groups.size;
  481. }
  482. /**
  483. * @returns {SortableChunkGroups} the chunkGroups that the said chunk is referenced in
  484. */
  485. get groupsIterable() {
  486. this._groups.sort();
  487. return this._groups;
  488. }
  489. /**
  490. * @returns {void}
  491. */
  492. disconnectFromGroups() {
  493. for (const chunkGroup of this._groups) {
  494. chunkGroup.removeChunk(this);
  495. }
  496. }
  497. /**
  498. * @param {Chunk} newChunk the new chunk that will be split out of
  499. * @returns {void}
  500. */
  501. split(newChunk) {
  502. for (const chunkGroup of this._groups) {
  503. chunkGroup.insertChunk(newChunk, this);
  504. newChunk.addGroup(chunkGroup);
  505. }
  506. for (const idHint of this.idNameHints) {
  507. newChunk.idNameHints.add(idHint);
  508. }
  509. newChunk.runtime = mergeRuntime(newChunk.runtime, this.runtime);
  510. }
  511. /**
  512. * @param {Hash} hash hash (will be modified)
  513. * @param {ChunkGraph} chunkGraph the chunk graph
  514. * @returns {void}
  515. */
  516. updateHash(hash, chunkGraph) {
  517. hash.update(
  518. `${this.id} ${this.ids ? this.ids.join() : ""} ${this.name || ""} `
  519. );
  520. const xor = new StringXor();
  521. for (const m of chunkGraph.getChunkModulesIterable(this)) {
  522. xor.add(chunkGraph.getModuleHash(m, this.runtime));
  523. }
  524. xor.updateHash(hash);
  525. const entryModules =
  526. chunkGraph.getChunkEntryModulesWithChunkGroupIterable(this);
  527. for (const [m, chunkGroup] of entryModules) {
  528. hash.update(
  529. `entry${chunkGraph.getModuleId(m)}${
  530. /** @type {ChunkGroup} */ (chunkGroup).id
  531. }`
  532. );
  533. }
  534. }
  535. /**
  536. * @returns {Chunks} a set of all the async chunks
  537. */
  538. getAllAsyncChunks() {
  539. /** @type {Queue} */
  540. const queue = new Set();
  541. /** @type {Chunks} */
  542. const chunks = new Set();
  543. const initialChunks = intersect(
  544. Array.from(this.groupsIterable, (g) => new Set(g.chunks))
  545. );
  546. /** @type {Queue} */
  547. const initialQueue = new Set(this.groupsIterable);
  548. for (const chunkGroup of initialQueue) {
  549. for (const child of chunkGroup.childrenIterable) {
  550. if (child instanceof Entrypoint) {
  551. initialQueue.add(child);
  552. } else {
  553. queue.add(child);
  554. }
  555. }
  556. }
  557. for (const chunkGroup of queue) {
  558. for (const chunk of chunkGroup.chunks) {
  559. if (!initialChunks.has(chunk)) {
  560. chunks.add(chunk);
  561. }
  562. }
  563. for (const child of chunkGroup.childrenIterable) {
  564. queue.add(child);
  565. }
  566. }
  567. return chunks;
  568. }
  569. /**
  570. * @returns {Chunks} a set of all the initial chunks (including itself)
  571. */
  572. getAllInitialChunks() {
  573. /** @type {Chunks} */
  574. const chunks = new Set();
  575. /** @type {Queue} */
  576. const queue = new Set(this.groupsIterable);
  577. for (const group of queue) {
  578. if (group.isInitial()) {
  579. for (const c of group.chunks) chunks.add(c);
  580. for (const g of group.childrenIterable) queue.add(g);
  581. }
  582. }
  583. return chunks;
  584. }
  585. /**
  586. * @returns {Chunks} a set of all the referenced chunks (including itself)
  587. */
  588. getAllReferencedChunks() {
  589. /** @type {Queue} */
  590. const queue = new Set(this.groupsIterable);
  591. /** @type {Chunks} */
  592. const chunks = new Set();
  593. for (const chunkGroup of queue) {
  594. for (const chunk of chunkGroup.chunks) {
  595. chunks.add(chunk);
  596. }
  597. for (const child of chunkGroup.childrenIterable) {
  598. queue.add(child);
  599. }
  600. }
  601. return chunks;
  602. }
  603. /**
  604. * @returns {Entrypoints} a set of all the referenced entrypoints
  605. */
  606. getAllReferencedAsyncEntrypoints() {
  607. /** @type {Queue} */
  608. const queue = new Set(this.groupsIterable);
  609. /** @type {Entrypoints} */
  610. const entrypoints = new Set();
  611. for (const chunkGroup of queue) {
  612. for (const entrypoint of chunkGroup.asyncEntrypointsIterable) {
  613. entrypoints.add(/** @type {Entrypoint} */ (entrypoint));
  614. }
  615. for (const child of chunkGroup.childrenIterable) {
  616. queue.add(child);
  617. }
  618. }
  619. return entrypoints;
  620. }
  621. /**
  622. * @returns {boolean} true, if the chunk references async chunks
  623. */
  624. hasAsyncChunks() {
  625. /** @type {Queue} */
  626. const queue = new Set();
  627. const initialChunks = intersect(
  628. Array.from(this.groupsIterable, (g) => new Set(g.chunks))
  629. );
  630. for (const chunkGroup of this.groupsIterable) {
  631. for (const child of chunkGroup.childrenIterable) {
  632. queue.add(child);
  633. }
  634. }
  635. for (const chunkGroup of queue) {
  636. for (const chunk of chunkGroup.chunks) {
  637. if (!initialChunks.has(chunk)) {
  638. return true;
  639. }
  640. }
  641. for (const child of chunkGroup.childrenIterable) {
  642. queue.add(child);
  643. }
  644. }
  645. return false;
  646. }
  647. /**
  648. * @param {ChunkGraph} chunkGraph the chunk graph
  649. * @param {ChunkFilterPredicate=} filterFn function used to filter chunks
  650. * @returns {Record<string, ChunkId[]>} a record object of names to lists of child ids(?)
  651. */
  652. getChildIdsByOrders(chunkGraph, filterFn) {
  653. /** @type {Map<string, {order: number, group: ChunkGroup}[]>} */
  654. const lists = new Map();
  655. for (const group of this.groupsIterable) {
  656. if (group.chunks[group.chunks.length - 1] === this) {
  657. for (const childGroup of group.childrenIterable) {
  658. for (const key of Object.keys(childGroup.options)) {
  659. if (key.endsWith("Order")) {
  660. const name = key.slice(0, key.length - "Order".length);
  661. let list = lists.get(name);
  662. if (list === undefined) {
  663. list = [];
  664. lists.set(name, list);
  665. }
  666. list.push({
  667. order:
  668. /** @type {number} */
  669. (
  670. childGroup.options[
  671. /** @type {keyof ChunkGroupOptions} */
  672. (key)
  673. ]
  674. ),
  675. group: childGroup
  676. });
  677. }
  678. }
  679. }
  680. }
  681. }
  682. /** @type {Record<string, ChunkId[]>} */
  683. const result = Object.create(null);
  684. for (const [name, list] of lists) {
  685. list.sort((a, b) => {
  686. const cmp = b.order - a.order;
  687. if (cmp !== 0) return cmp;
  688. return a.group.compareTo(chunkGraph, b.group);
  689. });
  690. /** @type {Set<ChunkId>} */
  691. const chunkIdSet = new Set();
  692. for (const item of list) {
  693. for (const chunk of item.group.chunks) {
  694. if (filterFn && !filterFn(chunk, chunkGraph)) continue;
  695. chunkIdSet.add(/** @type {ChunkId} */ (chunk.id));
  696. }
  697. }
  698. if (chunkIdSet.size > 0) {
  699. result[name] = [...chunkIdSet];
  700. }
  701. }
  702. return result;
  703. }
  704. /**
  705. * @param {ChunkGraph} chunkGraph the chunk graph
  706. * @param {string} type option name
  707. * @returns {{ onChunks: Chunk[], chunks: Chunks }[] | undefined} referenced chunks for a specific type
  708. */
  709. getChildrenOfTypeInOrder(chunkGraph, type) {
  710. const list = [];
  711. for (const group of this.groupsIterable) {
  712. for (const childGroup of group.childrenIterable) {
  713. const order =
  714. childGroup.options[/** @type {keyof ChunkGroupOptions} */ (type)];
  715. if (order === undefined) continue;
  716. list.push({
  717. order,
  718. group,
  719. childGroup
  720. });
  721. }
  722. }
  723. if (list.length === 0) return;
  724. list.sort((a, b) => {
  725. const cmp =
  726. /** @type {number} */ (b.order) - /** @type {number} */ (a.order);
  727. if (cmp !== 0) return cmp;
  728. return a.group.compareTo(chunkGraph, b.group);
  729. });
  730. const result = [];
  731. let lastEntry;
  732. for (const { group, childGroup } of list) {
  733. if (lastEntry && lastEntry.onChunks === group.chunks) {
  734. for (const chunk of childGroup.chunks) {
  735. lastEntry.chunks.add(chunk);
  736. }
  737. } else {
  738. result.push(
  739. (lastEntry = {
  740. onChunks: group.chunks,
  741. chunks: new Set(childGroup.chunks)
  742. })
  743. );
  744. }
  745. }
  746. return result;
  747. }
  748. /**
  749. * @param {ChunkGraph} chunkGraph the chunk graph
  750. * @param {boolean=} includeDirectChildren include direct children (by default only children of async children are included)
  751. * @param {ChunkFilterPredicate=} filterFn function used to filter chunks
  752. * @returns {ChunkChildIdsByOrdersMapByData} a record object of names to lists of child ids(?) by chunk id
  753. */
  754. getChildIdsByOrdersMap(chunkGraph, includeDirectChildren, filterFn) {
  755. /** @type {ChunkChildIdsByOrdersMapByData} */
  756. const chunkMaps = Object.create(null);
  757. /**
  758. * @param {Chunk} chunk a chunk
  759. * @returns {void}
  760. */
  761. const addChildIdsByOrdersToMap = (chunk) => {
  762. const data = chunk.getChildIdsByOrders(chunkGraph, filterFn);
  763. for (const key of Object.keys(data)) {
  764. let chunkMap = chunkMaps[key];
  765. if (chunkMap === undefined) {
  766. chunkMaps[key] = chunkMap = Object.create(null);
  767. }
  768. chunkMap[/** @type {ChunkId} */ (chunk.id)] = data[key];
  769. }
  770. };
  771. if (includeDirectChildren) {
  772. /** @type {Chunks} */
  773. const chunks = new Set();
  774. for (const chunkGroup of this.groupsIterable) {
  775. for (const chunk of chunkGroup.chunks) {
  776. chunks.add(chunk);
  777. }
  778. }
  779. for (const chunk of chunks) {
  780. addChildIdsByOrdersToMap(chunk);
  781. }
  782. }
  783. for (const chunk of this.getAllAsyncChunks()) {
  784. addChildIdsByOrdersToMap(chunk);
  785. }
  786. return chunkMaps;
  787. }
  788. /**
  789. * @param {ChunkGraph} chunkGraph the chunk graph
  790. * @param {string} type option name
  791. * @param {boolean=} includeDirectChildren include direct children (by default only children of async children are included)
  792. * @param {ChunkFilterPredicate=} filterFn function used to filter chunks
  793. * @returns {boolean} true when the child is of type order, otherwise false
  794. */
  795. hasChildByOrder(chunkGraph, type, includeDirectChildren, filterFn) {
  796. if (includeDirectChildren) {
  797. /** @type {Chunks} */
  798. const chunks = new Set();
  799. for (const chunkGroup of this.groupsIterable) {
  800. for (const chunk of chunkGroup.chunks) {
  801. chunks.add(chunk);
  802. }
  803. }
  804. for (const chunk of chunks) {
  805. const data = chunk.getChildIdsByOrders(chunkGraph, filterFn);
  806. if (data[type] !== undefined) return true;
  807. }
  808. }
  809. for (const chunk of this.getAllAsyncChunks()) {
  810. const data = chunk.getChildIdsByOrders(chunkGraph, filterFn);
  811. if (data[type] !== undefined) return true;
  812. }
  813. return false;
  814. }
  815. }
  816. module.exports = Chunk;