@eslint-react/core 5.2.1-next.1 → 5.2.1-next.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 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: ast.FunctionID;
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
- * Represents the kind of a React function
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 React Client Function
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 React Server Function
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 React Function
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: ast.FunctionID;
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 | ast.FunctionInitPath;
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): ast.FunctionID;
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: ast.FunctionID;
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 = ast.getClassId(node);
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 && ast.hasCallInFunctionInitPath("memo", initPath)) flag |= FunctionComponentFlag.Memo;
427
- if (initPath != null && ast.hasCallInFunctionInitPath("forwardRef", initPath)) flag |= FunctionComponentFlag.ForwardRef;
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 = ast.getFunctionId(node);
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 && ast.getFunctionId(node) == null && !isFunctionComponentWrapperCall(context, parent) && !isCreateElementCall(context, parent):
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 = ast.getFunctionId(node);
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 = ast.getFunctionInitPath(node);
710
- const directives = ast.getFunctionDirectives(node);
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 = ast.getFunctionId(node);
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-next.1",
3
+ "version": "5.2.1-next.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-next.1",
39
- "@eslint-react/eslint": "5.2.1-next.1",
40
- "@eslint-react/jsx": "5.2.1-next.1",
41
- "@eslint-react/shared": "5.2.1-next.1",
42
- "@eslint-react/var": "5.2.1-next.1"
38
+ "@eslint-react/ast": "5.2.1-next.3",
39
+ "@eslint-react/eslint": "5.2.1-next.3",
40
+ "@eslint-react/jsx": "5.2.1-next.3",
41
+ "@eslint-react/shared": "5.2.1-next.3",
42
+ "@eslint-react/var": "5.2.1-next.3"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@typescript-eslint/typescript-estree": "^8.58.1",