source: imaps-frontend/node_modules/webpack/lib/optimize/LimitChunkCountPlugin.js

main
Last change on this file was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 4 days ago

F4 Finalna Verzija

  • Property mode set to 100644
File size: 8.7 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const { STAGE_ADVANCED } = require("../OptimizationStages");
9const LazyBucketSortedSet = require("../util/LazyBucketSortedSet");
10const { compareChunks } = require("../util/comparators");
11const createSchemaValidation = require("../util/create-schema-validation");
12
13/** @typedef {import("../../declarations/plugins/optimize/LimitChunkCountPlugin").LimitChunkCountPluginOptions} LimitChunkCountPluginOptions */
14/** @typedef {import("../Chunk")} Chunk */
15/** @typedef {import("../Compiler")} Compiler */
16
17const 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 * @typedef {object} ChunkCombination
28 * @property {boolean} deleted this is set to true when combination was removed
29 * @property {number} sizeDiff
30 * @property {number} integratedSize
31 * @property {Chunk} a
32 * @property {Chunk} b
33 * @property {number} aIdx
34 * @property {number} bIdx
35 * @property {number} aSize
36 * @property {number} bSize
37 */
38
39/**
40 * @template K, V
41 * @param {Map<K, Set<V>>} map map
42 * @param {K} key key
43 * @param {V} value value
44 */
45const addToSetMap = (map, key, value) => {
46 const set = map.get(key);
47 if (set === undefined) {
48 map.set(key, new Set([value]));
49 } else {
50 set.add(value);
51 }
52};
53
54class LimitChunkCountPlugin {
55 /**
56 * @param {LimitChunkCountPluginOptions=} options options object
57 */
58 constructor(options) {
59 validate(options);
60 this.options = /** @type {LimitChunkCountPluginOptions} */ (options);
61 }
62
63 /**
64 * @param {Compiler} compiler the webpack compiler
65 * @returns {void}
66 */
67 apply(compiler) {
68 const options = this.options;
69 compiler.hooks.compilation.tap("LimitChunkCountPlugin", compilation => {
70 compilation.hooks.optimizeChunks.tap(
71 {
72 name: "LimitChunkCountPlugin",
73 stage: STAGE_ADVANCED
74 },
75 chunks => {
76 const chunkGraph = compilation.chunkGraph;
77 const maxChunks = options.maxChunks;
78 if (!maxChunks) return;
79 if (maxChunks < 1) return;
80 if (compilation.chunks.size <= maxChunks) return;
81
82 let remainingChunksToMerge = compilation.chunks.size - maxChunks;
83
84 // order chunks in a deterministic way
85 const compareChunksWithGraph = compareChunks(chunkGraph);
86 const orderedChunks = Array.from(chunks).sort(compareChunksWithGraph);
87
88 // create a lazy sorted data structure to keep all combinations
89 // this is large. Size = chunks * (chunks - 1) / 2
90 // It uses a multi layer bucket sort plus normal sort in the last layer
91 // It's also lazy so only accessed buckets are sorted
92 const combinations = new LazyBucketSortedSet(
93 // Layer 1: ordered by largest size benefit
94 c => c.sizeDiff,
95 (a, b) => b - a,
96 // Layer 2: ordered by smallest combined size
97 /**
98 * @param {ChunkCombination} c combination
99 * @returns {number} integrated size
100 */
101 c => c.integratedSize,
102 (a, b) => a - b,
103 // Layer 3: ordered by position difference in orderedChunk (-> to be deterministic)
104 /**
105 * @param {ChunkCombination} c combination
106 * @returns {number} position difference
107 */
108 c => c.bIdx - c.aIdx,
109 (a, b) => a - b,
110 // Layer 4: ordered by position in orderedChunk (-> to be deterministic)
111 (a, b) => a.bIdx - b.bIdx
112 );
113
114 // we keep a mapping from chunk to all combinations
115 // but this mapping is not kept up-to-date with deletions
116 // so `deleted` flag need to be considered when iterating this
117 /** @type {Map<Chunk, Set<ChunkCombination>>} */
118 const combinationsByChunk = new Map();
119
120 for (const [bIdx, b] of orderedChunks.entries()) {
121 // create combination pairs with size and integrated size
122 for (let aIdx = 0; aIdx < bIdx; aIdx++) {
123 const a = orderedChunks[aIdx];
124 // filter pairs that can not be integrated!
125 if (!chunkGraph.canChunksBeIntegrated(a, b)) continue;
126
127 const integratedSize = chunkGraph.getIntegratedChunksSize(
128 a,
129 b,
130 options
131 );
132
133 const aSize = chunkGraph.getChunkSize(a, options);
134 const bSize = chunkGraph.getChunkSize(b, options);
135 const c = {
136 deleted: false,
137 sizeDiff: aSize + bSize - integratedSize,
138 integratedSize,
139 a,
140 b,
141 aIdx,
142 bIdx,
143 aSize,
144 bSize
145 };
146 combinations.add(c);
147 addToSetMap(combinationsByChunk, a, c);
148 addToSetMap(combinationsByChunk, b, c);
149 }
150 }
151
152 // list of modified chunks during this run
153 // combinations affected by this change are skipped to allow
154 // further optimizations
155 /** @type {Set<Chunk>} */
156 const modifiedChunks = new Set();
157
158 let changed = false;
159 loop: while (true) {
160 const combination = combinations.popFirst();
161 if (combination === undefined) break;
162
163 combination.deleted = true;
164 const { a, b, integratedSize } = combination;
165
166 // skip over pair when
167 // one of the already merged chunks is a parent of one of the chunks
168 if (modifiedChunks.size > 0) {
169 const queue = new Set(a.groupsIterable);
170 for (const group of b.groupsIterable) {
171 queue.add(group);
172 }
173 for (const group of queue) {
174 for (const mChunk of modifiedChunks) {
175 if (mChunk !== a && mChunk !== b && mChunk.isInGroup(group)) {
176 // This is a potential pair which needs recalculation
177 // We can't do that now, but it merge before following pairs
178 // so we leave space for it, and consider chunks as modified
179 // just for the worse case
180 remainingChunksToMerge--;
181 if (remainingChunksToMerge <= 0) break loop;
182 modifiedChunks.add(a);
183 modifiedChunks.add(b);
184 continue loop;
185 }
186 }
187 for (const parent of group.parentsIterable) {
188 queue.add(parent);
189 }
190 }
191 }
192
193 // merge the chunks
194 if (chunkGraph.canChunksBeIntegrated(a, b)) {
195 chunkGraph.integrateChunks(a, b);
196 compilation.chunks.delete(b);
197
198 // flag chunk a as modified as further optimization are possible for all children here
199 modifiedChunks.add(a);
200
201 changed = true;
202 remainingChunksToMerge--;
203 if (remainingChunksToMerge <= 0) break;
204
205 // Update all affected combinations
206 // delete all combination with the removed chunk
207 // we will use combinations with the kept chunk instead
208 for (const combination of /** @type {Set<ChunkCombination>} */ (
209 combinationsByChunk.get(a)
210 )) {
211 if (combination.deleted) continue;
212 combination.deleted = true;
213 combinations.delete(combination);
214 }
215
216 // Update combinations with the kept chunk with new sizes
217 for (const combination of /** @type {Set<ChunkCombination>} */ (
218 combinationsByChunk.get(b)
219 )) {
220 if (combination.deleted) continue;
221 if (combination.a === b) {
222 if (!chunkGraph.canChunksBeIntegrated(a, combination.b)) {
223 combination.deleted = true;
224 combinations.delete(combination);
225 continue;
226 }
227 // Update size
228 const newIntegratedSize = chunkGraph.getIntegratedChunksSize(
229 a,
230 combination.b,
231 options
232 );
233 const finishUpdate = combinations.startUpdate(combination);
234 combination.a = a;
235 combination.integratedSize = newIntegratedSize;
236 combination.aSize = integratedSize;
237 combination.sizeDiff =
238 combination.bSize + integratedSize - newIntegratedSize;
239 finishUpdate();
240 } else if (combination.b === b) {
241 if (!chunkGraph.canChunksBeIntegrated(combination.a, a)) {
242 combination.deleted = true;
243 combinations.delete(combination);
244 continue;
245 }
246 // Update size
247 const newIntegratedSize = chunkGraph.getIntegratedChunksSize(
248 combination.a,
249 a,
250 options
251 );
252
253 const finishUpdate = combinations.startUpdate(combination);
254 combination.b = a;
255 combination.integratedSize = newIntegratedSize;
256 combination.bSize = integratedSize;
257 combination.sizeDiff =
258 integratedSize + combination.aSize - newIntegratedSize;
259 finishUpdate();
260 }
261 }
262 combinationsByChunk.set(
263 a,
264 /** @type {Set<ChunkCombination>} */ (
265 combinationsByChunk.get(b)
266 )
267 );
268 combinationsByChunk.delete(b);
269 }
270 }
271 if (changed) return true;
272 }
273 );
274 });
275 }
276}
277module.exports = LimitChunkCountPlugin;
Note: See TracBrowser for help on using the repository browser.