MinChunkSizePlugin.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { STAGE_ADVANCED } = require("../OptimizationStages");
  7. /** @typedef {import("../../declarations/plugins/optimize/MinChunkSizePlugin").MinChunkSizePluginOptions} MinChunkSizePluginOptions */
  8. /** @typedef {import("../Chunk")} Chunk */
  9. /** @typedef {import("../Compiler")} Compiler */
  10. const PLUGIN_NAME = "MinChunkSizePlugin";
  11. class MinChunkSizePlugin {
  12. /**
  13. * Creates an instance of MinChunkSizePlugin.
  14. * @param {MinChunkSizePluginOptions} options options object
  15. */
  16. constructor(options) {
  17. /** @type {MinChunkSizePluginOptions} */
  18. this.options = options;
  19. }
  20. /**
  21. * Applies the plugin by registering its hooks on the compiler.
  22. * @param {Compiler} compiler the compiler instance
  23. * @returns {void}
  24. */
  25. apply(compiler) {
  26. compiler.hooks.validate.tap(PLUGIN_NAME, () => {
  27. compiler.validate(
  28. () => require("../../schemas/plugins/optimize/MinChunkSizePlugin.json"),
  29. this.options,
  30. {
  31. name: "Min Chunk Size Plugin",
  32. baseDataPath: "options"
  33. },
  34. (options) =>
  35. require("../../schemas/plugins/optimize/MinChunkSizePlugin.check")(
  36. options
  37. )
  38. );
  39. });
  40. compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
  41. compilation.hooks.optimizeChunks.tap(
  42. {
  43. name: PLUGIN_NAME,
  44. stage: STAGE_ADVANCED
  45. },
  46. (chunks) => {
  47. const chunkGraph = compilation.chunkGraph;
  48. const equalOptions = {
  49. chunkOverhead: 1,
  50. entryChunkMultiplicator: 1
  51. };
  52. /** @type {Map<Chunk, number>} */
  53. const chunkSizesMap = new Map();
  54. /** @type {[Chunk, Chunk][]} */
  55. const combinations = [];
  56. /** @type {Chunk[]} */
  57. const smallChunks = [];
  58. /** @type {Chunk[]} */
  59. const visitedChunks = [];
  60. for (const a of chunks) {
  61. // check if one of the chunks sizes is smaller than the minChunkSize
  62. // and filter pairs that can NOT be integrated!
  63. if (
  64. chunkGraph.getChunkSize(a, equalOptions) <
  65. this.options.minChunkSize
  66. ) {
  67. smallChunks.push(a);
  68. for (const b of visitedChunks) {
  69. if (chunkGraph.canChunksBeIntegrated(b, a)) {
  70. combinations.push([b, a]);
  71. }
  72. }
  73. } else {
  74. for (const b of smallChunks) {
  75. if (chunkGraph.canChunksBeIntegrated(b, a)) {
  76. combinations.push([b, a]);
  77. }
  78. }
  79. }
  80. chunkSizesMap.set(a, chunkGraph.getChunkSize(a, this.options));
  81. visitedChunks.push(a);
  82. }
  83. const sortedSizeFilteredExtendedPairCombinations = combinations
  84. .map((pair) => {
  85. // extend combination pairs with size and integrated size
  86. const a = /** @type {number} */ (chunkSizesMap.get(pair[0]));
  87. const b = /** @type {number} */ (chunkSizesMap.get(pair[1]));
  88. const ab = chunkGraph.getIntegratedChunksSize(
  89. pair[0],
  90. pair[1],
  91. this.options
  92. );
  93. /** @type {[number, number, Chunk, Chunk]} */
  94. const extendedPair = [a + b - ab, ab, pair[0], pair[1]];
  95. return extendedPair;
  96. })
  97. .sort((a, b) => {
  98. // sadly javascript does an in place sort here
  99. // sort by size
  100. const diff = b[0] - a[0];
  101. if (diff !== 0) return diff;
  102. return a[1] - b[1];
  103. });
  104. if (sortedSizeFilteredExtendedPairCombinations.length === 0) return;
  105. const pair = sortedSizeFilteredExtendedPairCombinations[0];
  106. chunkGraph.integrateChunks(pair[2], pair[3]);
  107. compilation.chunks.delete(pair[3]);
  108. return true;
  109. }
  110. );
  111. });
  112. }
  113. }
  114. module.exports = MinChunkSizePlugin;