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