source: frontend/node_modules/webpack/lib/optimize/RemoveParentModulesPlugin.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 11 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 6.8 KB
RevLine 
[9af201e]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_BASIC } = require("../OptimizationStages");
9
10/** @typedef {import("../Chunk")} Chunk */
11/** @typedef {import("../ChunkGroup")} ChunkGroup */
12/** @typedef {import("../Compiler")} Compiler */
13/** @typedef {import("../Module")} Module */
14
15/**
16 * Intersects multiple masks represented as bigints
17 * @param {bigint[]} masks The module masks to intersect
18 * @returns {bigint} The intersection of all masks
19 */
20function intersectMasks(masks) {
21 let result = masks[0];
22 for (let i = masks.length - 1; i >= 1; i--) {
23 result &= masks[i];
24 }
25 return result;
26}
27
28const ZERO_BIGINT = BigInt(0);
29const ONE_BIGINT = BigInt(1);
30const THIRTY_TWO_BIGINT = BigInt(32);
31
32/**
33 * Parses the module mask and returns the modules represented by it
34 * @param {bigint} mask the module mask
35 * @param {Module[]} ordinalModules the modules in the order they were added to the mask (LSB is index 0)
36 * @returns {Generator<Module, undefined, undefined>} the modules represented by the mask
37 */
38function* getModulesFromMask(mask, ordinalModules) {
39 let offset = 31;
40 while (mask !== ZERO_BIGINT) {
41 // Consider the last 32 bits, since that's what Math.clz32 can handle
42 let last32 = Number(BigInt.asUintN(32, mask));
43 while (last32 > 0) {
44 const last = Math.clz32(last32);
45 // The number of trailing zeros is the number trimmed off the input mask + 31 - the number of leading zeros
46 // The 32 is baked into the initial value of offset
47 const moduleIndex = offset - last;
48 // The number of trailing zeros is the index into the array generated by getOrCreateModuleMask
49 const module = ordinalModules[moduleIndex];
50 yield module;
51 // Remove the matched module from the mask
52 // Since we can only count leading zeros, not trailing, we can't just downshift the mask
53 last32 &= ~(1 << (31 - last));
54 }
55
56 // Remove the processed chunk from the mask
57 mask >>= THIRTY_TWO_BIGINT;
58 offset += 32;
59 }
60}
61
62const PLUGIN_NAME = "RemoveParentModulesPlugin";
63
64class RemoveParentModulesPlugin {
65 /**
66 * Applies the plugin by registering its hooks on the compiler.
67 * @param {Compiler} compiler the compiler
68 * @returns {void}
69 */
70 apply(compiler) {
71 compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
72 /**
73 * Handles the hook callback for this code path.
74 * @param {Iterable<Chunk>} chunks the chunks
75 * @param {ChunkGroup[]} chunkGroups the chunk groups
76 */
77 const handler = (chunks, chunkGroups) => {
78 const chunkGraph = compilation.chunkGraph;
79 /** @type {Set<ChunkGroup>} */
80 const queue = new Set();
81 /** @type {WeakMap<ChunkGroup, bigint | undefined>} */
82 const availableModulesMap = new WeakMap();
83
84 let nextModuleMask = ONE_BIGINT;
85 /** @type {WeakMap<Module, bigint>} */
86 const maskByModule = new WeakMap();
87 /** @type {Module[]} */
88 const ordinalModules = [];
89
90 /**
91 * Gets or create module mask.
92 * @param {Module} mod the module to get the mask for
93 * @returns {bigint} the module mask to uniquely identify the module
94 */
95 const getOrCreateModuleMask = (mod) => {
96 let id = maskByModule.get(mod);
97 if (id === undefined) {
98 id = nextModuleMask;
99 ordinalModules.push(mod);
100 maskByModule.set(mod, id);
101 nextModuleMask <<= ONE_BIGINT;
102 }
103 return id;
104 };
105
106 // Initialize masks by chunk and by chunk group for quicker comparisons
107 /** @type {WeakMap<Chunk, bigint>} */
108 const chunkMasks = new WeakMap();
109 for (const chunk of chunks) {
110 let mask = ZERO_BIGINT;
111 for (const m of chunkGraph.getChunkModulesIterable(chunk)) {
112 const id = getOrCreateModuleMask(m);
113 mask |= id;
114 }
115 chunkMasks.set(chunk, mask);
116 }
117
118 /** @type {WeakMap<ChunkGroup, bigint>} */
119 const chunkGroupMasks = new WeakMap();
120 for (const chunkGroup of chunkGroups) {
121 let mask = ZERO_BIGINT;
122 for (const chunk of chunkGroup.chunks) {
123 const chunkMask = chunkMasks.get(chunk);
124 if (chunkMask !== undefined) {
125 mask |= chunkMask;
126 }
127 }
128 chunkGroupMasks.set(chunkGroup, mask);
129 }
130
131 for (const chunkGroup of compilation.entrypoints.values()) {
132 // initialize available modules for chunks without parents
133 availableModulesMap.set(chunkGroup, ZERO_BIGINT);
134 for (const child of chunkGroup.childrenIterable) {
135 queue.add(child);
136 }
137 }
138 for (const chunkGroup of compilation.asyncEntrypoints) {
139 // initialize available modules for chunks without parents
140 availableModulesMap.set(chunkGroup, ZERO_BIGINT);
141 for (const child of chunkGroup.childrenIterable) {
142 queue.add(child);
143 }
144 }
145
146 for (const chunkGroup of queue) {
147 let availableModulesMask = availableModulesMap.get(chunkGroup);
148 let changed = false;
149 for (const parent of chunkGroup.parentsIterable) {
150 const availableModulesInParent = availableModulesMap.get(parent);
151 if (availableModulesInParent !== undefined) {
152 const parentMask =
153 availableModulesInParent |
154 /** @type {bigint} */ (chunkGroupMasks.get(parent));
155 // If we know the available modules in parent: process these
156 if (availableModulesMask === undefined) {
157 // if we have not own info yet: create new entry
158 availableModulesMask = parentMask;
159 changed = true;
160 } else {
161 const newMask = availableModulesMask & parentMask;
162 if (newMask !== availableModulesMask) {
163 changed = true;
164 availableModulesMask = newMask;
165 }
166 }
167 }
168 }
169
170 if (changed) {
171 availableModulesMap.set(chunkGroup, availableModulesMask);
172 // if something changed: enqueue our children
173 for (const child of chunkGroup.childrenIterable) {
174 // Push the child to the end of the queue
175 queue.delete(child);
176 queue.add(child);
177 }
178 }
179 }
180
181 // now we have available modules for every chunk
182 for (const chunk of chunks) {
183 const chunkMask = chunkMasks.get(chunk);
184 if (chunkMask === undefined) continue; // No info about this chunk
185
186 const availableModulesSets = Array.from(
187 chunk.groupsIterable,
188 (chunkGroup) => availableModulesMap.get(chunkGroup)
189 );
190 if (availableModulesSets.includes(undefined)) continue; // No info about this chunk group
191
192 const availableModulesMask = intersectMasks(
193 /** @type {bigint[]} */
194 (availableModulesSets)
195 );
196 const toRemoveMask = chunkMask & availableModulesMask;
197 if (toRemoveMask !== ZERO_BIGINT) {
198 for (const module of getModulesFromMask(
199 toRemoveMask,
200 ordinalModules
201 )) {
202 chunkGraph.disconnectChunkAndModule(chunk, module);
203 }
204 }
205 }
206 };
207 compilation.hooks.optimizeChunks.tap(
208 {
209 name: PLUGIN_NAME,
210 stage: STAGE_BASIC
211 },
212 handler
213 );
214 });
215 }
216}
217
218module.exports = RemoveParentModulesPlugin;
Note: See TracBrowser for help on using the repository browser.