|
'use strict'; |
|
|
|
Object.defineProperty(exports, '__esModule', { value: true }); |
|
|
|
var eslintVisitorKeys = require('eslint-visitor-keys'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getInnermostScope(initialScope, node) { |
|
const location = node.range[0]; |
|
|
|
let scope = initialScope; |
|
let found = false; |
|
do { |
|
found = false; |
|
for (const childScope of scope.childScopes) { |
|
const range = childScope.block.range; |
|
|
|
if (range[0] <= location && location < range[1]) { |
|
scope = childScope; |
|
found = true; |
|
break |
|
} |
|
} |
|
} while (found) |
|
|
|
return scope |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function findVariable(initialScope, nameOrNode) { |
|
let name = ""; |
|
let scope = initialScope; |
|
|
|
if (typeof nameOrNode === "string") { |
|
name = nameOrNode; |
|
} else { |
|
name = nameOrNode.name; |
|
scope = getInnermostScope(scope, nameOrNode); |
|
} |
|
|
|
while (scope != null) { |
|
const variable = scope.set.get(name); |
|
if (variable != null) { |
|
return variable |
|
} |
|
scope = scope.upper; |
|
} |
|
|
|
return null |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function negate0(token) { |
|
return !this(token) |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function negate(f) { |
|
return negate0.bind(f) |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function isPunctuatorTokenWithValue(token, value) { |
|
return token.type === "Punctuator" && token.value === value |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function isArrowToken(token) { |
|
return isPunctuatorTokenWithValue(token, "=>") |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function isCommaToken(token) { |
|
return isPunctuatorTokenWithValue(token, ",") |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function isSemicolonToken(token) { |
|
return isPunctuatorTokenWithValue(token, ";") |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function isColonToken(token) { |
|
return isPunctuatorTokenWithValue(token, ":") |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function isOpeningParenToken(token) { |
|
return isPunctuatorTokenWithValue(token, "(") |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function isClosingParenToken(token) { |
|
return isPunctuatorTokenWithValue(token, ")") |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function isOpeningBracketToken(token) { |
|
return isPunctuatorTokenWithValue(token, "[") |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function isClosingBracketToken(token) { |
|
return isPunctuatorTokenWithValue(token, "]") |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function isOpeningBraceToken(token) { |
|
return isPunctuatorTokenWithValue(token, "{") |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function isClosingBraceToken(token) { |
|
return isPunctuatorTokenWithValue(token, "}") |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function isCommentToken(token) { |
|
return ["Block", "Line", "Shebang"].includes(token.type) |
|
} |
|
|
|
const isNotArrowToken = negate(isArrowToken); |
|
const isNotCommaToken = negate(isCommaToken); |
|
const isNotSemicolonToken = negate(isSemicolonToken); |
|
const isNotColonToken = negate(isColonToken); |
|
const isNotOpeningParenToken = negate(isOpeningParenToken); |
|
const isNotClosingParenToken = negate(isClosingParenToken); |
|
const isNotOpeningBracketToken = negate(isOpeningBracketToken); |
|
const isNotClosingBracketToken = negate(isClosingBracketToken); |
|
const isNotOpeningBraceToken = negate(isOpeningBraceToken); |
|
const isNotClosingBraceToken = negate(isClosingBraceToken); |
|
const isNotCommentToken = negate(isCommentToken); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getOpeningParenOfParams(node, sourceCode) { |
|
return node.id |
|
? sourceCode.getTokenAfter(node.id, isOpeningParenToken) |
|
: sourceCode.getFirstToken(node, isOpeningParenToken) |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getFunctionHeadLocation(node, sourceCode) { |
|
const parent = node.parent; |
|
let start = null; |
|
let end = null; |
|
|
|
if (node.type === "ArrowFunctionExpression") { |
|
const arrowToken = sourceCode.getTokenBefore(node.body, isArrowToken); |
|
|
|
start = arrowToken.loc.start; |
|
end = arrowToken.loc.end; |
|
} else if ( |
|
parent.type === "Property" || |
|
parent.type === "MethodDefinition" || |
|
parent.type === "PropertyDefinition" |
|
) { |
|
start = parent.loc.start; |
|
end = getOpeningParenOfParams(node, sourceCode).loc.start; |
|
} else { |
|
start = node.loc.start; |
|
end = getOpeningParenOfParams(node, sourceCode).loc.start; |
|
} |
|
|
|
return { |
|
start: { ...start }, |
|
end: { ...end }, |
|
} |
|
} |
|
|
|
|
|
|
|
const globalObject = |
|
typeof globalThis !== "undefined" |
|
? globalThis |
|
: typeof self !== "undefined" |
|
? self |
|
: typeof window !== "undefined" |
|
? window |
|
: typeof global !== "undefined" |
|
? global |
|
: {}; |
|
|
|
const builtinNames = Object.freeze( |
|
new Set([ |
|
"Array", |
|
"ArrayBuffer", |
|
"BigInt", |
|
"BigInt64Array", |
|
"BigUint64Array", |
|
"Boolean", |
|
"DataView", |
|
"Date", |
|
"decodeURI", |
|
"decodeURIComponent", |
|
"encodeURI", |
|
"encodeURIComponent", |
|
"escape", |
|
"Float32Array", |
|
"Float64Array", |
|
"Function", |
|
"Infinity", |
|
"Int16Array", |
|
"Int32Array", |
|
"Int8Array", |
|
"isFinite", |
|
"isNaN", |
|
"isPrototypeOf", |
|
"JSON", |
|
"Map", |
|
"Math", |
|
"NaN", |
|
"Number", |
|
"Object", |
|
"parseFloat", |
|
"parseInt", |
|
"Promise", |
|
"Proxy", |
|
"Reflect", |
|
"RegExp", |
|
"Set", |
|
"String", |
|
"Symbol", |
|
"Uint16Array", |
|
"Uint32Array", |
|
"Uint8Array", |
|
"Uint8ClampedArray", |
|
"undefined", |
|
"unescape", |
|
"WeakMap", |
|
"WeakSet", |
|
]), |
|
); |
|
const callAllowed = new Set( |
|
[ |
|
Array.isArray, |
|
Array.of, |
|
Array.prototype.at, |
|
Array.prototype.concat, |
|
Array.prototype.entries, |
|
Array.prototype.every, |
|
Array.prototype.filter, |
|
Array.prototype.find, |
|
Array.prototype.findIndex, |
|
Array.prototype.flat, |
|
Array.prototype.includes, |
|
Array.prototype.indexOf, |
|
Array.prototype.join, |
|
Array.prototype.keys, |
|
Array.prototype.lastIndexOf, |
|
Array.prototype.slice, |
|
Array.prototype.some, |
|
Array.prototype.toString, |
|
Array.prototype.values, |
|
typeof BigInt === "function" ? BigInt : undefined, |
|
Boolean, |
|
Date, |
|
Date.parse, |
|
decodeURI, |
|
decodeURIComponent, |
|
encodeURI, |
|
encodeURIComponent, |
|
escape, |
|
isFinite, |
|
isNaN, |
|
isPrototypeOf, |
|
Map, |
|
Map.prototype.entries, |
|
Map.prototype.get, |
|
Map.prototype.has, |
|
Map.prototype.keys, |
|
Map.prototype.values, |
|
...Object.getOwnPropertyNames(Math) |
|
.filter((k) => k !== "random") |
|
.map((k) => Math[k]) |
|
.filter((f) => typeof f === "function"), |
|
Number, |
|
Number.isFinite, |
|
Number.isNaN, |
|
Number.parseFloat, |
|
Number.parseInt, |
|
Number.prototype.toExponential, |
|
Number.prototype.toFixed, |
|
Number.prototype.toPrecision, |
|
Number.prototype.toString, |
|
Object, |
|
Object.entries, |
|
Object.is, |
|
Object.isExtensible, |
|
Object.isFrozen, |
|
Object.isSealed, |
|
Object.keys, |
|
Object.values, |
|
parseFloat, |
|
parseInt, |
|
RegExp, |
|
Set, |
|
Set.prototype.entries, |
|
Set.prototype.has, |
|
Set.prototype.keys, |
|
Set.prototype.values, |
|
String, |
|
String.fromCharCode, |
|
String.fromCodePoint, |
|
String.raw, |
|
String.prototype.at, |
|
String.prototype.charAt, |
|
String.prototype.charCodeAt, |
|
String.prototype.codePointAt, |
|
String.prototype.concat, |
|
String.prototype.endsWith, |
|
String.prototype.includes, |
|
String.prototype.indexOf, |
|
String.prototype.lastIndexOf, |
|
String.prototype.normalize, |
|
String.prototype.padEnd, |
|
String.prototype.padStart, |
|
String.prototype.slice, |
|
String.prototype.startsWith, |
|
String.prototype.substr, |
|
String.prototype.substring, |
|
String.prototype.toLowerCase, |
|
String.prototype.toString, |
|
String.prototype.toUpperCase, |
|
String.prototype.trim, |
|
String.prototype.trimEnd, |
|
String.prototype.trimLeft, |
|
String.prototype.trimRight, |
|
String.prototype.trimStart, |
|
Symbol.for, |
|
Symbol.keyFor, |
|
unescape, |
|
].filter((f) => typeof f === "function"), |
|
); |
|
const callPassThrough = new Set([ |
|
Object.freeze, |
|
Object.preventExtensions, |
|
Object.seal, |
|
]); |
|
|
|
|
|
const getterAllowed = [ |
|
[Map, new Set(["size"])], |
|
[ |
|
RegExp, |
|
new Set([ |
|
"dotAll", |
|
"flags", |
|
"global", |
|
"hasIndices", |
|
"ignoreCase", |
|
"multiline", |
|
"source", |
|
"sticky", |
|
"unicode", |
|
]), |
|
], |
|
[Set, new Set(["size"])], |
|
]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
function getPropertyDescriptor(object, name) { |
|
let x = object; |
|
while ((typeof x === "object" || typeof x === "function") && x !== null) { |
|
const d = Object.getOwnPropertyDescriptor(x, name); |
|
if (d) { |
|
return d |
|
} |
|
x = Object.getPrototypeOf(x); |
|
} |
|
return null |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function isGetter(object, name) { |
|
const d = getPropertyDescriptor(object, name); |
|
return d != null && d.get != null |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getElementValues(nodeList, initialScope) { |
|
const valueList = []; |
|
|
|
for (let i = 0; i < nodeList.length; ++i) { |
|
const elementNode = nodeList[i]; |
|
|
|
if (elementNode == null) { |
|
valueList.length = i + 1; |
|
} else if (elementNode.type === "SpreadElement") { |
|
const argument = getStaticValueR(elementNode.argument, initialScope); |
|
if (argument == null) { |
|
return null |
|
} |
|
valueList.push(...argument.value); |
|
} else { |
|
const element = getStaticValueR(elementNode, initialScope); |
|
if (element == null) { |
|
return null |
|
} |
|
valueList.push(element.value); |
|
} |
|
} |
|
|
|
return valueList |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function isEffectivelyConst(variable) { |
|
const refs = variable.references; |
|
|
|
const inits = refs.filter((r) => r.init).length; |
|
const reads = refs.filter((r) => r.isReadOnly()).length; |
|
if (inits === 1 && reads + inits === refs.length) { |
|
|
|
return true |
|
} |
|
return false |
|
} |
|
|
|
const operations = Object.freeze({ |
|
ArrayExpression(node, initialScope) { |
|
const elements = getElementValues(node.elements, initialScope); |
|
return elements != null ? { value: elements } : null |
|
}, |
|
|
|
AssignmentExpression(node, initialScope) { |
|
if (node.operator === "=") { |
|
return getStaticValueR(node.right, initialScope) |
|
} |
|
return null |
|
}, |
|
|
|
|
|
BinaryExpression(node, initialScope) { |
|
if (node.operator === "in" || node.operator === "instanceof") { |
|
|
|
return null |
|
} |
|
|
|
const left = getStaticValueR(node.left, initialScope); |
|
const right = getStaticValueR(node.right, initialScope); |
|
if (left != null && right != null) { |
|
switch (node.operator) { |
|
case "==": |
|
return { value: left.value == right.value } |
|
case "!=": |
|
return { value: left.value != right.value } |
|
case "===": |
|
return { value: left.value === right.value } |
|
case "!==": |
|
return { value: left.value !== right.value } |
|
case "<": |
|
return { value: left.value < right.value } |
|
case "<=": |
|
return { value: left.value <= right.value } |
|
case ">": |
|
return { value: left.value > right.value } |
|
case ">=": |
|
return { value: left.value >= right.value } |
|
case "<<": |
|
return { value: left.value << right.value } |
|
case ">>": |
|
return { value: left.value >> right.value } |
|
case ">>>": |
|
return { value: left.value >>> right.value } |
|
case "+": |
|
return { value: left.value + right.value } |
|
case "-": |
|
return { value: left.value - right.value } |
|
case "*": |
|
return { value: left.value * right.value } |
|
case "/": |
|
return { value: left.value / right.value } |
|
case "%": |
|
return { value: left.value % right.value } |
|
case "**": |
|
return { value: left.value ** right.value } |
|
case "|": |
|
return { value: left.value | right.value } |
|
case "^": |
|
return { value: left.value ^ right.value } |
|
case "&": |
|
return { value: left.value & right.value } |
|
|
|
|
|
} |
|
} |
|
|
|
return null |
|
}, |
|
|
|
CallExpression(node, initialScope) { |
|
const calleeNode = node.callee; |
|
const args = getElementValues(node.arguments, initialScope); |
|
|
|
if (args != null) { |
|
if (calleeNode.type === "MemberExpression") { |
|
if (calleeNode.property.type === "PrivateIdentifier") { |
|
return null |
|
} |
|
const object = getStaticValueR(calleeNode.object, initialScope); |
|
if (object != null) { |
|
if ( |
|
object.value == null && |
|
(object.optional || node.optional) |
|
) { |
|
return { value: undefined, optional: true } |
|
} |
|
const property = getStaticPropertyNameValue( |
|
calleeNode, |
|
initialScope, |
|
); |
|
|
|
if (property != null) { |
|
const receiver = object.value; |
|
const methodName = property.value; |
|
if (callAllowed.has(receiver[methodName])) { |
|
return { value: receiver[methodName](...args) } |
|
} |
|
if (callPassThrough.has(receiver[methodName])) { |
|
return { value: args[0] } |
|
} |
|
} |
|
} |
|
} else { |
|
const callee = getStaticValueR(calleeNode, initialScope); |
|
if (callee != null) { |
|
if (callee.value == null && node.optional) { |
|
return { value: undefined, optional: true } |
|
} |
|
const func = callee.value; |
|
if (callAllowed.has(func)) { |
|
return { value: func(...args) } |
|
} |
|
if (callPassThrough.has(func)) { |
|
return { value: args[0] } |
|
} |
|
} |
|
} |
|
} |
|
|
|
return null |
|
}, |
|
|
|
ConditionalExpression(node, initialScope) { |
|
const test = getStaticValueR(node.test, initialScope); |
|
if (test != null) { |
|
return test.value |
|
? getStaticValueR(node.consequent, initialScope) |
|
: getStaticValueR(node.alternate, initialScope) |
|
} |
|
return null |
|
}, |
|
|
|
ExpressionStatement(node, initialScope) { |
|
return getStaticValueR(node.expression, initialScope) |
|
}, |
|
|
|
Identifier(node, initialScope) { |
|
if (initialScope != null) { |
|
const variable = findVariable(initialScope, node); |
|
|
|
|
|
if ( |
|
variable != null && |
|
variable.defs.length === 0 && |
|
builtinNames.has(variable.name) && |
|
variable.name in globalObject |
|
) { |
|
return { value: globalObject[variable.name] } |
|
} |
|
|
|
|
|
if (variable != null && variable.defs.length === 1) { |
|
const def = variable.defs[0]; |
|
if ( |
|
def.parent && |
|
def.type === "Variable" && |
|
(def.parent.kind === "const" || |
|
isEffectivelyConst(variable)) && |
|
|
|
def.node.id.type === "Identifier" |
|
) { |
|
return getStaticValueR(def.node.init, initialScope) |
|
} |
|
} |
|
} |
|
return null |
|
}, |
|
|
|
Literal(node) { |
|
|
|
if ((node.regex != null || node.bigint != null) && node.value == null) { |
|
|
|
return null |
|
} |
|
return { value: node.value } |
|
}, |
|
|
|
LogicalExpression(node, initialScope) { |
|
const left = getStaticValueR(node.left, initialScope); |
|
if (left != null) { |
|
if ( |
|
(node.operator === "||" && Boolean(left.value) === true) || |
|
(node.operator === "&&" && Boolean(left.value) === false) || |
|
(node.operator === "??" && left.value != null) |
|
) { |
|
return left |
|
} |
|
|
|
const right = getStaticValueR(node.right, initialScope); |
|
if (right != null) { |
|
return right |
|
} |
|
} |
|
|
|
return null |
|
}, |
|
|
|
MemberExpression(node, initialScope) { |
|
if (node.property.type === "PrivateIdentifier") { |
|
return null |
|
} |
|
const object = getStaticValueR(node.object, initialScope); |
|
if (object != null) { |
|
if (object.value == null && (object.optional || node.optional)) { |
|
return { value: undefined, optional: true } |
|
} |
|
const property = getStaticPropertyNameValue(node, initialScope); |
|
|
|
if (property != null) { |
|
if (!isGetter(object.value, property.value)) { |
|
return { value: object.value[property.value] } |
|
} |
|
|
|
for (const [classFn, allowed] of getterAllowed) { |
|
if ( |
|
object.value instanceof classFn && |
|
allowed.has(property.value) |
|
) { |
|
return { value: object.value[property.value] } |
|
} |
|
} |
|
} |
|
} |
|
return null |
|
}, |
|
|
|
ChainExpression(node, initialScope) { |
|
const expression = getStaticValueR(node.expression, initialScope); |
|
if (expression != null) { |
|
return { value: expression.value } |
|
} |
|
return null |
|
}, |
|
|
|
NewExpression(node, initialScope) { |
|
const callee = getStaticValueR(node.callee, initialScope); |
|
const args = getElementValues(node.arguments, initialScope); |
|
|
|
if (callee != null && args != null) { |
|
const Func = callee.value; |
|
if (callAllowed.has(Func)) { |
|
return { value: new Func(...args) } |
|
} |
|
} |
|
|
|
return null |
|
}, |
|
|
|
ObjectExpression(node, initialScope) { |
|
const object = {}; |
|
|
|
for (const propertyNode of node.properties) { |
|
if (propertyNode.type === "Property") { |
|
if (propertyNode.kind !== "init") { |
|
return null |
|
} |
|
const key = getStaticPropertyNameValue( |
|
propertyNode, |
|
initialScope, |
|
); |
|
const value = getStaticValueR(propertyNode.value, initialScope); |
|
if (key == null || value == null) { |
|
return null |
|
} |
|
object[key.value] = value.value; |
|
} else if ( |
|
propertyNode.type === "SpreadElement" || |
|
propertyNode.type === "ExperimentalSpreadProperty" |
|
) { |
|
const argument = getStaticValueR( |
|
propertyNode.argument, |
|
initialScope, |
|
); |
|
if (argument == null) { |
|
return null |
|
} |
|
Object.assign(object, argument.value); |
|
} else { |
|
return null |
|
} |
|
} |
|
|
|
return { value: object } |
|
}, |
|
|
|
SequenceExpression(node, initialScope) { |
|
const last = node.expressions[node.expressions.length - 1]; |
|
return getStaticValueR(last, initialScope) |
|
}, |
|
|
|
TaggedTemplateExpression(node, initialScope) { |
|
const tag = getStaticValueR(node.tag, initialScope); |
|
const expressions = getElementValues( |
|
node.quasi.expressions, |
|
initialScope, |
|
); |
|
|
|
if (tag != null && expressions != null) { |
|
const func = tag.value; |
|
const strings = node.quasi.quasis.map((q) => q.value.cooked); |
|
strings.raw = node.quasi.quasis.map((q) => q.value.raw); |
|
|
|
if (func === String.raw) { |
|
return { value: func(strings, ...expressions) } |
|
} |
|
} |
|
|
|
return null |
|
}, |
|
|
|
TemplateLiteral(node, initialScope) { |
|
const expressions = getElementValues(node.expressions, initialScope); |
|
if (expressions != null) { |
|
let value = node.quasis[0].value.cooked; |
|
for (let i = 0; i < expressions.length; ++i) { |
|
value += expressions[i]; |
|
value += node.quasis[i + 1].value.cooked; |
|
} |
|
return { value } |
|
} |
|
return null |
|
}, |
|
|
|
UnaryExpression(node, initialScope) { |
|
if (node.operator === "delete") { |
|
|
|
return null |
|
} |
|
if (node.operator === "void") { |
|
return { value: undefined } |
|
} |
|
|
|
const arg = getStaticValueR(node.argument, initialScope); |
|
if (arg != null) { |
|
switch (node.operator) { |
|
case "-": |
|
return { value: -arg.value } |
|
case "+": |
|
return { value: +arg.value } |
|
case "!": |
|
return { value: !arg.value } |
|
case "~": |
|
return { value: ~arg.value } |
|
case "typeof": |
|
return { value: typeof arg.value } |
|
|
|
|
|
} |
|
} |
|
|
|
return null |
|
}, |
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getStaticValueR(node, initialScope) { |
|
if (node != null && Object.hasOwnProperty.call(operations, node.type)) { |
|
return operations[node.type](node, initialScope) |
|
} |
|
return null |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getStaticPropertyNameValue(node, initialScope) { |
|
const nameNode = node.type === "Property" ? node.key : node.property; |
|
|
|
if (node.computed) { |
|
return getStaticValueR(nameNode, initialScope) |
|
} |
|
|
|
if (nameNode.type === "Identifier") { |
|
return { value: nameNode.name } |
|
} |
|
|
|
if (nameNode.type === "Literal") { |
|
if (nameNode.bigint) { |
|
return { value: nameNode.bigint } |
|
} |
|
return { value: String(nameNode.value) } |
|
} |
|
|
|
return null |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getStaticValue(node, initialScope = null) { |
|
try { |
|
return getStaticValueR(node, initialScope) |
|
} catch (_error) { |
|
return null |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getStringIfConstant(node, initialScope = null) { |
|
|
|
if (node && node.type === "Literal" && node.value === null) { |
|
if (node.regex) { |
|
return `/${node.regex.pattern}/${node.regex.flags}` |
|
} |
|
if (node.bigint) { |
|
return node.bigint |
|
} |
|
} |
|
|
|
const evaluated = getStaticValue(node, initialScope); |
|
return evaluated && String(evaluated.value) |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getPropertyName(node, initialScope) { |
|
switch (node.type) { |
|
case "MemberExpression": |
|
if (node.computed) { |
|
return getStringIfConstant(node.property, initialScope) |
|
} |
|
if (node.property.type === "PrivateIdentifier") { |
|
return null |
|
} |
|
return node.property.name |
|
|
|
case "Property": |
|
case "MethodDefinition": |
|
case "PropertyDefinition": |
|
if (node.computed) { |
|
return getStringIfConstant(node.key, initialScope) |
|
} |
|
if (node.key.type === "Literal") { |
|
return String(node.key.value) |
|
} |
|
if (node.key.type === "PrivateIdentifier") { |
|
return null |
|
} |
|
return node.key.name |
|
|
|
|
|
} |
|
|
|
return null |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getFunctionNameWithKind(node, sourceCode) { |
|
const parent = node.parent; |
|
const tokens = []; |
|
const isObjectMethod = parent.type === "Property" && parent.value === node; |
|
const isClassMethod = |
|
parent.type === "MethodDefinition" && parent.value === node; |
|
const isClassFieldMethod = |
|
parent.type === "PropertyDefinition" && parent.value === node; |
|
|
|
|
|
if (isClassMethod || isClassFieldMethod) { |
|
if (parent.static) { |
|
tokens.push("static"); |
|
} |
|
if (parent.key.type === "PrivateIdentifier") { |
|
tokens.push("private"); |
|
} |
|
} |
|
if (node.async) { |
|
tokens.push("async"); |
|
} |
|
if (node.generator) { |
|
tokens.push("generator"); |
|
} |
|
|
|
|
|
if (isObjectMethod || isClassMethod) { |
|
if (parent.kind === "constructor") { |
|
return "constructor" |
|
} |
|
if (parent.kind === "get") { |
|
tokens.push("getter"); |
|
} else if (parent.kind === "set") { |
|
tokens.push("setter"); |
|
} else { |
|
tokens.push("method"); |
|
} |
|
} else if (isClassFieldMethod) { |
|
tokens.push("method"); |
|
} else { |
|
if (node.type === "ArrowFunctionExpression") { |
|
tokens.push("arrow"); |
|
} |
|
tokens.push("function"); |
|
} |
|
|
|
|
|
if (isObjectMethod || isClassMethod || isClassFieldMethod) { |
|
if (parent.key.type === "PrivateIdentifier") { |
|
tokens.push(`#${parent.key.name}`); |
|
} else { |
|
const name = getPropertyName(parent); |
|
if (name) { |
|
tokens.push(`'${name}'`); |
|
} else if (sourceCode) { |
|
const keyText = sourceCode.getText(parent.key); |
|
if (!keyText.includes("\n")) { |
|
tokens.push(`[${keyText}]`); |
|
} |
|
} |
|
} |
|
} else if (node.id) { |
|
tokens.push(`'${node.id.name}'`); |
|
} else if ( |
|
parent.type === "VariableDeclarator" && |
|
parent.id && |
|
parent.id.type === "Identifier" |
|
) { |
|
tokens.push(`'${parent.id.name}'`); |
|
} else if ( |
|
(parent.type === "AssignmentExpression" || |
|
parent.type === "AssignmentPattern") && |
|
parent.left && |
|
parent.left.type === "Identifier" |
|
) { |
|
tokens.push(`'${parent.left.name}'`); |
|
} else if ( |
|
parent.type === "ExportDefaultDeclaration" && |
|
parent.declaration === node |
|
) { |
|
tokens.push("'default'"); |
|
} |
|
|
|
return tokens.join(" ") |
|
} |
|
|
|
const typeConversionBinaryOps = Object.freeze( |
|
new Set([ |
|
"==", |
|
"!=", |
|
"<", |
|
"<=", |
|
">", |
|
">=", |
|
"<<", |
|
">>", |
|
">>>", |
|
"+", |
|
"-", |
|
"*", |
|
"/", |
|
"%", |
|
"|", |
|
"^", |
|
"&", |
|
"in", |
|
]), |
|
); |
|
const typeConversionUnaryOps = Object.freeze(new Set(["-", "+", "!", "~"])); |
|
|
|
|
|
|
|
|
|
|
|
|
|
function isNode(x) { |
|
return x !== null && typeof x === "object" && typeof x.type === "string" |
|
} |
|
|
|
const visitor = Object.freeze( |
|
Object.assign(Object.create(null), { |
|
$visit(node, options, visitorKeys) { |
|
const { type } = node; |
|
|
|
if (typeof this[type] === "function") { |
|
return this[type](node, options, visitorKeys) |
|
} |
|
|
|
return this.$visitChildren(node, options, visitorKeys) |
|
}, |
|
|
|
$visitChildren(node, options, visitorKeys) { |
|
const { type } = node; |
|
|
|
for (const key of visitorKeys[type] || eslintVisitorKeys.getKeys(node)) { |
|
const value = node[key]; |
|
|
|
if (Array.isArray(value)) { |
|
for (const element of value) { |
|
if ( |
|
isNode(element) && |
|
this.$visit(element, options, visitorKeys) |
|
) { |
|
return true |
|
} |
|
} |
|
} else if ( |
|
isNode(value) && |
|
this.$visit(value, options, visitorKeys) |
|
) { |
|
return true |
|
} |
|
} |
|
|
|
return false |
|
}, |
|
|
|
ArrowFunctionExpression() { |
|
return false |
|
}, |
|
AssignmentExpression() { |
|
return true |
|
}, |
|
AwaitExpression() { |
|
return true |
|
}, |
|
BinaryExpression(node, options, visitorKeys) { |
|
if ( |
|
options.considerImplicitTypeConversion && |
|
typeConversionBinaryOps.has(node.operator) && |
|
(node.left.type !== "Literal" || node.right.type !== "Literal") |
|
) { |
|
return true |
|
} |
|
return this.$visitChildren(node, options, visitorKeys) |
|
}, |
|
CallExpression() { |
|
return true |
|
}, |
|
FunctionExpression() { |
|
return false |
|
}, |
|
ImportExpression() { |
|
return true |
|
}, |
|
MemberExpression(node, options, visitorKeys) { |
|
if (options.considerGetters) { |
|
return true |
|
} |
|
if ( |
|
options.considerImplicitTypeConversion && |
|
node.computed && |
|
node.property.type !== "Literal" |
|
) { |
|
return true |
|
} |
|
return this.$visitChildren(node, options, visitorKeys) |
|
}, |
|
MethodDefinition(node, options, visitorKeys) { |
|
if ( |
|
options.considerImplicitTypeConversion && |
|
node.computed && |
|
node.key.type !== "Literal" |
|
) { |
|
return true |
|
} |
|
return this.$visitChildren(node, options, visitorKeys) |
|
}, |
|
NewExpression() { |
|
return true |
|
}, |
|
Property(node, options, visitorKeys) { |
|
if ( |
|
options.considerImplicitTypeConversion && |
|
node.computed && |
|
node.key.type !== "Literal" |
|
) { |
|
return true |
|
} |
|
return this.$visitChildren(node, options, visitorKeys) |
|
}, |
|
PropertyDefinition(node, options, visitorKeys) { |
|
if ( |
|
options.considerImplicitTypeConversion && |
|
node.computed && |
|
node.key.type !== "Literal" |
|
) { |
|
return true |
|
} |
|
return this.$visitChildren(node, options, visitorKeys) |
|
}, |
|
UnaryExpression(node, options, visitorKeys) { |
|
if (node.operator === "delete") { |
|
return true |
|
} |
|
if ( |
|
options.considerImplicitTypeConversion && |
|
typeConversionUnaryOps.has(node.operator) && |
|
node.argument.type !== "Literal" |
|
) { |
|
return true |
|
} |
|
return this.$visitChildren(node, options, visitorKeys) |
|
}, |
|
UpdateExpression() { |
|
return true |
|
}, |
|
YieldExpression() { |
|
return true |
|
}, |
|
}), |
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function hasSideEffect( |
|
node, |
|
sourceCode, |
|
{ considerGetters = false, considerImplicitTypeConversion = false } = {}, |
|
) { |
|
return visitor.$visit( |
|
node, |
|
{ considerGetters, considerImplicitTypeConversion }, |
|
sourceCode.visitorKeys || eslintVisitorKeys.KEYS, |
|
) |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getParentSyntaxParen(node, sourceCode) { |
|
const parent = node.parent; |
|
|
|
switch (parent.type) { |
|
case "CallExpression": |
|
case "NewExpression": |
|
if (parent.arguments.length === 1 && parent.arguments[0] === node) { |
|
return sourceCode.getTokenAfter( |
|
parent.callee, |
|
isOpeningParenToken, |
|
) |
|
} |
|
return null |
|
|
|
case "DoWhileStatement": |
|
if (parent.test === node) { |
|
return sourceCode.getTokenAfter( |
|
parent.body, |
|
isOpeningParenToken, |
|
) |
|
} |
|
return null |
|
|
|
case "IfStatement": |
|
case "WhileStatement": |
|
if (parent.test === node) { |
|
return sourceCode.getFirstToken(parent, 1) |
|
} |
|
return null |
|
|
|
case "ImportExpression": |
|
if (parent.source === node) { |
|
return sourceCode.getFirstToken(parent, 1) |
|
} |
|
return null |
|
|
|
case "SwitchStatement": |
|
if (parent.discriminant === node) { |
|
return sourceCode.getFirstToken(parent, 1) |
|
} |
|
return null |
|
|
|
case "WithStatement": |
|
if (parent.object === node) { |
|
return sourceCode.getFirstToken(parent, 1) |
|
} |
|
return null |
|
|
|
default: |
|
return null |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function isParenthesized( |
|
timesOrNode, |
|
nodeOrSourceCode, |
|
optionalSourceCode, |
|
) { |
|
let times, node, sourceCode, maybeLeftParen, maybeRightParen; |
|
if (typeof timesOrNode === "number") { |
|
times = timesOrNode | 0; |
|
node = nodeOrSourceCode; |
|
sourceCode = optionalSourceCode; |
|
if (!(times >= 1)) { |
|
throw new TypeError("'times' should be a positive integer.") |
|
} |
|
} else { |
|
times = 1; |
|
node = timesOrNode; |
|
sourceCode = nodeOrSourceCode; |
|
} |
|
|
|
if ( |
|
node == null || |
|
|
|
node.parent == null || |
|
|
|
(node.parent.type === "CatchClause" && node.parent.param === node) |
|
) { |
|
return false |
|
} |
|
|
|
maybeLeftParen = maybeRightParen = node; |
|
do { |
|
maybeLeftParen = sourceCode.getTokenBefore(maybeLeftParen); |
|
maybeRightParen = sourceCode.getTokenAfter(maybeRightParen); |
|
} while ( |
|
maybeLeftParen != null && |
|
maybeRightParen != null && |
|
isOpeningParenToken(maybeLeftParen) && |
|
isClosingParenToken(maybeRightParen) && |
|
|
|
maybeLeftParen !== getParentSyntaxParen(node, sourceCode) && |
|
--times > 0 |
|
) |
|
|
|
return times === 0 |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
const placeholder = /\$(?:[$&`']|[1-9][0-9]?)/gu; |
|
|
|
|
|
const internal = new WeakMap(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function isEscaped(str, index) { |
|
let escaped = false; |
|
for (let i = index - 1; i >= 0 && str.charCodeAt(i) === 0x5c; --i) { |
|
escaped = !escaped; |
|
} |
|
return escaped |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function replaceS(matcher, str, replacement) { |
|
const chunks = []; |
|
let index = 0; |
|
|
|
|
|
let match = null; |
|
|
|
|
|
|
|
|
|
|
|
function replacer(key) { |
|
switch (key) { |
|
case "$$": |
|
return "$" |
|
case "$&": |
|
return match[0] |
|
case "$`": |
|
return str.slice(0, match.index) |
|
case "$'": |
|
return str.slice(match.index + match[0].length) |
|
default: { |
|
const i = key.slice(1); |
|
if (i in match) { |
|
return match[i] |
|
} |
|
return key |
|
} |
|
} |
|
} |
|
|
|
for (match of matcher.execAll(str)) { |
|
chunks.push(str.slice(index, match.index)); |
|
chunks.push(replacement.replace(placeholder, replacer)); |
|
index = match.index + match[0].length; |
|
} |
|
chunks.push(str.slice(index)); |
|
|
|
return chunks.join("") |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function replaceF(matcher, str, replace) { |
|
const chunks = []; |
|
let index = 0; |
|
|
|
for (const match of matcher.execAll(str)) { |
|
chunks.push(str.slice(index, match.index)); |
|
chunks.push(String(replace(...match, match.index, match.input))); |
|
index = match.index + match[0].length; |
|
} |
|
chunks.push(str.slice(index)); |
|
|
|
return chunks.join("") |
|
} |
|
|
|
|
|
|
|
|
|
class PatternMatcher { |
|
|
|
|
|
|
|
|
|
|
|
constructor(pattern, { escaped = false } = {}) { |
|
if (!(pattern instanceof RegExp)) { |
|
throw new TypeError("'pattern' should be a RegExp instance.") |
|
} |
|
if (!pattern.flags.includes("g")) { |
|
throw new Error("'pattern' should contains 'g' flag.") |
|
} |
|
|
|
internal.set(this, { |
|
pattern: new RegExp(pattern.source, pattern.flags), |
|
escaped: Boolean(escaped), |
|
}); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*execAll(str) { |
|
const { pattern, escaped } = internal.get(this); |
|
let match = null; |
|
let lastIndex = 0; |
|
|
|
pattern.lastIndex = 0; |
|
while ((match = pattern.exec(str)) != null) { |
|
if (escaped || !isEscaped(str, match.index)) { |
|
lastIndex = pattern.lastIndex; |
|
yield match; |
|
pattern.lastIndex = lastIndex; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
test(str) { |
|
const it = this.execAll(str); |
|
const ret = it.next(); |
|
return !ret.done |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[Symbol.replace](str, replacer) { |
|
return typeof replacer === "function" |
|
? replaceF(this, String(str), replacer) |
|
: replaceS(this, String(str), String(replacer)) |
|
} |
|
} |
|
|
|
const IMPORT_TYPE = /^(?:Import|Export(?:All|Default|Named))Declaration$/u; |
|
const has = Function.call.bind(Object.hasOwnProperty); |
|
|
|
const READ = Symbol("read"); |
|
const CALL = Symbol("call"); |
|
const CONSTRUCT = Symbol("construct"); |
|
const ESM = Symbol("esm"); |
|
|
|
const requireCall = { require: { [CALL]: true } }; |
|
|
|
|
|
|
|
|
|
|
|
|
|
function isModifiedGlobal(variable) { |
|
return ( |
|
variable == null || |
|
variable.defs.length !== 0 || |
|
variable.references.some((r) => r.isWrite()) |
|
) |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function isPassThrough(node) { |
|
const parent = node.parent; |
|
|
|
switch (parent && parent.type) { |
|
case "ConditionalExpression": |
|
return parent.consequent === node || parent.alternate === node |
|
case "LogicalExpression": |
|
return true |
|
case "SequenceExpression": |
|
return parent.expressions[parent.expressions.length - 1] === node |
|
case "ChainExpression": |
|
return true |
|
|
|
default: |
|
return false |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
class ReferenceTracker { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constructor( |
|
globalScope, |
|
{ |
|
mode = "strict", |
|
globalObjectNames = ["global", "globalThis", "self", "window"], |
|
} = {}, |
|
) { |
|
this.variableStack = []; |
|
this.globalScope = globalScope; |
|
this.mode = mode; |
|
this.globalObjectNames = globalObjectNames.slice(0); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*iterateGlobalReferences(traceMap) { |
|
for (const key of Object.keys(traceMap)) { |
|
const nextTraceMap = traceMap[key]; |
|
const path = [key]; |
|
const variable = this.globalScope.set.get(key); |
|
|
|
if (isModifiedGlobal(variable)) { |
|
continue |
|
} |
|
|
|
yield* this._iterateVariableReferences( |
|
variable, |
|
path, |
|
nextTraceMap, |
|
true, |
|
); |
|
} |
|
|
|
for (const key of this.globalObjectNames) { |
|
const path = []; |
|
const variable = this.globalScope.set.get(key); |
|
|
|
if (isModifiedGlobal(variable)) { |
|
continue |
|
} |
|
|
|
yield* this._iterateVariableReferences( |
|
variable, |
|
path, |
|
traceMap, |
|
false, |
|
); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*iterateCjsReferences(traceMap) { |
|
for (const { node } of this.iterateGlobalReferences(requireCall)) { |
|
const key = getStringIfConstant(node.arguments[0]); |
|
if (key == null || !has(traceMap, key)) { |
|
continue |
|
} |
|
|
|
const nextTraceMap = traceMap[key]; |
|
const path = [key]; |
|
|
|
if (nextTraceMap[READ]) { |
|
yield { |
|
node, |
|
path, |
|
type: READ, |
|
info: nextTraceMap[READ], |
|
}; |
|
} |
|
yield* this._iteratePropertyReferences(node, path, nextTraceMap); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*iterateEsmReferences(traceMap) { |
|
const programNode = this.globalScope.block; |
|
|
|
for (const node of programNode.body) { |
|
if (!IMPORT_TYPE.test(node.type) || node.source == null) { |
|
continue |
|
} |
|
const moduleId = node.source.value; |
|
|
|
if (!has(traceMap, moduleId)) { |
|
continue |
|
} |
|
const nextTraceMap = traceMap[moduleId]; |
|
const path = [moduleId]; |
|
|
|
if (nextTraceMap[READ]) { |
|
yield { node, path, type: READ, info: nextTraceMap[READ] }; |
|
} |
|
|
|
if (node.type === "ExportAllDeclaration") { |
|
for (const key of Object.keys(nextTraceMap)) { |
|
const exportTraceMap = nextTraceMap[key]; |
|
if (exportTraceMap[READ]) { |
|
yield { |
|
node, |
|
path: path.concat(key), |
|
type: READ, |
|
info: exportTraceMap[READ], |
|
}; |
|
} |
|
} |
|
} else { |
|
for (const specifier of node.specifiers) { |
|
const esm = has(nextTraceMap, ESM); |
|
const it = this._iterateImportReferences( |
|
specifier, |
|
path, |
|
esm |
|
? nextTraceMap |
|
: this.mode === "legacy" |
|
? { default: nextTraceMap, ...nextTraceMap } |
|
: { default: nextTraceMap }, |
|
); |
|
|
|
if (esm) { |
|
yield* it; |
|
} else { |
|
for (const report of it) { |
|
report.path = report.path.filter(exceptDefault); |
|
if ( |
|
report.path.length >= 2 || |
|
report.type !== READ |
|
) { |
|
yield report; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*_iterateVariableReferences(variable, path, traceMap, shouldReport) { |
|
if (this.variableStack.includes(variable)) { |
|
return |
|
} |
|
this.variableStack.push(variable); |
|
try { |
|
for (const reference of variable.references) { |
|
if (!reference.isRead()) { |
|
continue |
|
} |
|
const node = reference.identifier; |
|
|
|
if (shouldReport && traceMap[READ]) { |
|
yield { node, path, type: READ, info: traceMap[READ] }; |
|
} |
|
yield* this._iteratePropertyReferences(node, path, traceMap); |
|
} |
|
} finally { |
|
this.variableStack.pop(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*_iteratePropertyReferences(rootNode, path, traceMap) { |
|
let node = rootNode; |
|
while (isPassThrough(node)) { |
|
node = node.parent; |
|
} |
|
|
|
const parent = node.parent; |
|
if (parent.type === "MemberExpression") { |
|
if (parent.object === node) { |
|
const key = getPropertyName(parent); |
|
if (key == null || !has(traceMap, key)) { |
|
return |
|
} |
|
|
|
path = path.concat(key); |
|
const nextTraceMap = traceMap[key]; |
|
if (nextTraceMap[READ]) { |
|
yield { |
|
node: parent, |
|
path, |
|
type: READ, |
|
info: nextTraceMap[READ], |
|
}; |
|
} |
|
yield* this._iteratePropertyReferences( |
|
parent, |
|
path, |
|
nextTraceMap, |
|
); |
|
} |
|
return |
|
} |
|
if (parent.type === "CallExpression") { |
|
if (parent.callee === node && traceMap[CALL]) { |
|
yield { node: parent, path, type: CALL, info: traceMap[CALL] }; |
|
} |
|
return |
|
} |
|
if (parent.type === "NewExpression") { |
|
if (parent.callee === node && traceMap[CONSTRUCT]) { |
|
yield { |
|
node: parent, |
|
path, |
|
type: CONSTRUCT, |
|
info: traceMap[CONSTRUCT], |
|
}; |
|
} |
|
return |
|
} |
|
if (parent.type === "AssignmentExpression") { |
|
if (parent.right === node) { |
|
yield* this._iterateLhsReferences(parent.left, path, traceMap); |
|
yield* this._iteratePropertyReferences(parent, path, traceMap); |
|
} |
|
return |
|
} |
|
if (parent.type === "AssignmentPattern") { |
|
if (parent.right === node) { |
|
yield* this._iterateLhsReferences(parent.left, path, traceMap); |
|
} |
|
return |
|
} |
|
if (parent.type === "VariableDeclarator") { |
|
if (parent.init === node) { |
|
yield* this._iterateLhsReferences(parent.id, path, traceMap); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*_iterateLhsReferences(patternNode, path, traceMap) { |
|
if (patternNode.type === "Identifier") { |
|
const variable = findVariable(this.globalScope, patternNode); |
|
if (variable != null) { |
|
yield* this._iterateVariableReferences( |
|
variable, |
|
path, |
|
traceMap, |
|
false, |
|
); |
|
} |
|
return |
|
} |
|
if (patternNode.type === "ObjectPattern") { |
|
for (const property of patternNode.properties) { |
|
const key = getPropertyName(property); |
|
|
|
if (key == null || !has(traceMap, key)) { |
|
continue |
|
} |
|
|
|
const nextPath = path.concat(key); |
|
const nextTraceMap = traceMap[key]; |
|
if (nextTraceMap[READ]) { |
|
yield { |
|
node: property, |
|
path: nextPath, |
|
type: READ, |
|
info: nextTraceMap[READ], |
|
}; |
|
} |
|
yield* this._iterateLhsReferences( |
|
property.value, |
|
nextPath, |
|
nextTraceMap, |
|
); |
|
} |
|
return |
|
} |
|
if (patternNode.type === "AssignmentPattern") { |
|
yield* this._iterateLhsReferences(patternNode.left, path, traceMap); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*_iterateImportReferences(specifierNode, path, traceMap) { |
|
const type = specifierNode.type; |
|
|
|
if (type === "ImportSpecifier" || type === "ImportDefaultSpecifier") { |
|
const key = |
|
type === "ImportDefaultSpecifier" |
|
? "default" |
|
: specifierNode.imported.name; |
|
if (!has(traceMap, key)) { |
|
return |
|
} |
|
|
|
path = path.concat(key); |
|
const nextTraceMap = traceMap[key]; |
|
if (nextTraceMap[READ]) { |
|
yield { |
|
node: specifierNode, |
|
path, |
|
type: READ, |
|
info: nextTraceMap[READ], |
|
}; |
|
} |
|
yield* this._iterateVariableReferences( |
|
findVariable(this.globalScope, specifierNode.local), |
|
path, |
|
nextTraceMap, |
|
false, |
|
); |
|
|
|
return |
|
} |
|
|
|
if (type === "ImportNamespaceSpecifier") { |
|
yield* this._iterateVariableReferences( |
|
findVariable(this.globalScope, specifierNode.local), |
|
path, |
|
traceMap, |
|
false, |
|
); |
|
return |
|
} |
|
|
|
if (type === "ExportSpecifier") { |
|
const key = specifierNode.local.name; |
|
if (!has(traceMap, key)) { |
|
return |
|
} |
|
|
|
path = path.concat(key); |
|
const nextTraceMap = traceMap[key]; |
|
if (nextTraceMap[READ]) { |
|
yield { |
|
node: specifierNode, |
|
path, |
|
type: READ, |
|
info: nextTraceMap[READ], |
|
}; |
|
} |
|
} |
|
} |
|
} |
|
|
|
ReferenceTracker.READ = READ; |
|
ReferenceTracker.CALL = CALL; |
|
ReferenceTracker.CONSTRUCT = CONSTRUCT; |
|
ReferenceTracker.ESM = ESM; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function exceptDefault(name, index) { |
|
return !(index === 1 && name === "default") |
|
} |
|
|
|
var index = { |
|
CALL, |
|
CONSTRUCT, |
|
ESM, |
|
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, |
|
PatternMatcher, |
|
READ, |
|
ReferenceTracker, |
|
}; |
|
|
|
exports.CALL = CALL; |
|
exports.CONSTRUCT = CONSTRUCT; |
|
exports.ESM = ESM; |
|
exports.PatternMatcher = PatternMatcher; |
|
exports.READ = READ; |
|
exports.ReferenceTracker = ReferenceTracker; |
|
exports["default"] = index; |
|
exports.findVariable = findVariable; |
|
exports.getFunctionHeadLocation = getFunctionHeadLocation; |
|
exports.getFunctionNameWithKind = getFunctionNameWithKind; |
|
exports.getInnermostScope = getInnermostScope; |
|
exports.getPropertyName = getPropertyName; |
|
exports.getStaticValue = getStaticValue; |
|
exports.getStringIfConstant = getStringIfConstant; |
|
exports.hasSideEffect = hasSideEffect; |
|
exports.isArrowToken = isArrowToken; |
|
exports.isClosingBraceToken = isClosingBraceToken; |
|
exports.isClosingBracketToken = isClosingBracketToken; |
|
exports.isClosingParenToken = isClosingParenToken; |
|
exports.isColonToken = isColonToken; |
|
exports.isCommaToken = isCommaToken; |
|
exports.isCommentToken = isCommentToken; |
|
exports.isNotArrowToken = isNotArrowToken; |
|
exports.isNotClosingBraceToken = isNotClosingBraceToken; |
|
exports.isNotClosingBracketToken = isNotClosingBracketToken; |
|
exports.isNotClosingParenToken = isNotClosingParenToken; |
|
exports.isNotColonToken = isNotColonToken; |
|
exports.isNotCommaToken = isNotCommaToken; |
|
exports.isNotCommentToken = isNotCommentToken; |
|
exports.isNotOpeningBraceToken = isNotOpeningBraceToken; |
|
exports.isNotOpeningBracketToken = isNotOpeningBracketToken; |
|
exports.isNotOpeningParenToken = isNotOpeningParenToken; |
|
exports.isNotSemicolonToken = isNotSemicolonToken; |
|
exports.isOpeningBraceToken = isOpeningBraceToken; |
|
exports.isOpeningBracketToken = isOpeningBracketToken; |
|
exports.isOpeningParenToken = isOpeningParenToken; |
|
exports.isParenthesized = isParenthesized; |
|
exports.isSemicolonToken = isSemicolonToken; |
|
|
|
|