UNPKG

70.4 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5"use strict";
6
7// Syntax: https://developer.mozilla.org/en/SpiderMonkey/Parser_API
8
9const acorn = require("acorn");
10const { Tapable, SyncBailHook, HookMap } = require("tapable");
11const util = require("util");
12const vm = require("vm");
13const BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
14const StackedSetMap = require("./util/StackedSetMap");
15
16const acornParser = acorn.Parser;
17
18const joinRanges = (startRange, endRange) => {
19 if (!endRange) return startRange;
20 if (!startRange) return endRange;
21 return [startRange[0], endRange[1]];
22};
23
24const defaultParserOptions = {
25 ranges: true,
26 locations: true,
27 ecmaVersion: 11,
28 sourceType: "module",
29 onComment: null
30};
31
32// regexp to match at least one "magic comment"
33const webpackCommentRegExp = new RegExp(/(^|\W)webpack[A-Z]{1,}[A-Za-z]{1,}:/);
34
35const EMPTY_COMMENT_OPTIONS = {
36 options: null,
37 errors: null
38};
39
40class Parser extends Tapable {
41 constructor(options, sourceType = "auto") {
42 super();
43 this.hooks = {
44 evaluateTypeof: new HookMap(() => new SyncBailHook(["expression"])),
45 evaluate: new HookMap(() => new SyncBailHook(["expression"])),
46 evaluateIdentifier: new HookMap(() => new SyncBailHook(["expression"])),
47 evaluateDefinedIdentifier: new HookMap(
48 () => new SyncBailHook(["expression"])
49 ),
50 evaluateCallExpressionMember: new HookMap(
51 () => new SyncBailHook(["expression", "param"])
52 ),
53 statement: new SyncBailHook(["statement"]),
54 statementIf: new SyncBailHook(["statement"]),
55 label: new HookMap(() => new SyncBailHook(["statement"])),
56 import: new SyncBailHook(["statement", "source"]),
57 importSpecifier: new SyncBailHook([
58 "statement",
59 "source",
60 "exportName",
61 "identifierName"
62 ]),
63 export: new SyncBailHook(["statement"]),
64 exportImport: new SyncBailHook(["statement", "source"]),
65 exportDeclaration: new SyncBailHook(["statement", "declaration"]),
66 exportExpression: new SyncBailHook(["statement", "declaration"]),
67 exportSpecifier: new SyncBailHook([
68 "statement",
69 "identifierName",
70 "exportName",
71 "index"
72 ]),
73 exportImportSpecifier: new SyncBailHook([
74 "statement",
75 "source",
76 "identifierName",
77 "exportName",
78 "index"
79 ]),
80 varDeclaration: new HookMap(() => new SyncBailHook(["declaration"])),
81 varDeclarationLet: new HookMap(() => new SyncBailHook(["declaration"])),
82 varDeclarationConst: new HookMap(() => new SyncBailHook(["declaration"])),
83 varDeclarationVar: new HookMap(() => new SyncBailHook(["declaration"])),
84 canRename: new HookMap(() => new SyncBailHook(["initExpression"])),
85 rename: new HookMap(() => new SyncBailHook(["initExpression"])),
86 assigned: new HookMap(() => new SyncBailHook(["expression"])),
87 assign: new HookMap(() => new SyncBailHook(["expression"])),
88 typeof: new HookMap(() => new SyncBailHook(["expression"])),
89 importCall: new SyncBailHook(["expression"]),
90 call: new HookMap(() => new SyncBailHook(["expression"])),
91 callAnyMember: new HookMap(() => new SyncBailHook(["expression"])),
92 new: new HookMap(() => new SyncBailHook(["expression"])),
93 expression: new HookMap(() => new SyncBailHook(["expression"])),
94 expressionAnyMember: new HookMap(() => new SyncBailHook(["expression"])),
95 expressionConditionalOperator: new SyncBailHook(["expression"]),
96 expressionLogicalOperator: new SyncBailHook(["expression"]),
97 program: new SyncBailHook(["ast", "comments"])
98 };
99 const HOOK_MAP_COMPAT_CONFIG = {
100 evaluateTypeof: /^evaluate typeof (.+)$/,
101 evaluateIdentifier: /^evaluate Identifier (.+)$/,
102 evaluateDefinedIdentifier: /^evaluate defined Identifier (.+)$/,
103 evaluateCallExpressionMember: /^evaluate CallExpression .(.+)$/,
104 evaluate: /^evaluate (.+)$/,
105 label: /^label (.+)$/,
106 varDeclarationLet: /^var-let (.+)$/,
107 varDeclarationConst: /^var-const (.+)$/,
108 varDeclarationVar: /^var-var (.+)$/,
109 varDeclaration: /^var (.+)$/,
110 canRename: /^can-rename (.+)$/,
111 rename: /^rename (.+)$/,
112 typeof: /^typeof (.+)$/,
113 assigned: /^assigned (.+)$/,
114 assign: /^assign (.+)$/,
115 callAnyMember: /^call (.+)\.\*$/,
116 call: /^call (.+)$/,
117 new: /^new (.+)$/,
118 expressionConditionalOperator: /^expression \?:$/,
119 expressionAnyMember: /^expression (.+)\.\*$/,
120 expression: /^expression (.+)$/
121 };
122 this._pluginCompat.tap("Parser", options => {
123 for (const name of Object.keys(HOOK_MAP_COMPAT_CONFIG)) {
124 const regexp = HOOK_MAP_COMPAT_CONFIG[name];
125 const match = regexp.exec(options.name);
126 if (match) {
127 if (match[1]) {
128 this.hooks[name].tap(
129 match[1],
130 options.fn.name || "unnamed compat plugin",
131 options.fn.bind(this)
132 );
133 } else {
134 this.hooks[name].tap(
135 options.fn.name || "unnamed compat plugin",
136 options.fn.bind(this)
137 );
138 }
139 return true;
140 }
141 }
142 });
143 this.options = options;
144 this.sourceType = sourceType;
145 this.scope = undefined;
146 this.state = undefined;
147 this.comments = undefined;
148 this.initializeEvaluating();
149 }
150
151 initializeEvaluating() {
152 this.hooks.evaluate.for("Literal").tap("Parser", expr => {
153 switch (typeof expr.value) {
154 case "number":
155 return new BasicEvaluatedExpression()
156 .setNumber(expr.value)
157 .setRange(expr.range);
158 case "string":
159 return new BasicEvaluatedExpression()
160 .setString(expr.value)
161 .setRange(expr.range);
162 case "boolean":
163 return new BasicEvaluatedExpression()
164 .setBoolean(expr.value)
165 .setRange(expr.range);
166 }
167 if (expr.value === null) {
168 return new BasicEvaluatedExpression().setNull().setRange(expr.range);
169 }
170 if (expr.value instanceof RegExp) {
171 return new BasicEvaluatedExpression()
172 .setRegExp(expr.value)
173 .setRange(expr.range);
174 }
175 });
176 this.hooks.evaluate.for("LogicalExpression").tap("Parser", expr => {
177 let left;
178 let leftAsBool;
179 let right;
180 if (expr.operator === "&&") {
181 left = this.evaluateExpression(expr.left);
182 leftAsBool = left && left.asBool();
183 if (leftAsBool === false) return left.setRange(expr.range);
184 if (leftAsBool !== true) return;
185 right = this.evaluateExpression(expr.right);
186 return right.setRange(expr.range);
187 } else if (expr.operator === "||") {
188 left = this.evaluateExpression(expr.left);
189 leftAsBool = left && left.asBool();
190 if (leftAsBool === true) return left.setRange(expr.range);
191 if (leftAsBool !== false) return;
192 right = this.evaluateExpression(expr.right);
193 return right.setRange(expr.range);
194 }
195 });
196 this.hooks.evaluate.for("BinaryExpression").tap("Parser", expr => {
197 let left;
198 let right;
199 let res;
200 if (expr.operator === "+") {
201 left = this.evaluateExpression(expr.left);
202 right = this.evaluateExpression(expr.right);
203 if (!left || !right) return;
204 res = new BasicEvaluatedExpression();
205 if (left.isString()) {
206 if (right.isString()) {
207 res.setString(left.string + right.string);
208 } else if (right.isNumber()) {
209 res.setString(left.string + right.number);
210 } else if (
211 right.isWrapped() &&
212 right.prefix &&
213 right.prefix.isString()
214 ) {
215 // "left" + ("prefix" + inner + "postfix")
216 // => ("leftprefix" + inner + "postfix")
217 res.setWrapped(
218 new BasicEvaluatedExpression()
219 .setString(left.string + right.prefix.string)
220 .setRange(joinRanges(left.range, right.prefix.range)),
221 right.postfix,
222 right.wrappedInnerExpressions
223 );
224 } else if (right.isWrapped()) {
225 // "left" + ([null] + inner + "postfix")
226 // => ("left" + inner + "postfix")
227 res.setWrapped(left, right.postfix, right.wrappedInnerExpressions);
228 } else {
229 // "left" + expr
230 // => ("left" + expr + "")
231 res.setWrapped(left, null, [right]);
232 }
233 } else if (left.isNumber()) {
234 if (right.isString()) {
235 res.setString(left.number + right.string);
236 } else if (right.isNumber()) {
237 res.setNumber(left.number + right.number);
238 } else {
239 return;
240 }
241 } else if (left.isWrapped()) {
242 if (left.postfix && left.postfix.isString() && right.isString()) {
243 // ("prefix" + inner + "postfix") + "right"
244 // => ("prefix" + inner + "postfixright")
245 res.setWrapped(
246 left.prefix,
247 new BasicEvaluatedExpression()
248 .setString(left.postfix.string + right.string)
249 .setRange(joinRanges(left.postfix.range, right.range)),
250 left.wrappedInnerExpressions
251 );
252 } else if (
253 left.postfix &&
254 left.postfix.isString() &&
255 right.isNumber()
256 ) {
257 // ("prefix" + inner + "postfix") + 123
258 // => ("prefix" + inner + "postfix123")
259 res.setWrapped(
260 left.prefix,
261 new BasicEvaluatedExpression()
262 .setString(left.postfix.string + right.number)
263 .setRange(joinRanges(left.postfix.range, right.range)),
264 left.wrappedInnerExpressions
265 );
266 } else if (right.isString()) {
267 // ("prefix" + inner + [null]) + "right"
268 // => ("prefix" + inner + "right")
269 res.setWrapped(left.prefix, right, left.wrappedInnerExpressions);
270 } else if (right.isNumber()) {
271 // ("prefix" + inner + [null]) + 123
272 // => ("prefix" + inner + "123")
273 res.setWrapped(
274 left.prefix,
275 new BasicEvaluatedExpression()
276 .setString(right.number + "")
277 .setRange(right.range),
278 left.wrappedInnerExpressions
279 );
280 } else if (right.isWrapped()) {
281 // ("prefix1" + inner1 + "postfix1") + ("prefix2" + inner2 + "postfix2")
282 // ("prefix1" + inner1 + "postfix1" + "prefix2" + inner2 + "postfix2")
283 res.setWrapped(
284 left.prefix,
285 right.postfix,
286 left.wrappedInnerExpressions &&
287 right.wrappedInnerExpressions &&
288 left.wrappedInnerExpressions
289 .concat(left.postfix ? [left.postfix] : [])
290 .concat(right.prefix ? [right.prefix] : [])
291 .concat(right.wrappedInnerExpressions)
292 );
293 } else {
294 // ("prefix" + inner + postfix) + expr
295 // => ("prefix" + inner + postfix + expr + [null])
296 res.setWrapped(
297 left.prefix,
298 null,
299 left.wrappedInnerExpressions &&
300 left.wrappedInnerExpressions.concat(
301 left.postfix ? [left.postfix, right] : [right]
302 )
303 );
304 }
305 } else {
306 if (right.isString()) {
307 // left + "right"
308 // => ([null] + left + "right")
309 res.setWrapped(null, right, [left]);
310 } else if (right.isWrapped()) {
311 // left + (prefix + inner + "postfix")
312 // => ([null] + left + prefix + inner + "postfix")
313 res.setWrapped(
314 null,
315 right.postfix,
316 right.wrappedInnerExpressions &&
317 (right.prefix ? [left, right.prefix] : [left]).concat(
318 right.wrappedInnerExpressions
319 )
320 );
321 } else {
322 return;
323 }
324 }
325 res.setRange(expr.range);
326 return res;
327 } else if (expr.operator === "-") {
328 left = this.evaluateExpression(expr.left);
329 right = this.evaluateExpression(expr.right);
330 if (!left || !right) return;
331 if (!left.isNumber() || !right.isNumber()) return;
332 res = new BasicEvaluatedExpression();
333 res.setNumber(left.number - right.number);
334 res.setRange(expr.range);
335 return res;
336 } else if (expr.operator === "*") {
337 left = this.evaluateExpression(expr.left);
338 right = this.evaluateExpression(expr.right);
339 if (!left || !right) return;
340 if (!left.isNumber() || !right.isNumber()) return;
341 res = new BasicEvaluatedExpression();
342 res.setNumber(left.number * right.number);
343 res.setRange(expr.range);
344 return res;
345 } else if (expr.operator === "/") {
346 left = this.evaluateExpression(expr.left);
347 right = this.evaluateExpression(expr.right);
348 if (!left || !right) return;
349 if (!left.isNumber() || !right.isNumber()) return;
350 res = new BasicEvaluatedExpression();
351 res.setNumber(left.number / right.number);
352 res.setRange(expr.range);
353 return res;
354 } else if (expr.operator === "**") {
355 left = this.evaluateExpression(expr.left);
356 right = this.evaluateExpression(expr.right);
357 if (!left || !right) return;
358 if (!left.isNumber() || !right.isNumber()) return;
359 res = new BasicEvaluatedExpression();
360 res.setNumber(Math.pow(left.number, right.number));
361 res.setRange(expr.range);
362 return res;
363 } else if (expr.operator === "==" || expr.operator === "===") {
364 left = this.evaluateExpression(expr.left);
365 right = this.evaluateExpression(expr.right);
366 if (!left || !right) return;
367 res = new BasicEvaluatedExpression();
368 res.setRange(expr.range);
369 if (left.isString() && right.isString()) {
370 return res.setBoolean(left.string === right.string);
371 } else if (left.isNumber() && right.isNumber()) {
372 return res.setBoolean(left.number === right.number);
373 } else if (left.isBoolean() && right.isBoolean()) {
374 return res.setBoolean(left.bool === right.bool);
375 }
376 } else if (expr.operator === "!=" || expr.operator === "!==") {
377 left = this.evaluateExpression(expr.left);
378 right = this.evaluateExpression(expr.right);
379 if (!left || !right) return;
380 res = new BasicEvaluatedExpression();
381 res.setRange(expr.range);
382 if (left.isString() && right.isString()) {
383 return res.setBoolean(left.string !== right.string);
384 } else if (left.isNumber() && right.isNumber()) {
385 return res.setBoolean(left.number !== right.number);
386 } else if (left.isBoolean() && right.isBoolean()) {
387 return res.setBoolean(left.bool !== right.bool);
388 }
389 } else if (expr.operator === "&") {
390 left = this.evaluateExpression(expr.left);
391 right = this.evaluateExpression(expr.right);
392 if (!left || !right) return;
393 if (!left.isNumber() || !right.isNumber()) return;
394 res = new BasicEvaluatedExpression();
395 res.setNumber(left.number & right.number);
396 res.setRange(expr.range);
397 return res;
398 } else if (expr.operator === "|") {
399 left = this.evaluateExpression(expr.left);
400 right = this.evaluateExpression(expr.right);
401 if (!left || !right) return;
402 if (!left.isNumber() || !right.isNumber()) return;
403 res = new BasicEvaluatedExpression();
404 res.setNumber(left.number | right.number);
405 res.setRange(expr.range);
406 return res;
407 } else if (expr.operator === "^") {
408 left = this.evaluateExpression(expr.left);
409 right = this.evaluateExpression(expr.right);
410 if (!left || !right) return;
411 if (!left.isNumber() || !right.isNumber()) return;
412 res = new BasicEvaluatedExpression();
413 res.setNumber(left.number ^ right.number);
414 res.setRange(expr.range);
415 return res;
416 } else if (expr.operator === ">>>") {
417 left = this.evaluateExpression(expr.left);
418 right = this.evaluateExpression(expr.right);
419 if (!left || !right) return;
420 if (!left.isNumber() || !right.isNumber()) return;
421 res = new BasicEvaluatedExpression();
422 res.setNumber(left.number >>> right.number);
423 res.setRange(expr.range);
424 return res;
425 } else if (expr.operator === ">>") {
426 left = this.evaluateExpression(expr.left);
427 right = this.evaluateExpression(expr.right);
428 if (!left || !right) return;
429 if (!left.isNumber() || !right.isNumber()) return;
430 res = new BasicEvaluatedExpression();
431 res.setNumber(left.number >> right.number);
432 res.setRange(expr.range);
433 return res;
434 } else if (expr.operator === "<<") {
435 left = this.evaluateExpression(expr.left);
436 right = this.evaluateExpression(expr.right);
437 if (!left || !right) return;
438 if (!left.isNumber() || !right.isNumber()) return;
439 res = new BasicEvaluatedExpression();
440 res.setNumber(left.number << right.number);
441 res.setRange(expr.range);
442 return res;
443 }
444 });
445 this.hooks.evaluate.for("UnaryExpression").tap("Parser", expr => {
446 if (expr.operator === "typeof") {
447 let res;
448 let name;
449 if (expr.argument.type === "Identifier") {
450 name =
451 this.scope.renames.get(expr.argument.name) || expr.argument.name;
452 if (!this.scope.definitions.has(name)) {
453 const hook = this.hooks.evaluateTypeof.get(name);
454 if (hook !== undefined) {
455 res = hook.call(expr);
456 if (res !== undefined) return res;
457 }
458 }
459 }
460 if (expr.argument.type === "MemberExpression") {
461 const exprName = this.getNameForExpression(expr.argument);
462 if (exprName && exprName.free) {
463 const hook = this.hooks.evaluateTypeof.get(exprName.name);
464 if (hook !== undefined) {
465 res = hook.call(expr);
466 if (res !== undefined) return res;
467 }
468 }
469 }
470 if (expr.argument.type === "FunctionExpression") {
471 return new BasicEvaluatedExpression()
472 .setString("function")
473 .setRange(expr.range);
474 }
475 const arg = this.evaluateExpression(expr.argument);
476 if (arg.isString() || arg.isWrapped()) {
477 return new BasicEvaluatedExpression()
478 .setString("string")
479 .setRange(expr.range);
480 }
481 if (arg.isNumber()) {
482 return new BasicEvaluatedExpression()
483 .setString("number")
484 .setRange(expr.range);
485 }
486 if (arg.isBoolean()) {
487 return new BasicEvaluatedExpression()
488 .setString("boolean")
489 .setRange(expr.range);
490 }
491 if (arg.isArray() || arg.isConstArray() || arg.isRegExp()) {
492 return new BasicEvaluatedExpression()
493 .setString("object")
494 .setRange(expr.range);
495 }
496 } else if (expr.operator === "!") {
497 const argument = this.evaluateExpression(expr.argument);
498 if (!argument) return;
499 if (argument.isBoolean()) {
500 return new BasicEvaluatedExpression()
501 .setBoolean(!argument.bool)
502 .setRange(expr.range);
503 }
504 if (argument.isTruthy()) {
505 return new BasicEvaluatedExpression()
506 .setBoolean(false)
507 .setRange(expr.range);
508 }
509 if (argument.isFalsy()) {
510 return new BasicEvaluatedExpression()
511 .setBoolean(true)
512 .setRange(expr.range);
513 }
514 if (argument.isString()) {
515 return new BasicEvaluatedExpression()
516 .setBoolean(!argument.string)
517 .setRange(expr.range);
518 }
519 if (argument.isNumber()) {
520 return new BasicEvaluatedExpression()
521 .setBoolean(!argument.number)
522 .setRange(expr.range);
523 }
524 } else if (expr.operator === "~") {
525 const argument = this.evaluateExpression(expr.argument);
526 if (!argument) return;
527 if (!argument.isNumber()) return;
528 const res = new BasicEvaluatedExpression();
529 res.setNumber(~argument.number);
530 res.setRange(expr.range);
531 return res;
532 }
533 });
534 this.hooks.evaluateTypeof.for("undefined").tap("Parser", expr => {
535 return new BasicEvaluatedExpression()
536 .setString("undefined")
537 .setRange(expr.range);
538 });
539 this.hooks.evaluate.for("Identifier").tap("Parser", expr => {
540 const name = this.scope.renames.get(expr.name) || expr.name;
541 if (!this.scope.definitions.has(expr.name)) {
542 const hook = this.hooks.evaluateIdentifier.get(name);
543 if (hook !== undefined) {
544 const result = hook.call(expr);
545 if (result) return result;
546 }
547 return new BasicEvaluatedExpression()
548 .setIdentifier(name)
549 .setRange(expr.range);
550 } else {
551 const hook = this.hooks.evaluateDefinedIdentifier.get(name);
552 if (hook !== undefined) {
553 return hook.call(expr);
554 }
555 }
556 });
557 this.hooks.evaluate.for("ThisExpression").tap("Parser", expr => {
558 const name = this.scope.renames.get("this");
559 if (name) {
560 const hook = this.hooks.evaluateIdentifier.get(name);
561 if (hook !== undefined) {
562 const result = hook.call(expr);
563 if (result) return result;
564 }
565 return new BasicEvaluatedExpression()
566 .setIdentifier(name)
567 .setRange(expr.range);
568 }
569 });
570 this.hooks.evaluate.for("MemberExpression").tap("Parser", expression => {
571 let exprName = this.getNameForExpression(expression);
572 if (exprName) {
573 if (exprName.free) {
574 const hook = this.hooks.evaluateIdentifier.get(exprName.name);
575 if (hook !== undefined) {
576 const result = hook.call(expression);
577 if (result) return result;
578 }
579 return new BasicEvaluatedExpression()
580 .setIdentifier(exprName.name)
581 .setRange(expression.range);
582 } else {
583 const hook = this.hooks.evaluateDefinedIdentifier.get(exprName.name);
584 if (hook !== undefined) {
585 return hook.call(expression);
586 }
587 }
588 }
589 });
590 this.hooks.evaluate.for("CallExpression").tap("Parser", expr => {
591 if (expr.callee.type !== "MemberExpression") return;
592 if (
593 expr.callee.property.type !==
594 (expr.callee.computed ? "Literal" : "Identifier")
595 )
596 return;
597 const param = this.evaluateExpression(expr.callee.object);
598 if (!param) return;
599 const property = expr.callee.property.name || expr.callee.property.value;
600 const hook = this.hooks.evaluateCallExpressionMember.get(property);
601 if (hook !== undefined) {
602 return hook.call(expr, param);
603 }
604 });
605 this.hooks.evaluateCallExpressionMember
606 .for("replace")
607 .tap("Parser", (expr, param) => {
608 if (!param.isString()) return;
609 if (expr.arguments.length !== 2) return;
610 let arg1 = this.evaluateExpression(expr.arguments[0]);
611 let arg2 = this.evaluateExpression(expr.arguments[1]);
612 if (!arg1.isString() && !arg1.isRegExp()) return;
613 arg1 = arg1.regExp || arg1.string;
614 if (!arg2.isString()) return;
615 arg2 = arg2.string;
616 return new BasicEvaluatedExpression()
617 .setString(param.string.replace(arg1, arg2))
618 .setRange(expr.range);
619 });
620 ["substr", "substring"].forEach(fn => {
621 this.hooks.evaluateCallExpressionMember
622 .for(fn)
623 .tap("Parser", (expr, param) => {
624 if (!param.isString()) return;
625 let arg1;
626 let result,
627 str = param.string;
628 switch (expr.arguments.length) {
629 case 1:
630 arg1 = this.evaluateExpression(expr.arguments[0]);
631 if (!arg1.isNumber()) return;
632 result = str[fn](arg1.number);
633 break;
634 case 2: {
635 arg1 = this.evaluateExpression(expr.arguments[0]);
636 const arg2 = this.evaluateExpression(expr.arguments[1]);
637 if (!arg1.isNumber()) return;
638 if (!arg2.isNumber()) return;
639 result = str[fn](arg1.number, arg2.number);
640 break;
641 }
642 default:
643 return;
644 }
645 return new BasicEvaluatedExpression()
646 .setString(result)
647 .setRange(expr.range);
648 });
649 });
650
651 /**
652 * @param {string} kind "cooked" | "raw"
653 * @param {TODO} templateLiteralExpr TemplateLiteral expr
654 * @returns {{quasis: BasicEvaluatedExpression[], parts: BasicEvaluatedExpression[]}} Simplified template
655 */
656 const getSimplifiedTemplateResult = (kind, templateLiteralExpr) => {
657 const quasis = [];
658 const parts = [];
659
660 for (let i = 0; i < templateLiteralExpr.quasis.length; i++) {
661 const quasiExpr = templateLiteralExpr.quasis[i];
662 const quasi = quasiExpr.value[kind];
663
664 if (i > 0) {
665 const prevExpr = parts[parts.length - 1];
666 const expr = this.evaluateExpression(
667 templateLiteralExpr.expressions[i - 1]
668 );
669 const exprAsString = expr.asString();
670 if (typeof exprAsString === "string") {
671 // We can merge quasi + expr + quasi when expr
672 // is a const string
673
674 prevExpr.setString(prevExpr.string + exprAsString + quasi);
675 prevExpr.setRange([prevExpr.range[0], quasiExpr.range[1]]);
676 // We unset the expression as it doesn't match to a single expression
677 prevExpr.setExpression(undefined);
678 continue;
679 }
680 parts.push(expr);
681 }
682
683 const part = new BasicEvaluatedExpression()
684 .setString(quasi)
685 .setRange(quasiExpr.range)
686 .setExpression(quasiExpr);
687 quasis.push(part);
688 parts.push(part);
689 }
690 return {
691 quasis,
692 parts
693 };
694 };
695
696 this.hooks.evaluate.for("TemplateLiteral").tap("Parser", node => {
697 const { quasis, parts } = getSimplifiedTemplateResult("cooked", node);
698 if (parts.length === 1) {
699 return parts[0].setRange(node.range);
700 }
701 return new BasicEvaluatedExpression()
702 .setTemplateString(quasis, parts, "cooked")
703 .setRange(node.range);
704 });
705 this.hooks.evaluate.for("TaggedTemplateExpression").tap("Parser", node => {
706 if (this.evaluateExpression(node.tag).identifier !== "String.raw") return;
707 const { quasis, parts } = getSimplifiedTemplateResult("raw", node.quasi);
708 if (parts.length === 1) {
709 return parts[0].setRange(node.range);
710 }
711 return new BasicEvaluatedExpression()
712 .setTemplateString(quasis, parts, "raw")
713 .setRange(node.range);
714 });
715
716 this.hooks.evaluateCallExpressionMember
717 .for("concat")
718 .tap("Parser", (expr, param) => {
719 if (!param.isString() && !param.isWrapped()) return;
720
721 let stringSuffix = null;
722 let hasUnknownParams = false;
723 for (let i = expr.arguments.length - 1; i >= 0; i--) {
724 const argExpr = this.evaluateExpression(expr.arguments[i]);
725 if (!argExpr.isString() && !argExpr.isNumber()) {
726 hasUnknownParams = true;
727 break;
728 }
729
730 const value = argExpr.isString()
731 ? argExpr.string
732 : "" + argExpr.number;
733
734 const newString = value + (stringSuffix ? stringSuffix.string : "");
735 const newRange = [
736 argExpr.range[0],
737 (stringSuffix || argExpr).range[1]
738 ];
739 stringSuffix = new BasicEvaluatedExpression()
740 .setString(newString)
741 .setRange(newRange);
742 }
743
744 if (hasUnknownParams) {
745 const prefix = param.isString() ? param : param.prefix;
746 return new BasicEvaluatedExpression()
747 .setWrapped(prefix, stringSuffix)
748 .setRange(expr.range);
749 } else if (param.isWrapped()) {
750 const postfix = stringSuffix || param.postfix;
751 return new BasicEvaluatedExpression()
752 .setWrapped(param.prefix, postfix)
753 .setRange(expr.range);
754 } else {
755 const newString =
756 param.string + (stringSuffix ? stringSuffix.string : "");
757 return new BasicEvaluatedExpression()
758 .setString(newString)
759 .setRange(expr.range);
760 }
761 });
762 this.hooks.evaluateCallExpressionMember
763 .for("split")
764 .tap("Parser", (expr, param) => {
765 if (!param.isString()) return;
766 if (expr.arguments.length !== 1) return;
767 let result;
768 const arg = this.evaluateExpression(expr.arguments[0]);
769 if (arg.isString()) {
770 result = param.string.split(arg.string);
771 } else if (arg.isRegExp()) {
772 result = param.string.split(arg.regExp);
773 } else {
774 return;
775 }
776 return new BasicEvaluatedExpression()
777 .setArray(result)
778 .setRange(expr.range);
779 });
780 this.hooks.evaluate.for("ConditionalExpression").tap("Parser", expr => {
781 const condition = this.evaluateExpression(expr.test);
782 const conditionValue = condition.asBool();
783 let res;
784 if (conditionValue === undefined) {
785 const consequent = this.evaluateExpression(expr.consequent);
786 const alternate = this.evaluateExpression(expr.alternate);
787 if (!consequent || !alternate) return;
788 res = new BasicEvaluatedExpression();
789 if (consequent.isConditional()) {
790 res.setOptions(consequent.options);
791 } else {
792 res.setOptions([consequent]);
793 }
794 if (alternate.isConditional()) {
795 res.addOptions(alternate.options);
796 } else {
797 res.addOptions([alternate]);
798 }
799 } else {
800 res = this.evaluateExpression(
801 conditionValue ? expr.consequent : expr.alternate
802 );
803 }
804 res.setRange(expr.range);
805 return res;
806 });
807 this.hooks.evaluate.for("ArrayExpression").tap("Parser", expr => {
808 const items = expr.elements.map(element => {
809 return element !== null && this.evaluateExpression(element);
810 });
811 if (!items.every(Boolean)) return;
812 return new BasicEvaluatedExpression()
813 .setItems(items)
814 .setRange(expr.range);
815 });
816 }
817
818 getRenameIdentifier(expr) {
819 const result = this.evaluateExpression(expr);
820 if (result && result.isIdentifier()) {
821 return result.identifier;
822 }
823 }
824
825 walkClass(classy) {
826 if (classy.superClass) this.walkExpression(classy.superClass);
827 if (classy.body && classy.body.type === "ClassBody") {
828 const wasTopLevel = this.scope.topLevelScope;
829 this.scope.topLevelScope = false;
830 for (const methodDefinition of classy.body.body) {
831 if (methodDefinition.type === "MethodDefinition") {
832 this.walkMethodDefinition(methodDefinition);
833 }
834 }
835 this.scope.topLevelScope = wasTopLevel;
836 }
837 }
838
839 walkMethodDefinition(methodDefinition) {
840 if (methodDefinition.computed && methodDefinition.key) {
841 this.walkExpression(methodDefinition.key);
842 }
843 if (methodDefinition.value) {
844 this.walkExpression(methodDefinition.value);
845 }
846 }
847
848 // Prewalking iterates the scope for variable declarations
849 prewalkStatements(statements) {
850 for (let index = 0, len = statements.length; index < len; index++) {
851 const statement = statements[index];
852 this.prewalkStatement(statement);
853 }
854 }
855
856 // Block-Prewalking iterates the scope for block variable declarations
857 blockPrewalkStatements(statements) {
858 for (let index = 0, len = statements.length; index < len; index++) {
859 const statement = statements[index];
860 this.blockPrewalkStatement(statement);
861 }
862 }
863
864 // Walking iterates the statements and expressions and processes them
865 walkStatements(statements) {
866 for (let index = 0, len = statements.length; index < len; index++) {
867 const statement = statements[index];
868 this.walkStatement(statement);
869 }
870 }
871
872 prewalkStatement(statement) {
873 switch (statement.type) {
874 case "BlockStatement":
875 this.prewalkBlockStatement(statement);
876 break;
877 case "DoWhileStatement":
878 this.prewalkDoWhileStatement(statement);
879 break;
880 case "ExportAllDeclaration":
881 this.prewalkExportAllDeclaration(statement);
882 break;
883 case "ExportDefaultDeclaration":
884 this.prewalkExportDefaultDeclaration(statement);
885 break;
886 case "ExportNamedDeclaration":
887 this.prewalkExportNamedDeclaration(statement);
888 break;
889 case "ForInStatement":
890 this.prewalkForInStatement(statement);
891 break;
892 case "ForOfStatement":
893 this.prewalkForOfStatement(statement);
894 break;
895 case "ForStatement":
896 this.prewalkForStatement(statement);
897 break;
898 case "FunctionDeclaration":
899 this.prewalkFunctionDeclaration(statement);
900 break;
901 case "IfStatement":
902 this.prewalkIfStatement(statement);
903 break;
904 case "ImportDeclaration":
905 this.prewalkImportDeclaration(statement);
906 break;
907 case "LabeledStatement":
908 this.prewalkLabeledStatement(statement);
909 break;
910 case "SwitchStatement":
911 this.prewalkSwitchStatement(statement);
912 break;
913 case "TryStatement":
914 this.prewalkTryStatement(statement);
915 break;
916 case "VariableDeclaration":
917 this.prewalkVariableDeclaration(statement);
918 break;
919 case "WhileStatement":
920 this.prewalkWhileStatement(statement);
921 break;
922 case "WithStatement":
923 this.prewalkWithStatement(statement);
924 break;
925 }
926 }
927
928 blockPrewalkStatement(statement) {
929 switch (statement.type) {
930 case "VariableDeclaration":
931 this.blockPrewalkVariableDeclaration(statement);
932 break;
933 case "ExportDefaultDeclaration":
934 this.blockPrewalkExportDefaultDeclaration(statement);
935 break;
936 case "ExportNamedDeclaration":
937 this.blockPrewalkExportNamedDeclaration(statement);
938 break;
939 case "ClassDeclaration":
940 this.blockPrewalkClassDeclaration(statement);
941 break;
942 }
943 }
944
945 walkStatement(statement) {
946 if (this.hooks.statement.call(statement) !== undefined) return;
947 switch (statement.type) {
948 case "BlockStatement":
949 this.walkBlockStatement(statement);
950 break;
951 case "ClassDeclaration":
952 this.walkClassDeclaration(statement);
953 break;
954 case "DoWhileStatement":
955 this.walkDoWhileStatement(statement);
956 break;
957 case "ExportDefaultDeclaration":
958 this.walkExportDefaultDeclaration(statement);
959 break;
960 case "ExportNamedDeclaration":
961 this.walkExportNamedDeclaration(statement);
962 break;
963 case "ExpressionStatement":
964 this.walkExpressionStatement(statement);
965 break;
966 case "ForInStatement":
967 this.walkForInStatement(statement);
968 break;
969 case "ForOfStatement":
970 this.walkForOfStatement(statement);
971 break;
972 case "ForStatement":
973 this.walkForStatement(statement);
974 break;
975 case "FunctionDeclaration":
976 this.walkFunctionDeclaration(statement);
977 break;
978 case "IfStatement":
979 this.walkIfStatement(statement);
980 break;
981 case "LabeledStatement":
982 this.walkLabeledStatement(statement);
983 break;
984 case "ReturnStatement":
985 this.walkReturnStatement(statement);
986 break;
987 case "SwitchStatement":
988 this.walkSwitchStatement(statement);
989 break;
990 case "ThrowStatement":
991 this.walkThrowStatement(statement);
992 break;
993 case "TryStatement":
994 this.walkTryStatement(statement);
995 break;
996 case "VariableDeclaration":
997 this.walkVariableDeclaration(statement);
998 break;
999 case "WhileStatement":
1000 this.walkWhileStatement(statement);
1001 break;
1002 case "WithStatement":
1003 this.walkWithStatement(statement);
1004 break;
1005 }
1006 }
1007
1008 // Real Statements
1009 prewalkBlockStatement(statement) {
1010 this.prewalkStatements(statement.body);
1011 }
1012
1013 walkBlockStatement(statement) {
1014 this.inBlockScope(() => {
1015 const body = statement.body;
1016 this.blockPrewalkStatements(body);
1017 this.walkStatements(body);
1018 });
1019 }
1020
1021 walkExpressionStatement(statement) {
1022 this.walkExpression(statement.expression);
1023 }
1024
1025 prewalkIfStatement(statement) {
1026 this.prewalkStatement(statement.consequent);
1027 if (statement.alternate) {
1028 this.prewalkStatement(statement.alternate);
1029 }
1030 }
1031
1032 walkIfStatement(statement) {
1033 const result = this.hooks.statementIf.call(statement);
1034 if (result === undefined) {
1035 this.walkExpression(statement.test);
1036 this.walkStatement(statement.consequent);
1037 if (statement.alternate) {
1038 this.walkStatement(statement.alternate);
1039 }
1040 } else {
1041 if (result) {
1042 this.walkStatement(statement.consequent);
1043 } else if (statement.alternate) {
1044 this.walkStatement(statement.alternate);
1045 }
1046 }
1047 }
1048
1049 prewalkLabeledStatement(statement) {
1050 this.prewalkStatement(statement.body);
1051 }
1052
1053 walkLabeledStatement(statement) {
1054 const hook = this.hooks.label.get(statement.label.name);
1055 if (hook !== undefined) {
1056 const result = hook.call(statement);
1057 if (result === true) return;
1058 }
1059 this.walkStatement(statement.body);
1060 }
1061
1062 prewalkWithStatement(statement) {
1063 this.prewalkStatement(statement.body);
1064 }
1065
1066 walkWithStatement(statement) {
1067 this.walkExpression(statement.object);
1068 this.walkStatement(statement.body);
1069 }
1070
1071 prewalkSwitchStatement(statement) {
1072 this.prewalkSwitchCases(statement.cases);
1073 }
1074
1075 walkSwitchStatement(statement) {
1076 this.walkExpression(statement.discriminant);
1077 this.walkSwitchCases(statement.cases);
1078 }
1079
1080 walkTerminatingStatement(statement) {
1081 if (statement.argument) this.walkExpression(statement.argument);
1082 }
1083
1084 walkReturnStatement(statement) {
1085 this.walkTerminatingStatement(statement);
1086 }
1087
1088 walkThrowStatement(statement) {
1089 this.walkTerminatingStatement(statement);
1090 }
1091
1092 prewalkTryStatement(statement) {
1093 this.prewalkStatement(statement.block);
1094 }
1095
1096 walkTryStatement(statement) {
1097 if (this.scope.inTry) {
1098 this.walkStatement(statement.block);
1099 } else {
1100 this.scope.inTry = true;
1101 this.walkStatement(statement.block);
1102 this.scope.inTry = false;
1103 }
1104 if (statement.handler) this.walkCatchClause(statement.handler);
1105 if (statement.finalizer) this.walkStatement(statement.finalizer);
1106 }
1107
1108 prewalkWhileStatement(statement) {
1109 this.prewalkStatement(statement.body);
1110 }
1111
1112 walkWhileStatement(statement) {
1113 this.walkExpression(statement.test);
1114 this.walkStatement(statement.body);
1115 }
1116
1117 prewalkDoWhileStatement(statement) {
1118 this.prewalkStatement(statement.body);
1119 }
1120
1121 walkDoWhileStatement(statement) {
1122 this.walkStatement(statement.body);
1123 this.walkExpression(statement.test);
1124 }
1125
1126 prewalkForStatement(statement) {
1127 if (statement.init) {
1128 if (statement.init.type === "VariableDeclaration") {
1129 this.prewalkStatement(statement.init);
1130 }
1131 }
1132 this.prewalkStatement(statement.body);
1133 }
1134
1135 walkForStatement(statement) {
1136 this.inBlockScope(() => {
1137 if (statement.init) {
1138 if (statement.init.type === "VariableDeclaration") {
1139 this.blockPrewalkVariableDeclaration(statement.init);
1140 this.walkStatement(statement.init);
1141 } else {
1142 this.walkExpression(statement.init);
1143 }
1144 }
1145 if (statement.test) {
1146 this.walkExpression(statement.test);
1147 }
1148 if (statement.update) {
1149 this.walkExpression(statement.update);
1150 }
1151 const body = statement.body;
1152 if (body.type === "BlockStatement") {
1153 // no need to add additional scope
1154 this.blockPrewalkStatements(body.body);
1155 this.walkStatements(body.body);
1156 } else {
1157 this.walkStatement(body);
1158 }
1159 });
1160 }
1161
1162 prewalkForInStatement(statement) {
1163 if (statement.left.type === "VariableDeclaration") {
1164 this.prewalkVariableDeclaration(statement.left);
1165 }
1166 this.prewalkStatement(statement.body);
1167 }
1168
1169 walkForInStatement(statement) {
1170 this.inBlockScope(() => {
1171 if (statement.left.type === "VariableDeclaration") {
1172 this.blockPrewalkVariableDeclaration(statement.left);
1173 this.walkVariableDeclaration(statement.left);
1174 } else {
1175 this.walkPattern(statement.left);
1176 }
1177 this.walkExpression(statement.right);
1178 const body = statement.body;
1179 if (body.type === "BlockStatement") {
1180 // no need to add additional scope
1181 this.blockPrewalkStatements(body.body);
1182 this.walkStatements(body.body);
1183 } else {
1184 this.walkStatement(body);
1185 }
1186 });
1187 }
1188
1189 prewalkForOfStatement(statement) {
1190 if (statement.left.type === "VariableDeclaration") {
1191 this.prewalkVariableDeclaration(statement.left);
1192 }
1193 this.prewalkStatement(statement.body);
1194 }
1195
1196 walkForOfStatement(statement) {
1197 this.inBlockScope(() => {
1198 if (statement.left.type === "VariableDeclaration") {
1199 this.blockPrewalkVariableDeclaration(statement.left);
1200 this.walkVariableDeclaration(statement.left);
1201 } else {
1202 this.walkPattern(statement.left);
1203 }
1204 this.walkExpression(statement.right);
1205 const body = statement.body;
1206 if (body.type === "BlockStatement") {
1207 // no need to add additional scope
1208 this.blockPrewalkStatements(body.body);
1209 this.walkStatements(body.body);
1210 } else {
1211 this.walkStatement(body);
1212 }
1213 });
1214 }
1215
1216 // Declarations
1217 prewalkFunctionDeclaration(statement) {
1218 if (statement.id) {
1219 this.scope.renames.set(statement.id.name, null);
1220 this.scope.definitions.add(statement.id.name);
1221 }
1222 }
1223
1224 walkFunctionDeclaration(statement) {
1225 const wasTopLevel = this.scope.topLevelScope;
1226 this.scope.topLevelScope = false;
1227 this.inFunctionScope(true, statement.params, () => {
1228 for (const param of statement.params) {
1229 this.walkPattern(param);
1230 }
1231 if (statement.body.type === "BlockStatement") {
1232 this.detectStrictMode(statement.body.body);
1233 this.prewalkStatement(statement.body);
1234 this.walkStatement(statement.body);
1235 } else {
1236 this.walkExpression(statement.body);
1237 }
1238 });
1239 this.scope.topLevelScope = wasTopLevel;
1240 }
1241
1242 prewalkImportDeclaration(statement) {
1243 const source = statement.source.value;
1244 this.hooks.import.call(statement, source);
1245 for (const specifier of statement.specifiers) {
1246 const name = specifier.local.name;
1247 this.scope.renames.set(name, null);
1248 this.scope.definitions.add(name);
1249 switch (specifier.type) {
1250 case "ImportDefaultSpecifier":
1251 this.hooks.importSpecifier.call(statement, source, "default", name);
1252 break;
1253 case "ImportSpecifier":
1254 this.hooks.importSpecifier.call(
1255 statement,
1256 source,
1257 specifier.imported.name,
1258 name
1259 );
1260 break;
1261 case "ImportNamespaceSpecifier":
1262 this.hooks.importSpecifier.call(statement, source, null, name);
1263 break;
1264 }
1265 }
1266 }
1267
1268 enterDeclaration(declaration, onIdent) {
1269 switch (declaration.type) {
1270 case "VariableDeclaration":
1271 for (const declarator of declaration.declarations) {
1272 switch (declarator.type) {
1273 case "VariableDeclarator": {
1274 this.enterPattern(declarator.id, onIdent);
1275 break;
1276 }
1277 }
1278 }
1279 break;
1280 case "FunctionDeclaration":
1281 this.enterPattern(declaration.id, onIdent);
1282 break;
1283 case "ClassDeclaration":
1284 this.enterPattern(declaration.id, onIdent);
1285 break;
1286 }
1287 }
1288
1289 blockPrewalkExportNamedDeclaration(statement) {
1290 if (statement.declaration) {
1291 this.blockPrewalkStatement(statement.declaration);
1292 }
1293 }
1294
1295 prewalkExportNamedDeclaration(statement) {
1296 let source;
1297 if (statement.source) {
1298 source = statement.source.value;
1299 this.hooks.exportImport.call(statement, source);
1300 } else {
1301 this.hooks.export.call(statement);
1302 }
1303 if (statement.declaration) {
1304 if (
1305 !this.hooks.exportDeclaration.call(statement, statement.declaration)
1306 ) {
1307 this.prewalkStatement(statement.declaration);
1308 let index = 0;
1309 this.enterDeclaration(statement.declaration, def => {
1310 this.hooks.exportSpecifier.call(statement, def, def, index++);
1311 });
1312 }
1313 }
1314 if (statement.specifiers) {
1315 for (
1316 let specifierIndex = 0;
1317 specifierIndex < statement.specifiers.length;
1318 specifierIndex++
1319 ) {
1320 const specifier = statement.specifiers[specifierIndex];
1321 switch (specifier.type) {
1322 case "ExportSpecifier": {
1323 const name = specifier.exported.name;
1324 if (source) {
1325 this.hooks.exportImportSpecifier.call(
1326 statement,
1327 source,
1328 specifier.local.name,
1329 name,
1330 specifierIndex
1331 );
1332 } else {
1333 this.hooks.exportSpecifier.call(
1334 statement,
1335 specifier.local.name,
1336 name,
1337 specifierIndex
1338 );
1339 }
1340 break;
1341 }
1342 }
1343 }
1344 }
1345 }
1346
1347 walkExportNamedDeclaration(statement) {
1348 if (statement.declaration) {
1349 this.walkStatement(statement.declaration);
1350 }
1351 }
1352
1353 blockPrewalkExportDefaultDeclaration(statement) {
1354 if (statement.declaration.type === "ClassDeclaration") {
1355 this.blockPrewalkClassDeclaration(statement.declaration);
1356 }
1357 }
1358
1359 prewalkExportDefaultDeclaration(statement) {
1360 this.prewalkStatement(statement.declaration);
1361 if (
1362 statement.declaration.id &&
1363 statement.declaration.type !== "FunctionExpression" &&
1364 statement.declaration.type !== "ClassExpression"
1365 ) {
1366 this.hooks.exportSpecifier.call(
1367 statement,
1368 statement.declaration.id.name,
1369 "default"
1370 );
1371 }
1372 }
1373
1374 walkExportDefaultDeclaration(statement) {
1375 this.hooks.export.call(statement);
1376 if (
1377 statement.declaration.id &&
1378 statement.declaration.type !== "FunctionExpression" &&
1379 statement.declaration.type !== "ClassExpression"
1380 ) {
1381 if (
1382 !this.hooks.exportDeclaration.call(statement, statement.declaration)
1383 ) {
1384 this.walkStatement(statement.declaration);
1385 }
1386 } else {
1387 // Acorn parses `export default function() {}` as `FunctionDeclaration` and
1388 // `export default class {}` as `ClassDeclaration`, both with `id = null`.
1389 // These nodes must be treated as expressions.
1390 if (statement.declaration.type === "FunctionDeclaration") {
1391 this.walkFunctionDeclaration(statement.declaration);
1392 } else if (statement.declaration.type === "ClassDeclaration") {
1393 this.walkClassDeclaration(statement.declaration);
1394 } else {
1395 this.walkExpression(statement.declaration);
1396 }
1397 if (!this.hooks.exportExpression.call(statement, statement.declaration)) {
1398 this.hooks.exportSpecifier.call(
1399 statement,
1400 statement.declaration,
1401 "default"
1402 );
1403 }
1404 }
1405 }
1406
1407 prewalkExportAllDeclaration(statement) {
1408 const source = statement.source.value;
1409 this.hooks.exportImport.call(statement, source);
1410 this.hooks.exportImportSpecifier.call(statement, source, null, null, 0);
1411 }
1412
1413 prewalkVariableDeclaration(statement) {
1414 if (statement.kind !== "var") return;
1415 this._prewalkVariableDeclaration(statement, this.hooks.varDeclarationVar);
1416 }
1417
1418 blockPrewalkVariableDeclaration(statement) {
1419 if (statement.kind === "var") return;
1420 const hookMap =
1421 statement.kind === "const"
1422 ? this.hooks.varDeclarationConst
1423 : this.hooks.varDeclarationLet;
1424 this._prewalkVariableDeclaration(statement, hookMap);
1425 }
1426
1427 _prewalkVariableDeclaration(statement, hookMap) {
1428 for (const declarator of statement.declarations) {
1429 switch (declarator.type) {
1430 case "VariableDeclarator": {
1431 this.enterPattern(declarator.id, (name, decl) => {
1432 let hook = hookMap.get(name);
1433 if (hook === undefined || !hook.call(decl)) {
1434 hook = this.hooks.varDeclaration.get(name);
1435 if (hook === undefined || !hook.call(decl)) {
1436 this.scope.renames.set(name, null);
1437 this.scope.definitions.add(name);
1438 }
1439 }
1440 });
1441 break;
1442 }
1443 }
1444 }
1445 }
1446
1447 walkVariableDeclaration(statement) {
1448 for (const declarator of statement.declarations) {
1449 switch (declarator.type) {
1450 case "VariableDeclarator": {
1451 const renameIdentifier =
1452 declarator.init && this.getRenameIdentifier(declarator.init);
1453 if (renameIdentifier && declarator.id.type === "Identifier") {
1454 const hook = this.hooks.canRename.get(renameIdentifier);
1455 if (hook !== undefined && hook.call(declarator.init)) {
1456 // renaming with "var a = b;"
1457 const hook = this.hooks.rename.get(renameIdentifier);
1458 if (hook === undefined || !hook.call(declarator.init)) {
1459 this.scope.renames.set(
1460 declarator.id.name,
1461 this.scope.renames.get(renameIdentifier) || renameIdentifier
1462 );
1463 this.scope.definitions.delete(declarator.id.name);
1464 }
1465 break;
1466 }
1467 }
1468 this.walkPattern(declarator.id);
1469 if (declarator.init) this.walkExpression(declarator.init);
1470 break;
1471 }
1472 }
1473 }
1474 }
1475
1476 blockPrewalkClassDeclaration(statement) {
1477 if (statement.id) {
1478 this.scope.renames.set(statement.id.name, null);
1479 this.scope.definitions.add(statement.id.name);
1480 }
1481 }
1482
1483 walkClassDeclaration(statement) {
1484 this.walkClass(statement);
1485 }
1486
1487 prewalkSwitchCases(switchCases) {
1488 for (let index = 0, len = switchCases.length; index < len; index++) {
1489 const switchCase = switchCases[index];
1490 this.prewalkStatements(switchCase.consequent);
1491 }
1492 }
1493
1494 walkSwitchCases(switchCases) {
1495 for (let index = 0, len = switchCases.length; index < len; index++) {
1496 const switchCase = switchCases[index];
1497
1498 if (switchCase.test) {
1499 this.walkExpression(switchCase.test);
1500 }
1501 this.walkStatements(switchCase.consequent);
1502 }
1503 }
1504
1505 walkCatchClause(catchClause) {
1506 this.inBlockScope(() => {
1507 // Error binding is optional in catch clause since ECMAScript 2019
1508 if (catchClause.param !== null) {
1509 this.enterPattern(catchClause.param, ident => {
1510 this.scope.renames.set(ident, null);
1511 this.scope.definitions.add(ident);
1512 });
1513 this.walkPattern(catchClause.param);
1514 }
1515 this.prewalkStatement(catchClause.body);
1516 this.walkStatement(catchClause.body);
1517 });
1518 }
1519
1520 walkPattern(pattern) {
1521 switch (pattern.type) {
1522 case "ArrayPattern":
1523 this.walkArrayPattern(pattern);
1524 break;
1525 case "AssignmentPattern":
1526 this.walkAssignmentPattern(pattern);
1527 break;
1528 case "MemberExpression":
1529 this.walkMemberExpression(pattern);
1530 break;
1531 case "ObjectPattern":
1532 this.walkObjectPattern(pattern);
1533 break;
1534 case "RestElement":
1535 this.walkRestElement(pattern);
1536 break;
1537 }
1538 }
1539
1540 walkAssignmentPattern(pattern) {
1541 this.walkExpression(pattern.right);
1542 this.walkPattern(pattern.left);
1543 }
1544
1545 walkObjectPattern(pattern) {
1546 for (let i = 0, len = pattern.properties.length; i < len; i++) {
1547 const prop = pattern.properties[i];
1548 if (prop) {
1549 if (prop.computed) this.walkExpression(prop.key);
1550 if (prop.value) this.walkPattern(prop.value);
1551 }
1552 }
1553 }
1554
1555 walkArrayPattern(pattern) {
1556 for (let i = 0, len = pattern.elements.length; i < len; i++) {
1557 const element = pattern.elements[i];
1558 if (element) this.walkPattern(element);
1559 }
1560 }
1561
1562 walkRestElement(pattern) {
1563 this.walkPattern(pattern.argument);
1564 }
1565
1566 walkExpressions(expressions) {
1567 for (const expression of expressions) {
1568 if (expression) {
1569 this.walkExpression(expression);
1570 }
1571 }
1572 }
1573
1574 walkExpression(expression) {
1575 switch (expression.type) {
1576 case "ArrayExpression":
1577 this.walkArrayExpression(expression);
1578 break;
1579 case "ArrowFunctionExpression":
1580 this.walkArrowFunctionExpression(expression);
1581 break;
1582 case "AssignmentExpression":
1583 this.walkAssignmentExpression(expression);
1584 break;
1585 case "AwaitExpression":
1586 this.walkAwaitExpression(expression);
1587 break;
1588 case "BinaryExpression":
1589 this.walkBinaryExpression(expression);
1590 break;
1591 case "CallExpression":
1592 this.walkCallExpression(expression);
1593 break;
1594 case "ClassExpression":
1595 this.walkClassExpression(expression);
1596 break;
1597 case "ConditionalExpression":
1598 this.walkConditionalExpression(expression);
1599 break;
1600 case "FunctionExpression":
1601 this.walkFunctionExpression(expression);
1602 break;
1603 case "Identifier":
1604 this.walkIdentifier(expression);
1605 break;
1606 case "LogicalExpression":
1607 this.walkLogicalExpression(expression);
1608 break;
1609 case "MemberExpression":
1610 this.walkMemberExpression(expression);
1611 break;
1612 case "NewExpression":
1613 this.walkNewExpression(expression);
1614 break;
1615 case "ObjectExpression":
1616 this.walkObjectExpression(expression);
1617 break;
1618 case "SequenceExpression":
1619 this.walkSequenceExpression(expression);
1620 break;
1621 case "SpreadElement":
1622 this.walkSpreadElement(expression);
1623 break;
1624 case "TaggedTemplateExpression":
1625 this.walkTaggedTemplateExpression(expression);
1626 break;
1627 case "TemplateLiteral":
1628 this.walkTemplateLiteral(expression);
1629 break;
1630 case "ThisExpression":
1631 this.walkThisExpression(expression);
1632 break;
1633 case "UnaryExpression":
1634 this.walkUnaryExpression(expression);
1635 break;
1636 case "UpdateExpression":
1637 this.walkUpdateExpression(expression);
1638 break;
1639 case "YieldExpression":
1640 this.walkYieldExpression(expression);
1641 break;
1642 }
1643 }
1644
1645 walkAwaitExpression(expression) {
1646 this.walkExpression(expression.argument);
1647 }
1648
1649 walkArrayExpression(expression) {
1650 if (expression.elements) {
1651 this.walkExpressions(expression.elements);
1652 }
1653 }
1654
1655 walkSpreadElement(expression) {
1656 if (expression.argument) {
1657 this.walkExpression(expression.argument);
1658 }
1659 }
1660
1661 walkObjectExpression(expression) {
1662 for (
1663 let propIndex = 0, len = expression.properties.length;
1664 propIndex < len;
1665 propIndex++
1666 ) {
1667 const prop = expression.properties[propIndex];
1668 if (prop.type === "SpreadElement") {
1669 this.walkExpression(prop.argument);
1670 continue;
1671 }
1672 if (prop.computed) {
1673 this.walkExpression(prop.key);
1674 }
1675 if (prop.shorthand) {
1676 this.scope.inShorthand = true;
1677 }
1678 this.walkExpression(prop.value);
1679 if (prop.shorthand) {
1680 this.scope.inShorthand = false;
1681 }
1682 }
1683 }
1684
1685 walkFunctionExpression(expression) {
1686 const wasTopLevel = this.scope.topLevelScope;
1687 this.scope.topLevelScope = false;
1688 const scopeParams = expression.params;
1689
1690 // Add function name in scope for recursive calls
1691 if (expression.id) {
1692 scopeParams.push(expression.id.name);
1693 }
1694
1695 this.inFunctionScope(true, scopeParams, () => {
1696 for (const param of expression.params) {
1697 this.walkPattern(param);
1698 }
1699 if (expression.body.type === "BlockStatement") {
1700 this.detectStrictMode(expression.body.body);
1701 this.prewalkStatement(expression.body);
1702 this.walkStatement(expression.body);
1703 } else {
1704 this.walkExpression(expression.body);
1705 }
1706 });
1707 this.scope.topLevelScope = wasTopLevel;
1708 }
1709
1710 walkArrowFunctionExpression(expression) {
1711 this.inFunctionScope(false, expression.params, () => {
1712 for (const param of expression.params) {
1713 this.walkPattern(param);
1714 }
1715 if (expression.body.type === "BlockStatement") {
1716 this.detectStrictMode(expression.body.body);
1717 this.prewalkStatement(expression.body);
1718 this.walkStatement(expression.body);
1719 } else {
1720 this.walkExpression(expression.body);
1721 }
1722 });
1723 }
1724
1725 walkSequenceExpression(expression) {
1726 if (expression.expressions) this.walkExpressions(expression.expressions);
1727 }
1728
1729 walkUpdateExpression(expression) {
1730 this.walkExpression(expression.argument);
1731 }
1732
1733 walkUnaryExpression(expression) {
1734 if (expression.operator === "typeof") {
1735 const exprName = this.getNameForExpression(expression.argument);
1736 if (exprName && exprName.free) {
1737 const hook = this.hooks.typeof.get(exprName.name);
1738 if (hook !== undefined) {
1739 const result = hook.call(expression);
1740 if (result === true) return;
1741 }
1742 }
1743 }
1744 this.walkExpression(expression.argument);
1745 }
1746
1747 walkLeftRightExpression(expression) {
1748 this.walkExpression(expression.left);
1749 this.walkExpression(expression.right);
1750 }
1751
1752 walkBinaryExpression(expression) {
1753 this.walkLeftRightExpression(expression);
1754 }
1755
1756 walkLogicalExpression(expression) {
1757 const result = this.hooks.expressionLogicalOperator.call(expression);
1758 if (result === undefined) {
1759 this.walkLeftRightExpression(expression);
1760 } else {
1761 if (result) {
1762 this.walkExpression(expression.right);
1763 }
1764 }
1765 }
1766
1767 walkAssignmentExpression(expression) {
1768 const renameIdentifier = this.getRenameIdentifier(expression.right);
1769 if (expression.left.type === "Identifier" && renameIdentifier) {
1770 const hook = this.hooks.canRename.get(renameIdentifier);
1771 if (hook !== undefined && hook.call(expression.right)) {
1772 // renaming "a = b;"
1773 const hook = this.hooks.rename.get(renameIdentifier);
1774 if (hook === undefined || !hook.call(expression.right)) {
1775 this.scope.renames.set(expression.left.name, renameIdentifier);
1776 this.scope.definitions.delete(expression.left.name);
1777 }
1778 return;
1779 }
1780 }
1781 if (expression.left.type === "Identifier") {
1782 const assignedHook = this.hooks.assigned.get(expression.left.name);
1783 if (assignedHook === undefined || !assignedHook.call(expression)) {
1784 this.walkExpression(expression.right);
1785 }
1786 this.scope.renames.set(expression.left.name, null);
1787 const assignHook = this.hooks.assign.get(expression.left.name);
1788 if (assignHook === undefined || !assignHook.call(expression)) {
1789 this.walkExpression(expression.left);
1790 }
1791 return;
1792 }
1793 this.walkExpression(expression.right);
1794 this.walkPattern(expression.left);
1795 this.enterPattern(expression.left, (name, decl) => {
1796 this.scope.renames.set(name, null);
1797 });
1798 }
1799
1800 walkConditionalExpression(expression) {
1801 const result = this.hooks.expressionConditionalOperator.call(expression);
1802 if (result === undefined) {
1803 this.walkExpression(expression.test);
1804 this.walkExpression(expression.consequent);
1805 if (expression.alternate) {
1806 this.walkExpression(expression.alternate);
1807 }
1808 } else {
1809 if (result) {
1810 this.walkExpression(expression.consequent);
1811 } else if (expression.alternate) {
1812 this.walkExpression(expression.alternate);
1813 }
1814 }
1815 }
1816
1817 walkNewExpression(expression) {
1818 const callee = this.evaluateExpression(expression.callee);
1819 if (callee.isIdentifier()) {
1820 const hook = this.hooks.new.get(callee.identifier);
1821 if (hook !== undefined) {
1822 const result = hook.call(expression);
1823 if (result === true) {
1824 return;
1825 }
1826 }
1827 }
1828
1829 this.walkExpression(expression.callee);
1830 if (expression.arguments) {
1831 this.walkExpressions(expression.arguments);
1832 }
1833 }
1834
1835 walkYieldExpression(expression) {
1836 if (expression.argument) {
1837 this.walkExpression(expression.argument);
1838 }
1839 }
1840
1841 walkTemplateLiteral(expression) {
1842 if (expression.expressions) {
1843 this.walkExpressions(expression.expressions);
1844 }
1845 }
1846
1847 walkTaggedTemplateExpression(expression) {
1848 if (expression.tag) {
1849 this.walkExpression(expression.tag);
1850 }
1851 if (expression.quasi && expression.quasi.expressions) {
1852 this.walkExpressions(expression.quasi.expressions);
1853 }
1854 }
1855
1856 walkClassExpression(expression) {
1857 this.walkClass(expression);
1858 }
1859
1860 _walkIIFE(functionExpression, options, currentThis) {
1861 const renameArgOrThis = argOrThis => {
1862 const renameIdentifier = this.getRenameIdentifier(argOrThis);
1863 if (renameIdentifier) {
1864 const hook = this.hooks.canRename.get(renameIdentifier);
1865 if (hook !== undefined && hook.call(argOrThis)) {
1866 const hook = this.hooks.rename.get(renameIdentifier);
1867 if (hook === undefined || !hook.call(argOrThis)) {
1868 return renameIdentifier;
1869 }
1870 }
1871 }
1872 this.walkExpression(argOrThis);
1873 };
1874 const params = functionExpression.params;
1875 const renameThis = currentThis ? renameArgOrThis(currentThis) : null;
1876 const args = options.map(renameArgOrThis);
1877 const wasTopLevel = this.scope.topLevelScope;
1878 this.scope.topLevelScope = false;
1879 const scopeParams = params.filter((identifier, idx) => !args[idx]);
1880
1881 // Add function name in scope for recursive calls
1882 if (functionExpression.id) {
1883 scopeParams.push(functionExpression.id.name);
1884 }
1885
1886 this.inFunctionScope(true, scopeParams, () => {
1887 if (renameThis) {
1888 this.scope.renames.set("this", renameThis);
1889 }
1890 for (let i = 0; i < args.length; i++) {
1891 const param = args[i];
1892 if (!param) continue;
1893 if (!params[i] || params[i].type !== "Identifier") continue;
1894 this.scope.renames.set(params[i].name, param);
1895 }
1896 if (functionExpression.body.type === "BlockStatement") {
1897 this.prewalkStatement(functionExpression.body);
1898 this.walkStatement(functionExpression.body);
1899 } else {
1900 this.walkExpression(functionExpression.body);
1901 }
1902 });
1903 this.scope.topLevelScope = wasTopLevel;
1904 }
1905
1906 walkCallExpression(expression) {
1907 if (
1908 expression.callee.type === "MemberExpression" &&
1909 expression.callee.object.type === "FunctionExpression" &&
1910 !expression.callee.computed &&
1911 (expression.callee.property.name === "call" ||
1912 expression.callee.property.name === "bind") &&
1913 expression.arguments.length > 0
1914 ) {
1915 // (function(…) { }.call/bind(?, …))
1916 this._walkIIFE(
1917 expression.callee.object,
1918 expression.arguments.slice(1),
1919 expression.arguments[0]
1920 );
1921 } else if (expression.callee.type === "FunctionExpression") {
1922 // (function(…) { }(…))
1923 this._walkIIFE(expression.callee, expression.arguments, null);
1924 } else if (expression.callee.type === "Import") {
1925 let result = this.hooks.importCall.call(expression);
1926 if (result === true) return;
1927
1928 if (expression.arguments) this.walkExpressions(expression.arguments);
1929 } else {
1930 const callee = this.evaluateExpression(expression.callee);
1931 if (callee.isIdentifier()) {
1932 const callHook = this.hooks.call.get(callee.identifier);
1933 if (callHook !== undefined) {
1934 let result = callHook.call(expression);
1935 if (result === true) return;
1936 }
1937 let identifier = callee.identifier.replace(/\.[^.]+$/, "");
1938 if (identifier !== callee.identifier) {
1939 const callAnyHook = this.hooks.callAnyMember.get(identifier);
1940 if (callAnyHook !== undefined) {
1941 let result = callAnyHook.call(expression);
1942 if (result === true) return;
1943 }
1944 }
1945 }
1946
1947 if (expression.callee) this.walkExpression(expression.callee);
1948 if (expression.arguments) this.walkExpressions(expression.arguments);
1949 }
1950 }
1951
1952 walkMemberExpression(expression) {
1953 const exprName = this.getNameForExpression(expression);
1954 if (exprName && exprName.free) {
1955 const expressionHook = this.hooks.expression.get(exprName.name);
1956 if (expressionHook !== undefined) {
1957 const result = expressionHook.call(expression);
1958 if (result === true) return;
1959 }
1960 const expressionAnyMemberHook = this.hooks.expressionAnyMember.get(
1961 exprName.nameGeneral
1962 );
1963 if (expressionAnyMemberHook !== undefined) {
1964 const result = expressionAnyMemberHook.call(expression);
1965 if (result === true) return;
1966 }
1967 }
1968 this.walkExpression(expression.object);
1969 if (expression.computed === true) this.walkExpression(expression.property);
1970 }
1971
1972 walkThisExpression(expression) {
1973 const expressionHook = this.hooks.expression.get("this");
1974 if (expressionHook !== undefined) {
1975 expressionHook.call(expression);
1976 }
1977 }
1978
1979 walkIdentifier(expression) {
1980 if (!this.scope.definitions.has(expression.name)) {
1981 const hook = this.hooks.expression.get(
1982 this.scope.renames.get(expression.name) || expression.name
1983 );
1984 if (hook !== undefined) {
1985 const result = hook.call(expression);
1986 if (result === true) return;
1987 }
1988 }
1989 }
1990
1991 /**
1992 * @deprecated
1993 * @param {any} params scope params
1994 * @param {function(): void} fn inner function
1995 * @returns {void}
1996 */
1997 inScope(params, fn) {
1998 const oldScope = this.scope;
1999 this.scope = {
2000 topLevelScope: oldScope.topLevelScope,
2001 inTry: false,
2002 inShorthand: false,
2003 isStrict: oldScope.isStrict,
2004 definitions: oldScope.definitions.createChild(),
2005 renames: oldScope.renames.createChild()
2006 };
2007
2008 this.scope.renames.set("this", null);
2009
2010 this.enterPatterns(params, ident => {
2011 this.scope.renames.set(ident, null);
2012 this.scope.definitions.add(ident);
2013 });
2014
2015 fn();
2016
2017 this.scope = oldScope;
2018 }
2019
2020 inFunctionScope(hasThis, params, fn) {
2021 const oldScope = this.scope;
2022 this.scope = {
2023 topLevelScope: oldScope.topLevelScope,
2024 inTry: false,
2025 inShorthand: false,
2026 isStrict: oldScope.isStrict,
2027 definitions: oldScope.definitions.createChild(),
2028 renames: oldScope.renames.createChild()
2029 };
2030
2031 if (hasThis) {
2032 this.scope.renames.set("this", null);
2033 }
2034
2035 this.enterPatterns(params, ident => {
2036 this.scope.renames.set(ident, null);
2037 this.scope.definitions.add(ident);
2038 });
2039
2040 fn();
2041
2042 this.scope = oldScope;
2043 }
2044
2045 inBlockScope(fn) {
2046 const oldScope = this.scope;
2047 this.scope = {
2048 topLevelScope: oldScope.topLevelScope,
2049 inTry: oldScope.inTry,
2050 inShorthand: false,
2051 isStrict: oldScope.isStrict,
2052 definitions: oldScope.definitions.createChild(),
2053 renames: oldScope.renames.createChild()
2054 };
2055
2056 fn();
2057
2058 this.scope = oldScope;
2059 }
2060
2061 detectStrictMode(statements) {
2062 const isStrict =
2063 statements.length >= 1 &&
2064 statements[0].type === "ExpressionStatement" &&
2065 statements[0].expression.type === "Literal" &&
2066 statements[0].expression.value === "use strict";
2067 if (isStrict) {
2068 this.scope.isStrict = true;
2069 }
2070 }
2071
2072 enterPatterns(patterns, onIdent) {
2073 for (const pattern of patterns) {
2074 if (typeof pattern !== "string") {
2075 this.enterPattern(pattern, onIdent);
2076 } else if (pattern) {
2077 onIdent(pattern);
2078 }
2079 }
2080 }
2081
2082 enterPattern(pattern, onIdent) {
2083 if (!pattern) return;
2084 switch (pattern.type) {
2085 case "ArrayPattern":
2086 this.enterArrayPattern(pattern, onIdent);
2087 break;
2088 case "AssignmentPattern":
2089 this.enterAssignmentPattern(pattern, onIdent);
2090 break;
2091 case "Identifier":
2092 this.enterIdentifier(pattern, onIdent);
2093 break;
2094 case "ObjectPattern":
2095 this.enterObjectPattern(pattern, onIdent);
2096 break;
2097 case "RestElement":
2098 this.enterRestElement(pattern, onIdent);
2099 break;
2100 case "Property":
2101 this.enterPattern(pattern.value, onIdent);
2102 break;
2103 }
2104 }
2105
2106 enterIdentifier(pattern, onIdent) {
2107 onIdent(pattern.name, pattern);
2108 }
2109
2110 enterObjectPattern(pattern, onIdent) {
2111 for (
2112 let propIndex = 0, len = pattern.properties.length;
2113 propIndex < len;
2114 propIndex++
2115 ) {
2116 const prop = pattern.properties[propIndex];
2117 this.enterPattern(prop, onIdent);
2118 }
2119 }
2120
2121 enterArrayPattern(pattern, onIdent) {
2122 for (
2123 let elementIndex = 0, len = pattern.elements.length;
2124 elementIndex < len;
2125 elementIndex++
2126 ) {
2127 const element = pattern.elements[elementIndex];
2128 this.enterPattern(element, onIdent);
2129 }
2130 }
2131
2132 enterRestElement(pattern, onIdent) {
2133 this.enterPattern(pattern.argument, onIdent);
2134 }
2135
2136 enterAssignmentPattern(pattern, onIdent) {
2137 this.enterPattern(pattern.left, onIdent);
2138 }
2139
2140 evaluateExpression(expression) {
2141 try {
2142 const hook = this.hooks.evaluate.get(expression.type);
2143 if (hook !== undefined) {
2144 const result = hook.call(expression);
2145 if (result !== undefined) {
2146 if (result) {
2147 result.setExpression(expression);
2148 }
2149 return result;
2150 }
2151 }
2152 } catch (e) {
2153 console.warn(e);
2154 // ignore error
2155 }
2156 return new BasicEvaluatedExpression()
2157 .setRange(expression.range)
2158 .setExpression(expression);
2159 }
2160
2161 parseString(expression) {
2162 switch (expression.type) {
2163 case "BinaryExpression":
2164 if (expression.operator === "+") {
2165 return (
2166 this.parseString(expression.left) +
2167 this.parseString(expression.right)
2168 );
2169 }
2170 break;
2171 case "Literal":
2172 return expression.value + "";
2173 }
2174 throw new Error(
2175 expression.type + " is not supported as parameter for require"
2176 );
2177 }
2178
2179 parseCalculatedString(expression) {
2180 switch (expression.type) {
2181 case "BinaryExpression":
2182 if (expression.operator === "+") {
2183 const left = this.parseCalculatedString(expression.left);
2184 const right = this.parseCalculatedString(expression.right);
2185 if (left.code) {
2186 return {
2187 range: left.range,
2188 value: left.value,
2189 code: true,
2190 conditional: false
2191 };
2192 } else if (right.code) {
2193 return {
2194 range: [
2195 left.range[0],
2196 right.range ? right.range[1] : left.range[1]
2197 ],
2198 value: left.value + right.value,
2199 code: true,
2200 conditional: false
2201 };
2202 } else {
2203 return {
2204 range: [left.range[0], right.range[1]],
2205 value: left.value + right.value,
2206 code: false,
2207 conditional: false
2208 };
2209 }
2210 }
2211 break;
2212 case "ConditionalExpression": {
2213 const consequent = this.parseCalculatedString(expression.consequent);
2214 const alternate = this.parseCalculatedString(expression.alternate);
2215 const items = [];
2216 if (consequent.conditional) {
2217 items.push(...consequent.conditional);
2218 } else if (!consequent.code) {
2219 items.push(consequent);
2220 } else {
2221 break;
2222 }
2223 if (alternate.conditional) {
2224 items.push(...alternate.conditional);
2225 } else if (!alternate.code) {
2226 items.push(alternate);
2227 } else {
2228 break;
2229 }
2230 return {
2231 range: undefined,
2232 value: "",
2233 code: true,
2234 conditional: items
2235 };
2236 }
2237 case "Literal":
2238 return {
2239 range: expression.range,
2240 value: expression.value + "",
2241 code: false,
2242 conditional: false
2243 };
2244 }
2245 return {
2246 range: undefined,
2247 value: "",
2248 code: true,
2249 conditional: false
2250 };
2251 }
2252
2253 parse(source, initialState) {
2254 let ast;
2255 let comments;
2256 if (typeof source === "object" && source !== null) {
2257 ast = source;
2258 comments = source.comments;
2259 } else {
2260 comments = [];
2261 ast = Parser.parse(source, {
2262 sourceType: this.sourceType,
2263 onComment: comments
2264 });
2265 }
2266
2267 const oldScope = this.scope;
2268 const oldState = this.state;
2269 const oldComments = this.comments;
2270 this.scope = {
2271 topLevelScope: true,
2272 inTry: false,
2273 inShorthand: false,
2274 isStrict: false,
2275 definitions: new StackedSetMap(),
2276 renames: new StackedSetMap()
2277 };
2278 const state = (this.state = initialState || {});
2279 this.comments = comments;
2280 if (this.hooks.program.call(ast, comments) === undefined) {
2281 this.detectStrictMode(ast.body);
2282 this.prewalkStatements(ast.body);
2283 this.blockPrewalkStatements(ast.body);
2284 this.walkStatements(ast.body);
2285 }
2286 this.scope = oldScope;
2287 this.state = oldState;
2288 this.comments = oldComments;
2289 return state;
2290 }
2291
2292 evaluate(source) {
2293 const ast = Parser.parse("(" + source + ")", {
2294 sourceType: this.sourceType,
2295 locations: false
2296 });
2297 // TODO(https://github.com/acornjs/acorn/issues/741)
2298 // @ts-ignore
2299 if (ast.body.length !== 1 || ast.body[0].type !== "ExpressionStatement") {
2300 throw new Error("evaluate: Source is not a expression");
2301 }
2302 // TODO(https://github.com/acornjs/acorn/issues/741)
2303 // @ts-ignore
2304 return this.evaluateExpression(ast.body[0].expression);
2305 }
2306
2307 getComments(range) {
2308 return this.comments.filter(
2309 comment => comment.range[0] >= range[0] && comment.range[1] <= range[1]
2310 );
2311 }
2312
2313 parseCommentOptions(range) {
2314 const comments = this.getComments(range);
2315 if (comments.length === 0) {
2316 return EMPTY_COMMENT_OPTIONS;
2317 }
2318 let options = {};
2319 let errors = [];
2320 for (const comment of comments) {
2321 const { value } = comment;
2322 if (value && webpackCommentRegExp.test(value)) {
2323 // try compile only if webpack options comment is present
2324 try {
2325 const val = vm.runInNewContext(`(function(){return {${value}};})()`);
2326 Object.assign(options, val);
2327 } catch (e) {
2328 e.comment = comment;
2329 errors.push(e);
2330 }
2331 }
2332 }
2333 return { options, errors };
2334 }
2335
2336 getNameForExpression(expression) {
2337 let expr = expression;
2338 const exprName = [];
2339 while (
2340 expr.type === "MemberExpression" &&
2341 expr.property.type === (expr.computed ? "Literal" : "Identifier")
2342 ) {
2343 exprName.push(expr.computed ? expr.property.value : expr.property.name);
2344 expr = expr.object;
2345 }
2346 let free;
2347 if (expr.type === "Identifier") {
2348 free = !this.scope.definitions.has(expr.name);
2349 exprName.push(this.scope.renames.get(expr.name) || expr.name);
2350 } else if (
2351 expr.type === "ThisExpression" &&
2352 this.scope.renames.get("this")
2353 ) {
2354 free = true;
2355 exprName.push(this.scope.renames.get("this"));
2356 } else if (expr.type === "ThisExpression") {
2357 free = this.scope.topLevelScope;
2358 exprName.push("this");
2359 } else {
2360 return null;
2361 }
2362 let prefix = "";
2363 for (let i = exprName.length - 1; i >= 2; i--) {
2364 prefix += exprName[i] + ".";
2365 }
2366 if (exprName.length > 1) {
2367 prefix += exprName[1];
2368 }
2369 const name = prefix ? prefix + "." + exprName[0] : exprName[0];
2370 const nameGeneral = prefix;
2371 return {
2372 name,
2373 nameGeneral,
2374 free
2375 };
2376 }
2377
2378 static parse(code, options) {
2379 const type = options ? options.sourceType : "module";
2380 const parserOptions = Object.assign(
2381 Object.create(null),
2382 defaultParserOptions,
2383 options
2384 );
2385
2386 if (type === "auto") {
2387 parserOptions.sourceType = "module";
2388 } else if (parserOptions.sourceType === "script") {
2389 parserOptions.allowReturnOutsideFunction = true;
2390 }
2391
2392 let ast;
2393 let error;
2394 let threw = false;
2395 try {
2396 ast = acornParser.parse(code, parserOptions);
2397 } catch (e) {
2398 error = e;
2399 threw = true;
2400 }
2401
2402 if (threw && type === "auto") {
2403 parserOptions.sourceType = "script";
2404 parserOptions.allowReturnOutsideFunction = true;
2405 if (Array.isArray(parserOptions.onComment)) {
2406 parserOptions.onComment.length = 0;
2407 }
2408 try {
2409 ast = acornParser.parse(code, parserOptions);
2410 threw = false;
2411 } catch (e) {
2412 threw = true;
2413 }
2414 }
2415
2416 if (threw) {
2417 throw error;
2418 }
2419
2420 return ast;
2421 }
2422}
2423
2424// TODO remove in webpack 5
2425Object.defineProperty(Parser.prototype, "getCommentOptions", {
2426 configurable: false,
2427 value: util.deprecate(
2428 /**
2429 * @deprecated
2430 * @param {TODO} range Range
2431 * @returns {void}
2432 * @this {Parser}
2433 */
2434 function(range) {
2435 return this.parseCommentOptions(range).options;
2436 },
2437 "Parser.getCommentOptions: Use Parser.parseCommentOptions(range) instead"
2438 )
2439});
2440
2441module.exports = Parser;