@eslint-react/core 5.2.0-next.0 → 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.
Files changed (3) hide show
  1. package/dist/index.d.ts +123 -54
  2. package/dist/index.js +158 -14
  3. package/package.json +9 -9
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
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
- import { SemanticNode as SemanticNode$1 } from "@eslint-react/core";
4
4
  import { RuleContext } from "@eslint-react/eslint";
5
5
  import { RegExpLike } from "@eslint-react/shared";
6
6
  import { ESLintUtils, TSESTree as TSESTree$1 } from "@typescript-eslint/utils";
@@ -113,11 +113,63 @@ declare const isUseStateCall: isAPICall.ReturnType;
113
113
  declare const isUseSyncExternalStoreCall: isAPICall.ReturnType;
114
114
  declare const isUseTransitionCall: isAPICall.ReturnType;
115
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
124
+ //#region src/semantic.d.ts
125
+ /**
126
+ * Represents a semantic node in the AST
127
+ * This is the base interface for all semantic nodes in the React semantic analysis
128
+ */
129
+ interface SemanticNode {
130
+ /** The identifier of the node */
131
+ id: null | TSESTree.Node;
132
+ /** The unique key of the node */
133
+ key: string;
134
+ /** The kind of the node */
135
+ kind: string;
136
+ /** The name of the node */
137
+ name: null | string;
138
+ /** The flag of the node */
139
+ flag: bigint;
140
+ /** The hint of the node */
141
+ hint: bigint;
142
+ /** The AST node */
143
+ node: TSESTree.Node;
144
+ }
145
+ /**
146
+ * Represents a semantic function node in the AST
147
+ * This interface extends SemanticNode and provides additional properties for function analysis
148
+ */
149
+ interface SemanticFunc extends SemanticNode {
150
+ /** The identifier of the function */
151
+ id: null | TSESTree.Node;
152
+ /** The AST node of the function */
153
+ node: ast.TSESTreeFunction;
154
+ /** The name of the function */
155
+ name: string | null;
156
+ /** The return type annotation of the function */
157
+ type: TSESTree.TSTypeAnnotation | null;
158
+ /** The body of the function */
159
+ body: TSESTree.BlockStatement | TSESTree.Expression;
160
+ /** The directives of the function (ex: "use strict", "use client", "use server", etc.) */
161
+ directives: ast.TSESTreeDirective[];
162
+ /** The parameters of the function */
163
+ parameters: TSESTree.Parameter[];
164
+ /** The type parameters of the function */
165
+ typeParameters: TSESTree.TSTypeParameterDeclaration | null;
166
+ }
167
+ //#endregion
116
168
  //#region src/class-component.d.ts
117
169
  /**
118
170
  * @deprecated Class components are legacy. This type exists only to support legacy rules.
119
171
  */
120
- interface ClassComponentSemanticNode extends SemanticNode$1 {
172
+ interface ClassComponentSemanticNode extends SemanticNode {
121
173
  id: null | TSESTree.BindingName;
122
174
  kind: "class-component";
123
175
  displayName: null | TSESTree.Expression;
@@ -214,77 +266,94 @@ declare namespace getClassComponentCollector {
214
266
  */
215
267
  declare function getClassComponentCollector(context: RuleContext): getClassComponentCollector.ReturnType;
216
268
  //#endregion
217
- //#region src/semantic.d.ts
269
+ //#region src/function.d.ts
218
270
  /**
219
- * Represents a semantic node in the AST
220
- * This is the base interface for all semantic nodes in the React semantic analysis
271
+ * Type representing the return type of `getFunctionId`.
221
272
  */
222
- interface SemanticNode {
223
- /** The identifier of the node */
224
- id: null | TSESTree.Node;
225
- /** The unique key of the node */
226
- key: string;
227
- /** The kind of the node */
228
- kind: string;
229
- /** The name of the node */
230
- name: null | string;
231
- /** The flag of the node */
232
- flag: bigint;
233
- /** The hint of the node */
234
- hint: bigint;
235
- /** The AST node */
236
- node: TSESTree.Node;
237
- }
273
+ type FunctionID = ReturnType<typeof getFunctionId>;
238
274
  /**
239
- * Represents a semantic function node in the AST
240
- * This interface extends SemanticNode and provides additional properties for function analysis
275
+ * Represents various AST paths for function declarations.
276
+ * Each tuple type represents a specific function definition pattern.
241
277
  */
242
- interface SemanticFunc extends SemanticNode {
243
- /** The identifier of the function */
244
- id: ast.FunctionID;
245
- /** The AST node of the function */
246
- node: ast.TSESTreeFunction;
247
- /** The name of the function */
248
- name: string | null;
249
- /** The return type annotation of the function */
250
- type: TSESTree.TSTypeAnnotation | null;
251
- /** The body of the function */
252
- body: TSESTree.BlockStatement | TSESTree.Expression;
253
- /** The directives of the function (ex: "use strict", "use client", "use server", etc.) */
254
- directives: ast.TSESTreeDirective[];
255
- /** The parameters of the function */
256
- parameters: TSESTree.Parameter[];
257
- /** The type parameters of the function */
258
- typeParameters: TSESTree.TSTypeParameterDeclaration | null;
259
- }
260
- //#endregion
261
- //#region src/function.d.ts
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];
262
279
  /**
263
- * Represents the kind of a React function
280
+ * Represents the kind of a function.
264
281
  */
265
282
  type FunctionKind = "client-function" | "server-function";
266
283
  /**
267
- * Represents a React Client Function
284
+ * Represents a client function semantic node.
268
285
  */
269
286
  interface ClientFunctionSemanticNode extends SemanticFunc {
270
287
  /**
271
- * The kind of function
288
+ * The kind of function.
272
289
  */
273
290
  kind: "client-function";
274
291
  }
275
292
  /**
276
- * Represents a React Server Function
293
+ * Represents a server function semantic node.
277
294
  */
278
295
  interface ServerFunctionSemanticNode extends SemanticFunc {
279
296
  /**
280
- * The kind of function
297
+ * The kind of function.
281
298
  */
282
299
  kind: "server-function";
283
300
  }
284
301
  /**
285
- * Represents a React Function
302
+ * Represents a function semantic node.
286
303
  */
287
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;
288
357
  //#endregion
289
358
  //#region src/function-component.d.ts
290
359
  /**
@@ -294,7 +363,7 @@ interface FunctionComponentSemanticNode extends SemanticNode {
294
363
  /**
295
364
  * The identifier or identifier sequence of the component
296
365
  */
297
- id: ast.FunctionID;
366
+ id: FunctionID;
298
367
  /**
299
368
  * The kind of component
300
369
  */
@@ -318,7 +387,7 @@ interface FunctionComponentSemanticNode extends SemanticNode {
318
387
  /**
319
388
  * The initialization path of the function
320
389
  */
321
- initPath: null | ast.FunctionInitPath;
390
+ initPath: null | FunctionInitPath;
322
391
  /**
323
392
  * Indicates if the component is inside an export default declaration
324
393
  */
@@ -377,7 +446,7 @@ declare function isFunctionComponentWrapperCallback(context: RuleContext, node:
377
446
  * @param node The AST node to get the function component identifier from
378
447
  * @internal
379
448
  */
380
- declare function getFunctionComponentId(context: RuleContext, node: ast.TSESTreeFunction): ast.FunctionID;
449
+ declare function getFunctionComponentId(context: RuleContext, node: ast.TSESTreeFunction): FunctionID;
381
450
  /**
382
451
  * Check if a string matches the strict component name pattern
383
452
  * @param name The name to check
@@ -463,7 +532,7 @@ declare function getFunctionComponentCollector(context: RuleContext, options?: g
463
532
  */
464
533
  interface HookSemanticNode extends SemanticNode {
465
534
  /** The identifier of the hook */
466
- id: ast.FunctionID;
535
+ id: FunctionID;
467
536
  /** The AST node of the hook */
468
537
  node: ast.TSESTreeFunction;
469
538
  /** The kind of hook */
@@ -670,4 +739,4 @@ type TypeVariant = "any" | "bigint" | "boolean" | "enum" | "never" | "nullish" |
670
739
  */
671
740
  declare function getTypeVariants(types: ts.Type[]): Set<TypeVariant>;
672
741
  //#endregion
673
- 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,7 +1,7 @@
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
- import { isAPIFromReact as isAPIFromReact$1 } from "@eslint-react/core";
5
5
  import "@eslint-react/eslint";
6
6
  import { ulid } from "ulid";
7
7
  import { JsxDetectionHint, isJsxLike } from "@eslint-react/jsx";
@@ -10,7 +10,7 @@ import { AST_NODE_TYPES as AST_NODE_TYPES$1 } from "@typescript-eslint/utils";
10
10
  import { P, isMatching, match } from "ts-pattern";
11
11
  import ts from "typescript";
12
12
 
13
- //#region ../../../.pkgs/eff/dist/index.js
13
+ //#region ../../.pkgs/eff/dist/index.js
14
14
  /**
15
15
  * Creates a function that can be used in a data-last (aka `pipe`able) or
16
16
  * data-first style.
@@ -255,6 +255,19 @@ const isUseStateCall = isAPICall("useState");
255
255
  const isUseSyncExternalStoreCall = isAPICall("useSyncExternalStore");
256
256
  const isUseTransitionCall = isAPICall("useTransition");
257
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
+
258
271
  //#endregion
259
272
  //#region src/class-component.ts
260
273
  function isClassComponent(node, context) {
@@ -264,11 +277,11 @@ function isClassComponent(node, context) {
264
277
  case node.superClass.type === AST_NODE_TYPES.Identifier:
265
278
  if (!re.test(node.superClass.name)) return false;
266
279
  if (context == null) return true;
267
- return isAPIFromReact$1(node.superClass.name, context.sourceCode.getScope(node), "react");
280
+ return isAPIFromReact(node.superClass.name, context.sourceCode.getScope(node), "react");
268
281
  case node.superClass.type === AST_NODE_TYPES.MemberExpression && node.superClass.property.type === AST_NODE_TYPES.Identifier:
269
282
  if (!re.test(node.superClass.property.name)) return false;
270
283
  if (context == null) return true;
271
- if (node.superClass.object.type === AST_NODE_TYPES.Identifier) return isAPIFromReact$1(node.superClass.object.name, context.sourceCode.getScope(node), "react");
284
+ if (node.superClass.object.type === AST_NODE_TYPES.Identifier) return isAPIFromReact(node.superClass.object.name, context.sourceCode.getScope(node), "react");
272
285
  return true;
273
286
  }
274
287
  }
@@ -380,7 +393,7 @@ function getClassComponentCollector(context) {
380
393
  const getText = (n) => context.sourceCode.getText(n);
381
394
  const collect = (node) => {
382
395
  if (!isClassComponent(node)) return;
383
- const id = ast.getClassId(node);
396
+ const id = getClassId(node);
384
397
  const key = ulid();
385
398
  const name = id == null ? null : ast.getFullyQualifiedName(id, getText);
386
399
  components.set(key, {
@@ -404,6 +417,137 @@ function getClassComponentCollector(context) {
404
417
  };
405
418
  }
406
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
+
407
551
  //#endregion
408
552
  //#region src/function-component.ts
409
553
  /**
@@ -424,8 +568,8 @@ const FunctionComponentFlag = {
424
568
  */
425
569
  function getFunctionComponentFlagFromInitPath(initPath) {
426
570
  let flag = FunctionComponentFlag.None;
427
- if (initPath != null && ast.hasCallInFunctionInitPath("memo", initPath)) flag |= FunctionComponentFlag.Memo;
428
- 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;
429
573
  return flag;
430
574
  }
431
575
  /**
@@ -458,7 +602,7 @@ function isFunctionComponentWrapperCallback(context, node) {
458
602
  * @internal
459
603
  */
460
604
  function getFunctionComponentId(context, node) {
461
- const functionId = ast.getFunctionId(node);
605
+ const functionId = getFunctionId(node);
462
606
  if (functionId != null) return functionId;
463
607
  let parent = node.parent;
464
608
  while (ast.isTypeExpression(parent)) parent = parent.parent;
@@ -550,7 +694,7 @@ function isFunctionComponentDefinition(context, node, hint) {
550
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":
551
695
  if (hint & FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayFlatMapCallback) return false;
552
696
  break;
553
- 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):
554
698
  if (hint & FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArbitraryCallExpressionCallback) return false;
555
699
  break;
556
700
  }
@@ -617,7 +761,7 @@ function isHookId(id) {
617
761
  */
618
762
  function isHookDefinition(node) {
619
763
  if (node == null) return false;
620
- const id = ast.getFunctionId(node);
764
+ const id = getFunctionId(node);
621
765
  switch (id?.type) {
622
766
  case AST_NODE_TYPES.Identifier: return isHookName(id.name);
623
767
  case AST_NODE_TYPES.MemberExpression: return "name" in id.property && isHookName(id.property.name);
@@ -707,8 +851,8 @@ function getFunctionComponentCollector(context, options = {}) {
707
851
  const isExportDefaultDeclaration = exp != null && ast.getUnderlyingExpression(exp.declaration) === node;
708
852
  const id = getFunctionComponentId(context, node);
709
853
  const name = id == null ? null : ast.getFullyQualifiedName(id, getText);
710
- const initPath = ast.getFunctionInitPath(node);
711
- const directives = ast.getFunctionDirectives(node);
854
+ const initPath = getFunctionInitPath(node);
855
+ const directives = getFunctionDirectives(node);
712
856
  const entry = {
713
857
  id,
714
858
  key,
@@ -792,7 +936,7 @@ function getHookCollector(context) {
792
936
  const getText = (n) => context.sourceCode.getText(n);
793
937
  const getCurrentEntry = () => functionEntries.at(-1) ?? null;
794
938
  const onFunctionEnter = (node) => {
795
- const id = ast.getFunctionId(node);
939
+ const id = getFunctionId(node);
796
940
  const key = ulid();
797
941
  const entry = {
798
942
  id,
@@ -990,4 +1134,4 @@ function getTypeVariants(types) {
990
1134
  }
991
1135
 
992
1136
  //#endregion
993
- 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.0-next.0",
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": {
@@ -9,7 +9,7 @@
9
9
  "repository": {
10
10
  "type": "git",
11
11
  "url": "git+https://github.com/Rel1cx/eslint-react.git",
12
- "directory": "packages/@eslint-react/core"
12
+ "directory": "packages/core"
13
13
  },
14
14
  "license": "MIT",
15
15
  "author": "Rel1cx",
@@ -35,17 +35,17 @@
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.0-next.0",
39
- "@eslint-react/jsx": "5.2.0-next.0",
40
- "@eslint-react/shared": "5.2.0-next.0",
41
- "@eslint-react/eslint": "5.2.0-next.0",
42
- "@eslint-react/var": "5.2.0-next.0"
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",
46
46
  "tsdown": "^0.21.7",
47
- "@local/eff": "3.0.0-beta.72",
48
- "@local/configs": "0.0.0"
47
+ "@local/configs": "0.0.0",
48
+ "@local/eff": "3.0.0-beta.72"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "eslint": "^10.2.0",