@eslint-react/var 5.28.1 → 5.28.3

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
@@ -181,7 +181,12 @@ export declare function isValueEqual(context: RuleContext, a: TSESTree.Node, b:
181
181
  * - `localOnly`: If `true`, only consider variables declared in the same scope as the identifier
182
182
  * (this will miss variables declared in an outer scope). When `false` (default), traverse the
183
183
  * scope chain upward via `findVariable` so that references to outer-scope bindings are resolved
184
- * correctly.
184
+ * correctly. Ignored when `boundary` is set.
185
+ * - `boundary`: A node (typically the enclosing component or hook function) that bounds the
186
+ * scope-chain traversal. The identifier is resolved only if its binding is declared in a scope
187
+ * between the identifier's own scope and the boundary's scope (inclusive); bindings declared
188
+ * outside the boundary (e.g. module-level constants, which are stable across renders) are
189
+ * treated as unresolvable. The boundary node must be an ancestor of `node`.
185
190
  * @returns The resolved node, or `null` if the identifier cannot be resolved to a value node.
186
191
  *
187
192
  * For origin/pedigree tracking that maps destructured bindings to the declarator's
@@ -189,6 +194,7 @@ export declare function isValueEqual(context: RuleContext, a: TSESTree.Node, b:
189
194
  */
190
195
  export declare function resolve(context: RuleContext, node: TSESTree.Identifier, options?: Partial<{
191
196
  at: number;
197
+ boundary: TSESTree.Node;
192
198
  localOnly: boolean;
193
199
  }>): TSESTree.Node | null;
194
200
  //#endregion
@@ -227,7 +233,7 @@ export type ObjectType = {
227
233
  node: TSESTree.ObjectExpression | TSESTree.CallExpression;
228
234
  } | {
229
235
  kind: "class";
230
- node: TSESTree.ClassExpression;
236
+ node: TSESTree.ClassDeclaration | TSESTree.ClassExpression;
231
237
  } | {
232
238
  kind: "instance";
233
239
  node: TSESTree.NewExpression | TSESTree.ThisExpression;
@@ -246,9 +252,18 @@ export type ObjectType = {
246
252
  * Resolve the object type of the node.
247
253
  * @param context The ESLint rule context.
248
254
  * @param node The node to resolve.
255
+ * @param options Optional settings:
256
+ * - `boundary`: A node (typically the enclosing component or hook function) that bounds
257
+ * identifier resolution. Identifiers are resolved through their initializer only when the
258
+ * binding is declared within the boundary — this catches render-scoped constructions referenced
259
+ * from nested callbacks, while values declared outside the boundary (e.g. module-level
260
+ * constants, which are stable across renders) are treated as unresolvable. When omitted, only
261
+ * the identifier's own scope is searched.
249
262
  * @returns The object type of the node, or `null` when it cannot be resolved.
250
263
  */
251
- export declare function resolveObjectType(context: RuleContext, node: TSESTree.Node | null | undefined): ObjectType | null;
264
+ export declare function resolveObjectType(context: RuleContext, node: TSESTree.Node | null | undefined, options?: Partial<{
265
+ boundary: TSESTree.Node;
266
+ }>): ObjectType | null;
252
267
  //#endregion
253
268
  //#region src/resolve-origin.d.ts
254
269
  /**
package/dist/index.js CHANGED
@@ -318,9 +318,9 @@ function isInitializedFromReactNative(name, initialScope, importSource = "react-
318
318
  //#endregion
319
319
  //#region src/resolve.ts
320
320
  function resolve(context, node, options) {
321
- const { at = 0, localOnly = false } = options ?? {};
321
+ const { at = 0, boundary, localOnly = false } = options ?? {};
322
322
  const scope = context.sourceCode.getScope(node);
323
- const variable = localOnly ? scope.set.get(node.name) : findVariable(scope, node);
323
+ const variable = boundary != null ? findVariableWithinBoundary(scope, node.name, context.sourceCode.getScope(boundary)) : localOnly ? scope.set.get(node.name) : findVariable(scope, node);
324
324
  if (variable == null) return null;
325
325
  const def = variable.defs.at(at);
326
326
  if (def == null) return null;
@@ -345,6 +345,14 @@ function resolve(context, node, options) {
345
345
  default: return null;
346
346
  }
347
347
  }
348
+ function findVariableWithinBoundary(scope, name, boundaryScope) {
349
+ while (scope != null) {
350
+ const variable = scope.set.get(name);
351
+ if (variable != null || scope === boundaryScope) return variable ?? null;
352
+ scope = scope.upper;
353
+ }
354
+ return null;
355
+ }
348
356
  //#endregion
349
357
  //#region src/resolve-enclosing-assignment-target.ts
350
358
  function resolveEnclosingAssignmentTarget(node) {
@@ -360,7 +368,7 @@ function resolveEnclosingAssignmentTarget(node) {
360
368
  }
361
369
  //#endregion
362
370
  //#region src/resolve-object-type.ts
363
- function resolveObjectType(context, node) {
371
+ function resolveObjectType(context, node, options) {
364
372
  if (node == null) return null;
365
373
  switch (node.type) {
366
374
  case AST_NODE_TYPES.JSXElement:
@@ -376,6 +384,7 @@ function resolveObjectType(context, node) {
376
384
  kind: "plain",
377
385
  node
378
386
  };
387
+ case AST_NODE_TYPES.ClassDeclaration:
379
388
  case AST_NODE_TYPES.ClassExpression: return {
380
389
  kind: "class",
381
390
  node
@@ -398,22 +407,24 @@ function resolveObjectType(context, node) {
398
407
  };
399
408
  return null;
400
409
  case AST_NODE_TYPES.Identifier: {
401
- if ((context.sourceCode.getScope(node).set.get(node.name)?.defs.at(-1))?.type === DefinitionType.Parameter) return null;
402
- const initNode = resolve(context, node, {
410
+ const initNode = options?.boundary != null ? resolve(context, node, {
411
+ at: -1,
412
+ boundary: options.boundary
413
+ }) : resolve(context, node, {
403
414
  at: -1,
404
415
  localOnly: true
405
416
  });
406
417
  if (initNode == null) return null;
407
- return resolveObjectType(context, initNode);
418
+ return resolveObjectType(context, initNode, options);
408
419
  }
409
- case AST_NODE_TYPES.MemberExpression: return resolveObjectType(context, node.object);
420
+ case AST_NODE_TYPES.MemberExpression: return resolveObjectType(context, node.object, options);
410
421
  case AST_NODE_TYPES.AssignmentExpression:
411
- case AST_NODE_TYPES.AssignmentPattern: return resolveObjectType(context, node.right);
412
- case AST_NODE_TYPES.LogicalExpression: return resolveObjectType(context, node.left) ?? resolveObjectType(context, node.right);
413
- case AST_NODE_TYPES.ConditionalExpression: return resolveObjectType(context, node.consequent) ?? resolveObjectType(context, node.alternate);
422
+ case AST_NODE_TYPES.AssignmentPattern: return resolveObjectType(context, node.right, options);
423
+ case AST_NODE_TYPES.LogicalExpression: return resolveObjectType(context, node.left, options) ?? resolveObjectType(context, node.right, options);
424
+ case AST_NODE_TYPES.ConditionalExpression: return resolveObjectType(context, node.consequent, options) ?? resolveObjectType(context, node.alternate, options);
414
425
  case AST_NODE_TYPES.SequenceExpression:
415
426
  if (node.expressions.length === 0) return null;
416
- return resolveObjectType(context, node.expressions.at(-1) ?? null);
427
+ return resolveObjectType(context, node.expressions.at(-1) ?? null, options);
417
428
  case AST_NODE_TYPES.CallExpression: {
418
429
  const callee = Extract.unwrap(node.callee);
419
430
  switch (true) {
@@ -457,7 +468,7 @@ function resolveObjectType(context, node) {
457
468
  }
458
469
  default:
459
470
  if (!hasProperty(node, "expression") || typeof node.expression !== "object") return null;
460
- return resolveObjectType(context, node.expression);
471
+ return resolveObjectType(context, node.expression, options);
461
472
  }
462
473
  }
463
474
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eslint-react/var",
3
- "version": "5.28.1",
3
+ "version": "5.28.3",
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": {
@@ -29,11 +29,11 @@
29
29
  "dist"
30
30
  ],
31
31
  "dependencies": {
32
- "@eslint-react/ast": "5.28.1",
33
- "@eslint-react/eslint": "5.28.1",
34
- "@typescript-eslint/scope-manager": "^8.71.2",
35
- "@typescript-eslint/types": "^8.71.2",
36
- "@typescript-eslint/utils": "^8.71.2",
32
+ "@eslint-react/ast": "5.28.3",
33
+ "@eslint-react/eslint": "5.28.3",
34
+ "@typescript-eslint/scope-manager": "^8.72.0",
35
+ "@typescript-eslint/types": "^8.72.0",
36
+ "@typescript-eslint/utils": "^8.72.0",
37
37
  "ts-pattern": "^5.9.2"
38
38
  },
39
39
  "devDependencies": {
@@ -42,7 +42,7 @@
42
42
  "@local/testkit": "0.0.0",
43
43
  "@tsconfig/node20": "^20.1.10",
44
44
  "@types/node": "^20.19.43",
45
- "@typescript-eslint/typescript-estree": "^8.71.2",
45
+ "@typescript-eslint/typescript-estree": "^8.72.0",
46
46
  "eslint": "^10.13.0",
47
47
  "tsdown": "^0.23.1",
48
48
  "typescript": "6.0.3",