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.

875 lines
23 KiB

4 years ago
  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 {string} 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. /**
  419. * @param {Chunk} a chunk
  420. * @param {Chunk} b chunk
  421. * @returns {boolean} true, if a is always available when b is reached
  422. */
  423. const isAvailable = (a, b) => {
  424. const queue = new Set(b.groupsIterable);
  425. for (const chunkGroup of queue) {
  426. if (a.isInGroup(chunkGroup)) continue;
  427. if (chunkGroup.isInitial()) return false;
  428. for (const parent of chunkGroup.parentsIterable) {
  429. queue.add(parent);
  430. }
  431. }
  432. return true;
  433. };
  434. const selfHasRuntime = this.hasRuntime();
  435. const otherChunkHasRuntime = otherChunk.hasRuntime();
  436. if (selfHasRuntime !== otherChunkHasRuntime) {
  437. if (selfHasRuntime) {
  438. return isAvailable(this, otherChunk);
  439. } else if (otherChunkHasRuntime) {
  440. return isAvailable(otherChunk, this);
  441. } else {
  442. return false;
  443. }
  444. }
  445. if (this.hasEntryModule() || otherChunk.hasEntryModule()) {
  446. return false;
  447. }
  448. return true;
  449. }
  450. /**
  451. *
  452. * @param {number} size the size
  453. * @param {Object} options the options passed in
  454. * @returns {number} the multiplier returned
  455. */
  456. addMultiplierAndOverhead(size, options) {
  457. const overhead =
  458. typeof options.chunkOverhead === "number" ? options.chunkOverhead : 10000;
  459. const multiplicator = this.canBeInitial()
  460. ? options.entryChunkMultiplicator || 10
  461. : 1;
  462. return size * multiplicator + overhead;
  463. }
  464. /**
  465. * @returns {number} the size of all modules
  466. */
  467. modulesSize() {
  468. return this._modules.getFromUnorderedCache(getModulesSize);
  469. }
  470. /**
  471. * @param {Object} options the size display options
  472. * @returns {number} the chunk size
  473. */
  474. size(options = {}) {
  475. return this.addMultiplierAndOverhead(this.modulesSize(), options);
  476. }
  477. /**
  478. * @param {Chunk} otherChunk the other chunk
  479. * @param {TODO} options the options for this function
  480. * @returns {number | false} the size, or false if it can't be integrated
  481. */
  482. integratedSize(otherChunk, options) {
  483. // Chunk if it's possible to integrate this chunk
  484. if (!this.canBeIntegrated(otherChunk)) {
  485. return false;
  486. }
  487. let integratedModulesSize = this.modulesSize();
  488. // only count modules that do not exist in this chunk!
  489. for (const otherModule of otherChunk._modules) {
  490. if (!this._modules.has(otherModule)) {
  491. integratedModulesSize += otherModule.size();
  492. }
  493. }
  494. return this.addMultiplierAndOverhead(integratedModulesSize, options);
  495. }
  496. /**
  497. * @param {function(Module, Module): -1|0|1=} sortByFn a predicate function used to sort modules
  498. * @returns {void}
  499. */
  500. sortModules(sortByFn) {
  501. this._modules.sortWith(sortByFn || sortModuleById);
  502. }
  503. sortItems() {
  504. this.sortModules();
  505. }
  506. /**
  507. * @returns {Set<Chunk>} a set of all the async chunks
  508. */
  509. getAllAsyncChunks() {
  510. const queue = new Set();
  511. const chunks = new Set();
  512. const initialChunks = intersect(
  513. Array.from(this.groupsIterable, g => new Set(g.chunks))
  514. );
  515. for (const chunkGroup of this.groupsIterable) {
  516. for (const child of chunkGroup.childrenIterable) {
  517. queue.add(child);
  518. }
  519. }
  520. for (const chunkGroup of queue) {
  521. for (const chunk of chunkGroup.chunks) {
  522. if (!initialChunks.has(chunk)) {
  523. chunks.add(chunk);
  524. }
  525. }
  526. for (const child of chunkGroup.childrenIterable) {
  527. queue.add(child);
  528. }
  529. }
  530. return chunks;
  531. }
  532. /**
  533. * @typedef {Object} ChunkMaps
  534. * @property {Record<string|number, string>} hash
  535. * @property {Record<string|number, Record<string, string>>} contentHash
  536. * @property {Record<string|number, string>} name
  537. */
  538. /**
  539. * @param {boolean} realHash should the full hash or the rendered hash be used
  540. * @returns {ChunkMaps} the chunk map information
  541. */
  542. getChunkMaps(realHash) {
  543. /** @type {Record<string|number, string>} */
  544. const chunkHashMap = Object.create(null);
  545. /** @type {Record<string|number, Record<string, string>>} */
  546. const chunkContentHashMap = Object.create(null);
  547. /** @type {Record<string|number, string>} */
  548. const chunkNameMap = Object.create(null);
  549. for (const chunk of this.getAllAsyncChunks()) {
  550. chunkHashMap[chunk.id] = realHash ? chunk.hash : chunk.renderedHash;
  551. for (const key of Object.keys(chunk.contentHash)) {
  552. if (!chunkContentHashMap[key]) {
  553. chunkContentHashMap[key] = Object.create(null);
  554. }
  555. chunkContentHashMap[key][chunk.id] = chunk.contentHash[key];
  556. }
  557. if (chunk.name) {
  558. chunkNameMap[chunk.id] = chunk.name;
  559. }
  560. }
  561. return {
  562. hash: chunkHashMap,
  563. contentHash: chunkContentHashMap,
  564. name: chunkNameMap
  565. };
  566. }
  567. /**
  568. * @returns {Record<string, Set<TODO>[]>} a record object of names to lists of child ids(?)
  569. */
  570. getChildIdsByOrders() {
  571. const lists = new Map();
  572. for (const group of this.groupsIterable) {
  573. if (group.chunks[group.chunks.length - 1] === this) {
  574. for (const childGroup of group.childrenIterable) {
  575. // TODO webpack 5 remove this check for options
  576. if (typeof childGroup.options === "object") {
  577. for (const key of Object.keys(childGroup.options)) {
  578. if (key.endsWith("Order")) {
  579. const name = key.substr(0, key.length - "Order".length);
  580. let list = lists.get(name);
  581. if (list === undefined) lists.set(name, (list = []));
  582. list.push({
  583. order: childGroup.options[key],
  584. group: childGroup
  585. });
  586. }
  587. }
  588. }
  589. }
  590. }
  591. }
  592. const result = Object.create(null);
  593. for (const [name, list] of lists) {
  594. list.sort((a, b) => {
  595. const cmp = b.order - a.order;
  596. if (cmp !== 0) return cmp;
  597. // TODO webpack 5 remove this check of compareTo
  598. if (a.group.compareTo) {
  599. return a.group.compareTo(b.group);
  600. }
  601. return 0;
  602. });
  603. result[name] = Array.from(
  604. list.reduce((set, item) => {
  605. for (const chunk of item.group.chunks) {
  606. set.add(chunk.id);
  607. }
  608. return set;
  609. }, new Set())
  610. );
  611. }
  612. return result;
  613. }
  614. getChildIdsByOrdersMap(includeDirectChildren) {
  615. const chunkMaps = Object.create(null);
  616. const addChildIdsByOrdersToMap = chunk => {
  617. const data = chunk.getChildIdsByOrders();
  618. for (const key of Object.keys(data)) {
  619. let chunkMap = chunkMaps[key];
  620. if (chunkMap === undefined) {
  621. chunkMaps[key] = chunkMap = Object.create(null);
  622. }
  623. chunkMap[chunk.id] = data[key];
  624. }
  625. };
  626. if (includeDirectChildren) {
  627. const chunks = new Set();
  628. for (const chunkGroup of this.groupsIterable) {
  629. for (const chunk of chunkGroup.chunks) {
  630. chunks.add(chunk);
  631. }
  632. }
  633. for (const chunk of chunks) {
  634. addChildIdsByOrdersToMap(chunk);
  635. }
  636. }
  637. for (const chunk of this.getAllAsyncChunks()) {
  638. addChildIdsByOrdersToMap(chunk);
  639. }
  640. return chunkMaps;
  641. }
  642. /**
  643. * @typedef {Object} ChunkModuleMaps
  644. * @property {Record<string|number, (string|number)[]>} id
  645. * @property {Record<string|number, string>} hash
  646. */
  647. /**
  648. * @param {ModuleFilterPredicate} filterFn function used to filter modules
  649. * @returns {ChunkModuleMaps} module map information
  650. */
  651. getChunkModuleMaps(filterFn) {
  652. /** @type {Record<string|number, (string|number)[]>} */
  653. const chunkModuleIdMap = Object.create(null);
  654. /** @type {Record<string|number, string>} */
  655. const chunkModuleHashMap = Object.create(null);
  656. for (const chunk of this.getAllAsyncChunks()) {
  657. /** @type {(string|number)[]} */
  658. let array;
  659. for (const module of chunk.modulesIterable) {
  660. if (filterFn(module)) {
  661. if (array === undefined) {
  662. array = [];
  663. chunkModuleIdMap[chunk.id] = array;
  664. }
  665. array.push(module.id);
  666. chunkModuleHashMap[module.id] = module.renderedHash;
  667. }
  668. }
  669. if (array !== undefined) {
  670. array.sort();
  671. }
  672. }
  673. return {
  674. id: chunkModuleIdMap,
  675. hash: chunkModuleHashMap
  676. };
  677. }
  678. /**
  679. *
  680. * @param {function(Module): boolean} filterFn predicate function used to filter modules
  681. * @param {function(Chunk): boolean} filterChunkFn predicate function used to filter chunks
  682. * @returns {boolean} return true if module exists in graph
  683. */
  684. hasModuleInGraph(filterFn, filterChunkFn) {
  685. const queue = new Set(this.groupsIterable);
  686. const chunksProcessed = new Set();
  687. for (const chunkGroup of queue) {
  688. for (const chunk of chunkGroup.chunks) {
  689. if (!chunksProcessed.has(chunk)) {
  690. chunksProcessed.add(chunk);
  691. if (!filterChunkFn || filterChunkFn(chunk)) {
  692. for (const module of chunk.modulesIterable) {
  693. if (filterFn(module)) {
  694. return true;
  695. }
  696. }
  697. }
  698. }
  699. }
  700. for (const child of chunkGroup.childrenIterable) {
  701. queue.add(child);
  702. }
  703. }
  704. return false;
  705. }
  706. toString() {
  707. return `Chunk[${Array.from(this._modules).join()}]`;
  708. }
  709. }
  710. // TODO remove in webpack 5
  711. Object.defineProperty(Chunk.prototype, "forEachModule", {
  712. configurable: false,
  713. value: util.deprecate(
  714. /**
  715. * @deprecated
  716. * @this {Chunk}
  717. * @typedef {function(any, any, Set<any>): void} ForEachModuleCallback
  718. * @param {ForEachModuleCallback} fn Callback function
  719. * @returns {void}
  720. */
  721. function(fn) {
  722. this._modules.forEach(fn);
  723. },
  724. "Chunk.forEachModule: Use for(const module of chunk.modulesIterable) instead"
  725. )
  726. });
  727. // TODO remove in webpack 5
  728. Object.defineProperty(Chunk.prototype, "mapModules", {
  729. configurable: false,
  730. value: util.deprecate(
  731. /**
  732. * @deprecated
  733. * @this {Chunk}
  734. * @typedef {function(any, number): any} MapModulesCallback
  735. * @param {MapModulesCallback} fn Callback function
  736. * @returns {TODO[]} result of mapped modules
  737. */
  738. function(fn) {
  739. return Array.from(this._modules, fn);
  740. },
  741. "Chunk.mapModules: Use Array.from(chunk.modulesIterable, fn) instead"
  742. )
  743. });
  744. // TODO remove in webpack 5
  745. Object.defineProperty(Chunk.prototype, "chunks", {
  746. configurable: false,
  747. get() {
  748. throw new Error("Chunk.chunks: Use ChunkGroup.getChildren() instead");
  749. },
  750. set() {
  751. throw new Error("Chunk.chunks: Use ChunkGroup.add/removeChild() instead");
  752. }
  753. });
  754. // TODO remove in webpack 5
  755. Object.defineProperty(Chunk.prototype, "parents", {
  756. configurable: false,
  757. get() {
  758. throw new Error("Chunk.parents: Use ChunkGroup.getParents() instead");
  759. },
  760. set() {
  761. throw new Error("Chunk.parents: Use ChunkGroup.add/removeParent() instead");
  762. }
  763. });
  764. // TODO remove in webpack 5
  765. Object.defineProperty(Chunk.prototype, "blocks", {
  766. configurable: false,
  767. get() {
  768. throw new Error("Chunk.blocks: Use ChunkGroup.getBlocks() instead");
  769. },
  770. set() {
  771. throw new Error("Chunk.blocks: Use ChunkGroup.add/removeBlock() instead");
  772. }
  773. });
  774. // TODO remove in webpack 5
  775. Object.defineProperty(Chunk.prototype, "entrypoints", {
  776. configurable: false,
  777. get() {
  778. throw new Error(
  779. "Chunk.entrypoints: Use Chunks.groupsIterable and filter by instanceof Entrypoint instead"
  780. );
  781. },
  782. set() {
  783. throw new Error("Chunk.entrypoints: Use Chunks.addGroup instead");
  784. }
  785. });
  786. module.exports = Chunk;