MinChunkSizePlugin.js 3.3 KB

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