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.

112 lines
3.3 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 validateOptions = require("schema-utils");
  7. const schema = require("../../schemas/plugins/optimize/OccurrenceOrderModuleIdsPlugin.json");
  8. /** @typedef {import("../../declarations/plugins/optimize/OccurrenceOrderModuleIdsPlugin").OccurrenceOrderModuleIdsPluginOptions} OccurrenceOrderModuleIdsPluginOptions */
  9. class OccurrenceOrderModuleIdsPlugin {
  10. /**
  11. * @param {OccurrenceOrderModuleIdsPluginOptions=} options options object
  12. */
  13. constructor(options = {}) {
  14. validateOptions(schema, options, "Occurrence Order Module Ids Plugin");
  15. this.options = options;
  16. }
  17. apply(compiler) {
  18. const prioritiseInitial = this.options.prioritiseInitial;
  19. compiler.hooks.compilation.tap(
  20. "OccurrenceOrderModuleIdsPlugin",
  21. compilation => {
  22. compilation.hooks.optimizeModuleOrder.tap(
  23. "OccurrenceOrderModuleIdsPlugin",
  24. modules => {
  25. const occursInInitialChunksMap = new Map();
  26. const occursInAllChunksMap = new Map();
  27. const initialChunkChunkMap = new Map();
  28. const entryCountMap = new Map();
  29. for (const m of modules) {
  30. let initial = 0;
  31. let entry = 0;
  32. for (const c of m.chunksIterable) {
  33. if (c.canBeInitial()) initial++;
  34. if (c.entryModule === m) entry++;
  35. }
  36. initialChunkChunkMap.set(m, initial);
  37. entryCountMap.set(m, entry);
  38. }
  39. const countOccursInEntry = (sum, r) => {
  40. if (!r.module) {
  41. return sum;
  42. }
  43. const count = initialChunkChunkMap.get(r.module);
  44. if (!count) {
  45. return sum;
  46. }
  47. return sum + count;
  48. };
  49. const countOccurs = (sum, r) => {
  50. if (!r.module) {
  51. return sum;
  52. }
  53. let factor = 1;
  54. if (typeof r.dependency.getNumberOfIdOccurrences === "function") {
  55. factor = r.dependency.getNumberOfIdOccurrences();
  56. }
  57. if (factor === 0) {
  58. return sum;
  59. }
  60. return sum + factor * r.module.getNumberOfChunks();
  61. };
  62. if (prioritiseInitial) {
  63. for (const m of modules) {
  64. const result =
  65. m.reasons.reduce(countOccursInEntry, 0) +
  66. initialChunkChunkMap.get(m) +
  67. entryCountMap.get(m);
  68. occursInInitialChunksMap.set(m, result);
  69. }
  70. }
  71. const originalOrder = new Map();
  72. let i = 0;
  73. for (const m of modules) {
  74. const result =
  75. m.reasons.reduce(countOccurs, 0) +
  76. m.getNumberOfChunks() +
  77. entryCountMap.get(m);
  78. occursInAllChunksMap.set(m, result);
  79. originalOrder.set(m, i++);
  80. }
  81. modules.sort((a, b) => {
  82. if (prioritiseInitial) {
  83. const aEntryOccurs = occursInInitialChunksMap.get(a);
  84. const bEntryOccurs = occursInInitialChunksMap.get(b);
  85. if (aEntryOccurs > bEntryOccurs) return -1;
  86. if (aEntryOccurs < bEntryOccurs) return 1;
  87. }
  88. const aOccurs = occursInAllChunksMap.get(a);
  89. const bOccurs = occursInAllChunksMap.get(b);
  90. if (aOccurs > bOccurs) return -1;
  91. if (aOccurs < bOccurs) return 1;
  92. const orgA = originalOrder.get(a);
  93. const orgB = originalOrder.get(b);
  94. return orgA - orgB;
  95. });
  96. }
  97. );
  98. }
  99. );
  100. }
  101. }
  102. module.exports = OccurrenceOrderModuleIdsPlugin;