@eslint-react/core 5.2.1-beta.1 → 5.2.1-beta.3
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 +82 -12
- package/dist/index.js +155 -10
- package/package.json +6 -6
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as ast from "@eslint-react/ast";
|
|
2
|
+
import { TSESTreeClass, TSESTreeDirective, TSESTreeFunction } from "@eslint-react/ast";
|
|
2
3
|
import { TSESTree } from "@typescript-eslint/types";
|
|
3
4
|
import { RuleContext } from "@eslint-react/eslint";
|
|
4
5
|
import { RegExpLike } from "@eslint-react/shared";
|
|
@@ -112,6 +113,14 @@ declare const isUseStateCall: isAPICall.ReturnType;
|
|
|
112
113
|
declare const isUseSyncExternalStoreCall: isAPICall.ReturnType;
|
|
113
114
|
declare const isUseTransitionCall: isAPICall.ReturnType;
|
|
114
115
|
//#endregion
|
|
116
|
+
//#region src/class.d.ts
|
|
117
|
+
/**
|
|
118
|
+
* Get the class identifier of a class node
|
|
119
|
+
* @param node The class node to get the identifier from
|
|
120
|
+
* @returns The class identifier or null if not found
|
|
121
|
+
*/
|
|
122
|
+
declare function getClassId(node: TSESTreeClass): TSESTree.BindingName | null;
|
|
123
|
+
//#endregion
|
|
115
124
|
//#region src/semantic.d.ts
|
|
116
125
|
/**
|
|
117
126
|
* Represents a semantic node in the AST
|
|
@@ -139,7 +148,7 @@ interface SemanticNode {
|
|
|
139
148
|
*/
|
|
140
149
|
interface SemanticFunc extends SemanticNode {
|
|
141
150
|
/** The identifier of the function */
|
|
142
|
-
id:
|
|
151
|
+
id: null | TSESTree.Node;
|
|
143
152
|
/** The AST node of the function */
|
|
144
153
|
node: ast.TSESTreeFunction;
|
|
145
154
|
/** The name of the function */
|
|
@@ -259,31 +268,92 @@ declare function getClassComponentCollector(context: RuleContext): getClassCompo
|
|
|
259
268
|
//#endregion
|
|
260
269
|
//#region src/function.d.ts
|
|
261
270
|
/**
|
|
262
|
-
*
|
|
271
|
+
* Type representing the return type of `getFunctionId`.
|
|
272
|
+
*/
|
|
273
|
+
type FunctionID = ReturnType<typeof getFunctionId>;
|
|
274
|
+
/**
|
|
275
|
+
* Represents various AST paths for function declarations.
|
|
276
|
+
* Each tuple type represents a specific function definition pattern.
|
|
277
|
+
*/
|
|
278
|
+
type FunctionInitPath = readonly [TSESTree.FunctionDeclaration] | readonly [TSESTree.VariableDeclaration, TSESTree.VariableDeclarator, TSESTreeFunction] | readonly [TSESTree.VariableDeclaration, TSESTree.VariableDeclarator, TSESTree.CallExpression, TSESTreeFunction] | readonly [TSESTree.VariableDeclaration, TSESTree.VariableDeclarator, TSESTree.CallExpression, TSESTree.CallExpression, TSESTreeFunction] | readonly [TSESTree.VariableDeclaration, TSESTree.VariableDeclarator, TSESTree.ObjectExpression, TSESTree.Property, TSESTreeFunction] | readonly [TSESTree.VariableDeclaration, TSESTree.VariableDeclarator, TSESTree.ObjectExpression, TSESTree.Property, TSESTree.CallExpression, TSESTreeFunction] | readonly [TSESTree.VariableDeclaration, TSESTree.VariableDeclarator, TSESTree.ObjectExpression, TSESTree.Property, TSESTree.CallExpression, TSESTree.CallExpression, TSESTreeFunction] | readonly [TSESTree.ClassDeclaration | TSESTree.ClassExpression, TSESTree.ClassBody, TSESTree.MethodDefinition, TSESTreeFunction] | readonly [TSESTree.ClassDeclaration | TSESTree.ClassExpression, TSESTree.ClassBody, TSESTree.PropertyDefinition, TSESTreeFunction];
|
|
279
|
+
/**
|
|
280
|
+
* Represents the kind of a function.
|
|
263
281
|
*/
|
|
264
282
|
type FunctionKind = "client-function" | "server-function";
|
|
265
283
|
/**
|
|
266
|
-
* Represents a
|
|
284
|
+
* Represents a client function semantic node.
|
|
267
285
|
*/
|
|
268
286
|
interface ClientFunctionSemanticNode extends SemanticFunc {
|
|
269
287
|
/**
|
|
270
|
-
* The kind of function
|
|
288
|
+
* The kind of function.
|
|
271
289
|
*/
|
|
272
290
|
kind: "client-function";
|
|
273
291
|
}
|
|
274
292
|
/**
|
|
275
|
-
* Represents a
|
|
293
|
+
* Represents a server function semantic node.
|
|
276
294
|
*/
|
|
277
295
|
interface ServerFunctionSemanticNode extends SemanticFunc {
|
|
278
296
|
/**
|
|
279
|
-
* The kind of function
|
|
297
|
+
* The kind of function.
|
|
280
298
|
*/
|
|
281
299
|
kind: "server-function";
|
|
282
300
|
}
|
|
283
301
|
/**
|
|
284
|
-
* Represents a
|
|
302
|
+
* Represents a function semantic node.
|
|
285
303
|
*/
|
|
286
304
|
type FunctionSemanticNode = ClientFunctionSemanticNode | ServerFunctionSemanticNode;
|
|
305
|
+
/**
|
|
306
|
+
* Gets the static identifier of a function AST node.
|
|
307
|
+
*
|
|
308
|
+
* @remarks
|
|
309
|
+
* For function declarations this is straightforward. For anonymous function
|
|
310
|
+
* expressions it is more complex. This function roughly detects the same AST
|
|
311
|
+
* nodes as the ECMAScript spec's `IsAnonymousFunctionDefinition()` with some
|
|
312
|
+
* exceptions to better fit our use case.
|
|
313
|
+
*
|
|
314
|
+
* Ported from {@link https://github.com/facebook/react/blob/bb8a76c6cc77ea2976d690ea09f5a1b3d9b1792a/packages/eslint-plugin-react-hooks/src/rules/RulesOfHooks.ts#L860 | RulesOfHooks.ts}
|
|
315
|
+
*
|
|
316
|
+
* @param node - The function node to analyze.
|
|
317
|
+
* @returns The identifier node if found, `null` otherwise.
|
|
318
|
+
*/
|
|
319
|
+
declare function getFunctionId(node: TSESTree.Expression | TSESTreeFunction): TSESTree.ArrayExpression | TSESTree.ArrayPattern | TSESTree.ArrowFunctionExpression | TSESTree.AssignmentExpression | TSESTree.AwaitExpression | TSESTree.PrivateInExpression | TSESTree.SymmetricBinaryExpression | 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.PrivateIdentifier | TSESTree.SequenceExpression | TSESTree.Super | TSESTree.TaggedTemplateExpression | TSESTree.TemplateLiteral | TSESTree.ThisExpression | TSESTree.TSAsExpression | TSESTree.TSInstantiationExpression | TSESTree.TSNonNullExpression | TSESTree.TSSatisfiesExpression | TSESTree.TSTypeAssertion | TSESTree.UnaryExpressionBitwiseNot | TSESTree.UnaryExpressionDelete | TSESTree.UnaryExpressionMinus | TSESTree.UnaryExpressionNot | TSESTree.UnaryExpressionPlus | TSESTree.UnaryExpressionTypeof | TSESTree.UnaryExpressionVoid | TSESTree.UpdateExpression | TSESTree.YieldExpression | null;
|
|
320
|
+
/**
|
|
321
|
+
* Identifies the initialization path of a function node in the AST.
|
|
322
|
+
*
|
|
323
|
+
* @param node - The function node to analyze.
|
|
324
|
+
* @returns The function initialization path or `null` if not identifiable.
|
|
325
|
+
*/
|
|
326
|
+
declare function getFunctionInitPath(node: TSESTreeFunction): null | FunctionInitPath;
|
|
327
|
+
/**
|
|
328
|
+
* Checks if a specific function call exists in the function initialization path.
|
|
329
|
+
*
|
|
330
|
+
* @param callName - The name of the call to check for (e.g., "memo", "forwardRef").
|
|
331
|
+
* @param initPath - The function initialization path to search in.
|
|
332
|
+
* @returns `true` if the call exists in the path, `false` otherwise.
|
|
333
|
+
*/
|
|
334
|
+
declare function isFunctionHasCallInInitPath(callName: string, initPath: FunctionInitPath): boolean;
|
|
335
|
+
/**
|
|
336
|
+
* Checks if a function is empty.
|
|
337
|
+
*
|
|
338
|
+
* @param node - The function node to check.
|
|
339
|
+
* @returns `true` if the function is empty, `false` otherwise.
|
|
340
|
+
*/
|
|
341
|
+
declare function isFunctionEmpty(node: TSESTreeFunction): boolean;
|
|
342
|
+
/**
|
|
343
|
+
* Gets all directive expression statements from the top of a function body.
|
|
344
|
+
*
|
|
345
|
+
* @param node - The function AST node.
|
|
346
|
+
* @returns An array of directive expression statements.
|
|
347
|
+
*/
|
|
348
|
+
declare function getFunctionDirectives(node: TSESTreeFunction): TSESTreeDirective[];
|
|
349
|
+
/**
|
|
350
|
+
* Checks if a directive with the given name exists in the function directives.
|
|
351
|
+
*
|
|
352
|
+
* @param node - The function AST node.
|
|
353
|
+
* @param name - The directive name to check (e.g., "use memo", "use no memo").
|
|
354
|
+
* @returns `true` if the directive exists, `false` otherwise.
|
|
355
|
+
*/
|
|
356
|
+
declare function isFunctionHasDirective(node: TSESTreeFunction, name: string): boolean;
|
|
287
357
|
//#endregion
|
|
288
358
|
//#region src/function-component.d.ts
|
|
289
359
|
/**
|
|
@@ -293,7 +363,7 @@ interface FunctionComponentSemanticNode extends SemanticNode {
|
|
|
293
363
|
/**
|
|
294
364
|
* The identifier or identifier sequence of the component
|
|
295
365
|
*/
|
|
296
|
-
id:
|
|
366
|
+
id: FunctionID;
|
|
297
367
|
/**
|
|
298
368
|
* The kind of component
|
|
299
369
|
*/
|
|
@@ -317,7 +387,7 @@ interface FunctionComponentSemanticNode extends SemanticNode {
|
|
|
317
387
|
/**
|
|
318
388
|
* The initialization path of the function
|
|
319
389
|
*/
|
|
320
|
-
initPath: null |
|
|
390
|
+
initPath: null | FunctionInitPath;
|
|
321
391
|
/**
|
|
322
392
|
* Indicates if the component is inside an export default declaration
|
|
323
393
|
*/
|
|
@@ -376,7 +446,7 @@ declare function isFunctionComponentWrapperCallback(context: RuleContext, node:
|
|
|
376
446
|
* @param node The AST node to get the function component identifier from
|
|
377
447
|
* @internal
|
|
378
448
|
*/
|
|
379
|
-
declare function getFunctionComponentId(context: RuleContext, node: ast.TSESTreeFunction):
|
|
449
|
+
declare function getFunctionComponentId(context: RuleContext, node: ast.TSESTreeFunction): FunctionID;
|
|
380
450
|
/**
|
|
381
451
|
* Check if a string matches the strict component name pattern
|
|
382
452
|
* @param name The name to check
|
|
@@ -462,7 +532,7 @@ declare function getFunctionComponentCollector(context: RuleContext, options?: g
|
|
|
462
532
|
*/
|
|
463
533
|
interface HookSemanticNode extends SemanticNode {
|
|
464
534
|
/** The identifier of the hook */
|
|
465
|
-
id:
|
|
535
|
+
id: FunctionID;
|
|
466
536
|
/** The AST node of the hook */
|
|
467
537
|
node: ast.TSESTreeFunction;
|
|
468
538
|
/** The kind of hook */
|
|
@@ -669,4 +739,4 @@ type TypeVariant = "any" | "bigint" | "boolean" | "enum" | "never" | "nullish" |
|
|
|
669
739
|
*/
|
|
670
740
|
declare function getTypeVariants(types: ts.Type[]): Set<TypeVariant>;
|
|
671
741
|
//#endregion
|
|
672
|
-
export { ClassComponentSemanticNode, ClientFunctionSemanticNode, DEFAULT_COMPONENT_DETECTION_HINT, FunctionComponentDetectionHint, FunctionComponentFlag, FunctionComponentSemanticNode, FunctionKind, FunctionSemanticNode, HookSemanticNode, REACT_BUILTIN_HOOK_NAMES, SemanticFunc, SemanticNode, ServerFunctionSemanticNode, TypeVariant, getClassComponentCollector, getFullyQualifiedNameEx, getFunctionComponentCollector, getFunctionComponentFlagFromInitPath, getFunctionComponentId, getHookCollector, getTypeVariants, isAPI, isAPICall, isAPIFromReact, isAPIFromReactNative, isAnyType, isAssignmentToThisState, isBigIntType, isBooleanLiteralType, isBooleanType, isCaptureOwnerStack, isCaptureOwnerStackCall, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isClassComponentLoose, 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, isFunctionWithLooseComponentName, isGetChildContext, isGetDefaultProps, isGetDerivedStateFromError, isGetDerivedStateFromProps, isGetInitialState, isGetSnapshotBeforeUpdate, isHookCall, isHookDefinition, isHookId, isHookName, 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 };
|
|
742
|
+
export { ClassComponentSemanticNode, ClientFunctionSemanticNode, DEFAULT_COMPONENT_DETECTION_HINT, FunctionComponentDetectionHint, FunctionComponentFlag, FunctionComponentSemanticNode, FunctionID, FunctionInitPath, FunctionKind, FunctionSemanticNode, HookSemanticNode, REACT_BUILTIN_HOOK_NAMES, SemanticFunc, SemanticNode, ServerFunctionSemanticNode, TypeVariant, getClassComponentCollector, getClassId, getFullyQualifiedNameEx, getFunctionComponentCollector, getFunctionComponentFlagFromInitPath, getFunctionComponentId, getFunctionDirectives, getFunctionId, getFunctionInitPath, getHookCollector, getTypeVariants, isAPI, isAPICall, isAPIFromReact, isAPIFromReactNative, isAnyType, isAssignmentToThisState, isBigIntType, isBooleanLiteralType, isBooleanType, isCaptureOwnerStack, isCaptureOwnerStackCall, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isClassComponentLoose, 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, 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 };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as ast from "@eslint-react/ast";
|
|
2
|
+
import { isDirective, isMethodOrProperty, isTypeAssertionExpression, isTypeExpression } from "@eslint-react/ast";
|
|
2
3
|
import { resolveImportSource } from "@eslint-react/var";
|
|
3
4
|
import { AST_NODE_TYPES } from "@typescript-eslint/types";
|
|
4
5
|
import "@eslint-react/eslint";
|
|
@@ -254,6 +255,19 @@ const isUseStateCall = isAPICall("useState");
|
|
|
254
255
|
const isUseSyncExternalStoreCall = isAPICall("useSyncExternalStore");
|
|
255
256
|
const isUseTransitionCall = isAPICall("useTransition");
|
|
256
257
|
|
|
258
|
+
//#endregion
|
|
259
|
+
//#region src/class.ts
|
|
260
|
+
/**
|
|
261
|
+
* Get the class identifier of a class node
|
|
262
|
+
* @param node The class node to get the identifier from
|
|
263
|
+
* @returns The class identifier or null if not found
|
|
264
|
+
*/
|
|
265
|
+
function getClassId(node) {
|
|
266
|
+
if (node.id != null) return node.id;
|
|
267
|
+
if (node.parent.type === AST_NODE_TYPES.VariableDeclarator) return node.parent.id;
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
|
|
257
271
|
//#endregion
|
|
258
272
|
//#region src/class-component.ts
|
|
259
273
|
function isClassComponent(node, context) {
|
|
@@ -379,7 +393,7 @@ function getClassComponentCollector(context) {
|
|
|
379
393
|
const getText = (n) => context.sourceCode.getText(n);
|
|
380
394
|
const collect = (node) => {
|
|
381
395
|
if (!isClassComponent(node)) return;
|
|
382
|
-
const id =
|
|
396
|
+
const id = getClassId(node);
|
|
383
397
|
const key = ulid();
|
|
384
398
|
const name = id == null ? null : ast.getFullyQualifiedName(id, getText);
|
|
385
399
|
components.set(key, {
|
|
@@ -403,6 +417,137 @@ function getClassComponentCollector(context) {
|
|
|
403
417
|
};
|
|
404
418
|
}
|
|
405
419
|
|
|
420
|
+
//#endregion
|
|
421
|
+
//#region src/function.ts
|
|
422
|
+
/**
|
|
423
|
+
* Gets the static identifier of a function AST node.
|
|
424
|
+
*
|
|
425
|
+
* @remarks
|
|
426
|
+
* For function declarations this is straightforward. For anonymous function
|
|
427
|
+
* expressions it is more complex. This function roughly detects the same AST
|
|
428
|
+
* nodes as the ECMAScript spec's `IsAnonymousFunctionDefinition()` with some
|
|
429
|
+
* exceptions to better fit our use case.
|
|
430
|
+
*
|
|
431
|
+
* Ported from {@link https://github.com/facebook/react/blob/bb8a76c6cc77ea2976d690ea09f5a1b3d9b1792a/packages/eslint-plugin-react-hooks/src/rules/RulesOfHooks.ts#L860 | RulesOfHooks.ts}
|
|
432
|
+
*
|
|
433
|
+
* @param node - The function node to analyze.
|
|
434
|
+
* @returns The identifier node if found, `null` otherwise.
|
|
435
|
+
*/
|
|
436
|
+
function getFunctionId(node) {
|
|
437
|
+
switch (true) {
|
|
438
|
+
case "id" in node && node.id != null: return node.id;
|
|
439
|
+
case node.parent.type === AST_NODE_TYPES.VariableDeclarator && node.parent.init === node: return node.parent.id;
|
|
440
|
+
case node.parent.type === AST_NODE_TYPES.AssignmentExpression && node.parent.right === node && node.parent.operator === "=": return node.parent.left;
|
|
441
|
+
case node.parent.type === AST_NODE_TYPES.Property && node.parent.value === node && !node.parent.computed: return node.parent.key;
|
|
442
|
+
case isMethodOrProperty(node.parent) && node.parent.value === node: return node.parent.key;
|
|
443
|
+
case node.parent.type === AST_NODE_TYPES.AssignmentPattern && node.parent.right === node: return node.parent.left;
|
|
444
|
+
case node.parent.type === AST_NODE_TYPES.ConditionalExpression: return getFunctionId(node.parent);
|
|
445
|
+
case isTypeAssertionExpression(node.parent): return getFunctionId(node.parent);
|
|
446
|
+
}
|
|
447
|
+
return null;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Identifies the initialization path of a function node in the AST.
|
|
451
|
+
*
|
|
452
|
+
* @param node - The function node to analyze.
|
|
453
|
+
* @returns The function initialization path or `null` if not identifiable.
|
|
454
|
+
*/
|
|
455
|
+
function getFunctionInitPath(node) {
|
|
456
|
+
if (node.type === AST_NODE_TYPES.FunctionDeclaration) return [node];
|
|
457
|
+
let parent = node.parent;
|
|
458
|
+
while (isTypeExpression(parent)) parent = parent.parent;
|
|
459
|
+
switch (true) {
|
|
460
|
+
case parent.type === AST_NODE_TYPES.VariableDeclarator: return [
|
|
461
|
+
parent.parent,
|
|
462
|
+
parent,
|
|
463
|
+
node
|
|
464
|
+
];
|
|
465
|
+
case parent.type === AST_NODE_TYPES.CallExpression && parent.parent.type === AST_NODE_TYPES.VariableDeclarator: return [
|
|
466
|
+
parent.parent.parent,
|
|
467
|
+
parent.parent,
|
|
468
|
+
parent,
|
|
469
|
+
node
|
|
470
|
+
];
|
|
471
|
+
case parent.type === AST_NODE_TYPES.CallExpression && parent.parent.type === AST_NODE_TYPES.CallExpression && parent.parent.parent.type === AST_NODE_TYPES.VariableDeclarator: return [
|
|
472
|
+
parent.parent.parent.parent,
|
|
473
|
+
parent.parent.parent,
|
|
474
|
+
parent.parent,
|
|
475
|
+
parent,
|
|
476
|
+
node
|
|
477
|
+
];
|
|
478
|
+
case parent.type === AST_NODE_TYPES.Property && parent.parent.type === AST_NODE_TYPES.ObjectExpression && parent.parent.parent.type === AST_NODE_TYPES.VariableDeclarator: return [
|
|
479
|
+
parent.parent.parent.parent,
|
|
480
|
+
parent.parent.parent,
|
|
481
|
+
parent.parent,
|
|
482
|
+
parent,
|
|
483
|
+
node
|
|
484
|
+
];
|
|
485
|
+
case parent.type === AST_NODE_TYPES.MethodDefinition: return [
|
|
486
|
+
parent.parent.parent,
|
|
487
|
+
parent.parent,
|
|
488
|
+
parent,
|
|
489
|
+
node
|
|
490
|
+
];
|
|
491
|
+
case parent.type === AST_NODE_TYPES.PropertyDefinition: return [
|
|
492
|
+
parent.parent.parent,
|
|
493
|
+
parent.parent,
|
|
494
|
+
parent,
|
|
495
|
+
node
|
|
496
|
+
];
|
|
497
|
+
}
|
|
498
|
+
return null;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Checks if a specific function call exists in the function initialization path.
|
|
502
|
+
*
|
|
503
|
+
* @param callName - The name of the call to check for (e.g., "memo", "forwardRef").
|
|
504
|
+
* @param initPath - The function initialization path to search in.
|
|
505
|
+
* @returns `true` if the call exists in the path, `false` otherwise.
|
|
506
|
+
*/
|
|
507
|
+
function isFunctionHasCallInInitPath(callName, initPath) {
|
|
508
|
+
return initPath.some((node) => {
|
|
509
|
+
if (node.type !== AST_NODE_TYPES.CallExpression) return false;
|
|
510
|
+
const { callee } = node;
|
|
511
|
+
if (callee.type === AST_NODE_TYPES.Identifier) return callee.name === callName;
|
|
512
|
+
if (callee.type === AST_NODE_TYPES.MemberExpression && "name" in callee.property) return callee.property.name === callName;
|
|
513
|
+
return false;
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Checks if a function is empty.
|
|
518
|
+
*
|
|
519
|
+
* @param node - The function node to check.
|
|
520
|
+
* @returns `true` if the function is empty, `false` otherwise.
|
|
521
|
+
*/
|
|
522
|
+
function isFunctionEmpty(node) {
|
|
523
|
+
return node.body.type === AST_NODE_TYPES.BlockStatement && node.body.body.length === 0;
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Gets all directive expression statements from the top of a function body.
|
|
527
|
+
*
|
|
528
|
+
* @param node - The function AST node.
|
|
529
|
+
* @returns An array of directive expression statements.
|
|
530
|
+
*/
|
|
531
|
+
function getFunctionDirectives(node) {
|
|
532
|
+
const directives = [];
|
|
533
|
+
if (node.body.type !== AST_NODE_TYPES.BlockStatement) return directives;
|
|
534
|
+
for (const stmt of node.body.body) {
|
|
535
|
+
if (!isDirective(stmt)) continue;
|
|
536
|
+
directives.push(stmt);
|
|
537
|
+
}
|
|
538
|
+
return directives;
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Checks if a directive with the given name exists in the function directives.
|
|
542
|
+
*
|
|
543
|
+
* @param node - The function AST node.
|
|
544
|
+
* @param name - The directive name to check (e.g., "use memo", "use no memo").
|
|
545
|
+
* @returns `true` if the directive exists, `false` otherwise.
|
|
546
|
+
*/
|
|
547
|
+
function isFunctionHasDirective(node, name) {
|
|
548
|
+
return getFunctionDirectives(node).some((d) => d.directive === name);
|
|
549
|
+
}
|
|
550
|
+
|
|
406
551
|
//#endregion
|
|
407
552
|
//#region src/function-component.ts
|
|
408
553
|
/**
|
|
@@ -423,8 +568,8 @@ const FunctionComponentFlag = {
|
|
|
423
568
|
*/
|
|
424
569
|
function getFunctionComponentFlagFromInitPath(initPath) {
|
|
425
570
|
let flag = FunctionComponentFlag.None;
|
|
426
|
-
if (initPath != null &&
|
|
427
|
-
if (initPath != null &&
|
|
571
|
+
if (initPath != null && isFunctionHasCallInInitPath("memo", initPath)) flag |= FunctionComponentFlag.Memo;
|
|
572
|
+
if (initPath != null && isFunctionHasCallInInitPath("forwardRef", initPath)) flag |= FunctionComponentFlag.ForwardRef;
|
|
428
573
|
return flag;
|
|
429
574
|
}
|
|
430
575
|
/**
|
|
@@ -457,7 +602,7 @@ function isFunctionComponentWrapperCallback(context, node) {
|
|
|
457
602
|
* @internal
|
|
458
603
|
*/
|
|
459
604
|
function getFunctionComponentId(context, node) {
|
|
460
|
-
const functionId =
|
|
605
|
+
const functionId = getFunctionId(node);
|
|
461
606
|
if (functionId != null) return functionId;
|
|
462
607
|
let parent = node.parent;
|
|
463
608
|
while (ast.isTypeExpression(parent)) parent = parent.parent;
|
|
@@ -549,7 +694,7 @@ function isFunctionComponentDefinition(context, node, hint) {
|
|
|
549
694
|
case parent.type === AST_NODE_TYPES.CallExpression && parent.callee.type === AST_NODE_TYPES.MemberExpression && parent.callee.property.type === AST_NODE_TYPES.Identifier && parent.callee.property.name === "flatMap":
|
|
550
695
|
if (hint & FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayFlatMapCallback) return false;
|
|
551
696
|
break;
|
|
552
|
-
case parent.type === AST_NODE_TYPES.CallExpression &&
|
|
697
|
+
case parent.type === AST_NODE_TYPES.CallExpression && getFunctionId(node) == null && !isFunctionComponentWrapperCall(context, parent) && !isCreateElementCall(context, parent):
|
|
553
698
|
if (hint & FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArbitraryCallExpressionCallback) return false;
|
|
554
699
|
break;
|
|
555
700
|
}
|
|
@@ -616,7 +761,7 @@ function isHookId(id) {
|
|
|
616
761
|
*/
|
|
617
762
|
function isHookDefinition(node) {
|
|
618
763
|
if (node == null) return false;
|
|
619
|
-
const id =
|
|
764
|
+
const id = getFunctionId(node);
|
|
620
765
|
switch (id?.type) {
|
|
621
766
|
case AST_NODE_TYPES.Identifier: return isHookName(id.name);
|
|
622
767
|
case AST_NODE_TYPES.MemberExpression: return "name" in id.property && isHookName(id.property.name);
|
|
@@ -706,8 +851,8 @@ function getFunctionComponentCollector(context, options = {}) {
|
|
|
706
851
|
const isExportDefaultDeclaration = exp != null && ast.getUnderlyingExpression(exp.declaration) === node;
|
|
707
852
|
const id = getFunctionComponentId(context, node);
|
|
708
853
|
const name = id == null ? null : ast.getFullyQualifiedName(id, getText);
|
|
709
|
-
const initPath =
|
|
710
|
-
const directives =
|
|
854
|
+
const initPath = getFunctionInitPath(node);
|
|
855
|
+
const directives = getFunctionDirectives(node);
|
|
711
856
|
const entry = {
|
|
712
857
|
id,
|
|
713
858
|
key,
|
|
@@ -791,7 +936,7 @@ function getHookCollector(context) {
|
|
|
791
936
|
const getText = (n) => context.sourceCode.getText(n);
|
|
792
937
|
const getCurrentEntry = () => functionEntries.at(-1) ?? null;
|
|
793
938
|
const onFunctionEnter = (node) => {
|
|
794
|
-
const id =
|
|
939
|
+
const id = getFunctionId(node);
|
|
795
940
|
const key = ulid();
|
|
796
941
|
const entry = {
|
|
797
942
|
id,
|
|
@@ -989,4 +1134,4 @@ function getTypeVariants(types) {
|
|
|
989
1134
|
}
|
|
990
1135
|
|
|
991
1136
|
//#endregion
|
|
992
|
-
export { DEFAULT_COMPONENT_DETECTION_HINT, FunctionComponentDetectionHint, FunctionComponentFlag, REACT_BUILTIN_HOOK_NAMES, getClassComponentCollector, getFullyQualifiedNameEx, getFunctionComponentCollector, getFunctionComponentFlagFromInitPath, getFunctionComponentId, getHookCollector, getTypeVariants, isAPI, isAPICall, isAPIFromReact, isAPIFromReactNative, isAnyType, isAssignmentToThisState, isBigIntType, isBooleanLiteralType, isBooleanType, isCaptureOwnerStack, isCaptureOwnerStackCall, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isClassComponentLoose, 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, isFunctionWithLooseComponentName, isGetChildContext, isGetDefaultProps, isGetDerivedStateFromError, isGetDerivedStateFromProps, isGetInitialState, isGetSnapshotBeforeUpdate, isHookCall, isHookDefinition, isHookId, isHookName, 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 };
|
|
1137
|
+
export { DEFAULT_COMPONENT_DETECTION_HINT, FunctionComponentDetectionHint, FunctionComponentFlag, REACT_BUILTIN_HOOK_NAMES, getClassComponentCollector, getClassId, getFullyQualifiedNameEx, getFunctionComponentCollector, getFunctionComponentFlagFromInitPath, getFunctionComponentId, getFunctionDirectives, getFunctionId, getFunctionInitPath, getHookCollector, getTypeVariants, isAPI, isAPICall, isAPIFromReact, isAPIFromReactNative, isAnyType, isAssignmentToThisState, isBigIntType, isBooleanLiteralType, isBooleanType, isCaptureOwnerStack, isCaptureOwnerStackCall, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isClassComponentLoose, 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, 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 };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eslint-react/core",
|
|
3
|
-
"version": "5.2.1-beta.
|
|
3
|
+
"version": "5.2.1-beta.3",
|
|
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": {
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
"@typescript-eslint/utils": "^8.58.1",
|
|
36
36
|
"ts-pattern": "^5.9.0",
|
|
37
37
|
"ulid": "^3.0.2",
|
|
38
|
-
"@eslint-react/ast": "5.2.1-beta.
|
|
39
|
-
"@eslint-react/
|
|
40
|
-
"@eslint-react/shared": "5.2.1-beta.
|
|
41
|
-
"@eslint-react/var": "5.2.1-beta.
|
|
42
|
-
"@eslint-react/
|
|
38
|
+
"@eslint-react/ast": "5.2.1-beta.3",
|
|
39
|
+
"@eslint-react/eslint": "5.2.1-beta.3",
|
|
40
|
+
"@eslint-react/shared": "5.2.1-beta.3",
|
|
41
|
+
"@eslint-react/var": "5.2.1-beta.3",
|
|
42
|
+
"@eslint-react/jsx": "5.2.1-beta.3"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@typescript-eslint/typescript-estree": "^8.58.1",
|