@eslint-react/var 3.0.0-next.62 → 3.0.0-next.64

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 CHANGED
@@ -1,38 +1,8 @@
1
- import { unit } from "@eslint-react/eff";
1
+ import { Scope } from "@typescript-eslint/scope-manager";
2
2
  import { TSESTree } from "@typescript-eslint/types";
3
- import { Scope, Variable } from "@typescript-eslint/scope-manager";
4
3
  import { RuleContext } from "@eslint-react/shared";
5
4
 
6
- //#region src/find-enclosing-assignment-target.d.ts
7
- /**
8
- * Finds the enclosing assignment target (variable, property, etc.) for a given node
9
- *
10
- * @todo Verify correctness and completeness of this function
11
- * @param node The starting node
12
- * @returns The enclosing assignment target node, or undefined if not found
13
- */
14
- declare function findEnclosingAssignmentTarget(node: TSESTree.Node): TSESTree.ArrayExpression | TSESTree.ArrayPattern | TSESTree.ArrowFunctionExpression | TSESTree.AssignmentExpression | TSESTree.AwaitExpression | TSESTree.PrivateInExpression | TSESTree.SymmetricBinaryExpression | TSESTree.CallExpression | TSESTree.ChainExpression | TSESTree.ClassExpression | TSESTree.ConditionalExpression | TSESTree.FunctionExpression | TSESTree.Identifier | TSESTree.ImportExpression | TSESTree.JSXElement | TSESTree.JSXFragment | TSESTree.BigIntLiteral | TSESTree.BooleanLiteral | TSESTree.NullLiteral | TSESTree.NumberLiteral | TSESTree.RegExpLiteral | TSESTree.StringLiteral | TSESTree.LogicalExpression | TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedName | TSESTree.MetaProperty | TSESTree.NewExpression | TSESTree.ObjectExpression | TSESTree.ObjectPattern | TSESTree.PrivateIdentifier | TSESTree.SequenceExpression | TSESTree.Super | TSESTree.TaggedTemplateExpression | TSESTree.TemplateLiteral | TSESTree.ThisExpression | TSESTree.TSAsExpression | TSESTree.TSInstantiationExpression | TSESTree.TSNonNullExpression | TSESTree.TSSatisfiesExpression | TSESTree.TSTypeAssertion | TSESTree.UnaryExpressionBitwiseNot | TSESTree.UnaryExpressionDelete | TSESTree.UnaryExpressionMinus | TSESTree.UnaryExpressionNot | TSESTree.UnaryExpressionPlus | TSESTree.UnaryExpressionTypeof | TSESTree.UnaryExpressionVoid | TSESTree.UpdateExpression | TSESTree.YieldExpression | undefined;
15
- /**
16
- * Type representing the possible assignment targets returned by `findEnclosingAssignmentTarget`
17
- */
18
- type AssignmentTarget = ReturnType<typeof findEnclosingAssignmentTarget>;
19
- //#endregion
20
- //#region src/find-variable.d.ts
21
- /**
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
29
- */
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
- };
34
- //#endregion
35
- //#region src/get-object-type.d.ts
5
+ //#region src/compute-object-type.d.ts
36
6
  /**
37
7
  * Represents the type classification of an object node
38
8
  */
@@ -65,10 +35,23 @@ type ObjectType = {
65
35
  /**
66
36
  * Detect the ObjectType of a given node
67
37
  * @param node The node to check
68
- * @param initialScope The initial scope to check for variable declarations
69
38
  * @returns The ObjectType of the node, or undefined if not detected
70
39
  */
71
- declare function getObjectType(node: TSESTree.Node | unit, initialScope: Scope): ObjectType | unit;
40
+ declare function computeObjectType(context: RuleContext, node: TSESTree.Node | null): ObjectType | null;
41
+ //#endregion
42
+ //#region src/find-enclosing-assignment-target.d.ts
43
+ /**
44
+ * Finds the enclosing assignment target (variable, property, etc.) for a given node
45
+ *
46
+ * @todo Verify correctness and completeness of this function
47
+ * @param node The starting node
48
+ * @returns The enclosing assignment target node, or null if not found
49
+ */
50
+ declare function findEnclosingAssignmentTarget(node: TSESTree.Node): TSESTree.ArrayExpression | TSESTree.ArrayPattern | TSESTree.ArrowFunctionExpression | TSESTree.AssignmentExpression | TSESTree.AwaitExpression | TSESTree.PrivateInExpression | TSESTree.SymmetricBinaryExpression | TSESTree.CallExpression | TSESTree.ChainExpression | TSESTree.ClassExpression | TSESTree.ConditionalExpression | TSESTree.FunctionExpression | TSESTree.Identifier | TSESTree.ImportExpression | TSESTree.JSXElement | TSESTree.JSXFragment | TSESTree.BigIntLiteral | TSESTree.BooleanLiteral | TSESTree.NullLiteral | TSESTree.NumberLiteral | TSESTree.RegExpLiteral | TSESTree.StringLiteral | TSESTree.LogicalExpression | TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedName | TSESTree.MetaProperty | TSESTree.NewExpression | TSESTree.ObjectExpression | TSESTree.ObjectPattern | TSESTree.PrivateIdentifier | TSESTree.SequenceExpression | TSESTree.Super | TSESTree.TaggedTemplateExpression | TSESTree.TemplateLiteral | TSESTree.ThisExpression | TSESTree.TSAsExpression | TSESTree.TSInstantiationExpression | TSESTree.TSNonNullExpression | TSESTree.TSSatisfiesExpression | TSESTree.TSTypeAssertion | TSESTree.UnaryExpressionBitwiseNot | TSESTree.UnaryExpressionDelete | TSESTree.UnaryExpressionMinus | TSESTree.UnaryExpressionNot | TSESTree.UnaryExpressionPlus | TSESTree.UnaryExpressionTypeof | TSESTree.UnaryExpressionVoid | TSESTree.UpdateExpression | TSESTree.YieldExpression | null;
51
+ /**
52
+ * Type representing the possible assignment targets returned by `findEnclosingAssignmentTarget`
53
+ */
54
+ type AssignmentTarget = ReturnType<typeof findEnclosingAssignmentTarget>;
72
55
  //#endregion
73
56
  //#region src/is-assignment-target-equal.d.ts
74
57
  /**
@@ -92,4 +75,36 @@ declare function isAssignmentTargetEqual(context: RuleContext, a: TSESTree.Node,
92
75
  */
93
76
  declare function isValueEqual(a: TSESTree.Node, b: TSESTree.Node, initialScopes: [aScope: Scope, bScope: Scope]): boolean;
94
77
  //#endregion
95
- export { AssignmentTarget, ObjectType, findEnclosingAssignmentTarget, findVariable, getObjectType, isAssignmentTargetEqual, isValueEqual };
78
+ //#region src/resolve.d.ts
79
+ /**
80
+ * Resolves an identifier to the AST node that represents its value,
81
+ * suitable for use in ESLint rule analysis.
82
+ *
83
+ * The resolution follows these rules per definition type:
84
+ *
85
+ * | Definition type | `def.node` | Returns |
86
+ * |--------------------------|----------------------------------------------|------------------------------------|
87
+ * | `Variable` | `VariableDeclarator` | `def.node.init` (or `null`) |
88
+ * | `FunctionName` | `FunctionDeclaration` / `FunctionExpression` | `def.node` |
89
+ * | `ClassName` | `ClassDeclaration` / `ClassExpression` | `def.node` |
90
+ * | `Parameter` | containing function node | `def.node` (if a real function) |
91
+ * | `TSEnumName` | `TSEnumDeclaration` | `def.node` |
92
+ * | `TSEnumMember` | `TSEnumMember` | `def.node.initializer` (or `null`) |
93
+ * | `ImportBinding` | import specifier | `null` |
94
+ * | `CatchClause` | `CatchClause` | `null` |
95
+ * | `TSModuleName` | `TSModuleDeclaration` | `null` |
96
+ * | `Type` | type alias node | `null` |
97
+ * | `ImplicitGlobalVariable` | any node | `null` |
98
+ *
99
+ * @param context The ESLint rule context used for scope lookup.
100
+ * @param node The identifier to resolve.
101
+ * @param at Which definition to use when multiple exist (default: `0`; pass `-1` for the last).
102
+ * @param localOnly When `true`, look up the variable only in the node's own scope (faster, but
103
+ * will miss variables declared in an outer scope). When `false` (default), traverse the scope
104
+ * chain upward via `findVariable` so that references to outer-scope bindings are resolved
105
+ * correctly.
106
+ * @returns The resolved node, or `null` if the identifier cannot be resolved to a value node.
107
+ */
108
+ declare function resolve(context: RuleContext, node: TSESTree.Identifier, at?: number, localOnly?: boolean): TSESTree.Node | null;
109
+ //#endregion
110
+ export { AssignmentTarget, ObjectType, computeObjectType, findEnclosingAssignmentTarget, isAssignmentTargetEqual, isValueEqual, resolve };
package/dist/index.js CHANGED
@@ -1,54 +1,74 @@
1
- import { dual, unit } from "@eslint-react/eff";
2
- import { AST_NODE_TYPES } from "@typescript-eslint/types";
3
- import * as astUtils from "@typescript-eslint/utils/ast-utils";
4
- import { getStaticValue } from "@typescript-eslint/utils/ast-utils";
5
1
  import { DefinitionType } from "@typescript-eslint/scope-manager";
2
+ import { AST_NODE_TYPES } from "@typescript-eslint/types";
6
3
  import * as ast from "@eslint-react/ast";
4
+ import { findVariable, getStaticValue } from "@typescript-eslint/utils/ast-utils";
7
5
 
8
- //#region src/find-enclosing-assignment-target.ts
6
+ //#region src/resolve.ts
9
7
  /**
10
- * Finds the enclosing assignment target (variable, property, etc.) for a given node
8
+ * Resolves an identifier to the AST node that represents its value,
9
+ * suitable for use in ESLint rule analysis.
11
10
  *
12
- * @todo Verify correctness and completeness of this function
13
- * @param node The starting node
14
- * @returns The enclosing assignment target node, or undefined if not found
11
+ * The resolution follows these rules per definition type:
12
+ *
13
+ * | Definition type | `def.node` | Returns |
14
+ * |--------------------------|----------------------------------------------|------------------------------------|
15
+ * | `Variable` | `VariableDeclarator` | `def.node.init` (or `null`) |
16
+ * | `FunctionName` | `FunctionDeclaration` / `FunctionExpression` | `def.node` |
17
+ * | `ClassName` | `ClassDeclaration` / `ClassExpression` | `def.node` |
18
+ * | `Parameter` | containing function node | `def.node` (if a real function) |
19
+ * | `TSEnumName` | `TSEnumDeclaration` | `def.node` |
20
+ * | `TSEnumMember` | `TSEnumMember` | `def.node.initializer` (or `null`) |
21
+ * | `ImportBinding` | import specifier | `null` |
22
+ * | `CatchClause` | `CatchClause` | `null` |
23
+ * | `TSModuleName` | `TSModuleDeclaration` | `null` |
24
+ * | `Type` | type alias node | `null` |
25
+ * | `ImplicitGlobalVariable` | any node | `null` |
26
+ *
27
+ * @param context The ESLint rule context used for scope lookup.
28
+ * @param node The identifier to resolve.
29
+ * @param at Which definition to use when multiple exist (default: `0`; pass `-1` for the last).
30
+ * @param localOnly When `true`, look up the variable only in the node's own scope (faster, but
31
+ * will miss variables declared in an outer scope). When `false` (default), traverse the scope
32
+ * chain upward via `findVariable` so that references to outer-scope bindings are resolved
33
+ * correctly.
34
+ * @returns The resolved node, or `null` if the identifier cannot be resolved to a value node.
15
35
  */
16
- function findEnclosingAssignmentTarget(node) {
17
- switch (true) {
18
- case node.type === AST_NODE_TYPES.VariableDeclarator: return node.id;
19
- case node.type === AST_NODE_TYPES.AssignmentExpression: return node.left;
20
- case node.type === AST_NODE_TYPES.PropertyDefinition: return node.key;
21
- case node.type === AST_NODE_TYPES.BlockStatement || node.type === AST_NODE_TYPES.Program || node.parent === node: return unit;
22
- default: return findEnclosingAssignmentTarget(node.parent);
36
+ function resolve(context, node, at = 0, localOnly = false) {
37
+ const scope = context.sourceCode.getScope(node);
38
+ const variable = localOnly ? scope.set.get(node.name) : findVariable(scope, node);
39
+ if (variable == null) return null;
40
+ const def = variable.defs.at(at);
41
+ if (def == null) return null;
42
+ switch (def.type) {
43
+ case DefinitionType.FunctionName: return def.node;
44
+ case DefinitionType.ClassName: return def.node;
45
+ case DefinitionType.Variable: {
46
+ const { init } = def.node;
47
+ if (init == null) return null;
48
+ if ("declarations" in init) return null;
49
+ return init;
50
+ }
51
+ case DefinitionType.Parameter: return ast.isFunction(def.node) ? def.node : null;
52
+ case DefinitionType.TSEnumName: return def.node;
53
+ case DefinitionType.TSEnumMember: return def.node.initializer ?? null;
54
+ case DefinitionType.ImportBinding: return null;
55
+ case DefinitionType.CatchClause: return null;
56
+ case DefinitionType.TSModuleName: return null;
57
+ case DefinitionType.Type: return null;
58
+ case DefinitionType.ImplicitGlobalVariable: return null;
59
+ default: return null;
23
60
  }
24
61
  }
25
62
 
26
63
  //#endregion
27
- //#region src/find-variable.ts
28
- /**
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
36
- */
37
- const findVariable = dual(2, (nameOrNode, initialScope) => {
38
- if (nameOrNode == null) return unit;
39
- return astUtils.findVariable(initialScope, nameOrNode) ?? unit;
40
- });
41
-
42
- //#endregion
43
- //#region src/get-object-type.ts
64
+ //#region src/compute-object-type.ts
44
65
  /**
45
66
  * Detect the ObjectType of a given node
46
67
  * @param node The node to check
47
- * @param initialScope The initial scope to check for variable declarations
48
68
  * @returns The ObjectType of the node, or undefined if not detected
49
69
  */
50
- function getObjectType(node, initialScope) {
51
- if (node == null) return unit;
70
+ function computeObjectType(context, node) {
71
+ if (node == null) return null;
52
72
  switch (node.type) {
53
73
  case AST_NODE_TYPES.JSXElement:
54
74
  case AST_NODE_TYPES.JSXFragment: return {
@@ -83,42 +103,53 @@ function getObjectType(node, initialScope) {
83
103
  kind: "regexp",
84
104
  node
85
105
  };
86
- return unit;
106
+ return null;
87
107
  case AST_NODE_TYPES.Identifier: {
88
- const initNode = resolve(initialScope.set.get(node.name));
89
- if (initNode == null) return unit;
90
- return getObjectType(initNode, initialScope);
108
+ if ((context.sourceCode.getScope(node).set.get(node.name)?.defs.at(-1))?.type === DefinitionType.Parameter) return null;
109
+ const initNode = resolve(context, node, -1, true);
110
+ if (initNode == null) return null;
111
+ return computeObjectType(context, initNode);
91
112
  }
92
113
  case AST_NODE_TYPES.MemberExpression:
93
- if (!("object" in node)) return unit;
94
- return getObjectType(node.object, initialScope);
114
+ if (!("object" in node)) return null;
115
+ return computeObjectType(context, node.object);
95
116
  case AST_NODE_TYPES.AssignmentExpression:
96
117
  case AST_NODE_TYPES.AssignmentPattern:
97
- if (!("right" in node)) return unit;
98
- return getObjectType(node.right, initialScope);
99
- case AST_NODE_TYPES.LogicalExpression: return getObjectType(node.right, initialScope);
100
- case AST_NODE_TYPES.ConditionalExpression: return getObjectType(node.consequent, initialScope) ?? getObjectType(node.alternate, initialScope);
118
+ if (!("right" in node)) return null;
119
+ return computeObjectType(context, node.right);
120
+ case AST_NODE_TYPES.LogicalExpression: return computeObjectType(context, node.right);
121
+ case AST_NODE_TYPES.ConditionalExpression: return computeObjectType(context, node.consequent) ?? computeObjectType(context, node.alternate);
101
122
  case AST_NODE_TYPES.SequenceExpression:
102
- if (node.expressions.length === 0) return unit;
103
- return getObjectType(node.expressions[node.expressions.length - 1], initialScope);
123
+ if (node.expressions.length === 0) return null;
124
+ return computeObjectType(context, node.expressions[node.expressions.length - 1] ?? null);
104
125
  case AST_NODE_TYPES.CallExpression: return {
105
126
  kind: "unknown",
106
127
  node,
107
128
  reason: "call-expression"
108
129
  };
109
130
  default:
110
- if (!("expression" in node) || typeof node.expression !== "object") return unit;
111
- return getObjectType(node.expression, initialScope);
131
+ if (!("expression" in node) || typeof node.expression !== "object") return null;
132
+ return computeObjectType(context, node.expression);
112
133
  }
113
134
  }
114
- function resolve(v) {
115
- if (v == null) return unit;
116
- const def = v.defs.at(-1);
117
- if (def == null) return unit;
118
- if (def.type === DefinitionType.Variable) return def.node.init;
119
- if (def.type === DefinitionType.Parameter) return unit;
120
- if (def.type === DefinitionType.ImportBinding) return unit;
121
- return def.node;
135
+
136
+ //#endregion
137
+ //#region src/find-enclosing-assignment-target.ts
138
+ /**
139
+ * Finds the enclosing assignment target (variable, property, etc.) for a given node
140
+ *
141
+ * @todo Verify correctness and completeness of this function
142
+ * @param node The starting node
143
+ * @returns The enclosing assignment target node, or null if not found
144
+ */
145
+ function findEnclosingAssignmentTarget(node) {
146
+ switch (true) {
147
+ case node.type === AST_NODE_TYPES.VariableDeclarator: return node.id;
148
+ case node.type === AST_NODE_TYPES.AssignmentExpression: return node.left;
149
+ case node.type === AST_NODE_TYPES.PropertyDefinition: return node.key;
150
+ case node.type === AST_NODE_TYPES.BlockStatement || node.type === AST_NODE_TYPES.Program || node.parent === node: return null;
151
+ default: return findEnclosingAssignmentTarget(node.parent);
152
+ }
122
153
  }
123
154
 
124
155
  //#endregion
@@ -145,10 +176,10 @@ function isValueEqual(a, b, initialScopes) {
145
176
  case a.type === AST_NODE_TYPES.Literal && b.type === AST_NODE_TYPES.Literal: return a.value === b.value;
146
177
  case a.type === AST_NODE_TYPES.TemplateElement && b.type === AST_NODE_TYPES.TemplateElement: return a.value.cooked === b.value.cooked;
147
178
  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);
179
+ const aVar = findVariable(aScope, a);
180
+ const bVar = findVariable(bScope, b);
150
181
  const resolve = (variable) => {
151
- if (variable == null) return unit;
182
+ if (variable == null) return null;
152
183
  const def = variable.defs.at(0);
153
184
  if (def != null) switch (true) {
154
185
  case def.type === DefinitionType.FunctionName && def.node.type === AST_NODE_TYPES.FunctionDeclaration: return def.node;
@@ -156,7 +187,7 @@ function isValueEqual(a, b, initialScopes) {
156
187
  case "init" in def.node && def.node.init != null && !("declarations" in def.node.init): return def.node.init;
157
188
  }
158
189
  if (def?.type === DefinitionType.Parameter && ast.isFunction(def.node)) return def.node;
159
- return unit;
190
+ return null;
160
191
  };
161
192
  const aVarInit = resolve(aVar);
162
193
  const bVarInit = resolve(bVar);
@@ -214,4 +245,4 @@ function isAssignmentTargetEqual(context, a, b) {
214
245
  }
215
246
 
216
247
  //#endregion
217
- export { findEnclosingAssignmentTarget, findVariable, getObjectType, isAssignmentTargetEqual, isValueEqual };
248
+ export { computeObjectType, findEnclosingAssignmentTarget, isAssignmentTargetEqual, isValueEqual, resolve };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eslint-react/var",
3
- "version": "3.0.0-next.62",
3
+ "version": "3.0.0-next.64",
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,11 +34,12 @@
34
34
  "@typescript-eslint/types": "canary",
35
35
  "@typescript-eslint/utils": "canary",
36
36
  "ts-pattern": "^5.9.0",
37
- "@eslint-react/ast": "3.0.0-next.62",
38
- "@eslint-react/eff": "3.0.0-next.62",
39
- "@eslint-react/shared": "3.0.0-next.62"
37
+ "@eslint-react/ast": "3.0.0-next.64",
38
+ "@eslint-react/eff": "3.0.0-next.64",
39
+ "@eslint-react/shared": "3.0.0-next.64"
40
40
  },
41
41
  "devDependencies": {
42
+ "@typescript-eslint/typescript-estree": "canary",
42
43
  "tsdown": "^0.21.0-beta.2",
43
44
  "@local/configs": "0.0.0"
44
45
  },