@eslint-react/core 3.0.0-next.63 → 3.0.0-next.65

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,16 +1,17 @@
1
1
  import * as ast from "@eslint-react/ast";
2
- import { constFalse, constTrue, dual, flip, getOrElseUpdate, identity, unit } from "@eslint-react/eff";
2
+ import { constFalse, constTrue, dual, flip, getOrElseUpdate, identity } from "@eslint-react/eff";
3
3
  import { AST_NODE_TYPES } from "@typescript-eslint/types";
4
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 { resolve } from "@eslint-react/var";
7
8
  import { AST_NODE_TYPES as AST_NODE_TYPES$1 } from "@typescript-eslint/utils";
8
9
 
9
10
  //#region src/api/find-import-source.ts
10
11
  /**
11
12
  * Get the arguments of a require expression
12
13
  * @param node The node to match
13
- * @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
14
15
  * @internal
15
16
  */
16
17
  function getRequireExpressionArguments(node) {
@@ -30,22 +31,22 @@ function getRequireExpressionArguments(node) {
30
31
  * Find the import source of a variable
31
32
  * @param name The variable name
32
33
  * @param initialScope The initial scope to search
33
- * @returns The import source or undefined if not found
34
+ * @returns The import source or null if not found
34
35
  */
35
36
  function findImportSource(name, initialScope) {
36
37
  const latestDef = findVariable(initialScope, name)?.defs.at(-1);
37
- if (latestDef == null) return unit;
38
+ if (latestDef == null) return null;
38
39
  const { node, parent } = latestDef;
39
40
  if (node.type === AST_NODE_TYPES.VariableDeclarator && node.init != null) {
40
41
  const { init } = node;
41
42
  if (init.type === AST_NODE_TYPES.MemberExpression && init.object.type === AST_NODE_TYPES.Identifier) return findImportSource(init.object.name, initialScope);
42
43
  if (init.type === AST_NODE_TYPES.Identifier) return findImportSource(init.name, initialScope);
43
44
  const arg0 = getRequireExpressionArguments(init)?.[0];
44
- if (arg0 == null || !ast.isLiteral(arg0, "string")) return unit;
45
+ if (arg0 == null || !ast.isLiteral(arg0, "string")) return null;
45
46
  return arg0.value;
46
47
  }
47
48
  if (parent?.type === AST_NODE_TYPES.ImportDeclaration) return parent.source.value;
48
- return unit;
49
+ return null;
49
50
  }
50
51
 
51
52
  //#endregion
@@ -312,7 +313,7 @@ function useHookCollector(context) {
312
313
  const hooks = /* @__PURE__ */ new Map();
313
314
  const functionEntries = [];
314
315
  const getText = (n) => context.sourceCode.getText(n);
315
- const getCurrentEntry = () => functionEntries.at(-1);
316
+ const getCurrentEntry = () => functionEntries.at(-1) ?? null;
316
317
  const onFunctionEnter = (node) => {
317
318
  const id = ast.getFunctionId(node);
318
319
  const key = idGen$2.next();
@@ -400,7 +401,7 @@ function getJsxAttributeName(context, node) {
400
401
  * @param initialScope (Optional) The initial scope to use for variable resolution
401
402
  */
402
403
  function getJsxAttribute(context, node, initialScope) {
403
- const scope = initialScope ?? context.sourceCode.getScope(node);
404
+ initialScope ?? context.sourceCode.getScope(node);
404
405
  const attributes = node.openingElement.attributes;
405
406
  /**
406
407
  * Finds the last occurrence of a specific attribute
@@ -411,15 +412,7 @@ function getJsxAttribute(context, node, initialScope) {
411
412
  if (attr.type === AST_NODE_TYPES.JSXAttribute) return getJsxAttributeName(context, attr) === name;
412
413
  switch (attr.argument.type) {
413
414
  case AST_NODE_TYPES.Identifier: {
414
- const variable = findVariable(scope, attr.argument.name);
415
- function resolve(v) {
416
- if (v == null) return unit;
417
- const def = v.defs.at(0);
418
- if (def == null) return unit;
419
- if ("init" in def.node && def.node.init != null && !("declarations" in def.node.init)) return def.node.init;
420
- return unit;
421
- }
422
- const initNode = resolve(variable);
415
+ const initNode = resolve(context, attr.argument);
423
416
  if (initNode?.type === AST_NODE_TYPES.ObjectExpression) return ast.findProperty(initNode.properties, name) != null;
424
417
  return false;
425
418
  }
@@ -477,14 +470,14 @@ function resolveJsxAttributeValue(context, attribute) {
477
470
  kind: "element",
478
471
  node: node.value,
479
472
  toStatic() {
480
- return unit;
473
+ return null;
481
474
  }
482
475
  };
483
476
  case AST_NODE_TYPES.JSXSpreadChild: return {
484
477
  kind: "spreadChild",
485
478
  node: node.value.expression,
486
479
  toStatic() {
487
- return unit;
480
+ return null;
488
481
  }
489
482
  };
490
483
  }
@@ -498,8 +491,8 @@ function resolveJsxAttributeValue(context, attribute) {
498
491
  kind: "spreadProps",
499
492
  node: node.argument,
500
493
  toStatic(name) {
501
- if (name == null) return unit;
502
- 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);
503
496
  }
504
497
  };
505
498
  }
@@ -593,13 +586,12 @@ function isJsxText(node) {
593
586
  * Determine if a node represents JSX-like content based on heuristics
594
587
  * Supports configuration through hint flags to customize detection behavior
595
588
  *
596
- * @param code The source code with scope lookup capability
597
- * @param code.getScope The function to get the scope of a node
589
+ * @param context The rule context with scope lookup capability
598
590
  * @param node The AST node to analyze
599
591
  * @param hint The configuration flags to adjust detection behavior
600
592
  * @returns boolean Whether the node is considered JSX-like
601
593
  */
602
- function isJsxLike(code, node, hint = DEFAULT_JSX_DETECTION_HINT) {
594
+ function isJsxLike(context, node, hint = DEFAULT_JSX_DETECTION_HINT) {
603
595
  if (node == null) return false;
604
596
  if (ast.isJSX(node)) return true;
605
597
  switch (node.type) {
@@ -615,27 +607,27 @@ function isJsxLike(code, node, hint = DEFAULT_JSX_DETECTION_HINT) {
615
607
  case AST_NODE_TYPES.TemplateLiteral: return !(hint & JsxDetectionHint.DoNotIncludeJsxWithStringValue);
616
608
  case AST_NODE_TYPES.ArrayExpression:
617
609
  if (node.elements.length === 0) return !(hint & JsxDetectionHint.DoNotIncludeJsxWithEmptyArrayValue);
618
- if (hint & JsxDetectionHint.RequireAllArrayElementsToBeJsx) return node.elements.every((n) => isJsxLike(code, n, hint));
619
- 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));
620
612
  case AST_NODE_TYPES.LogicalExpression:
621
- if (hint & JsxDetectionHint.RequireBothSidesOfLogicalExpressionToBeJsx) return isJsxLike(code, node.left, hint) && isJsxLike(code, node.right, hint);
622
- 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);
623
615
  case AST_NODE_TYPES.ConditionalExpression: {
624
616
  function leftHasJSX(node) {
625
617
  if (Array.isArray(node.consequent)) {
626
618
  if (node.consequent.length === 0) return !(hint & JsxDetectionHint.DoNotIncludeJsxWithEmptyArrayValue);
627
- if (hint & JsxDetectionHint.RequireAllArrayElementsToBeJsx) return node.consequent.every((n) => isJsxLike(code, n, hint));
628
- 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));
629
621
  }
630
- return isJsxLike(code, node.consequent, hint);
622
+ return isJsxLike(context, node.consequent, hint);
631
623
  }
632
624
  function rightHasJSX(node) {
633
- return isJsxLike(code, node.alternate, hint);
625
+ return isJsxLike(context, node.alternate, hint);
634
626
  }
635
627
  if (hint & JsxDetectionHint.RequireBothBranchesOfConditionalExpressionToBeJsx) return leftHasJSX(node) && rightHasJSX(node);
636
628
  return leftHasJSX(node) || rightHasJSX(node);
637
629
  }
638
- 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);
639
631
  case AST_NODE_TYPES.CallExpression:
640
632
  if (hint & JsxDetectionHint.DoNotIncludeJsxWithCreateElementValue) return false;
641
633
  switch (node.callee.type) {
@@ -643,19 +635,10 @@ function isJsxLike(code, node, hint = DEFAULT_JSX_DETECTION_HINT) {
643
635
  case AST_NODE_TYPES.MemberExpression: return node.callee.property.type === AST_NODE_TYPES.Identifier && node.callee.property.name === "createElement";
644
636
  }
645
637
  return false;
646
- case AST_NODE_TYPES.Identifier: {
647
- const { name } = node;
648
- if (name === "undefined") return !(hint & JsxDetectionHint.DoNotIncludeJsxWithUndefinedValue);
638
+ case AST_NODE_TYPES.Identifier:
639
+ if (node.name === "undefined") return !(hint & JsxDetectionHint.DoNotIncludeJsxWithUndefinedValue);
649
640
  if (ast.isJSXTagNameExpression(node)) return true;
650
- function resolve(v) {
651
- if (v == null) return unit;
652
- const def = v.defs.at(0);
653
- if (def == null) return unit;
654
- if ("init" in def.node && def.node.init != null && !("declarations" in def.node.init)) return def.node.init;
655
- return unit;
656
- }
657
- return isJsxLike(code, resolve(findVariable(code.getScope(node), name)), hint);
658
- }
641
+ return isJsxLike(context, resolve(context, node), hint);
659
642
  }
660
643
  return false;
661
644
  }
@@ -713,7 +696,7 @@ function isJsxFragmentElement(context, node, jsxConfig) {
713
696
  * @param node The starting AST node
714
697
  * @param test Optional predicate function to test if the attribute meets criteria
715
698
  * Defaults to always returning true (matches any attribute)
716
- * @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
717
700
  */
718
701
  function findParentJsxAttribute(node, test = constTrue) {
719
702
  const guard = (node) => {
@@ -901,7 +884,7 @@ function isComponentWrapperCallbackLoose(context, node) {
901
884
  * Get function component identifier from `const Component = memo(() => {});`
902
885
  * @param context The rule context
903
886
  * @param node The function node to analyze
904
- * @returns The function identifier or `unit` if not found
887
+ * @returns The function identifier or `null` if not found
905
888
  */
906
889
  function getFunctionComponentId(context, node) {
907
890
  const functionId = ast.getFunctionId(node);
@@ -909,7 +892,7 @@ function getFunctionComponentId(context, node) {
909
892
  const { parent } = node;
910
893
  if (parent.type === AST_NODE_TYPES.CallExpression && isComponentWrapperCallLoose(context, parent) && parent.parent.type === AST_NODE_TYPES.VariableDeclarator) return parent.parent.id;
911
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;
912
- return unit;
895
+ return null;
913
896
  }
914
897
 
915
898
  //#endregion
@@ -1049,14 +1032,14 @@ function useComponentCollector(context, options = {}) {
1049
1032
  const functionEntries = [];
1050
1033
  const components = /* @__PURE__ */ new Map();
1051
1034
  const getText = (n) => context.sourceCode.getText(n);
1052
- const getCurrentEntry = () => functionEntries.at(-1);
1035
+ const getCurrentEntry = () => functionEntries.at(-1) ?? null;
1053
1036
  const onFunctionEnter = (node) => {
1054
1037
  const key = idGen$1.next();
1055
1038
  const exp = ast.findParentNode(node, (n) => n.type === AST_NODE_TYPES.ExportDefaultDeclaration);
1056
1039
  const isExportDefault = exp != null;
1057
1040
  const isExportDefaultDeclaration = exp != null && ast.getUnderlyingExpression(exp.declaration) === node;
1058
1041
  const id = getFunctionComponentId(context, node);
1059
- const name = id == null ? unit : ast.getFullyQualifiedName(id, getText);
1042
+ const name = id == null ? null : ast.getFullyQualifiedName(id, getText);
1060
1043
  const initPath = ast.getFunctionInitPath(node);
1061
1044
  const directives = ast.getFunctionDirectives(node);
1062
1045
  const entry = {
@@ -1065,7 +1048,7 @@ function useComponentCollector(context, options = {}) {
1065
1048
  kind: "function-component",
1066
1049
  name,
1067
1050
  directives,
1068
- displayName: unit,
1051
+ displayName: null,
1069
1052
  flag: getComponentFlagFromInitPath(initPath),
1070
1053
  hint,
1071
1054
  hookCalls: [],
@@ -1103,13 +1086,13 @@ function useComponentCollector(context, options = {}) {
1103
1086
  if (body.type === AST_NODE_TYPES.BlockStatement) return;
1104
1087
  entry.rets.push(body);
1105
1088
  if (!entry.isComponentDefinition) return;
1106
- if (!components.has(entry.key) && !isJsxLike(context.sourceCode, body, hint)) return;
1089
+ if (!components.has(entry.key) && !isJsxLike(context, body, hint)) return;
1107
1090
  components.set(entry.key, entry);
1108
1091
  },
1109
1092
  ...collectDisplayName ? { [ast.SEL_DISPLAY_NAME_ASSIGNMENT_EXPRESSION](node) {
1110
1093
  const { left, right } = node;
1111
1094
  if (left.type !== AST_NODE_TYPES.MemberExpression) return;
1112
- 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;
1113
1096
  const component = [...components.values()].findLast(({ name }) => name != null && name === componentName);
1114
1097
  if (component == null) return;
1115
1098
  component.displayName = right;
@@ -1128,7 +1111,7 @@ function useComponentCollector(context, options = {}) {
1128
1111
  entry.rets.push(node.argument);
1129
1112
  if (!entry.isComponentDefinition) return;
1130
1113
  const { argument } = node;
1131
- if (!components.has(entry.key) && !isJsxLike(context.sourceCode, argument, hint)) return;
1114
+ if (!components.has(entry.key) && !isJsxLike(context, argument, hint)) return;
1132
1115
  components.set(entry.key, entry);
1133
1116
  }
1134
1117
  }
@@ -1153,14 +1136,14 @@ function useComponentCollectorLegacy(context) {
1153
1136
  if (!isClassComponent(node)) return;
1154
1137
  const id = ast.getClassId(node);
1155
1138
  const key = idGen.next();
1156
- const name = id == null ? unit : ast.getFullyQualifiedName(id, getText);
1139
+ const name = id == null ? null : ast.getFullyQualifiedName(id, getText);
1157
1140
  const flag = isPureComponent(node) ? ComponentFlag.PureComponent : ComponentFlag.None;
1158
1141
  components.set(key, {
1159
1142
  id,
1160
1143
  key,
1161
1144
  kind: "class-component",
1162
1145
  name,
1163
- displayName: unit,
1146
+ displayName: null,
1164
1147
  flag,
1165
1148
  hint: 0n,
1166
1149
  methods: [],
@@ -1208,7 +1191,7 @@ function isInitializedFromRef(name, initialScope) {
1208
1191
  * Get the init expression of a ref variable
1209
1192
  * @param name The variable name
1210
1193
  * @param initialScope The initial scope
1211
- * @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
1212
1195
  */
1213
1196
  function getRefInit(name, initialScope) {
1214
1197
  for (const { node } of findVariable(initialScope, name)?.defs ?? []) {
@@ -1220,7 +1203,7 @@ function getRefInit(name, initialScope) {
1220
1203
  case init.type === AST_NODE_TYPES$1.CallExpression && isUseRefCall(init): return init;
1221
1204
  }
1222
1205
  }
1223
- return unit;
1206
+ return null;
1224
1207
  }
1225
1208
 
1226
1209
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eslint-react/core",
3
- "version": "3.0.0-next.63",
3
+ "version": "3.0.0-next.65",
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/shared": "3.0.0-next.63",
38
- "@eslint-react/ast": "3.0.0-next.63",
39
- "@eslint-react/var": "3.0.0-next.63",
40
- "@eslint-react/eff": "3.0.0-next.63"
37
+ "@eslint-react/ast": "3.0.0-next.65",
38
+ "@eslint-react/shared": "3.0.0-next.65",
39
+ "@eslint-react/var": "3.0.0-next.65",
40
+ "@eslint-react/eff": "3.0.0-next.65"
41
41
  },
42
42
  "devDependencies": {
43
43
  "tsdown": "^0.21.0-beta.2",