OccurrenceModuleIdsPlugin.js 4.8 KB

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