@eslint-react/var 3.0.0-next.6 → 3.0.0-next.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +31 -79
- package/dist/index.js +116 -225
- package/package.json +6 -6
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { TSESTree } from "@typescript-eslint/types";
|
|
|
3
3
|
import { Scope, Variable } from "@typescript-eslint/scope-manager";
|
|
4
4
|
import { RuleContext } from "@eslint-react/shared";
|
|
5
5
|
|
|
6
|
-
//#region src/
|
|
6
|
+
//#region src/find-enclosing-assignment-target.d.ts
|
|
7
7
|
/**
|
|
8
8
|
* Finds the enclosing assignment target (variable, property, etc.) for a given node
|
|
9
9
|
*
|
|
@@ -16,54 +16,23 @@ declare function findEnclosingAssignmentTarget(node: TSESTree.Node): TSESTree.Ar
|
|
|
16
16
|
* Type representing the possible assignment targets returned by `findEnclosingAssignmentTarget`
|
|
17
17
|
*/
|
|
18
18
|
type AssignmentTarget = ReturnType<typeof findEnclosingAssignmentTarget>;
|
|
19
|
-
/**
|
|
20
|
-
* Check if two assignment targets are equal
|
|
21
|
-
* Compares nodes directly or by their values
|
|
22
|
-
* @param context The rule context
|
|
23
|
-
* @param a The first node to compare
|
|
24
|
-
* @param b The second node to compare
|
|
25
|
-
* @returns True if the assignment targets are equal
|
|
26
|
-
* @internal
|
|
27
|
-
*/
|
|
28
|
-
declare function isAssignmentTargetEqual(context: RuleContext, a: TSESTree.Node, b: TSESTree.Node): boolean;
|
|
29
|
-
//#endregion
|
|
30
|
-
//#region src/var-definition.d.ts
|
|
31
|
-
/**
|
|
32
|
-
* Get the definition node of a variable at a specific definition index
|
|
33
|
-
* @param variable The variable to get the definition node from
|
|
34
|
-
* @param at The index of the definition to retrieve (negative index supported)
|
|
35
|
-
* @returns The definition node or unit if not found
|
|
36
|
-
*/
|
|
37
|
-
declare function getVariableDefinitionNode(variable: Variable | unit, at: number): unit | TSESTree.ClassDeclaration | TSESTree.ClassDeclarationWithName | TSESTree.ClassDeclarationWithOptionalName | TSESTree.Expression | TSESTree.FunctionDeclaration | TSESTree.FunctionDeclarationWithName | TSESTree.FunctionDeclarationWithOptionalName;
|
|
38
|
-
/**
|
|
39
|
-
* Get the definition node of a variable at a specific definition index (loose version)
|
|
40
|
-
* Also returns the function node if the definition is a parameter
|
|
41
|
-
* @param variable The variable to get the definition node from
|
|
42
|
-
* @param at The index of the definition to retrieve
|
|
43
|
-
* @returns The definition node or unit if not found
|
|
44
|
-
*/
|
|
45
|
-
declare function getVariableDefinitionNodeLoose(variable: Variable | unit, at: number): unit | TSESTree.ClassDeclaration | TSESTree.ClassDeclarationWithName | TSESTree.ClassDeclarationWithOptionalName | TSESTree.Expression | TSESTree.FunctionDeclaration | TSESTree.FunctionDeclarationWithName | TSESTree.FunctionDeclarationWithOptionalName;
|
|
46
19
|
//#endregion
|
|
47
|
-
//#region src/
|
|
20
|
+
//#region src/find-variable.d.ts
|
|
48
21
|
/**
|
|
49
|
-
* Find
|
|
50
|
-
* @param
|
|
51
|
-
* @
|
|
52
|
-
* @
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
//#region src/var-node-equality.d.ts
|
|
57
|
-
/**
|
|
58
|
-
* Determine whether node value equals to another node value
|
|
59
|
-
* @param a node to compare
|
|
60
|
-
* @param b node to compare
|
|
61
|
-
* @param initialScopes initial scopes of the two nodes
|
|
62
|
-
* @returns `true` if node value equal
|
|
22
|
+
* Find a variable by name or identifier node in the scope chain
|
|
23
|
+
* @param initialScope The scope to start searching from
|
|
24
|
+
* @returns The found variable or unit if not found
|
|
25
|
+
* @overload
|
|
26
|
+
* @param nameOrNode The variable name or identifier node to find
|
|
27
|
+
* @param initialScope The scope to start searching from
|
|
28
|
+
* @returns The found variable or unit if not found
|
|
63
29
|
*/
|
|
64
|
-
declare
|
|
30
|
+
declare const findVariable: {
|
|
31
|
+
(initialScope: Scope): (nameOrNode: string | TSESTree.Identifier | unit) => Variable | unit;
|
|
32
|
+
(nameOrNode: string | TSESTree.Identifier | unit, initialScope: Scope): Variable | unit;
|
|
33
|
+
};
|
|
65
34
|
//#endregion
|
|
66
|
-
//#region src/
|
|
35
|
+
//#region src/get-object-type.d.ts
|
|
67
36
|
/**
|
|
68
37
|
* Represents the type classification of an object node
|
|
69
38
|
*/
|
|
@@ -101,43 +70,26 @@ type ObjectType = {
|
|
|
101
70
|
*/
|
|
102
71
|
declare function getObjectType(node: TSESTree.Node | unit, initialScope: Scope): ObjectType | unit;
|
|
103
72
|
//#endregion
|
|
104
|
-
//#region src/
|
|
73
|
+
//#region src/is-assignment-target-equal.d.ts
|
|
105
74
|
/**
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
* @param
|
|
109
|
-
* @param
|
|
110
|
-
* @param
|
|
111
|
-
* @
|
|
112
|
-
* @
|
|
75
|
+
* Check if two assignment targets are equal
|
|
76
|
+
* Compares nodes directly or by their values
|
|
77
|
+
* @param context The rule context
|
|
78
|
+
* @param a The first node to compare
|
|
79
|
+
* @param b The second node to compare
|
|
80
|
+
* @returns True if the assignment targets are equal
|
|
81
|
+
* @internal
|
|
113
82
|
*/
|
|
114
|
-
declare function
|
|
83
|
+
declare function isAssignmentTargetEqual(context: RuleContext, a: TSESTree.Node, b: TSESTree.Node): boolean;
|
|
115
84
|
//#endregion
|
|
116
|
-
//#region src/
|
|
117
|
-
/**
|
|
118
|
-
* Get all variables from the given scope up to the global scope
|
|
119
|
-
* @param initialScope The scope to start from
|
|
120
|
-
* @returns All variables from the given scope up to the global scope
|
|
121
|
-
*/
|
|
122
|
-
declare function getVariables(initialScope: Scope): Variable[];
|
|
85
|
+
//#region src/is-value-equal.d.ts
|
|
123
86
|
/**
|
|
124
|
-
*
|
|
125
|
-
* @param
|
|
126
|
-
* @
|
|
127
|
-
* @
|
|
128
|
-
* @
|
|
129
|
-
* @param initialScope The scope to start searching from
|
|
130
|
-
* @returns The found variable or unit if not found
|
|
131
|
-
*/
|
|
132
|
-
declare const findVariable: {
|
|
133
|
-
(initialScope: Scope): (nameOrNode: string | TSESTree.Identifier | unit) => Variable | unit;
|
|
134
|
-
(nameOrNode: string | TSESTree.Identifier | unit, initialScope: Scope): Variable | unit;
|
|
135
|
-
};
|
|
136
|
-
/**
|
|
137
|
-
* Get all child scopes recursively from a given scope
|
|
138
|
-
* @param scope The scope to get child scopes from
|
|
139
|
-
* @returns Array of all child scopes including the input scope
|
|
87
|
+
* Determine whether node value equals to another node value
|
|
88
|
+
* @param a node to compare
|
|
89
|
+
* @param b node to compare
|
|
90
|
+
* @param initialScopes initial scopes of the two nodes
|
|
91
|
+
* @returns `true` if node value equal
|
|
140
92
|
*/
|
|
141
|
-
declare function
|
|
93
|
+
declare function isValueEqual(a: TSESTree.Node, b: TSESTree.Node, initialScopes: [aScope: Scope, bScope: Scope]): boolean;
|
|
142
94
|
//#endregion
|
|
143
|
-
export { AssignmentTarget, ObjectType, findEnclosingAssignmentTarget,
|
|
95
|
+
export { AssignmentTarget, ObjectType, findEnclosingAssignmentTarget, findVariable, getObjectType, isAssignmentTargetEqual, isValueEqual };
|
package/dist/index.js
CHANGED
|
@@ -1,154 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { dual, identity, unit } from "@eslint-react/eff";
|
|
1
|
+
import { dual, unit } from "@eslint-react/eff";
|
|
3
2
|
import { AST_NODE_TYPES } from "@typescript-eslint/types";
|
|
4
3
|
import * as astUtils from "@typescript-eslint/utils/ast-utils";
|
|
5
4
|
import { getStaticValue } from "@typescript-eslint/utils/ast-utils";
|
|
6
|
-
import { DefinitionType
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
//#region src/var-definition.ts
|
|
10
|
-
/**
|
|
11
|
-
* Get the definition node of a variable at a specific definition index
|
|
12
|
-
* @param variable The variable to get the definition node from
|
|
13
|
-
* @param at The index of the definition to retrieve (negative index supported)
|
|
14
|
-
* @returns The definition node or unit if not found
|
|
15
|
-
*/
|
|
16
|
-
function getVariableDefinitionNode(variable, at) {
|
|
17
|
-
if (variable == null) return unit;
|
|
18
|
-
const def = variable.defs.at(at);
|
|
19
|
-
if (def == null) return unit;
|
|
20
|
-
switch (true) {
|
|
21
|
-
case def.type === DefinitionType.FunctionName && def.node.type === AST_NODE_TYPES.FunctionDeclaration: return def.node;
|
|
22
|
-
case def.type === DefinitionType.ClassName && def.node.type === AST_NODE_TYPES.ClassDeclaration: return def.node;
|
|
23
|
-
case "init" in def.node && def.node.init != null && !("declarations" in def.node.init): return def.node.init;
|
|
24
|
-
default: return unit;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Get the definition node of a variable at a specific definition index (loose version)
|
|
29
|
-
* Also returns the function node if the definition is a parameter
|
|
30
|
-
* @param variable The variable to get the definition node from
|
|
31
|
-
* @param at The index of the definition to retrieve
|
|
32
|
-
* @returns The definition node or unit if not found
|
|
33
|
-
*/
|
|
34
|
-
function getVariableDefinitionNodeLoose(variable, at) {
|
|
35
|
-
if (variable == null) return unit;
|
|
36
|
-
const node = getVariableDefinitionNode(variable, at);
|
|
37
|
-
if (node != null) return node;
|
|
38
|
-
const def = variable.defs.at(at);
|
|
39
|
-
if (def?.type === DefinitionType.Parameter && ast.isFunction(def.node)) return def.node;
|
|
40
|
-
return unit;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
//#endregion
|
|
44
|
-
//#region src/var-scope.ts
|
|
45
|
-
/**
|
|
46
|
-
* Get all variables from the given scope up to the global scope
|
|
47
|
-
* @param initialScope The scope to start from
|
|
48
|
-
* @returns All variables from the given scope up to the global scope
|
|
49
|
-
*/
|
|
50
|
-
function getVariables(initialScope) {
|
|
51
|
-
let scope = initialScope;
|
|
52
|
-
const variables = [...scope.variables];
|
|
53
|
-
while (scope.type !== ScopeType.global) {
|
|
54
|
-
scope = scope.upper;
|
|
55
|
-
variables.push(...scope.variables);
|
|
56
|
-
}
|
|
57
|
-
return variables.reverse();
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Find a variable by name or identifier node in the scope chain
|
|
61
|
-
* @param initialScope The scope to start searching from
|
|
62
|
-
* @returns The found variable or unit if not found
|
|
63
|
-
* @overload
|
|
64
|
-
* @param nameOrNode The variable name or identifier node to find
|
|
65
|
-
* @param initialScope The scope to start searching from
|
|
66
|
-
* @returns The found variable or unit if not found
|
|
67
|
-
*/
|
|
68
|
-
const findVariable = dual(2, (nameOrNode, initialScope) => {
|
|
69
|
-
if (nameOrNode == null) return unit;
|
|
70
|
-
return astUtils.findVariable(initialScope, nameOrNode) ?? unit;
|
|
71
|
-
});
|
|
72
|
-
/**
|
|
73
|
-
* Get all child scopes recursively from a given scope
|
|
74
|
-
* @param scope The scope to get child scopes from
|
|
75
|
-
* @returns Array of all child scopes including the input scope
|
|
76
|
-
*/
|
|
77
|
-
function getChildScopes(scope) {
|
|
78
|
-
const scopes = [scope];
|
|
79
|
-
for (const childScope of scope.childScopes) scopes.push(...getChildScopes(childScope));
|
|
80
|
-
return scopes;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
//#endregion
|
|
84
|
-
//#region src/var-node-equality.ts
|
|
85
|
-
const thisBlockTypes = [
|
|
86
|
-
AST_NODE_TYPES.FunctionDeclaration,
|
|
87
|
-
AST_NODE_TYPES.FunctionExpression,
|
|
88
|
-
AST_NODE_TYPES.ClassBody,
|
|
89
|
-
AST_NODE_TYPES.Program
|
|
90
|
-
];
|
|
91
|
-
/**
|
|
92
|
-
* Determine whether node value equals to another node value
|
|
93
|
-
* @param a node to compare
|
|
94
|
-
* @param b node to compare
|
|
95
|
-
* @param initialScopes initial scopes of the two nodes
|
|
96
|
-
* @returns `true` if node value equal
|
|
97
|
-
*/
|
|
98
|
-
function isNodeEqual(a, b, initialScopes) {
|
|
99
|
-
a = ast.isTypeExpression(a) ? ast.getUnderlyingExpression(a) : a;
|
|
100
|
-
b = ast.isTypeExpression(b) ? ast.getUnderlyingExpression(b) : b;
|
|
101
|
-
const [aScope, bScope] = initialScopes;
|
|
102
|
-
switch (true) {
|
|
103
|
-
case a === b: return true;
|
|
104
|
-
case a.type === AST_NODE_TYPES.Literal && b.type === AST_NODE_TYPES.Literal: return a.value === b.value;
|
|
105
|
-
case a.type === AST_NODE_TYPES.TemplateElement && b.type === AST_NODE_TYPES.TemplateElement: return a.value.cooked === b.value.cooked;
|
|
106
|
-
case a.type === AST_NODE_TYPES.Identifier && b.type === AST_NODE_TYPES.Identifier: {
|
|
107
|
-
const aVar = findVariable(a, aScope);
|
|
108
|
-
const bVar = findVariable(b, bScope);
|
|
109
|
-
const aVarNode = getVariableDefinitionNodeLoose(aVar, 0);
|
|
110
|
-
const bVarNode = getVariableDefinitionNodeLoose(bVar, 0);
|
|
111
|
-
const aVarNodeParent = aVarNode?.parent;
|
|
112
|
-
const bVarNodeParent = bVarNode?.parent;
|
|
113
|
-
const aDef = aVar?.defs.at(0);
|
|
114
|
-
const bDef = bVar?.defs.at(0);
|
|
115
|
-
const aDefParentParent = aDef?.parent?.parent;
|
|
116
|
-
const bDefParentParent = bDef?.parent?.parent;
|
|
117
|
-
switch (true) {
|
|
118
|
-
case aVarNodeParent?.type === AST_NODE_TYPES.CallExpression && bVarNodeParent?.type === AST_NODE_TYPES.CallExpression && ast.isFunction(aVarNode) && ast.isFunction(bVarNode): {
|
|
119
|
-
if (!ast.isNodeEqual(aVarNodeParent.callee, bVarNodeParent.callee)) return false;
|
|
120
|
-
const aParams = aVarNode.params;
|
|
121
|
-
const bParams = bVarNode.params;
|
|
122
|
-
const aPos = aParams.findIndex((x) => ast.isNodeEqual(x, a));
|
|
123
|
-
const bPos = bParams.findIndex((x) => ast.isNodeEqual(x, b));
|
|
124
|
-
return aPos !== -1 && bPos !== -1 && aPos === bPos;
|
|
125
|
-
}
|
|
126
|
-
case aDefParentParent?.type === AST_NODE_TYPES.ForOfStatement && bDefParentParent?.type === AST_NODE_TYPES.ForOfStatement: {
|
|
127
|
-
const aLeft = aDefParentParent.left;
|
|
128
|
-
const bLeft = bDefParentParent.left;
|
|
129
|
-
if (aLeft.type !== bLeft.type) return false;
|
|
130
|
-
const aRight = aDefParentParent.right;
|
|
131
|
-
const bRight = bDefParentParent.right;
|
|
132
|
-
return ast.isNodeEqual(aRight, bRight);
|
|
133
|
-
}
|
|
134
|
-
default: return aVar != null && bVar != null && aVar === bVar;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
case a.type === AST_NODE_TYPES.MemberExpression && b.type === AST_NODE_TYPES.MemberExpression: return ast.isNodeEqual(a.property, b.property) && isNodeEqual(a.object, b.object, initialScopes);
|
|
138
|
-
case a.type === AST_NODE_TYPES.ThisExpression && b.type === AST_NODE_TYPES.ThisExpression:
|
|
139
|
-
if (aScope.block === bScope.block) return true;
|
|
140
|
-
return ast.findParentNode(a, ast.isOneOf(thisBlockTypes)) === ast.findParentNode(b, ast.isOneOf(thisBlockTypes));
|
|
141
|
-
default: {
|
|
142
|
-
const aStatic = getStaticValue(a, aScope);
|
|
143
|
-
const bStatic = getStaticValue(b, bScope);
|
|
144
|
-
return aStatic != null && bStatic != null && aStatic.value === bStatic.value;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
5
|
+
import { DefinitionType } from "@typescript-eslint/scope-manager";
|
|
6
|
+
import * as ast from "@eslint-react/ast";
|
|
148
7
|
|
|
149
|
-
//#
|
|
150
|
-
//#region src/var-assignment-target.ts
|
|
151
|
-
/** eslint-disable jsdoc/require-param */
|
|
8
|
+
//#region src/find-enclosing-assignment-target.ts
|
|
152
9
|
/**
|
|
153
10
|
* Finds the enclosing assignment target (variable, property, etc.) for a given node
|
|
154
11
|
*
|
|
@@ -165,63 +22,25 @@ function findEnclosingAssignmentTarget(node) {
|
|
|
165
22
|
default: return findEnclosingAssignmentTarget(node.parent);
|
|
166
23
|
}
|
|
167
24
|
}
|
|
168
|
-
/**
|
|
169
|
-
* Check if two assignment targets are equal
|
|
170
|
-
* Compares nodes directly or by their values
|
|
171
|
-
* @param context The rule context
|
|
172
|
-
* @param a The first node to compare
|
|
173
|
-
* @param b The second node to compare
|
|
174
|
-
* @returns True if the assignment targets are equal
|
|
175
|
-
* @internal
|
|
176
|
-
*/
|
|
177
|
-
function isAssignmentTargetEqual(context, a, b) {
|
|
178
|
-
return ast.isNodeEqual(a, b) || isNodeEqual(a, b, [context.sourceCode.getScope(a), context.sourceCode.getScope(b)]);
|
|
179
|
-
}
|
|
180
25
|
|
|
181
26
|
//#endregion
|
|
182
|
-
//#region src/
|
|
27
|
+
//#region src/find-variable.ts
|
|
183
28
|
/**
|
|
184
|
-
*
|
|
185
|
-
* @param
|
|
186
|
-
* @returns The
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
arguments: P.select(),
|
|
192
|
-
callee: {
|
|
193
|
-
type: AST_NODE_TYPES.Identifier,
|
|
194
|
-
name: "require"
|
|
195
|
-
}
|
|
196
|
-
}, identity).with({
|
|
197
|
-
type: AST_NODE_TYPES.MemberExpression,
|
|
198
|
-
object: P.select()
|
|
199
|
-
}, getRequireExpressionArguments).otherwise(() => null);
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Find the import source of a variable
|
|
203
|
-
* @param name The variable name
|
|
204
|
-
* @param initialScope The initial scope to search
|
|
205
|
-
* @returns The import source or undefined if not found
|
|
29
|
+
* Find a variable by name or identifier node in the scope chain
|
|
30
|
+
* @param initialScope The scope to start searching from
|
|
31
|
+
* @returns The found variable or unit if not found
|
|
32
|
+
* @overload
|
|
33
|
+
* @param nameOrNode The variable name or identifier node to find
|
|
34
|
+
* @param initialScope The scope to start searching from
|
|
35
|
+
* @returns The found variable or unit if not found
|
|
206
36
|
*/
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if (node.type === AST_NODE_TYPES.VariableDeclarator && node.init != null) {
|
|
212
|
-
const { init } = node;
|
|
213
|
-
if (init.type === AST_NODE_TYPES.MemberExpression && init.object.type === AST_NODE_TYPES.Identifier) return findImportSource(init.object.name, initialScope);
|
|
214
|
-
if (init.type === AST_NODE_TYPES.Identifier) return findImportSource(init.name, initialScope);
|
|
215
|
-
const arg0 = getRequireExpressionArguments(init)?.[0];
|
|
216
|
-
if (arg0 == null || !ast.isLiteral(arg0, "string")) return unit;
|
|
217
|
-
return arg0.value;
|
|
218
|
-
}
|
|
219
|
-
if (parent?.type === AST_NODE_TYPES.ImportDeclaration) return parent.source.value;
|
|
220
|
-
return unit;
|
|
221
|
-
}
|
|
37
|
+
const findVariable = dual(2, (nameOrNode, initialScope) => {
|
|
38
|
+
if (nameOrNode == null) return unit;
|
|
39
|
+
return astUtils.findVariable(initialScope, nameOrNode) ?? unit;
|
|
40
|
+
});
|
|
222
41
|
|
|
223
42
|
//#endregion
|
|
224
|
-
//#region src/
|
|
43
|
+
//#region src/get-object-type.ts
|
|
225
44
|
/**
|
|
226
45
|
* Detect the ObjectType of a given node
|
|
227
46
|
* @param node The node to check
|
|
@@ -265,9 +84,20 @@ function getObjectType(node, initialScope) {
|
|
|
265
84
|
node
|
|
266
85
|
};
|
|
267
86
|
return unit;
|
|
268
|
-
case AST_NODE_TYPES.Identifier:
|
|
269
|
-
|
|
270
|
-
|
|
87
|
+
case AST_NODE_TYPES.Identifier: {
|
|
88
|
+
function resolve(v) {
|
|
89
|
+
if (v == null) return unit;
|
|
90
|
+
const def = v.defs.at(-1);
|
|
91
|
+
if (def == null) return unit;
|
|
92
|
+
if (def.type === DefinitionType.Variable) return def.node.init;
|
|
93
|
+
if (def.type === DefinitionType.Parameter) return unit;
|
|
94
|
+
if (def.type === DefinitionType.ImportBinding) return unit;
|
|
95
|
+
return def.node;
|
|
96
|
+
}
|
|
97
|
+
const initNode = resolve(initialScope.set.get(node.name));
|
|
98
|
+
if (initNode == null) return unit;
|
|
99
|
+
return getObjectType(initNode, initialScope);
|
|
100
|
+
}
|
|
271
101
|
case AST_NODE_TYPES.MemberExpression:
|
|
272
102
|
if (!("object" in node)) return unit;
|
|
273
103
|
return getObjectType(node.object, initialScope);
|
|
@@ -292,35 +122,96 @@ function getObjectType(node, initialScope) {
|
|
|
292
122
|
}
|
|
293
123
|
|
|
294
124
|
//#endregion
|
|
295
|
-
//#region src/
|
|
125
|
+
//#region src/is-value-equal.ts
|
|
126
|
+
const thisBlockTypes = [
|
|
127
|
+
AST_NODE_TYPES.FunctionDeclaration,
|
|
128
|
+
AST_NODE_TYPES.FunctionExpression,
|
|
129
|
+
AST_NODE_TYPES.ClassBody,
|
|
130
|
+
AST_NODE_TYPES.Program
|
|
131
|
+
];
|
|
296
132
|
/**
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
* @param
|
|
300
|
-
* @param
|
|
301
|
-
* @
|
|
302
|
-
* @param seen Set of already seen variable names to prevent circular references
|
|
303
|
-
* @returns The found property or unit if not found
|
|
133
|
+
* Determine whether node value equals to another node value
|
|
134
|
+
* @param a node to compare
|
|
135
|
+
* @param b node to compare
|
|
136
|
+
* @param initialScopes initial scopes of the two nodes
|
|
137
|
+
* @returns `true` if node value equal
|
|
304
138
|
*/
|
|
305
|
-
function
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
139
|
+
function isValueEqual(a, b, initialScopes) {
|
|
140
|
+
a = ast.isTypeExpression(a) ? ast.getUnderlyingExpression(a) : a;
|
|
141
|
+
b = ast.isTypeExpression(b) ? ast.getUnderlyingExpression(b) : b;
|
|
142
|
+
const [aScope, bScope] = initialScopes;
|
|
143
|
+
switch (true) {
|
|
144
|
+
case a === b: return true;
|
|
145
|
+
case a.type === AST_NODE_TYPES.Literal && b.type === AST_NODE_TYPES.Literal: return a.value === b.value;
|
|
146
|
+
case a.type === AST_NODE_TYPES.TemplateElement && b.type === AST_NODE_TYPES.TemplateElement: return a.value.cooked === b.value.cooked;
|
|
147
|
+
case a.type === AST_NODE_TYPES.Identifier && b.type === AST_NODE_TYPES.Identifier: {
|
|
148
|
+
const aVar = findVariable(a, aScope);
|
|
149
|
+
const bVar = findVariable(b, bScope);
|
|
150
|
+
const resolve = (variable) => {
|
|
151
|
+
if (variable == null) return unit;
|
|
152
|
+
const def = variable.defs.at(0);
|
|
153
|
+
if (def != null) switch (true) {
|
|
154
|
+
case def.type === DefinitionType.FunctionName && def.node.type === AST_NODE_TYPES.FunctionDeclaration: return def.node;
|
|
155
|
+
case def.type === DefinitionType.ClassName && def.node.type === AST_NODE_TYPES.ClassDeclaration: return def.node;
|
|
156
|
+
case "init" in def.node && def.node.init != null && !("declarations" in def.node.init): return def.node.init;
|
|
315
157
|
}
|
|
316
|
-
return
|
|
158
|
+
if (def?.type === DefinitionType.Parameter && ast.isFunction(def.node)) return def.node;
|
|
159
|
+
return unit;
|
|
160
|
+
};
|
|
161
|
+
const aVarInit = resolve(aVar);
|
|
162
|
+
const bVarInit = resolve(bVar);
|
|
163
|
+
const aVarInitParent = aVarInit?.parent;
|
|
164
|
+
const bVarInitParent = bVarInit?.parent;
|
|
165
|
+
const aDef = aVar?.defs.at(0);
|
|
166
|
+
const bDef = bVar?.defs.at(0);
|
|
167
|
+
const aDefParentParent = aDef?.parent?.parent;
|
|
168
|
+
const bDefParentParent = bDef?.parent?.parent;
|
|
169
|
+
switch (true) {
|
|
170
|
+
case aVarInitParent?.type === AST_NODE_TYPES.CallExpression && bVarInitParent?.type === AST_NODE_TYPES.CallExpression && ast.isFunction(aVarInit) && ast.isFunction(bVarInit): {
|
|
171
|
+
if (!ast.isNodeEqual(aVarInitParent.callee, bVarInitParent.callee)) return false;
|
|
172
|
+
const aParams = aVarInit.params;
|
|
173
|
+
const bParams = bVarInit.params;
|
|
174
|
+
const aPos = aParams.findIndex((x) => ast.isNodeEqual(x, a));
|
|
175
|
+
const bPos = bParams.findIndex((x) => ast.isNodeEqual(x, b));
|
|
176
|
+
return aPos !== -1 && bPos !== -1 && aPos === bPos;
|
|
177
|
+
}
|
|
178
|
+
case aDefParentParent?.type === AST_NODE_TYPES.ForOfStatement && bDefParentParent?.type === AST_NODE_TYPES.ForOfStatement: {
|
|
179
|
+
const aLeft = aDefParentParent.left;
|
|
180
|
+
const bLeft = bDefParentParent.left;
|
|
181
|
+
if (aLeft.type !== bLeft.type) return false;
|
|
182
|
+
const aRight = aDefParentParent.right;
|
|
183
|
+
const bRight = bDefParentParent.right;
|
|
184
|
+
return ast.isNodeEqual(aRight, bRight);
|
|
185
|
+
}
|
|
186
|
+
default: return aVar != null && bVar != null && aVar === bVar;
|
|
317
187
|
}
|
|
318
|
-
case AST_NODE_TYPES.ObjectExpression: return findProperty(name, prop.argument.properties, initialScope, seen) != null;
|
|
319
|
-
default: return false;
|
|
320
188
|
}
|
|
321
|
-
return
|
|
322
|
-
|
|
189
|
+
case a.type === AST_NODE_TYPES.MemberExpression && b.type === AST_NODE_TYPES.MemberExpression: return ast.isNodeEqual(a.property, b.property) && isValueEqual(a.object, b.object, initialScopes);
|
|
190
|
+
case a.type === AST_NODE_TYPES.ThisExpression && b.type === AST_NODE_TYPES.ThisExpression:
|
|
191
|
+
if (aScope.block === bScope.block) return true;
|
|
192
|
+
return ast.findParentNode(a, ast.isOneOf(thisBlockTypes)) === ast.findParentNode(b, ast.isOneOf(thisBlockTypes));
|
|
193
|
+
default: {
|
|
194
|
+
const aStatic = getStaticValue(a, aScope);
|
|
195
|
+
const bStatic = getStaticValue(b, bScope);
|
|
196
|
+
return aStatic != null && bStatic != null && aStatic.value === bStatic.value;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
//#endregion
|
|
202
|
+
//#region src/is-assignment-target-equal.ts
|
|
203
|
+
/**
|
|
204
|
+
* Check if two assignment targets are equal
|
|
205
|
+
* Compares nodes directly or by their values
|
|
206
|
+
* @param context The rule context
|
|
207
|
+
* @param a The first node to compare
|
|
208
|
+
* @param b The second node to compare
|
|
209
|
+
* @returns True if the assignment targets are equal
|
|
210
|
+
* @internal
|
|
211
|
+
*/
|
|
212
|
+
function isAssignmentTargetEqual(context, a, b) {
|
|
213
|
+
return ast.isNodeEqual(a, b) || isValueEqual(a, b, [context.sourceCode.getScope(a), context.sourceCode.getScope(b)]);
|
|
323
214
|
}
|
|
324
215
|
|
|
325
216
|
//#endregion
|
|
326
|
-
export { findEnclosingAssignmentTarget,
|
|
217
|
+
export { findEnclosingAssignmentTarget, findVariable, getObjectType, isAssignmentTargetEqual, isValueEqual };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eslint-react/var",
|
|
3
|
-
"version": "3.0.0-next.
|
|
3
|
+
"version": "3.0.0-next.61",
|
|
4
4
|
"description": "ESLint React's TSESTree AST utility module for static analysis of variables.",
|
|
5
5
|
"homepage": "https://github.com/Rel1cx/eslint-react",
|
|
6
6
|
"bugs": {
|
|
@@ -34,12 +34,12 @@
|
|
|
34
34
|
"@typescript-eslint/types": "canary",
|
|
35
35
|
"@typescript-eslint/utils": "canary",
|
|
36
36
|
"ts-pattern": "^5.9.0",
|
|
37
|
-
"@eslint-react/
|
|
38
|
-
"@eslint-react/eff": "3.0.0-next.
|
|
39
|
-
"@eslint-react/
|
|
37
|
+
"@eslint-react/shared": "3.0.0-next.61",
|
|
38
|
+
"@eslint-react/eff": "3.0.0-next.61",
|
|
39
|
+
"@eslint-react/ast": "3.0.0-next.61"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"tsdown": "^0.
|
|
42
|
+
"tsdown": "^0.21.0-beta.2",
|
|
43
43
|
"@local/configs": "0.0.0"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
@@ -52,6 +52,6 @@
|
|
|
52
52
|
"scripts": {
|
|
53
53
|
"build": "tsdown --dts-resolve",
|
|
54
54
|
"lint:publish": "publint",
|
|
55
|
-
"lint:ts": "
|
|
55
|
+
"lint:ts": "tsl"
|
|
56
56
|
}
|
|
57
57
|
}
|