source: imaps-frontend/node_modules/@eslint-community/eslint-utils/index.mjs

main
Last change on this file was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

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