@eslint-react/var 5.20.5 → 5.20.7

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
@@ -110,15 +110,6 @@ export interface ImportLookupOptions {
110
110
  */
111
111
  export declare function createImportLookup(program: TSESTree.Program, options: ImportLookupOptions): ImportLookup;
112
112
  //#endregion
113
- //#region src/get-require-expression-arguments.d.ts
114
- /**
115
- * Get the arguments of a require expression.
116
- * @param node The node to check.
117
- * @returns The require expression arguments, or `null` when the node is not a require expression.
118
- * @internal
119
- */
120
- export declare function getRequireExpressionArguments(node: TSESTree.Node): TSESTree.CallExpressionArgument[] | null;
121
- //#endregion
122
113
  //#region src/is-assignment-target-equal.d.ts
123
114
  /**
124
115
  * Check if two assignment targets are equal, either directly or by their values.
@@ -171,17 +162,17 @@ export declare function isValueEqual(context: RuleContext, a: TSESTree.Node, b:
171
162
  *
172
163
  * | Definition type | `def.node` | Returns |
173
164
  * |--------------------------|----------------------------------------------|------------------------------------|
174
- * | `Variable` | `VariableDeclarator` | `def.node.init` (or `null`) |
175
- * | `FunctionName` | `FunctionDeclaration` / `FunctionExpression` | `def.node` |
165
+ * | `CatchClause` | `CatchClause` | `null` |
176
166
  * | `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`) |
167
+ * | `FunctionName` | `FunctionDeclaration` / `FunctionExpression` | `def.node` |
168
+ * | `ImplicitGlobalVariable` | any node | `null` |
180
169
  * | `ImportBinding` | import specifier | `null` |
181
- * | `CatchClause` | `CatchClause` | `null` |
170
+ * | `Parameter` | containing function node | `null` (the value is supplied by the caller) |
171
+ * | `TSEnumMember` | `TSEnumMember` | `def.node.initializer` (or `null`) |
172
+ * | `TSEnumName` | `TSEnumDeclaration` | `def.node` |
182
173
  * | `TSModuleName` | `TSModuleDeclaration` | `null` |
183
174
  * | `Type` | type alias node | `null` |
184
- * | `ImplicitGlobalVariable` | any node | `null` |
175
+ * | `Variable` | `VariableDeclarator` | `def.node.init` for a plain identifier binding; `null` for destructured bindings or missing init |
185
176
  *
186
177
  * @param context The ESLint rule context.
187
178
  * @param node The identifier to resolve.
@@ -192,6 +183,9 @@ export declare function isValueEqual(context: RuleContext, a: TSESTree.Node, b:
192
183
  * scope chain upward via `findVariable` so that references to outer-scope bindings are resolved
193
184
  * correctly.
194
185
  * @returns The resolved node, or `null` if the identifier cannot be resolved to a value node.
186
+ *
187
+ * For origin/pedigree tracking that maps destructured bindings to the declarator's
188
+ * initializer, see {@link resolveOrigin}.
195
189
  */
196
190
  export declare function resolve(context: RuleContext, node: TSESTree.Identifier, options?: Partial<{
197
191
  at: number;
@@ -200,7 +194,7 @@ export declare function resolve(context: RuleContext, node: TSESTree.Identifier,
200
194
  //#endregion
201
195
  //#region src/resolve-enclosing-assignment-target.d.ts
202
196
  /** The possible assignment targets returned by {@link resolveEnclosingAssignmentTarget}. */
203
- export type AssignmentTarget = ReturnType<typeof resolveEnclosingAssignmentTarget>;
197
+ export type EnclosingAssignmentTarget = ReturnType<typeof resolveEnclosingAssignmentTarget>;
204
198
  /**
205
199
  * Resolve the enclosing assignment target (variable, property, etc.) of the node.
206
200
  * @param node The starting node for the upward search.
@@ -255,4 +249,51 @@ export type ObjectType = {
255
249
  * @returns The object type of the node, or `null` when it cannot be resolved.
256
250
  */
257
251
  export declare function resolveObjectType(context: RuleContext, node: TSESTree.Node | null): ObjectType | null;
252
+ //#endregion
253
+ //#region src/resolve-origin.d.ts
254
+ /**
255
+ * Resolve an identifier to the AST node its value **originates from**,
256
+ * suitable for origin/pedigree tracking in ESLint rule analysis.
257
+ *
258
+ * The resolution follows these rules per definition type:
259
+ *
260
+ * | Definition type | `def.node` | Returns |
261
+ * |--------------------------|----------------------------------------------|------------------------------------|
262
+ * | `CatchClause` | `CatchClause` | `null` |
263
+ * | `ClassName` | `ClassDeclaration` / `ClassExpression` | `def.node` |
264
+ * | `FunctionName` | `FunctionDeclaration` / `FunctionExpression` | `def.node` |
265
+ * | `ImplicitGlobalVariable` | any node | `null` |
266
+ * | `ImportBinding` | import specifier | `def.node` (the import specifier) |
267
+ * | `Parameter` | containing function node | `def.node` (if a real function) |
268
+ * | `TSEnumMember` | `TSEnumMember` | `def.node.initializer` (or `null`) |
269
+ * | `TSEnumName` | `TSEnumDeclaration` | `def.node` |
270
+ * | `TSModuleName` | `TSModuleDeclaration` | `null` |
271
+ * | `Type` | type alias node | `null` |
272
+ * | `Variable` | `VariableDeclarator` | `def.node.init` (or `null`), including for destructured bindings |
273
+ *
274
+ * Unlike {@link resolve}, a binding declared through a destructuring
275
+ * pattern (e.g. `setState` in `const [state, setState] = useState()`) resolves to the
276
+ * declarator's initializer (the `useState()` call), i.e. the source expression the
277
+ * binding derives from rather than the binding's own value; and a parameter resolves
278
+ * to the containing function node (the binding's declaration site) instead of `null`;
279
+ * and an import binding resolves to its import specifier (whose parent
280
+ * `ImportDeclaration` carries the module source) instead of `null`.
281
+ *
282
+ * Use this for origin/pedigree tracking ("what produced this value?"); use
283
+ * {@link resolve} when the precise value of the binding is needed.
284
+ *
285
+ * @param context The ESLint rule context.
286
+ * @param node The identifier to resolve.
287
+ * @param options Optional settings:
288
+ * - `at`: Index of the definition to resolve (default: `0` for the first definition).
289
+ * - `localOnly`: If `true`, only consider variables declared in the same scope as the identifier
290
+ * (this will miss variables declared in an outer scope). When `false` (default), traverse the
291
+ * scope chain upward via `findVariable` so that references to outer-scope bindings are resolved
292
+ * correctly.
293
+ * @returns The resolved origin node, or `null` if the identifier cannot be resolved.
294
+ */
295
+ export declare function resolveOrigin(context: RuleContext, node: TSESTree.Identifier, options?: Partial<{
296
+ at: number;
297
+ localOnly: boolean;
298
+ }>): TSESTree.Node | null;
258
299
  //#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
  },
@@ -583,45 +583,37 @@ function createImportLookup(program, options) {
583
583
  }
584
584
 
585
585
  //#endregion
586
- //#region src/get-require-expression-arguments.ts
586
+ //#region src/resolve-origin.ts
587
587
  /**
588
- * Get the arguments of a require expression.
589
- * @param node The node to check.
590
- * @returns The require expression arguments, or `null` when the node is not a require expression.
591
- * @internal
592
- */
593
- function getRequireExpressionArguments(node) {
594
- const unwrapped = Extract.unwrap(node);
595
- if (unwrapped.type === AST_NODE_TYPES.CallExpression) {
596
- const callee = Extract.unwrap(unwrapped.callee);
597
- if (Check.isIdentifier(callee, "require")) return unwrapped.arguments;
598
- return null;
599
- }
600
- if (unwrapped.type === AST_NODE_TYPES.MemberExpression) return getRequireExpressionArguments(unwrapped.object);
601
- return null;
602
- }
603
-
604
- //#endregion
605
- //#region src/resolve.ts
606
- /**
607
- * Resolve an identifier to the AST node that represents its value,
608
- * suitable for use in ESLint rule analysis.
588
+ * Resolve an identifier to the AST node its value **originates from**,
589
+ * suitable for origin/pedigree tracking in ESLint rule analysis.
609
590
  *
610
591
  * The resolution follows these rules per definition type:
611
592
  *
612
593
  * | Definition type | `def.node` | Returns |
613
594
  * |--------------------------|----------------------------------------------|------------------------------------|
614
- * | `Variable` | `VariableDeclarator` | `def.node.init` (or `null`) |
615
- * | `FunctionName` | `FunctionDeclaration` / `FunctionExpression` | `def.node` |
595
+ * | `CatchClause` | `CatchClause` | `null` |
616
596
  * | `ClassName` | `ClassDeclaration` / `ClassExpression` | `def.node` |
597
+ * | `FunctionName` | `FunctionDeclaration` / `FunctionExpression` | `def.node` |
598
+ * | `ImplicitGlobalVariable` | any node | `null` |
599
+ * | `ImportBinding` | import specifier | `def.node` (the import specifier) |
617
600
  * | `Parameter` | containing function node | `def.node` (if a real function) |
618
- * | `TSEnumName` | `TSEnumDeclaration` | `def.node` |
619
601
  * | `TSEnumMember` | `TSEnumMember` | `def.node.initializer` (or `null`) |
620
- * | `ImportBinding` | import specifier | `null` |
621
- * | `CatchClause` | `CatchClause` | `null` |
602
+ * | `TSEnumName` | `TSEnumDeclaration` | `def.node` |
622
603
  * | `TSModuleName` | `TSModuleDeclaration` | `null` |
623
604
  * | `Type` | type alias node | `null` |
624
- * | `ImplicitGlobalVariable` | any node | `null` |
605
+ * | `Variable` | `VariableDeclarator` | `def.node.init` (or `null`), including for destructured bindings |
606
+ *
607
+ * Unlike {@link resolve}, a binding declared through a destructuring
608
+ * pattern (e.g. `setState` in `const [state, setState] = useState()`) resolves to the
609
+ * declarator's initializer (the `useState()` call), i.e. the source expression the
610
+ * binding derives from rather than the binding's own value; and a parameter resolves
611
+ * to the containing function node (the binding's declaration site) instead of `null`;
612
+ * and an import binding resolves to its import specifier (whose parent
613
+ * `ImportDeclaration` carries the module source) instead of `null`.
614
+ *
615
+ * Use this for origin/pedigree tracking ("what produced this value?"); use
616
+ * {@link resolve} when the precise value of the binding is needed.
625
617
  *
626
618
  * @param context The ESLint rule context.
627
619
  * @param node The identifier to resolve.
@@ -631,9 +623,9 @@ function getRequireExpressionArguments(node) {
631
623
  * (this will miss variables declared in an outer scope). When `false` (default), traverse the
632
624
  * scope chain upward via `findVariable` so that references to outer-scope bindings are resolved
633
625
  * correctly.
634
- * @returns The resolved node, or `null` if the identifier cannot be resolved to a value node.
626
+ * @returns The resolved origin node, or `null` if the identifier cannot be resolved.
635
627
  */
636
- function resolve(context, node, options) {
628
+ function resolveOrigin(context, node, options) {
637
629
  const { at = 0, localOnly = false } = options ?? {};
638
630
  const scope = context.sourceCode.getScope(node);
639
631
  const variable = localOnly ? scope.set.get(node.name) : findVariable(scope, node);
@@ -652,7 +644,7 @@ function resolve(context, node, options) {
652
644
  case DefinitionType.Parameter: return Check.isFunction(def.node) ? def.node : null;
653
645
  case DefinitionType.TSEnumName: return def.node;
654
646
  case DefinitionType.TSEnumMember: return def.node.initializer ?? null;
655
- case DefinitionType.ImportBinding: return null;
647
+ case DefinitionType.ImportBinding: return def.node;
656
648
  case DefinitionType.CatchClause: return null;
657
649
  case DefinitionType.TSModuleName: return null;
658
650
  case DefinitionType.Type: return null;
@@ -685,8 +677,8 @@ function isValueEqual(context, a, b) {
685
677
  case a.type === AST_NODE_TYPES.Literal && b.type === AST_NODE_TYPES.Literal: return a.value === b.value;
686
678
  case a.type === AST_NODE_TYPES.TemplateElement && b.type === AST_NODE_TYPES.TemplateElement: return a.value.cooked === b.value.cooked;
687
679
  case Check.isIdentifier(a) && Check.isIdentifier(b): {
688
- const aDefNode = resolve(context, a);
689
- const bDefNode = resolve(context, b);
680
+ const aDefNode = resolveOrigin(context, a);
681
+ const bDefNode = resolveOrigin(context, b);
690
682
  const aDefNodeParent = aDefNode?.parent;
691
683
  const bDefNodeParent = bDefNode?.parent;
692
684
  const aVar = findVariable(aScope, a);
@@ -746,6 +738,21 @@ function isAssignmentTargetEqual(context, a, b) {
746
738
  //#endregion
747
739
  //#region src/resolve-import-source.ts
748
740
  /**
741
+ * Get the arguments of a require expression.
742
+ * @param node The node to check.
743
+ * @returns The require expression arguments, or `null` when the node is not a require expression.
744
+ */
745
+ function getRequireExpressionArguments(node) {
746
+ const unwrapped = Extract.unwrap(node);
747
+ if (unwrapped.type === AST_NODE_TYPES.CallExpression) {
748
+ const callee = Extract.unwrap(unwrapped.callee);
749
+ if (Check.isIdentifier(callee, "require")) return unwrapped.arguments;
750
+ return null;
751
+ }
752
+ if (unwrapped.type === AST_NODE_TYPES.MemberExpression) return getRequireExpressionArguments(unwrapped.object);
753
+ return null;
754
+ }
755
+ /**
749
756
  * Resolve the import source of a variable by walking its latest definition.
750
757
  * @param name The variable name.
751
758
  * @param initialScope The initial scope.
@@ -805,6 +812,70 @@ function isInitializedFromReactNative(name, initialScope, importSource = "react-
805
812
  ].includes(name.toLowerCase()) || Boolean(resolveImportSource(name, initialScope)?.startsWith(importSource));
806
813
  }
807
814
 
815
+ //#endregion
816
+ //#region src/resolve.ts
817
+ /**
818
+ * Resolve an identifier to the AST node that represents its value,
819
+ * suitable for use in ESLint rule analysis.
820
+ *
821
+ * The resolution follows these rules per definition type:
822
+ *
823
+ * | Definition type | `def.node` | Returns |
824
+ * |--------------------------|----------------------------------------------|------------------------------------|
825
+ * | `CatchClause` | `CatchClause` | `null` |
826
+ * | `ClassName` | `ClassDeclaration` / `ClassExpression` | `def.node` |
827
+ * | `FunctionName` | `FunctionDeclaration` / `FunctionExpression` | `def.node` |
828
+ * | `ImplicitGlobalVariable` | any node | `null` |
829
+ * | `ImportBinding` | import specifier | `null` |
830
+ * | `Parameter` | containing function node | `null` (the value is supplied by the caller) |
831
+ * | `TSEnumMember` | `TSEnumMember` | `def.node.initializer` (or `null`) |
832
+ * | `TSEnumName` | `TSEnumDeclaration` | `def.node` |
833
+ * | `TSModuleName` | `TSModuleDeclaration` | `null` |
834
+ * | `Type` | type alias node | `null` |
835
+ * | `Variable` | `VariableDeclarator` | `def.node.init` for a plain identifier binding; `null` for destructured bindings or missing init |
836
+ *
837
+ * @param context The ESLint rule context.
838
+ * @param node The identifier to resolve.
839
+ * @param options Optional settings:
840
+ * - `at`: Index of the definition to resolve (default: `0` for the first definition).
841
+ * - `localOnly`: If `true`, only consider variables declared in the same scope as the identifier
842
+ * (this will miss variables declared in an outer scope). When `false` (default), traverse the
843
+ * scope chain upward via `findVariable` so that references to outer-scope bindings are resolved
844
+ * correctly.
845
+ * @returns The resolved node, or `null` if the identifier cannot be resolved to a value node.
846
+ *
847
+ * For origin/pedigree tracking that maps destructured bindings to the declarator's
848
+ * initializer, see {@link resolveOrigin}.
849
+ */
850
+ function resolve(context, node, options) {
851
+ const { at = 0, localOnly = false } = options ?? {};
852
+ const scope = context.sourceCode.getScope(node);
853
+ const variable = localOnly ? scope.set.get(node.name) : findVariable(scope, node);
854
+ if (variable == null) return null;
855
+ const def = variable.defs.at(at);
856
+ if (def == null) return null;
857
+ switch (def.type) {
858
+ case DefinitionType.FunctionName: return def.node;
859
+ case DefinitionType.ClassName: return def.node;
860
+ case DefinitionType.Variable: {
861
+ const { id, init } = def.node;
862
+ if (id !== def.name) return null;
863
+ if (init == null) return null;
864
+ if ("declarations" in init) return null;
865
+ return init;
866
+ }
867
+ case DefinitionType.Parameter: return null;
868
+ case DefinitionType.TSEnumName: return def.node;
869
+ case DefinitionType.TSEnumMember: return def.node.initializer ?? null;
870
+ case DefinitionType.ImportBinding: return null;
871
+ case DefinitionType.CatchClause: return null;
872
+ case DefinitionType.TSModuleName: return null;
873
+ case DefinitionType.Type: return null;
874
+ case DefinitionType.ImplicitGlobalVariable: return null;
875
+ default: return null;
876
+ }
877
+ }
878
+
808
879
  //#endregion
809
880
  //#region src/resolve-enclosing-assignment-target.ts
810
881
  /**
@@ -934,4 +1005,4 @@ function resolveObjectType(context, node) {
934
1005
  }
935
1006
 
936
1007
  //#endregion
937
- export { createImportLookup, getRequireExpressionArguments, isAssignmentTargetEqual, isInitializedFromReact, isInitializedFromReactNative, isValueEqual, resolve, resolveEnclosingAssignmentTarget, resolveImportSource, resolveObjectType };
1008
+ export { createImportLookup, 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.7",
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.7",
33
+ "@eslint-react/eslint": "5.20.7",
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",