@eslint-react/var
Advanced tools
Comparing version
@@ -1,5 +0,7 @@ | ||
import { unit } from '@eslint-react/eff'; | ||
import { Scope, Variable } from '@typescript-eslint/scope-manager'; | ||
import { TSESTree } from '@typescript-eslint/types'; | ||
import { unit } from "@eslint-react/eff"; | ||
import { Scope, Variable } from "@typescript-eslint/scope-manager"; | ||
import { TSESTree } from "@typescript-eslint/types"; | ||
//#region src/var-collect.d.ts | ||
/** | ||
@@ -12,38 +14,39 @@ * Get all variables from the given scope up to the global scope | ||
declare const findVariable: { | ||
(initialScope: Scope): (nameOrNode: string | TSESTree.Identifier | unit) => Variable | unit; | ||
(nameOrNode: string | TSESTree.Identifier | unit, initialScope: Scope): Variable | unit; | ||
(initialScope: Scope): (nameOrNode: string | TSESTree.Identifier | unit) => Variable | unit; | ||
(nameOrNode: string | TSESTree.Identifier | unit, initialScope: Scope): Variable | unit; | ||
}; | ||
declare function findPropertyInProperties(name: string, properties: (TSESTree.Property | TSESTree.RestElement | TSESTree.SpreadElement)[], initialScope: Scope, seen?: Set<string>): (typeof properties)[number] | unit; | ||
//#endregion | ||
//#region src/var-construction.d.ts | ||
declare const ConstructionDetectionHint: { | ||
None: bigint; | ||
StrictCallExpression: bigint; | ||
None: bigint; | ||
StrictCallExpression: bigint; | ||
}; | ||
type Construction = { | ||
kind: "ArrayExpression"; | ||
node: TSESTree.ArrayExpression; | ||
kind: "ArrayExpression"; | ||
node: TSESTree.ArrayExpression; | ||
} | { | ||
kind: "CallExpression"; | ||
node: TSESTree.CallExpression; | ||
kind: "CallExpression"; | ||
node: TSESTree.CallExpression; | ||
} | { | ||
kind: "ClassExpression"; | ||
node: TSESTree.ClassExpression; | ||
kind: "ClassExpression"; | ||
node: TSESTree.ClassExpression; | ||
} | { | ||
kind: "FunctionDeclaration"; | ||
node: TSESTree.FunctionDeclaration; | ||
kind: "FunctionDeclaration"; | ||
node: TSESTree.FunctionDeclaration; | ||
} | { | ||
kind: "FunctionExpression"; | ||
node: TSESTree.FunctionExpression | TSESTree.ArrowFunctionExpression; | ||
kind: "FunctionExpression"; | ||
node: TSESTree.FunctionExpression | TSESTree.ArrowFunctionExpression; | ||
} | { | ||
kind: "JSXElement"; | ||
node: TSESTree.JSXElement | TSESTree.JSXFragment; | ||
kind: "JSXElement"; | ||
node: TSESTree.JSXElement | TSESTree.JSXFragment; | ||
} | { | ||
kind: "NewExpression"; | ||
node: TSESTree.NewExpression; | ||
kind: "NewExpression"; | ||
node: TSESTree.NewExpression; | ||
} | { | ||
kind: "ObjectExpression"; | ||
node: TSESTree.ObjectExpression; | ||
kind: "ObjectExpression"; | ||
node: TSESTree.ObjectExpression; | ||
} | { | ||
kind: "RegExpLiteral"; | ||
node: TSESTree.RegExpLiteral; | ||
kind: "RegExpLiteral"; | ||
node: TSESTree.RegExpLiteral; | ||
}; | ||
@@ -58,35 +61,40 @@ /** | ||
declare function getConstruction(node: TSESTree.Node | unit, initialScope: Scope, hint?: bigint): Construction | unit; | ||
//#endregion | ||
//#region src/var-declarator-id.d.ts | ||
declare function getVariableDeclaratorId(node: TSESTree.Node | unit, prev?: TSESTree.Node): TSESTree.BindingName | TSESTree.Expression | unit; | ||
//#endregion | ||
//#region src/var-init-node.d.ts | ||
declare function getVariableInitNode(variable: Variable | unit, at: number): unit | TSESTree.ClassDeclaration | TSESTree.ClassDeclarationWithName | TSESTree.ClassDeclarationWithOptionalName | TSESTree.Expression | TSESTree.FunctionDeclaration | TSESTree.FunctionDeclarationWithName | TSESTree.FunctionDeclarationWithOptionalName; | ||
//#endregion | ||
//#region src/var-scope.d.ts | ||
declare function getChidScopes(scope: Scope): readonly Scope[]; | ||
//#endregion | ||
//#region src/var-value.d.ts | ||
type LazyValue = { | ||
kind: "lazy"; | ||
node: TSESTree.Node; | ||
initialScope: Scope | unit; | ||
kind: "lazy"; | ||
node: TSESTree.Node; | ||
initialScope: Scope | unit; | ||
} | { | ||
kind: "none"; | ||
node: TSESTree.Node; | ||
initialScope: Scope | unit; | ||
kind: "none"; | ||
node: TSESTree.Node; | ||
initialScope: Scope | unit; | ||
} | { | ||
kind: "some"; | ||
node: TSESTree.Node; | ||
value: unknown; | ||
initialScope: Scope | unit; | ||
kind: "some"; | ||
node: TSESTree.Node; | ||
value: unknown; | ||
initialScope: Scope | unit; | ||
}; | ||
declare function toStaticValue(lazyValue: LazyValue): { | ||
readonly kind: "none"; | ||
readonly node: TSESTree.Node; | ||
readonly initialScope: Scope | undefined; | ||
readonly value?: never; | ||
readonly kind: "none"; | ||
readonly node: TSESTree.Node; | ||
readonly initialScope: Scope | undefined; | ||
readonly value?: never; | ||
} | { | ||
readonly kind: "some"; | ||
readonly node: TSESTree.Node; | ||
readonly initialScope: Scope | undefined; | ||
readonly value: unknown; | ||
readonly kind: "some"; | ||
readonly node: TSESTree.Node; | ||
readonly initialScope: Scope | undefined; | ||
readonly value: unknown; | ||
}; | ||
//#endregion | ||
//#region src/var-value-equal.d.ts | ||
/** | ||
@@ -99,7 +107,4 @@ * Determines whether node value equals to another node value | ||
*/ | ||
declare function isNodeValueEqual(a: TSESTree.Node, b: TSESTree.Node, initialScopes: [ | ||
aScope: Scope, | ||
bScope: Scope | ||
]): boolean; | ||
export { type Construction, ConstructionDetectionHint, type LazyValue, findPropertyInProperties, findVariable, getChidScopes, getConstruction, getVariableDeclaratorId, getVariableInitNode, getVariables, isNodeValueEqual, toStaticValue }; | ||
declare function isNodeValueEqual(a: TSESTree.Node, b: TSESTree.Node, initialScopes: [aScope: Scope, bScope: Scope]): boolean; | ||
//#endregion | ||
export { Construction, ConstructionDetectionHint, LazyValue, findPropertyInProperties, findVariable, getChidScopes, getConstruction, getVariableDeclaratorId, getVariableInitNode, getVariables, isNodeValueEqual, toStaticValue }; |
@@ -1,256 +0,267 @@ | ||
import { dual, unit } from '@eslint-react/eff'; | ||
import { DefinitionType, ScopeType } from '@typescript-eslint/scope-manager'; | ||
import { AST_NODE_TYPES } from '@typescript-eslint/types'; | ||
import * as ASTUtils from '@typescript-eslint/utils/ast-utils'; | ||
import { getStaticValue } from '@typescript-eslint/utils/ast-utils'; | ||
import * as AST from '@eslint-react/ast'; | ||
import { dual, unit } from "@eslint-react/eff"; | ||
import { DefinitionType, ScopeType } from "@typescript-eslint/scope-manager"; | ||
import { AST_NODE_TYPES } from "@typescript-eslint/types"; | ||
import * as ASTUtils from "@typescript-eslint/utils/ast-utils"; | ||
import { getStaticValue } from "@typescript-eslint/utils/ast-utils"; | ||
import * as AST from "@eslint-react/ast"; | ||
// src/var-collect.ts | ||
//#region src/var-init-node.ts | ||
function getVariableInitNode(variable, at) { | ||
if (variable == null) return unit; | ||
const def = variable.defs.at(at); | ||
if (def == null) return unit; | ||
switch (true) { | ||
case (def.type === DefinitionType.FunctionName && def.node.type === AST_NODE_TYPES.FunctionDeclaration): | ||
return def.node; | ||
case (def.type === DefinitionType.ClassName && def.node.type === AST_NODE_TYPES.ClassDeclaration): | ||
return def.node; | ||
case ("init" in def.node && def.node.init != null && !("declarations" in def.node.init)): | ||
return def.node.init; | ||
default: | ||
return unit; | ||
} | ||
if (variable == null) return unit; | ||
const def = variable.defs.at(at); | ||
if (def == null) return unit; | ||
switch (true) { | ||
case def.type === DefinitionType.FunctionName && def.node.type === AST_NODE_TYPES.FunctionDeclaration: return def.node; | ||
case def.type === DefinitionType.ClassName && def.node.type === AST_NODE_TYPES.ClassDeclaration: return def.node; | ||
case "init" in def.node && def.node.init != null && !("declarations" in def.node.init): return def.node.init; | ||
default: return unit; | ||
} | ||
} | ||
// src/var-collect.ts | ||
//#endregion | ||
//#region src/var-collect.ts | ||
/** | ||
* Get all variables from the given scope up to the global scope | ||
* @param initialScope The scope to start from | ||
* @returns All variables from the given scope up to the global scope | ||
*/ | ||
function getVariables(initialScope) { | ||
let scope = initialScope; | ||
const variables = [...scope.variables]; | ||
while (scope.type !== ScopeType.global) { | ||
scope = scope.upper; | ||
variables.push(...scope.variables); | ||
} | ||
return variables.reverse(); | ||
let scope = initialScope; | ||
const variables = [...scope.variables]; | ||
while (scope.type !== ScopeType.global) { | ||
scope = scope.upper; | ||
variables.push(...scope.variables); | ||
} | ||
return variables.reverse(); | ||
} | ||
var findVariable2 = dual(2, (nameOrNode, initialScope) => { | ||
if (nameOrNode == null) return unit; | ||
return ASTUtils.findVariable(initialScope, nameOrNode) ?? unit; | ||
const findVariable = dual(2, (nameOrNode, initialScope) => { | ||
if (nameOrNode == null) return unit; | ||
return ASTUtils.findVariable(initialScope, nameOrNode) ?? unit; | ||
}); | ||
function findPropertyInProperties(name, properties, initialScope, seen = /* @__PURE__ */ new Set()) { | ||
return properties.findLast((prop) => { | ||
if (prop.type === AST_NODE_TYPES.Property) { | ||
return "name" in prop.key && prop.key.name === name; | ||
} | ||
if (prop.type === AST_NODE_TYPES.SpreadElement) { | ||
switch (prop.argument.type) { | ||
case AST_NODE_TYPES.Identifier: { | ||
if (seen.has(prop.argument.name)) return false; | ||
const variable = findVariable2(prop.argument.name, initialScope); | ||
const variableNode = getVariableInitNode(variable, 0); | ||
if (variableNode?.type === AST_NODE_TYPES.ObjectExpression) { | ||
seen.add(prop.argument.name); | ||
return findPropertyInProperties( | ||
name, | ||
variableNode.properties, | ||
initialScope, | ||
seen | ||
) != null; | ||
} | ||
return false; | ||
} | ||
case AST_NODE_TYPES.ObjectExpression: { | ||
return findPropertyInProperties( | ||
name, | ||
prop.argument.properties, | ||
initialScope, | ||
seen | ||
) != null; | ||
} | ||
default: | ||
return false; | ||
} | ||
} | ||
return false; | ||
}); | ||
return properties.findLast((prop) => { | ||
if (prop.type === AST_NODE_TYPES.Property) return "name" in prop.key && prop.key.name === name; | ||
if (prop.type === AST_NODE_TYPES.SpreadElement) switch (prop.argument.type) { | ||
case AST_NODE_TYPES.Identifier: { | ||
if (seen.has(prop.argument.name)) return false; | ||
const variable = findVariable(prop.argument.name, initialScope); | ||
const variableNode = getVariableInitNode(variable, 0); | ||
if (variableNode?.type === AST_NODE_TYPES.ObjectExpression) { | ||
seen.add(prop.argument.name); | ||
return findPropertyInProperties(name, variableNode.properties, initialScope, seen) != null; | ||
} | ||
return false; | ||
} | ||
case AST_NODE_TYPES.ObjectExpression: return findPropertyInProperties(name, prop.argument.properties, initialScope, seen) != null; | ||
default: return false; | ||
} | ||
return false; | ||
}); | ||
} | ||
var ConstructionDetectionHint = { | ||
None: 0n, | ||
StrictCallExpression: 1n << 0n | ||
//#endregion | ||
//#region src/var-construction.ts | ||
const ConstructionDetectionHint = { | ||
None: 0n, | ||
StrictCallExpression: 1n << 0n | ||
}; | ||
/** | ||
* Detects the construction type of a given node. | ||
* @param node The node to check. | ||
* @param initialScope The initial scope to check for variable declarations. | ||
* @param hint Optional hint to control the detection behavior. | ||
* @returns The construction type of the node, or `_` if not found. | ||
*/ | ||
function getConstruction(node, initialScope, hint = ConstructionDetectionHint.None) { | ||
if (node == null) return unit; | ||
switch (node.type) { | ||
case AST_NODE_TYPES.JSXElement: | ||
case AST_NODE_TYPES.JSXFragment: | ||
return { kind: "JSXElement", node }; | ||
case AST_NODE_TYPES.ArrayExpression: | ||
return { kind: "ArrayExpression", node }; | ||
case AST_NODE_TYPES.ObjectExpression: | ||
return { kind: "ObjectExpression", node }; | ||
case AST_NODE_TYPES.ClassExpression: | ||
return { kind: "ClassExpression", node }; | ||
case AST_NODE_TYPES.NewExpression: | ||
return { kind: "NewExpression", node }; | ||
case AST_NODE_TYPES.FunctionExpression: | ||
case AST_NODE_TYPES.ArrowFunctionExpression: | ||
return { kind: "FunctionExpression", node }; | ||
case AST_NODE_TYPES.CallExpression: { | ||
if (hint & ConstructionDetectionHint.StrictCallExpression) { | ||
return { kind: "CallExpression", node }; | ||
} | ||
return unit; | ||
} | ||
case AST_NODE_TYPES.MemberExpression: { | ||
if (!("object" in node)) return unit; | ||
return getConstruction(node.object, initialScope, hint); | ||
} | ||
case AST_NODE_TYPES.AssignmentExpression: | ||
case AST_NODE_TYPES.AssignmentPattern: { | ||
if (!("right" in node)) return unit; | ||
return getConstruction(node.right, initialScope, hint); | ||
} | ||
case AST_NODE_TYPES.LogicalExpression: { | ||
const lvc = getConstruction(node.left, initialScope, hint); | ||
if (lvc == null) return unit; | ||
return getConstruction(node.right, initialScope, hint); | ||
} | ||
case AST_NODE_TYPES.ConditionalExpression: { | ||
const cvc = getConstruction(node.consequent, initialScope, hint); | ||
if (cvc == null) return unit; | ||
return getConstruction(node.alternate, initialScope, hint); | ||
} | ||
case AST_NODE_TYPES.Identifier: { | ||
if (!("name" in node) || typeof node.name !== "string") { | ||
return unit; | ||
} | ||
const variable = initialScope.set.get(node.name); | ||
const variableNode = getVariableInitNode(variable, -1); | ||
return getConstruction(variableNode, initialScope, hint); | ||
} | ||
case AST_NODE_TYPES.Literal: { | ||
if ("regex" in node) { | ||
return { kind: "RegExpLiteral", node }; | ||
} | ||
return unit; | ||
} | ||
default: { | ||
if (!("expression" in node) || typeof node.expression !== "object") { | ||
return unit; | ||
} | ||
return getConstruction(node.expression, initialScope, hint); | ||
} | ||
} | ||
if (node == null) return unit; | ||
switch (node.type) { | ||
case AST_NODE_TYPES.JSXElement: | ||
case AST_NODE_TYPES.JSXFragment: return { | ||
kind: "JSXElement", | ||
node | ||
}; | ||
case AST_NODE_TYPES.ArrayExpression: return { | ||
kind: "ArrayExpression", | ||
node | ||
}; | ||
case AST_NODE_TYPES.ObjectExpression: return { | ||
kind: "ObjectExpression", | ||
node | ||
}; | ||
case AST_NODE_TYPES.ClassExpression: return { | ||
kind: "ClassExpression", | ||
node | ||
}; | ||
case AST_NODE_TYPES.NewExpression: return { | ||
kind: "NewExpression", | ||
node | ||
}; | ||
case AST_NODE_TYPES.FunctionExpression: | ||
case AST_NODE_TYPES.ArrowFunctionExpression: return { | ||
kind: "FunctionExpression", | ||
node | ||
}; | ||
case AST_NODE_TYPES.CallExpression: | ||
if (hint & ConstructionDetectionHint.StrictCallExpression) return { | ||
kind: "CallExpression", | ||
node | ||
}; | ||
return unit; | ||
case AST_NODE_TYPES.MemberExpression: | ||
if (!("object" in node)) return unit; | ||
return getConstruction(node.object, initialScope, hint); | ||
case AST_NODE_TYPES.AssignmentExpression: | ||
case AST_NODE_TYPES.AssignmentPattern: | ||
if (!("right" in node)) return unit; | ||
return getConstruction(node.right, initialScope, hint); | ||
case AST_NODE_TYPES.LogicalExpression: { | ||
const lvc = getConstruction(node.left, initialScope, hint); | ||
if (lvc == null) return unit; | ||
return getConstruction(node.right, initialScope, hint); | ||
} | ||
case AST_NODE_TYPES.ConditionalExpression: { | ||
const cvc = getConstruction(node.consequent, initialScope, hint); | ||
if (cvc == null) return unit; | ||
return getConstruction(node.alternate, initialScope, hint); | ||
} | ||
case AST_NODE_TYPES.Identifier: { | ||
if (!("name" in node) || typeof node.name !== "string") return unit; | ||
const variable = initialScope.set.get(node.name); | ||
const variableNode = getVariableInitNode(variable, -1); | ||
return getConstruction(variableNode, initialScope, hint); | ||
} | ||
case AST_NODE_TYPES.Literal: | ||
if ("regex" in node) return { | ||
kind: "RegExpLiteral", | ||
node | ||
}; | ||
return unit; | ||
default: | ||
if (!("expression" in node) || typeof node.expression !== "object") return unit; | ||
return getConstruction(node.expression, initialScope, hint); | ||
} | ||
} | ||
//#endregion | ||
//#region src/var-declarator-id.ts | ||
function getVariableDeclaratorId(node, prev) { | ||
if (node == null) return unit; | ||
switch (true) { | ||
case (node.type === AST_NODE_TYPES.VariableDeclarator && node.init === prev): | ||
return node.id; | ||
case (node.type === AST_NODE_TYPES.AssignmentExpression && node.right === prev): | ||
return node.left; | ||
case (node.type === AST_NODE_TYPES.BlockStatement || node.type === AST_NODE_TYPES.Program || node.parent === node): | ||
return unit; | ||
default: | ||
return getVariableDeclaratorId(node.parent, node); | ||
} | ||
if (node == null) return unit; | ||
switch (true) { | ||
case node.type === AST_NODE_TYPES.VariableDeclarator && node.init === prev: return node.id; | ||
case node.type === AST_NODE_TYPES.AssignmentExpression && node.right === prev: return node.left; | ||
case node.type === AST_NODE_TYPES.BlockStatement || node.type === AST_NODE_TYPES.Program || node.parent === node: return unit; | ||
default: return getVariableDeclaratorId(node.parent, node); | ||
} | ||
} | ||
// src/var-scope.ts | ||
//#endregion | ||
//#region src/var-scope.ts | ||
function getChidScopes(scope) { | ||
const scopes = [scope]; | ||
for (const childScope of scope.childScopes) { | ||
scopes.push(...getChidScopes(childScope)); | ||
} | ||
return scopes; | ||
const scopes = [scope]; | ||
for (const childScope of scope.childScopes) scopes.push(...getChidScopes(childScope)); | ||
return scopes; | ||
} | ||
//#endregion | ||
//#region src/var-value.ts | ||
function toStaticValue(lazyValue) { | ||
const { kind, node, initialScope } = lazyValue; | ||
if (kind !== "lazy") { | ||
return lazyValue; | ||
} | ||
const staticValue = initialScope == null ? getStaticValue(node) : getStaticValue(node, initialScope); | ||
return staticValue == null ? { kind: "none", node, initialScope } : { kind: "some", node, initialScope, value: staticValue.value }; | ||
const { kind, node, initialScope } = lazyValue; | ||
if (kind !== "lazy") return lazyValue; | ||
const staticValue = initialScope == null ? getStaticValue(node) : getStaticValue(node, initialScope); | ||
return staticValue == null ? { | ||
kind: "none", | ||
node, | ||
initialScope | ||
} : { | ||
kind: "some", | ||
node, | ||
initialScope, | ||
value: staticValue.value | ||
}; | ||
} | ||
var thisBlockTypes = [ | ||
AST_NODE_TYPES.FunctionDeclaration, | ||
AST_NODE_TYPES.FunctionExpression, | ||
AST_NODE_TYPES.ClassBody, | ||
AST_NODE_TYPES.Program | ||
//#endregion | ||
//#region src/var-value-equal.ts | ||
const thisBlockTypes = [ | ||
AST_NODE_TYPES.FunctionDeclaration, | ||
AST_NODE_TYPES.FunctionExpression, | ||
AST_NODE_TYPES.ClassBody, | ||
AST_NODE_TYPES.Program | ||
]; | ||
/** | ||
* Determines whether node value equals to another node value | ||
* @param a node to compare | ||
* @param b node to compare | ||
* @param initialScopes initial scopes of the two nodes | ||
* @returns `true` if node value equal | ||
*/ | ||
function isNodeValueEqual(a, b, initialScopes) { | ||
const [aScope, bScope] = initialScopes; | ||
switch (true) { | ||
case a === b: { | ||
return true; | ||
} | ||
case (a.type === AST_NODE_TYPES.Literal && b.type === AST_NODE_TYPES.Literal): { | ||
return a.value === b.value; | ||
} | ||
case (a.type === AST_NODE_TYPES.TemplateElement && b.type === AST_NODE_TYPES.TemplateElement): { | ||
return a.value.cooked === b.value.cooked; | ||
} | ||
case (a.type === AST_NODE_TYPES.Identifier && b.type === AST_NODE_TYPES.Identifier): { | ||
const aVar = findVariable2(a, aScope); | ||
const bVar = findVariable2(b, bScope); | ||
const aVarNode = getVariableInitNodeLoose(aVar, 0); | ||
const bVarNode = getVariableInitNodeLoose(bVar, 0); | ||
const aVarNodeParent = aVarNode?.parent; | ||
const bVarNodeParent = bVarNode?.parent; | ||
const aDef = aVar?.defs.at(0); | ||
const bDef = bVar?.defs.at(0); | ||
const aDefParentParent = aDef?.parent?.parent; | ||
const bDefParentParent = bDef?.parent?.parent; | ||
switch (true) { | ||
case (aVarNodeParent?.type === AST_NODE_TYPES.CallExpression && bVarNodeParent?.type === AST_NODE_TYPES.CallExpression && AST.isFunction(aVarNode) && AST.isFunction(bVarNode)): { | ||
if (!AST.isNodeEqual(aVarNodeParent.callee, bVarNodeParent.callee)) { | ||
return false; | ||
} | ||
const aParams = aVarNode.params; | ||
const bParams = bVarNode.params; | ||
const aPos = aParams.findIndex((x) => AST.isNodeEqual(x, a)); | ||
const bPos = bParams.findIndex((x) => AST.isNodeEqual(x, b)); | ||
return aPos !== -1 && bPos !== -1 && aPos === bPos; | ||
} | ||
case (aDefParentParent?.type === AST_NODE_TYPES.ForOfStatement && bDefParentParent?.type === AST_NODE_TYPES.ForOfStatement): { | ||
const aLeft = aDefParentParent.left; | ||
const bLeft = bDefParentParent.left; | ||
if (aLeft.type !== bLeft.type) { | ||
return false; | ||
} | ||
const aRight = aDefParentParent.right; | ||
const bRight = bDefParentParent.right; | ||
return AST.isNodeEqual(aRight, bRight); | ||
} | ||
default: { | ||
return aVar != null && bVar != null && aVar === bVar; | ||
} | ||
} | ||
} | ||
case (a.type === AST_NODE_TYPES.MemberExpression && b.type === AST_NODE_TYPES.MemberExpression): { | ||
return AST.isNodeEqual(a.property, b.property) && isNodeValueEqual(a.object, b.object, initialScopes); | ||
} | ||
case (a.type === AST_NODE_TYPES.ThisExpression && b.type === AST_NODE_TYPES.ThisExpression): { | ||
if (aScope.block === bScope.block) { | ||
return true; | ||
} | ||
const aFunction = AST.findParentNode(a, AST.isOneOf(thisBlockTypes)); | ||
const bFunction = AST.findParentNode(b, AST.isOneOf(thisBlockTypes)); | ||
return aFunction === bFunction; | ||
} | ||
default: { | ||
const aStatic = toStaticValue({ kind: "lazy", node: a, initialScope: aScope }); | ||
const bStatic = toStaticValue({ kind: "lazy", node: b, initialScope: bScope }); | ||
return aStatic.kind !== "none" && bStatic.kind !== "none" && aStatic.value === bStatic.value; | ||
} | ||
} | ||
const [aScope, bScope] = initialScopes; | ||
switch (true) { | ||
case a === b: return true; | ||
case a.type === AST_NODE_TYPES.Literal && b.type === AST_NODE_TYPES.Literal: return a.value === b.value; | ||
case a.type === AST_NODE_TYPES.TemplateElement && b.type === AST_NODE_TYPES.TemplateElement: return a.value.cooked === b.value.cooked; | ||
case a.type === AST_NODE_TYPES.Identifier && b.type === AST_NODE_TYPES.Identifier: { | ||
const aVar = findVariable(a, aScope); | ||
const bVar = findVariable(b, bScope); | ||
const aVarNode = getVariableInitNodeLoose(aVar, 0); | ||
const bVarNode = getVariableInitNodeLoose(bVar, 0); | ||
const aVarNodeParent = aVarNode?.parent; | ||
const bVarNodeParent = bVarNode?.parent; | ||
const aDef = aVar?.defs.at(0); | ||
const bDef = bVar?.defs.at(0); | ||
const aDefParentParent = aDef?.parent?.parent; | ||
const bDefParentParent = bDef?.parent?.parent; | ||
switch (true) { | ||
case aVarNodeParent?.type === AST_NODE_TYPES.CallExpression && bVarNodeParent?.type === AST_NODE_TYPES.CallExpression && AST.isFunction(aVarNode) && AST.isFunction(bVarNode): { | ||
if (!AST.isNodeEqual(aVarNodeParent.callee, bVarNodeParent.callee)) return false; | ||
const aParams = aVarNode.params; | ||
const bParams = bVarNode.params; | ||
const aPos = aParams.findIndex((x) => AST.isNodeEqual(x, a)); | ||
const bPos = bParams.findIndex((x) => AST.isNodeEqual(x, b)); | ||
return aPos !== -1 && bPos !== -1 && aPos === bPos; | ||
} | ||
case aDefParentParent?.type === AST_NODE_TYPES.ForOfStatement && bDefParentParent?.type === AST_NODE_TYPES.ForOfStatement: { | ||
const aLeft = aDefParentParent.left; | ||
const bLeft = bDefParentParent.left; | ||
if (aLeft.type !== bLeft.type) return false; | ||
const aRight = aDefParentParent.right; | ||
const bRight = bDefParentParent.right; | ||
return AST.isNodeEqual(aRight, bRight); | ||
} | ||
default: return aVar != null && bVar != null && aVar === bVar; | ||
} | ||
} | ||
case a.type === AST_NODE_TYPES.MemberExpression && b.type === AST_NODE_TYPES.MemberExpression: return AST.isNodeEqual(a.property, b.property) && isNodeValueEqual(a.object, b.object, initialScopes); | ||
case a.type === AST_NODE_TYPES.ThisExpression && b.type === AST_NODE_TYPES.ThisExpression: { | ||
if (aScope.block === bScope.block) return true; | ||
const aFunction = AST.findParentNode(a, AST.isOneOf(thisBlockTypes)); | ||
const bFunction = AST.findParentNode(b, AST.isOneOf(thisBlockTypes)); | ||
return aFunction === bFunction; | ||
} | ||
default: { | ||
const aStatic = toStaticValue({ | ||
kind: "lazy", | ||
node: a, | ||
initialScope: aScope | ||
}); | ||
const bStatic = toStaticValue({ | ||
kind: "lazy", | ||
node: b, | ||
initialScope: bScope | ||
}); | ||
return aStatic.kind !== "none" && bStatic.kind !== "none" && aStatic.value === bStatic.value; | ||
} | ||
} | ||
} | ||
function getVariableInitNodeLoose(variable, at) { | ||
if (variable == null) return unit; | ||
const node = getVariableInitNode(variable, at); | ||
if (node != null) return node; | ||
const def = variable.defs.at(at); | ||
if (def?.type === DefinitionType.Parameter && AST.isFunction(def.node)) return def.node; | ||
return unit; | ||
if (variable == null) return unit; | ||
const node = getVariableInitNode(variable, at); | ||
if (node != null) return node; | ||
const def = variable.defs.at(at); | ||
if (def?.type === DefinitionType.Parameter && AST.isFunction(def.node)) return def.node; | ||
return unit; | ||
} | ||
export { ConstructionDetectionHint, findPropertyInProperties, findVariable2 as findVariable, getChidScopes, getConstruction, getVariableDeclaratorId, getVariableInitNode, getVariables, isNodeValueEqual, toStaticValue }; | ||
//#endregion | ||
export { ConstructionDetectionHint, findPropertyInProperties, findVariable, getChidScopes, getConstruction, getVariableDeclaratorId, getVariableInitNode, getVariables, isNodeValueEqual, toStaticValue }; |
{ | ||
"name": "@eslint-react/var", | ||
"version": "2.0.0-beta.44", | ||
"version": "2.0.0-beta.47", | ||
"description": "ESLint React's TSESTree AST utility module for static analysis of variables.", | ||
@@ -35,7 +35,7 @@ "homepage": "https://github.com/Rel1cx/eslint-react", | ||
"ts-pattern": "^5.8.0", | ||
"@eslint-react/ast": "2.0.0-beta.44", | ||
"@eslint-react/eff": "2.0.0-beta.44" | ||
"@eslint-react/ast": "2.0.0-beta.47", | ||
"@eslint-react/eff": "2.0.0-beta.47" | ||
}, | ||
"devDependencies": { | ||
"tsup": "^8.5.0", | ||
"tsdown": "^0.14.2", | ||
"@local/configs": "0.0.0" | ||
@@ -47,3 +47,3 @@ }, | ||
"scripts": { | ||
"build": "tsup", | ||
"build": "tsdown --dts-resolve", | ||
"lint:publish": "publint", | ||
@@ -50,0 +50,0 @@ "lint:ts": "tsc --noEmit" |
16485
1.84%363
4.61%+ Added
+ Added
- Removed
- Removed