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