1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | "use strict";
|
7 |
|
8 | const { STAGE_ADVANCED } = require("../OptimizationStages");
|
9 | const LazyBucketSortedSet = require("../util/LazyBucketSortedSet");
|
10 | const { compareChunks } = require("../util/comparators");
|
11 | const createSchemaValidation = require("../util/create-schema-validation");
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | const validate = createSchemaValidation(
|
18 | require("../../schemas/plugins/optimize/LimitChunkCountPlugin.check.js"),
|
19 | () => require("../../schemas/plugins/optimize/LimitChunkCountPlugin.json"),
|
20 | {
|
21 | name: "Limit Chunk Count Plugin",
|
22 | baseDataPath: "options"
|
23 | }
|
24 | );
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 | const addToSetMap = (map, key, value) => {
|
40 | const set = map.get(key);
|
41 | if (set === undefined) {
|
42 | map.set(key, new Set([value]));
|
43 | } else {
|
44 | set.add(value);
|
45 | }
|
46 | };
|
47 |
|
48 | class LimitChunkCountPlugin {
|
49 | |
50 |
|
51 |
|
52 | constructor(options) {
|
53 | validate(options);
|
54 | this.options = options;
|
55 | }
|
56 |
|
57 | |
58 |
|
59 |
|
60 |
|
61 | apply(compiler) {
|
62 | const options = this.options;
|
63 | compiler.hooks.compilation.tap("LimitChunkCountPlugin", compilation => {
|
64 | compilation.hooks.optimizeChunks.tap(
|
65 | {
|
66 | name: "LimitChunkCountPlugin",
|
67 | stage: STAGE_ADVANCED
|
68 | },
|
69 | chunks => {
|
70 | const chunkGraph = compilation.chunkGraph;
|
71 | const maxChunks = options.maxChunks;
|
72 | if (!maxChunks) return;
|
73 | if (maxChunks < 1) return;
|
74 | if (compilation.chunks.size <= maxChunks) return;
|
75 |
|
76 | let remainingChunksToMerge = compilation.chunks.size - maxChunks;
|
77 |
|
78 |
|
79 | const compareChunksWithGraph = compareChunks(chunkGraph);
|
80 | const orderedChunks = Array.from(chunks).sort(compareChunksWithGraph);
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 | const combinations = new LazyBucketSortedSet(
|
87 |
|
88 | c => c.sizeDiff,
|
89 | (a, b) => b - a,
|
90 |
|
91 | c => c.integratedSize,
|
92 | (a, b) => a - b,
|
93 |
|
94 | c => c.bIdx - c.aIdx,
|
95 | (a, b) => a - b,
|
96 |
|
97 | (a, b) => a.bIdx - b.bIdx
|
98 | );
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 | const combinationsByChunk = new Map();
|
105 |
|
106 | orderedChunks.forEach((b, bIdx) => {
|
107 |
|
108 | for (let aIdx = 0; aIdx < bIdx; aIdx++) {
|
109 | const a = orderedChunks[aIdx];
|
110 |
|
111 | if (!chunkGraph.canChunksBeIntegrated(a, b)) continue;
|
112 |
|
113 | const integratedSize = chunkGraph.getIntegratedChunksSize(
|
114 | a,
|
115 | b,
|
116 | options
|
117 | );
|
118 |
|
119 | const aSize = chunkGraph.getChunkSize(a, options);
|
120 | const bSize = chunkGraph.getChunkSize(b, options);
|
121 | const c = {
|
122 | deleted: false,
|
123 | sizeDiff: aSize + bSize - integratedSize,
|
124 | integratedSize,
|
125 | a,
|
126 | b,
|
127 | aIdx,
|
128 | bIdx,
|
129 | aSize,
|
130 | bSize
|
131 | };
|
132 | combinations.add(c);
|
133 | addToSetMap(combinationsByChunk, a, c);
|
134 | addToSetMap(combinationsByChunk, b, c);
|
135 | }
|
136 | return combinations;
|
137 | });
|
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 | const modifiedChunks = new Set();
|
144 |
|
145 | let changed = false;
|
146 |
|
147 | loop: while (true) {
|
148 | const combination = combinations.popFirst();
|
149 | if (combination === undefined) break;
|
150 |
|
151 | combination.deleted = true;
|
152 | const { a, b, integratedSize } = combination;
|
153 |
|
154 |
|
155 |
|
156 | if (modifiedChunks.size > 0) {
|
157 | const queue = new Set(a.groupsIterable);
|
158 | for (const group of b.groupsIterable) {
|
159 | queue.add(group);
|
160 | }
|
161 | for (const group of queue) {
|
162 | for (const mChunk of modifiedChunks) {
|
163 | if (mChunk !== a && mChunk !== b && mChunk.isInGroup(group)) {
|
164 |
|
165 |
|
166 |
|
167 |
|
168 | remainingChunksToMerge--;
|
169 | if (remainingChunksToMerge <= 0) break loop;
|
170 | modifiedChunks.add(a);
|
171 | modifiedChunks.add(b);
|
172 | continue loop;
|
173 | }
|
174 | }
|
175 | for (const parent of group.parentsIterable) {
|
176 | queue.add(parent);
|
177 | }
|
178 | }
|
179 | }
|
180 |
|
181 |
|
182 | if (chunkGraph.canChunksBeIntegrated(a, b)) {
|
183 | chunkGraph.integrateChunks(a, b);
|
184 | compilation.chunks.delete(b);
|
185 |
|
186 |
|
187 | modifiedChunks.add(a);
|
188 |
|
189 | changed = true;
|
190 | remainingChunksToMerge--;
|
191 | if (remainingChunksToMerge <= 0) break;
|
192 |
|
193 |
|
194 |
|
195 |
|
196 | for (const combination of combinationsByChunk.get(a)) {
|
197 | if (combination.deleted) continue;
|
198 | combination.deleted = true;
|
199 | combinations.delete(combination);
|
200 | }
|
201 |
|
202 |
|
203 | for (const combination of combinationsByChunk.get(b)) {
|
204 | if (combination.deleted) continue;
|
205 | if (combination.a === b) {
|
206 | if (!chunkGraph.canChunksBeIntegrated(a, combination.b)) {
|
207 | combination.deleted = true;
|
208 | combinations.delete(combination);
|
209 | continue;
|
210 | }
|
211 |
|
212 | const newIntegratedSize = chunkGraph.getIntegratedChunksSize(
|
213 | a,
|
214 | combination.b,
|
215 | options
|
216 | );
|
217 | const finishUpdate = combinations.startUpdate(combination);
|
218 | combination.a = a;
|
219 | combination.integratedSize = newIntegratedSize;
|
220 | combination.aSize = integratedSize;
|
221 | combination.sizeDiff =
|
222 | combination.bSize + integratedSize - newIntegratedSize;
|
223 | finishUpdate();
|
224 | } else if (combination.b === b) {
|
225 | if (!chunkGraph.canChunksBeIntegrated(combination.a, a)) {
|
226 | combination.deleted = true;
|
227 | combinations.delete(combination);
|
228 | continue;
|
229 | }
|
230 |
|
231 | const newIntegratedSize = chunkGraph.getIntegratedChunksSize(
|
232 | combination.a,
|
233 | a,
|
234 | options
|
235 | );
|
236 |
|
237 | const finishUpdate = combinations.startUpdate(combination);
|
238 | combination.b = a;
|
239 | combination.integratedSize = newIntegratedSize;
|
240 | combination.bSize = integratedSize;
|
241 | combination.sizeDiff =
|
242 | integratedSize + combination.aSize - newIntegratedSize;
|
243 | finishUpdate();
|
244 | }
|
245 | }
|
246 | combinationsByChunk.set(a, combinationsByChunk.get(b));
|
247 | combinationsByChunk.delete(b);
|
248 | }
|
249 | }
|
250 | if (changed) return true;
|
251 | }
|
252 | );
|
253 | });
|
254 | }
|
255 | }
|
256 | module.exports = LimitChunkCountPlugin;
|