source: frontend/node_modules/webpack/lib/FlagDependencyUsagePlugin.js

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

Fix frontend appearance

  • Property mode set to 100644
File size: 11.1 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 Dependency = require("./Dependency");
9const { UsageState } = require("./ExportsInfo");
10const ModuleGraphConnection = require("./ModuleGraphConnection");
11const { STAGE_DEFAULT } = require("./OptimizationStages");
12const ArrayQueue = require("./util/ArrayQueue");
13const TupleQueue = require("./util/TupleQueue");
14const { getEntryRuntime, mergeRuntimeOwned } = require("./util/runtime");
15
16/** @typedef {import("./Compiler")} Compiler */
17/** @typedef {import("./DependenciesBlock")} DependenciesBlock */
18/** @typedef {import("./Dependency").ReferencedExport} ReferencedExport */
19/** @typedef {import("./Dependency").ReferencedExports} ReferencedExports */
20/** @typedef {import("./ExportsInfo")} ExportsInfo */
21/** @typedef {import("./Module")} Module */
22/** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */
23
24const { NO_EXPORTS_REFERENCED, EXPORTS_OBJECT_REFERENCED } = Dependency;
25
26const PLUGIN_NAME = "FlagDependencyUsagePlugin";
27const PLUGIN_LOGGER_NAME = `webpack.${PLUGIN_NAME}`;
28
29class FlagDependencyUsagePlugin {
30 /**
31 * Creates an instance of FlagDependencyUsagePlugin.
32 * @param {boolean} global do a global analysis instead of per runtime
33 */
34 constructor(global) {
35 /** @type {boolean} */
36 this.global = global;
37 }
38
39 /**
40 * Applies the plugin by registering its hooks on the compiler.
41 * @param {Compiler} compiler the compiler instance
42 * @returns {void}
43 */
44 apply(compiler) {
45 compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
46 const moduleGraph = compilation.moduleGraph;
47 compilation.hooks.optimizeDependencies.tap(
48 { name: PLUGIN_NAME, stage: STAGE_DEFAULT },
49 (modules) => {
50 if (compilation.moduleMemCaches) {
51 throw new Error(
52 "optimization.usedExports can't be used with cacheUnaffected as export usage is a global effect"
53 );
54 }
55
56 const logger = compilation.getLogger(PLUGIN_LOGGER_NAME);
57 /** @type {Map<ExportsInfo, Module>} */
58 const exportInfoToModuleMap = new Map();
59
60 /** @type {TupleQueue<Module, RuntimeSpec>} */
61 const queue = new TupleQueue();
62
63 /**
64 * Process referenced module.
65 * @param {Module} module module to process
66 * @param {ReferencedExports} usedExports list of used exports
67 * @param {RuntimeSpec} runtime part of which runtime
68 * @param {boolean} forceSideEffects always apply side effects
69 * @returns {void}
70 */
71 const processReferencedModule = (
72 module,
73 usedExports,
74 runtime,
75 forceSideEffects
76 ) => {
77 const exportsInfo = moduleGraph.getExportsInfo(module);
78 if (usedExports.length > 0) {
79 if (!module.buildMeta || !module.buildMeta.exportsType) {
80 if (exportsInfo.setUsedWithoutInfo(runtime)) {
81 queue.enqueue(module, runtime);
82 }
83 return;
84 }
85 for (const usedExportInfo of usedExports) {
86 /** @type {string[]} */
87 let usedExport;
88 let canMangle = true;
89 if (Array.isArray(usedExportInfo)) {
90 usedExport = usedExportInfo;
91 } else {
92 usedExport = usedExportInfo.name;
93 canMangle = usedExportInfo.canMangle !== false;
94 }
95 if (usedExport.length === 0) {
96 if (exportsInfo.setUsedInUnknownWay(runtime)) {
97 queue.enqueue(module, runtime);
98 }
99 } else {
100 let currentExportsInfo = exportsInfo;
101 for (let i = 0; i < usedExport.length; i++) {
102 const exportInfo = currentExportsInfo.getExportInfo(
103 usedExport[i]
104 );
105 if (canMangle === false) {
106 exportInfo.canMangleUse = false;
107 }
108 const lastOne = i === usedExport.length - 1;
109 if (!lastOne) {
110 const nestedInfo = exportInfo.getNestedExportsInfo();
111 if (nestedInfo) {
112 if (
113 exportInfo.setUsedConditionally(
114 (used) => used === UsageState.Unused,
115 UsageState.OnlyPropertiesUsed,
116 runtime
117 )
118 ) {
119 const currentModule =
120 currentExportsInfo === exportsInfo
121 ? module
122 : exportInfoToModuleMap.get(currentExportsInfo);
123 if (currentModule) {
124 queue.enqueue(currentModule, runtime);
125 }
126 }
127 currentExportsInfo = nestedInfo;
128 continue;
129 }
130 }
131 if (
132 exportInfo.setUsedConditionally(
133 (v) => v !== UsageState.Used,
134 UsageState.Used,
135 runtime
136 )
137 ) {
138 const currentModule =
139 currentExportsInfo === exportsInfo
140 ? module
141 : exportInfoToModuleMap.get(currentExportsInfo);
142 if (currentModule) {
143 queue.enqueue(currentModule, runtime);
144 }
145 }
146 break;
147 }
148 }
149 }
150 } else {
151 // for a module without side effects we stop tracking usage here when no export is used
152 // This module won't be evaluated in this case
153 // TODO webpack 6 remove this check
154 if (
155 !forceSideEffects &&
156 module.factoryMeta !== undefined &&
157 module.factoryMeta.sideEffectFree
158 ) {
159 return;
160 }
161 if (exportsInfo.setUsedForSideEffectsOnly(runtime)) {
162 queue.enqueue(module, runtime);
163 }
164 }
165 };
166
167 /**
168 * Processes the provided module.
169 * @param {DependenciesBlock} module the module
170 * @param {RuntimeSpec} runtime part of which runtime
171 * @param {boolean} forceSideEffects always apply side effects
172 * @returns {void}
173 */
174 const processModule = (module, runtime, forceSideEffects) => {
175 /** @typedef {Map<string, string[] | ReferencedExport>} ExportMaps */
176 /** @type {Map<Module, ReferencedExports | ExportMaps>} */
177 const map = new Map();
178
179 /** @type {ArrayQueue<DependenciesBlock>} */
180 const queue = new ArrayQueue();
181 queue.enqueue(module);
182 for (;;) {
183 const block = queue.dequeue();
184 if (block === undefined) break;
185 for (const b of block.blocks) {
186 if (b.groupOptions && b.groupOptions.entryOptions) {
187 processModule(
188 b,
189 this.global
190 ? undefined
191 : b.groupOptions.entryOptions.runtime || undefined,
192 true
193 );
194 } else {
195 queue.enqueue(b);
196 }
197 }
198 for (const dep of block.dependencies) {
199 const connection = moduleGraph.getConnection(dep);
200 if (!connection || !connection.module) {
201 continue;
202 }
203 const activeState = connection.getActiveState(runtime);
204 if (activeState === false) continue;
205 const { module } = connection;
206 if (activeState === ModuleGraphConnection.TRANSITIVE_ONLY) {
207 processModule(module, runtime, false);
208 continue;
209 }
210 const oldReferencedExports = map.get(module);
211 if (oldReferencedExports === EXPORTS_OBJECT_REFERENCED) {
212 continue;
213 }
214 const referencedExports =
215 compilation.getDependencyReferencedExports(dep, runtime);
216 if (
217 oldReferencedExports === undefined ||
218 oldReferencedExports === NO_EXPORTS_REFERENCED ||
219 referencedExports === EXPORTS_OBJECT_REFERENCED
220 ) {
221 map.set(module, referencedExports);
222 } else if (
223 oldReferencedExports !== undefined &&
224 referencedExports === NO_EXPORTS_REFERENCED
225 ) {
226 continue;
227 } else {
228 /** @type {undefined | ExportMaps} */
229 let exportsMap;
230 if (Array.isArray(oldReferencedExports)) {
231 exportsMap = new Map();
232 for (const item of oldReferencedExports) {
233 if (Array.isArray(item)) {
234 exportsMap.set(item.join("\n"), item);
235 } else {
236 exportsMap.set(item.name.join("\n"), item);
237 }
238 }
239 map.set(module, exportsMap);
240 } else {
241 exportsMap = oldReferencedExports;
242 }
243 for (const item of referencedExports) {
244 if (Array.isArray(item)) {
245 const key = item.join("\n");
246 const oldItem = exportsMap.get(key);
247 if (oldItem === undefined) {
248 exportsMap.set(key, item);
249 }
250 // if oldItem is already an array we have to do nothing
251 // if oldItem is an ReferencedExport object, we don't have to do anything
252 // as canMangle defaults to true for arrays
253 } else {
254 const key = item.name.join("\n");
255 const oldItem = exportsMap.get(key);
256 if (oldItem === undefined || Array.isArray(oldItem)) {
257 exportsMap.set(key, item);
258 } else {
259 exportsMap.set(key, {
260 name: item.name,
261 canMangle: item.canMangle && oldItem.canMangle
262 });
263 }
264 }
265 }
266 }
267 }
268 }
269
270 for (const [module, referencedExports] of map) {
271 if (Array.isArray(referencedExports)) {
272 processReferencedModule(
273 module,
274 referencedExports,
275 runtime,
276 forceSideEffects
277 );
278 } else {
279 processReferencedModule(
280 module,
281 [...referencedExports.values()],
282 runtime,
283 forceSideEffects
284 );
285 }
286 }
287 };
288
289 logger.time("initialize exports usage");
290 for (const module of modules) {
291 const exportsInfo = moduleGraph.getExportsInfo(module);
292 exportInfoToModuleMap.set(exportsInfo, module);
293 exportsInfo.setHasUseInfo();
294 }
295 logger.timeEnd("initialize exports usage");
296
297 logger.time("trace exports usage in graph");
298
299 /**
300 * Process entry dependency.
301 * @param {Dependency} dep dependency
302 * @param {RuntimeSpec} runtime runtime
303 */
304 const processEntryDependency = (dep, runtime) => {
305 const module = moduleGraph.getModule(dep);
306 if (module) {
307 processReferencedModule(
308 module,
309 NO_EXPORTS_REFERENCED,
310 runtime,
311 true
312 );
313 }
314 };
315 /** @type {RuntimeSpec} */
316 let globalRuntime;
317 for (const [
318 entryName,
319 { dependencies: deps, includeDependencies: includeDeps, options }
320 ] of compilation.entries) {
321 const runtime = this.global
322 ? undefined
323 : getEntryRuntime(compilation, entryName, options);
324 for (const dep of deps) {
325 processEntryDependency(dep, runtime);
326 }
327 for (const dep of includeDeps) {
328 processEntryDependency(dep, runtime);
329 }
330 globalRuntime = mergeRuntimeOwned(globalRuntime, runtime);
331 }
332 for (const dep of compilation.globalEntry.dependencies) {
333 processEntryDependency(dep, globalRuntime);
334 }
335 for (const dep of compilation.globalEntry.includeDependencies) {
336 processEntryDependency(dep, globalRuntime);
337 }
338
339 while (queue.length) {
340 const [module, runtime] = /** @type {[Module, RuntimeSpec]} */ (
341 queue.dequeue()
342 );
343 processModule(module, runtime, false);
344 }
345 logger.timeEnd("trace exports usage in graph");
346 }
347 );
348 });
349 }
350}
351
352module.exports = FlagDependencyUsagePlugin;
Note: See TracBrowser for help on using the repository browser.