source: frontend/node_modules/webpack/lib/optimize/InnerGraph.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: 10.0 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Sergey Melyukov @smelukov
4*/
5
6"use strict";
7
8const { UsageState } = require("../ExportsInfo");
9const JavascriptParser = require("../javascript/JavascriptParser");
10
11/** @typedef {import("../Dependency")} Dependency */
12/** @typedef {import("../Dependency").GetConditionFn} GetConditionFn */
13/** @typedef {import("../Module")} Module */
14/** @typedef {import("../ModuleGraph")} ModuleGraph */
15/** @typedef {import("../Parser").ParserState} ParserState */
16/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
17
18/** @typedef {Set<string | TopLevelSymbol>} InnerGraphValueSet */
19/** @typedef {InnerGraphValueSet | true} InnerGraphValue */
20/** @typedef {TopLevelSymbol | null} InnerGraphKey */
21/** @typedef {Map<InnerGraphKey, InnerGraphValue | undefined>} InnerGraph */
22/** @typedef {(value: boolean | Set<string> | undefined) => void} UsageCallback */
23
24/**
25 * Defines the state object type used by this module.
26 * @typedef {object} StateObject
27 * @property {InnerGraph} innerGraph
28 * @property {TopLevelSymbol=} currentTopLevelSymbol
29 * @property {Map<TopLevelSymbol, Set<UsageCallback>>} usageCallbackMap
30 */
31
32/** @typedef {false | StateObject} State */
33
34class TopLevelSymbol {
35 /**
36 * Creates an instance of TopLevelSymbol.
37 * @param {string} name name of the variable
38 */
39 constructor(name) {
40 /** @type {string} */
41 this.name = name;
42 }
43}
44
45module.exports.TopLevelSymbol = TopLevelSymbol;
46
47/** @type {WeakMap<ParserState, State>} */
48const parserStateMap = new WeakMap();
49const topLevelSymbolTag = Symbol("top level symbol");
50
51/**
52 * Returns state.
53 * @param {ParserState} parserState parser state
54 * @returns {State | undefined} state
55 */
56function getState(parserState) {
57 return parserStateMap.get(parserState);
58}
59
60/**
61 * Processes the provided state.
62 * @param {ParserState} state parser state
63 * @param {TopLevelSymbol | null} symbol the symbol, or null for all symbols
64 * @param {Usage} usage usage data
65 * @returns {void}
66 */
67module.exports.addUsage = (state, symbol, usage) => {
68 const innerGraphState = getState(state);
69
70 if (innerGraphState) {
71 const { innerGraph } = innerGraphState;
72 const info = innerGraph.get(symbol);
73 if (usage === true) {
74 innerGraph.set(symbol, true);
75 } else if (info === undefined) {
76 innerGraph.set(symbol, new Set([usage]));
77 } else if (info !== true) {
78 info.add(usage);
79 }
80 }
81};
82
83/** @typedef {string | TopLevelSymbol | true} Usage */
84
85/**
86 * Processes the provided parser.
87 * @param {JavascriptParser} parser the parser
88 * @param {string} name name of variable
89 * @param {Usage} usage usage data
90 * @returns {void}
91 */
92module.exports.addVariableUsage = (parser, name, usage) => {
93 const symbol =
94 /** @type {TopLevelSymbol} */ (
95 parser.getTagData(name, topLevelSymbolTag)
96 ) || module.exports.tagTopLevelSymbol(parser, name);
97 if (symbol) {
98 module.exports.addUsage(parser.state, symbol, usage);
99 }
100};
101
102/**
103 * Processes the provided parser state.
104 * @param {ParserState} parserState parser state
105 * @returns {void}
106 */
107module.exports.bailout = (parserState) => {
108 parserStateMap.set(parserState, false);
109};
110
111/**
112 * Processes the provided parser state.
113 * @param {ParserState} parserState parser state
114 * @returns {void}
115 */
116module.exports.enable = (parserState) => {
117 const state = parserStateMap.get(parserState);
118 if (state === false) {
119 return;
120 }
121 parserStateMap.set(parserState, {
122 innerGraph: new Map(),
123 currentTopLevelSymbol: undefined,
124 usageCallbackMap: new Map()
125 });
126};
127
128/** @typedef {Set<string> | boolean} UsedByExports */
129
130/**
131 * Usage callback map.
132 * @param {Dependency} dependency the dependency
133 * @param {UsedByExports | undefined} usedByExports usedByExports info
134 * @param {ModuleGraph} moduleGraph moduleGraph
135 * @returns {null | false | GetConditionFn} function to determine if the connection is active
136 */
137module.exports.getDependencyUsedByExportsCondition = (
138 dependency,
139 usedByExports,
140 moduleGraph
141) => {
142 if (usedByExports === false) return false;
143 if (usedByExports !== true && usedByExports !== undefined) {
144 const selfModule =
145 /** @type {Module} */
146 (moduleGraph.getParentModule(dependency));
147 const exportsInfo = moduleGraph.getExportsInfo(selfModule);
148 return (_connections, runtime) => {
149 for (const exportName of usedByExports) {
150 if (exportsInfo.getUsed(exportName, runtime) !== UsageState.Unused) {
151 return true;
152 }
153 }
154 return false;
155 };
156 }
157 return null;
158};
159
160/**
161 * Returns usage data.
162 * @param {ParserState} state parser state
163 * @returns {TopLevelSymbol | void} usage data
164 */
165module.exports.getTopLevelSymbol = (state) => {
166 const innerGraphState = getState(state);
167
168 if (innerGraphState) {
169 return innerGraphState.currentTopLevelSymbol;
170 }
171};
172
173/**
174 * Processes the provided state.
175 * @param {ParserState} state parser state
176 * @returns {void}
177 */
178module.exports.inferDependencyUsage = (state) => {
179 const innerGraphState = getState(state);
180
181 if (!innerGraphState) {
182 return;
183 }
184
185 const { innerGraph, usageCallbackMap } = innerGraphState;
186 /** @type {Map<InnerGraphKey, InnerGraphValueSet | undefined>} */
187 const processed = new Map();
188 // flatten graph to terminal nodes (string, undefined or true)
189 const nonTerminal = new Set(innerGraph.keys());
190 while (nonTerminal.size > 0) {
191 for (const key of nonTerminal) {
192 /** @type {InnerGraphValue} */
193 let newSet = new Set();
194 let isTerminal = true;
195 const value = innerGraph.get(key);
196 let alreadyProcessed = processed.get(key);
197 if (alreadyProcessed === undefined) {
198 /** @type {InnerGraphValueSet} */
199 alreadyProcessed = new Set();
200 processed.set(key, alreadyProcessed);
201 }
202 if (value !== true && value !== undefined) {
203 for (const item of value) {
204 alreadyProcessed.add(item);
205 }
206 for (const item of value) {
207 if (typeof item === "string") {
208 newSet.add(item);
209 } else {
210 const itemValue = innerGraph.get(item);
211 if (itemValue === true) {
212 newSet = true;
213 break;
214 }
215 if (itemValue !== undefined) {
216 for (const i of itemValue) {
217 if (i === key) continue;
218 if (alreadyProcessed.has(i)) continue;
219 newSet.add(i);
220 if (typeof i !== "string") {
221 isTerminal = false;
222 }
223 }
224 }
225 }
226 }
227 if (newSet === true) {
228 innerGraph.set(key, true);
229 } else if (newSet.size === 0) {
230 innerGraph.set(key, undefined);
231 } else {
232 innerGraph.set(key, newSet);
233 }
234 }
235 if (isTerminal) {
236 nonTerminal.delete(key);
237
238 // For the global key, merge with all other keys
239 if (key === null) {
240 const globalValue = innerGraph.get(null);
241 if (globalValue) {
242 for (const [key, value] of innerGraph) {
243 if (key !== null && value !== true) {
244 if (globalValue === true) {
245 innerGraph.set(key, true);
246 } else {
247 const newSet = new Set(value);
248 for (const item of globalValue) {
249 newSet.add(item);
250 }
251 innerGraph.set(key, newSet);
252 }
253 }
254 }
255 }
256 }
257 }
258 }
259 }
260
261 /** @type {Map<Dependency, true | Set<string>>} */
262 for (const [symbol, callbacks] of usageCallbackMap) {
263 const usage = /** @type {true | Set<string> | undefined} */ (
264 innerGraph.get(symbol)
265 );
266 for (const callback of callbacks) {
267 callback(usage === undefined ? false : usage);
268 }
269 }
270};
271
272/**
273 * Returns false, when unused. Otherwise true.
274 * @param {Dependency} dependency the dependency
275 * @param {UsedByExports | undefined} usedByExports usedByExports info
276 * @param {ModuleGraph} moduleGraph moduleGraph
277 * @param {RuntimeSpec} runtime runtime
278 * @returns {boolean} false, when unused. Otherwise true
279 */
280module.exports.isDependencyUsedByExports = (
281 dependency,
282 usedByExports,
283 moduleGraph,
284 runtime
285) => {
286 if (usedByExports === false) return false;
287 if (usedByExports !== true && usedByExports !== undefined) {
288 const selfModule =
289 /** @type {Module} */
290 (moduleGraph.getParentModule(dependency));
291 const exportsInfo = moduleGraph.getExportsInfo(selfModule);
292 let used = false;
293 for (const exportName of usedByExports) {
294 if (exportsInfo.getUsed(exportName, runtime) !== UsageState.Unused) {
295 used = true;
296 }
297 }
298 if (!used) return false;
299 }
300 return true;
301};
302
303/**
304 * Returns true, when enabled.
305 * @param {ParserState} parserState parser state
306 * @returns {boolean} true, when enabled
307 */
308module.exports.isEnabled = (parserState) => {
309 const state = parserStateMap.get(parserState);
310 return Boolean(state);
311};
312
313/**
314 * Processes the provided state.
315 * @param {ParserState} state parser state
316 * @param {UsageCallback} onUsageCallback on usage callback
317 */
318module.exports.onUsage = (state, onUsageCallback) => {
319 const innerGraphState = getState(state);
320
321 if (innerGraphState) {
322 const { usageCallbackMap, currentTopLevelSymbol } = innerGraphState;
323 if (currentTopLevelSymbol) {
324 let callbacks = usageCallbackMap.get(currentTopLevelSymbol);
325
326 if (callbacks === undefined) {
327 /** @type {Set<UsageCallback>} */
328 callbacks = new Set();
329 usageCallbackMap.set(currentTopLevelSymbol, callbacks);
330 }
331
332 callbacks.add(onUsageCallback);
333 } else {
334 onUsageCallback(true);
335 }
336 } else {
337 onUsageCallback(undefined);
338 }
339};
340
341/**
342 * Processes the provided state.
343 * @param {ParserState} state parser state
344 * @param {TopLevelSymbol | undefined} symbol the symbol
345 */
346module.exports.setTopLevelSymbol = (state, symbol) => {
347 const innerGraphState = getState(state);
348
349 if (innerGraphState) {
350 innerGraphState.currentTopLevelSymbol = symbol;
351 }
352};
353
354/**
355 * Returns symbol.
356 * @param {JavascriptParser} parser parser
357 * @param {string} name name of variable
358 * @returns {TopLevelSymbol | undefined} symbol
359 */
360module.exports.tagTopLevelSymbol = (parser, name) => {
361 const innerGraphState = getState(parser.state);
362 if (!innerGraphState) return;
363
364 parser.defineVariable(name);
365
366 const existingTag = /** @type {TopLevelSymbol} */ (
367 parser.getTagData(name, topLevelSymbolTag)
368 );
369 if (existingTag) {
370 return existingTag;
371 }
372
373 const symbol = new TopLevelSymbol(name);
374 parser.tagVariable(
375 name,
376 topLevelSymbolTag,
377 symbol,
378 JavascriptParser.VariableInfoFlags.Normal
379 );
380 return symbol;
381};
382
383module.exports.topLevelSymbolTag = topLevelSymbolTag;
Note: See TracBrowser for help on using the repository browser.