1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | "use strict";
|
7 |
|
8 | const PureExpressionDependency = require("../dependencies/PureExpressionDependency");
|
9 | const InnerGraph = require("./InnerGraph");
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | const { topLevelSymbolTag } = InnerGraph;
|
23 |
|
24 | class InnerGraphPlugin {
|
25 | |
26 |
|
27 |
|
28 |
|
29 |
|
30 | apply(compiler) {
|
31 | compiler.hooks.compilation.tap(
|
32 | "InnerGraphPlugin",
|
33 | (compilation, { normalModuleFactory }) => {
|
34 | const logger = compilation.getLogger("webpack.InnerGraphPlugin");
|
35 |
|
36 | compilation.dependencyTemplates.set(
|
37 | PureExpressionDependency,
|
38 | new PureExpressionDependency.Template()
|
39 | );
|
40 |
|
41 | |
42 |
|
43 |
|
44 |
|
45 |
|
46 | const handler = (parser, parserOptions) => {
|
47 | const onUsageSuper = sup => {
|
48 | InnerGraph.onUsage(parser.state, usedByExports => {
|
49 | switch (usedByExports) {
|
50 | case undefined:
|
51 | case true:
|
52 | return;
|
53 | default: {
|
54 | const dep = new PureExpressionDependency(sup.range);
|
55 | dep.loc = sup.loc;
|
56 | dep.usedByExports = usedByExports;
|
57 | parser.state.module.addDependency(dep);
|
58 | break;
|
59 | }
|
60 | }
|
61 | });
|
62 | };
|
63 |
|
64 | parser.hooks.program.tap("InnerGraphPlugin", () => {
|
65 | InnerGraph.enable(parser.state);
|
66 | });
|
67 |
|
68 | parser.hooks.finish.tap("InnerGraphPlugin", () => {
|
69 | if (!InnerGraph.isEnabled(parser.state)) return;
|
70 |
|
71 | logger.time("infer dependency usage");
|
72 | InnerGraph.inferDependencyUsage(parser.state);
|
73 | logger.timeAggregate("infer dependency usage");
|
74 | });
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 | const statementWithTopLevelSymbol = new WeakMap();
|
87 |
|
88 | const statementPurePart = new WeakMap();
|
89 |
|
90 |
|
91 | const classWithTopLevelSymbol = new WeakMap();
|
92 |
|
93 |
|
94 | const declWithTopLevelSymbol = new WeakMap();
|
95 |
|
96 | const pureDeclarators = new WeakSet();
|
97 |
|
98 |
|
99 |
|
100 | parser.hooks.preStatement.tap("InnerGraphPlugin", statement => {
|
101 | if (!InnerGraph.isEnabled(parser.state)) return;
|
102 |
|
103 | if (parser.scope.topLevelScope === true) {
|
104 | if (statement.type === "FunctionDeclaration") {
|
105 | const name = statement.id ? statement.id.name : "*default*";
|
106 | const fn = InnerGraph.tagTopLevelSymbol(parser, name);
|
107 | statementWithTopLevelSymbol.set(statement, fn);
|
108 | return true;
|
109 | }
|
110 | }
|
111 | });
|
112 |
|
113 | parser.hooks.blockPreStatement.tap("InnerGraphPlugin", statement => {
|
114 | if (!InnerGraph.isEnabled(parser.state)) return;
|
115 |
|
116 | if (parser.scope.topLevelScope === true) {
|
117 | if (statement.type === "ClassDeclaration") {
|
118 | const name = statement.id ? statement.id.name : "*default*";
|
119 | const fn = InnerGraph.tagTopLevelSymbol(parser, name);
|
120 | classWithTopLevelSymbol.set(statement, fn);
|
121 | return true;
|
122 | }
|
123 | if (statement.type === "ExportDefaultDeclaration") {
|
124 | const name = "*default*";
|
125 | const fn = InnerGraph.tagTopLevelSymbol(parser, name);
|
126 | const decl = statement.declaration;
|
127 | if (
|
128 | decl.type === "ClassExpression" ||
|
129 | decl.type === "ClassDeclaration"
|
130 | ) {
|
131 | classWithTopLevelSymbol.set(decl, fn);
|
132 | } else if (parser.isPure(decl, statement.range[0])) {
|
133 | statementWithTopLevelSymbol.set(statement, fn);
|
134 | if (
|
135 | !decl.type.endsWith("FunctionExpression") &&
|
136 | !decl.type.endsWith("Declaration") &&
|
137 | decl.type !== "Literal"
|
138 | ) {
|
139 | statementPurePart.set(statement, decl);
|
140 | }
|
141 | }
|
142 | }
|
143 | }
|
144 | });
|
145 |
|
146 | parser.hooks.preDeclarator.tap(
|
147 | "InnerGraphPlugin",
|
148 | (decl, statement) => {
|
149 | if (!InnerGraph.isEnabled(parser.state)) return;
|
150 | if (
|
151 | parser.scope.topLevelScope === true &&
|
152 | decl.init &&
|
153 | decl.id.type === "Identifier"
|
154 | ) {
|
155 | const name = decl.id.name;
|
156 | if (decl.init.type === "ClassExpression") {
|
157 | const fn = InnerGraph.tagTopLevelSymbol(parser, name);
|
158 | classWithTopLevelSymbol.set(decl.init, fn);
|
159 | } else if (parser.isPure(decl.init, decl.id.range[1])) {
|
160 | const fn = InnerGraph.tagTopLevelSymbol(parser, name);
|
161 | declWithTopLevelSymbol.set(decl, fn);
|
162 | if (
|
163 | !decl.init.type.endsWith("FunctionExpression") &&
|
164 | decl.init.type !== "Literal"
|
165 | ) {
|
166 | pureDeclarators.add(decl);
|
167 | }
|
168 | return true;
|
169 | }
|
170 | }
|
171 | }
|
172 | );
|
173 |
|
174 |
|
175 |
|
176 |
|
177 |
|
178 |
|
179 |
|
180 |
|
181 |
|
182 |
|
183 |
|
184 |
|
185 |
|
186 |
|
187 |
|
188 |
|
189 |
|
190 | parser.hooks.statement.tap("InnerGraphPlugin", statement => {
|
191 | if (!InnerGraph.isEnabled(parser.state)) return;
|
192 | if (parser.scope.topLevelScope === true) {
|
193 | InnerGraph.setTopLevelSymbol(parser.state, undefined);
|
194 |
|
195 | const fn = statementWithTopLevelSymbol.get(statement);
|
196 | if (fn) {
|
197 | InnerGraph.setTopLevelSymbol(parser.state, fn);
|
198 | const purePart = statementPurePart.get(statement);
|
199 | if (purePart) {
|
200 | InnerGraph.onUsage(parser.state, usedByExports => {
|
201 | switch (usedByExports) {
|
202 | case undefined:
|
203 | case true:
|
204 | return;
|
205 | default: {
|
206 | const dep = new PureExpressionDependency(
|
207 | purePart.range
|
208 | );
|
209 | dep.loc = statement.loc;
|
210 | dep.usedByExports = usedByExports;
|
211 | parser.state.module.addDependency(dep);
|
212 | break;
|
213 | }
|
214 | }
|
215 | });
|
216 | }
|
217 | }
|
218 | }
|
219 | });
|
220 |
|
221 | parser.hooks.classExtendsExpression.tap(
|
222 | "InnerGraphPlugin",
|
223 | (expr, statement) => {
|
224 | if (!InnerGraph.isEnabled(parser.state)) return;
|
225 | if (parser.scope.topLevelScope === true) {
|
226 | const fn = classWithTopLevelSymbol.get(statement);
|
227 | if (
|
228 | fn &&
|
229 | parser.isPure(
|
230 | expr,
|
231 | statement.id ? statement.id.range[1] : statement.range[0]
|
232 | )
|
233 | ) {
|
234 | InnerGraph.setTopLevelSymbol(parser.state, fn);
|
235 | onUsageSuper(expr);
|
236 | }
|
237 | }
|
238 | }
|
239 | );
|
240 |
|
241 | parser.hooks.classBodyElement.tap(
|
242 | "InnerGraphPlugin",
|
243 | (element, classDefinition) => {
|
244 | if (!InnerGraph.isEnabled(parser.state)) return;
|
245 | if (parser.scope.topLevelScope === true) {
|
246 | const fn = classWithTopLevelSymbol.get(classDefinition);
|
247 | if (fn) {
|
248 | InnerGraph.setTopLevelSymbol(parser.state, undefined);
|
249 | }
|
250 | }
|
251 | }
|
252 | );
|
253 |
|
254 | parser.hooks.classBodyValue.tap(
|
255 | "InnerGraphPlugin",
|
256 | (expression, element, classDefinition) => {
|
257 | if (!InnerGraph.isEnabled(parser.state)) return;
|
258 | if (parser.scope.topLevelScope === true) {
|
259 | const fn = classWithTopLevelSymbol.get(classDefinition);
|
260 | if (fn) {
|
261 | if (
|
262 | !element.static ||
|
263 | parser.isPure(
|
264 | expression,
|
265 | element.key ? element.key.range[1] : element.range[0]
|
266 | )
|
267 | ) {
|
268 | InnerGraph.setTopLevelSymbol(parser.state, fn);
|
269 | if (element.type !== "MethodDefinition" && element.static) {
|
270 | InnerGraph.onUsage(parser.state, usedByExports => {
|
271 | switch (usedByExports) {
|
272 | case undefined:
|
273 | case true:
|
274 | return;
|
275 | default: {
|
276 | const dep = new PureExpressionDependency(
|
277 | expression.range
|
278 | );
|
279 | dep.loc = expression.loc;
|
280 | dep.usedByExports = usedByExports;
|
281 | parser.state.module.addDependency(dep);
|
282 | break;
|
283 | }
|
284 | }
|
285 | });
|
286 | }
|
287 | } else {
|
288 | InnerGraph.setTopLevelSymbol(parser.state, undefined);
|
289 | }
|
290 | }
|
291 | }
|
292 | }
|
293 | );
|
294 |
|
295 | parser.hooks.declarator.tap("InnerGraphPlugin", (decl, statement) => {
|
296 | if (!InnerGraph.isEnabled(parser.state)) return;
|
297 | const fn = declWithTopLevelSymbol.get(decl);
|
298 |
|
299 | if (fn) {
|
300 | InnerGraph.setTopLevelSymbol(parser.state, fn);
|
301 | if (pureDeclarators.has(decl)) {
|
302 | if (decl.init.type === "ClassExpression") {
|
303 | if (decl.init.superClass) {
|
304 | onUsageSuper(decl.init.superClass);
|
305 | }
|
306 | } else {
|
307 | InnerGraph.onUsage(parser.state, usedByExports => {
|
308 | switch (usedByExports) {
|
309 | case undefined:
|
310 | case true:
|
311 | return;
|
312 | default: {
|
313 | const dep = new PureExpressionDependency(
|
314 | decl.init.range
|
315 | );
|
316 | dep.loc = decl.loc;
|
317 | dep.usedByExports = usedByExports;
|
318 | parser.state.module.addDependency(dep);
|
319 | break;
|
320 | }
|
321 | }
|
322 | });
|
323 | }
|
324 | }
|
325 | parser.walkExpression(decl.init);
|
326 | InnerGraph.setTopLevelSymbol(parser.state, undefined);
|
327 | return true;
|
328 | }
|
329 | });
|
330 |
|
331 | parser.hooks.expression
|
332 | .for(topLevelSymbolTag)
|
333 | .tap("InnerGraphPlugin", () => {
|
334 | const topLevelSymbol = (
|
335 | parser.currentTagData
|
336 | );
|
337 | const currentTopLevelSymbol = InnerGraph.getTopLevelSymbol(
|
338 | parser.state
|
339 | );
|
340 | InnerGraph.addUsage(
|
341 | parser.state,
|
342 | topLevelSymbol,
|
343 | currentTopLevelSymbol || true
|
344 | );
|
345 | });
|
346 | parser.hooks.assign
|
347 | .for(topLevelSymbolTag)
|
348 | .tap("InnerGraphPlugin", expr => {
|
349 | if (!InnerGraph.isEnabled(parser.state)) return;
|
350 | if (expr.operator === "=") return true;
|
351 | });
|
352 | };
|
353 | normalModuleFactory.hooks.parser
|
354 | .for("javascript/auto")
|
355 | .tap("InnerGraphPlugin", handler);
|
356 | normalModuleFactory.hooks.parser
|
357 | .for("javascript/esm")
|
358 | .tap("InnerGraphPlugin", handler);
|
359 |
|
360 | compilation.hooks.finishModules.tap("InnerGraphPlugin", () => {
|
361 | logger.timeAggregateEnd("infer dependency usage");
|
362 | });
|
363 | }
|
364 | );
|
365 | }
|
366 | }
|
367 |
|
368 | module.exports = InnerGraphPlugin;
|