MergeDuplicateChunksPlugin.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { STAGE_BASIC } = require("../OptimizationStages");
  7. const { runtimeEqual } = require("../util/runtime");
  8. /** @typedef {import("../Compiler")} Compiler */
  9. class MergeDuplicateChunksPlugin {
  10. /**
  11. * @param {Compiler} compiler the compiler
  12. * @returns {void}
  13. */
  14. apply(compiler) {
  15. compiler.hooks.compilation.tap(
  16. "MergeDuplicateChunksPlugin",
  17. compilation => {
  18. compilation.hooks.optimizeChunks.tap(
  19. {
  20. name: "MergeDuplicateChunksPlugin",
  21. stage: STAGE_BASIC
  22. },
  23. chunks => {
  24. const { chunkGraph, moduleGraph } = compilation;
  25. // remember already tested chunks for performance
  26. const notDuplicates = new Set();
  27. // for each chunk
  28. for (const chunk of chunks) {
  29. // track a Set of all chunk that could be duplicates
  30. let possibleDuplicates;
  31. for (const module of chunkGraph.getChunkModulesIterable(chunk)) {
  32. if (possibleDuplicates === undefined) {
  33. // when possibleDuplicates is not yet set,
  34. // create a new Set from chunks of the current module
  35. // including only chunks with the same number of modules
  36. for (const dup of chunkGraph.getModuleChunksIterable(
  37. module
  38. )) {
  39. if (
  40. dup !== chunk &&
  41. chunkGraph.getNumberOfChunkModules(chunk) ===
  42. chunkGraph.getNumberOfChunkModules(dup) &&
  43. !notDuplicates.has(dup)
  44. ) {
  45. // delay allocating the new Set until here, reduce memory pressure
  46. if (possibleDuplicates === undefined) {
  47. possibleDuplicates = new Set();
  48. }
  49. possibleDuplicates.add(dup);
  50. }
  51. }
  52. // when no chunk is possible we can break here
  53. if (possibleDuplicates === undefined) break;
  54. } else {
  55. // validate existing possible duplicates
  56. for (const dup of possibleDuplicates) {
  57. // remove possible duplicate when module is not contained
  58. if (!chunkGraph.isModuleInChunk(module, dup)) {
  59. possibleDuplicates.delete(dup);
  60. }
  61. }
  62. // when all chunks has been removed we can break here
  63. if (possibleDuplicates.size === 0) break;
  64. }
  65. }
  66. // when we found duplicates
  67. if (
  68. possibleDuplicates !== undefined &&
  69. possibleDuplicates.size > 0
  70. ) {
  71. outer: for (const otherChunk of possibleDuplicates) {
  72. if (otherChunk.hasRuntime() !== chunk.hasRuntime()) continue;
  73. if (chunkGraph.getNumberOfEntryModules(chunk) > 0) continue;
  74. if (chunkGraph.getNumberOfEntryModules(otherChunk) > 0)
  75. continue;
  76. if (!runtimeEqual(chunk.runtime, otherChunk.runtime)) {
  77. for (const module of chunkGraph.getChunkModulesIterable(
  78. chunk
  79. )) {
  80. const exportsInfo = moduleGraph.getExportsInfo(module);
  81. if (
  82. !exportsInfo.isEquallyUsed(
  83. chunk.runtime,
  84. otherChunk.runtime
  85. )
  86. ) {
  87. continue outer;
  88. }
  89. }
  90. }
  91. // merge them
  92. if (chunkGraph.canChunksBeIntegrated(chunk, otherChunk)) {
  93. chunkGraph.integrateChunks(chunk, otherChunk);
  94. compilation.chunks.delete(otherChunk);
  95. }
  96. }
  97. }
  98. // don't check already processed chunks twice
  99. notDuplicates.add(chunk);
  100. }
  101. }
  102. );
  103. }
  104. );
  105. }
  106. }
  107. module.exports = MergeDuplicateChunksPlugin;