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