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