AggressiveMergingPlugin.js 2.6 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. * Defines the aggressive merging plugin options type used by this module.
  11. * @typedef {object} AggressiveMergingPluginOptions
  12. * @property {number=} minSizeReduce minimal size reduction to trigger merging
  13. */
  14. const PLUGIN_NAME = "AggressiveMergingPlugin";
  15. class AggressiveMergingPlugin {
  16. /**
  17. * Creates an instance of AggressiveMergingPlugin.
  18. * @param {AggressiveMergingPluginOptions=} options options object
  19. */
  20. constructor(options) {
  21. if (
  22. (options !== undefined && typeof options !== "object") ||
  23. Array.isArray(options)
  24. ) {
  25. throw new Error(
  26. "Argument should be an options object. To use defaults, pass in nothing.\nFor more info on options, see https://webpack.js.org/plugins/"
  27. );
  28. }
  29. /** @type {AggressiveMergingPluginOptions} */
  30. this.options = options || {};
  31. }
  32. /**
  33. * Applies the plugin by registering its hooks on the compiler.
  34. * @param {Compiler} compiler the compiler instance
  35. * @returns {void}
  36. */
  37. apply(compiler) {
  38. const options = this.options;
  39. const minSizeReduce = options.minSizeReduce || 1.5;
  40. compiler.hooks.thisCompilation.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. /** @type {{ a: Chunk, b: Chunk, improvement: number }[]} */
  49. const combinations = [];
  50. for (const a of chunks) {
  51. if (a.canBeInitial()) continue;
  52. for (const b of chunks) {
  53. if (b.canBeInitial()) continue;
  54. if (b === a) break;
  55. if (!chunkGraph.canChunksBeIntegrated(a, b)) {
  56. continue;
  57. }
  58. const aSize = chunkGraph.getChunkSize(b, {
  59. chunkOverhead: 0
  60. });
  61. const bSize = chunkGraph.getChunkSize(a, {
  62. chunkOverhead: 0
  63. });
  64. const abSize = chunkGraph.getIntegratedChunksSize(b, a, {
  65. chunkOverhead: 0
  66. });
  67. const improvement = (aSize + bSize) / abSize;
  68. combinations.push({
  69. a,
  70. b,
  71. improvement
  72. });
  73. }
  74. }
  75. combinations.sort((a, b) => b.improvement - a.improvement);
  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. module.exports = AggressiveMergingPlugin;