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