source: frontend/node_modules/webpack/lib/dependencies/AMDDefineDependencyParserPlugin.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 16.1 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 RuntimeGlobals = require("../RuntimeGlobals");
9const AMDDefineDependency = require("./AMDDefineDependency");
10const AMDRequireArrayDependency = require("./AMDRequireArrayDependency");
11const AMDRequireContextDependency = require("./AMDRequireContextDependency");
12const AMDRequireItemDependency = require("./AMDRequireItemDependency");
13const ConstDependency = require("./ConstDependency");
14const ContextDependencyHelpers = require("./ContextDependencyHelpers");
15const DynamicExports = require("./DynamicExports");
16const LocalModuleDependency = require("./LocalModuleDependency");
17const { addLocalModule, getLocalModule } = require("./LocalModulesHelpers");
18
19/** @typedef {import("estree").ArrowFunctionExpression} ArrowFunctionExpression */
20/** @typedef {import("estree").CallExpression} CallExpression */
21/** @typedef {import("estree").Expression} Expression */
22/** @typedef {import("estree").FunctionExpression} FunctionExpression */
23/** @typedef {import("estree").Identifier} Identifier */
24/** @typedef {import("estree").Literal} Literal */
25/** @typedef {import("estree").MemberExpression} MemberExpression */
26/** @typedef {import("estree").ObjectExpression} ObjectExpression */
27/** @typedef {import("estree").SpreadElement} SpreadElement */
28/** @typedef {import("../../declarations/WebpackOptions").JavascriptParserOptions} JavascriptParserOptions */
29/** @typedef {import("../Dependency")} Dependency */
30/** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
31/** @typedef {import("../javascript/BasicEvaluatedExpression")} BasicEvaluatedExpression */
32/** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
33/** @typedef {import("../javascript/JavascriptParser").ExportedVariableInfo} ExportedVariableInfo */
34/** @typedef {import("../javascript/JavascriptParser").Range} Range */
35/** @typedef {import("./LocalModule")} LocalModule */
36
37/**
38 * Checks whether this object is bound function expression.
39 * @param {Expression | SpreadElement} expr expression
40 * @returns {expr is CallExpression} true if it's a bound function expression
41 */
42const isBoundFunctionExpression = (expr) => {
43 if (expr.type !== "CallExpression") return false;
44 if (expr.callee.type !== "MemberExpression") return false;
45 if (expr.callee.computed) return false;
46 if (expr.callee.object.type !== "FunctionExpression") return false;
47 if (expr.callee.property.type !== "Identifier") return false;
48 if (expr.callee.property.name !== "bind") return false;
49 return true;
50};
51
52/** @typedef {FunctionExpression | ArrowFunctionExpression} UnboundFunctionExpression */
53
54/**
55 * Checks whether this object is unbound function expression.
56 * @param {Expression | SpreadElement} expr expression
57 * @returns {expr is FunctionExpression | ArrowFunctionExpression} true when unbound function expression
58 */
59const isUnboundFunctionExpression = (expr) => {
60 if (expr.type === "FunctionExpression") return true;
61 if (expr.type === "ArrowFunctionExpression") return true;
62 return false;
63};
64
65/**
66 * Checks whether this object is callable.
67 * @param {Expression | SpreadElement} expr expression
68 * @returns {expr is FunctionExpression | ArrowFunctionExpression | CallExpression} true when callable
69 */
70const isCallable = (expr) => {
71 if (isUnboundFunctionExpression(expr)) return true;
72 if (isBoundFunctionExpression(expr)) return true;
73 return false;
74};
75
76/** @typedef {Record<number, string>} Identifiers */
77
78const PLUGIN_NAME = "AMDDefineDependencyParserPlugin";
79
80class AMDDefineDependencyParserPlugin {
81 /**
82 * Creates an instance of AMDDefineDependencyParserPlugin.
83 * @param {JavascriptParserOptions} options parserOptions
84 */
85 constructor(options) {
86 this.options = options;
87 }
88
89 /**
90 * Applies the plugin by registering its hooks on the compiler.
91 * @param {JavascriptParser} parser the parser
92 * @returns {void}
93 */
94 apply(parser) {
95 parser.hooks.call
96 .for("define")
97 .tap(PLUGIN_NAME, this.processCallDefine.bind(this, parser));
98 }
99
100 /**
101 * Processes the provided parser.
102 * @param {JavascriptParser} parser the parser
103 * @param {CallExpression} expr call expression
104 * @param {BasicEvaluatedExpression} param param
105 * @param {Identifiers} identifiers identifiers
106 * @param {string=} namedModule named module
107 * @returns {boolean | undefined} result
108 */
109 processArray(parser, expr, param, identifiers, namedModule) {
110 if (param.isArray()) {
111 const items = /** @type {BasicEvaluatedExpression[]} */ (param.items);
112 for (const [idx, item] of items.entries()) {
113 if (
114 item.isString() &&
115 ["require", "module", "exports"].includes(
116 /** @type {string} */ (item.string)
117 )
118 ) {
119 identifiers[idx] =
120 /** @type {string} */
121 (item.string);
122 }
123 const result = this.processItem(parser, expr, item, namedModule);
124 if (result === undefined) {
125 this.processContext(parser, expr, item);
126 }
127 }
128 return true;
129 } else if (param.isConstArray()) {
130 /** @type {(string | LocalModuleDependency | AMDRequireItemDependency)[]} */
131 const deps = [];
132 const array = /** @type {string[]} */ (param.array);
133 for (const [idx, request] of array.entries()) {
134 /** @type {string | LocalModuleDependency | AMDRequireItemDependency} */
135 let dep;
136 /** @type {undefined | null | LocalModule} */
137 let localModule;
138 if (request === "require") {
139 identifiers[idx] = request;
140 dep = RuntimeGlobals.require;
141 } else if (["exports", "module"].includes(request)) {
142 identifiers[idx] = request;
143 dep = request;
144 } else if ((localModule = getLocalModule(parser.state, request))) {
145 localModule.flagUsed();
146 dep = new LocalModuleDependency(localModule, undefined, false);
147 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
148 parser.state.module.addPresentationalDependency(dep);
149 } else {
150 dep = this.newRequireItemDependency(request);
151 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
152 dep.optional = Boolean(parser.scope.inTry);
153 parser.state.current.addDependency(dep);
154 }
155 deps.push(dep);
156 }
157 const dep = this.newRequireArrayDependency(
158 deps,
159 /** @type {Range} */ (param.range)
160 );
161 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
162 dep.optional = Boolean(parser.scope.inTry);
163 parser.state.module.addPresentationalDependency(dep);
164 return true;
165 }
166 }
167
168 /**
169 * Processes the provided parser.
170 * @param {JavascriptParser} parser the parser
171 * @param {CallExpression} expr call expression
172 * @param {BasicEvaluatedExpression} param param
173 * @param {string=} namedModule named module
174 * @returns {boolean | undefined} result
175 */
176 processItem(parser, expr, param, namedModule) {
177 if (param.isConditional()) {
178 const options = /** @type {BasicEvaluatedExpression[]} */ (param.options);
179 for (const item of options) {
180 const result = this.processItem(parser, expr, item);
181 if (result === undefined) {
182 this.processContext(parser, expr, item);
183 }
184 }
185
186 return true;
187 } else if (param.isString()) {
188 /** @type {Dependency} */
189 let dep;
190 /** @type {undefined | null | LocalModule} */
191 let localModule;
192
193 if (param.string === "require") {
194 dep = new ConstDependency(
195 RuntimeGlobals.require,
196 /** @type {Range} */ (param.range),
197 [RuntimeGlobals.require]
198 );
199 } else if (param.string === "exports") {
200 dep = new ConstDependency(
201 "exports",
202 /** @type {Range} */ (param.range),
203 [RuntimeGlobals.exports]
204 );
205 } else if (param.string === "module") {
206 dep = new ConstDependency(
207 "module",
208 /** @type {Range} */ (param.range),
209 [RuntimeGlobals.module]
210 );
211 } else if (
212 (localModule = getLocalModule(
213 parser.state,
214 /** @type {string} */ (param.string),
215 namedModule
216 ))
217 ) {
218 localModule.flagUsed();
219 dep = new LocalModuleDependency(localModule, param.range, false);
220 } else {
221 dep = this.newRequireItemDependency(
222 /** @type {string} */ (param.string),
223 param.range
224 );
225 dep.optional = Boolean(parser.scope.inTry);
226 parser.state.current.addDependency(dep);
227 return true;
228 }
229 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
230 parser.state.module.addPresentationalDependency(dep);
231 return true;
232 }
233 }
234
235 /**
236 * Processes the provided parser.
237 * @param {JavascriptParser} parser the parser
238 * @param {CallExpression} expr call expression
239 * @param {BasicEvaluatedExpression} param param
240 * @returns {boolean | undefined} result
241 */
242 processContext(parser, expr, param) {
243 const dep = ContextDependencyHelpers.create(
244 AMDRequireContextDependency,
245 /** @type {Range} */ (param.range),
246 param,
247 expr,
248 this.options,
249 {
250 category: "amd"
251 },
252 parser
253 );
254 if (!dep) return;
255 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
256 dep.optional = Boolean(parser.scope.inTry);
257 parser.state.current.addDependency(dep);
258 return true;
259 }
260
261 /**
262 * Process call define.
263 * @param {JavascriptParser} parser the parser
264 * @param {CallExpression} expr call expression
265 * @returns {boolean | undefined} result
266 */
267 processCallDefine(parser, expr) {
268 /** @type {Expression | SpreadElement | undefined} */
269 let array;
270 /** @type {FunctionExpression | ArrowFunctionExpression | CallExpression | Identifier | undefined} */
271 let fn;
272 /** @type {ObjectExpression | Identifier | undefined} */
273 let obj;
274 /** @type {string | undefined} */
275 let namedModule;
276 switch (expr.arguments.length) {
277 case 1:
278 if (isCallable(expr.arguments[0])) {
279 // define(f() {…})
280 fn = expr.arguments[0];
281 } else if (expr.arguments[0].type === "ObjectExpression") {
282 // define({…})
283 obj = expr.arguments[0];
284 } else {
285 // define(expr)
286 // unclear if function or object
287 obj = fn = /** @type {Identifier} */ (expr.arguments[0]);
288 }
289 break;
290 case 2:
291 if (expr.arguments[0].type === "Literal") {
292 namedModule = /** @type {string} */ (expr.arguments[0].value);
293 // define("…", …)
294 if (isCallable(expr.arguments[1])) {
295 // define("…", f() {…})
296 fn = expr.arguments[1];
297 } else if (expr.arguments[1].type === "ObjectExpression") {
298 // define("…", {…})
299 obj = expr.arguments[1];
300 } else {
301 // define("…", expr)
302 // unclear if function or object
303 obj = fn = /** @type {Identifier} */ (expr.arguments[1]);
304 }
305 } else {
306 array = expr.arguments[0];
307 if (isCallable(expr.arguments[1])) {
308 // define([…], f() {})
309 fn = expr.arguments[1];
310 } else if (expr.arguments[1].type === "ObjectExpression") {
311 // define([…], {…})
312 obj = expr.arguments[1];
313 } else {
314 // define([…], expr)
315 // unclear if function or object
316 obj = fn = /** @type {Identifier} */ (expr.arguments[1]);
317 }
318 }
319 break;
320 case 3:
321 // define("…", […], f() {…})
322 namedModule =
323 /** @type {string} */
324 (
325 /** @type {Literal} */
326 (expr.arguments[0]).value
327 );
328 array = expr.arguments[1];
329 if (isCallable(expr.arguments[2])) {
330 // define("…", […], f() {})
331 fn = expr.arguments[2];
332 } else if (expr.arguments[2].type === "ObjectExpression") {
333 // define("…", […], {…})
334 obj = expr.arguments[2];
335 } else {
336 // define("…", […], expr)
337 // unclear if function or object
338 obj = fn = /** @type {Identifier} */ (expr.arguments[2]);
339 }
340 break;
341 default:
342 return;
343 }
344 DynamicExports.bailout(parser.state);
345 /** @type {Identifier[] | null} */
346 let fnParams = null;
347 let fnParamsOffset = 0;
348 if (fn) {
349 if (isUnboundFunctionExpression(fn)) {
350 fnParams =
351 /** @type {Identifier[]} */
352 (fn.params);
353 } else if (isBoundFunctionExpression(fn)) {
354 const object =
355 /** @type {FunctionExpression} */
356 (/** @type {MemberExpression} */ (fn.callee).object);
357
358 fnParams =
359 /** @type {Identifier[]} */
360 (object.params);
361 fnParamsOffset = fn.arguments.length - 1;
362 if (fnParamsOffset < 0) {
363 fnParamsOffset = 0;
364 }
365 }
366 }
367 /** @type {Map<string, ExportedVariableInfo>} */
368 const fnRenames = new Map();
369 if (array) {
370 /** @type {Identifiers} */
371 const identifiers = {};
372 const param = parser.evaluateExpression(array);
373 const result = this.processArray(
374 parser,
375 expr,
376 param,
377 identifiers,
378 namedModule
379 );
380 if (!result) return;
381 if (fnParams) {
382 fnParams = fnParams.slice(fnParamsOffset).filter((param, idx) => {
383 if (identifiers[idx]) {
384 fnRenames.set(param.name, parser.getVariableInfo(identifiers[idx]));
385 return false;
386 }
387 return true;
388 });
389 }
390 } else {
391 const identifiers = ["require", "exports", "module"];
392 if (fnParams) {
393 fnParams = fnParams.slice(fnParamsOffset).filter((param, idx) => {
394 if (identifiers[idx]) {
395 fnRenames.set(param.name, parser.getVariableInfo(identifiers[idx]));
396 return false;
397 }
398 return true;
399 });
400 }
401 }
402 /** @type {boolean | undefined} */
403 let inTry;
404 if (fn && isUnboundFunctionExpression(fn)) {
405 inTry = parser.scope.inTry;
406 parser.inFunctionScope(
407 true,
408 /** @type {Identifier[]} */ (fnParams),
409 () => {
410 for (const [name, varInfo] of fnRenames) {
411 parser.setVariable(name, varInfo);
412 }
413 parser.scope.inTry = /** @type {boolean} */ (inTry);
414 if (fn.body.type === "BlockStatement") {
415 parser.detectMode(fn.body.body);
416 const prev = parser.prevStatement;
417 parser.preWalkStatement(fn.body);
418 parser.prevStatement = prev;
419 parser.walkStatement(fn.body);
420 } else {
421 parser.walkExpression(fn.body);
422 }
423 }
424 );
425 } else if (fn && isBoundFunctionExpression(fn)) {
426 inTry = parser.scope.inTry;
427
428 const object =
429 /** @type {FunctionExpression} */
430 (/** @type {MemberExpression} */ (fn.callee).object);
431
432 parser.inFunctionScope(
433 true,
434 /** @type {Identifier[]} */
435 (object.params).filter(
436 (i) => !["require", "module", "exports"].includes(i.name)
437 ),
438 () => {
439 for (const [name, varInfo] of fnRenames) {
440 parser.setVariable(name, varInfo);
441 }
442 parser.scope.inTry = /** @type {boolean} */ (inTry);
443 parser.detectMode(object.body.body);
444 const prev = parser.prevStatement;
445 parser.preWalkStatement(object.body);
446 parser.prevStatement = prev;
447 parser.walkStatement(object.body);
448 }
449 );
450 if (fn.arguments) {
451 parser.walkExpressions(fn.arguments);
452 }
453 } else if (fn || obj) {
454 parser.walkExpression(
455 /** @type {FunctionExpression | ArrowFunctionExpression | CallExpression | ObjectExpression | Identifier} */
456 (fn || obj)
457 );
458 }
459
460 const dep = this.newDefineDependency(
461 /** @type {Range} */ (expr.range),
462 array ? /** @type {Range} */ (array.range) : null,
463 fn ? /** @type {Range} */ (fn.range) : null,
464 obj ? /** @type {Range} */ (obj.range) : null,
465 namedModule || null
466 );
467 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
468 if (namedModule) {
469 dep.localModule = addLocalModule(parser.state, namedModule);
470 }
471 parser.state.module.addPresentationalDependency(dep);
472 return true;
473 }
474
475 /**
476 * New define dependency.
477 * @param {Range} range range
478 * @param {Range | null} arrayRange array range
479 * @param {Range | null} functionRange function range
480 * @param {Range | null} objectRange object range
481 * @param {string | null} namedModule true, when define is called with a name
482 * @returns {AMDDefineDependency} AMDDefineDependency
483 */
484 newDefineDependency(
485 range,
486 arrayRange,
487 functionRange,
488 objectRange,
489 namedModule
490 ) {
491 return new AMDDefineDependency(
492 range,
493 arrayRange,
494 functionRange,
495 objectRange,
496 namedModule
497 );
498 }
499
500 /**
501 * New require array dependency.
502 * @param {(string | LocalModuleDependency | AMDRequireItemDependency)[]} depsArray deps array
503 * @param {Range} range range
504 * @returns {AMDRequireArrayDependency} AMDRequireArrayDependency
505 */
506 newRequireArrayDependency(depsArray, range) {
507 return new AMDRequireArrayDependency(depsArray, range);
508 }
509
510 /**
511 * New require item dependency.
512 * @param {string} request request
513 * @param {Range=} range range
514 * @returns {AMDRequireItemDependency} AMDRequireItemDependency
515 */
516 newRequireItemDependency(request, range) {
517 return new AMDRequireItemDependency(request, range);
518 }
519}
520
521module.exports = AMDDefineDependencyParserPlugin;
Note: See TracBrowser for help on using the repository browser.