source: imaps-frontend/node_modules/@eslint-community/eslint-utils/index.js@ 0c6b92a

main
Last change on this file since 0c6b92a was 0c6b92a, checked in by stefan toskovski <stefantoska84@…>, 5 weeks ago

Pred finalna verzija

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