@eslint-react/var 5.20.5 → 5.20.6

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
@@ -36,6 +36,8 @@ export interface ImportEntry {
36
36
  * time, and results preserve source order.
37
37
  */
38
38
  export interface ImportLookup {
39
+ /** Return every import entry, in source order. */
40
+ all(): readonly ImportEntry[];
39
41
  /**
40
42
  * Look up the import entry a local name is bound to.
41
43
  *
@@ -50,8 +52,6 @@ export interface ImportLookup {
50
52
  * @returns The matching entries in source order, possibly empty.
51
53
  */
52
54
  bindingsOf(name: string): readonly ImportEntry[];
53
- /** Return every import entry, in source order. */
54
- all(): readonly ImportEntry[];
55
55
  /**
56
56
  * Check whether a local name is bound to a specific imported export.
57
57
  *
@@ -69,6 +69,11 @@ export interface ImportLookup {
69
69
  }
70
70
  /** Options for {@link createImportLookup}. */
71
71
  export interface ImportLookupOptions {
72
+ /**
73
+ * Local names to pre-register as namespace bindings without an import
74
+ * statement, e.g. a `ReactDOM` global provided by the environment.
75
+ */
76
+ builtinNamespaces?: readonly string[];
72
77
  /**
73
78
  * The base import source to track, e.g. `"react-dom"`.
74
79
  *
@@ -76,11 +81,6 @@ export interface ImportLookupOptions {
76
81
  * matches a source of `"react-dom"`.
77
82
  */
78
83
  source: string;
79
- /**
80
- * Local names to pre-register as namespace bindings without an import
81
- * statement, e.g. a `ReactDOM` global provided by the environment.
82
- */
83
- builtinNamespaces?: readonly string[];
84
84
  }
85
85
  /**
86
86
  * Create a lookup of local import bindings from a source by scanning the
@@ -171,17 +171,17 @@ export declare function isValueEqual(context: RuleContext, a: TSESTree.Node, b:
171
171
  *
172
172
  * | Definition type | `def.node` | Returns |
173
173
  * |--------------------------|----------------------------------------------|------------------------------------|
174
- * | `Variable` | `VariableDeclarator` | `def.node.init` (or `null`) |
175
- * | `FunctionName` | `FunctionDeclaration` / `FunctionExpression` | `def.node` |
174
+ * | `CatchClause` | `CatchClause` | `null` |
176
175
  * | `ClassName` | `ClassDeclaration` / `ClassExpression` | `def.node` |
177
- * | `Parameter` | containing function node | `def.node` (if a real function) |
178
- * | `TSEnumName` | `TSEnumDeclaration` | `def.node` |
179
- * | `TSEnumMember` | `TSEnumMember` | `def.node.initializer` (or `null`) |
176
+ * | `FunctionName` | `FunctionDeclaration` / `FunctionExpression` | `def.node` |
177
+ * | `ImplicitGlobalVariable` | any node | `null` |
180
178
  * | `ImportBinding` | import specifier | `null` |
181
- * | `CatchClause` | `CatchClause` | `null` |
179
+ * | `Parameter` | containing function node | `null` (the value is supplied by the caller) |
180
+ * | `TSEnumMember` | `TSEnumMember` | `def.node.initializer` (or `null`) |
181
+ * | `TSEnumName` | `TSEnumDeclaration` | `def.node` |
182
182
  * | `TSModuleName` | `TSModuleDeclaration` | `null` |
183
183
  * | `Type` | type alias node | `null` |
184
- * | `ImplicitGlobalVariable` | any node | `null` |
184
+ * | `Variable` | `VariableDeclarator` | `def.node.init` for a plain identifier binding; `null` for destructured bindings or missing init |
185
185
  *
186
186
  * @param context The ESLint rule context.
187
187
  * @param node The identifier to resolve.
@@ -192,6 +192,9 @@ export declare function isValueEqual(context: RuleContext, a: TSESTree.Node, b:
192
192
  * scope chain upward via `findVariable` so that references to outer-scope bindings are resolved
193
193
  * correctly.
194
194
  * @returns The resolved node, or `null` if the identifier cannot be resolved to a value node.
195
+ *
196
+ * For origin/pedigree tracking that maps destructured bindings to the declarator's
197
+ * initializer, see {@link resolveOrigin}.
195
198
  */
196
199
  export declare function resolve(context: RuleContext, node: TSESTree.Identifier, options?: Partial<{
197
200
  at: number;
@@ -255,4 +258,51 @@ export type ObjectType = {
255
258
  * @returns The object type of the node, or `null` when it cannot be resolved.
256
259
  */
257
260
  export declare function resolveObjectType(context: RuleContext, node: TSESTree.Node | null): ObjectType | null;
261
+ //#endregion
262
+ //#region src/resolve-origin.d.ts
263
+ /**
264
+ * Resolve an identifier to the AST node its value **originates from**,
265
+ * suitable for origin/pedigree tracking in ESLint rule analysis.
266
+ *
267
+ * The resolution follows these rules per definition type:
268
+ *
269
+ * | Definition type | `def.node` | Returns |
270
+ * |--------------------------|----------------------------------------------|------------------------------------|
271
+ * | `CatchClause` | `CatchClause` | `null` |
272
+ * | `ClassName` | `ClassDeclaration` / `ClassExpression` | `def.node` |
273
+ * | `FunctionName` | `FunctionDeclaration` / `FunctionExpression` | `def.node` |
274
+ * | `ImplicitGlobalVariable` | any node | `null` |
275
+ * | `ImportBinding` | import specifier | `def.node` (the import specifier) |
276
+ * | `Parameter` | containing function node | `def.node` (if a real function) |
277
+ * | `TSEnumMember` | `TSEnumMember` | `def.node.initializer` (or `null`) |
278
+ * | `TSEnumName` | `TSEnumDeclaration` | `def.node` |
279
+ * | `TSModuleName` | `TSModuleDeclaration` | `null` |
280
+ * | `Type` | type alias node | `null` |
281
+ * | `Variable` | `VariableDeclarator` | `def.node.init` (or `null`), including for destructured bindings |
282
+ *
283
+ * Unlike {@link resolve}, a binding declared through a destructuring
284
+ * pattern (e.g. `setState` in `const [state, setState] = useState()`) resolves to the
285
+ * declarator's initializer (the `useState()` call), i.e. the source expression the
286
+ * binding derives from rather than the binding's own value; and a parameter resolves
287
+ * to the containing function node (the binding's declaration site) instead of `null`;
288
+ * and an import binding resolves to its import specifier (whose parent
289
+ * `ImportDeclaration` carries the module source) instead of `null`.
290
+ *
291
+ * Use this for origin/pedigree tracking ("what produced this value?"); use
292
+ * {@link resolve} when the precise value of the binding is needed.
293
+ *
294
+ * @param context The ESLint rule context.
295
+ * @param node The identifier to resolve.
296
+ * @param options Optional settings:
297
+ * - `at`: Index of the definition to resolve (default: `0` for the first definition).
298
+ * - `localOnly`: If `true`, only consider variables declared in the same scope as the identifier
299
+ * (this will miss variables declared in an outer scope). When `false` (default), traverse the
300
+ * scope chain upward via `findVariable` so that references to outer-scope bindings are resolved
301
+ * correctly.
302
+ * @returns The resolved origin node, or `null` if the identifier cannot be resolved.
303
+ */
304
+ export declare function resolveOrigin(context: RuleContext, node: TSESTree.Identifier, options?: Partial<{
305
+ at: number;
306
+ localOnly: boolean;
307
+ }>): TSESTree.Node | null;
258
308
  //#endregion
package/dist/index.js CHANGED
@@ -512,7 +512,7 @@ const takeWhile = dual(2, (xs, pred) => {
512
512
  * ```
513
513
  */
514
514
  function createImportLookup(program, options) {
515
- const { source, builtinNamespaces = [] } = options;
515
+ const { builtinNamespaces = [], source } = options;
516
516
  const entries = [];
517
517
  const byLocal = /* @__PURE__ */ new Map();
518
518
  const byName = /* @__PURE__ */ new Map();
@@ -563,15 +563,15 @@ function createImportLookup(program, options) {
563
563
  }
564
564
  }
565
565
  return {
566
+ all() {
567
+ return entries;
568
+ },
566
569
  binding(local) {
567
570
  return byLocal.get(local);
568
571
  },
569
572
  bindingsOf(name) {
570
573
  return byName.get(name) ?? [];
571
574
  },
572
- all() {
573
- return entries;
574
- },
575
575
  has(local, name) {
576
576
  return byLocal.get(local)?.name === name;
577
577
  },
@@ -602,26 +602,37 @@ function getRequireExpressionArguments(node) {
602
602
  }
603
603
 
604
604
  //#endregion
605
- //#region src/resolve.ts
605
+ //#region src/resolve-origin.ts
606
606
  /**
607
- * Resolve an identifier to the AST node that represents its value,
608
- * suitable for use in ESLint rule analysis.
607
+ * Resolve an identifier to the AST node its value **originates from**,
608
+ * suitable for origin/pedigree tracking in ESLint rule analysis.
609
609
  *
610
610
  * The resolution follows these rules per definition type:
611
611
  *
612
612
  * | Definition type | `def.node` | Returns |
613
613
  * |--------------------------|----------------------------------------------|------------------------------------|
614
- * | `Variable` | `VariableDeclarator` | `def.node.init` (or `null`) |
615
- * | `FunctionName` | `FunctionDeclaration` / `FunctionExpression` | `def.node` |
614
+ * | `CatchClause` | `CatchClause` | `null` |
616
615
  * | `ClassName` | `ClassDeclaration` / `ClassExpression` | `def.node` |
616
+ * | `FunctionName` | `FunctionDeclaration` / `FunctionExpression` | `def.node` |
617
+ * | `ImplicitGlobalVariable` | any node | `null` |
618
+ * | `ImportBinding` | import specifier | `def.node` (the import specifier) |
617
619
  * | `Parameter` | containing function node | `def.node` (if a real function) |
618
- * | `TSEnumName` | `TSEnumDeclaration` | `def.node` |
619
620
  * | `TSEnumMember` | `TSEnumMember` | `def.node.initializer` (or `null`) |
620
- * | `ImportBinding` | import specifier | `null` |
621
- * | `CatchClause` | `CatchClause` | `null` |
621
+ * | `TSEnumName` | `TSEnumDeclaration` | `def.node` |
622
622
  * | `TSModuleName` | `TSModuleDeclaration` | `null` |
623
623
  * | `Type` | type alias node | `null` |
624
- * | `ImplicitGlobalVariable` | any node | `null` |
624
+ * | `Variable` | `VariableDeclarator` | `def.node.init` (or `null`), including for destructured bindings |
625
+ *
626
+ * Unlike {@link resolve}, a binding declared through a destructuring
627
+ * pattern (e.g. `setState` in `const [state, setState] = useState()`) resolves to the
628
+ * declarator's initializer (the `useState()` call), i.e. the source expression the
629
+ * binding derives from rather than the binding's own value; and a parameter resolves
630
+ * to the containing function node (the binding's declaration site) instead of `null`;
631
+ * and an import binding resolves to its import specifier (whose parent
632
+ * `ImportDeclaration` carries the module source) instead of `null`.
633
+ *
634
+ * Use this for origin/pedigree tracking ("what produced this value?"); use
635
+ * {@link resolve} when the precise value of the binding is needed.
625
636
  *
626
637
  * @param context The ESLint rule context.
627
638
  * @param node The identifier to resolve.
@@ -631,9 +642,9 @@ function getRequireExpressionArguments(node) {
631
642
  * (this will miss variables declared in an outer scope). When `false` (default), traverse the
632
643
  * scope chain upward via `findVariable` so that references to outer-scope bindings are resolved
633
644
  * correctly.
634
- * @returns The resolved node, or `null` if the identifier cannot be resolved to a value node.
645
+ * @returns The resolved origin node, or `null` if the identifier cannot be resolved.
635
646
  */
636
- function resolve(context, node, options) {
647
+ function resolveOrigin(context, node, options) {
637
648
  const { at = 0, localOnly = false } = options ?? {};
638
649
  const scope = context.sourceCode.getScope(node);
639
650
  const variable = localOnly ? scope.set.get(node.name) : findVariable(scope, node);
@@ -652,7 +663,7 @@ function resolve(context, node, options) {
652
663
  case DefinitionType.Parameter: return Check.isFunction(def.node) ? def.node : null;
653
664
  case DefinitionType.TSEnumName: return def.node;
654
665
  case DefinitionType.TSEnumMember: return def.node.initializer ?? null;
655
- case DefinitionType.ImportBinding: return null;
666
+ case DefinitionType.ImportBinding: return def.node;
656
667
  case DefinitionType.CatchClause: return null;
657
668
  case DefinitionType.TSModuleName: return null;
658
669
  case DefinitionType.Type: return null;
@@ -685,8 +696,8 @@ function isValueEqual(context, a, b) {
685
696
  case a.type === AST_NODE_TYPES.Literal && b.type === AST_NODE_TYPES.Literal: return a.value === b.value;
686
697
  case a.type === AST_NODE_TYPES.TemplateElement && b.type === AST_NODE_TYPES.TemplateElement: return a.value.cooked === b.value.cooked;
687
698
  case Check.isIdentifier(a) && Check.isIdentifier(b): {
688
- const aDefNode = resolve(context, a);
689
- const bDefNode = resolve(context, b);
699
+ const aDefNode = resolveOrigin(context, a);
700
+ const bDefNode = resolveOrigin(context, b);
690
701
  const aDefNodeParent = aDefNode?.parent;
691
702
  const bDefNodeParent = bDefNode?.parent;
692
703
  const aVar = findVariable(aScope, a);
@@ -805,6 +816,70 @@ function isInitializedFromReactNative(name, initialScope, importSource = "react-
805
816
  ].includes(name.toLowerCase()) || Boolean(resolveImportSource(name, initialScope)?.startsWith(importSource));
806
817
  }
807
818
 
819
+ //#endregion
820
+ //#region src/resolve.ts
821
+ /**
822
+ * Resolve an identifier to the AST node that represents its value,
823
+ * suitable for use in ESLint rule analysis.
824
+ *
825
+ * The resolution follows these rules per definition type:
826
+ *
827
+ * | Definition type | `def.node` | Returns |
828
+ * |--------------------------|----------------------------------------------|------------------------------------|
829
+ * | `CatchClause` | `CatchClause` | `null` |
830
+ * | `ClassName` | `ClassDeclaration` / `ClassExpression` | `def.node` |
831
+ * | `FunctionName` | `FunctionDeclaration` / `FunctionExpression` | `def.node` |
832
+ * | `ImplicitGlobalVariable` | any node | `null` |
833
+ * | `ImportBinding` | import specifier | `null` |
834
+ * | `Parameter` | containing function node | `null` (the value is supplied by the caller) |
835
+ * | `TSEnumMember` | `TSEnumMember` | `def.node.initializer` (or `null`) |
836
+ * | `TSEnumName` | `TSEnumDeclaration` | `def.node` |
837
+ * | `TSModuleName` | `TSModuleDeclaration` | `null` |
838
+ * | `Type` | type alias node | `null` |
839
+ * | `Variable` | `VariableDeclarator` | `def.node.init` for a plain identifier binding; `null` for destructured bindings or missing init |
840
+ *
841
+ * @param context The ESLint rule context.
842
+ * @param node The identifier to resolve.
843
+ * @param options Optional settings:
844
+ * - `at`: Index of the definition to resolve (default: `0` for the first definition).
845
+ * - `localOnly`: If `true`, only consider variables declared in the same scope as the identifier
846
+ * (this will miss variables declared in an outer scope). When `false` (default), traverse the
847
+ * scope chain upward via `findVariable` so that references to outer-scope bindings are resolved
848
+ * correctly.
849
+ * @returns The resolved node, or `null` if the identifier cannot be resolved to a value node.
850
+ *
851
+ * For origin/pedigree tracking that maps destructured bindings to the declarator's
852
+ * initializer, see {@link resolveOrigin}.
853
+ */
854
+ function resolve(context, node, options) {
855
+ const { at = 0, localOnly = false } = options ?? {};
856
+ const scope = context.sourceCode.getScope(node);
857
+ const variable = localOnly ? scope.set.get(node.name) : findVariable(scope, node);
858
+ if (variable == null) return null;
859
+ const def = variable.defs.at(at);
860
+ if (def == null) return null;
861
+ switch (def.type) {
862
+ case DefinitionType.FunctionName: return def.node;
863
+ case DefinitionType.ClassName: return def.node;
864
+ case DefinitionType.Variable: {
865
+ const { id, init } = def.node;
866
+ if (id !== def.name) return null;
867
+ if (init == null) return null;
868
+ if ("declarations" in init) return null;
869
+ return init;
870
+ }
871
+ case DefinitionType.Parameter: return null;
872
+ case DefinitionType.TSEnumName: return def.node;
873
+ case DefinitionType.TSEnumMember: return def.node.initializer ?? null;
874
+ case DefinitionType.ImportBinding: return null;
875
+ case DefinitionType.CatchClause: return null;
876
+ case DefinitionType.TSModuleName: return null;
877
+ case DefinitionType.Type: return null;
878
+ case DefinitionType.ImplicitGlobalVariable: return null;
879
+ default: return null;
880
+ }
881
+ }
882
+
808
883
  //#endregion
809
884
  //#region src/resolve-enclosing-assignment-target.ts
810
885
  /**
@@ -934,4 +1009,4 @@ function resolveObjectType(context, node) {
934
1009
  }
935
1010
 
936
1011
  //#endregion
937
- export { createImportLookup, getRequireExpressionArguments, isAssignmentTargetEqual, isInitializedFromReact, isInitializedFromReactNative, isValueEqual, resolve, resolveEnclosingAssignmentTarget, resolveImportSource, resolveObjectType };
1012
+ export { createImportLookup, getRequireExpressionArguments, isAssignmentTargetEqual, isInitializedFromReact, isInitializedFromReactNative, isValueEqual, resolve, resolveEnclosingAssignmentTarget, resolveImportSource, resolveObjectType, resolveOrigin };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eslint-react/var",
3
- "version": "5.20.5",
3
+ "version": "5.20.6",
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,8 +29,8 @@
29
29
  "dist"
30
30
  ],
31
31
  "dependencies": {
32
- "@eslint-react/ast": "5.20.5",
33
- "@eslint-react/eslint": "5.20.5",
32
+ "@eslint-react/ast": "5.20.6",
33
+ "@eslint-react/eslint": "5.20.6",
34
34
  "@typescript-eslint/scope-manager": "^8.70.0",
35
35
  "@typescript-eslint/types": "^8.70.0",
36
36
  "@typescript-eslint/utils": "^8.70.0",