@eslint-react/core 2.0.0-next.171 → 2.0.0-next.173

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
@@ -369,54 +369,46 @@ declare function isReactHook(node: AST.TSESTreeFunction | unit): boolean;
369
369
  * @param node The node to check.
370
370
  * @returns `true` if the node is a React Hook call, `false` otherwise.
371
371
  */
372
- declare function isReactHookCall(node: TSESTree.Node | unit): boolean;
372
+ declare function isReactHookCall(node: TSESTree.Node | unit): node is TSESTree.CallExpression;
373
373
  /**
374
- * Checks if a node is a call to a specific React hook, with React import validation.
374
+ * Checks if a node is a call to a specific React hook.
375
375
  * Returns a function that accepts a hook name to check against.
376
- * @param context The rule context
377
- * @param node The AST node to check
378
- * @returns A function that takes a hook name and returns boolean
379
- */
380
- declare function isReactHookCallWithName(context: RuleContext, node: TSESTree.Node | unit): (name: string) => boolean;
381
- /**
382
- * Lightweight version of isReactHookCallWithName that doesn't check imports.
383
376
  * @param node The AST node to check
384
377
  * @returns A function that takes a hook name and returns boolean
385
378
  */
386
- declare function isReactHookCallWithNameLoose(node: TSESTree.Node | unit): (name: string) => boolean;
379
+ declare function isReactHookCallWithName(node: TSESTree.Node | unit): (name: string) => boolean;
387
380
  /**
388
381
  * Checks if a node is a call to a specific React hook or one of its aliases.
389
- * @param context The rule context
390
382
  * @param name The primary hook name to check
391
383
  * @param alias Optional array of alias names to also accept
392
384
  * @returns Function that checks if a node matches the hook name or aliases
393
385
  */
394
- declare function isReactHookCallWithNameAlias(context: RuleContext, name: string, alias?: unit | string[]): (node: TSESTree.CallExpression) => boolean;
386
+ declare function isReactHookCallWithNameAlias(name: string, alias?: unit | string[]): (node: TSESTree.CallExpression) => boolean;
395
387
  /**
396
388
  * Detects useEffect calls and variations (useLayoutEffect, etc.) using regex pattern.
397
389
  * @param node The AST node to check
398
390
  * @returns True if the node is a useEffect-like call
399
391
  */
400
- declare function isUseEffectCallLoose(node: TSESTree.Node | unit): boolean;
401
- declare const isUseCall: (context: RuleContext, node: TSESTree.Node | undefined) => boolean;
402
- declare const isUseActionStateCall: (context: RuleContext, node: TSESTree.Node | undefined) => boolean;
403
- declare const isUseCallbackCall: (context: RuleContext, node: TSESTree.Node | undefined) => boolean;
404
- declare const isUseContextCall: (context: RuleContext, node: TSESTree.Node | undefined) => boolean;
405
- declare const isUseDebugValueCall: (context: RuleContext, node: TSESTree.Node | undefined) => boolean;
406
- declare const isUseDeferredValueCall: (context: RuleContext, node: TSESTree.Node | undefined) => boolean;
407
- declare const isUseEffectCall: (context: RuleContext, node: TSESTree.Node | undefined) => boolean;
408
- declare const isUseFormStatusCall: (context: RuleContext, node: TSESTree.Node | undefined) => boolean;
409
- declare const isUseIdCall: (context: RuleContext, node: TSESTree.Node | undefined) => boolean;
410
- declare const isUseImperativeHandleCall: (context: RuleContext, node: TSESTree.Node | undefined) => boolean;
411
- declare const isUseInsertionEffectCall: (context: RuleContext, node: TSESTree.Node | undefined) => boolean;
412
- declare const isUseLayoutEffectCall: (context: RuleContext, node: TSESTree.Node | undefined) => boolean;
413
- declare const isUseMemoCall: (context: RuleContext, node: TSESTree.Node | undefined) => boolean;
414
- declare const isUseOptimisticCall: (context: RuleContext, node: TSESTree.Node | undefined) => boolean;
415
- declare const isUseReducerCall: (context: RuleContext, node: TSESTree.Node | undefined) => boolean;
416
- declare const isUseRefCall: (context: RuleContext, node: TSESTree.Node | undefined) => boolean;
417
- declare const isUseStateCall: (context: RuleContext, node: TSESTree.Node | undefined) => boolean;
418
- declare const isUseSyncExternalStoreCall: (context: RuleContext, node: TSESTree.Node | undefined) => boolean;
419
- declare const isUseTransitionCall: (context: RuleContext, node: TSESTree.Node | undefined) => boolean;
392
+ declare function isUseEffectLikeCall(node: TSESTree.Node | unit): boolean;
393
+ declare const isUseCall: (node: TSESTree.Node | undefined) => boolean;
394
+ declare const isUseActionStateCall: (node: TSESTree.Node | undefined) => boolean;
395
+ declare const isUseCallbackCall: (node: TSESTree.Node | undefined) => boolean;
396
+ declare const isUseContextCall: (node: TSESTree.Node | undefined) => boolean;
397
+ declare const isUseDebugValueCall: (node: TSESTree.Node | undefined) => boolean;
398
+ declare const isUseDeferredValueCall: (node: TSESTree.Node | undefined) => boolean;
399
+ declare const isUseEffectCall: (node: TSESTree.Node | undefined) => boolean;
400
+ declare const isUseFormStatusCall: (node: TSESTree.Node | undefined) => boolean;
401
+ declare const isUseIdCall: (node: TSESTree.Node | undefined) => boolean;
402
+ declare const isUseImperativeHandleCall: (node: TSESTree.Node | undefined) => boolean;
403
+ declare const isUseInsertionEffectCall: (node: TSESTree.Node | undefined) => boolean;
404
+ declare const isUseLayoutEffectCall: (node: TSESTree.Node | undefined) => boolean;
405
+ declare const isUseMemoCall: (node: TSESTree.Node | undefined) => boolean;
406
+ declare const isUseOptimisticCall: (node: TSESTree.Node | undefined) => boolean;
407
+ declare const isUseReducerCall: (node: TSESTree.Node | undefined) => boolean;
408
+ declare const isUseRefCall: (node: TSESTree.Node | undefined) => boolean;
409
+ declare const isUseStateCall: (node: TSESTree.Node | undefined) => boolean;
410
+ declare const isUseSyncExternalStoreCall: (node: TSESTree.Node | undefined) => boolean;
411
+ declare const isUseTransitionCall: (node: TSESTree.Node | undefined) => boolean;
420
412
  //#endregion
421
413
  //#region src/hook/hook-name.d.ts
422
414
  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"];
@@ -748,7 +740,7 @@ declare function getTypeVariants(types: ts.Type[]): Set<TypeVariant>;
748
740
  * @param prev The previous AST node in the traversal (used for context)
749
741
  * @internal
750
742
  */
751
- declare function getInstanceId(node: TSESTree.Node, prev?: TSESTree.Node): TSESTree.JSXElement | TSESTree.BigIntLiteral | TSESTree.BooleanLiteral | TSESTree.NullLiteral | TSESTree.NumberLiteral | TSESTree.RegExpLiteral | TSESTree.StringLiteral | TSESTree.ArrayExpression | TSESTree.ArrayPattern | TSESTree.ArrowFunctionExpression | TSESTree.AssignmentExpression | TSESTree.AwaitExpression | TSESTree.BinaryExpression | TSESTree.CallExpression | TSESTree.ChainExpression | TSESTree.ClassExpression | TSESTree.ConditionalExpression | TSESTree.FunctionExpression | TSESTree.Identifier | TSESTree.ImportExpression | TSESTree.JSXFragment | TSESTree.TemplateLiteral | TSESTree.LogicalExpression | TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedName | TSESTree.MetaProperty | TSESTree.NewExpression | TSESTree.ObjectExpression | TSESTree.ObjectPattern | TSESTree.SequenceExpression | TSESTree.Super | TSESTree.TaggedTemplateExpression | TSESTree.ThisExpression | TSESTree.TSAsExpression | TSESTree.TSInstantiationExpression | TSESTree.TSNonNullExpression | TSESTree.TSSatisfiesExpression | TSESTree.TSTypeAssertion | TSESTree.UnaryExpression | TSESTree.UpdateExpression | TSESTree.YieldExpression | TSESTree.PrivateIdentifier | undefined;
743
+ declare function getInstanceId(node: TSESTree.Node, prev?: TSESTree.Node): TSESTree.ArrayExpression | TSESTree.ArrayPattern | TSESTree.ArrowFunctionExpression | TSESTree.AssignmentExpression | TSESTree.AwaitExpression | TSESTree.BinaryExpression | TSESTree.CallExpression | TSESTree.ChainExpression | TSESTree.ClassExpression | TSESTree.ConditionalExpression | TSESTree.FunctionExpression | TSESTree.Identifier | TSESTree.ImportExpression | TSESTree.JSXElement | TSESTree.JSXFragment | TSESTree.BigIntLiteral | TSESTree.BooleanLiteral | TSESTree.NullLiteral | TSESTree.NumberLiteral | TSESTree.RegExpLiteral | TSESTree.StringLiteral | TSESTree.TemplateLiteral | TSESTree.LogicalExpression | TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedName | TSESTree.MetaProperty | TSESTree.NewExpression | TSESTree.ObjectExpression | TSESTree.ObjectPattern | TSESTree.SequenceExpression | TSESTree.Super | TSESTree.TaggedTemplateExpression | TSESTree.ThisExpression | TSESTree.TSAsExpression | TSESTree.TSInstantiationExpression | TSESTree.TSNonNullExpression | TSESTree.TSSatisfiesExpression | TSESTree.TSTypeAssertion | TSESTree.UnaryExpression | TSESTree.UpdateExpression | TSESTree.YieldExpression | TSESTree.PrivateIdentifier | undefined;
752
744
  //#endregion
753
745
  //#region src/utils/is-from-react.d.ts
754
746
  /**
@@ -807,4 +799,4 @@ declare const isForwardRefCall: isReactAPICall.ReturnType;
807
799
  declare const isMemoCall: isReactAPICall.ReturnType;
808
800
  declare const isLazyCall: isReactAPICall.ReturnType;
809
801
  //#endregion
810
- export { AttributeValue, ClassComponent, Component, ComponentDetectionHint, ComponentEffectPhaseKind, ComponentFlag, ComponentKind, ComponentLifecyclePhaseKind, ComponentPhaseKind, ComponentPhaseRelevance, ComponentStateKind, DEFAULT_COMPONENT_DETECTION_HINT, DEFAULT_JSX_DETECTION_HINT, EffectKind, FunctionComponent, Hook, JSXDetectionHint, REACT_BUILTIN_HOOK_NAMES, SemanticEntry, SemanticNode, TypeVariant, findParentAttribute, getAttribute, getAttributeName, getComponentFlagFromInitPath, getComponentNameFromId, getElementType, getFunctionComponentId, getInstanceId, getTypeVariants, hasAnyAttribute, hasAttribute, hasEveryAttribute, hasNoneOrLooseComponentName, isAnyType, isAssignmentToThisState, isBigIntType, isBooleanType, isCaptureOwnerStack, isCaptureOwnerStackCall, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOfCreateElement, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isCloneElement, isCloneElementCall, isComponentDefinition, isComponentDidCatch, isComponentDidMount, isComponentDidUpdate, isComponentName, isComponentNameLoose, isComponentWillMount, isComponentWillReceiveProps, isComponentWillUnmount, isComponentWillUpdate, isComponentWrapperCall, isComponentWrapperCallLoose, isCreateContext, isCreateContextCall, isCreateElement, isCreateElementCall, isCreateRef, isCreateRefCall, isDeclaredInRenderPropLoose, isDirectValueOfRenderPropertyLoose, isEnumType, isFalseLiteralType, isFalsyBigIntType, isFalsyNumberType, isFalsyStringType, isForwardRef, isForwardRefCall, isFragmentElement, isFunctionOfRenderMethod, isGetChildContext, isGetDefaultProps, isGetDerivedStateFromError, isGetDerivedStateFromProps, isGetInitialState, isGetSnapshotBeforeUpdate, isHostElement, isInitializedFromReact, isInstanceIdEqual, isInversePhase, isJsxLike, isJsxText, isLazy, isLazyCall, isMemo, isMemoCall, isNeverType, isNullishType, isNumberType, isObjectType, isPureComponent, isReactAPI, isReactAPICall, isReactHook, isReactHookCall, isReactHookCallWithName, isReactHookCallWithNameAlias, isReactHookCallWithNameLoose, isReactHookId, isReactHookName, isRender, isRenderFunctionLoose, isRenderMethodLike, isRenderPropLoose, isShouldComponentUpdate, isStringType, isThisSetState, isTrueLiteralType, isTruthyBigIntType, isTruthyNumberType, isTruthyStringType, isUnknownType, isUnsafeComponentWillMount, isUnsafeComponentWillReceiveProps, isUnsafeComponentWillUpdate, isUseActionStateCall, isUseCall, isUseCallbackCall, isUseContextCall, isUseDebugValueCall, isUseDeferredValueCall, isUseEffectCall, isUseEffectCallLoose, isUseFormStatusCall, isUseIdCall, isUseImperativeHandleCall, isUseInsertionEffectCall, isUseLayoutEffectCall, isUseMemoCall, isUseOptimisticCall, isUseReducerCall, isUseRefCall, isUseStateCall, isUseSyncExternalStoreCall, isUseTransitionCall, resolveAttributeValue, stringifyJsx, useComponentCollector, useComponentCollectorLegacy, useHookCollector };
802
+ export { AttributeValue, ClassComponent, Component, ComponentDetectionHint, ComponentEffectPhaseKind, ComponentFlag, ComponentKind, ComponentLifecyclePhaseKind, ComponentPhaseKind, ComponentPhaseRelevance, ComponentStateKind, DEFAULT_COMPONENT_DETECTION_HINT, DEFAULT_JSX_DETECTION_HINT, EffectKind, FunctionComponent, Hook, JSXDetectionHint, REACT_BUILTIN_HOOK_NAMES, SemanticEntry, SemanticNode, TypeVariant, findParentAttribute, getAttribute, getAttributeName, getComponentFlagFromInitPath, getComponentNameFromId, getElementType, getFunctionComponentId, getInstanceId, getTypeVariants, hasAnyAttribute, hasAttribute, hasEveryAttribute, hasNoneOrLooseComponentName, isAnyType, isAssignmentToThisState, isBigIntType, isBooleanType, isCaptureOwnerStack, isCaptureOwnerStackCall, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOfCreateElement, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isCloneElement, isCloneElementCall, isComponentDefinition, isComponentDidCatch, isComponentDidMount, isComponentDidUpdate, isComponentName, isComponentNameLoose, isComponentWillMount, isComponentWillReceiveProps, isComponentWillUnmount, isComponentWillUpdate, isComponentWrapperCall, isComponentWrapperCallLoose, isCreateContext, isCreateContextCall, isCreateElement, isCreateElementCall, isCreateRef, isCreateRefCall, isDeclaredInRenderPropLoose, isDirectValueOfRenderPropertyLoose, isEnumType, isFalseLiteralType, isFalsyBigIntType, isFalsyNumberType, isFalsyStringType, isForwardRef, isForwardRefCall, isFragmentElement, isFunctionOfRenderMethod, isGetChildContext, isGetDefaultProps, isGetDerivedStateFromError, isGetDerivedStateFromProps, isGetInitialState, isGetSnapshotBeforeUpdate, isHostElement, isInitializedFromReact, isInstanceIdEqual, isInversePhase, isJsxLike, isJsxText, isLazy, isLazyCall, isMemo, isMemoCall, isNeverType, isNullishType, isNumberType, isObjectType, isPureComponent, isReactAPI, isReactAPICall, isReactHook, isReactHookCall, isReactHookCallWithName, isReactHookCallWithNameAlias, isReactHookId, isReactHookName, isRender, isRenderFunctionLoose, isRenderMethodLike, isRenderPropLoose, isShouldComponentUpdate, isStringType, isThisSetState, isTrueLiteralType, isTruthyBigIntType, isTruthyNumberType, isTruthyStringType, isUnknownType, isUnsafeComponentWillMount, isUnsafeComponentWillReceiveProps, isUnsafeComponentWillUpdate, isUseActionStateCall, isUseCall, isUseCallbackCall, isUseContextCall, isUseDebugValueCall, isUseDeferredValueCall, isUseEffectCall, isUseEffectLikeCall, isUseFormStatusCall, isUseIdCall, isUseImperativeHandleCall, isUseInsertionEffectCall, isUseLayoutEffectCall, isUseMemoCall, isUseOptimisticCall, isUseReducerCall, isUseRefCall, isUseStateCall, isUseSyncExternalStoreCall, isUseTransitionCall, resolveAttributeValue, stringifyJsx, useComponentCollector, useComponentCollectorLegacy, useHookCollector };
package/dist/index.js CHANGED
@@ -2,10 +2,10 @@ import { AST_NODE_TYPES } from "@typescript-eslint/types";
2
2
  import * as AST from "@eslint-react/ast";
3
3
  import { constFalse, constTrue, dual, flip, identity, unit } from "@eslint-react/eff";
4
4
  import { RegExp, Selector } from "@eslint-react/kit";
5
- import { DEFAULT_ESLINT_REACT_SETTINGS, coerceSettings, getId } from "@eslint-react/shared";
5
+ import { getId } from "@eslint-react/shared";
6
6
  import * as VAR from "@eslint-react/var";
7
- import { P, isMatching, match } from "ts-pattern";
8
7
  import { getStaticValue } from "@typescript-eslint/utils/ast-utils";
8
+ import { P, isMatching, match } from "ts-pattern";
9
9
  import birecord from "birecord";
10
10
  import { isFalseLiteralType, isTrueLiteralType, isTypeFlagSet } from "ts-api-utils";
11
11
  import ts from "typescript";
@@ -72,77 +72,6 @@ function isChildrenOfCreateElement(context, node) {
72
72
  return parent.arguments.slice(2).some((arg) => arg === node);
73
73
  }
74
74
 
75
- //#endregion
76
- //#region src/utils/get-instance-id.ts
77
- /**
78
- * Gets the identifier node of an instance based on AST node relationships.
79
- * Used for tracking where hooks or components are being assigned in the code.
80
- * @param node The current AST node to evaluate
81
- * @param prev The previous AST node in the traversal (used for context)
82
- * @internal
83
- */
84
- function getInstanceId(node, prev) {
85
- switch (true) {
86
- case node.type === AST_NODE_TYPES.VariableDeclarator && node.init === prev: return node.id;
87
- case node.type === AST_NODE_TYPES.AssignmentExpression && node.right === prev: return node.left;
88
- case node.type === AST_NODE_TYPES.PropertyDefinition && node.value === prev: return node.key;
89
- case node.type === AST_NODE_TYPES.BlockStatement || node.type === AST_NODE_TYPES.Program || node.parent === node: return unit;
90
- default: return getInstanceId(node.parent, node);
91
- }
92
- }
93
-
94
- //#endregion
95
- //#region src/utils/is-from-react.ts
96
- /**
97
- * Get the arguments of a require expression
98
- * @param node The node to match
99
- * @returns The require expression arguments or undefined if the node is not a require expression
100
- */
101
- function getRequireExpressionArguments(node) {
102
- return match(node).with({
103
- type: AST_NODE_TYPES.CallExpression,
104
- arguments: P.select(),
105
- callee: {
106
- type: AST_NODE_TYPES.Identifier,
107
- name: "require"
108
- }
109
- }, identity).with({
110
- type: AST_NODE_TYPES.MemberExpression,
111
- object: P.select()
112
- }, getRequireExpressionArguments).otherwise(() => null);
113
- }
114
- /**
115
- * Check if an identifier name is initialized from react
116
- * @param name The top-level identifier's name
117
- * @param importSource The import source to check against
118
- * @param initialScope Initial scope to search for the identifier
119
- * @returns Whether the identifier name is initialized from react
120
- * @internal
121
- */
122
- function isInitializedFromReact(name, importSource, initialScope) {
123
- if (name.toLowerCase() === "react") return true;
124
- const latestDef = VAR.findVariable(name, initialScope)?.defs.at(-1);
125
- if (latestDef == null) return false;
126
- const { node, parent } = latestDef;
127
- if (node.type === AST_NODE_TYPES.VariableDeclarator && node.init != null) {
128
- const { init } = node;
129
- if (init.type === AST_NODE_TYPES.MemberExpression && init.object.type === AST_NODE_TYPES.Identifier) return isInitializedFromReact(init.object.name, importSource, initialScope);
130
- if (init.type === AST_NODE_TYPES.Identifier) return isInitializedFromReact(init.name, importSource, initialScope);
131
- const args = getRequireExpressionArguments(init);
132
- const arg0 = args?.[0];
133
- if (arg0 == null || !AST.isLiteral(arg0, "string")) return false;
134
- return arg0.value === importSource || arg0.value.startsWith(`${importSource}/`);
135
- }
136
- return parent?.type === AST_NODE_TYPES.ImportDeclaration && parent.source.value === importSource;
137
- }
138
-
139
- //#endregion
140
- //#region src/utils/is-instance-id-equal.ts
141
- /** @internal */
142
- function isInstanceIdEqual(context, a, b) {
143
- return AST.isNodeEqual(a, b) || VAR.isNodeValueEqual(a, b, [context.sourceCode.getScope(a), context.sourceCode.getScope(b)]);
144
- }
145
-
146
75
  //#endregion
147
76
  //#region src/hook/hook-name.ts
148
77
  const REACT_BUILTIN_HOOK_NAMES = [
@@ -201,30 +130,12 @@ function isReactHookCall(node) {
201
130
  return false;
202
131
  }
203
132
  /**
204
- * Checks if a node is a call to a specific React hook, with React import validation.
133
+ * Checks if a node is a call to a specific React hook.
205
134
  * Returns a function that accepts a hook name to check against.
206
- * @param context The rule context
207
135
  * @param node The AST node to check
208
136
  * @returns A function that takes a hook name and returns boolean
209
137
  */
210
- function isReactHookCallWithName(context, node) {
211
- if (node == null || node.type !== AST_NODE_TYPES.CallExpression) return constFalse;
212
- const { importSource = DEFAULT_ESLINT_REACT_SETTINGS.importSource, skipImportCheck = true } = coerceSettings(context.settings);
213
- const initialScope = context.sourceCode.getScope(node);
214
- return (name) => {
215
- switch (true) {
216
- case node.callee.type === AST_NODE_TYPES.Identifier && node.callee.name === name: return skipImportCheck || isInitializedFromReact(name, importSource, initialScope);
217
- case node.callee.type === AST_NODE_TYPES.MemberExpression && node.callee.property.type === AST_NODE_TYPES.Identifier && node.callee.property.name === name && "name" in node.callee.object: return skipImportCheck || isInitializedFromReact(node.callee.object.name, importSource, initialScope);
218
- default: return false;
219
- }
220
- };
221
- }
222
- /**
223
- * Lightweight version of isReactHookCallWithName that doesn't check imports.
224
- * @param node The AST node to check
225
- * @returns A function that takes a hook name and returns boolean
226
- */
227
- function isReactHookCallWithNameLoose(node) {
138
+ function isReactHookCallWithName(node) {
228
139
  if (node == null || node.type !== AST_NODE_TYPES.CallExpression) return constFalse;
229
140
  return (name) => {
230
141
  switch (node.callee.type) {
@@ -236,19 +147,16 @@ function isReactHookCallWithNameLoose(node) {
236
147
  }
237
148
  /**
238
149
  * Checks if a node is a call to a specific React hook or one of its aliases.
239
- * @param context The rule context
240
150
  * @param name The primary hook name to check
241
151
  * @param alias Optional array of alias names to also accept
242
152
  * @returns Function that checks if a node matches the hook name or aliases
243
153
  */
244
- function isReactHookCallWithNameAlias(context, name, alias = []) {
245
- const { importSource = DEFAULT_ESLINT_REACT_SETTINGS.importSource, skipImportCheck = true } = coerceSettings(context.settings);
154
+ function isReactHookCallWithNameAlias(name, alias = []) {
246
155
  return (node) => {
247
- const initialScope = context.sourceCode.getScope(node);
248
156
  switch (true) {
249
- case node.callee.type === AST_NODE_TYPES.Identifier && node.callee.name === name: return skipImportCheck || isInitializedFromReact(name, importSource, initialScope);
250
- case node.callee.type === AST_NODE_TYPES.MemberExpression && node.callee.property.type === AST_NODE_TYPES.Identifier && node.callee.property.name === name && "name" in node.callee.object: return skipImportCheck || isInitializedFromReact(node.callee.object.name, importSource, initialScope);
251
- default: return alias.some(isReactHookCallWithNameLoose(node));
157
+ case node.callee.type === AST_NODE_TYPES.Identifier && node.callee.name === name: return true;
158
+ case node.callee.type === AST_NODE_TYPES.MemberExpression && node.callee.property.type === AST_NODE_TYPES.Identifier && node.callee.property.name === name && "name" in node.callee.object: return true;
159
+ default: return alias.some(isReactHookCallWithName(node));
252
160
  }
253
161
  };
254
162
  }
@@ -257,7 +165,7 @@ function isReactHookCallWithNameAlias(context, name, alias = []) {
257
165
  * @param node The AST node to check
258
166
  * @returns True if the node is a useEffect-like call
259
167
  */
260
- function isUseEffectCallLoose(node) {
168
+ function isUseEffectLikeCall(node) {
261
169
  if (node == null) return false;
262
170
  if (node.type !== AST_NODE_TYPES.CallExpression) return false;
263
171
  switch (node.callee.type) {
@@ -813,6 +721,77 @@ function isComponentDefinition(context, node, hint) {
813
721
  return significantParent == null || significantParent.type !== AST_NODE_TYPES.JSXExpressionContainer;
814
722
  }
815
723
 
724
+ //#endregion
725
+ //#region src/utils/get-instance-id.ts
726
+ /**
727
+ * Gets the identifier node of an instance based on AST node relationships.
728
+ * Used for tracking where hooks or components are being assigned in the code.
729
+ * @param node The current AST node to evaluate
730
+ * @param prev The previous AST node in the traversal (used for context)
731
+ * @internal
732
+ */
733
+ function getInstanceId(node, prev) {
734
+ switch (true) {
735
+ case node.type === AST_NODE_TYPES.VariableDeclarator && node.init === prev: return node.id;
736
+ case node.type === AST_NODE_TYPES.AssignmentExpression && node.right === prev: return node.left;
737
+ case node.type === AST_NODE_TYPES.PropertyDefinition && node.value === prev: return node.key;
738
+ case node.type === AST_NODE_TYPES.BlockStatement || node.type === AST_NODE_TYPES.Program || node.parent === node: return unit;
739
+ default: return getInstanceId(node.parent, node);
740
+ }
741
+ }
742
+
743
+ //#endregion
744
+ //#region src/utils/is-from-react.ts
745
+ /**
746
+ * Get the arguments of a require expression
747
+ * @param node The node to match
748
+ * @returns The require expression arguments or undefined if the node is not a require expression
749
+ */
750
+ function getRequireExpressionArguments(node) {
751
+ return match(node).with({
752
+ type: AST_NODE_TYPES.CallExpression,
753
+ arguments: P.select(),
754
+ callee: {
755
+ type: AST_NODE_TYPES.Identifier,
756
+ name: "require"
757
+ }
758
+ }, identity).with({
759
+ type: AST_NODE_TYPES.MemberExpression,
760
+ object: P.select()
761
+ }, getRequireExpressionArguments).otherwise(() => null);
762
+ }
763
+ /**
764
+ * Check if an identifier name is initialized from react
765
+ * @param name The top-level identifier's name
766
+ * @param importSource The import source to check against
767
+ * @param initialScope Initial scope to search for the identifier
768
+ * @returns Whether the identifier name is initialized from react
769
+ * @internal
770
+ */
771
+ function isInitializedFromReact(name, importSource, initialScope) {
772
+ if (name.toLowerCase() === "react") return true;
773
+ const latestDef = VAR.findVariable(name, initialScope)?.defs.at(-1);
774
+ if (latestDef == null) return false;
775
+ const { node, parent } = latestDef;
776
+ if (node.type === AST_NODE_TYPES.VariableDeclarator && node.init != null) {
777
+ const { init } = node;
778
+ if (init.type === AST_NODE_TYPES.MemberExpression && init.object.type === AST_NODE_TYPES.Identifier) return isInitializedFromReact(init.object.name, importSource, initialScope);
779
+ if (init.type === AST_NODE_TYPES.Identifier) return isInitializedFromReact(init.name, importSource, initialScope);
780
+ const args = getRequireExpressionArguments(init);
781
+ const arg0 = args?.[0];
782
+ if (arg0 == null || !AST.isLiteral(arg0, "string")) return false;
783
+ return arg0.value === importSource || arg0.value.startsWith(`${importSource}/`);
784
+ }
785
+ return parent?.type === AST_NODE_TYPES.ImportDeclaration && parent.source.value === importSource;
786
+ }
787
+
788
+ //#endregion
789
+ //#region src/utils/is-instance-id-equal.ts
790
+ /** @internal */
791
+ function isInstanceIdEqual(context, a, b) {
792
+ return AST.isNodeEqual(a, b) || VAR.isNodeValueEqual(a, b, [context.sourceCode.getScope(a), context.sourceCode.getScope(b)]);
793
+ }
794
+
816
795
  //#endregion
817
796
  //#region src/component/component-wrapper.ts
818
797
  /**
@@ -833,7 +812,7 @@ function isComponentWrapperCall(context, node) {
833
812
  */
834
813
  function isComponentWrapperCallLoose(context, node) {
835
814
  if (node.type !== AST_NODE_TYPES.CallExpression) return false;
836
- return isComponentWrapperCall(context, node) || isUseCallbackCall(context, node);
815
+ return isComponentWrapperCall(context, node) || isUseCallbackCall(node);
837
816
  }
838
817
 
839
818
  //#endregion
@@ -1239,4 +1218,4 @@ function getTypeVariants(types) {
1239
1218
  }
1240
1219
 
1241
1220
  //#endregion
1242
- export { ComponentDetectionHint, ComponentFlag, ComponentPhaseRelevance, DEFAULT_COMPONENT_DETECTION_HINT, DEFAULT_JSX_DETECTION_HINT, JSXDetectionHint, REACT_BUILTIN_HOOK_NAMES, findParentAttribute, getAttribute, getAttributeName, getComponentFlagFromInitPath, getComponentNameFromId, getElementType, getFunctionComponentId, getInstanceId, getTypeVariants, hasAnyAttribute, hasAttribute, hasEveryAttribute, hasNoneOrLooseComponentName, isAnyType, isAssignmentToThisState, isBigIntType, isBooleanType, isCaptureOwnerStack, isCaptureOwnerStackCall, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOfCreateElement, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isCloneElement, isCloneElementCall, isComponentDefinition, isComponentDidCatch, isComponentDidMount, isComponentDidUpdate, isComponentName, isComponentNameLoose, isComponentWillMount, isComponentWillReceiveProps, isComponentWillUnmount, isComponentWillUpdate, isComponentWrapperCall, isComponentWrapperCallLoose, isCreateContext, isCreateContextCall, isCreateElement, isCreateElementCall, isCreateRef, isCreateRefCall, isDeclaredInRenderPropLoose, isDirectValueOfRenderPropertyLoose, isEnumType, isFalseLiteralType, isFalsyBigIntType, isFalsyNumberType, isFalsyStringType, isForwardRef, isForwardRefCall, isFragmentElement, isFunctionOfRenderMethod, isGetChildContext, isGetDefaultProps, isGetDerivedStateFromError, isGetDerivedStateFromProps, isGetInitialState, isGetSnapshotBeforeUpdate, isHostElement, isInitializedFromReact, isInstanceIdEqual, isInversePhase, isJsxLike, isJsxText, isLazy, isLazyCall, isMemo, isMemoCall, isNeverType, isNullishType, isNumberType, isObjectType, isPureComponent, isReactAPI, isReactAPICall, isReactHook, isReactHookCall, isReactHookCallWithName, isReactHookCallWithNameAlias, isReactHookCallWithNameLoose, isReactHookId, isReactHookName, isRender, isRenderFunctionLoose, isRenderMethodLike, isRenderPropLoose, isShouldComponentUpdate, isStringType, isThisSetState, isTrueLiteralType, isTruthyBigIntType, isTruthyNumberType, isTruthyStringType, isUnknownType, isUnsafeComponentWillMount, isUnsafeComponentWillReceiveProps, isUnsafeComponentWillUpdate, isUseActionStateCall, isUseCall, isUseCallbackCall, isUseContextCall, isUseDebugValueCall, isUseDeferredValueCall, isUseEffectCall, isUseEffectCallLoose, isUseFormStatusCall, isUseIdCall, isUseImperativeHandleCall, isUseInsertionEffectCall, isUseLayoutEffectCall, isUseMemoCall, isUseOptimisticCall, isUseReducerCall, isUseRefCall, isUseStateCall, isUseSyncExternalStoreCall, isUseTransitionCall, resolveAttributeValue, stringifyJsx, useComponentCollector, useComponentCollectorLegacy, useHookCollector };
1221
+ export { ComponentDetectionHint, ComponentFlag, ComponentPhaseRelevance, DEFAULT_COMPONENT_DETECTION_HINT, DEFAULT_JSX_DETECTION_HINT, JSXDetectionHint, REACT_BUILTIN_HOOK_NAMES, findParentAttribute, getAttribute, getAttributeName, getComponentFlagFromInitPath, getComponentNameFromId, getElementType, getFunctionComponentId, getInstanceId, getTypeVariants, hasAnyAttribute, hasAttribute, hasEveryAttribute, hasNoneOrLooseComponentName, isAnyType, isAssignmentToThisState, isBigIntType, isBooleanType, isCaptureOwnerStack, isCaptureOwnerStackCall, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOfCreateElement, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isCloneElement, isCloneElementCall, isComponentDefinition, isComponentDidCatch, isComponentDidMount, isComponentDidUpdate, isComponentName, isComponentNameLoose, isComponentWillMount, isComponentWillReceiveProps, isComponentWillUnmount, isComponentWillUpdate, isComponentWrapperCall, isComponentWrapperCallLoose, isCreateContext, isCreateContextCall, isCreateElement, isCreateElementCall, isCreateRef, isCreateRefCall, isDeclaredInRenderPropLoose, isDirectValueOfRenderPropertyLoose, isEnumType, isFalseLiteralType, isFalsyBigIntType, isFalsyNumberType, isFalsyStringType, isForwardRef, isForwardRefCall, isFragmentElement, isFunctionOfRenderMethod, isGetChildContext, isGetDefaultProps, isGetDerivedStateFromError, isGetDerivedStateFromProps, isGetInitialState, isGetSnapshotBeforeUpdate, isHostElement, isInitializedFromReact, isInstanceIdEqual, isInversePhase, isJsxLike, isJsxText, isLazy, isLazyCall, isMemo, isMemoCall, isNeverType, isNullishType, isNumberType, isObjectType, isPureComponent, isReactAPI, isReactAPICall, isReactHook, isReactHookCall, isReactHookCallWithName, isReactHookCallWithNameAlias, isReactHookId, isReactHookName, isRender, isRenderFunctionLoose, isRenderMethodLike, isRenderPropLoose, isShouldComponentUpdate, isStringType, isThisSetState, isTrueLiteralType, isTruthyBigIntType, isTruthyNumberType, isTruthyStringType, isUnknownType, isUnsafeComponentWillMount, isUnsafeComponentWillReceiveProps, isUnsafeComponentWillUpdate, isUseActionStateCall, isUseCall, isUseCallbackCall, isUseContextCall, isUseDebugValueCall, isUseDeferredValueCall, isUseEffectCall, isUseEffectLikeCall, isUseFormStatusCall, isUseIdCall, isUseImperativeHandleCall, isUseInsertionEffectCall, isUseLayoutEffectCall, isUseMemoCall, isUseOptimisticCall, isUseReducerCall, isUseRefCall, isUseStateCall, isUseSyncExternalStoreCall, isUseTransitionCall, resolveAttributeValue, stringifyJsx, useComponentCollector, useComponentCollectorLegacy, useHookCollector };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eslint-react/core",
3
- "version": "2.0.0-next.171",
3
+ "version": "2.0.0-next.173",
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,11 +34,11 @@
34
34
  "birecord": "^0.1.1",
35
35
  "ts-api-utils": "^2.1.0",
36
36
  "ts-pattern": "^5.8.0",
37
- "@eslint-react/ast": "2.0.0-next.171",
38
- "@eslint-react/eff": "2.0.0-next.171",
39
- "@eslint-react/shared": "2.0.0-next.171",
40
- "@eslint-react/kit": "2.0.0-next.171",
41
- "@eslint-react/var": "2.0.0-next.171"
37
+ "@eslint-react/eff": "2.0.0-next.173",
38
+ "@eslint-react/ast": "2.0.0-next.173",
39
+ "@eslint-react/kit": "2.0.0-next.173",
40
+ "@eslint-react/shared": "2.0.0-next.173",
41
+ "@eslint-react/var": "2.0.0-next.173"
42
42
  },
43
43
  "devDependencies": {
44
44
  "tsdown": "^0.14.2",