OccurrenceModuleIdsPlugin.js 5.0 KB

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