OccurrenceModuleIdsPlugin.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const {
  7. compareModulesByPreOrderIndexOrIdentifier
  8. } = require("../util/comparators");
  9. const {
  10. assignAscendingModuleIds,
  11. getUsedModuleIdsAndModules
  12. } = require("./IdHelpers");
  13. /**
  14. * @import {
  15. * OccurrenceModuleIdsPluginOptions
  16. * } from "../../declarations/plugins/ids/OccurrenceModuleIdsPlugin"
  17. */
  18. /** @import Compiler from "../Compiler" */
  19. /** @import Module from "../Module" */
  20. const PLUGIN_NAME = "OccurrenceModuleIdsPlugin";
  21. class OccurrenceModuleIdsPlugin {
  22. /**
  23. * Creates an instance of OccurrenceModuleIdsPlugin.
  24. * @param {OccurrenceModuleIdsPluginOptions=} options options object
  25. */
  26. constructor(options = {}) {
  27. /** @type {OccurrenceModuleIdsPluginOptions} */
  28. this.options = options;
  29. }
  30. /**
  31. * Applies the plugin by registering its hooks on the compiler.
  32. * @param {Compiler} compiler the compiler instance
  33. * @returns {void}
  34. */
  35. apply(compiler) {
  36. compiler.hooks.validate.tap(PLUGIN_NAME, () => {
  37. compiler.validate(
  38. () =>
  39. require("../../schemas/plugins/ids/OccurrenceModuleIdsPlugin.json"),
  40. this.options,
  41. {
  42. name: "Occurrence Order Module Ids Plugin",
  43. baseDataPath: "options"
  44. },
  45. (options) =>
  46. require("../../schemas/plugins/ids/OccurrenceModuleIdsPlugin.check")(
  47. options
  48. )
  49. );
  50. });
  51. compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
  52. const moduleGraph = compilation.moduleGraph;
  53. compilation.hooks.moduleIds.tap(PLUGIN_NAME, () => {
  54. const chunkGraph = compilation.chunkGraph;
  55. const [usedIds, modulesInOccurrenceOrder] =
  56. getUsedModuleIdsAndModules(compilation);
  57. /** @type {Map<Module, number>} */
  58. const occursInInitialChunksMap = new Map();
  59. /** @type {Map<Module, number>} */
  60. const occursInAllChunksMap = new Map();
  61. /** @type {Map<Module, number>} */
  62. const initialChunkChunkMap = new Map();
  63. /** @type {Map<Module, number>} */
  64. const entryCountMap = new Map();
  65. for (const m of modulesInOccurrenceOrder) {
  66. let initial = 0;
  67. let entry = 0;
  68. for (const c of chunkGraph.getModuleChunksIterable(m)) {
  69. if (c.canBeInitial()) initial++;
  70. if (chunkGraph.isEntryModuleInChunk(m, c)) entry++;
  71. }
  72. initialChunkChunkMap.set(m, initial);
  73. entryCountMap.set(m, entry);
  74. }
  75. /**
  76. * Count occurs in entry.
  77. * @param {Module} module module
  78. * @returns {number} count of occurs
  79. */
  80. const countOccursInEntry = (module) => {
  81. let sum = 0;
  82. for (const [
  83. originModule,
  84. connections
  85. ] of moduleGraph.getIncomingConnectionsByOriginModule(module)) {
  86. if (!originModule) continue;
  87. if (!connections.some((c) => c.isTargetActive(undefined))) continue;
  88. sum += initialChunkChunkMap.get(originModule) || 0;
  89. }
  90. return sum;
  91. };
  92. /**
  93. * Returns count of occurs.
  94. * @param {Module} module module
  95. * @returns {number} count of occurs
  96. */
  97. const countOccurs = (module) => {
  98. let sum = 0;
  99. for (const [
  100. originModule,
  101. connections
  102. ] of moduleGraph.getIncomingConnectionsByOriginModule(module)) {
  103. if (!originModule) continue;
  104. const chunkModules =
  105. chunkGraph.getNumberOfModuleChunks(originModule);
  106. for (const c of connections) {
  107. if (!c.isTargetActive(undefined)) continue;
  108. if (!c.dependency) continue;
  109. const factor = c.dependency.getNumberOfIdOccurrences();
  110. if (factor === 0) continue;
  111. sum += factor * chunkModules;
  112. }
  113. }
  114. return sum;
  115. };
  116. if (this.options.prioritiseInitial) {
  117. for (const m of modulesInOccurrenceOrder) {
  118. const result =
  119. countOccursInEntry(m) +
  120. /** @type {number} */ (initialChunkChunkMap.get(m)) +
  121. /** @type {number} */ (entryCountMap.get(m));
  122. occursInInitialChunksMap.set(m, result);
  123. }
  124. }
  125. for (const m of modulesInOccurrenceOrder) {
  126. const result =
  127. countOccurs(m) +
  128. chunkGraph.getNumberOfModuleChunks(m) +
  129. /** @type {number} */ (entryCountMap.get(m));
  130. occursInAllChunksMap.set(m, result);
  131. }
  132. const naturalCompare = compareModulesByPreOrderIndexOrIdentifier(
  133. compilation.moduleGraph
  134. );
  135. modulesInOccurrenceOrder.sort((a, b) => {
  136. if (this.options.prioritiseInitial) {
  137. const aEntryOccurs =
  138. /** @type {number} */
  139. (occursInInitialChunksMap.get(a));
  140. const bEntryOccurs =
  141. /** @type {number} */
  142. (occursInInitialChunksMap.get(b));
  143. if (aEntryOccurs > bEntryOccurs) return -1;
  144. if (aEntryOccurs < bEntryOccurs) return 1;
  145. }
  146. const aOccurs = /** @type {number} */ (occursInAllChunksMap.get(a));
  147. const bOccurs = /** @type {number} */ (occursInAllChunksMap.get(b));
  148. if (aOccurs > bOccurs) return -1;
  149. if (aOccurs < bOccurs) return 1;
  150. return naturalCompare(a, b);
  151. });
  152. assignAscendingModuleIds(
  153. usedIds,
  154. modulesInOccurrenceOrder,
  155. compilation
  156. );
  157. });
  158. });
  159. }
  160. }
  161. module.exports = OccurrenceModuleIdsPlugin;