@eslint-react/core 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,5 +1,4 @@
1
1
  import * as ast from "@eslint-react/ast";
2
- import { unit } from "@eslint-react/eff";
3
2
  import { TSESTree } from "@typescript-eslint/types";
4
3
  import { RegExpLike, RuleContext } from "@eslint-react/shared";
5
4
  import { ESLintUtils, TSESTree as TSESTree$1 } from "@typescript-eslint/utils";
@@ -11,9 +10,9 @@ import * as typescript from "typescript";
11
10
  * Find the import source of a variable
12
11
  * @param name The variable name
13
12
  * @param initialScope The initial scope to search
14
- * @returns The import source or undefined if not found
13
+ * @returns The import source or null if not found
15
14
  */
16
- declare function findImportSource(name: string, initialScope: Scope): string | undefined;
15
+ declare function findImportSource(name: string, initialScope: Scope): string | null;
17
16
  //#endregion
18
17
  //#region src/api/is-from-react.d.ts
19
18
  /**
@@ -38,8 +37,8 @@ declare function isInitializedFromReactNative(name: string, initialScope: Scope,
38
37
  //#region src/api/is-react-api.d.ts
39
38
  declare namespace isReactAPI {
40
39
  type ReturnType = {
41
- (context: RuleContext, node: unit | null | TSESTree.Node): node is TSESTree.Identifier | TSESTree.MemberExpression;
42
- (context: RuleContext): (node: unit | null | TSESTree.Node) => node is TSESTree.MemberExpression | TSESTree.Identifier;
40
+ (context: RuleContext, node: null | TSESTree.Node): node is TSESTree.Identifier | TSESTree.MemberExpression;
41
+ (context: RuleContext): (node: null | TSESTree.Node) => node is TSESTree.MemberExpression | TSESTree.Identifier;
43
42
  };
44
43
  }
45
44
  /**
@@ -50,8 +49,8 @@ declare namespace isReactAPI {
50
49
  declare function isReactAPI(api: string): isReactAPI.ReturnType;
51
50
  declare namespace isReactAPICall {
52
51
  type ReturnType = {
53
- (context: RuleContext, node: unit | null | TSESTree.Node): node is TSESTree.CallExpression;
54
- (context: RuleContext): (node: unit | null | TSESTree.Node) => node is TSESTree.CallExpression;
52
+ (context: RuleContext, node: null | TSESTree.Node): node is TSESTree.CallExpression;
53
+ (context: RuleContext): (node: null | TSESTree.Node) => node is TSESTree.CallExpression;
55
54
  };
56
55
  }
57
56
  /**
@@ -134,13 +133,13 @@ declare function isComponentDefinition(context: RuleContext, node: ast.TSESTreeF
134
133
  */
135
134
  interface SemanticNode {
136
135
  /** The identifier of the node */
137
- id: unit | TSESTree.Node;
136
+ id: null | TSESTree.Node;
138
137
  /** The unique key of the node */
139
138
  key: string;
140
139
  /** The kind of the node */
141
140
  kind: string;
142
141
  /** The name of the node */
143
- name: unit | string;
142
+ name: null | string;
144
143
  /** The flag of the node */
145
144
  flag: bigint;
146
145
  /** The hint of the node */
@@ -160,9 +159,9 @@ interface SemanticFunc extends SemanticNode {
160
159
  /** The AST node of the function */
161
160
  node: ast.TSESTreeFunction;
162
161
  /** The name of the function */
163
- name: string | unit;
162
+ name: string | null;
164
163
  /** The return type annotation of the function */
165
- type: TSESTree.TSTypeAnnotation | unit;
164
+ type: TSESTree.TSTypeAnnotation | null;
166
165
  /** The body of the function */
167
166
  body: TSESTree.BlockStatement | TSESTree.Expression;
168
167
  /** The directives of the function (e.g., "use strict", "use client", "use server", etc.) */
@@ -170,7 +169,7 @@ interface SemanticFunc extends SemanticNode {
170
169
  /** The parameters of the function */
171
170
  parameters: TSESTree.Parameter[];
172
171
  /** The type parameters of the function */
173
- typeParameters: TSESTree.TSTypeParameterDeclaration | unit;
172
+ typeParameters: TSESTree.TSTypeParameterDeclaration | null;
174
173
  }
175
174
  //#endregion
176
175
  //#region src/component/component-semantic-node.d.ts
@@ -205,7 +204,7 @@ interface FunctionComponentSemanticNode extends SemanticNode {
205
204
  /**
206
205
  * The initialization path of the function
207
206
  */
208
- initPath: unit | ast.FunctionInitPath;
207
+ initPath: null | ast.FunctionInitPath;
209
208
  /**
210
209
  * Indicates if the component is inside an export default declaration
211
210
  */
@@ -221,7 +220,7 @@ interface FunctionComponentSemanticNode extends SemanticNode {
221
220
  /**
222
221
  * The display name of the component
223
222
  */
224
- displayName: unit | TSESTree.Expression;
223
+ displayName: null | TSESTree.Expression;
225
224
  /**
226
225
  * The directives used in the function (e.g., "use strict", "use client", etc.)
227
226
  */
@@ -234,7 +233,7 @@ interface ClassComponentSemanticNode extends SemanticNode {
234
233
  /**
235
234
  * The identifier of the component
236
235
  */
237
- id: unit | TSESTree.BindingName;
236
+ id: null | TSESTree.BindingName;
238
237
  /**
239
238
  * The kind of component
240
239
  */
@@ -258,7 +257,7 @@ interface ClassComponentSemanticNode extends SemanticNode {
258
257
  /**
259
258
  * The display name of the component
260
259
  */
261
- displayName: unit | TSESTree.Expression;
260
+ displayName: null | TSESTree.Expression;
262
261
  }
263
262
  /**
264
263
  * Represents a React Component
@@ -278,7 +277,7 @@ declare namespace useComponentCollector {
278
277
  ctx: {
279
278
  getAllComponents: (node: TSESTree.Program) => FunctionComponentSemanticNode[];
280
279
  getCurrentEntries: () => FunctionEntry$1[];
281
- getCurrentEntry: () => FunctionEntry$1 | unit;
280
+ getCurrentEntry: () => FunctionEntry$1 | null;
282
281
  };
283
282
  visitor: ESLintUtils.RuleListener;
284
283
  };
@@ -413,7 +412,7 @@ declare function getComponentFlagFromInitPath(initPath: FunctionComponentSemanti
413
412
  * Get function component identifier from `const Component = memo(() => {});`
414
413
  * @param context The rule context
415
414
  * @param node The function node to analyze
416
- * @returns The function identifier or `unit` if not found
415
+ * @returns The function identifier or `null` if not found
417
416
  */
418
417
  declare function getFunctionComponentId(context: RuleContext, node: ast.TSESTreeFunction): ast.FunctionID;
419
418
  //#endregion
@@ -502,12 +501,12 @@ type FunctionSemanticNode = ClientFunctionSemanticNode | ServerFunctionSemanticN
502
501
  * Determine if a node is the setup function passed to a useEffect-like hook
503
502
  * @param node The AST node to check
504
503
  */
505
- declare function isUseEffectSetupCallback(node: TSESTree.Node | unit): boolean;
504
+ declare function isUseEffectSetupCallback(node: TSESTree.Node | null): boolean;
506
505
  /**
507
506
  * Determine if a node is the cleanup function returned by a useEffect-like hook's setup function
508
507
  * @param node The AST node to check
509
508
  */
510
- declare function isUseEffectCleanupCallback(node: TSESTree.Node | unit): boolean;
509
+ declare function isUseEffectCleanupCallback(node: TSESTree.Node | null): boolean;
511
510
  //#endregion
512
511
  //#region src/hook/hook-semantic-node.d.ts
513
512
  /**
@@ -537,7 +536,7 @@ declare namespace useHookCollector {
537
536
  ctx: {
538
537
  getAllHooks(node: TSESTree$1.Program): HookSemanticNode[];
539
538
  getCurrentEntries(): FunctionEntry[];
540
- getCurrentEntry(): FunctionEntry | unit;
539
+ getCurrentEntry(): FunctionEntry | null;
541
540
  };
542
541
  visitor: ESLintUtils.RuleListener;
543
542
  };
@@ -563,53 +562,53 @@ declare function isHookId(id: TSESTree.Node): id is TSESTree.Identifier | TSESTr
563
562
  * @param node The function node to check
564
563
  * @returns True if the function is a React Hook, false otherwise
565
564
  */
566
- declare function isHook(node: ast.TSESTreeFunction | unit): boolean;
565
+ declare function isHook(node: ast.TSESTreeFunction | null): boolean;
567
566
  /**
568
567
  * Check if the given node is a React Hook call by its name.
569
568
  * @param node The node to check.
570
569
  * @returns `true` if the node is a React Hook call, `false` otherwise.
571
570
  */
572
- declare function isHookCall(node: TSESTree.Node | unit): node is TSESTree.CallExpression;
571
+ declare function isHookCall(node: TSESTree.Node | null): node is TSESTree.CallExpression;
573
572
  /**
574
573
  * Check if a node is a call to a specific React hook.
575
574
  * Returns a function that accepts a hook name to check against.
576
575
  * @param node The AST node to check
577
576
  * @returns A function that takes a hook name and returns boolean
578
577
  */
579
- declare function isHookCallWithName(node: TSESTree.Node | unit): (name: string) => boolean;
578
+ declare function isHookCallWithName(node: TSESTree.Node | null): (name: string) => boolean;
580
579
  /**
581
580
  * Detect useEffect calls and variations (useLayoutEffect, etc.) using a regex pattern
582
581
  * @param node The AST node to check
583
582
  * @param additionalEffectHooks Regex pattern matching custom hooks that should be treated as effect hooks
584
583
  * @returns True if the node is a useEffect-like call
585
584
  */
586
- declare function isUseEffectLikeCall(node: TSESTree.Node | unit, additionalEffectHooks?: RegExpLike): node is TSESTree.CallExpression;
585
+ declare function isUseEffectLikeCall(node: TSESTree.Node | null, additionalEffectHooks?: RegExpLike): node is TSESTree.CallExpression;
587
586
  /**
588
587
  * Detect useState calls and variations (useCustomState, etc.) using a regex pattern
589
588
  * @param node The AST node to check
590
589
  * @param additionalStateHooks Regex pattern matching custom hooks that should be treated as state hooks
591
590
  * @returns True if the node is a useState-like call
592
591
  */
593
- declare function isUseStateLikeCall(node: TSESTree.Node | unit, additionalStateHooks?: RegExpLike): node is TSESTree.CallExpression;
594
- declare const isUseCall: (node: TSESTree.Node | undefined) => boolean;
595
- declare const isUseActionStateCall: (node: TSESTree.Node | undefined) => boolean;
596
- declare const isUseCallbackCall: (node: TSESTree.Node | undefined) => boolean;
597
- declare const isUseContextCall: (node: TSESTree.Node | undefined) => boolean;
598
- declare const isUseDebugValueCall: (node: TSESTree.Node | undefined) => boolean;
599
- declare const isUseDeferredValueCall: (node: TSESTree.Node | undefined) => boolean;
600
- declare const isUseEffectCall: (node: TSESTree.Node | undefined) => boolean;
601
- declare const isUseFormStatusCall: (node: TSESTree.Node | undefined) => boolean;
602
- declare const isUseIdCall: (node: TSESTree.Node | undefined) => boolean;
603
- declare const isUseImperativeHandleCall: (node: TSESTree.Node | undefined) => boolean;
604
- declare const isUseInsertionEffectCall: (node: TSESTree.Node | undefined) => boolean;
605
- declare const isUseLayoutEffectCall: (node: TSESTree.Node | undefined) => boolean;
606
- declare const isUseMemoCall: (node: TSESTree.Node | undefined) => boolean;
607
- declare const isUseOptimisticCall: (node: TSESTree.Node | undefined) => boolean;
608
- declare const isUseReducerCall: (node: TSESTree.Node | undefined) => boolean;
609
- declare const isUseRefCall: (node: TSESTree.Node | undefined) => boolean;
610
- declare const isUseStateCall: (node: TSESTree.Node | undefined) => boolean;
611
- declare const isUseSyncExternalStoreCall: (node: TSESTree.Node | undefined) => boolean;
612
- declare const isUseTransitionCall: (node: TSESTree.Node | undefined) => boolean;
592
+ declare function isUseStateLikeCall(node: TSESTree.Node | null, additionalStateHooks?: RegExpLike): node is TSESTree.CallExpression;
593
+ declare const isUseCall: (node: TSESTree.Node | null) => boolean;
594
+ declare const isUseActionStateCall: (node: TSESTree.Node | null) => boolean;
595
+ declare const isUseCallbackCall: (node: TSESTree.Node | null) => boolean;
596
+ declare const isUseContextCall: (node: TSESTree.Node | null) => boolean;
597
+ declare const isUseDebugValueCall: (node: TSESTree.Node | null) => boolean;
598
+ declare const isUseDeferredValueCall: (node: TSESTree.Node | null) => boolean;
599
+ declare const isUseEffectCall: (node: TSESTree.Node | null) => boolean;
600
+ declare const isUseFormStatusCall: (node: TSESTree.Node | null) => boolean;
601
+ declare const isUseIdCall: (node: TSESTree.Node | null) => boolean;
602
+ declare const isUseImperativeHandleCall: (node: TSESTree.Node | null) => boolean;
603
+ declare const isUseInsertionEffectCall: (node: TSESTree.Node | null) => boolean;
604
+ declare const isUseLayoutEffectCall: (node: TSESTree.Node | null) => boolean;
605
+ declare const isUseMemoCall: (node: TSESTree.Node | null) => boolean;
606
+ declare const isUseOptimisticCall: (node: TSESTree.Node | null) => boolean;
607
+ declare const isUseReducerCall: (node: TSESTree.Node | null) => boolean;
608
+ declare const isUseRefCall: (node: TSESTree.Node | null) => boolean;
609
+ declare const isUseStateCall: (node: TSESTree.Node | null) => boolean;
610
+ declare const isUseSyncExternalStoreCall: (node: TSESTree.Node | null) => boolean;
611
+ declare const isUseTransitionCall: (node: TSESTree.Node | null) => boolean;
613
612
  //#endregion
614
613
  //#region src/hook/hook-name.d.ts
615
614
  declare const REACT_BUILTIN_HOOK_NAMES: readonly ["use", "useActionState", "useCallback", "useContext", "useDebugValue", "useDeferredValue", "useEffect", "useFormStatus", "useId", "useImperativeHandle", "useInsertionEffect", "useLayoutEffect", "useMemo", "useOptimistic", "useReducer", "useRef", "useState", "useSyncExternalStore", "useTransition"];
@@ -690,11 +689,11 @@ declare function resolveJsxAttributeValue(context: RuleContext, attribute: ast.T
690
689
  } | {
691
690
  readonly kind: "element";
692
691
  readonly node: TSESTree.JSXElement;
693
- readonly toStatic: () => undefined;
692
+ readonly toStatic: () => null;
694
693
  } | {
695
694
  readonly kind: "spreadChild";
696
695
  readonly node: TSESTree.JSXEmptyExpression | TSESTree.Expression;
697
- readonly toStatic: () => undefined;
696
+ readonly toStatic: () => null;
698
697
  } | {
699
698
  readonly kind: "spreadProps";
700
699
  readonly node: TSESTree.Expression;
@@ -763,20 +762,17 @@ declare const DEFAULT_JSX_DETECTION_HINT: bigint;
763
762
  * @param node The AST node to check
764
763
  * @returns `true` if the node is a `JSXText` or a `Literal` node
765
764
  */
766
- declare function isJsxText(node: TSESTree$1.Node | null | unit): node is TSESTree$1.JSXText | TSESTree$1.Literal;
765
+ declare function isJsxText(node: TSESTree$1.Node | null): node is TSESTree$1.JSXText | TSESTree$1.Literal;
767
766
  /**
768
767
  * Determine if a node represents JSX-like content based on heuristics
769
768
  * Supports configuration through hint flags to customize detection behavior
770
769
  *
771
- * @param code The source code with scope lookup capability
772
- * @param code.getScope The function to get the scope of a node
770
+ * @param context The rule context with scope lookup capability
773
771
  * @param node The AST node to analyze
774
772
  * @param hint The configuration flags to adjust detection behavior
775
773
  * @returns boolean Whether the node is considered JSX-like
776
774
  */
777
- declare function isJsxLike(code: {
778
- getScope: (node: TSESTree$1.Node) => Scope;
779
- }, node: TSESTree$1.Node | unit | null, hint?: JsxDetectionHint): boolean;
775
+ declare function isJsxLike(context: RuleContext, node: TSESTree$1.Node | null, hint?: JsxDetectionHint): boolean;
780
776
  //#endregion
781
777
  //#region src/jsx/jsx-element-is.d.ts
782
778
  /**
@@ -819,9 +815,9 @@ declare function getJsxElementType(context: RuleContext, node: TSESTree.JSXEleme
819
815
  * @param node The starting AST node
820
816
  * @param test Optional predicate function to test if the attribute meets criteria
821
817
  * Defaults to always returning true (matches any attribute)
822
- * @returns The first matching JSX attribute node found when traversing upwards, or undefined
818
+ * @returns The first matching JSX attribute node found when traversing upwards, or null
823
819
  */
824
- declare function findParentJsxAttribute(node: TSESTree.Node, test?: (node: TSESTree.JSXAttribute) => boolean): TSESTree.JSXAttribute | unit;
820
+ declare function findParentJsxAttribute(node: TSESTree.Node, test?: (node: TSESTree.JSXAttribute) => boolean): TSESTree.JSXAttribute | null;
825
821
  //#endregion
826
822
  //#region src/jsx/jsx-stringify.d.ts
827
823
  /**
@@ -847,9 +843,9 @@ declare function isInitializedFromRef(name: string, initialScope: Scope): boolea
847
843
  * Get the init expression of a ref variable
848
844
  * @param name The variable name
849
845
  * @param initialScope The initial scope
850
- * @returns The init expression node if the variable is derived from a ref, or undefined otherwise
846
+ * @returns The init expression node if the variable is derived from a ref, or null otherwise
851
847
  */
852
- declare function getRefInit(name: string, initialScope: Scope): TSESTree$1.Expression | unit;
848
+ declare function getRefInit(name: string, initialScope: Scope): TSESTree$1.Expression | null;
853
849
  //#endregion
854
850
  //#region src/ref/ref-name.d.ts
855
851
  /**
package/dist/index.js CHANGED
@@ -1,17 +1,17 @@
1
1
  import * as ast from "@eslint-react/ast";
2
- import { constFalse, constTrue, dual, flip, getOrElseUpdate, identity, unit } from "@eslint-react/eff";
3
- import { findVariable } from "@eslint-react/var";
2
+ import { constFalse, constTrue, dual, flip, getOrElseUpdate, identity } from "@eslint-react/eff";
4
3
  import { AST_NODE_TYPES } from "@typescript-eslint/types";
4
+ import { findVariable, getStaticValue } from "@typescript-eslint/utils/ast-utils";
5
5
  import { P, match } from "ts-pattern";
6
6
  import { IdGenerator, RE_ANNOTATION_JSX, RE_ANNOTATION_JSX_FRAG, RE_ANNOTATION_JSX_IMPORT_SOURCE, RE_ANNOTATION_JSX_RUNTIME, RE_COMPONENT_NAME, RE_COMPONENT_NAME_LOOSE } from "@eslint-react/shared";
7
- import { getStaticValue } from "@typescript-eslint/utils/ast-utils";
7
+ import { resolve } from "@eslint-react/var";
8
8
  import { AST_NODE_TYPES as AST_NODE_TYPES$1 } from "@typescript-eslint/utils";
9
9
 
10
10
  //#region src/api/find-import-source.ts
11
11
  /**
12
12
  * Get the arguments of a require expression
13
13
  * @param node The node to match
14
- * @returns The require expression arguments or undefined if the node is not a require expression
14
+ * @returns The require expression arguments or null if the node is not a require expression
15
15
  * @internal
16
16
  */
17
17
  function getRequireExpressionArguments(node) {
@@ -31,22 +31,22 @@ function getRequireExpressionArguments(node) {
31
31
  * Find the import source of a variable
32
32
  * @param name The variable name
33
33
  * @param initialScope The initial scope to search
34
- * @returns The import source or undefined if not found
34
+ * @returns The import source or null if not found
35
35
  */
36
36
  function findImportSource(name, initialScope) {
37
- const latestDef = findVariable(name, initialScope)?.defs.at(-1);
38
- if (latestDef == null) return unit;
37
+ const latestDef = findVariable(initialScope, name)?.defs.at(-1);
38
+ if (latestDef == null) return null;
39
39
  const { node, parent } = latestDef;
40
40
  if (node.type === AST_NODE_TYPES.VariableDeclarator && node.init != null) {
41
41
  const { init } = node;
42
42
  if (init.type === AST_NODE_TYPES.MemberExpression && init.object.type === AST_NODE_TYPES.Identifier) return findImportSource(init.object.name, initialScope);
43
43
  if (init.type === AST_NODE_TYPES.Identifier) return findImportSource(init.name, initialScope);
44
44
  const arg0 = getRequireExpressionArguments(init)?.[0];
45
- if (arg0 == null || !ast.isLiteral(arg0, "string")) return unit;
45
+ if (arg0 == null || !ast.isLiteral(arg0, "string")) return null;
46
46
  return arg0.value;
47
47
  }
48
48
  if (parent?.type === AST_NODE_TYPES.ImportDeclaration) return parent.source.value;
49
- return unit;
49
+ return null;
50
50
  }
51
51
 
52
52
  //#endregion
@@ -313,7 +313,7 @@ function useHookCollector(context) {
313
313
  const hooks = /* @__PURE__ */ new Map();
314
314
  const functionEntries = [];
315
315
  const getText = (n) => context.sourceCode.getText(n);
316
- const getCurrentEntry = () => functionEntries.at(-1);
316
+ const getCurrentEntry = () => functionEntries.at(-1) ?? null;
317
317
  const onFunctionEnter = (node) => {
318
318
  const id = ast.getFunctionId(node);
319
319
  const key = idGen$2.next();
@@ -401,7 +401,7 @@ function getJsxAttributeName(context, node) {
401
401
  * @param initialScope (Optional) The initial scope to use for variable resolution
402
402
  */
403
403
  function getJsxAttribute(context, node, initialScope) {
404
- const scope = initialScope ?? context.sourceCode.getScope(node);
404
+ initialScope ?? context.sourceCode.getScope(node);
405
405
  const attributes = node.openingElement.attributes;
406
406
  /**
407
407
  * Finds the last occurrence of a specific attribute
@@ -412,15 +412,7 @@ function getJsxAttribute(context, node, initialScope) {
412
412
  if (attr.type === AST_NODE_TYPES.JSXAttribute) return getJsxAttributeName(context, attr) === name;
413
413
  switch (attr.argument.type) {
414
414
  case AST_NODE_TYPES.Identifier: {
415
- const variable = findVariable(attr.argument.name, scope);
416
- function resolve(v) {
417
- if (v == null) return unit;
418
- const def = v.defs.at(0);
419
- if (def == null) return unit;
420
- if ("init" in def.node && def.node.init != null && !("declarations" in def.node.init)) return def.node.init;
421
- return unit;
422
- }
423
- const initNode = resolve(variable);
415
+ const initNode = resolve(context, attr.argument);
424
416
  if (initNode?.type === AST_NODE_TYPES.ObjectExpression) return ast.findProperty(initNode.properties, name) != null;
425
417
  return false;
426
418
  }
@@ -478,14 +470,14 @@ function resolveJsxAttributeValue(context, attribute) {
478
470
  kind: "element",
479
471
  node: node.value,
480
472
  toStatic() {
481
- return unit;
473
+ return null;
482
474
  }
483
475
  };
484
476
  case AST_NODE_TYPES.JSXSpreadChild: return {
485
477
  kind: "spreadChild",
486
478
  node: node.value.expression,
487
479
  toStatic() {
488
- return unit;
480
+ return null;
489
481
  }
490
482
  };
491
483
  }
@@ -499,8 +491,8 @@ function resolveJsxAttributeValue(context, attribute) {
499
491
  kind: "spreadProps",
500
492
  node: node.argument,
501
493
  toStatic(name) {
502
- if (name == null) return unit;
503
- return match(getStaticValue(node.argument, initialScope)?.value).with({ [name]: P.select(P.any) }, identity).otherwise(() => unit);
494
+ if (name == null) return null;
495
+ return match(getStaticValue(node.argument, initialScope)?.value).with({ [name]: P.select(P.any) }, identity).otherwise(() => null);
504
496
  }
505
497
  };
506
498
  }
@@ -594,13 +586,12 @@ function isJsxText(node) {
594
586
  * Determine if a node represents JSX-like content based on heuristics
595
587
  * Supports configuration through hint flags to customize detection behavior
596
588
  *
597
- * @param code The source code with scope lookup capability
598
- * @param code.getScope The function to get the scope of a node
589
+ * @param context The rule context with scope lookup capability
599
590
  * @param node The AST node to analyze
600
591
  * @param hint The configuration flags to adjust detection behavior
601
592
  * @returns boolean Whether the node is considered JSX-like
602
593
  */
603
- function isJsxLike(code, node, hint = DEFAULT_JSX_DETECTION_HINT) {
594
+ function isJsxLike(context, node, hint = DEFAULT_JSX_DETECTION_HINT) {
604
595
  if (node == null) return false;
605
596
  if (ast.isJSX(node)) return true;
606
597
  switch (node.type) {
@@ -616,27 +607,27 @@ function isJsxLike(code, node, hint = DEFAULT_JSX_DETECTION_HINT) {
616
607
  case AST_NODE_TYPES.TemplateLiteral: return !(hint & JsxDetectionHint.DoNotIncludeJsxWithStringValue);
617
608
  case AST_NODE_TYPES.ArrayExpression:
618
609
  if (node.elements.length === 0) return !(hint & JsxDetectionHint.DoNotIncludeJsxWithEmptyArrayValue);
619
- if (hint & JsxDetectionHint.RequireAllArrayElementsToBeJsx) return node.elements.every((n) => isJsxLike(code, n, hint));
620
- return node.elements.some((n) => isJsxLike(code, n, hint));
610
+ if (hint & JsxDetectionHint.RequireAllArrayElementsToBeJsx) return node.elements.every((n) => isJsxLike(context, n, hint));
611
+ return node.elements.some((n) => isJsxLike(context, n, hint));
621
612
  case AST_NODE_TYPES.LogicalExpression:
622
- if (hint & JsxDetectionHint.RequireBothSidesOfLogicalExpressionToBeJsx) return isJsxLike(code, node.left, hint) && isJsxLike(code, node.right, hint);
623
- return isJsxLike(code, node.left, hint) || isJsxLike(code, node.right, hint);
613
+ if (hint & JsxDetectionHint.RequireBothSidesOfLogicalExpressionToBeJsx) return isJsxLike(context, node.left, hint) && isJsxLike(context, node.right, hint);
614
+ return isJsxLike(context, node.left, hint) || isJsxLike(context, node.right, hint);
624
615
  case AST_NODE_TYPES.ConditionalExpression: {
625
616
  function leftHasJSX(node) {
626
617
  if (Array.isArray(node.consequent)) {
627
618
  if (node.consequent.length === 0) return !(hint & JsxDetectionHint.DoNotIncludeJsxWithEmptyArrayValue);
628
- if (hint & JsxDetectionHint.RequireAllArrayElementsToBeJsx) return node.consequent.every((n) => isJsxLike(code, n, hint));
629
- return node.consequent.some((n) => isJsxLike(code, n, hint));
619
+ if (hint & JsxDetectionHint.RequireAllArrayElementsToBeJsx) return node.consequent.every((n) => isJsxLike(context, n, hint));
620
+ return node.consequent.some((n) => isJsxLike(context, n, hint));
630
621
  }
631
- return isJsxLike(code, node.consequent, hint);
622
+ return isJsxLike(context, node.consequent, hint);
632
623
  }
633
624
  function rightHasJSX(node) {
634
- return isJsxLike(code, node.alternate, hint);
625
+ return isJsxLike(context, node.alternate, hint);
635
626
  }
636
627
  if (hint & JsxDetectionHint.RequireBothBranchesOfConditionalExpressionToBeJsx) return leftHasJSX(node) && rightHasJSX(node);
637
628
  return leftHasJSX(node) || rightHasJSX(node);
638
629
  }
639
- case AST_NODE_TYPES.SequenceExpression: return isJsxLike(code, node.expressions.at(-1), hint);
630
+ case AST_NODE_TYPES.SequenceExpression: return isJsxLike(context, node.expressions.at(-1) ?? null, hint);
640
631
  case AST_NODE_TYPES.CallExpression:
641
632
  if (hint & JsxDetectionHint.DoNotIncludeJsxWithCreateElementValue) return false;
642
633
  switch (node.callee.type) {
@@ -644,19 +635,10 @@ function isJsxLike(code, node, hint = DEFAULT_JSX_DETECTION_HINT) {
644
635
  case AST_NODE_TYPES.MemberExpression: return node.callee.property.type === AST_NODE_TYPES.Identifier && node.callee.property.name === "createElement";
645
636
  }
646
637
  return false;
647
- case AST_NODE_TYPES.Identifier: {
648
- const { name } = node;
649
- if (name === "undefined") return !(hint & JsxDetectionHint.DoNotIncludeJsxWithUndefinedValue);
638
+ case AST_NODE_TYPES.Identifier:
639
+ if (node.name === "undefined") return !(hint & JsxDetectionHint.DoNotIncludeJsxWithUndefinedValue);
650
640
  if (ast.isJSXTagNameExpression(node)) return true;
651
- function resolve(v) {
652
- if (v == null) return unit;
653
- const def = v.defs.at(0);
654
- if (def == null) return unit;
655
- if ("init" in def.node && def.node.init != null && !("declarations" in def.node.init)) return def.node.init;
656
- return unit;
657
- }
658
- return isJsxLike(code, resolve(findVariable(name, code.getScope(node))), hint);
659
- }
641
+ return isJsxLike(context, resolve(context, node), hint);
660
642
  }
661
643
  return false;
662
644
  }
@@ -714,7 +696,7 @@ function isJsxFragmentElement(context, node, jsxConfig) {
714
696
  * @param node The starting AST node
715
697
  * @param test Optional predicate function to test if the attribute meets criteria
716
698
  * Defaults to always returning true (matches any attribute)
717
- * @returns The first matching JSX attribute node found when traversing upwards, or undefined
699
+ * @returns The first matching JSX attribute node found when traversing upwards, or null
718
700
  */
719
701
  function findParentJsxAttribute(node, test = constTrue) {
720
702
  const guard = (node) => {
@@ -902,7 +884,7 @@ function isComponentWrapperCallbackLoose(context, node) {
902
884
  * Get function component identifier from `const Component = memo(() => {});`
903
885
  * @param context The rule context
904
886
  * @param node The function node to analyze
905
- * @returns The function identifier or `unit` if not found
887
+ * @returns The function identifier or `null` if not found
906
888
  */
907
889
  function getFunctionComponentId(context, node) {
908
890
  const functionId = ast.getFunctionId(node);
@@ -910,7 +892,7 @@ function getFunctionComponentId(context, node) {
910
892
  const { parent } = node;
911
893
  if (parent.type === AST_NODE_TYPES.CallExpression && isComponentWrapperCallLoose(context, parent) && parent.parent.type === AST_NODE_TYPES.VariableDeclarator) return parent.parent.id;
912
894
  if (parent.type === AST_NODE_TYPES.CallExpression && isComponentWrapperCallLoose(context, parent) && parent.parent.type === AST_NODE_TYPES.CallExpression && isComponentWrapperCallLoose(context, parent.parent) && parent.parent.parent.type === AST_NODE_TYPES.VariableDeclarator) return parent.parent.parent.id;
913
- return unit;
895
+ return null;
914
896
  }
915
897
 
916
898
  //#endregion
@@ -1050,14 +1032,14 @@ function useComponentCollector(context, options = {}) {
1050
1032
  const functionEntries = [];
1051
1033
  const components = /* @__PURE__ */ new Map();
1052
1034
  const getText = (n) => context.sourceCode.getText(n);
1053
- const getCurrentEntry = () => functionEntries.at(-1);
1035
+ const getCurrentEntry = () => functionEntries.at(-1) ?? null;
1054
1036
  const onFunctionEnter = (node) => {
1055
1037
  const key = idGen$1.next();
1056
1038
  const exp = ast.findParentNode(node, (n) => n.type === AST_NODE_TYPES.ExportDefaultDeclaration);
1057
1039
  const isExportDefault = exp != null;
1058
1040
  const isExportDefaultDeclaration = exp != null && ast.getUnderlyingExpression(exp.declaration) === node;
1059
1041
  const id = getFunctionComponentId(context, node);
1060
- const name = id == null ? unit : ast.getFullyQualifiedName(id, getText);
1042
+ const name = id == null ? null : ast.getFullyQualifiedName(id, getText);
1061
1043
  const initPath = ast.getFunctionInitPath(node);
1062
1044
  const directives = ast.getFunctionDirectives(node);
1063
1045
  const entry = {
@@ -1066,7 +1048,7 @@ function useComponentCollector(context, options = {}) {
1066
1048
  kind: "function-component",
1067
1049
  name,
1068
1050
  directives,
1069
- displayName: unit,
1051
+ displayName: null,
1070
1052
  flag: getComponentFlagFromInitPath(initPath),
1071
1053
  hint,
1072
1054
  hookCalls: [],
@@ -1104,13 +1086,13 @@ function useComponentCollector(context, options = {}) {
1104
1086
  if (body.type === AST_NODE_TYPES.BlockStatement) return;
1105
1087
  entry.rets.push(body);
1106
1088
  if (!entry.isComponentDefinition) return;
1107
- if (!components.has(entry.key) && !isJsxLike(context.sourceCode, body, hint)) return;
1089
+ if (!components.has(entry.key) && !isJsxLike(context, body, hint)) return;
1108
1090
  components.set(entry.key, entry);
1109
1091
  },
1110
1092
  ...collectDisplayName ? { [ast.SEL_DISPLAY_NAME_ASSIGNMENT_EXPRESSION](node) {
1111
1093
  const { left, right } = node;
1112
1094
  if (left.type !== AST_NODE_TYPES.MemberExpression) return;
1113
- const componentName = left.object.type === AST_NODE_TYPES.Identifier ? left.object.name : unit;
1095
+ const componentName = left.object.type === AST_NODE_TYPES.Identifier ? left.object.name : null;
1114
1096
  const component = [...components.values()].findLast(({ name }) => name != null && name === componentName);
1115
1097
  if (component == null) return;
1116
1098
  component.displayName = right;
@@ -1129,7 +1111,7 @@ function useComponentCollector(context, options = {}) {
1129
1111
  entry.rets.push(node.argument);
1130
1112
  if (!entry.isComponentDefinition) return;
1131
1113
  const { argument } = node;
1132
- if (!components.has(entry.key) && !isJsxLike(context.sourceCode, argument, hint)) return;
1114
+ if (!components.has(entry.key) && !isJsxLike(context, argument, hint)) return;
1133
1115
  components.set(entry.key, entry);
1134
1116
  }
1135
1117
  }
@@ -1154,14 +1136,14 @@ function useComponentCollectorLegacy(context) {
1154
1136
  if (!isClassComponent(node)) return;
1155
1137
  const id = ast.getClassId(node);
1156
1138
  const key = idGen.next();
1157
- const name = id == null ? unit : ast.getFullyQualifiedName(id, getText);
1139
+ const name = id == null ? null : ast.getFullyQualifiedName(id, getText);
1158
1140
  const flag = isPureComponent(node) ? ComponentFlag.PureComponent : ComponentFlag.None;
1159
1141
  components.set(key, {
1160
1142
  id,
1161
1143
  key,
1162
1144
  kind: "class-component",
1163
1145
  name,
1164
- displayName: unit,
1146
+ displayName: null,
1165
1147
  flag,
1166
1148
  hint: 0n,
1167
1149
  methods: [],
@@ -1209,10 +1191,10 @@ function isInitializedFromRef(name, initialScope) {
1209
1191
  * Get the init expression of a ref variable
1210
1192
  * @param name The variable name
1211
1193
  * @param initialScope The initial scope
1212
- * @returns The init expression node if the variable is derived from a ref, or undefined otherwise
1194
+ * @returns The init expression node if the variable is derived from a ref, or null otherwise
1213
1195
  */
1214
1196
  function getRefInit(name, initialScope) {
1215
- for (const { node } of findVariable(initialScope)(name)?.defs ?? []) {
1197
+ for (const { node } of findVariable(initialScope, name)?.defs ?? []) {
1216
1198
  if (node.type !== AST_NODE_TYPES$1.VariableDeclarator) continue;
1217
1199
  const init = node.init;
1218
1200
  if (init == null) continue;
@@ -1221,7 +1203,7 @@ function getRefInit(name, initialScope) {
1221
1203
  case init.type === AST_NODE_TYPES$1.CallExpression && isUseRefCall(init): return init;
1222
1204
  }
1223
1205
  }
1224
- return unit;
1206
+ return null;
1225
1207
  }
1226
1208
 
1227
1209
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eslint-react/core",
3
- "version": "3.0.0-next.62",
3
+ "version": "3.0.0-next.64",
4
4
  "description": "ESLint React's ESLint utility module for static analysis of React core APIs and patterns.",
5
5
  "homepage": "https://github.com/Rel1cx/eslint-react",
6
6
  "bugs": {
@@ -34,10 +34,10 @@
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/var": "3.0.0-next.62",
39
- "@eslint-react/eff": "3.0.0-next.62",
40
- "@eslint-react/shared": "3.0.0-next.62"
37
+ "@eslint-react/ast": "3.0.0-next.64",
38
+ "@eslint-react/shared": "3.0.0-next.64",
39
+ "@eslint-react/eff": "3.0.0-next.64",
40
+ "@eslint-react/var": "3.0.0-next.64"
41
41
  },
42
42
  "devDependencies": {
43
43
  "tsdown": "^0.21.0-beta.2",