Chunk.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859
  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. remove() {
  324. // cleanup modules
  325. // Array.from is used here to create a clone, because removeChunk modifies this._modules
  326. for (const module of Array.from(this._modules)) {
  327. module.removeChunk(this);
  328. }
  329. for (const chunkGroup of this._groups) {
  330. chunkGroup.removeChunk(this);
  331. }
  332. }
  333. /**
  334. *
  335. * @param {Module} module module to move
  336. * @param {Chunk} otherChunk other chunk to move it to
  337. * @returns {void}
  338. */
  339. moveModule(module, otherChunk) {
  340. GraphHelpers.disconnectChunkAndModule(this, module);
  341. GraphHelpers.connectChunkAndModule(otherChunk, module);
  342. module.rewriteChunkInReasons(this, [otherChunk]);
  343. }
  344. /**
  345. *
  346. * @param {Chunk} otherChunk the chunk to integrate with
  347. * @param {ModuleReason} reason reason why the module is being integrated
  348. * @returns {boolean} returns true or false if integration succeeds or fails
  349. */
  350. integrate(otherChunk, reason) {
  351. if (!this.canBeIntegrated(otherChunk)) {
  352. return false;
  353. }
  354. // Pick a new name for the integrated chunk
  355. if (this.name && otherChunk.name) {
  356. if (this.hasEntryModule() === otherChunk.hasEntryModule()) {
  357. // When both chunks have entry modules or none have one, use
  358. // shortest name
  359. if (this.name.length !== otherChunk.name.length) {
  360. this.name =
  361. this.name.length < otherChunk.name.length
  362. ? this.name
  363. : otherChunk.name;
  364. } else {
  365. this.name = this.name < otherChunk.name ? this.name : otherChunk.name;
  366. }
  367. } else if (otherChunk.hasEntryModule()) {
  368. // Pick the name of the chunk with the entry module
  369. this.name = otherChunk.name;
  370. }
  371. } else if (otherChunk.name) {
  372. this.name = otherChunk.name;
  373. }
  374. // Array.from is used here to create a clone, because moveModule modifies otherChunk._modules
  375. for (const module of Array.from(otherChunk._modules)) {
  376. otherChunk.moveModule(module, this);
  377. }
  378. otherChunk._modules.clear();
  379. if (otherChunk.entryModule) {
  380. this.entryModule = otherChunk.entryModule;
  381. }
  382. for (const chunkGroup of otherChunk._groups) {
  383. chunkGroup.replaceChunk(otherChunk, this);
  384. this.addGroup(chunkGroup);
  385. }
  386. otherChunk._groups.clear();
  387. return true;
  388. }
  389. /**
  390. * @param {Chunk} newChunk the new chunk that will be split out of the current chunk
  391. * @returns {void}
  392. */
  393. split(newChunk) {
  394. for (const chunkGroup of this._groups) {
  395. chunkGroup.insertChunk(newChunk, this);
  396. newChunk.addGroup(chunkGroup);
  397. }
  398. }
  399. isEmpty() {
  400. return this._modules.size === 0;
  401. }
  402. updateHash(hash) {
  403. hash.update(`${this.id} `);
  404. hash.update(this.ids ? this.ids.join(",") : "");
  405. hash.update(`${this.name || ""} `);
  406. for (const m of this._modules) {
  407. hash.update(m.hash);
  408. }
  409. }
  410. canBeIntegrated(otherChunk) {
  411. if (this.preventIntegration || otherChunk.preventIntegration) {
  412. return false;
  413. }
  414. const isAvailable = (a, b) => {
  415. const queue = new Set(b.groupsIterable);
  416. for (const chunkGroup of queue) {
  417. if (a.isInGroup(chunkGroup)) continue;
  418. if (chunkGroup.isInitial()) return false;
  419. for (const parent of chunkGroup.parentsIterable) {
  420. queue.add(parent);
  421. }
  422. }
  423. return true;
  424. };
  425. const selfHasRuntime = this.hasRuntime();
  426. const otherChunkHasRuntime = otherChunk.hasRuntime();
  427. if (selfHasRuntime !== otherChunkHasRuntime) {
  428. if (selfHasRuntime) {
  429. return isAvailable(this, otherChunk);
  430. } else if (otherChunkHasRuntime) {
  431. return isAvailable(otherChunk, this);
  432. } else {
  433. return false;
  434. }
  435. }
  436. if (this.hasEntryModule() || otherChunk.hasEntryModule()) {
  437. return false;
  438. }
  439. return true;
  440. }
  441. /**
  442. *
  443. * @param {number} size the size
  444. * @param {Object} options the options passed in
  445. * @returns {number} the multiplier returned
  446. */
  447. addMultiplierAndOverhead(size, options) {
  448. const overhead =
  449. typeof options.chunkOverhead === "number" ? options.chunkOverhead : 10000;
  450. const multiplicator = this.canBeInitial()
  451. ? options.entryChunkMultiplicator || 10
  452. : 1;
  453. return size * multiplicator + overhead;
  454. }
  455. /**
  456. * @returns {number} the size of all modules
  457. */
  458. modulesSize() {
  459. return this._modules.getFromUnorderedCache(getModulesSize);
  460. }
  461. /**
  462. * @param {Object} options the size display options
  463. * @returns {number} the chunk size
  464. */
  465. size(options) {
  466. return this.addMultiplierAndOverhead(this.modulesSize(), options);
  467. }
  468. /**
  469. * @param {Chunk} otherChunk the other chunk
  470. * @param {TODO} options the options for this function
  471. * @returns {number | false} the size, or false if it can't be integrated
  472. */
  473. integratedSize(otherChunk, options) {
  474. // Chunk if it's possible to integrate this chunk
  475. if (!this.canBeIntegrated(otherChunk)) {
  476. return false;
  477. }
  478. let integratedModulesSize = this.modulesSize();
  479. // only count modules that do not exist in this chunk!
  480. for (const otherModule of otherChunk._modules) {
  481. if (!this._modules.has(otherModule)) {
  482. integratedModulesSize += otherModule.size();
  483. }
  484. }
  485. return this.addMultiplierAndOverhead(integratedModulesSize, options);
  486. }
  487. /**
  488. * @param {function(Module, Module): -1|0|1=} sortByFn a predicate function used to sort modules
  489. * @returns {void}
  490. */
  491. sortModules(sortByFn) {
  492. this._modules.sortWith(sortByFn || sortModuleById);
  493. }
  494. sortItems() {
  495. this.sortModules();
  496. }
  497. /**
  498. * @returns {Set<Chunk>} a set of all the async chunks
  499. */
  500. getAllAsyncChunks() {
  501. const queue = new Set();
  502. const chunks = new Set();
  503. const initialChunks = intersect(
  504. Array.from(this.groupsIterable, g => new Set(g.chunks))
  505. );
  506. for (const chunkGroup of this.groupsIterable) {
  507. for (const child of chunkGroup.childrenIterable) {
  508. queue.add(child);
  509. }
  510. }
  511. for (const chunkGroup of queue) {
  512. for (const chunk of chunkGroup.chunks) {
  513. if (!initialChunks.has(chunk)) {
  514. chunks.add(chunk);
  515. }
  516. }
  517. for (const child of chunkGroup.childrenIterable) {
  518. queue.add(child);
  519. }
  520. }
  521. return chunks;
  522. }
  523. /**
  524. * @typedef {Object} ChunkMaps
  525. * @property {Record<string|number, string>} hash
  526. * @property {Record<string|number, Record<string, string>>} contentHash
  527. * @property {Record<string|number, string>} name
  528. */
  529. /**
  530. * @param {boolean} realHash should the full hash or the rendered hash be used
  531. * @returns {ChunkMaps} the chunk map information
  532. */
  533. getChunkMaps(realHash) {
  534. /** @type {Record<string|number, string>} */
  535. const chunkHashMap = Object.create(null);
  536. /** @type {Record<string|number, Record<string, string>>} */
  537. const chunkContentHashMap = Object.create(null);
  538. /** @type {Record<string|number, string>} */
  539. const chunkNameMap = Object.create(null);
  540. for (const chunk of this.getAllAsyncChunks()) {
  541. chunkHashMap[chunk.id] = realHash ? chunk.hash : chunk.renderedHash;
  542. for (const key of Object.keys(chunk.contentHash)) {
  543. if (!chunkContentHashMap[key]) {
  544. chunkContentHashMap[key] = Object.create(null);
  545. }
  546. chunkContentHashMap[key][chunk.id] = chunk.contentHash[key];
  547. }
  548. if (chunk.name) {
  549. chunkNameMap[chunk.id] = chunk.name;
  550. }
  551. }
  552. return {
  553. hash: chunkHashMap,
  554. contentHash: chunkContentHashMap,
  555. name: chunkNameMap
  556. };
  557. }
  558. /**
  559. * @returns {Record<string, Set<TODO>[]>} a record object of names to lists of child ids(?)
  560. */
  561. getChildIdsByOrders() {
  562. const lists = new Map();
  563. for (const group of this.groupsIterable) {
  564. if (group.chunks[group.chunks.length - 1] === this) {
  565. for (const childGroup of group.childrenIterable) {
  566. // TODO webpack 5 remove this check for options
  567. if (typeof childGroup.options === "object") {
  568. for (const key of Object.keys(childGroup.options)) {
  569. if (key.endsWith("Order")) {
  570. const name = key.substr(0, key.length - "Order".length);
  571. let list = lists.get(name);
  572. if (list === undefined) lists.set(name, (list = []));
  573. list.push({
  574. order: childGroup.options[key],
  575. group: childGroup
  576. });
  577. }
  578. }
  579. }
  580. }
  581. }
  582. }
  583. const result = Object.create(null);
  584. for (const [name, list] of lists) {
  585. list.sort((a, b) => {
  586. const cmp = b.order - a.order;
  587. if (cmp !== 0) return cmp;
  588. // TODO webpack 5 remove this check of compareTo
  589. if (a.group.compareTo) {
  590. return a.group.compareTo(b.group);
  591. }
  592. return 0;
  593. });
  594. result[name] = Array.from(
  595. list.reduce((set, item) => {
  596. for (const chunk of item.group.chunks) {
  597. set.add(chunk.id);
  598. }
  599. return set;
  600. }, new Set())
  601. );
  602. }
  603. return result;
  604. }
  605. getChildIdsByOrdersMap(includeDirectChildren) {
  606. const chunkMaps = Object.create(null);
  607. const addChildIdsByOrdersToMap = chunk => {
  608. const data = chunk.getChildIdsByOrders();
  609. for (const key of Object.keys(data)) {
  610. let chunkMap = chunkMaps[key];
  611. if (chunkMap === undefined) {
  612. chunkMaps[key] = chunkMap = Object.create(null);
  613. }
  614. chunkMap[chunk.id] = data[key];
  615. }
  616. };
  617. if (includeDirectChildren) {
  618. addChildIdsByOrdersToMap(this);
  619. }
  620. for (const chunk of this.getAllAsyncChunks()) {
  621. addChildIdsByOrdersToMap(chunk);
  622. }
  623. return chunkMaps;
  624. }
  625. /**
  626. * @typedef {Object} ChunkModuleMaps
  627. * @property {Record<string|number, (string|number)[]>} id
  628. * @property {Record<string|number, string>} hash
  629. */
  630. /**
  631. * @param {ModuleFilterPredicate} filterFn function used to filter modules
  632. * @returns {ChunkModuleMaps} module map information
  633. */
  634. getChunkModuleMaps(filterFn) {
  635. /** @type {Record<string|number, (string|number)[]>} */
  636. const chunkModuleIdMap = Object.create(null);
  637. /** @type {Record<string|number, string>} */
  638. const chunkModuleHashMap = Object.create(null);
  639. for (const chunk of this.getAllAsyncChunks()) {
  640. /** @type {(string|number)[]} */
  641. let array;
  642. for (const module of chunk.modulesIterable) {
  643. if (filterFn(module)) {
  644. if (array === undefined) {
  645. array = [];
  646. chunkModuleIdMap[chunk.id] = array;
  647. }
  648. array.push(module.id);
  649. chunkModuleHashMap[module.id] = module.renderedHash;
  650. }
  651. }
  652. if (array !== undefined) {
  653. array.sort();
  654. }
  655. }
  656. return {
  657. id: chunkModuleIdMap,
  658. hash: chunkModuleHashMap
  659. };
  660. }
  661. /**
  662. *
  663. * @param {function(Module): boolean} filterFn predicate function used to filter modules
  664. * @param {function(Chunk): boolean} filterChunkFn predicate function used to filter chunks
  665. * @returns {boolean} return true if module exists in graph
  666. */
  667. hasModuleInGraph(filterFn, filterChunkFn) {
  668. const queue = new Set(this.groupsIterable);
  669. const chunksProcessed = new Set();
  670. for (const chunkGroup of queue) {
  671. for (const chunk of chunkGroup.chunks) {
  672. if (!chunksProcessed.has(chunk)) {
  673. chunksProcessed.add(chunk);
  674. if (!filterChunkFn || filterChunkFn(chunk)) {
  675. for (const module of chunk.modulesIterable) {
  676. if (filterFn(module)) {
  677. return true;
  678. }
  679. }
  680. }
  681. }
  682. }
  683. for (const child of chunkGroup.childrenIterable) {
  684. queue.add(child);
  685. }
  686. }
  687. return false;
  688. }
  689. toString() {
  690. return `Chunk[${Array.from(this._modules).join()}]`;
  691. }
  692. }
  693. // TODO remove in webpack 5
  694. Object.defineProperty(Chunk.prototype, "forEachModule", {
  695. configurable: false,
  696. value: util.deprecate(
  697. /**
  698. * @deprecated
  699. * @this {Chunk}
  700. * @typedef {function(any, any, Set<any>): void} ForEachModuleCallback
  701. * @param {ForEachModuleCallback} fn Callback function
  702. * @returns {void}
  703. */
  704. function(fn) {
  705. this._modules.forEach(fn);
  706. },
  707. "Chunk.forEachModule: Use for(const module of chunk.modulesIterable) instead"
  708. )
  709. });
  710. // TODO remove in webpack 5
  711. Object.defineProperty(Chunk.prototype, "mapModules", {
  712. configurable: false,
  713. value: util.deprecate(
  714. /**
  715. * @deprecated
  716. * @this {Chunk}
  717. * @typedef {function(any, number): any} MapModulesCallback
  718. * @param {MapModulesCallback} fn Callback function
  719. * @returns {TODO[]} result of mapped modules
  720. */
  721. function(fn) {
  722. return Array.from(this._modules, fn);
  723. },
  724. "Chunk.mapModules: Use Array.from(chunk.modulesIterable, fn) instead"
  725. )
  726. });
  727. // TODO remove in webpack 5
  728. Object.defineProperty(Chunk.prototype, "chunks", {
  729. configurable: false,
  730. get() {
  731. throw new Error("Chunk.chunks: Use ChunkGroup.getChildren() instead");
  732. },
  733. set() {
  734. throw new Error("Chunk.chunks: Use ChunkGroup.add/removeChild() instead");
  735. }
  736. });
  737. // TODO remove in webpack 5
  738. Object.defineProperty(Chunk.prototype, "parents", {
  739. configurable: false,
  740. get() {
  741. throw new Error("Chunk.parents: Use ChunkGroup.getParents() instead");
  742. },
  743. set() {
  744. throw new Error("Chunk.parents: Use ChunkGroup.add/removeParent() instead");
  745. }
  746. });
  747. // TODO remove in webpack 5
  748. Object.defineProperty(Chunk.prototype, "blocks", {
  749. configurable: false,
  750. get() {
  751. throw new Error("Chunk.blocks: Use ChunkGroup.getBlocks() instead");
  752. },
  753. set() {
  754. throw new Error("Chunk.blocks: Use ChunkGroup.add/removeBlock() instead");
  755. }
  756. });
  757. // TODO remove in webpack 5
  758. Object.defineProperty(Chunk.prototype, "entrypoints", {
  759. configurable: false,
  760. get() {
  761. throw new Error(
  762. "Chunk.entrypoints: Use Chunks.groupsIterable and filter by instanceof Entrypoint instead"
  763. );
  764. },
  765. set() {
  766. throw new Error("Chunk.entrypoints: Use Chunks.addGroup instead");
  767. }
  768. });
  769. module.exports = Chunk;