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