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