MergeDuplicateChunksPlugin.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  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 createSchemaValidation = require("../util/create-schema-validation");
  8. const { runtimeEqual } = require("../util/runtime");
  9. /** @typedef {import("../../declarations/plugins/optimize/MergeDuplicateChunksPlugin").MergeDuplicateChunksPluginOptions} MergeDuplicateChunksPluginOptions */
  10. /** @typedef {import("../Compiler")} Compiler */
  11. const validate = createSchemaValidation(
  12. require("../../schemas/plugins/optimize/MergeDuplicateChunksPlugin.check"),
  13. () =>
  14. require("../../schemas/plugins/optimize/MergeDuplicateChunksPlugin.json"),
  15. {
  16. name: "Merge Duplicate Chunks Plugin",
  17. baseDataPath: "options"
  18. }
  19. );
  20. const PLUGIN_NAME = "MergeDuplicateChunksPlugin";
  21. class MergeDuplicateChunksPlugin {
  22. /**
  23. * @param {MergeDuplicateChunksPluginOptions} options options object
  24. */
  25. constructor(options = { stage: STAGE_BASIC }) {
  26. validate(options);
  27. this.options = options;
  28. }
  29. /**
  30. * @param {Compiler} compiler the compiler
  31. * @returns {void}
  32. */
  33. apply(compiler) {
  34. compiler.hooks.compilation.tap(PLUGIN_NAME, compilation => {
  35. compilation.hooks.optimizeChunks.tap(
  36. {
  37. name: PLUGIN_NAME,
  38. stage: this.options.stage
  39. },
  40. chunks => {
  41. const { chunkGraph, moduleGraph } = compilation;
  42. // remember already tested chunks for performance
  43. const notDuplicates = new Set();
  44. // for each chunk
  45. for (const chunk of chunks) {
  46. // track a Set of all chunk that could be duplicates
  47. let possibleDuplicates;
  48. for (const module of chunkGraph.getChunkModulesIterable(chunk)) {
  49. if (possibleDuplicates === undefined) {
  50. // when possibleDuplicates is not yet set,
  51. // create a new Set from chunks of the current module
  52. // including only chunks with the same number of modules
  53. for (const dup of chunkGraph.getModuleChunksIterable(module)) {
  54. if (
  55. dup !== chunk &&
  56. chunkGraph.getNumberOfChunkModules(chunk) ===
  57. chunkGraph.getNumberOfChunkModules(dup) &&
  58. !notDuplicates.has(dup)
  59. ) {
  60. // delay allocating the new Set until here, reduce memory pressure
  61. if (possibleDuplicates === undefined) {
  62. possibleDuplicates = new Set();
  63. }
  64. possibleDuplicates.add(dup);
  65. }
  66. }
  67. // when no chunk is possible we can break here
  68. if (possibleDuplicates === undefined) break;
  69. } else {
  70. // validate existing possible duplicates
  71. for (const dup of possibleDuplicates) {
  72. // remove possible duplicate when module is not contained
  73. if (!chunkGraph.isModuleInChunk(module, dup)) {
  74. possibleDuplicates.delete(dup);
  75. }
  76. }
  77. // when all chunks has been removed we can break here
  78. if (possibleDuplicates.size === 0) break;
  79. }
  80. }
  81. // when we found duplicates
  82. if (
  83. possibleDuplicates !== undefined &&
  84. possibleDuplicates.size > 0
  85. ) {
  86. outer: for (const otherChunk of possibleDuplicates) {
  87. if (otherChunk.hasRuntime() !== chunk.hasRuntime()) continue;
  88. if (chunkGraph.getNumberOfEntryModules(chunk) > 0) continue;
  89. if (chunkGraph.getNumberOfEntryModules(otherChunk) > 0) {
  90. continue;
  91. }
  92. if (!runtimeEqual(chunk.runtime, otherChunk.runtime)) {
  93. for (const module of chunkGraph.getChunkModulesIterable(
  94. chunk
  95. )) {
  96. const exportsInfo = moduleGraph.getExportsInfo(module);
  97. if (
  98. !exportsInfo.isEquallyUsed(
  99. chunk.runtime,
  100. otherChunk.runtime
  101. )
  102. ) {
  103. continue outer;
  104. }
  105. }
  106. }
  107. // merge them
  108. if (chunkGraph.canChunksBeIntegrated(chunk, otherChunk)) {
  109. chunkGraph.integrateChunks(chunk, otherChunk);
  110. compilation.chunks.delete(otherChunk);
  111. }
  112. }
  113. }
  114. // don't check already processed chunks twice
  115. notDuplicates.add(chunk);
  116. }
  117. }
  118. );
  119. });
  120. }
  121. }
  122. module.exports = MergeDuplicateChunksPlugin;