@eslint-react/core 5.17.3 → 5.18.0

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +263 -92
  2. package/dist/index.js +233 -47
  3. package/package.json +13 -13
package/dist/index.js CHANGED
@@ -511,69 +511,133 @@ function isAPICall(api) {
511
511
  };
512
512
  return dual(2, func);
513
513
  }
514
+ /** Check if the node is a React `captureOwnerStack` API identifier or member expression. */
514
515
  const isCaptureOwnerStack = isAPI("captureOwnerStack");
516
+ /** Check if the node is a React `Children.count` API identifier or member expression. */
515
517
  const isChildrenCount = isAPI("Children.count");
518
+ /** Check if the node is a React `Children.forEach` API identifier or member expression. */
516
519
  const isChildrenForEach = isAPI("Children.forEach");
520
+ /** Check if the node is a React `Children.map` API identifier or member expression. */
517
521
  const isChildrenMap = isAPI("Children.map");
522
+ /** Check if the node is a React `Children.only` API identifier or member expression. */
518
523
  const isChildrenOnly = isAPI("Children.only");
524
+ /** Check if the node is a React `Children.toArray` API identifier or member expression. */
519
525
  const isChildrenToArray = isAPI("Children.toArray");
526
+ /** Check if the node is a React `cloneElement` API identifier or member expression. */
520
527
  const isCloneElement = isAPI("cloneElement");
528
+ /** Check if the node is a React `createContext` API identifier or member expression. */
521
529
  const isCreateContext = isAPI("createContext");
530
+ /** Check if the node is a React `createElement` API identifier or member expression. */
522
531
  const isCreateElement = isAPI("createElement");
532
+ /** Check if the node is a React `createRef` API identifier or member expression. */
523
533
  const isCreateRef = isAPI("createRef");
534
+ /** Check if the node is a React `forwardRef` API identifier or member expression. */
524
535
  const isForwardRef = isAPI("forwardRef");
536
+ /** Check if the node is a React `memo` API identifier or member expression. */
525
537
  const isMemo = isAPI("memo");
538
+ /** Check if the node is a React `lazy` API identifier or member expression. */
526
539
  const isLazy = isAPI("lazy");
540
+ /** Check if the node is a call expression to the React `captureOwnerStack` API. */
527
541
  const isCaptureOwnerStackCall = isAPICall("captureOwnerStack");
542
+ /** Check if the node is a call expression to the React `Children.count` API. */
528
543
  const isChildrenCountCall = isAPICall("Children.count");
544
+ /** Check if the node is a call expression to the React `Children.forEach` API. */
529
545
  const isChildrenForEachCall = isAPICall("Children.forEach");
546
+ /** Check if the node is a call expression to the React `Children.map` API. */
530
547
  const isChildrenMapCall = isAPICall("Children.map");
548
+ /** Check if the node is a call expression to the React `Children.only` API. */
531
549
  const isChildrenOnlyCall = isAPICall("Children.only");
550
+ /** Check if the node is a call expression to the React `Children.toArray` API. */
532
551
  const isChildrenToArrayCall = isAPICall("Children.toArray");
552
+ /** Check if the node is a call expression to the React `cloneElement` API. */
533
553
  const isCloneElementCall = isAPICall("cloneElement");
554
+ /** Check if the node is a call expression to the React `createContext` API. */
534
555
  const isCreateContextCall = isAPICall("createContext");
556
+ /** Check if the node is a call expression to the React `createElement` API. */
535
557
  const isCreateElementCall = isAPICall("createElement");
558
+ /** Check if the node is a call expression to the React `createRef` API. */
536
559
  const isCreateRefCall = isAPICall("createRef");
560
+ /** Check if the node is a call expression to the React `forwardRef` API. */
537
561
  const isForwardRefCall = isAPICall("forwardRef");
562
+ /** Check if the node is a call expression to the React `memo` API. */
538
563
  const isMemoCall = isAPICall("memo");
564
+ /** Check if the node is a call expression to the React `lazy` API. */
539
565
  const isLazyCall = isAPICall("lazy");
566
+ /** Check if the node is a React `use` API identifier or member expression. */
540
567
  const isUse = isAPI("use");
568
+ /** Check if the node is a React `useActionState` API identifier or member expression. */
541
569
  const isUseActionState = isAPI("useActionState");
570
+ /** Check if the node is a React `useCallback` API identifier or member expression. */
542
571
  const isUseCallback = isAPI("useCallback");
572
+ /** Check if the node is a React `useContext` API identifier or member expression. */
543
573
  const isUseContext = isAPI("useContext");
574
+ /** Check if the node is a React `useDebugValue` API identifier or member expression. */
544
575
  const isUseDebugValue = isAPI("useDebugValue");
576
+ /** Check if the node is a React `useDeferredValue` API identifier or member expression. */
545
577
  const isUseDeferredValue = isAPI("useDeferredValue");
578
+ /** Check if the node is a React `useEffect` API identifier or member expression. */
546
579
  const isUseEffect = isAPI("useEffect");
580
+ /** Check if the node is a React `useFormStatus` API identifier or member expression. */
547
581
  const isUseFormStatus = isAPI("useFormStatus");
582
+ /** Check if the node is a React `useId` API identifier or member expression. */
548
583
  const isUseId = isAPI("useId");
584
+ /** Check if the node is a React `useImperativeHandle` API identifier or member expression. */
549
585
  const isUseImperativeHandle = isAPI("useImperativeHandle");
586
+ /** Check if the node is a React `useInsertionEffect` API identifier or member expression. */
550
587
  const isUseInsertionEffect = isAPI("useInsertionEffect");
588
+ /** Check if the node is a React `useLayoutEffect` API identifier or member expression. */
551
589
  const isUseLayoutEffect = isAPI("useLayoutEffect");
590
+ /** Check if the node is a React `useMemo` API identifier or member expression. */
552
591
  const isUseMemo = isAPI("useMemo");
592
+ /** Check if the node is a React `useOptimistic` API identifier or member expression. */
553
593
  const isUseOptimistic = isAPI("useOptimistic");
594
+ /** Check if the node is a React `useReducer` API identifier or member expression. */
554
595
  const isUseReducer = isAPI("useReducer");
596
+ /** Check if the node is a React `useRef` API identifier or member expression. */
555
597
  const isUseRef = isAPI("useRef");
598
+ /** Check if the node is a React `useState` API identifier or member expression. */
556
599
  const isUseState = isAPI("useState");
600
+ /** Check if the node is a React `useSyncExternalStore` API identifier or member expression. */
557
601
  const isUseSyncExternalStore = isAPI("useSyncExternalStore");
602
+ /** Check if the node is a React `useTransition` API identifier or member expression. */
558
603
  const isUseTransition = isAPI("useTransition");
604
+ /** Check if the node is a call expression to the React `use` API. */
559
605
  const isUseCall = isAPICall("use");
606
+ /** Check if the node is a call expression to the React `useActionState` API. */
560
607
  const isUseActionStateCall = isAPICall("useActionState");
608
+ /** Check if the node is a call expression to the React `useCallback` API. */
561
609
  const isUseCallbackCall = isAPICall("useCallback");
610
+ /** Check if the node is a call expression to the React `useContext` API. */
562
611
  const isUseContextCall = isAPICall("useContext");
612
+ /** Check if the node is a call expression to the React `useDebugValue` API. */
563
613
  const isUseDebugValueCall = isAPICall("useDebugValue");
614
+ /** Check if the node is a call expression to the React `useDeferredValue` API. */
564
615
  const isUseDeferredValueCall = isAPICall("useDeferredValue");
616
+ /** Check if the node is a call expression to the React `useEffect` API. */
565
617
  const isUseEffectCall = isAPICall("useEffect");
618
+ /** Check if the node is a call expression to the React `useFormStatus` API. */
566
619
  const isUseFormStatusCall = isAPICall("useFormStatus");
620
+ /** Check if the node is a call expression to the React `useId` API. */
567
621
  const isUseIdCall = isAPICall("useId");
622
+ /** Check if the node is a call expression to the React `useImperativeHandle` API. */
568
623
  const isUseImperativeHandleCall = isAPICall("useImperativeHandle");
624
+ /** Check if the node is a call expression to the React `useInsertionEffect` API. */
569
625
  const isUseInsertionEffectCall = isAPICall("useInsertionEffect");
626
+ /** Check if the node is a call expression to the React `useLayoutEffect` API. */
570
627
  const isUseLayoutEffectCall = isAPICall("useLayoutEffect");
628
+ /** Check if the node is a call expression to the React `useMemo` API. */
571
629
  const isUseMemoCall = isAPICall("useMemo");
630
+ /** Check if the node is a call expression to the React `useOptimistic` API. */
572
631
  const isUseOptimisticCall = isAPICall("useOptimistic");
632
+ /** Check if the node is a call expression to the React `useReducer` API. */
573
633
  const isUseReducerCall = isAPICall("useReducer");
634
+ /** Check if the node is a call expression to the React `useRef` API. */
574
635
  const isUseRefCall = isAPICall("useRef");
636
+ /** Check if the node is a call expression to the React `useState` API. */
575
637
  const isUseStateCall = isAPICall("useState");
638
+ /** Check if the node is a call expression to the React `useSyncExternalStore` API. */
576
639
  const isUseSyncExternalStoreCall = isAPICall("useSyncExternalStore");
640
+ /** Check if the node is a call expression to the React `useTransition` API. */
577
641
  const isUseTransitionCall = isAPICall("useTransition");
578
642
 
579
643
  //#endregion
@@ -581,7 +645,7 @@ const isUseTransitionCall = isAPICall("useTransition");
581
645
  /**
582
646
  * Get the class identifier of a class node.
583
647
  * @param node The class node to get the identifier from.
584
- * @returns The class identifier or null if not found.
648
+ * @returns The class identifier or `null` if not found.
585
649
  */
586
650
  function getClassId(node) {
587
651
  if (node.id != null) return node.id;
@@ -591,6 +655,11 @@ function getClassId(node) {
591
655
 
592
656
  //#endregion
593
657
  //#region src/class-component.ts
658
+ /**
659
+ * Check if the node is a class component (extends `Component` or `PureComponent`).
660
+ * @param node The node to check.
661
+ * @returns `true` if the node is a class component.
662
+ */
594
663
  function isClassComponent(node) {
595
664
  if ("superClass" in node && node.superClass != null) {
596
665
  const re = /^(?:Pure)?Component$/u;
@@ -602,7 +671,9 @@ function isClassComponent(node) {
602
671
  return false;
603
672
  }
604
673
  /**
674
+ * Check if the node is a pure component (extends `PureComponent`).
605
675
  * @param node The AST node to check.
676
+ * @returns `true` if the node is a pure component.
606
677
  * @deprecated Class components are legacy. This function exists only to support legacy rules.
607
678
  */
608
679
  function isPureComponent(node) {
@@ -655,19 +726,28 @@ const isGetDerivedStateFromProps = createLifecycleChecker("getDerivedStateFromPr
655
726
  /** @deprecated Class components are legacy. */
656
727
  const isGetDerivedStateFromError = createLifecycleChecker("getDerivedStateFromError", true);
657
728
  /**
729
+ * Check if the node is a render-like method of a class component.
658
730
  * @param node The AST node to check.
731
+ * @returns `true` if the node is a render-like method.
659
732
  * @deprecated Class components are legacy. This function exists only to support legacy rules.
660
733
  */
661
734
  function isRenderMethodLike(node) {
662
735
  return Check.isPropertyOrMethod(node) && node.key.type === AST_NODE_TYPES.Identifier && node.key.name.startsWith("render") && Check.isOneOf([AST_NODE_TYPES.ClassDeclaration, AST_NODE_TYPES.ClassExpression])(node.parent.parent);
663
736
  }
737
+ /**
738
+ * Check if the function is a callback passed to a class component's render method.
739
+ * @param node The function node to check.
740
+ * @returns `true` if the function is a render method callback.
741
+ */
664
742
  function isRenderMethodCallback(node) {
665
743
  const parent = node.parent;
666
744
  const greatGrandparent = parent.parent?.parent;
667
745
  return greatGrandparent != null && isRenderMethodLike(parent) && isClassComponent(greatGrandparent);
668
746
  }
669
747
  /**
748
+ * Check if the call expression is a `this.setState(...)` call.
670
749
  * @param node The call expression node to check.
750
+ * @returns `true` if the node is a `this.setState(...)` call.
671
751
  * @deprecated Class components are legacy. This function exists only to support legacy rules.
672
752
  */
673
753
  function isThisSetStateCall(node) {
@@ -675,7 +755,9 @@ function isThisSetStateCall(node) {
675
755
  return callee.type === AST_NODE_TYPES.MemberExpression && callee.object.type === AST_NODE_TYPES.ThisExpression && Extract.getCalleeName(node) === "setState";
676
756
  }
677
757
  /**
758
+ * Check if the assignment expression assigns to `this.state`.
678
759
  * @param node The assignment expression node to check.
760
+ * @returns `true` if the node assigns to `this.state`.
679
761
  * @deprecated Class components are legacy. This function exists only to support legacy rules.
680
762
  */
681
763
  function isAssignmentToThisState(node) {
@@ -692,6 +774,7 @@ function isAssignmentToThisState(node) {
692
774
  //#endregion
693
775
  //#region src/class-component-collector.ts
694
776
  /**
777
+ * Get an api and visitor object for the rule to collect class components.
695
778
  * @param context The rule context.
696
779
  * @deprecated Class components are legacy. This function exists only to support legacy rules.
697
780
  */
@@ -730,7 +813,7 @@ function getClassComponentCollector(context) {
730
813
  //#endregion
731
814
  //#region src/function.ts
732
815
  /**
733
- * Gets the static identifier of a function AST node.
816
+ * Get the static identifier of a function AST node.
734
817
  *
735
818
  * @remarks
736
819
  * For function declarations this is straightforward. For anonymous function
@@ -757,7 +840,7 @@ function getFunctionId(node) {
757
840
  return null;
758
841
  }
759
842
  /**
760
- * Identifies the initialization path of a function node in the AST.
843
+ * Get the initialization path of a function node in the AST.
761
844
  *
762
845
  * @param node The function node to analyze.
763
846
  * @returns The function initialization path or `null` if not identifiable.
@@ -808,7 +891,7 @@ function getFunctionInitPath(node) {
808
891
  return null;
809
892
  }
810
893
  /**
811
- * Checks if a specific function call exists in the function initialization path.
894
+ * Check if a specific function call exists in the function initialization path.
812
895
  *
813
896
  * @param callName The name of the call to check for (e.g., "memo", "forwardRef").
814
897
  * @param initPath The function initialization path to search in.
@@ -824,7 +907,7 @@ function isFunctionHasCallInInitPath(callName, initPath) {
824
907
  });
825
908
  }
826
909
  /**
827
- * Checks if a function is empty.
910
+ * Check if a function is empty.
828
911
  *
829
912
  * @param node The function node to check.
830
913
  * @returns `true` if the function is empty, `false` otherwise.
@@ -832,6 +915,11 @@ function isFunctionHasCallInInitPath(callName, initPath) {
832
915
  function isFunctionEmpty(node) {
833
916
  return node.body.type === AST_NODE_TYPES.BlockStatement && node.body.body.length === 0;
834
917
  }
918
+ /**
919
+ * Get the directives of a function (ex: "use strict", "use client", "use server").
920
+ * @param node The function node to get the directives from.
921
+ * @returns The directives of the function.
922
+ */
835
923
  function getFunctionDirectives(node) {
836
924
  const directives = [];
837
925
  if (node.body.type !== AST_NODE_TYPES.BlockStatement) return directives;
@@ -845,7 +933,7 @@ function getFunctionDirectives(node) {
845
933
  return directives;
846
934
  }
847
935
  /**
848
- * Checks if a directive with the given name exists in the function directives.
936
+ * Check if a directive with the given name exists in the function directives.
849
937
  *
850
938
  * @param node The function AST node.
851
939
  * @param name The directive name to check (e.g., "use memo", "use no memo").
@@ -854,6 +942,7 @@ function getFunctionDirectives(node) {
854
942
  function isFunctionHasDirective(node, name) {
855
943
  return getFunctionDirectives(node).some((d) => d.directive === name);
856
944
  }
945
+ /** The esquery selector matching `displayName` assignment expressions. */
857
946
  const SEL_FUNCTION_DISPLAY_NAME_ASSIGNMENT = [
858
947
  "AssignmentExpression",
859
948
  "[operator='=']",
@@ -867,17 +956,29 @@ const SEL_FUNCTION_DISPLAY_NAME_ASSIGNMENT = [
867
956
  * Hints for JSX detection.
868
957
  */
869
958
  const JsxDetectionHint = {
959
+ /** No hints set. */
870
960
  None: 0n,
961
+ /** Do not treat `null` values as JSX-like. */
871
962
  DoNotIncludeJsxWithNullValue: 1n << 0n,
963
+ /** Do not treat number values as JSX-like. */
872
964
  DoNotIncludeJsxWithNumberValue: 1n << 1n,
965
+ /** Do not treat bigint values as JSX-like. */
873
966
  DoNotIncludeJsxWithBigIntValue: 1n << 2n,
967
+ /** Do not treat string values as JSX-like. */
874
968
  DoNotIncludeJsxWithStringValue: 1n << 3n,
969
+ /** Do not treat boolean values as JSX-like. */
875
970
  DoNotIncludeJsxWithBooleanValue: 1n << 4n,
971
+ /** Do not treat undefined values as JSX-like. */
876
972
  DoNotIncludeJsxWithUndefinedValue: 1n << 5n,
973
+ /** Do not treat empty array values as JSX-like. */
877
974
  DoNotIncludeJsxWithEmptyArrayValue: 1n << 6n,
975
+ /** Do not treat `createElement` calls as JSX-like. */
878
976
  DoNotIncludeJsxWithCreateElementValue: 1n << 7n,
977
+ /** Require all array elements to be JSX-like for the array to be JSX-like. */
879
978
  RequireAllArrayElementsToBeJsx: 1n << 8n,
979
+ /** Require both sides of a logical expression to be JSX-like. */
880
980
  RequireBothSidesOfLogicalExpressionToBeJsx: 1n << 9n,
981
+ /** Require both branches of a conditional expression to be JSX-like. */
881
982
  RequireBothBranchesOfConditionalExpressionToBeJsx: 1n << 10n
882
983
  };
883
984
  /**
@@ -889,9 +990,9 @@ const JsxDetectionHint = {
889
990
  */
890
991
  const DEFAULT_JSX_DETECTION_HINT = 0n | JsxDetectionHint.DoNotIncludeJsxWithNumberValue | JsxDetectionHint.DoNotIncludeJsxWithBigIntValue | JsxDetectionHint.DoNotIncludeJsxWithBooleanValue | JsxDetectionHint.DoNotIncludeJsxWithStringValue | JsxDetectionHint.DoNotIncludeJsxWithUndefinedValue;
891
992
  /**
892
- * Determine whether a node represents JSX-like content based on heuristics.
993
+ * Check if the node represents JSX-like content based on heuristics.
893
994
  *
894
- * The detection behaviour is configurable through {@link JsxDetectionHint}
995
+ * The detection behavior is configurable through {@link JsxDetectionHint}
895
996
  * bit-flags so that callers can opt individual value kinds in or out.
896
997
  *
897
998
  * Identifiers are resolved to their definitions via scope analysis;
@@ -899,8 +1000,8 @@ const DEFAULT_JSX_DETECTION_HINT = 0n | JsxDetectionHint.DoNotIncludeJsxWithNumb
899
1000
  * treated as not JSX-like instead of recursing indefinitely.
900
1001
  *
901
1002
  * @param context The ESLint rule context (needed for variable resolution).
902
- * @param node The AST node to analyse.
903
- * @param hint Optional bit-flags to adjust detection behaviour. Defaults to {@link DEFAULT_JSX_DETECTION_HINT}.
1003
+ * @param node The AST node to analyze.
1004
+ * @param hint Optional bit-flags to adjust detection behavior. Defaults to {@link DEFAULT_JSX_DETECTION_HINT}.
904
1005
  * @returns Whether the node is considered JSX-like.
905
1006
  *
906
1007
  * @example
@@ -1023,6 +1124,7 @@ function getFunctionComponentId(context, node) {
1023
1124
  /**
1024
1125
  * Check if a string matches the strict component name pattern.
1025
1126
  * @param name The name to check.
1127
+ * @returns `true` if the name matches the strict component name pattern.
1026
1128
  */
1027
1129
  function isFunctionComponentName(name) {
1028
1130
  return RE_COMPONENT_NAME.test(name);
@@ -1030,6 +1132,7 @@ function isFunctionComponentName(name) {
1030
1132
  /**
1031
1133
  * Check if a string matches the loose component name pattern.
1032
1134
  * @param name The name to check.
1135
+ * @returns `true` if the name matches the loose component name pattern.
1033
1136
  */
1034
1137
  function isFunctionComponentNameLoose(name) {
1035
1138
  return RE_COMPONENT_NAME_LOOSE.test(name);
@@ -1039,7 +1142,7 @@ function isFunctionComponentNameLoose(name) {
1039
1142
  * @param context The rule context.
1040
1143
  * @param fn The function to check.
1041
1144
  * @param allowNone Whether to allow no name.
1042
- * @returns Whether the function has a loose component name.
1145
+ * @returns `true` if the function has a loose component name.
1043
1146
  */
1044
1147
  function isFunctionWithLooseComponentName(context, fn, allowNone = false) {
1045
1148
  const id = getFunctionComponentId(context, fn);
@@ -1053,13 +1156,21 @@ function isFunctionWithLooseComponentName(context, fn, allowNone = false) {
1053
1156
  */
1054
1157
  const FunctionComponentDetectionHint = {
1055
1158
  ...JsxDetectionHint,
1159
+ /** Exclude functions defined as class methods from component detection. */
1056
1160
  DoNotIncludeFunctionDefinedAsClassMethod: 1n << 11n,
1161
+ /** Exclude functions defined as class properties from component detection. */
1057
1162
  DoNotIncludeFunctionDefinedAsClassProperty: 1n << 12n,
1163
+ /** Exclude functions defined as object methods from component detection. */
1058
1164
  DoNotIncludeFunctionDefinedAsObjectMethod: 1n << 13n,
1165
+ /** Exclude functions defined as array expression elements from component detection. */
1059
1166
  DoNotIncludeFunctionDefinedAsArrayExpressionElement: 1n << 14n,
1167
+ /** Exclude functions defined as array pattern elements from component detection. */
1060
1168
  DoNotIncludeFunctionDefinedAsArrayPatternElement: 1n << 15n,
1169
+ /** Exclude functions defined as array flatMap callbacks from component detection. */
1061
1170
  DoNotIncludeFunctionDefinedAsArrayFlatMapCallback: 1n << 16n,
1171
+ /** Exclude functions defined as array map callbacks from component detection. */
1062
1172
  DoNotIncludeFunctionDefinedAsArrayMapCallback: 1n << 17n,
1173
+ /** Exclude functions defined as arbitrary call expression callbacks from component detection. */
1063
1174
  DoNotIncludeFunctionDefinedAsArbitraryCallExpressionCallback: 1n << 18n
1064
1175
  };
1065
1176
  /**
@@ -1067,7 +1178,7 @@ const FunctionComponentDetectionHint = {
1067
1178
  */
1068
1179
  const DEFAULT_COMPONENT_DETECTION_HINT = 0n | FunctionComponentDetectionHint.DoNotIncludeJsxWithBigIntValue | FunctionComponentDetectionHint.DoNotIncludeJsxWithBooleanValue | FunctionComponentDetectionHint.DoNotIncludeJsxWithNumberValue | FunctionComponentDetectionHint.DoNotIncludeJsxWithStringValue | FunctionComponentDetectionHint.DoNotIncludeJsxWithUndefinedValue | FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArbitraryCallExpressionCallback | FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayExpressionElement | FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayFlatMapCallback | FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayMapCallback | FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayPatternElement | FunctionComponentDetectionHint.RequireAllArrayElementsToBeJsx | FunctionComponentDetectionHint.RequireBothBranchesOfConditionalExpressionToBeJsx | FunctionComponentDetectionHint.RequireBothSidesOfLogicalExpressionToBeJsx;
1069
1180
  /**
1070
- * Determine if a function node represents a valid React component definition.
1181
+ * Check if the function node is a valid React component definition.
1071
1182
  *
1072
1183
  * @param context The rule context.
1073
1184
  * @param node The function node to analyze.
@@ -1130,6 +1241,7 @@ function isFunctionComponentDefinition(context, node, hint) {
1130
1241
 
1131
1242
  //#endregion
1132
1243
  //#region src/hook.ts
1244
+ /** The names of React's built-in hooks. */
1133
1245
  const REACT_BUILTIN_HOOK_NAMES = [
1134
1246
  "use",
1135
1247
  "useActionState",
@@ -1152,9 +1264,9 @@ const REACT_BUILTIN_HOOK_NAMES = [
1152
1264
  "useTransition"
1153
1265
  ];
1154
1266
  /**
1155
- * Catch all identifiers that begin with "use" followed by an uppercase Latin
1156
- * character to exclude identifiers like "user".
1267
+ * Check if the name is a hook name (starts with `use` followed by an uppercase letter or digit).
1157
1268
  * @param name The name of the identifier to check.
1269
+ * @returns `true` if the name is a hook name.
1158
1270
  * @see https://github.com/facebook/react/blob/1d6c8168db1d82713202e842df3167787ffa00ed/packages/eslint-plugin-react-hooks/src/rules/RulesOfHooks.ts#L16
1159
1271
  */
1160
1272
  function isHookName(name) {
@@ -1182,9 +1294,9 @@ function isHookTag(tag) {
1182
1294
  return isHookId(Extract.unwrap(tag));
1183
1295
  }
1184
1296
  /**
1185
- * Determine if a function node is a React Hook based on its name.
1297
+ * Check if the function node is a hook definition based on its name.
1186
1298
  * @param node The function node to check.
1187
- * @returns True if the function is a React Hook, false otherwise.
1299
+ * @returns `true` if the function is a hook definition.
1188
1300
  */
1189
1301
  function isHookDefinition(node) {
1190
1302
  if (node == null) return false;
@@ -1196,7 +1308,7 @@ function isHookDefinition(node) {
1196
1308
  }
1197
1309
  }
1198
1310
  /**
1199
- * Check if the given node is a React Hook call by its name.
1311
+ * Check if the node is a React Hook call by its name.
1200
1312
  * @param node The node to check.
1201
1313
  * @returns `true` if the node is a React Hook call, `false` otherwise.
1202
1314
  */
@@ -1208,23 +1320,23 @@ function isHookCall(node) {
1208
1320
  return isHookName(name);
1209
1321
  }
1210
1322
  /**
1211
- * Detect useEffect calls and variations (useLayoutEffect, etc.) using a regex pattern.
1323
+ * Check if the node is a useRef-like call (ex: `useRef` or a custom ref hook).
1212
1324
  * @param node The AST node to check.
1213
- * @param additionalEffectHooks Regex pattern matching custom hooks that should be treated as effect hooks.
1214
- * @returns True if the node is a useEffect-like call.
1325
+ * @param additionalRefHooks Regex pattern matching custom hooks that should be treated as ref hooks.
1326
+ * @returns `true` if the node is a useRef-like call.
1215
1327
  */
1216
- function isUseEffectLikeCall(node, additionalEffectHooks = { test: constFalse }) {
1328
+ function isUseRefLikeCall(node, additionalRefHooks = { test: constFalse }) {
1217
1329
  if (node == null) return false;
1218
1330
  if (node.type !== AST_NODE_TYPES.CallExpression) return false;
1219
1331
  const name = Extract.getCalleeName(node);
1220
1332
  if (name == null) return false;
1221
- return /^use\w*Effect$/u.test(name) || additionalEffectHooks.test(name);
1333
+ return name === "useRef" || additionalRefHooks.test(name);
1222
1334
  }
1223
1335
  /**
1224
- * Detect useState calls and variations using a regex pattern.
1336
+ * Check if the node is a useState-like call (ex: `useState` or a custom state hook).
1225
1337
  * @param node The AST node to check.
1226
1338
  * @param additionalStateHooks Regex pattern matching custom hooks that should be treated as state hooks.
1227
- * @returns True if the node is a useState-like call.
1339
+ * @returns `true` if the node is a useState-like call.
1228
1340
  */
1229
1341
  function isUseStateLikeCall(node, additionalStateHooks = { test: constFalse }) {
1230
1342
  if (node == null) return false;
@@ -1234,8 +1346,22 @@ function isUseStateLikeCall(node, additionalStateHooks = { test: constFalse }) {
1234
1346
  return name === "useState" || additionalStateHooks.test(name);
1235
1347
  }
1236
1348
  /**
1237
- * Determine if a node is the setup function passed to a useEffect-like hook.
1349
+ * Check if the node is a useEffect-like call (ex: `useEffect`, `useLayoutEffect`, or a custom effect hook).
1238
1350
  * @param node The AST node to check.
1351
+ * @param additionalEffectHooks Regex pattern matching custom hooks that should be treated as effect hooks.
1352
+ * @returns `true` if the node is a useEffect-like call.
1353
+ */
1354
+ function isUseEffectLikeCall(node, additionalEffectHooks = { test: constFalse }) {
1355
+ if (node == null) return false;
1356
+ if (node.type !== AST_NODE_TYPES.CallExpression) return false;
1357
+ const name = Extract.getCalleeName(node);
1358
+ if (name == null) return false;
1359
+ return /^use\w*Effect$/u.test(name) || additionalEffectHooks.test(name);
1360
+ }
1361
+ /**
1362
+ * Check if the node is the setup callback passed to a useEffect-like call.
1363
+ * @param node The AST node to check.
1364
+ * @returns `true` if the node is a useEffect setup callback.
1239
1365
  */
1240
1366
  function isUseEffectSetupCallback(node) {
1241
1367
  if (node == null) return false;
@@ -1243,8 +1369,9 @@ function isUseEffectSetupCallback(node) {
1243
1369
  return expr.parent?.type === AST_NODE_TYPES.CallExpression && expr.parent.arguments.at(0) === expr && isUseEffectLikeCall(expr.parent);
1244
1370
  }
1245
1371
  /**
1246
- * Determine if a node is the cleanup function returned by a useEffect-like hook's setup function.
1372
+ * Check if the node is the cleanup callback returned by a useEffect-like setup callback.
1247
1373
  * @param node The AST node to check.
1374
+ * @returns `true` if the node is a useEffect cleanup callback.
1248
1375
  */
1249
1376
  function isUseEffectCleanupCallback(node) {
1250
1377
  if (node == null) return false;
@@ -1520,50 +1647,109 @@ function isFlagSetOnObject(obj, flag) {
1520
1647
  return isFlagSet(obj.flags, flag);
1521
1648
  }
1522
1649
  const isTypeFlagSet = isFlagSetOnObject;
1650
+ /**
1651
+ * Check if the type is a boolean literal type.
1652
+ * @param type The type to check.
1653
+ * @returns `true` if the type is a boolean literal type.
1654
+ */
1523
1655
  function isBooleanLiteralType(type) {
1524
1656
  return isTypeFlagSet(type, ts.TypeFlags.BooleanLiteral);
1525
1657
  }
1526
- /** @internal */
1658
+ /**
1659
+ * Check if the type is the `false` literal type.
1660
+ * @internal
1661
+ */
1527
1662
  const isFalseLiteralType = (type) => isBooleanLiteralType(type) && type.intrinsicName === "false";
1528
- /** @internal */
1663
+ /**
1664
+ * Check if the type is the `true` literal type.
1665
+ * @internal
1666
+ */
1529
1667
  const isTrueLiteralType = (type) => isBooleanLiteralType(type) && type.intrinsicName === "true";
1530
- /** @internal */
1668
+ /**
1669
+ * Check if the type is an any-like type.
1670
+ * @internal
1671
+ */
1531
1672
  const isAnyType = (type) => isTypeFlagSet(type, ts.TypeFlags.TypeParameter | ts.TypeFlags.Any);
1532
- /** @internal */
1673
+ /**
1674
+ * Check if the type is a bigint-like type.
1675
+ * @internal
1676
+ */
1533
1677
  const isBigIntType = (type) => isTypeFlagSet(type, ts.TypeFlags.BigIntLike);
1534
- /** @internal */
1678
+ /**
1679
+ * Check if the type is a boolean-like type.
1680
+ * @internal
1681
+ */
1535
1682
  const isBooleanType = (type) => isTypeFlagSet(type, ts.TypeFlags.BooleanLike);
1536
- /** @internal */
1683
+ /**
1684
+ * Check if the type is an enum-like type.
1685
+ * @internal
1686
+ */
1537
1687
  const isEnumType = (type) => isTypeFlagSet(type, ts.TypeFlags.EnumLike);
1538
- /** @internal */
1688
+ /**
1689
+ * Check if the type is a falsy bigint literal type.
1690
+ * @internal
1691
+ */
1539
1692
  const isFalsyBigIntType = (type) => type.isLiteral() && isMatching({ value: { base10Value: "0" } }, type);
1540
- /** @internal */
1693
+ /**
1694
+ * Check if the type is a falsy number literal type.
1695
+ * @internal
1696
+ */
1541
1697
  const isFalsyNumberType = (type) => type.isNumberLiteral() && type.value === 0;
1542
- /** @internal */
1698
+ /**
1699
+ * Check if the type is a falsy string literal type.
1700
+ * @internal
1701
+ */
1543
1702
  const isFalsyStringType = (type) => type.isStringLiteral() && type.value === "";
1544
- /** @internal */
1703
+ /**
1704
+ * Check if the type is the never type.
1705
+ * @internal
1706
+ */
1545
1707
  const isNeverType = (type) => isTypeFlagSet(type, ts.TypeFlags.Never);
1546
- /** @internal */
1708
+ /**
1709
+ * Check if the type is a nullish type (null, undefined, or void).
1710
+ * @internal
1711
+ */
1547
1712
  const isNullishType = (type) => isTypeFlagSet(type, ts.TypeFlags.Null | ts.TypeFlags.Undefined | ts.TypeFlags.VoidLike);
1548
- /** @internal */
1713
+ /**
1714
+ * Check if the type is a number-like type.
1715
+ * @internal
1716
+ */
1549
1717
  const isNumberType = (type) => isTypeFlagSet(type, ts.TypeFlags.NumberLike);
1550
- /** @internal */
1718
+ /**
1719
+ * Check if the type is an object type.
1720
+ * @internal
1721
+ */
1551
1722
  const isObjectType = (type) => !isTypeFlagSet(type, ts.TypeFlags.Null | ts.TypeFlags.Undefined | ts.TypeFlags.VoidLike | ts.TypeFlags.BooleanLike | ts.TypeFlags.StringLike | ts.TypeFlags.NumberLike | ts.TypeFlags.BigIntLike | ts.TypeFlags.TypeParameter | ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.Never);
1552
- /** @internal */
1723
+ /**
1724
+ * Check if the type is a string-like type.
1725
+ * @internal
1726
+ */
1553
1727
  const isStringType = (type) => isTypeFlagSet(type, ts.TypeFlags.StringLike);
1554
- /** @internal */
1728
+ /**
1729
+ * Check if the type is a truthy bigint literal type.
1730
+ * @internal
1731
+ */
1555
1732
  const isTruthyBigIntType = (type) => type.isLiteral() && isMatching({ value: { base10Value: P.not("0") } }, type);
1556
- /** @internal */
1733
+ /**
1734
+ * Check if the type is a truthy number literal type.
1735
+ * @internal
1736
+ */
1557
1737
  const isTruthyNumberType = (type) => type.isNumberLiteral() && type.value !== 0;
1558
- /** @internal */
1738
+ /**
1739
+ * Check if the type is a truthy string literal type.
1740
+ * @internal
1741
+ */
1559
1742
  const isTruthyStringType = (type) => type.isStringLiteral() && type.value !== "";
1560
- /** @internal */
1743
+ /**
1744
+ * Check if the type is the unknown type.
1745
+ * @internal
1746
+ */
1561
1747
  const isUnknownType = (type) => isTypeFlagSet(type, ts.TypeFlags.Unknown);
1562
1748
 
1563
1749
  //#endregion
1564
1750
  //#region src/type-name.ts
1565
1751
  /**
1566
- * An enhanced version of getFullyQualifiedName that handles cases that original function does not handle.
1752
+ * Get the fully qualified name of a symbol, handling cases that `ts.TypeChecker.getFullyQualifiedName` does not handle (ex: `export as namespace preact`).
1567
1753
  * @param checker The TypeScript type checker.
1568
1754
  * @param symbol The symbol to get fully qualified name for.
1569
1755
  * @returns The fully qualified name of the symbol.
@@ -1616,10 +1802,10 @@ function getFullyQualifiedNameEx(checker, symbol) {
1616
1802
  //#endregion
1617
1803
  //#region src/type-variant.ts
1618
1804
  /**
1619
- * Ported from https://github.com/typescript-eslint/typescript-eslint/blob/eb736bbfc22554694400e6a4f97051d845d32e0b/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts#L826 with some enhancements.
1620
1805
  * Get the variants of an array of types.
1621
1806
  * @param types The types to get the variants of.
1622
1807
  * @returns The variants of the types.
1808
+ * @remarks Ported from https://github.com/typescript-eslint/typescript-eslint/blob/eb736bbfc22554694400e6a4f97051d845d32e0b/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts#L826 with some enhancements.
1623
1809
  * @internal
1624
1810
  */
1625
1811
  function getTypeVariants(types) {
@@ -1658,4 +1844,4 @@ function getTypeVariants(types) {
1658
1844
  }
1659
1845
 
1660
1846
  //#endregion
1661
- export { DEFAULT_COMPONENT_DETECTION_HINT, DEFAULT_JSX_DETECTION_HINT, FunctionComponentDetectionHint, FunctionComponentFlag, JsxDetectionHint, REACT_BUILTIN_HOOK_NAMES, SEL_FUNCTION_DISPLAY_NAME_ASSIGNMENT, getClassComponentCollector, getClassId, getFullyQualifiedNameEx, getFunctionComponentCollector, getFunctionComponentFlagFromInitPath, getFunctionComponentId, getFunctionDirectives, getFunctionId, getFunctionInitPath, getHookCollector, getJsxConfig, getJsxConfigFromAnnotation, getJsxConfigFromCompilerOptions, getTypeVariants, isAPI, isAPICall, isAnyType, isAssignmentToThisState, isBigIntType, isBooleanLiteralType, isBooleanType, isCaptureOwnerStack, isCaptureOwnerStackCall, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isCloneElement, isCloneElementCall, isComponentDidCatch, isComponentDidMount, isComponentDidUpdate, isComponentWillMount, isComponentWillReceiveProps, isComponentWillUnmount, isComponentWillUpdate, isCreateContext, isCreateContextCall, isCreateElement, isCreateElementCall, isCreateRef, isCreateRefCall, isEnumType, isFalseLiteralType, isFalsyBigIntType, isFalsyNumberType, isFalsyStringType, isForwardRef, isForwardRefCall, isFunctionComponentDefinition, isFunctionComponentName, isFunctionComponentNameLoose, isFunctionComponentWrapperCall, isFunctionComponentWrapperCallback, isFunctionEmpty, isFunctionHasCallInInitPath, isFunctionHasDirective, isFunctionWithLooseComponentName, isGetChildContext, isGetDefaultProps, isGetDerivedStateFromError, isGetDerivedStateFromProps, isGetInitialState, isGetSnapshotBeforeUpdate, isHookCall, isHookDefinition, isHookId, isHookName, isHookTag, isJsxLike, isLazy, isLazyCall, isMemo, isMemoCall, isNeverType, isNullishType, isNumberType, isObjectType, isPureComponent, isRender, isRenderMethodCallback, isRenderMethodLike, isShouldComponentUpdate, isStringType, isThisSetStateCall, isTrueLiteralType, isTruthyBigIntType, isTruthyNumberType, isTruthyStringType, isUnknownType, isUnsafeComponentWillMount, isUnsafeComponentWillReceiveProps, isUnsafeComponentWillUpdate, isUse, isUseActionState, isUseActionStateCall, isUseCall, isUseCallback, isUseCallbackCall, isUseContext, isUseContextCall, isUseDebugValue, isUseDebugValueCall, isUseDeferredValue, isUseDeferredValueCall, isUseEffect, isUseEffectCall, isUseEffectCleanupCallback, isUseEffectLikeCall, isUseEffectSetupCallback, isUseFormStatus, isUseFormStatusCall, isUseId, isUseIdCall, isUseImperativeHandle, isUseImperativeHandleCall, isUseInsertionEffect, isUseInsertionEffectCall, isUseLayoutEffect, isUseLayoutEffectCall, isUseMemo, isUseMemoCall, isUseOptimistic, isUseOptimisticCall, isUseReducer, isUseReducerCall, isUseRef, isUseRefCall, isUseState, isUseStateCall, isUseStateLikeCall, isUseSyncExternalStore, isUseSyncExternalStoreCall, isUseTransition, isUseTransitionCall };
1847
+ export { DEFAULT_COMPONENT_DETECTION_HINT, DEFAULT_JSX_DETECTION_HINT, FunctionComponentDetectionHint, FunctionComponentFlag, JsxDetectionHint, REACT_BUILTIN_HOOK_NAMES, SEL_FUNCTION_DISPLAY_NAME_ASSIGNMENT, getClassComponentCollector, getClassId, getFullyQualifiedNameEx, getFunctionComponentCollector, getFunctionComponentFlagFromInitPath, getFunctionComponentId, getFunctionDirectives, getFunctionId, getFunctionInitPath, getHookCollector, getJsxConfig, getJsxConfigFromAnnotation, getJsxConfigFromCompilerOptions, getTypeVariants, isAPI, isAPICall, isAnyType, isAssignmentToThisState, isBigIntType, isBooleanLiteralType, isBooleanType, isCaptureOwnerStack, isCaptureOwnerStackCall, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isCloneElement, isCloneElementCall, isComponentDidCatch, isComponentDidMount, isComponentDidUpdate, isComponentWillMount, isComponentWillReceiveProps, isComponentWillUnmount, isComponentWillUpdate, isCreateContext, isCreateContextCall, isCreateElement, isCreateElementCall, isCreateRef, isCreateRefCall, isEnumType, isFalseLiteralType, isFalsyBigIntType, isFalsyNumberType, isFalsyStringType, isForwardRef, isForwardRefCall, isFunctionComponentDefinition, isFunctionComponentName, isFunctionComponentNameLoose, isFunctionComponentWrapperCall, isFunctionComponentWrapperCallback, isFunctionEmpty, isFunctionHasCallInInitPath, isFunctionHasDirective, isFunctionWithLooseComponentName, isGetChildContext, isGetDefaultProps, isGetDerivedStateFromError, isGetDerivedStateFromProps, isGetInitialState, isGetSnapshotBeforeUpdate, isHookCall, isHookDefinition, isHookId, isHookName, isHookTag, isJsxLike, isLazy, isLazyCall, isMemo, isMemoCall, isNeverType, isNullishType, isNumberType, isObjectType, isPureComponent, isRender, isRenderMethodCallback, isRenderMethodLike, isShouldComponentUpdate, isStringType, isThisSetStateCall, isTrueLiteralType, isTruthyBigIntType, isTruthyNumberType, isTruthyStringType, isUnknownType, isUnsafeComponentWillMount, isUnsafeComponentWillReceiveProps, isUnsafeComponentWillUpdate, isUse, isUseActionState, isUseActionStateCall, isUseCall, isUseCallback, isUseCallbackCall, isUseContext, isUseContextCall, isUseDebugValue, isUseDebugValueCall, isUseDeferredValue, isUseDeferredValueCall, isUseEffect, isUseEffectCall, isUseEffectCleanupCallback, isUseEffectLikeCall, isUseEffectSetupCallback, isUseFormStatus, isUseFormStatusCall, isUseId, isUseIdCall, isUseImperativeHandle, isUseImperativeHandleCall, isUseInsertionEffect, isUseInsertionEffectCall, isUseLayoutEffect, isUseLayoutEffectCall, isUseMemo, isUseMemoCall, isUseOptimistic, isUseOptimisticCall, isUseReducer, isUseReducerCall, isUseRef, isUseRefCall, isUseRefLikeCall, isUseState, isUseStateCall, isUseStateLikeCall, isUseSyncExternalStore, isUseSyncExternalStoreCall, isUseTransition, isUseTransitionCall };