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.

Chunk.js 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const util = require("util");
  7. const SortableSet = require("./util/SortableSet");
  8. const intersect = require("./util/SetHelpers").intersect;
  9. const GraphHelpers = require("./GraphHelpers");
  10. const Entrypoint = require("./Entrypoint");
  11. let debugId = 1000;
  12. const ERR_CHUNK_ENTRY = "Chunk.entry was removed. Use hasRuntime()";
  13. const ERR_CHUNK_INITIAL =
  14. "Chunk.initial was removed. Use canBeInitial/isOnlyInitial()";
  15. /** @typedef {import("./Module")} Module */
  16. /** @typedef {import("./ChunkGroup")} ChunkGroup */
  17. /** @typedef {import("./ModuleReason")} ModuleReason */
  18. /** @typedef {import("webpack-sources").Source} Source */
  19. /** @typedef {import("./util/createHash").Hash} Hash */
  20. /**
  21. * @typedef {Object} WithId an object who has an id property *
  22. * @property {string | number} id the id of the object
  23. */
  24. /**
  25. * Compare two Modules based on their ids for sorting
  26. * @param {Module} a module
  27. * @param {Module} b module
  28. * @returns {-1|0|1} sort value
  29. */
  30. // TODO use @callback
  31. /** @typedef {(a: Module, b: Module) => -1|0|1} ModuleSortPredicate */
  32. /** @typedef {(m: Module) => boolean} ModuleFilterPredicate */
  33. /** @typedef {(c: Chunk) => boolean} ChunkFilterPredicate */
  34. const sortModuleById = (a, b) => {
  35. if (a.id < b.id) return -1;
  36. if (b.id < a.id) return 1;
  37. return 0;
  38. };
  39. /**
  40. * Compare two ChunkGroups based on their ids for sorting
  41. * @param {ChunkGroup} a chunk group
  42. * @param {ChunkGroup} b chunk group
  43. * @returns {-1|0|1} sort value
  44. */
  45. const sortChunkGroupById = (a, b) => {
  46. if (a.id < b.id) return -1;
  47. if (b.id < a.id) return 1;
  48. return 0;
  49. };
  50. /**
  51. * Compare two Identifiables , based on their ids for sorting
  52. * @param {Module} a first object with ident fn
  53. * @param {Module} b second object with ident fn
  54. * @returns {-1|0|1} The order number of the sort
  55. */
  56. const sortByIdentifier = (a, b) => {
  57. if (a.identifier() > b.identifier()) return 1;
  58. if (a.identifier() < b.identifier()) return -1;
  59. return 0;
  60. };
  61. /**
  62. * @returns {string} a concatenation of module identifiers sorted
  63. * @param {SortableSet} set to pull module identifiers from
  64. */
  65. const getModulesIdent = set => {
  66. set.sort();
  67. let str = "";
  68. for (const m of set) {
  69. str += m.identifier() + "#";
  70. }
  71. return str;
  72. };
  73. /**
  74. * @template T
  75. * @param {SortableSet<T>} set the sortable set to convert to array
  76. * @returns {Array<T>} the array returned from Array.from(set)
  77. */
  78. const getArray = set => Array.from(set);
  79. /**
  80. * @param {SortableSet<Module>} set the sortable Set to get the count/size of
  81. * @returns {number} the size of the modules
  82. */
  83. const getModulesSize = set => {
  84. let size = 0;
  85. for (const module of set) {
  86. size += module.size();
  87. }
  88. return size;
  89. };
  90. /**
  91. * A Chunk is a unit of encapsulation for Modules.
  92. * Chunks are "rendered" into bundles that get emitted when the build completes.
  93. */
  94. class Chunk {
  95. /**
  96. * @param {string=} name of chunk being created, is optional (for subclasses)
  97. */
  98. constructor(name) {
  99. /** @type {number | null} */
  100. this.id = null;
  101. /** @type {number[] | null} */
  102. this.ids = null;
  103. /** @type {number} */
  104. this.debugId = debugId++;
  105. /** @type {string} */
  106. this.name = name;
  107. /** @type {boolean} */
  108. this.preventIntegration = false;
  109. /** @type {Module=} */
  110. this.entryModule = undefined;
  111. /** @private @type {SortableSet<Module>} */
  112. this._modules = new SortableSet(undefined, sortByIdentifier);
  113. /** @type {string?} */
  114. this.filenameTemplate = undefined;
  115. /** @private @type {SortableSet<ChunkGroup>} */
  116. this._groups = new SortableSet(undefined, sortChunkGroupById);
  117. /** @type {string[]} */
  118. this.files = [];
  119. /** @type {boolean} */
  120. this.rendered = false;
  121. /** @type {string=} */
  122. this.hash = undefined;
  123. /** @type {Object} */
  124. this.contentHash = Object.create(null);
  125. /** @type {string=} */
  126. this.renderedHash = undefined;
  127. /** @type {string=} */
  128. this.chunkReason = undefined;
  129. /** @type {boolean} */
  130. this.extraAsync = false;
  131. this.removedModules = undefined;
  132. }
  133. /**
  134. * @deprecated Chunk.entry has been deprecated. Please use .hasRuntime() instead
  135. * @returns {never} Throws an error trying to access this property
  136. */
  137. get entry() {
  138. throw new Error(ERR_CHUNK_ENTRY);
  139. }
  140. /**
  141. * @deprecated .entry has been deprecated. Please use .hasRuntime() instead
  142. * @param {never} data The data that was attempting to be set
  143. * @returns {never} Throws an error trying to access this property
  144. */
  145. set entry(data) {
  146. throw new Error(ERR_CHUNK_ENTRY);
  147. }
  148. /**
  149. * @deprecated Chunk.initial was removed. Use canBeInitial/isOnlyInitial()
  150. * @returns {never} Throws an error trying to access this property
  151. */
  152. get initial() {
  153. throw new Error(ERR_CHUNK_INITIAL);
  154. }
  155. /**
  156. * @deprecated Chunk.initial was removed. Use canBeInitial/isOnlyInitial()
  157. * @param {never} data The data attempting to be set
  158. * @returns {never} Throws an error trying to access this property
  159. */
  160. set initial(data) {
  161. throw new Error(ERR_CHUNK_INITIAL);
  162. }
  163. /**
  164. * @returns {boolean} whether or not the Chunk will have a runtime
  165. */
  166. hasRuntime() {
  167. for (const chunkGroup of this._groups) {
  168. if (
  169. chunkGroup.isInitial() &&
  170. chunkGroup instanceof Entrypoint &&
  171. chunkGroup.getRuntimeChunk() === this
  172. ) {
  173. return true;
  174. }
  175. }
  176. return false;
  177. }
  178. /**
  179. * @returns {boolean} whether or not this chunk can be an initial chunk
  180. */
  181. canBeInitial() {
  182. for (const chunkGroup of this._groups) {
  183. if (chunkGroup.isInitial()) return true;
  184. }
  185. return false;
  186. }
  187. /**
  188. * @returns {boolean} whether this chunk can only be an initial chunk
  189. */
  190. isOnlyInitial() {
  191. if (this._groups.size <= 0) return false;
  192. for (const chunkGroup of this._groups) {
  193. if (!chunkGroup.isInitial()) return false;
  194. }
  195. return true;
  196. }
  197. /**
  198. * @returns {boolean} if this chunk contains the entry module
  199. */
  200. hasEntryModule() {
  201. return !!this.entryModule;
  202. }
  203. /**
  204. * @param {Module} module the module that will be added to this chunk.
  205. * @returns {boolean} returns true if the chunk doesn't have the module and it was added
  206. */
  207. addModule(module) {
  208. if (!this._modules.has(module)) {
  209. this._modules.add(module);
  210. return true;
  211. }
  212. return false;
  213. }
  214. /**
  215. * @param {Module} module the module that will be removed from this chunk
  216. * @returns {boolean} returns true if chunk exists and is successfully deleted
  217. */
  218. removeModule(module) {
  219. if (this._modules.delete(module)) {
  220. module.removeChunk(this);
  221. return true;
  222. }
  223. return false;
  224. }
  225. /**
  226. * @param {Module[]} modules the new modules to be set
  227. * @returns {void} set new modules to this chunk and return nothing
  228. */
  229. setModules(modules) {
  230. this._modules = new SortableSet(modules, sortByIdentifier);
  231. }
  232. /**
  233. * @returns {number} the amount of modules in chunk
  234. */
  235. getNumberOfModules() {
  236. return this._modules.size;
  237. }
  238. /**
  239. * @returns {SortableSet} return the modules SortableSet for this chunk
  240. */
  241. get modulesIterable() {
  242. return this._modules;
  243. }
  244. /**
  245. * @param {ChunkGroup} chunkGroup the chunkGroup the chunk is being added
  246. * @returns {boolean} returns true if chunk is not apart of chunkGroup and is added successfully
  247. */
  248. addGroup(chunkGroup) {
  249. if (this._groups.has(chunkGroup)) return false;
  250. this._groups.add(chunkGroup);
  251. return true;
  252. }
  253. /**
  254. * @param {ChunkGroup} chunkGroup the chunkGroup the chunk is being removed from
  255. * @returns {boolean} returns true if chunk does exist in chunkGroup and is removed
  256. */
  257. removeGroup(chunkGroup) {
  258. if (!this._groups.has(chunkGroup)) return false;
  259. this._groups.delete(chunkGroup);
  260. return true;
  261. }
  262. /**
  263. * @param {ChunkGroup} chunkGroup the chunkGroup to check
  264. * @returns {boolean} returns true if chunk has chunkGroup reference and exists in chunkGroup
  265. */
  266. isInGroup(chunkGroup) {
  267. return this._groups.has(chunkGroup);
  268. }
  269. /**
  270. * @returns {number} the amount of groups said chunk is in
  271. */
  272. getNumberOfGroups() {
  273. return this._groups.size;
  274. }
  275. /**
  276. * @returns {SortableSet<ChunkGroup>} the chunkGroups that said chunk is referenced in
  277. */
  278. get groupsIterable() {
  279. return this._groups;
  280. }
  281. /**
  282. * @param {Chunk} otherChunk the chunk to compare itself with
  283. * @returns {-1|0|1} this is a comparitor function like sort and returns -1, 0, or 1 based on sort order
  284. */
  285. compareTo(otherChunk) {
  286. if (this.name && !otherChunk.name) return -1;
  287. if (!this.name && otherChunk.name) return 1;
  288. if (this.name < otherChunk.name) return -1;
  289. if (this.name > otherChunk.name) return 1;
  290. if (this._modules.size > otherChunk._modules.size) return -1;
  291. if (this._modules.size < otherChunk._modules.size) return 1;
  292. this._modules.sort();
  293. otherChunk._modules.sort();
  294. const a = this._modules[Symbol.iterator]();
  295. const b = otherChunk._modules[Symbol.iterator]();
  296. // eslint-disable-next-line no-constant-condition
  297. while (true) {
  298. const aItem = a.next();
  299. if (aItem.done) return 0;
  300. const bItem = b.next();
  301. const aModuleIdentifier = aItem.value.identifier();
  302. const bModuleIdentifier = bItem.value.identifier();
  303. if (aModuleIdentifier < bModuleIdentifier) return -1;
  304. if (aModuleIdentifier > bModuleIdentifier) return 1;
  305. }
  306. }
  307. /**
  308. * @param {Module} module Module to check
  309. * @returns {boolean} returns true if module does exist in this chunk
  310. */
  311. containsModule(module) {
  312. return this._modules.has(module);
  313. }
  314. /**
  315. * @returns {Module[]} an array of modules (do not modify)
  316. */
  317. getModules() {
  318. return this._modules.getFromCache(getArray);
  319. }
  320. getModulesIdent() {
  321. return this._modules.getFromUnorderedCache(getModulesIdent);
  322. }
  323. /**
  324. * @param {string=} reason reason why chunk is removed
  325. * @returns {void}
  326. */
  327. remove(reason) {
  328. // cleanup modules
  329. // Array.from is used here to create a clone, because removeChunk modifies this._modules
  330. for (const module of Array.from(this._modules)) {
  331. module.removeChunk(this);
  332. }
  333. for (const chunkGroup of this._groups) {
  334. chunkGroup.removeChunk(this);
  335. }
  336. }
  337. /**
  338. *
  339. * @param {Module} module module to move
  340. * @param {Chunk} otherChunk other chunk to move it to
  341. * @returns {void}
  342. */
  343. moveModule(module, otherChunk) {
  344. GraphHelpers.disconnectChunkAndModule(this, module);
  345. GraphHelpers.connectChunkAndModule(otherChunk, module);
  346. module.rewriteChunkInReasons(this, [otherChunk]);
  347. }
  348. /**
  349. *
  350. * @param {Chunk} otherChunk the chunk to integrate with
  351. * @param {ModuleReason} reason reason why the module is being integrated
  352. * @returns {boolean} returns true or false if integration succeeds or fails
  353. */
  354. integrate(otherChunk, reason) {
  355. if (!this.canBeIntegrated(otherChunk)) {
  356. return false;
  357. }
  358. // Pick a new name for the integrated chunk
  359. if (this.name && otherChunk.name) {
  360. if (this.hasEntryModule() === otherChunk.hasEntryModule()) {
  361. // When both chunks have entry modules or none have one, use
  362. // shortest name
  363. if (this.name.length !== otherChunk.name.length) {
  364. this.name =
  365. this.name.length < otherChunk.name.length
  366. ? this.name
  367. : otherChunk.name;
  368. } else {
  369. this.name = this.name < otherChunk.name ? this.name : otherChunk.name;
  370. }
  371. } else if (otherChunk.hasEntryModule()) {
  372. // Pick the name of the chunk with the entry module
  373. this.name = otherChunk.name;
  374. }
  375. } else if (otherChunk.name) {
  376. this.name = otherChunk.name;
  377. }
  378. // Array.from is used here to create a clone, because moveModule modifies otherChunk._modules
  379. for (const module of Array.from(otherChunk._modules)) {
  380. otherChunk.moveModule(module, this);
  381. }
  382. otherChunk._modules.clear();
  383. if (otherChunk.entryModule) {
  384. this.entryModule = otherChunk.entryModule;
  385. }
  386. for (const chunkGroup of otherChunk._groups) {
  387. chunkGroup.replaceChunk(otherChunk, this);
  388. this.addGroup(chunkGroup);
  389. }
  390. otherChunk._groups.clear();
  391. return true;
  392. }
  393. /**
  394. * @param {Chunk} newChunk the new chunk that will be split out of the current chunk
  395. * @returns {void}
  396. */
  397. split(newChunk) {
  398. for (const chunkGroup of this._groups) {
  399. chunkGroup.insertChunk(newChunk, this);
  400. newChunk.addGroup(chunkGroup);
  401. }
  402. }
  403. isEmpty() {
  404. return this._modules.size === 0;
  405. }
  406. updateHash(hash) {
  407. hash.update(`${this.id} `);
  408. hash.update(this.ids ? this.ids.join(",") : "");
  409. hash.update(`${this.name || ""} `);
  410. for (const m of this._modules) {
  411. hash.update(m.hash);
  412. }
  413. }
  414. canBeIntegrated(otherChunk) {
  415. if (this.preventIntegration || otherChunk.preventIntegration) {
  416. return false;
  417. }
  418. const isAvailable = (a, b) => {
  419. const queue = new Set(b.groupsIterable);
  420. for (const chunkGroup of queue) {
  421. if (a.isInGroup(chunkGroup)) continue;
  422. if (chunkGroup.isInitial()) return false;
  423. for (const parent of chunkGroup.parentsIterable) {
  424. queue.add(parent);
  425. }
  426. }
  427. return true;
  428. };
  429. const selfHasRuntime = this.hasRuntime();
  430. const otherChunkHasRuntime = otherChunk.hasRuntime();
  431. if (selfHasRuntime !== otherChunkHasRuntime) {
  432. if (selfHasRuntime) {
  433. return isAvailable(this, otherChunk);
  434. } else if (otherChunkHasRuntime) {
  435. return isAvailable(otherChunk, this);
  436. } else {
  437. return false;
  438. }
  439. }
  440. if (this.hasEntryModule() || otherChunk.hasEntryModule()) {
  441. return false;
  442. }
  443. return true;
  444. }
  445. /**
  446. *
  447. * @param {number} size the size
  448. * @param {Object} options the options passed in
  449. * @returns {number} the multiplier returned
  450. */
  451. addMultiplierAndOverhead(size, options) {
  452. const overhead =
  453. typeof options.chunkOverhead === "number" ? options.chunkOverhead : 10000;
  454. const multiplicator = this.canBeInitial()
  455. ? options.entryChunkMultiplicator || 10
  456. : 1;
  457. return size * multiplicator + overhead;
  458. }
  459. /**
  460. * @returns {number} the size of all modules
  461. */
  462. modulesSize() {
  463. return this._modules.getFromUnorderedCache(getModulesSize);
  464. }
  465. /**
  466. * @param {Object} options the size display options
  467. * @returns {number} the chunk size
  468. */
  469. size(options = {}) {
  470. return this.addMultiplierAndOverhead(this.modulesSize(), options);
  471. }
  472. /**
  473. * @param {Chunk} otherChunk the other chunk
  474. * @param {TODO} options the options for this function
  475. * @returns {number | false} the size, or false if it can't be integrated
  476. */
  477. integratedSize(otherChunk, options) {
  478. // Chunk if it's possible to integrate this chunk
  479. if (!this.canBeIntegrated(otherChunk)) {
  480. return false;
  481. }
  482. let integratedModulesSize = this.modulesSize();
  483. // only count modules that do not exist in this chunk!
  484. for (const otherModule of otherChunk._modules) {
  485. if (!this._modules.has(otherModule)) {
  486. integratedModulesSize += otherModule.size();
  487. }
  488. }
  489. return this.addMultiplierAndOverhead(integratedModulesSize, options);
  490. }
  491. /**
  492. * @param {function(Module, Module): -1|0|1=} sortByFn a predicate function used to sort modules
  493. * @returns {void}
  494. */
  495. sortModules(sortByFn) {
  496. this._modules.sortWith(sortByFn || sortModuleById);
  497. }
  498. sortItems() {
  499. this.sortModules();
  500. }
  501. /**
  502. * @returns {Set<Chunk>} a set of all the async chunks
  503. */
  504. getAllAsyncChunks() {
  505. const queue = new Set();
  506. const chunks = new Set();
  507. const initialChunks = intersect(
  508. Array.from(this.groupsIterable, g => new Set(g.chunks))
  509. );
  510. for (const chunkGroup of this.groupsIterable) {
  511. for (const child of chunkGroup.childrenIterable) {
  512. queue.add(child);
  513. }
  514. }
  515. for (const chunkGroup of queue) {
  516. for (const chunk of chunkGroup.chunks) {
  517. if (!initialChunks.has(chunk)) {
  518. chunks.add(chunk);
  519. }
  520. }
  521. for (const child of chunkGroup.childrenIterable) {
  522. queue.add(child);
  523. }
  524. }
  525. return chunks;
  526. }
  527. /**
  528. * @typedef {Object} ChunkMaps
  529. * @property {Record<string|number, string>} hash
  530. * @property {Record<string|number, Record<string, string>>} contentHash
  531. * @property {Record<string|number, string>} name
  532. */
  533. /**
  534. * @param {boolean} realHash should the full hash or the rendered hash be used
  535. * @returns {ChunkMaps} the chunk map information
  536. */
  537. getChunkMaps(realHash) {
  538. /** @type {Record<string|number, string>} */
  539. const chunkHashMap = Object.create(null);
  540. /** @type {Record<string|number, Record<string, string>>} */
  541. const chunkContentHashMap = Object.create(null);
  542. /** @type {Record<string|number, string>} */
  543. const chunkNameMap = Object.create(null);
  544. for (const chunk of this.getAllAsyncChunks()) {
  545. chunkHashMap[chunk.id] = realHash ? chunk.hash : chunk.renderedHash;
  546. for (const key of Object.keys(chunk.contentHash)) {
  547. if (!chunkContentHashMap[key]) {
  548. chunkContentHashMap[key] = Object.create(null);
  549. }
  550. chunkContentHashMap[key][chunk.id] = chunk.contentHash[key];
  551. }
  552. if (chunk.name) {
  553. chunkNameMap[chunk.id] = chunk.name;
  554. }
  555. }
  556. return {
  557. hash: chunkHashMap,
  558. contentHash: chunkContentHashMap,
  559. name: chunkNameMap
  560. };
  561. }
  562. /**
  563. * @returns {Record<string, Set<TODO>[]>} a record object of names to lists of child ids(?)
  564. */
  565. getChildIdsByOrders() {
  566. const lists = new Map();
  567. for (const group of this.groupsIterable) {
  568. if (group.chunks[group.chunks.length - 1] === this) {
  569. for (const childGroup of group.childrenIterable) {
  570. // TODO webpack 5 remove this check for options
  571. if (typeof childGroup.options === "object") {
  572. for (const key of Object.keys(childGroup.options)) {
  573. if (key.endsWith("Order")) {
  574. const name = key.substr(0, key.length - "Order".length);
  575. let list = lists.get(name);
  576. if (list === undefined) lists.set(name, (list = []));
  577. list.push({
  578. order: childGroup.options[key],
  579. group: childGroup
  580. });
  581. }
  582. }
  583. }
  584. }
  585. }
  586. }
  587. const result = Object.create(null);
  588. for (const [name, list] of lists) {
  589. list.sort((a, b) => {
  590. const cmp = b.order - a.order;
  591. if (cmp !== 0) return cmp;
  592. // TODO webpack 5 remove this check of compareTo
  593. if (a.group.compareTo) {
  594. return a.group.compareTo(b.group);
  595. }
  596. return 0;
  597. });
  598. result[name] = Array.from(
  599. list.reduce((set, item) => {
  600. for (const chunk of item.group.chunks) {
  601. set.add(chunk.id);
  602. }
  603. return set;
  604. }, new Set())
  605. );
  606. }
  607. return result;
  608. }
  609. getChildIdsByOrdersMap(includeDirectChildren) {
  610. const chunkMaps = Object.create(null);
  611. const addChildIdsByOrdersToMap = chunk => {
  612. const data = chunk.getChildIdsByOrders();
  613. for (const key of Object.keys(data)) {
  614. let chunkMap = chunkMaps[key];
  615. if (chunkMap === undefined) {
  616. chunkMaps[key] = chunkMap = Object.create(null);
  617. }
  618. chunkMap[chunk.id] = data[key];
  619. }
  620. };
  621. if (includeDirectChildren) {
  622. const chunks = new Set();
  623. for (const chunkGroup of this.groupsIterable) {
  624. for (const chunk of chunkGroup.chunks) {
  625. chunks.add(chunk);
  626. }
  627. }
  628. for (const chunk of chunks) {
  629. addChildIdsByOrdersToMap(chunk);
  630. }
  631. }
  632. for (const chunk of this.getAllAsyncChunks()) {
  633. addChildIdsByOrdersToMap(chunk);
  634. }
  635. return chunkMaps;
  636. }
  637. /**
  638. * @typedef {Object} ChunkModuleMaps
  639. * @property {Record<string|number, (string|number)[]>} id
  640. * @property {Record<string|number, string>} hash
  641. */
  642. /**
  643. * @param {ModuleFilterPredicate} filterFn function used to filter modules
  644. * @returns {ChunkModuleMaps} module map information
  645. */
  646. getChunkModuleMaps(filterFn) {
  647. /** @type {Record<string|number, (string|number)[]>} */
  648. const chunkModuleIdMap = Object.create(null);
  649. /** @type {Record<string|number, string>} */
  650. const chunkModuleHashMap = Object.create(null);
  651. for (const chunk of this.getAllAsyncChunks()) {
  652. /** @type {(string|number)[]} */
  653. let array;
  654. for (const module of chunk.modulesIterable) {
  655. if (filterFn(module)) {
  656. if (array === undefined) {
  657. array = [];
  658. chunkModuleIdMap[chunk.id] = array;
  659. }
  660. array.push(module.id);
  661. chunkModuleHashMap[module.id] = module.renderedHash;
  662. }
  663. }
  664. if (array !== undefined) {
  665. array.sort();
  666. }
  667. }
  668. return {
  669. id: chunkModuleIdMap,
  670. hash: chunkModuleHashMap
  671. };
  672. }
  673. /**
  674. *
  675. * @param {function(Module): boolean} filterFn predicate function used to filter modules
  676. * @param {function(Chunk): boolean} filterChunkFn predicate function used to filter chunks
  677. * @returns {boolean} return true if module exists in graph
  678. */
  679. hasModuleInGraph(filterFn, filterChunkFn) {
  680. const queue = new Set(this.groupsIterable);
  681. const chunksProcessed = new Set();
  682. for (const chunkGroup of queue) {
  683. for (const chunk of chunkGroup.chunks) {
  684. if (!chunksProcessed.has(chunk)) {
  685. chunksProcessed.add(chunk);
  686. if (!filterChunkFn || filterChunkFn(chunk)) {
  687. for (const module of chunk.modulesIterable) {
  688. if (filterFn(module)) {
  689. return true;
  690. }
  691. }
  692. }
  693. }
  694. }
  695. for (const child of chunkGroup.childrenIterable) {
  696. queue.add(child);
  697. }
  698. }
  699. return false;
  700. }
  701. toString() {
  702. return `Chunk[${Array.from(this._modules).join()}]`;
  703. }
  704. }
  705. // TODO remove in webpack 5
  706. Object.defineProperty(Chunk.prototype, "forEachModule", {
  707. configurable: false,
  708. value: util.deprecate(
  709. /**
  710. * @deprecated
  711. * @this {Chunk}
  712. * @typedef {function(any, any, Set<any>): void} ForEachModuleCallback
  713. * @param {ForEachModuleCallback} fn Callback function
  714. * @returns {void}
  715. */
  716. function(fn) {
  717. this._modules.forEach(fn);
  718. },
  719. "Chunk.forEachModule: Use for(const module of chunk.modulesIterable) instead"
  720. )
  721. });
  722. // TODO remove in webpack 5
  723. Object.defineProperty(Chunk.prototype, "mapModules", {
  724. configurable: false,
  725. value: util.deprecate(
  726. /**
  727. * @deprecated
  728. * @this {Chunk}
  729. * @typedef {function(any, number): any} MapModulesCallback
  730. * @param {MapModulesCallback} fn Callback function
  731. * @returns {TODO[]} result of mapped modules
  732. */
  733. function(fn) {
  734. return Array.from(this._modules, fn);
  735. },
  736. "Chunk.mapModules: Use Array.from(chunk.modulesIterable, fn) instead"
  737. )
  738. });
  739. // TODO remove in webpack 5
  740. Object.defineProperty(Chunk.prototype, "chunks", {
  741. configurable: false,
  742. get() {
  743. throw new Error("Chunk.chunks: Use ChunkGroup.getChildren() instead");
  744. },
  745. set() {
  746. throw new Error("Chunk.chunks: Use ChunkGroup.add/removeChild() instead");
  747. }
  748. });
  749. // TODO remove in webpack 5
  750. Object.defineProperty(Chunk.prototype, "parents", {
  751. configurable: false,
  752. get() {
  753. throw new Error("Chunk.parents: Use ChunkGroup.getParents() instead");
  754. },
  755. set() {
  756. throw new Error("Chunk.parents: Use ChunkGroup.add/removeParent() instead");
  757. }
  758. });
  759. // TODO remove in webpack 5
  760. Object.defineProperty(Chunk.prototype, "blocks", {
  761. configurable: false,
  762. get() {
  763. throw new Error("Chunk.blocks: Use ChunkGroup.getBlocks() instead");
  764. },
  765. set() {
  766. throw new Error("Chunk.blocks: Use ChunkGroup.add/removeBlock() instead");
  767. }
  768. });
  769. // TODO remove in webpack 5
  770. Object.defineProperty(Chunk.prototype, "entrypoints", {
  771. configurable: false,
  772. get() {
  773. throw new Error(
  774. "Chunk.entrypoints: Use Chunks.groupsIterable and filter by instanceof Entrypoint instead"
  775. );
  776. },
  777. set() {
  778. throw new Error("Chunk.entrypoints: Use Chunks.addGroup instead");
  779. }
  780. });
  781. module.exports = Chunk;