1/*! @author Toru Nagashima <https://github.com/mysticatea> */
2import 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 */
10function 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 */
37function 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 */
64function 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 */
73function 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 */
82function 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 */
91function 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 */
100function 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 */
109function 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 */
118function 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 */
127function 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 */
136function 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 */
145function 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 */
154function 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 */
163function 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 */
172function isCommentToken(token) {
173    return (
174        token.type === "Line" ||
175        token.type === "Block" ||
176        token.type === "Shebang"
177    )
178}
179
180const isNotArrowToken = negate(isArrowToken);
181const isNotCommaToken = negate(isCommaToken);
182const isNotSemicolonToken = negate(isSemicolonToken);
183const isNotColonToken = negate(isColonToken);
184const isNotOpeningParenToken = negate(isOpeningParenToken);
185const isNotClosingParenToken = negate(isClosingParenToken);
186const isNotOpeningBracketToken = negate(isOpeningBracketToken);
187const isNotClosingBracketToken = negate(isClosingBracketToken);
188const isNotOpeningBraceToken = negate(isOpeningBraceToken);
189const isNotClosingBraceToken = negate(isClosingBraceToken);
190const 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 */
198function 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 */
210function 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
239const 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
250const 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);
300const 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);
344const 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 */
355function 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 */
372function 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 */
383function 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
409const 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 */
732function 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 */
745function 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 */
759function 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 */
780function 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 */
809function 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
856const 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);
878const typeConversionUnaryOps = Object.freeze(new Set(["-", "+", "!", "~"]));
879const 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 */
1010function 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 */
1029function 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 */
1095function 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
1140const placeholder = /\$(?:[$&`']|[1-9][0-9]?)/gu;
1141
1142/** @type {WeakMap<PatternMatcher, {pattern:RegExp,escaped:boolean}>} */
1143const 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 */
1151function 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 */
1166function 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 */
1214function 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 */
1231class 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
1295const IMPORT_TYPE = /^(?:Import|Export(?:All|Default|Named))Declaration$/u;
1296const has = Function.call.bind(Object.hasOwnProperty);
1297
1298const READ = Symbol("read");
1299const CALL = Symbol("call");
1300const CONSTRUCT = Symbol("construct");
1301const ESM = Symbol("esm");
1302
1303const 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 */
1310function 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 */
1324function 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 */
1343class 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
1728ReferenceTracker.READ = READ;
1729ReferenceTracker.CALL = CALL;
1730ReferenceTracker.CONSTRUCT = CONSTRUCT;
1731ReferenceTracker.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 */
1739function exceptDefault(name, index) {
1740    return !(index === 1 && name === "default")
1741}
1742
1743var 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
1783export default index;
1784export { 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
1786