AggressiveMergingPlugin.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  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("../Chunk")} Chunk */
  8. /** @typedef {import("../Compiler")} Compiler */
  9. /**
  10. * @typedef {object} AggressiveMergingPluginOptions
  11. * @property {number=} minSizeReduce minimal size reduction to trigger merging
  12. */
  13. class AggressiveMergingPlugin {
  14. /**
  15. * @param {AggressiveMergingPluginOptions=} [options] options object
  16. */
  17. constructor(options) {
  18. if (
  19. (options !== undefined && typeof options !== "object") ||
  20. Array.isArray(options)
  21. ) {
  22. throw new Error(
  23. "Argument should be an options object. To use defaults, pass in nothing.\nFor more info on options, see https://webpack.js.org/plugins/"
  24. );
  25. }
  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 minSizeReduce = options.minSizeReduce || 1.5;
  36. compiler.hooks.thisCompilation.tap(
  37. "AggressiveMergingPlugin",
  38. compilation => {
  39. compilation.hooks.optimizeChunks.tap(
  40. {
  41. name: "AggressiveMergingPlugin",
  42. stage: STAGE_ADVANCED
  43. },
  44. chunks => {
  45. const chunkGraph = compilation.chunkGraph;
  46. /** @type {{a: Chunk, b: Chunk, improvement: number}[]} */
  47. let combinations = [];
  48. for (const a of chunks) {
  49. if (a.canBeInitial()) continue;
  50. for (const b of chunks) {
  51. if (b.canBeInitial()) continue;
  52. if (b === a) break;
  53. if (!chunkGraph.canChunksBeIntegrated(a, b)) {
  54. continue;
  55. }
  56. const aSize = chunkGraph.getChunkSize(b, {
  57. chunkOverhead: 0
  58. });
  59. const bSize = chunkGraph.getChunkSize(a, {
  60. chunkOverhead: 0
  61. });
  62. const abSize = chunkGraph.getIntegratedChunksSize(b, a, {
  63. chunkOverhead: 0
  64. });
  65. const improvement = (aSize + bSize) / abSize;
  66. combinations.push({
  67. a,
  68. b,
  69. improvement
  70. });
  71. }
  72. }
  73. combinations.sort((a, b) => {
  74. return b.improvement - a.improvement;
  75. });
  76. const pair = combinations[0];
  77. if (!pair) return;
  78. if (pair.improvement < minSizeReduce) return;
  79. chunkGraph.integrateChunks(pair.b, pair.a);
  80. compilation.chunks.delete(pair.a);
  81. return true;
  82. }
  83. );
  84. }
  85. );
  86. }
  87. }
  88. module.exports = AggressiveMergingPlugin;