source: imaps-frontend/node_modules/webpack/lib/optimize/InnerGraphPlugin.js@ 79a0317

main
Last change on this file since 79a0317 was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 3 days ago

F4 Finalna Verzija

  • Property mode set to 100644
File size: 14.0 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 {
9 JAVASCRIPT_MODULE_TYPE_AUTO,
10 JAVASCRIPT_MODULE_TYPE_ESM
11} = require("../ModuleTypeConstants");
12const PureExpressionDependency = require("../dependencies/PureExpressionDependency");
13const InnerGraph = require("./InnerGraph");
14
15/** @typedef {import("estree").ClassDeclaration} ClassDeclaration */
16/** @typedef {import("estree").ClassExpression} ClassExpression */
17/** @typedef {import("estree").Expression} Expression */
18/** @typedef {import("estree").Node} Node */
19/** @typedef {import("estree").VariableDeclarator} VariableDeclaratorNode */
20/** @typedef {import("../../declarations/WebpackOptions").JavascriptParserOptions} JavascriptParserOptions */
21/** @typedef {import("../Compiler")} Compiler */
22/** @typedef {import("../Dependency")} Dependency */
23/** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
24/** @typedef {import("../dependencies/HarmonyImportSpecifierDependency")} HarmonyImportSpecifierDependency */
25/** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
26/** @typedef {import("../javascript/JavascriptParser").Range} Range */
27/** @typedef {import("./InnerGraph").InnerGraph} InnerGraph */
28/** @typedef {import("./InnerGraph").TopLevelSymbol} TopLevelSymbol */
29
30const { topLevelSymbolTag } = InnerGraph;
31
32const PLUGIN_NAME = "InnerGraphPlugin";
33
34class InnerGraphPlugin {
35 /**
36 * Apply the plugin
37 * @param {Compiler} compiler the compiler instance
38 * @returns {void}
39 */
40 apply(compiler) {
41 compiler.hooks.compilation.tap(
42 PLUGIN_NAME,
43 (compilation, { normalModuleFactory }) => {
44 const logger = compilation.getLogger("webpack.InnerGraphPlugin");
45
46 compilation.dependencyTemplates.set(
47 PureExpressionDependency,
48 new PureExpressionDependency.Template()
49 );
50
51 /**
52 * @param {JavascriptParser} parser the parser
53 * @param {JavascriptParserOptions} parserOptions options
54 * @returns {void}
55 */
56 const handler = (parser, parserOptions) => {
57 /**
58 * @param {Expression} sup sup
59 */
60 const onUsageSuper = sup => {
61 InnerGraph.onUsage(parser.state, usedByExports => {
62 switch (usedByExports) {
63 case undefined:
64 case true:
65 return;
66 default: {
67 const dep = new PureExpressionDependency(
68 /** @type {Range} */
69 (sup.range)
70 );
71 dep.loc = /** @type {DependencyLocation} */ (sup.loc);
72 dep.usedByExports = usedByExports;
73 parser.state.module.addDependency(dep);
74 break;
75 }
76 }
77 });
78 };
79
80 parser.hooks.program.tap(PLUGIN_NAME, () => {
81 InnerGraph.enable(parser.state);
82 });
83
84 parser.hooks.finish.tap(PLUGIN_NAME, () => {
85 if (!InnerGraph.isEnabled(parser.state)) return;
86
87 logger.time("infer dependency usage");
88 InnerGraph.inferDependencyUsage(parser.state);
89 logger.timeAggregate("infer dependency usage");
90 });
91
92 // During prewalking the following datastructures are filled with
93 // nodes that have a TopLevelSymbol assigned and
94 // variables are tagged with the assigned TopLevelSymbol
95
96 // We differ 3 types of nodes:
97 // 1. full statements (export default, function declaration)
98 // 2. classes (class declaration, class expression)
99 // 3. variable declarators (const x = ...)
100
101 /** @type {WeakMap<Node, TopLevelSymbol>} */
102 const statementWithTopLevelSymbol = new WeakMap();
103 /** @type {WeakMap<Node, Node>} */
104 const statementPurePart = new WeakMap();
105
106 /** @type {WeakMap<ClassExpression | ClassDeclaration, TopLevelSymbol>} */
107 const classWithTopLevelSymbol = new WeakMap();
108
109 /** @type {WeakMap<VariableDeclaratorNode, TopLevelSymbol>} */
110 const declWithTopLevelSymbol = new WeakMap();
111 /** @type {WeakSet<VariableDeclaratorNode>} */
112 const pureDeclarators = new WeakSet();
113
114 // The following hooks are used during prewalking:
115
116 parser.hooks.preStatement.tap(PLUGIN_NAME, statement => {
117 if (!InnerGraph.isEnabled(parser.state)) return;
118
119 if (
120 parser.scope.topLevelScope === true &&
121 statement.type === "FunctionDeclaration"
122 ) {
123 const name = statement.id ? statement.id.name : "*default*";
124 const fn =
125 /** @type {TopLevelSymbol} */
126 (InnerGraph.tagTopLevelSymbol(parser, name));
127 statementWithTopLevelSymbol.set(statement, fn);
128 return true;
129 }
130 });
131
132 parser.hooks.blockPreStatement.tap(PLUGIN_NAME, statement => {
133 if (!InnerGraph.isEnabled(parser.state)) return;
134
135 if (parser.scope.topLevelScope === true) {
136 if (
137 statement.type === "ClassDeclaration" &&
138 parser.isPure(
139 statement,
140 /** @type {Range} */ (statement.range)[0]
141 )
142 ) {
143 const name = statement.id ? statement.id.name : "*default*";
144 const fn = /** @type {TopLevelSymbol} */ (
145 InnerGraph.tagTopLevelSymbol(parser, name)
146 );
147 classWithTopLevelSymbol.set(statement, fn);
148 return true;
149 }
150 if (statement.type === "ExportDefaultDeclaration") {
151 const name = "*default*";
152 const fn =
153 /** @type {TopLevelSymbol} */
154 (InnerGraph.tagTopLevelSymbol(parser, name));
155 const decl = statement.declaration;
156 if (
157 (decl.type === "ClassExpression" ||
158 decl.type === "ClassDeclaration") &&
159 parser.isPure(
160 /** @type {ClassExpression | ClassDeclaration} */
161 (decl),
162 /** @type {Range} */
163 (decl.range)[0]
164 )
165 ) {
166 classWithTopLevelSymbol.set(
167 /** @type {ClassExpression | ClassDeclaration} */
168 (decl),
169 fn
170 );
171 } else if (
172 parser.isPure(
173 /** @type {Expression} */
174 (decl),
175 /** @type {Range} */
176 (statement.range)[0]
177 )
178 ) {
179 statementWithTopLevelSymbol.set(statement, fn);
180 if (
181 !decl.type.endsWith("FunctionExpression") &&
182 !decl.type.endsWith("Declaration") &&
183 decl.type !== "Literal"
184 ) {
185 statementPurePart.set(
186 statement,
187 /** @type {Expression} */
188 (decl)
189 );
190 }
191 }
192 }
193 }
194 });
195
196 parser.hooks.preDeclarator.tap(PLUGIN_NAME, (decl, statement) => {
197 if (!InnerGraph.isEnabled(parser.state)) return;
198 if (
199 parser.scope.topLevelScope === true &&
200 decl.init &&
201 decl.id.type === "Identifier"
202 ) {
203 const name = decl.id.name;
204 if (
205 decl.init.type === "ClassExpression" &&
206 parser.isPure(
207 decl.init,
208 /** @type {Range} */ (decl.id.range)[1]
209 )
210 ) {
211 const fn =
212 /** @type {TopLevelSymbol} */
213 (InnerGraph.tagTopLevelSymbol(parser, name));
214 classWithTopLevelSymbol.set(decl.init, fn);
215 } else if (
216 parser.isPure(
217 decl.init,
218 /** @type {Range} */ (decl.id.range)[1]
219 )
220 ) {
221 const fn =
222 /** @type {TopLevelSymbol} */
223 (InnerGraph.tagTopLevelSymbol(parser, name));
224 declWithTopLevelSymbol.set(decl, fn);
225 if (
226 !decl.init.type.endsWith("FunctionExpression") &&
227 decl.init.type !== "Literal"
228 ) {
229 pureDeclarators.add(decl);
230 }
231 }
232 }
233 });
234
235 // During real walking we set the TopLevelSymbol state to the assigned
236 // TopLevelSymbol by using the fill datastructures.
237
238 // In addition to tracking TopLevelSymbols, we sometimes need to
239 // add a PureExpressionDependency. This is needed to skip execution
240 // of pure expressions, even when they are not dropped due to
241 // minimizing. Otherwise symbols used there might not exist anymore
242 // as they are removed as unused by this optimization
243
244 // When we find a reference to a TopLevelSymbol, we register a
245 // TopLevelSymbol dependency from TopLevelSymbol in state to the
246 // referenced TopLevelSymbol. This way we get a graph of all
247 // TopLevelSymbols.
248
249 // The following hooks are called during walking:
250
251 parser.hooks.statement.tap(PLUGIN_NAME, statement => {
252 if (!InnerGraph.isEnabled(parser.state)) return;
253 if (parser.scope.topLevelScope === true) {
254 InnerGraph.setTopLevelSymbol(parser.state, undefined);
255
256 const fn = statementWithTopLevelSymbol.get(statement);
257 if (fn) {
258 InnerGraph.setTopLevelSymbol(parser.state, fn);
259 const purePart = statementPurePart.get(statement);
260 if (purePart) {
261 InnerGraph.onUsage(parser.state, usedByExports => {
262 switch (usedByExports) {
263 case undefined:
264 case true:
265 return;
266 default: {
267 const dep = new PureExpressionDependency(
268 /** @type {Range} */ (purePart.range)
269 );
270 dep.loc =
271 /** @type {DependencyLocation} */
272 (statement.loc);
273 dep.usedByExports = usedByExports;
274 parser.state.module.addDependency(dep);
275 break;
276 }
277 }
278 });
279 }
280 }
281 }
282 });
283
284 parser.hooks.classExtendsExpression.tap(
285 PLUGIN_NAME,
286 (expr, statement) => {
287 if (!InnerGraph.isEnabled(parser.state)) return;
288 if (parser.scope.topLevelScope === true) {
289 const fn = classWithTopLevelSymbol.get(statement);
290 if (
291 fn &&
292 parser.isPure(
293 expr,
294 statement.id
295 ? /** @type {Range} */ (statement.id.range)[1]
296 : /** @type {Range} */ (statement.range)[0]
297 )
298 ) {
299 InnerGraph.setTopLevelSymbol(parser.state, fn);
300 onUsageSuper(expr);
301 }
302 }
303 }
304 );
305
306 parser.hooks.classBodyElement.tap(
307 PLUGIN_NAME,
308 (element, classDefinition) => {
309 if (!InnerGraph.isEnabled(parser.state)) return;
310 if (parser.scope.topLevelScope === true) {
311 const fn = classWithTopLevelSymbol.get(classDefinition);
312 if (fn) {
313 InnerGraph.setTopLevelSymbol(parser.state, undefined);
314 }
315 }
316 }
317 );
318
319 parser.hooks.classBodyValue.tap(
320 PLUGIN_NAME,
321 (expression, element, classDefinition) => {
322 if (!InnerGraph.isEnabled(parser.state)) return;
323 if (parser.scope.topLevelScope === true) {
324 const fn = classWithTopLevelSymbol.get(classDefinition);
325 if (fn) {
326 if (
327 !element.static ||
328 parser.isPure(
329 expression,
330 element.key
331 ? /** @type {Range} */ (element.key.range)[1]
332 : /** @type {Range} */ (element.range)[0]
333 )
334 ) {
335 InnerGraph.setTopLevelSymbol(parser.state, fn);
336 if (element.type !== "MethodDefinition" && element.static) {
337 InnerGraph.onUsage(parser.state, usedByExports => {
338 switch (usedByExports) {
339 case undefined:
340 case true:
341 return;
342 default: {
343 const dep = new PureExpressionDependency(
344 /** @type {Range} */ (expression.range)
345 );
346 dep.loc =
347 /** @type {DependencyLocation} */
348 (expression.loc);
349 dep.usedByExports = usedByExports;
350 parser.state.module.addDependency(dep);
351 break;
352 }
353 }
354 });
355 }
356 } else {
357 InnerGraph.setTopLevelSymbol(parser.state, undefined);
358 }
359 }
360 }
361 }
362 );
363
364 parser.hooks.declarator.tap(PLUGIN_NAME, (decl, statement) => {
365 if (!InnerGraph.isEnabled(parser.state)) return;
366 const fn = declWithTopLevelSymbol.get(decl);
367
368 if (fn) {
369 InnerGraph.setTopLevelSymbol(parser.state, fn);
370 if (pureDeclarators.has(decl)) {
371 if (
372 /** @type {ClassExpression} */
373 (decl.init).type === "ClassExpression"
374 ) {
375 if (decl.init.superClass) {
376 onUsageSuper(decl.init.superClass);
377 }
378 } else {
379 InnerGraph.onUsage(parser.state, usedByExports => {
380 switch (usedByExports) {
381 case undefined:
382 case true:
383 return;
384 default: {
385 const dep = new PureExpressionDependency(
386 /** @type {Range} */ (
387 /** @type {ClassExpression} */
388 (decl.init).range
389 )
390 );
391 dep.loc = /** @type {DependencyLocation} */ (decl.loc);
392 dep.usedByExports = usedByExports;
393 parser.state.module.addDependency(dep);
394 break;
395 }
396 }
397 });
398 }
399 }
400 parser.walkExpression(decl.init);
401 InnerGraph.setTopLevelSymbol(parser.state, undefined);
402 return true;
403 } else if (
404 decl.id.type === "Identifier" &&
405 decl.init &&
406 decl.init.type === "ClassExpression" &&
407 classWithTopLevelSymbol.has(decl.init)
408 ) {
409 parser.walkExpression(decl.init);
410 InnerGraph.setTopLevelSymbol(parser.state, undefined);
411 return true;
412 }
413 });
414
415 parser.hooks.expression
416 .for(topLevelSymbolTag)
417 .tap(PLUGIN_NAME, () => {
418 const topLevelSymbol = /** @type {TopLevelSymbol} */ (
419 parser.currentTagData
420 );
421 const currentTopLevelSymbol = InnerGraph.getTopLevelSymbol(
422 parser.state
423 );
424 InnerGraph.addUsage(
425 parser.state,
426 topLevelSymbol,
427 currentTopLevelSymbol || true
428 );
429 });
430 parser.hooks.assign.for(topLevelSymbolTag).tap(PLUGIN_NAME, expr => {
431 if (!InnerGraph.isEnabled(parser.state)) return;
432 if (expr.operator === "=") return true;
433 });
434 };
435 normalModuleFactory.hooks.parser
436 .for(JAVASCRIPT_MODULE_TYPE_AUTO)
437 .tap(PLUGIN_NAME, handler);
438 normalModuleFactory.hooks.parser
439 .for(JAVASCRIPT_MODULE_TYPE_ESM)
440 .tap(PLUGIN_NAME, handler);
441
442 compilation.hooks.finishModules.tap(PLUGIN_NAME, () => {
443 logger.timeAggregateEnd("infer dependency usage");
444 });
445 }
446 );
447 }
448}
449
450module.exports = InnerGraphPlugin;
Note: See TracBrowser for help on using the repository browser.