UNPKG

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