@eslint-react/core 2.0.0-next.172 → 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 +28 -36
- package/dist/index.js +82 -103
- package/package.json +6 -6
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):
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
401
|
-
declare const isUseCall: (
|
|
402
|
-
declare const isUseActionStateCall: (
|
|
403
|
-
declare const isUseCallbackCall: (
|
|
404
|
-
declare const isUseContextCall: (
|
|
405
|
-
declare const isUseDebugValueCall: (
|
|
406
|
-
declare const isUseDeferredValueCall: (
|
|
407
|
-
declare const isUseEffectCall: (
|
|
408
|
-
declare const isUseFormStatusCall: (
|
|
409
|
-
declare const isUseIdCall: (
|
|
410
|
-
declare const isUseImperativeHandleCall: (
|
|
411
|
-
declare const isUseInsertionEffectCall: (
|
|
412
|
-
declare const isUseLayoutEffectCall: (
|
|
413
|
-
declare const isUseMemoCall: (
|
|
414
|
-
declare const isUseOptimisticCall: (
|
|
415
|
-
declare const isUseReducerCall: (
|
|
416
|
-
declare const isUseRefCall: (
|
|
417
|
-
declare const isUseStateCall: (
|
|
418
|
-
declare const isUseSyncExternalStoreCall: (
|
|
419
|
-
declare const isUseTransitionCall: (
|
|
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"];
|
|
@@ -478,7 +470,7 @@ declare function resolveAttributeValue(context: RuleContext, attribute: AST.TSES
|
|
|
478
470
|
readonly toStatic: () => string | number | bigint | boolean | RegExp | null;
|
|
479
471
|
} | {
|
|
480
472
|
readonly kind: "expression";
|
|
481
|
-
readonly node: TSESTree.
|
|
473
|
+
readonly node: TSESTree.Expression | TSESTree.JSXEmptyExpression;
|
|
482
474
|
readonly toStatic: () => unknown;
|
|
483
475
|
} | {
|
|
484
476
|
readonly kind: "element";
|
|
@@ -486,7 +478,7 @@ declare function resolveAttributeValue(context: RuleContext, attribute: AST.TSES
|
|
|
486
478
|
readonly toStatic: () => undefined;
|
|
487
479
|
} | {
|
|
488
480
|
readonly kind: "spreadChild";
|
|
489
|
-
readonly node: TSESTree.
|
|
481
|
+
readonly node: TSESTree.Expression | TSESTree.JSXEmptyExpression;
|
|
490
482
|
readonly toStatic: () => undefined;
|
|
491
483
|
} | {
|
|
492
484
|
readonly kind: "spreadProps";
|
|
@@ -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.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.LogicalExpression | TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedName | TSESTree.MetaProperty | TSESTree.NewExpression | TSESTree.ObjectExpression | TSESTree.ObjectPattern | TSESTree.
|
|
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,
|
|
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 {
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
251
|
-
default: return alias.some(
|
|
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
|
|
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(
|
|
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,
|
|
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.
|
|
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/
|
|
38
|
-
"@eslint-react/
|
|
39
|
-
"@eslint-react/kit": "2.0.0-next.
|
|
40
|
-
"@eslint-react/shared": "2.0.0-next.
|
|
41
|
-
"@eslint-react/var": "2.0.0-next.
|
|
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",
|