FlagIncludedChunksPlugin.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. /** @typedef {import("../Chunk")} Chunk */
  7. /** @typedef {import("../Compiler")} Compiler */
  8. /** @typedef {import("../Module")} Module */
  9. class FlagIncludedChunksPlugin {
  10. /**
  11. * Apply the plugin
  12. * @param {Compiler} compiler the compiler instance
  13. * @returns {void}
  14. */
  15. apply(compiler) {
  16. compiler.hooks.compilation.tap("FlagIncludedChunksPlugin", compilation => {
  17. compilation.hooks.optimizeChunkIds.tap(
  18. "FlagIncludedChunksPlugin",
  19. chunks => {
  20. const chunkGraph = compilation.chunkGraph;
  21. // prepare two bit integers for each module
  22. // 2^31 is the max number represented as SMI in v8
  23. // we want the bits distributed this way:
  24. // the bit 2^31 is pretty rar and only one module should get it
  25. // so it has a probability of 1 / modulesCount
  26. // the first bit (2^0) is the easiest and every module could get it
  27. // if it doesn't get a better bit
  28. // from bit 2^n to 2^(n+1) there is a probability of p
  29. // so 1 / modulesCount == p^31
  30. // <=> p = sqrt31(1 / modulesCount)
  31. // so we use a modulo of 1 / sqrt31(1 / modulesCount)
  32. /** @type {WeakMap<Module, number>} */
  33. const moduleBits = new WeakMap();
  34. const modulesCount = compilation.modules.size;
  35. // precalculate the modulo values for each bit
  36. const modulo = 1 / Math.pow(1 / modulesCount, 1 / 31);
  37. const modulos = Array.from(
  38. { length: 31 },
  39. (x, i) => Math.pow(modulo, i) | 0
  40. );
  41. // iterate all modules to generate bit values
  42. let i = 0;
  43. for (const module of compilation.modules) {
  44. let bit = 30;
  45. while (i % modulos[bit] !== 0) {
  46. bit--;
  47. }
  48. moduleBits.set(module, 1 << bit);
  49. i++;
  50. }
  51. // iterate all chunks to generate bitmaps
  52. /** @type {WeakMap<Chunk, number>} */
  53. const chunkModulesHash = new WeakMap();
  54. for (const chunk of chunks) {
  55. let hash = 0;
  56. for (const module of chunkGraph.getChunkModulesIterable(chunk)) {
  57. hash |= moduleBits.get(module);
  58. }
  59. chunkModulesHash.set(chunk, hash);
  60. }
  61. for (const chunkA of chunks) {
  62. const chunkAHash = chunkModulesHash.get(chunkA);
  63. const chunkAModulesCount =
  64. chunkGraph.getNumberOfChunkModules(chunkA);
  65. if (chunkAModulesCount === 0) continue;
  66. let bestModule = undefined;
  67. for (const module of chunkGraph.getChunkModulesIterable(chunkA)) {
  68. if (
  69. bestModule === undefined ||
  70. chunkGraph.getNumberOfModuleChunks(bestModule) >
  71. chunkGraph.getNumberOfModuleChunks(module)
  72. )
  73. bestModule = module;
  74. }
  75. loopB: for (const chunkB of chunkGraph.getModuleChunksIterable(
  76. bestModule
  77. )) {
  78. // as we iterate the same iterables twice
  79. // skip if we find ourselves
  80. if (chunkA === chunkB) continue;
  81. const chunkBModulesCount =
  82. chunkGraph.getNumberOfChunkModules(chunkB);
  83. // ids for empty chunks are not included
  84. if (chunkBModulesCount === 0) continue;
  85. // instead of swapping A and B just bail
  86. // as we loop twice the current A will be B and B then A
  87. if (chunkAModulesCount > chunkBModulesCount) continue;
  88. // is chunkA in chunkB?
  89. // we do a cheap check for the hash value
  90. const chunkBHash = chunkModulesHash.get(chunkB);
  91. if ((chunkBHash & chunkAHash) !== chunkAHash) continue;
  92. // compare all modules
  93. for (const m of chunkGraph.getChunkModulesIterable(chunkA)) {
  94. if (!chunkGraph.isModuleInChunk(m, chunkB)) continue loopB;
  95. }
  96. chunkB.ids.push(chunkA.id);
  97. }
  98. }
  99. }
  100. );
  101. });
  102. }
  103. }
  104. module.exports = FlagIncludedChunksPlugin;