Update .bashrc
[dotfiles/.git] / eslint-utils / index.mjs
1 /*! @author Toru Nagashima <https://github.com/mysticatea> */
2 import evk from 'eslint-visitor-keys';
3
4 /**
5  * Get the innermost scope which contains a given location.
6  * @param {Scope} initialScope The initial scope to search.
7  * @param {Node} node The location to search.
8  * @returns {Scope} The innermost scope.
9  */
10 function getInnermostScope(initialScope, node) {
11     const location = node.range[0];
12
13     let scope = initialScope;
14     let found = false;
15     do {
16         found = false;
17         for (const childScope of scope.childScopes) {
18             const range = childScope.block.range;
19
20             if (range[0] <= location && location < range[1]) {
21                 scope = childScope;
22                 found = true;
23                 break
24             }
25         }
26     } while (found)
27
28     return scope
29 }
30
31 /**
32  * Find the variable of a given name.
33  * @param {Scope} initialScope The scope to start finding.
34  * @param {string|Node} nameOrNode The variable name to find. If this is a Node object then it should be an Identifier node.
35  * @returns {Variable|null} The found variable or null.
36  */
37 function findVariable(initialScope, nameOrNode) {
38     let name = "";
39     let scope = initialScope;
40
41     if (typeof nameOrNode === "string") {
42         name = nameOrNode;
43     } else {
44         name = nameOrNode.name;
45         scope = getInnermostScope(scope, nameOrNode);
46     }
47
48     while (scope != null) {
49         const variable = scope.set.get(name);
50         if (variable != null) {
51             return variable
52         }
53         scope = scope.upper;
54     }
55
56     return null
57 }
58
59 /**
60  * Negate the result of `this` calling.
61  * @param {Token} token The token to check.
62  * @returns {boolean} `true` if the result of `this(token)` is `false`.
63  */
64 function negate0(token) {
65     return !this(token) //eslint-disable-line no-invalid-this
66 }
67
68 /**
69  * Creates the negate function of the given function.
70  * @param {function(Token):boolean} f - The function to negate.
71  * @returns {function(Token):boolean} Negated function.
72  */
73 function negate(f) {
74     return negate0.bind(f)
75 }
76
77 /**
78  * Checks if the given token is an arrow token or not.
79  * @param {Token} token - The token to check.
80  * @returns {boolean} `true` if the token is an arrow token.
81  */
82 function isArrowToken(token) {
83     return token.value === "=>" && token.type === "Punctuator"
84 }
85
86 /**
87  * Checks if the given token is a comma token or not.
88  * @param {Token} token - The token to check.
89  * @returns {boolean} `true` if the token is a comma token.
90  */
91 function isCommaToken(token) {
92     return token.value === "," && token.type === "Punctuator"
93 }
94
95 /**
96  * Checks if the given token is a semicolon token or not.
97  * @param {Token} token - The token to check.
98  * @returns {boolean} `true` if the token is a semicolon token.
99  */
100 function isSemicolonToken(token) {
101     return token.value === ";" && token.type === "Punctuator"
102 }
103
104 /**
105  * Checks if the given token is a colon token or not.
106  * @param {Token} token - The token to check.
107  * @returns {boolean} `true` if the token is a colon token.
108  */
109 function isColonToken(token) {
110     return token.value === ":" && token.type === "Punctuator"
111 }
112
113 /**
114  * Checks if the given token is an opening parenthesis token or not.
115  * @param {Token} token - The token to check.
116  * @returns {boolean} `true` if the token is an opening parenthesis token.
117  */
118 function isOpeningParenToken(token) {
119     return token.value === "(" && token.type === "Punctuator"
120 }
121
122 /**
123  * Checks if the given token is a closing parenthesis token or not.
124  * @param {Token} token - The token to check.
125  * @returns {boolean} `true` if the token is a closing parenthesis token.
126  */
127 function isClosingParenToken(token) {
128     return token.value === ")" && token.type === "Punctuator"
129 }
130
131 /**
132  * Checks if the given token is an opening square bracket token or not.
133  * @param {Token} token - The token to check.
134  * @returns {boolean} `true` if the token is an opening square bracket token.
135  */
136 function isOpeningBracketToken(token) {
137     return token.value === "[" && token.type === "Punctuator"
138 }
139
140 /**
141  * Checks if the given token is a closing square bracket token or not.
142  * @param {Token} token - The token to check.
143  * @returns {boolean} `true` if the token is a closing square bracket token.
144  */
145 function isClosingBracketToken(token) {
146     return token.value === "]" && token.type === "Punctuator"
147 }
148
149 /**
150  * Checks if the given token is an opening brace token or not.
151  * @param {Token} token - The token to check.
152  * @returns {boolean} `true` if the token is an opening brace token.
153  */
154 function isOpeningBraceToken(token) {
155     return token.value === "{" && token.type === "Punctuator"
156 }
157
158 /**
159  * Checks if the given token is a closing brace token or not.
160  * @param {Token} token - The token to check.
161  * @returns {boolean} `true` if the token is a closing brace token.
162  */
163 function isClosingBraceToken(token) {
164     return token.value === "}" && token.type === "Punctuator"
165 }
166
167 /**
168  * Checks if the given token is a comment token or not.
169  * @param {Token} token - The token to check.
170  * @returns {boolean} `true` if the token is a comment token.
171  */
172 function isCommentToken(token) {
173     return (
174         token.type === "Line" ||
175         token.type === "Block" ||
176         token.type === "Shebang"
177     )
178 }
179
180 const isNotArrowToken = negate(isArrowToken);
181 const isNotCommaToken = negate(isCommaToken);
182 const isNotSemicolonToken = negate(isSemicolonToken);
183 const isNotColonToken = negate(isColonToken);
184 const isNotOpeningParenToken = negate(isOpeningParenToken);
185 const isNotClosingParenToken = negate(isClosingParenToken);
186 const isNotOpeningBracketToken = negate(isOpeningBracketToken);
187 const isNotClosingBracketToken = negate(isClosingBracketToken);
188 const isNotOpeningBraceToken = negate(isOpeningBraceToken);
189 const isNotClosingBraceToken = negate(isClosingBraceToken);
190 const isNotCommentToken = negate(isCommentToken);
191
192 /**
193  * Get the `(` token of the given function node.
194  * @param {Node} node - The function node to get.
195  * @param {SourceCode} sourceCode - The source code object to get tokens.
196  * @returns {Token} `(` token.
197  */
198 function getOpeningParenOfParams(node, sourceCode) {
199     return node.id
200         ? sourceCode.getTokenAfter(node.id, isOpeningParenToken)
201         : sourceCode.getFirstToken(node, isOpeningParenToken)
202 }
203
204 /**
205  * Get the location of the given function node for reporting.
206  * @param {Node} node - The function node to get.
207  * @param {SourceCode} sourceCode - The source code object to get tokens.
208  * @returns {string} The location of the function node for reporting.
209  */
210 function getFunctionHeadLocation(node, sourceCode) {
211     const parent = node.parent;
212     let start = null;
213     let end = null;
214
215     if (node.type === "ArrowFunctionExpression") {
216         const arrowToken = sourceCode.getTokenBefore(node.body, isArrowToken);
217
218         start = arrowToken.loc.start;
219         end = arrowToken.loc.end;
220     } else if (
221         parent.type === "Property" ||
222         parent.type === "MethodDefinition"
223     ) {
224         start = parent.loc.start;
225         end = getOpeningParenOfParams(node, sourceCode).loc.start;
226     } else {
227         start = node.loc.start;
228         end = getOpeningParenOfParams(node, sourceCode).loc.start;
229     }
230
231     return {
232         start: Object.assign({}, start),
233         end: Object.assign({}, end),
234     }
235 }
236
237 /* globals BigInt, globalThis, global, self, window */
238
239 const globalObject =
240     typeof globalThis !== "undefined"
241         ? globalThis
242         : typeof self !== "undefined"
243         ? self
244         : typeof window !== "undefined"
245         ? window
246         : typeof global !== "undefined"
247         ? global
248         : {};
249
250 const builtinNames = Object.freeze(
251     new Set([
252         "Array",
253         "ArrayBuffer",
254         "BigInt",
255         "BigInt64Array",
256         "BigUint64Array",
257         "Boolean",
258         "DataView",
259         "Date",
260         "decodeURI",
261         "decodeURIComponent",
262         "encodeURI",
263         "encodeURIComponent",
264         "escape",
265         "Float32Array",
266         "Float64Array",
267         "Function",
268         "Infinity",
269         "Int16Array",
270         "Int32Array",
271         "Int8Array",
272         "isFinite",
273         "isNaN",
274         "isPrototypeOf",
275         "JSON",
276         "Map",
277         "Math",
278         "NaN",
279         "Number",
280         "Object",
281         "parseFloat",
282         "parseInt",
283         "Promise",
284         "Proxy",
285         "Reflect",
286         "RegExp",
287         "Set",
288         "String",
289         "Symbol",
290         "Uint16Array",
291         "Uint32Array",
292         "Uint8Array",
293         "Uint8ClampedArray",
294         "undefined",
295         "unescape",
296         "WeakMap",
297         "WeakSet",
298     ])
299 );
300 const callAllowed = new Set(
301     [
302         Array.isArray,
303         typeof BigInt === "function" ? BigInt : undefined,
304         Boolean,
305         Date,
306         Date.parse,
307         decodeURI,
308         decodeURIComponent,
309         encodeURI,
310         encodeURIComponent,
311         escape,
312         isFinite,
313         isNaN,
314         isPrototypeOf,
315         ...Object.getOwnPropertyNames(Math)
316             .map(k => Math[k])
317             .filter(f => typeof f === "function"),
318         Number,
319         Number.isFinite,
320         Number.isNaN,
321         Number.parseFloat,
322         Number.parseInt,
323         Object,
324         Object.entries,
325         Object.is,
326         Object.isExtensible,
327         Object.isFrozen,
328         Object.isSealed,
329         Object.keys,
330         Object.values,
331         parseFloat,
332         parseInt,
333         RegExp,
334         String,
335         String.fromCharCode,
336         String.fromCodePoint,
337         String.raw,
338         Symbol,
339         Symbol.for,
340         Symbol.keyFor,
341         unescape,
342     ].filter(f => typeof f === "function")
343 );
344 const callPassThrough = new Set([
345     Object.freeze,
346     Object.preventExtensions,
347     Object.seal,
348 ]);
349
350 /**
351  * Get the property descriptor.
352  * @param {object} object The object to get.
353  * @param {string|number|symbol} name The property name to get.
354  */
355 function getPropertyDescriptor(object, name) {
356     let x = object;
357     while ((typeof x === "object" || typeof x === "function") && x !== null) {
358         const d = Object.getOwnPropertyDescriptor(x, name);
359         if (d) {
360             return d
361         }
362         x = Object.getPrototypeOf(x);
363     }
364     return null
365 }
366
367 /**
368  * Check if a property is getter or not.
369  * @param {object} object The object to check.
370  * @param {string|number|symbol} name The property name to check.
371  */
372 function isGetter(object, name) {
373     const d = getPropertyDescriptor(object, name);
374     return d != null && d.get != null
375 }
376
377 /**
378  * Get the element values of a given node list.
379  * @param {Node[]} nodeList The node list to get values.
380  * @param {Scope|undefined} initialScope The initial scope to find variables.
381  * @returns {any[]|null} The value list if all nodes are constant. Otherwise, null.
382  */
383 function getElementValues(nodeList, initialScope) {
384     const valueList = [];
385
386     for (let i = 0; i < nodeList.length; ++i) {
387         const elementNode = nodeList[i];
388
389         if (elementNode == null) {
390             valueList.length = i + 1;
391         } else if (elementNode.type === "SpreadElement") {
392             const argument = getStaticValueR(elementNode.argument, initialScope);
393             if (argument == null) {
394                 return null
395             }
396             valueList.push(...argument.value);
397         } else {
398             const element = getStaticValueR(elementNode, initialScope);
399             if (element == null) {
400                 return null
401             }
402             valueList.push(element.value);
403         }
404     }
405
406     return valueList
407 }
408
409 const operations = Object.freeze({
410     ArrayExpression(node, initialScope) {
411         const elements = getElementValues(node.elements, initialScope);
412         return elements != null ? { value: elements } : null
413     },
414
415     AssignmentExpression(node, initialScope) {
416         if (node.operator === "=") {
417             return getStaticValueR(node.right, initialScope)
418         }
419         return null
420     },
421
422     //eslint-disable-next-line complexity
423     BinaryExpression(node, initialScope) {
424         if (node.operator === "in" || node.operator === "instanceof") {
425             // Not supported.
426             return null
427         }
428
429         const left = getStaticValueR(node.left, initialScope);
430         const right = getStaticValueR(node.right, initialScope);
431         if (left != null && right != null) {
432             switch (node.operator) {
433                 case "==":
434                     return { value: left.value == right.value } //eslint-disable-line eqeqeq
435                 case "!=":
436                     return { value: left.value != right.value } //eslint-disable-line eqeqeq
437                 case "===":
438                     return { value: left.value === right.value }
439                 case "!==":
440                     return { value: left.value !== right.value }
441                 case "<":
442                     return { value: left.value < right.value }
443                 case "<=":
444                     return { value: left.value <= right.value }
445                 case ">":
446                     return { value: left.value > right.value }
447                 case ">=":
448                     return { value: left.value >= right.value }
449                 case "<<":
450                     return { value: left.value << right.value }
451                 case ">>":
452                     return { value: left.value >> right.value }
453                 case ">>>":
454                     return { value: left.value >>> right.value }
455                 case "+":
456                     return { value: left.value + right.value }
457                 case "-":
458                     return { value: left.value - right.value }
459                 case "*":
460                     return { value: left.value * right.value }
461                 case "/":
462                     return { value: left.value / right.value }
463                 case "%":
464                     return { value: left.value % right.value }
465                 case "**":
466                     return { value: Math.pow(left.value, right.value) }
467                 case "|":
468                     return { value: left.value | right.value }
469                 case "^":
470                     return { value: left.value ^ right.value }
471                 case "&":
472                     return { value: left.value & right.value }
473
474                 // no default
475             }
476         }
477
478         return null
479     },
480
481     CallExpression(node, initialScope) {
482         const calleeNode = node.callee;
483         const args = getElementValues(node.arguments, initialScope);
484
485         if (args != null) {
486             if (calleeNode.type === "MemberExpression") {
487                 const object = getStaticValueR(calleeNode.object, initialScope);
488                 const property = calleeNode.computed
489                     ? getStaticValueR(calleeNode.property, initialScope)
490                     : { value: calleeNode.property.name };
491
492                 if (object != null && property != null) {
493                     const receiver = object.value;
494                     const methodName = property.value;
495                     if (callAllowed.has(receiver[methodName])) {
496                         return { value: receiver[methodName](...args) }
497                     }
498                     if (callPassThrough.has(receiver[methodName])) {
499                         return { value: args[0] }
500                     }
501                 }
502             } else {
503                 const callee = getStaticValueR(calleeNode, initialScope);
504                 if (callee != null) {
505                     const func = callee.value;
506                     if (callAllowed.has(func)) {
507                         return { value: func(...args) }
508                     }
509                     if (callPassThrough.has(func)) {
510                         return { value: args[0] }
511                     }
512                 }
513             }
514         }
515
516         return null
517     },
518
519     ConditionalExpression(node, initialScope) {
520         const test = getStaticValueR(node.test, initialScope);
521         if (test != null) {
522             return test.value
523                 ? getStaticValueR(node.consequent, initialScope)
524                 : getStaticValueR(node.alternate, initialScope)
525         }
526         return null
527     },
528
529     ExpressionStatement(node, initialScope) {
530         return getStaticValueR(node.expression, initialScope)
531     },
532
533     Identifier(node, initialScope) {
534         if (initialScope != null) {
535             const variable = findVariable(initialScope, node);
536
537             // Built-in globals.
538             if (
539                 variable != null &&
540                 variable.defs.length === 0 &&
541                 builtinNames.has(variable.name) &&
542                 variable.name in globalObject
543             ) {
544                 return { value: globalObject[variable.name] }
545             }
546
547             // Constants.
548             if (variable != null && variable.defs.length === 1) {
549                 const def = variable.defs[0];
550                 if (
551                     def.parent &&
552                     def.parent.kind === "const" &&
553                     // TODO(mysticatea): don't support destructuring here.
554                     def.node.id.type === "Identifier"
555                 ) {
556                     return getStaticValueR(def.node.init, initialScope)
557                 }
558             }
559         }
560         return null
561     },
562
563     Literal(node) {
564         //istanbul ignore if : this is implementation-specific behavior.
565         if ((node.regex != null || node.bigint != null) && node.value == null) {
566             // It was a RegExp/BigInt literal, but Node.js didn't support it.
567             return null
568         }
569         return { value: node.value }
570     },
571
572     LogicalExpression(node, initialScope) {
573         const left = getStaticValueR(node.left, initialScope);
574         if (left != null) {
575             if (
576                 (node.operator === "||" && Boolean(left.value) === true) ||
577                 (node.operator === "&&" && Boolean(left.value) === false)
578             ) {
579                 return left
580             }
581
582             const right = getStaticValueR(node.right, initialScope);
583             if (right != null) {
584                 return right
585             }
586         }
587
588         return null
589     },
590
591     MemberExpression(node, initialScope) {
592         const object = getStaticValueR(node.object, initialScope);
593         const property = node.computed
594             ? getStaticValueR(node.property, initialScope)
595             : { value: node.property.name };
596
597         if (
598             object != null &&
599             property != null &&
600             !isGetter(object.value, property.value)
601         ) {
602             return { value: object.value[property.value] }
603         }
604         return null
605     },
606
607     NewExpression(node, initialScope) {
608         const callee = getStaticValueR(node.callee, initialScope);
609         const args = getElementValues(node.arguments, initialScope);
610
611         if (callee != null && args != null) {
612             const Func = callee.value;
613             if (callAllowed.has(Func)) {
614                 return { value: new Func(...args) }
615             }
616         }
617
618         return null
619     },
620
621     ObjectExpression(node, initialScope) {
622         const object = {};
623
624         for (const propertyNode of node.properties) {
625             if (propertyNode.type === "Property") {
626                 if (propertyNode.kind !== "init") {
627                     return null
628                 }
629                 const key = propertyNode.computed
630                     ? getStaticValueR(propertyNode.key, initialScope)
631                     : { value: propertyNode.key.name };
632                 const value = getStaticValueR(propertyNode.value, initialScope);
633                 if (key == null || value == null) {
634                     return null
635                 }
636                 object[key.value] = value.value;
637             } else if (
638                 propertyNode.type === "SpreadElement" ||
639                 propertyNode.type === "ExperimentalSpreadProperty"
640             ) {
641                 const argument = getStaticValueR(
642                     propertyNode.argument,
643                     initialScope
644                 );
645                 if (argument == null) {
646                     return null
647                 }
648                 Object.assign(object, argument.value);
649             } else {
650                 return null
651             }
652         }
653
654         return { value: object }
655     },
656
657     SequenceExpression(node, initialScope) {
658         const last = node.expressions[node.expressions.length - 1];
659         return getStaticValueR(last, initialScope)
660     },
661
662     TaggedTemplateExpression(node, initialScope) {
663         const tag = getStaticValueR(node.tag, initialScope);
664         const expressions = getElementValues(
665             node.quasi.expressions,
666             initialScope
667         );
668
669         if (tag != null && expressions != null) {
670             const func = tag.value;
671             const strings = node.quasi.quasis.map(q => q.value.cooked);
672             strings.raw = node.quasi.quasis.map(q => q.value.raw);
673
674             if (func === String.raw) {
675                 return { value: func(strings, ...expressions) }
676             }
677         }
678
679         return null
680     },
681
682     TemplateLiteral(node, initialScope) {
683         const expressions = getElementValues(node.expressions, initialScope);
684         if (expressions != null) {
685             let value = node.quasis[0].value.cooked;
686             for (let i = 0; i < expressions.length; ++i) {
687                 value += expressions[i];
688                 value += node.quasis[i + 1].value.cooked;
689             }
690             return { value }
691         }
692         return null
693     },
694
695     UnaryExpression(node, initialScope) {
696         if (node.operator === "delete") {
697             // Not supported.
698             return null
699         }
700         if (node.operator === "void") {
701             return { value: undefined }
702         }
703
704         const arg = getStaticValueR(node.argument, initialScope);
705         if (arg != null) {
706             switch (node.operator) {
707                 case "-":
708                     return { value: -arg.value }
709                 case "+":
710                     return { value: +arg.value } //eslint-disable-line no-implicit-coercion
711                 case "!":
712                     return { value: !arg.value }
713                 case "~":
714                     return { value: ~arg.value }
715                 case "typeof":
716                     return { value: typeof arg.value }
717
718                 // no default
719             }
720         }
721
722         return null
723     },
724 });
725
726 /**
727  * Get the value of a given node if it's a static value.
728  * @param {Node} node The node to get.
729  * @param {Scope|undefined} initialScope The scope to start finding variable.
730  * @returns {{value:any}|null} The static value of the node, or `null`.
731  */
732 function getStaticValueR(node, initialScope) {
733     if (node != null && Object.hasOwnProperty.call(operations, node.type)) {
734         return operations[node.type](node, initialScope)
735     }
736     return null
737 }
738
739 /**
740  * Get the value of a given node if it's a static value.
741  * @param {Node} node The node to get.
742  * @param {Scope} [initialScope] The scope to start finding variable. Optional. If this scope was given, this tries to resolve identifier references which are in the given node as much as possible.
743  * @returns {{value:any}|null} The static value of the node, or `null`.
744  */
745 function getStaticValue(node, initialScope = null) {
746     try {
747         return getStaticValueR(node, initialScope)
748     } catch (_error) {
749         return null
750     }
751 }
752
753 /**
754  * Get the value of a given node if it's a literal or a template literal.
755  * @param {Node} node The node to get.
756  * @param {Scope} [initialScope] The scope to start finding variable. Optional. If the node is an Identifier node and this scope was given, this checks the variable of the identifier, and returns the value of it if the variable is a constant.
757  * @returns {string|null} The value of the node, or `null`.
758  */
759 function getStringIfConstant(node, initialScope = null) {
760     // Handle the literals that the platform doesn't support natively.
761     if (node && node.type === "Literal" && node.value === null) {
762         if (node.regex) {
763             return `/${node.regex.pattern}/${node.regex.flags}`
764         }
765         if (node.bigint) {
766             return node.bigint
767         }
768     }
769
770     const evaluated = getStaticValue(node, initialScope);
771     return evaluated && String(evaluated.value)
772 }
773
774 /**
775  * Get the property name from a MemberExpression node or a Property node.
776  * @param {Node} node The node to get.
777  * @param {Scope} [initialScope] The scope to start finding variable. Optional. If the node is a computed property node and this scope was given, this checks the computed property name by the `getStringIfConstant` function with the scope, and returns the value of it.
778  * @returns {string|null} The property name of the node.
779  */
780 function getPropertyName(node, initialScope) {
781     switch (node.type) {
782         case "MemberExpression":
783             if (node.computed) {
784                 return getStringIfConstant(node.property, initialScope)
785             }
786             return node.property.name
787
788         case "Property":
789         case "MethodDefinition":
790             if (node.computed) {
791                 return getStringIfConstant(node.key, initialScope)
792             }
793             if (node.key.type === "Literal") {
794                 return String(node.key.value)
795             }
796             return node.key.name
797
798         // no default
799     }
800
801     return null
802 }
803
804 /**
805  * Get the name and kind of the given function node.
806  * @param {ASTNode} node - The function node to get.
807  * @returns {string} The name and kind of the function node.
808  */
809 function getFunctionNameWithKind(node) {
810     const parent = node.parent;
811     const tokens = [];
812
813     if (parent.type === "MethodDefinition" && parent.static) {
814         tokens.push("static");
815     }
816     if (node.async) {
817         tokens.push("async");
818     }
819     if (node.generator) {
820         tokens.push("generator");
821     }
822
823     if (node.type === "ArrowFunctionExpression") {
824         tokens.push("arrow", "function");
825     } else if (
826         parent.type === "Property" ||
827         parent.type === "MethodDefinition"
828     ) {
829         if (parent.kind === "constructor") {
830             return "constructor"
831         }
832         if (parent.kind === "get") {
833             tokens.push("getter");
834         } else if (parent.kind === "set") {
835             tokens.push("setter");
836         } else {
837             tokens.push("method");
838         }
839     } else {
840         tokens.push("function");
841     }
842
843     if (node.id) {
844         tokens.push(`'${node.id.name}'`);
845     } else {
846         const name = getPropertyName(parent);
847
848         if (name) {
849             tokens.push(`'${name}'`);
850         }
851     }
852
853     return tokens.join(" ")
854 }
855
856 const typeConversionBinaryOps = Object.freeze(
857     new Set([
858         "==",
859         "!=",
860         "<",
861         "<=",
862         ">",
863         ">=",
864         "<<",
865         ">>",
866         ">>>",
867         "+",
868         "-",
869         "*",
870         "/",
871         "%",
872         "|",
873         "^",
874         "&",
875         "in",
876     ])
877 );
878 const typeConversionUnaryOps = Object.freeze(new Set(["-", "+", "!", "~"]));
879 const visitor = Object.freeze(
880     Object.assign(Object.create(null), {
881         $visit(node, options, visitorKeys) {
882             const { type } = node;
883
884             if (typeof this[type] === "function") {
885                 return this[type](node, options, visitorKeys)
886             }
887
888             return this.$visitChildren(node, options, visitorKeys)
889         },
890
891         $visitChildren(node, options, visitorKeys) {
892             const { type } = node;
893
894             for (const key of visitorKeys[type] || evk.getKeys(node)) {
895                 const value = node[key];
896
897                 if (Array.isArray(value)) {
898                     for (const element of value) {
899                         if (
900                             element &&
901                             this.$visit(element, options, visitorKeys)
902                         ) {
903                             return true
904                         }
905                     }
906                 } else if (value && this.$visit(value, options, visitorKeys)) {
907                     return true
908                 }
909             }
910
911             return false
912         },
913
914         ArrowFunctionExpression() {
915             return false
916         },
917         AssignmentExpression() {
918             return true
919         },
920         AwaitExpression() {
921             return true
922         },
923         BinaryExpression(node, options, visitorKeys) {
924             if (
925                 options.considerImplicitTypeConversion &&
926                 typeConversionBinaryOps.has(node.operator) &&
927                 (node.left.type !== "Literal" || node.right.type !== "Literal")
928             ) {
929                 return true
930             }
931             return this.$visitChildren(node, options, visitorKeys)
932         },
933         CallExpression() {
934             return true
935         },
936         FunctionExpression() {
937             return false
938         },
939         ImportExpression() {
940             return true
941         },
942         MemberExpression(node, options, visitorKeys) {
943             if (options.considerGetters) {
944                 return true
945             }
946             if (
947                 options.considerImplicitTypeConversion &&
948                 node.computed &&
949                 node.property.type !== "Literal"
950             ) {
951                 return true
952             }
953             return this.$visitChildren(node, options, visitorKeys)
954         },
955         MethodDefinition(node, options, visitorKeys) {
956             if (
957                 options.considerImplicitTypeConversion &&
958                 node.computed &&
959                 node.key.type !== "Literal"
960             ) {
961                 return true
962             }
963             return this.$visitChildren(node, options, visitorKeys)
964         },
965         NewExpression() {
966             return true
967         },
968         Property(node, options, visitorKeys) {
969             if (
970                 options.considerImplicitTypeConversion &&
971                 node.computed &&
972                 node.key.type !== "Literal"
973             ) {
974                 return true
975             }
976             return this.$visitChildren(node, options, visitorKeys)
977         },
978         UnaryExpression(node, options, visitorKeys) {
979             if (node.operator === "delete") {
980                 return true
981             }
982             if (
983                 options.considerImplicitTypeConversion &&
984                 typeConversionUnaryOps.has(node.operator) &&
985                 node.argument.type !== "Literal"
986             ) {
987                 return true
988             }
989             return this.$visitChildren(node, options, visitorKeys)
990         },
991         UpdateExpression() {
992             return true
993         },
994         YieldExpression() {
995             return true
996         },
997     })
998 );
999
1000 /**
1001  * Check whether a given node has any side effect or not.
1002  * @param {Node} node The node to get.
1003  * @param {SourceCode} sourceCode The source code object.
1004  * @param {object} [options] The option object.
1005  * @param {boolean} [options.considerGetters=false] If `true` then it considers member accesses as the node which has side effects.
1006  * @param {boolean} [options.considerImplicitTypeConversion=false] If `true` then it considers implicit type conversion as the node which has side effects.
1007  * @param {object} [options.visitorKeys=evk.KEYS] The keys to traverse nodes. Use `context.getSourceCode().visitorKeys`.
1008  * @returns {boolean} `true` if the node has a certain side effect.
1009  */
1010 function hasSideEffect(
1011     node,
1012     sourceCode,
1013     { considerGetters = false, considerImplicitTypeConversion = false } = {}
1014 ) {
1015     return visitor.$visit(
1016         node,
1017         { considerGetters, considerImplicitTypeConversion },
1018         sourceCode.visitorKeys || evk.KEYS
1019     )
1020 }
1021
1022 /**
1023  * Get the left parenthesis of the parent node syntax if it exists.
1024  * E.g., `if (a) {}` then the `(`.
1025  * @param {Node} node The AST node to check.
1026  * @param {SourceCode} sourceCode The source code object to get tokens.
1027  * @returns {Token|null} The left parenthesis of the parent node syntax
1028  */
1029 function getParentSyntaxParen(node, sourceCode) {
1030     const parent = node.parent;
1031
1032     switch (parent.type) {
1033         case "CallExpression":
1034         case "NewExpression":
1035             if (parent.arguments.length === 1 && parent.arguments[0] === node) {
1036                 return sourceCode.getTokenAfter(
1037                     parent.callee,
1038                     isOpeningParenToken
1039                 )
1040             }
1041             return null
1042
1043         case "DoWhileStatement":
1044             if (parent.test === node) {
1045                 return sourceCode.getTokenAfter(
1046                     parent.body,
1047                     isOpeningParenToken
1048                 )
1049             }
1050             return null
1051
1052         case "IfStatement":
1053         case "WhileStatement":
1054             if (parent.test === node) {
1055                 return sourceCode.getFirstToken(parent, 1)
1056             }
1057             return null
1058
1059         case "ImportExpression":
1060             if (parent.source === node) {
1061                 return sourceCode.getFirstToken(parent, 1)
1062             }
1063             return null
1064
1065         case "SwitchStatement":
1066             if (parent.discriminant === node) {
1067                 return sourceCode.getFirstToken(parent, 1)
1068             }
1069             return null
1070
1071         case "WithStatement":
1072             if (parent.object === node) {
1073                 return sourceCode.getFirstToken(parent, 1)
1074             }
1075             return null
1076
1077         default:
1078             return null
1079     }
1080 }
1081
1082 /**
1083  * Check whether a given node is parenthesized or not.
1084  * @param {number} times The number of parantheses.
1085  * @param {Node} node The AST node to check.
1086  * @param {SourceCode} sourceCode The source code object to get tokens.
1087  * @returns {boolean} `true` if the node is parenthesized the given times.
1088  */
1089 /**
1090  * Check whether a given node is parenthesized or not.
1091  * @param {Node} node The AST node to check.
1092  * @param {SourceCode} sourceCode The source code object to get tokens.
1093  * @returns {boolean} `true` if the node is parenthesized.
1094  */
1095 function isParenthesized(
1096     timesOrNode,
1097     nodeOrSourceCode,
1098     optionalSourceCode
1099 ) {
1100     let times, node, sourceCode, maybeLeftParen, maybeRightParen;
1101     if (typeof timesOrNode === "number") {
1102         times = timesOrNode | 0;
1103         node = nodeOrSourceCode;
1104         sourceCode = optionalSourceCode;
1105         if (!(times >= 1)) {
1106             throw new TypeError("'times' should be a positive integer.")
1107         }
1108     } else {
1109         times = 1;
1110         node = timesOrNode;
1111         sourceCode = nodeOrSourceCode;
1112     }
1113
1114     if (node == null) {
1115         return false
1116     }
1117
1118     maybeLeftParen = maybeRightParen = node;
1119     do {
1120         maybeLeftParen = sourceCode.getTokenBefore(maybeLeftParen);
1121         maybeRightParen = sourceCode.getTokenAfter(maybeRightParen);
1122     } while (
1123         maybeLeftParen != null &&
1124         maybeRightParen != null &&
1125         isOpeningParenToken(maybeLeftParen) &&
1126         isClosingParenToken(maybeRightParen) &&
1127         // Avoid false positive such as `if (a) {}`
1128         maybeLeftParen !== getParentSyntaxParen(node, sourceCode) &&
1129         --times > 0
1130     )
1131
1132     return times === 0
1133 }
1134
1135 /**
1136  * @author Toru Nagashima <https://github.com/mysticatea>
1137  * See LICENSE file in root directory for full license.
1138  */
1139
1140 const placeholder = /\$(?:[$&`']|[1-9][0-9]?)/gu;
1141
1142 /** @type {WeakMap<PatternMatcher, {pattern:RegExp,escaped:boolean}>} */
1143 const internal = new WeakMap();
1144
1145 /**
1146  * Check whether a given character is escaped or not.
1147  * @param {string} str The string to check.
1148  * @param {number} index The location of the character to check.
1149  * @returns {boolean} `true` if the character is escaped.
1150  */
1151 function isEscaped(str, index) {
1152     let escaped = false;
1153     for (let i = index - 1; i >= 0 && str.charCodeAt(i) === 0x5c; --i) {
1154         escaped = !escaped;
1155     }
1156     return escaped
1157 }
1158
1159 /**
1160  * Replace a given string by a given matcher.
1161  * @param {PatternMatcher} matcher The pattern matcher.
1162  * @param {string} str The string to be replaced.
1163  * @param {string} replacement The new substring to replace each matched part.
1164  * @returns {string} The replaced string.
1165  */
1166 function replaceS(matcher, str, replacement) {
1167     const chunks = [];
1168     let index = 0;
1169
1170     /** @type {RegExpExecArray} */
1171     let match = null;
1172
1173     /**
1174      * @param {string} key The placeholder.
1175      * @returns {string} The replaced string.
1176      */
1177     function replacer(key) {
1178         switch (key) {
1179             case "$$":
1180                 return "$"
1181             case "$&":
1182                 return match[0]
1183             case "$`":
1184                 return str.slice(0, match.index)
1185             case "$'":
1186                 return str.slice(match.index + match[0].length)
1187             default: {
1188                 const i = key.slice(1);
1189                 if (i in match) {
1190                     return match[i]
1191                 }
1192                 return key
1193             }
1194         }
1195     }
1196
1197     for (match of matcher.execAll(str)) {
1198         chunks.push(str.slice(index, match.index));
1199         chunks.push(replacement.replace(placeholder, replacer));
1200         index = match.index + match[0].length;
1201     }
1202     chunks.push(str.slice(index));
1203
1204     return chunks.join("")
1205 }
1206
1207 /**
1208  * Replace a given string by a given matcher.
1209  * @param {PatternMatcher} matcher The pattern matcher.
1210  * @param {string} str The string to be replaced.
1211  * @param {(...strs[])=>string} replace The function to replace each matched part.
1212  * @returns {string} The replaced string.
1213  */
1214 function replaceF(matcher, str, replace) {
1215     const chunks = [];
1216     let index = 0;
1217
1218     for (const match of matcher.execAll(str)) {
1219         chunks.push(str.slice(index, match.index));
1220         chunks.push(String(replace(...match, match.index, match.input)));
1221         index = match.index + match[0].length;
1222     }
1223     chunks.push(str.slice(index));
1224
1225     return chunks.join("")
1226 }
1227
1228 /**
1229  * The class to find patterns as considering escape sequences.
1230  */
1231 class PatternMatcher {
1232     /**
1233      * Initialize this matcher.
1234      * @param {RegExp} pattern The pattern to match.
1235      * @param {{escaped:boolean}} options The options.
1236      */
1237     constructor(pattern, { escaped = false } = {}) {
1238         if (!(pattern instanceof RegExp)) {
1239             throw new TypeError("'pattern' should be a RegExp instance.")
1240         }
1241         if (!pattern.flags.includes("g")) {
1242             throw new Error("'pattern' should contains 'g' flag.")
1243         }
1244
1245         internal.set(this, {
1246             pattern: new RegExp(pattern.source, pattern.flags),
1247             escaped: Boolean(escaped),
1248         });
1249     }
1250
1251     /**
1252      * Find the pattern in a given string.
1253      * @param {string} str The string to find.
1254      * @returns {IterableIterator<RegExpExecArray>} The iterator which iterate the matched information.
1255      */
1256     *execAll(str) {
1257         const { pattern, escaped } = internal.get(this);
1258         let match = null;
1259         let lastIndex = 0;
1260
1261         pattern.lastIndex = 0;
1262         while ((match = pattern.exec(str)) != null) {
1263             if (escaped || !isEscaped(str, match.index)) {
1264                 lastIndex = pattern.lastIndex;
1265                 yield match;
1266                 pattern.lastIndex = lastIndex;
1267             }
1268         }
1269     }
1270
1271     /**
1272      * Check whether the pattern is found in a given string.
1273      * @param {string} str The string to check.
1274      * @returns {boolean} `true` if the pattern was found in the string.
1275      */
1276     test(str) {
1277         const it = this.execAll(str);
1278         const ret = it.next();
1279         return !ret.done
1280     }
1281
1282     /**
1283      * Replace a given string.
1284      * @param {string} str The string to be replaced.
1285      * @param {(string|((...strs:string[])=>string))} replacer The string or function to replace. This is the same as the 2nd argument of `String.prototype.replace`.
1286      * @returns {string} The replaced string.
1287      */
1288     [Symbol.replace](str, replacer) {
1289         return typeof replacer === "function"
1290             ? replaceF(this, String(str), replacer)
1291             : replaceS(this, String(str), String(replacer))
1292     }
1293 }
1294
1295 const IMPORT_TYPE = /^(?:Import|Export(?:All|Default|Named))Declaration$/u;
1296 const has = Function.call.bind(Object.hasOwnProperty);
1297
1298 const READ = Symbol("read");
1299 const CALL = Symbol("call");
1300 const CONSTRUCT = Symbol("construct");
1301 const ESM = Symbol("esm");
1302
1303 const requireCall = { require: { [CALL]: true } };
1304
1305 /**
1306  * Check whether a given variable is modified or not.
1307  * @param {Variable} variable The variable to check.
1308  * @returns {boolean} `true` if the variable is modified.
1309  */
1310 function isModifiedGlobal(variable) {
1311     return (
1312         variable == null ||
1313         variable.defs.length !== 0 ||
1314         variable.references.some(r => r.isWrite())
1315     )
1316 }
1317
1318 /**
1319  * Check if the value of a given node is passed through to the parent syntax as-is.
1320  * For example, `a` and `b` in (`a || b` and `c ? a : b`) are passed through.
1321  * @param {Node} node A node to check.
1322  * @returns {boolean} `true` if the node is passed through.
1323  */
1324 function isPassThrough(node) {
1325     const parent = node.parent;
1326
1327     switch (parent && parent.type) {
1328         case "ConditionalExpression":
1329             return parent.consequent === node || parent.alternate === node
1330         case "LogicalExpression":
1331             return true
1332         case "SequenceExpression":
1333             return parent.expressions[parent.expressions.length - 1] === node
1334
1335         default:
1336             return false
1337     }
1338 }
1339
1340 /**
1341  * The reference tracker.
1342  */
1343 class ReferenceTracker {
1344     /**
1345      * Initialize this tracker.
1346      * @param {Scope} globalScope The global scope.
1347      * @param {object} [options] The options.
1348      * @param {"legacy"|"strict"} [options.mode="strict"] The mode to determine the ImportDeclaration's behavior for CJS modules.
1349      * @param {string[]} [options.globalObjectNames=["global","self","window"]] The variable names for Global Object.
1350      */
1351     constructor(
1352         globalScope,
1353         {
1354             mode = "strict",
1355             globalObjectNames = ["global", "self", "window"],
1356         } = {}
1357     ) {
1358         this.variableStack = [];
1359         this.globalScope = globalScope;
1360         this.mode = mode;
1361         this.globalObjectNames = globalObjectNames.slice(0);
1362     }
1363
1364     /**
1365      * Iterate the references of global variables.
1366      * @param {object} traceMap The trace map.
1367      * @returns {IterableIterator<{node:Node,path:string[],type:symbol,info:any}>} The iterator to iterate references.
1368      */
1369     *iterateGlobalReferences(traceMap) {
1370         for (const key of Object.keys(traceMap)) {
1371             const nextTraceMap = traceMap[key];
1372             const path = [key];
1373             const variable = this.globalScope.set.get(key);
1374
1375             if (isModifiedGlobal(variable)) {
1376                 continue
1377             }
1378
1379             yield* this._iterateVariableReferences(
1380                 variable,
1381                 path,
1382                 nextTraceMap,
1383                 true
1384             );
1385         }
1386
1387         for (const key of this.globalObjectNames) {
1388             const path = [];
1389             const variable = this.globalScope.set.get(key);
1390
1391             if (isModifiedGlobal(variable)) {
1392                 continue
1393             }
1394
1395             yield* this._iterateVariableReferences(
1396                 variable,
1397                 path,
1398                 traceMap,
1399                 false
1400             );
1401         }
1402     }
1403
1404     /**
1405      * Iterate the references of CommonJS modules.
1406      * @param {object} traceMap The trace map.
1407      * @returns {IterableIterator<{node:Node,path:string[],type:symbol,info:any}>} The iterator to iterate references.
1408      */
1409     *iterateCjsReferences(traceMap) {
1410         for (const { node } of this.iterateGlobalReferences(requireCall)) {
1411             const key = getStringIfConstant(node.arguments[0]);
1412             if (key == null || !has(traceMap, key)) {
1413                 continue
1414             }
1415
1416             const nextTraceMap = traceMap[key];
1417             const path = [key];
1418
1419             if (nextTraceMap[READ]) {
1420                 yield {
1421                     node,
1422                     path,
1423                     type: READ,
1424                     info: nextTraceMap[READ],
1425                 };
1426             }
1427             yield* this._iteratePropertyReferences(node, path, nextTraceMap);
1428         }
1429     }
1430
1431     /**
1432      * Iterate the references of ES modules.
1433      * @param {object} traceMap The trace map.
1434      * @returns {IterableIterator<{node:Node,path:string[],type:symbol,info:any}>} The iterator to iterate references.
1435      */
1436     *iterateEsmReferences(traceMap) {
1437         const programNode = this.globalScope.block;
1438
1439         for (const node of programNode.body) {
1440             if (!IMPORT_TYPE.test(node.type) || node.source == null) {
1441                 continue
1442             }
1443             const moduleId = node.source.value;
1444
1445             if (!has(traceMap, moduleId)) {
1446                 continue
1447             }
1448             const nextTraceMap = traceMap[moduleId];
1449             const path = [moduleId];
1450
1451             if (nextTraceMap[READ]) {
1452                 yield { node, path, type: READ, info: nextTraceMap[READ] };
1453             }
1454
1455             if (node.type === "ExportAllDeclaration") {
1456                 for (const key of Object.keys(nextTraceMap)) {
1457                     const exportTraceMap = nextTraceMap[key];
1458                     if (exportTraceMap[READ]) {
1459                         yield {
1460                             node,
1461                             path: path.concat(key),
1462                             type: READ,
1463                             info: exportTraceMap[READ],
1464                         };
1465                     }
1466                 }
1467             } else {
1468                 for (const specifier of node.specifiers) {
1469                     const esm = has(nextTraceMap, ESM);
1470                     const it = this._iterateImportReferences(
1471                         specifier,
1472                         path,
1473                         esm
1474                             ? nextTraceMap
1475                             : this.mode === "legacy"
1476                             ? Object.assign(
1477                                   { default: nextTraceMap },
1478                                   nextTraceMap
1479                               )
1480                             : { default: nextTraceMap }
1481                     );
1482
1483                     if (esm) {
1484                         yield* it;
1485                     } else {
1486                         for (const report of it) {
1487                             report.path = report.path.filter(exceptDefault);
1488                             if (
1489                                 report.path.length >= 2 ||
1490                                 report.type !== READ
1491                             ) {
1492                                 yield report;
1493                             }
1494                         }
1495                     }
1496                 }
1497             }
1498         }
1499     }
1500
1501     /**
1502      * Iterate the references for a given variable.
1503      * @param {Variable} variable The variable to iterate that references.
1504      * @param {string[]} path The current path.
1505      * @param {object} traceMap The trace map.
1506      * @param {boolean} shouldReport = The flag to report those references.
1507      * @returns {IterableIterator<{node:Node,path:string[],type:symbol,info:any}>} The iterator to iterate references.
1508      */
1509     *_iterateVariableReferences(variable, path, traceMap, shouldReport) {
1510         if (this.variableStack.includes(variable)) {
1511             return
1512         }
1513         this.variableStack.push(variable);
1514         try {
1515             for (const reference of variable.references) {
1516                 if (!reference.isRead()) {
1517                     continue
1518                 }
1519                 const node = reference.identifier;
1520
1521                 if (shouldReport && traceMap[READ]) {
1522                     yield { node, path, type: READ, info: traceMap[READ] };
1523                 }
1524                 yield* this._iteratePropertyReferences(node, path, traceMap);
1525             }
1526         } finally {
1527             this.variableStack.pop();
1528         }
1529     }
1530
1531     /**
1532      * Iterate the references for a given AST node.
1533      * @param rootNode The AST node to iterate references.
1534      * @param {string[]} path The current path.
1535      * @param {object} traceMap The trace map.
1536      * @returns {IterableIterator<{node:Node,path:string[],type:symbol,info:any}>} The iterator to iterate references.
1537      */
1538     //eslint-disable-next-line complexity
1539     *_iteratePropertyReferences(rootNode, path, traceMap) {
1540         let node = rootNode;
1541         while (isPassThrough(node)) {
1542             node = node.parent;
1543         }
1544
1545         const parent = node.parent;
1546         if (parent.type === "MemberExpression") {
1547             if (parent.object === node) {
1548                 const key = getPropertyName(parent);
1549                 if (key == null || !has(traceMap, key)) {
1550                     return
1551                 }
1552
1553                 path = path.concat(key); //eslint-disable-line no-param-reassign
1554                 const nextTraceMap = traceMap[key];
1555                 if (nextTraceMap[READ]) {
1556                     yield {
1557                         node: parent,
1558                         path,
1559                         type: READ,
1560                         info: nextTraceMap[READ],
1561                     };
1562                 }
1563                 yield* this._iteratePropertyReferences(
1564                     parent,
1565                     path,
1566                     nextTraceMap
1567                 );
1568             }
1569             return
1570         }
1571         if (parent.type === "CallExpression") {
1572             if (parent.callee === node && traceMap[CALL]) {
1573                 yield { node: parent, path, type: CALL, info: traceMap[CALL] };
1574             }
1575             return
1576         }
1577         if (parent.type === "NewExpression") {
1578             if (parent.callee === node && traceMap[CONSTRUCT]) {
1579                 yield {
1580                     node: parent,
1581                     path,
1582                     type: CONSTRUCT,
1583                     info: traceMap[CONSTRUCT],
1584                 };
1585             }
1586             return
1587         }
1588         if (parent.type === "AssignmentExpression") {
1589             if (parent.right === node) {
1590                 yield* this._iterateLhsReferences(parent.left, path, traceMap);
1591                 yield* this._iteratePropertyReferences(parent, path, traceMap);
1592             }
1593             return
1594         }
1595         if (parent.type === "AssignmentPattern") {
1596             if (parent.right === node) {
1597                 yield* this._iterateLhsReferences(parent.left, path, traceMap);
1598             }
1599             return
1600         }
1601         if (parent.type === "VariableDeclarator") {
1602             if (parent.init === node) {
1603                 yield* this._iterateLhsReferences(parent.id, path, traceMap);
1604             }
1605         }
1606     }
1607
1608     /**
1609      * Iterate the references for a given Pattern node.
1610      * @param {Node} patternNode The Pattern node to iterate references.
1611      * @param {string[]} path The current path.
1612      * @param {object} traceMap The trace map.
1613      * @returns {IterableIterator<{node:Node,path:string[],type:symbol,info:any}>} The iterator to iterate references.
1614      */
1615     *_iterateLhsReferences(patternNode, path, traceMap) {
1616         if (patternNode.type === "Identifier") {
1617             const variable = findVariable(this.globalScope, patternNode);
1618             if (variable != null) {
1619                 yield* this._iterateVariableReferences(
1620                     variable,
1621                     path,
1622                     traceMap,
1623                     false
1624                 );
1625             }
1626             return
1627         }
1628         if (patternNode.type === "ObjectPattern") {
1629             for (const property of patternNode.properties) {
1630                 const key = getPropertyName(property);
1631
1632                 if (key == null || !has(traceMap, key)) {
1633                     continue
1634                 }
1635
1636                 const nextPath = path.concat(key);
1637                 const nextTraceMap = traceMap[key];
1638                 if (nextTraceMap[READ]) {
1639                     yield {
1640                         node: property,
1641                         path: nextPath,
1642                         type: READ,
1643                         info: nextTraceMap[READ],
1644                     };
1645                 }
1646                 yield* this._iterateLhsReferences(
1647                     property.value,
1648                     nextPath,
1649                     nextTraceMap
1650                 );
1651             }
1652             return
1653         }
1654         if (patternNode.type === "AssignmentPattern") {
1655             yield* this._iterateLhsReferences(patternNode.left, path, traceMap);
1656         }
1657     }
1658
1659     /**
1660      * Iterate the references for a given ModuleSpecifier node.
1661      * @param {Node} specifierNode The ModuleSpecifier node to iterate references.
1662      * @param {string[]} path The current path.
1663      * @param {object} traceMap The trace map.
1664      * @returns {IterableIterator<{node:Node,path:string[],type:symbol,info:any}>} The iterator to iterate references.
1665      */
1666     *_iterateImportReferences(specifierNode, path, traceMap) {
1667         const type = specifierNode.type;
1668
1669         if (type === "ImportSpecifier" || type === "ImportDefaultSpecifier") {
1670             const key =
1671                 type === "ImportDefaultSpecifier"
1672                     ? "default"
1673                     : specifierNode.imported.name;
1674             if (!has(traceMap, key)) {
1675                 return
1676             }
1677
1678             path = path.concat(key); //eslint-disable-line no-param-reassign
1679             const nextTraceMap = traceMap[key];
1680             if (nextTraceMap[READ]) {
1681                 yield {
1682                     node: specifierNode,
1683                     path,
1684                     type: READ,
1685                     info: nextTraceMap[READ],
1686                 };
1687             }
1688             yield* this._iterateVariableReferences(
1689                 findVariable(this.globalScope, specifierNode.local),
1690                 path,
1691                 nextTraceMap,
1692                 false
1693             );
1694
1695             return
1696         }
1697
1698         if (type === "ImportNamespaceSpecifier") {
1699             yield* this._iterateVariableReferences(
1700                 findVariable(this.globalScope, specifierNode.local),
1701                 path,
1702                 traceMap,
1703                 false
1704             );
1705             return
1706         }
1707
1708         if (type === "ExportSpecifier") {
1709             const key = specifierNode.local.name;
1710             if (!has(traceMap, key)) {
1711                 return
1712             }
1713
1714             path = path.concat(key); //eslint-disable-line no-param-reassign
1715             const nextTraceMap = traceMap[key];
1716             if (nextTraceMap[READ]) {
1717                 yield {
1718                     node: specifierNode,
1719                     path,
1720                     type: READ,
1721                     info: nextTraceMap[READ],
1722                 };
1723             }
1724         }
1725     }
1726 }
1727
1728 ReferenceTracker.READ = READ;
1729 ReferenceTracker.CALL = CALL;
1730 ReferenceTracker.CONSTRUCT = CONSTRUCT;
1731 ReferenceTracker.ESM = ESM;
1732
1733 /**
1734  * This is a predicate function for Array#filter.
1735  * @param {string} name A name part.
1736  * @param {number} index The index of the name.
1737  * @returns {boolean} `false` if it's default.
1738  */
1739 function exceptDefault(name, index) {
1740     return !(index === 1 && name === "default")
1741 }
1742
1743 var index = {
1744     CALL,
1745     CONSTRUCT,
1746     ESM,
1747     findVariable,
1748     getFunctionHeadLocation,
1749     getFunctionNameWithKind,
1750     getInnermostScope,
1751     getPropertyName,
1752     getStaticValue,
1753     getStringIfConstant,
1754     hasSideEffect,
1755     isArrowToken,
1756     isClosingBraceToken,
1757     isClosingBracketToken,
1758     isClosingParenToken,
1759     isColonToken,
1760     isCommaToken,
1761     isCommentToken,
1762     isNotArrowToken,
1763     isNotClosingBraceToken,
1764     isNotClosingBracketToken,
1765     isNotClosingParenToken,
1766     isNotColonToken,
1767     isNotCommaToken,
1768     isNotCommentToken,
1769     isNotOpeningBraceToken,
1770     isNotOpeningBracketToken,
1771     isNotOpeningParenToken,
1772     isNotSemicolonToken,
1773     isOpeningBraceToken,
1774     isOpeningBracketToken,
1775     isOpeningParenToken,
1776     isParenthesized,
1777     isSemicolonToken,
1778     PatternMatcher,
1779     READ,
1780     ReferenceTracker,
1781 };
1782
1783 export default index;
1784 export { CALL, CONSTRUCT, ESM, PatternMatcher, READ, ReferenceTracker, findVariable, getFunctionHeadLocation, getFunctionNameWithKind, getInnermostScope, getPropertyName, getStaticValue, getStringIfConstant, hasSideEffect, isArrowToken, isClosingBraceToken, isClosingBracketToken, isClosingParenToken, isColonToken, isCommaToken, isCommentToken, isNotArrowToken, isNotClosingBraceToken, isNotClosingBracketToken, isNotClosingParenToken, isNotColonToken, isNotCommaToken, isNotCommentToken, isNotOpeningBraceToken, isNotOpeningBracketToken, isNotOpeningParenToken, isNotSemicolonToken, isOpeningBraceToken, isOpeningBracketToken, isOpeningParenToken, isParenthesized, isSemicolonToken };
1785 //# sourceMappingURL=index.mjs.map