@eslint-react/core 3.0.0-next.6 → 3.0.0-next.60

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 +111 -165
  2. package/dist/index.js +205 -285
  3. package/package.json +7 -7
package/dist/index.d.ts CHANGED
@@ -6,6 +6,15 @@ import { ESLintUtils, TSESTree as TSESTree$1 } from "@typescript-eslint/utils";
6
6
  import { Scope } from "@typescript-eslint/scope-manager";
7
7
  import * as typescript from "typescript";
8
8
 
9
+ //#region src/api/find-import-source.d.ts
10
+ /**
11
+ * Find the import source of a variable
12
+ * @param name The variable name
13
+ * @param initialScope The initial scope to search
14
+ * @returns The import source or undefined if not found
15
+ */
16
+ declare function findImportSource(name: string, initialScope: Scope): string | undefined;
17
+ //#endregion
9
18
  //#region src/api/is-from-react.d.ts
10
19
  /**
11
20
  * Check if a variable is initialized from React import
@@ -78,19 +87,19 @@ declare const isForwardRefCall: isReactAPICall.ReturnType;
78
87
  declare const isMemoCall: isReactAPICall.ReturnType;
79
88
  declare const isLazyCall: isReactAPICall.ReturnType;
80
89
  //#endregion
81
- //#region src/component/component-detection-hint.d.ts
90
+ //#region src/component/component-detection.d.ts
82
91
  type ComponentDetectionHint = bigint;
83
92
  /**
84
93
  * Hints for component collector
85
94
  */
86
95
  declare const ComponentDetectionHint: {
87
- readonly DoNotIncludeFunctionDefinedOnObjectMethod: bigint;
96
+ readonly DoNotIncludeFunctionDefinedAsArrayFlatMapCallback: bigint;
97
+ readonly DoNotIncludeFunctionDefinedAsArrayMapCallback: bigint;
98
+ readonly DoNotIncludeFunctionDefinedInArrayExpression: bigint;
99
+ readonly DoNotIncludeFunctionDefinedInArrayPattern: bigint;
88
100
  readonly DoNotIncludeFunctionDefinedOnClassMethod: bigint;
89
101
  readonly DoNotIncludeFunctionDefinedOnClassProperty: bigint;
90
- readonly DoNotIncludeFunctionDefinedInArrayPattern: bigint;
91
- readonly DoNotIncludeFunctionDefinedInArrayExpression: bigint;
92
- readonly DoNotIncludeFunctionDefinedAsArrayMapCallback: bigint;
93
- readonly DoNotIncludeFunctionDefinedAsArrayFlatMapCallback: bigint;
102
+ readonly DoNotIncludeFunctionDefinedOnObjectMethod: bigint;
94
103
  readonly None: 0n;
95
104
  readonly DoNotIncludeJsxWithNullValue: bigint;
96
105
  readonly DoNotIncludeJsxWithNumberValue: bigint;
@@ -108,6 +117,15 @@ declare const ComponentDetectionHint: {
108
117
  * Default component detection hint
109
118
  */
110
119
  declare const DEFAULT_COMPONENT_DETECTION_HINT: bigint;
120
+ /**
121
+ * Determine if a function node represents a valid React component definition
122
+ *
123
+ * @param context The rule context
124
+ * @param node The function node to analyze
125
+ * @param hint Component detection hints (bit flags) to customize detection logic
126
+ * @returns `true` if the node is considered a component definition
127
+ */
128
+ declare function isComponentDefinition(context: RuleContext, node: ast.TSESTreeFunction, hint: bigint): boolean;
111
129
  //#endregion
112
130
  //#region src/semantic/semantic-node.d.ts
113
131
  /**
@@ -123,12 +141,12 @@ interface SemanticNode {
123
141
  kind: string;
124
142
  /** The name of the node */
125
143
  name: unit | string;
126
- /** The AST node */
127
- node: TSESTree.Node;
128
144
  /** The flag of the node */
129
145
  flag: bigint;
130
146
  /** The hint of the node */
131
147
  hint: bigint;
148
+ /** The AST node */
149
+ node: TSESTree.Node;
132
150
  }
133
151
  //#endregion
134
152
  //#region src/semantic/semantic-func.d.ts
@@ -138,7 +156,7 @@ interface SemanticNode {
138
156
  */
139
157
  interface SemanticFunc extends SemanticNode {
140
158
  /** The identifier of the function */
141
- id: ast.FunctionID | unit;
159
+ id: ast.FunctionID;
142
160
  /** The AST node of the function */
143
161
  node: ast.TSESTreeFunction;
144
162
  /** The name of the function */
@@ -155,19 +173,6 @@ interface SemanticFunc extends SemanticNode {
155
173
  typeParameters: TSESTree.TSTypeParameterDeclaration | unit;
156
174
  }
157
175
  //#endregion
158
- //#region src/component/component-flag.d.ts
159
- type ComponentFlag = bigint;
160
- /**
161
- * Component flag constants
162
- */
163
- declare const ComponentFlag: {
164
- /** No flags set */None: bigint; /** Indicates the component is a pure component (e.g., extends PureComponent) */
165
- PureComponent: bigint; /** Indicates the component creates elements using `createElement` instead of JSX */
166
- CreateElement: bigint; /** Indicates the component is memoized (e.g., React.memo) */
167
- Memo: bigint; /** Indicates the component forwards a ref (e.g., React.forwardRef) */
168
- ForwardRef: bigint;
169
- };
170
- //#endregion
171
176
  //#region src/component/component-semantic-node.d.ts
172
177
  /**
173
178
  * Represents a React Function Component
@@ -176,7 +181,7 @@ interface FunctionComponentSemanticNode extends SemanticNode {
176
181
  /**
177
182
  * The identifier or identifier sequence of the component
178
183
  */
179
- id: unit | ast.FunctionID;
184
+ id: ast.FunctionID;
180
185
  /**
181
186
  * The kind of component
182
187
  */
@@ -188,11 +193,11 @@ interface FunctionComponentSemanticNode extends SemanticNode {
188
193
  /**
189
194
  * Flags describing the component's characteristics
190
195
  */
191
- flag: ComponentFlag;
196
+ flag: bigint;
192
197
  /**
193
198
  * Hint for how the component was detected
194
199
  */
195
- hint: ComponentDetectionHint;
200
+ hint: bigint;
196
201
  /**
197
202
  * List of expressions returned by the component
198
203
  */
@@ -241,11 +246,11 @@ interface ClassComponentSemanticNode extends SemanticNode {
241
246
  /**
242
247
  * Flags describing the component's characteristics
243
248
  */
244
- flag: ComponentFlag;
249
+ flag: bigint;
245
250
  /**
246
251
  * Hint for how the component was detected
247
252
  */
248
- hint: ComponentDetectionHint;
253
+ hint: bigint;
249
254
  /**
250
255
  * List of methods and properties in the class
251
256
  */
@@ -301,48 +306,8 @@ declare namespace useComponentCollectorLegacy {
301
306
  * @returns The ctx and visitor of the collector
302
307
  */
303
308
  declare function useComponentCollectorLegacy(context: RuleContext): useComponentCollectorLegacy.ReturnType;
304
- /**
305
- * Check whether the given node is a this.setState() call
306
- * @param node The node to check
307
- * @internal
308
- */
309
- declare function isThisSetState(node: TSESTree$1.CallExpression): boolean;
310
- /**
311
- * Check whether the given node is an assignment to this.state
312
- * @param node The node to check
313
- * @internal
314
- */
315
- declare function isAssignmentToThisState(node: TSESTree$1.AssignmentExpression): boolean;
316
- //#endregion
317
- //#region src/component/component-definition.d.ts
318
- /**
319
- * Determine if a function node represents a valid React component definition
320
- *
321
- * @param context The rule context
322
- * @param node The function node to analyze
323
- * @param hint Component detection hints (bit flags) to customize detection logic
324
- * @returns `true` if the node is considered a component definition
325
- */
326
- declare function isComponentDefinition(context: RuleContext, node: ast.TSESTreeFunction, hint: bigint): boolean;
327
- //#endregion
328
- //#region src/component/component-id.d.ts
329
- /**
330
- * Get function component identifier from `const Component = memo(() => {});`
331
- * @param context The rule context
332
- * @param node The function node to analyze
333
- * @returns The function identifier or `unit` if not found
334
- */
335
- declare function getFunctionComponentId(context: RuleContext, node: ast.TSESTreeFunction): ast.FunctionID | unit;
336
- //#endregion
337
- //#region src/component/component-init-path.d.ts
338
- /**
339
- * Get component flag from init path
340
- * @param initPath The init path of the function component
341
- * @returns The component flag
342
- */
343
- declare function getComponentFlagFromInitPath(initPath: FunctionComponentSemanticNode["initPath"]): bigint;
344
309
  //#endregion
345
- //#region src/component/component-is.d.ts
310
+ //#region src/component/component-detection-legacy.d.ts
346
311
  /**
347
312
  * Check if a node is a React class component
348
313
  * @param node The AST node to check
@@ -355,22 +320,6 @@ declare function isClassComponent(node: TSESTree.Node): node is ast.TSESTreeClas
355
320
  * @returns `true` if the node is a PureComponent, `false` otherwise
356
321
  */
357
322
  declare function isPureComponent(node: TSESTree.Node): boolean;
358
- //#endregion
359
- //#region src/component/component-method-callback.d.ts
360
- /**
361
- * Check if the given node is a componentDidMount callback
362
- * @param node The node to check
363
- * @returns True if the node is a componentDidMount callback, false otherwise
364
- */
365
- declare function isComponentDidMountCallback(node: TSESTree.Node): boolean;
366
- /**
367
- * Check if the given node is a componentWillUnmount callback
368
- * @param node The node to check
369
- * @returns True if the node is a componentWillUnmount callback, false otherwise
370
- */
371
- declare function isComponentWillUnmountCallback(node: TSESTree.Node): boolean;
372
- //#endregion
373
- //#region src/component/component-method-is.d.ts
374
323
  declare const isRender: (node: TSESTree.Node) => node is ast.TSESTreeMethodOrProperty;
375
324
  declare const isComponentDidCatch: (node: TSESTree.Node) => node is ast.TSESTreeMethodOrProperty;
376
325
  declare const isComponentDidMount: (node: TSESTree.Node) => node is ast.TSESTreeMethodOrProperty;
@@ -389,28 +338,18 @@ declare const isUnsafeComponentWillUpdate: (node: TSESTree.Node) => node is ast.
389
338
  declare const isGetDefaultProps: (node: TSESTree.Node) => node is ast.TSESTreeMethodOrProperty;
390
339
  declare const isGetDerivedStateFromProps: (node: TSESTree.Node) => node is ast.TSESTreeMethodOrProperty;
391
340
  declare const isGetDerivedStateFromError: (node: TSESTree.Node) => node is ast.TSESTreeMethodOrProperty;
392
- //#endregion
393
- //#region src/component/component-name.d.ts
394
341
  /**
395
- * Check if a string matches the strict component name pattern
396
- * @param name The name to check
397
- */
398
- declare function isComponentName(name: string): boolean;
399
- /**
400
- * Check if a string matches the loose component name pattern
401
- * @param name The name to check
342
+ * Check if the given node is a componentDidMount callback
343
+ * @param node The node to check
344
+ * @returns True if the node is a componentDidMount callback, false otherwise
402
345
  */
403
- declare function isComponentNameLoose(name: string): boolean;
346
+ declare function isComponentDidMountCallback(node: TSESTree.Node): boolean;
404
347
  /**
405
- * Check if a function has a loose component name
406
- * @param context The rule context
407
- * @param fn The function to check
408
- * @param allowNone Whether to allow no name
409
- * @returns Whether the function has a loose component name
348
+ * Check if the given node is a componentWillUnmount callback
349
+ * @param node The node to check
350
+ * @returns True if the node is a componentWillUnmount callback, false otherwise
410
351
  */
411
- declare function isFunctionWithLooseComponentName(context: RuleContext, fn: ast.TSESTreeFunction, allowNone?: boolean): boolean;
412
- //#endregion
413
- //#region src/component/component-render-method.d.ts
352
+ declare function isComponentWillUnmountCallback(node: TSESTree.Node): boolean;
414
353
  /**
415
354
  * Check whether given node is a render method of a class component
416
355
  * @example
@@ -424,57 +363,79 @@ declare function isFunctionWithLooseComponentName(context: RuleContext, fn: ast.
424
363
  * @returns `true` if node is a render function, `false` if not
425
364
  */
426
365
  declare function isRenderMethodLike(node: TSESTree.Node): node is ast.TSESTreeMethodOrProperty;
427
- //#endregion
428
- //#region src/component/component-render-prop.d.ts
429
366
  /**
430
- * Unsafe check whether given node is a render function
367
+ * Check if the given node is a function within a render method of a class component
368
+ *
369
+ * @param node The AST node to check
370
+ * @returns `true` if the node is a render function inside a class component
371
+ *
372
+ * @example
431
373
  * ```tsx
432
- * const renderRow = () => <div />
433
- * ` ^^^^^^^^^^^^`
434
- * _ = <Component renderRow={() => <div />} />
435
- * ` ^^^^^^^^^^^^^ `
374
+ * class Component extends React.Component {
375
+ * renderHeader = () => <div />; // Returns true
376
+ * }
436
377
  * ```
437
- * @param context The rule context
438
- * @param node The AST node to check
439
- * @returns `true` if node is a render function, `false` if not
440
378
  */
441
- declare function isRenderFunctionLoose(context: RuleContext, node: TSESTree.Node): node is ast.TSESTreeFunction;
379
+ declare function isRenderMethodCallback(node: ast.TSESTreeFunction): boolean;
442
380
  /**
443
- * Unsafe check whether given JSXAttribute is a render prop
444
- * ```tsx
445
- * _ = <Component renderRow={() => <div />} />
446
- * ` ^^^^^^^^^^^^^^^^^^^^^^^^^ `
447
- * ```
448
- * @param context The rule context
449
- * @param node The AST node to check
450
- * @returns `true` if node is a render prop, `false` if not
381
+ * Check whether the given node is a this.setState() call
382
+ * @param node The node to check
383
+ * @internal
451
384
  */
452
- declare function isRenderPropLoose(context: RuleContext, node: TSESTree.JSXAttribute): boolean;
385
+ declare function isThisSetState(node: TSESTree.CallExpression): boolean;
453
386
  /**
454
- * Unsafe check whether given node is declared directly inside a render property
455
- * ```tsx
456
- * const rows = { render: () => <div /> }
457
- * ` ^^^^^^^^^^^^^ `
458
- * _ = <Component rows={ [{ render: () => <div /> }] } />
459
- * ` ^^^^^^^^^^^^^ `
460
- * ```
387
+ * Check whether the given node is an assignment to this.state
388
+ * @param node The node to check
461
389
  * @internal
462
- * @param node The AST node to check
463
- * @returns `true` if component is declared inside a render property, `false` if not
464
390
  */
465
- declare function isDirectValueOfRenderPropertyLoose(node: TSESTree.Node): boolean;
391
+ declare function isAssignmentToThisState(node: TSESTree.AssignmentExpression): boolean;
392
+ //#endregion
393
+ //#region src/component/component-flag.d.ts
466
394
  /**
467
- * Unsafe check whether given node is declared inside a render prop
468
- * ```tsx
469
- * _ = <Component renderRow={"node"} />
470
- * ` ^^^^^^ `
471
- * _ = <Component rows={ [{ render: "node" }] } />
472
- * ` ^^^^^^ `
473
- * ```
474
- * @param node The AST node to check
475
- * @returns `true` if component is declared inside a render prop, `false` if not
395
+ * Component flag constants
396
+ */
397
+ declare const ComponentFlag: {
398
+ /** Indicates the component creates elements using `createElement` instead of JSX */CreateElement: bigint; /** Indicates the component forwards a ref (e.g., React.forwardRef) */
399
+ ForwardRef: bigint; /** Indicates the component is memoized (e.g., React.memo) */
400
+ Memo: bigint; /** No flags set */
401
+ None: bigint; /** Indicates the component is a pure component (e.g., extends PureComponent) */
402
+ PureComponent: bigint;
403
+ };
404
+ /**
405
+ * Get component flag from init path
406
+ * @param initPath The init path of the function component
407
+ * @returns The component flag
476
408
  */
477
- declare function isDeclaredInRenderPropLoose(node: TSESTree.Node): boolean;
409
+ declare function getComponentFlagFromInitPath(initPath: FunctionComponentSemanticNode["initPath"]): bigint;
410
+ //#endregion
411
+ //#region src/component/component-id.d.ts
412
+ /**
413
+ * Get function component identifier from `const Component = memo(() => {});`
414
+ * @param context The rule context
415
+ * @param node The function node to analyze
416
+ * @returns The function identifier or `unit` if not found
417
+ */
418
+ declare function getFunctionComponentId(context: RuleContext, node: ast.TSESTreeFunction): ast.FunctionID;
419
+ //#endregion
420
+ //#region src/component/component-name.d.ts
421
+ /**
422
+ * Check if a string matches the strict component name pattern
423
+ * @param name The name to check
424
+ */
425
+ declare function isComponentName(name: string): boolean;
426
+ /**
427
+ * Check if a string matches the loose component name pattern
428
+ * @param name The name to check
429
+ */
430
+ declare function isComponentNameLoose(name: string): boolean;
431
+ /**
432
+ * Check if a function has a loose component name
433
+ * @param context The rule context
434
+ * @param fn The function to check
435
+ * @param allowNone Whether to allow no name
436
+ * @returns Whether the function has a loose component name
437
+ */
438
+ declare function isFunctionWithLooseComponentName(context: RuleContext, fn: ast.TSESTreeFunction, allowNone?: boolean): boolean;
478
439
  //#endregion
479
440
  //#region src/component/component-wrapper.d.ts
480
441
  /**
@@ -510,7 +471,7 @@ declare function isComponentWrapperCallbackLoose(context: RuleContext, node: TSE
510
471
  /**
511
472
  * Represents the kind of a React function
512
473
  */
513
- type ComponentKind = "client-function" | "server-function";
474
+ type FunctionKind = "client-function" | "server-function";
514
475
  //#endregion
515
476
  //#region src/function/function-semantic-node.d.ts
516
477
  /**
@@ -536,24 +497,6 @@ interface ServerFunctionSemanticNode extends SemanticFunc {
536
497
  */
537
498
  type FunctionSemanticNode = ClientFunctionSemanticNode | ServerFunctionSemanticNode;
538
499
  //#endregion
539
- //#region src/hierarchy/find-enclosing-component-or-hook.d.ts
540
- type FindEnclosingComponentOrHookFilter = (n: TSESTree.Node, name: string | null) => boolean;
541
- /**
542
- * Find the enclosing React component or hook for a given AST node
543
- * @param node The AST node to start the search from
544
- * @param test Optional test function to customize component or hook identification
545
- * @returns The enclosing component or hook node, or `null` if none is ASAST.
546
- */
547
- declare function findEnclosingComponentOrHook(node: TSESTree.Node | unit, test?: FindEnclosingComponentOrHookFilter): TSESTree.ArrowFunctionExpression | TSESTree.FunctionDeclarationWithName | TSESTree.FunctionDeclarationWithOptionalName | TSESTree.FunctionExpression | undefined;
548
- //#endregion
549
- //#region src/hierarchy/is-inside-component-or-hook.d.ts
550
- /**
551
- * Check if a given AST node is inside a React component or hook
552
- * @param node The AST node to check
553
- * @returns True if the node is inside a component or hook, false otherwise
554
- */
555
- declare function isInsideComponentOrHook(node: TSESTree.Node | unit): boolean;
556
- //#endregion
557
500
  //#region src/hook/hook-callback.d.ts
558
501
  /**
559
502
  * Determine if a node is the setup function passed to a useEffect-like hook
@@ -573,7 +516,7 @@ declare function isUseEffectCleanupCallback(node: TSESTree.Node | unit): boolean
573
516
  */
574
517
  interface HookSemanticNode extends SemanticNode {
575
518
  /** The identifier of the hook */
576
- id: ast.FunctionID | unit;
519
+ id: ast.FunctionID;
577
520
  /** The AST node of the hook */
578
521
  node: ast.TSESTreeFunction;
579
522
  /** The name of the hook */
@@ -889,7 +832,10 @@ declare function findParentJsxAttribute(node: TSESTree.Node, test?: (node: TSEST
889
832
  */
890
833
  declare function stringifyJsx(node: TSESTree$1.JSXIdentifier | TSESTree$1.JSXNamespacedName | TSESTree$1.JSXMemberExpression | TSESTree$1.JSXOpeningElement | TSESTree$1.JSXClosingElement | TSESTree$1.JSXOpeningFragment | TSESTree$1.JSXClosingFragment | TSESTree$1.JSXText): string;
891
834
  //#endregion
892
- //#region src/ref/is-from-ref.d.ts
835
+ //#region src/ref/ref-id.d.ts
836
+ declare function isRefId(node: TSESTree.Expression | TSESTree.PrivateIdentifier): boolean;
837
+ //#endregion
838
+ //#region src/ref/ref-init.d.ts
893
839
  /**
894
840
  * Check if the variable with the given name is initialized or derived from a ref
895
841
  * @param name The variable name
@@ -911,6 +857,6 @@ declare function getRefInit(name: string, initialScope: Scope): TSESTree$1.Expre
911
857
  * @param name The name to check
912
858
  * @returns True if the name is "ref" or ends with "Ref"
913
859
  */
914
- declare function isRefName(name: string): boolean;
860
+ declare function isRefLikeName(name: string): boolean;
915
861
  //#endregion
916
- export { ClassComponentSemanticNode, ClientFunctionSemanticNode, ComponentDetectionHint, ComponentFlag, ComponentKind, ComponentSemanticNode, DEFAULT_COMPONENT_DETECTION_HINT, DEFAULT_JSX_DETECTION_HINT, FindEnclosingComponentOrHookFilter, FunctionComponentSemanticNode, FunctionSemanticNode, HookSemanticNode, JsxAttributeValue, JsxConfig, JsxDetectionHint, JsxEmit, REACT_BUILTIN_HOOK_NAMES, SemanticFunc, SemanticNode, ServerFunctionSemanticNode, findEnclosingComponentOrHook, findParentJsxAttribute, getComponentFlagFromInitPath, getFunctionComponentId, getJsxAttribute, getJsxAttributeName, getJsxConfigFromAnnotation, getJsxConfigFromContext, getJsxElementType, getRefInit, isAssignmentToThisState, isCaptureOwnerStack, isCaptureOwnerStackCall, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isCloneElement, isCloneElementCall, isComponentDefinition, isComponentDidCatch, isComponentDidMount, isComponentDidMountCallback, isComponentDidUpdate, isComponentName, isComponentNameLoose, isComponentWillMount, isComponentWillReceiveProps, isComponentWillUnmount, isComponentWillUnmountCallback, isComponentWillUpdate, isComponentWrapperCall, isComponentWrapperCallLoose, isComponentWrapperCallback, isComponentWrapperCallbackLoose, isCreateContext, isCreateContextCall, isCreateElement, isCreateElementCall, isCreateRef, isCreateRefCall, isDeclaredInRenderPropLoose, isDirectValueOfRenderPropertyLoose, isForwardRef, isForwardRefCall, isFunctionWithLooseComponentName, isGetChildContext, isGetDefaultProps, isGetDerivedStateFromError, isGetDerivedStateFromProps, isGetInitialState, isGetSnapshotBeforeUpdate, isHook, isHookCall, isHookCallWithName, isHookId, isHookName, isInitializedFromReact, isInitializedFromReactNative, isInitializedFromRef, isInsideComponentOrHook, isJsxFragmentElement, isJsxHostElement, isJsxLike, isJsxText, isLazy, isLazyCall, isMemo, isMemoCall, isPureComponent, isReactAPI, isReactAPICall, isRefName, isRender, isRenderFunctionLoose, isRenderMethodLike, isRenderPropLoose, isShouldComponentUpdate, isThisSetState, isUnsafeComponentWillMount, isUnsafeComponentWillReceiveProps, isUnsafeComponentWillUpdate, isUseActionStateCall, isUseCall, isUseCallbackCall, isUseContextCall, isUseDebugValueCall, isUseDeferredValueCall, isUseEffectCall, isUseEffectCleanupCallback, isUseEffectLikeCall, isUseEffectSetupCallback, isUseFormStatusCall, isUseIdCall, isUseImperativeHandleCall, isUseInsertionEffectCall, isUseLayoutEffectCall, isUseMemoCall, isUseOptimisticCall, isUseReducerCall, isUseRefCall, isUseStateCall, isUseStateLikeCall, isUseSyncExternalStoreCall, isUseTransitionCall, resolveJsxAttributeValue, stringifyJsx, useComponentCollector, useComponentCollectorLegacy, useHookCollector };
862
+ export { ClassComponentSemanticNode, ClientFunctionSemanticNode, ComponentDetectionHint, ComponentFlag, ComponentSemanticNode, DEFAULT_COMPONENT_DETECTION_HINT, DEFAULT_JSX_DETECTION_HINT, FunctionComponentSemanticNode, FunctionKind, FunctionSemanticNode, HookSemanticNode, JsxAttributeValue, JsxConfig, JsxDetectionHint, JsxEmit, REACT_BUILTIN_HOOK_NAMES, SemanticFunc, SemanticNode, ServerFunctionSemanticNode, findImportSource, findParentJsxAttribute, getComponentFlagFromInitPath, getFunctionComponentId, getJsxAttribute, getJsxAttributeName, getJsxConfigFromAnnotation, getJsxConfigFromContext, getJsxElementType, getRefInit, isAssignmentToThisState, isCaptureOwnerStack, isCaptureOwnerStackCall, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isCloneElement, isCloneElementCall, isComponentDefinition, isComponentDidCatch, isComponentDidMount, isComponentDidMountCallback, isComponentDidUpdate, isComponentName, isComponentNameLoose, isComponentWillMount, isComponentWillReceiveProps, isComponentWillUnmount, isComponentWillUnmountCallback, isComponentWillUpdate, isComponentWrapperCall, isComponentWrapperCallLoose, isComponentWrapperCallback, isComponentWrapperCallbackLoose, isCreateContext, isCreateContextCall, isCreateElement, isCreateElementCall, isCreateRef, isCreateRefCall, isForwardRef, isForwardRefCall, isFunctionWithLooseComponentName, isGetChildContext, isGetDefaultProps, isGetDerivedStateFromError, isGetDerivedStateFromProps, isGetInitialState, isGetSnapshotBeforeUpdate, isHook, isHookCall, isHookCallWithName, isHookId, isHookName, isInitializedFromReact, isInitializedFromReactNative, isInitializedFromRef, isJsxFragmentElement, isJsxHostElement, isJsxLike, isJsxText, isLazy, isLazyCall, isMemo, isMemoCall, isPureComponent, isReactAPI, isReactAPICall, isRefId, isRefLikeName, isRender, isRenderMethodCallback, isRenderMethodLike, isShouldComponentUpdate, isThisSetState, isUnsafeComponentWillMount, isUnsafeComponentWillReceiveProps, isUnsafeComponentWillUpdate, isUseActionStateCall, isUseCall, isUseCallbackCall, isUseContextCall, isUseDebugValueCall, isUseDeferredValueCall, isUseEffectCall, isUseEffectCleanupCallback, isUseEffectLikeCall, isUseEffectSetupCallback, isUseFormStatusCall, isUseIdCall, isUseImperativeHandleCall, isUseInsertionEffectCall, isUseLayoutEffectCall, isUseMemoCall, isUseOptimisticCall, isUseReducerCall, isUseRefCall, isUseStateCall, isUseStateLikeCall, isUseSyncExternalStoreCall, isUseTransitionCall, resolveJsxAttributeValue, stringifyJsx, useComponentCollector, useComponentCollectorLegacy, useHookCollector };
package/dist/index.js CHANGED
@@ -1,12 +1,55 @@
1
- import { findImportSource, findProperty, findVariable, getVariableDefinitionNode } from "@eslint-react/var";
2
1
  import * as ast from "@eslint-react/ast";
3
2
  import { constFalse, constTrue, dual, flip, getOrElseUpdate, identity, unit } from "@eslint-react/eff";
3
+ import { findVariable, getVariableInitializer } from "@eslint-react/var";
4
4
  import { AST_NODE_TYPES } from "@typescript-eslint/types";
5
+ import { P, match } from "ts-pattern";
5
6
  import { IdGenerator, RE_ANNOTATION_JSX, RE_ANNOTATION_JSX_FRAG, RE_ANNOTATION_JSX_IMPORT_SOURCE, RE_ANNOTATION_JSX_RUNTIME, RE_COMPONENT_NAME, RE_COMPONENT_NAME_LOOSE } from "@eslint-react/shared";
6
7
  import { getStaticValue } from "@typescript-eslint/utils/ast-utils";
7
- import { P, match } from "ts-pattern";
8
8
  import { AST_NODE_TYPES as AST_NODE_TYPES$1 } from "@typescript-eslint/utils";
9
9
 
10
+ //#region src/api/find-import-source.ts
11
+ /**
12
+ * Get the arguments of a require expression
13
+ * @param node The node to match
14
+ * @returns The require expression arguments or undefined if the node is not a require expression
15
+ * @internal
16
+ */
17
+ function getRequireExpressionArguments(node) {
18
+ return match(node).with({
19
+ type: AST_NODE_TYPES.CallExpression,
20
+ arguments: P.select(),
21
+ callee: {
22
+ type: AST_NODE_TYPES.Identifier,
23
+ name: "require"
24
+ }
25
+ }, identity).with({
26
+ type: AST_NODE_TYPES.MemberExpression,
27
+ object: P.select()
28
+ }, getRequireExpressionArguments).otherwise(() => null);
29
+ }
30
+ /**
31
+ * Find the import source of a variable
32
+ * @param name The variable name
33
+ * @param initialScope The initial scope to search
34
+ * @returns The import source or undefined if not found
35
+ */
36
+ function findImportSource(name, initialScope) {
37
+ const latestDef = findVariable(name, initialScope)?.defs.at(-1);
38
+ if (latestDef == null) return unit;
39
+ const { node, parent } = latestDef;
40
+ if (node.type === AST_NODE_TYPES.VariableDeclarator && node.init != null) {
41
+ const { init } = node;
42
+ if (init.type === AST_NODE_TYPES.MemberExpression && init.object.type === AST_NODE_TYPES.Identifier) return findImportSource(init.object.name, initialScope);
43
+ if (init.type === AST_NODE_TYPES.Identifier) return findImportSource(init.name, initialScope);
44
+ const arg0 = getRequireExpressionArguments(init)?.[0];
45
+ if (arg0 == null || !ast.isLiteral(arg0, "string")) return unit;
46
+ return arg0.value;
47
+ }
48
+ if (parent?.type === AST_NODE_TYPES.ImportDeclaration) return parent.source.value;
49
+ return unit;
50
+ }
51
+
52
+ //#endregion
10
53
  //#region src/api/is-from-react.ts
11
54
  /**
12
55
  * Check if a variable is initialized from React import
@@ -260,7 +303,7 @@ function isHookId(id) {
260
303
 
261
304
  //#endregion
262
305
  //#region src/hook/hook-collector.ts
263
- const idGen$2 = new IdGenerator("hook_");
306
+ const idGen$2 = new IdGenerator("hook:");
264
307
  /**
265
308
  * Get a ctx and visitor object for the rule to collect hooks
266
309
  * @param context The ESLint rule context
@@ -284,11 +327,11 @@ function useHookCollector(context) {
284
327
  key,
285
328
  kind: "function",
286
329
  name: ast.getFullyQualifiedName(id, getText),
287
- node,
288
330
  directives: [],
289
331
  flag: 0n,
290
332
  hint: 0n,
291
- hookCalls: []
333
+ hookCalls: [],
334
+ node
292
335
  });
293
336
  };
294
337
  const onFunctionExit = () => {
@@ -369,11 +412,11 @@ function getJsxAttribute(context, node, initialScope) {
369
412
  if (attr.type === AST_NODE_TYPES.JSXAttribute) return getJsxAttributeName(context, attr) === name;
370
413
  switch (attr.argument.type) {
371
414
  case AST_NODE_TYPES.Identifier: {
372
- const variableNode = getVariableDefinitionNode(findVariable(attr.argument.name, scope), 0);
373
- if (variableNode?.type === AST_NODE_TYPES.ObjectExpression) return findProperty(name, variableNode.properties, scope) != null;
415
+ const initNode = getVariableInitializer(findVariable(attr.argument.name, scope), 0);
416
+ if (initNode?.type === AST_NODE_TYPES.ObjectExpression) return ast.findProperty(initNode.properties, name) != null;
374
417
  return false;
375
418
  }
376
- case AST_NODE_TYPES.ObjectExpression: return findProperty(name, attr.argument.properties, scope) != null;
419
+ case AST_NODE_TYPES.ObjectExpression: return ast.findProperty(attr.argument.properties, name) != null;
377
420
  }
378
421
  return false;
379
422
  });
@@ -597,7 +640,7 @@ function isJsxLike(code, node, hint = DEFAULT_JSX_DETECTION_HINT) {
597
640
  const { name } = node;
598
641
  if (name === "undefined") return !(hint & JsxDetectionHint.DoNotIncludeJsxWithUndefinedValue);
599
642
  if (ast.isJSXTagNameExpression(node)) return true;
600
- return isJsxLike(code, getVariableDefinitionNode(findVariable(name, code.getScope(node)), 0), hint);
643
+ return isJsxLike(code, getVariableInitializer(findVariable(name, code.getScope(node)), 0), hint);
601
644
  }
602
645
  }
603
646
  return false;
@@ -666,27 +709,7 @@ function findParentJsxAttribute(node, test = constTrue) {
666
709
  }
667
710
 
668
711
  //#endregion
669
- //#region src/component/component-detection-hint.ts
670
- /**
671
- * Hints for component collector
672
- */
673
- const ComponentDetectionHint = {
674
- ...JsxDetectionHint,
675
- DoNotIncludeFunctionDefinedOnObjectMethod: 1n << 64n,
676
- DoNotIncludeFunctionDefinedOnClassMethod: 1n << 65n,
677
- DoNotIncludeFunctionDefinedOnClassProperty: 1n << 66n,
678
- DoNotIncludeFunctionDefinedInArrayPattern: 1n << 67n,
679
- DoNotIncludeFunctionDefinedInArrayExpression: 1n << 68n,
680
- DoNotIncludeFunctionDefinedAsArrayMapCallback: 1n << 69n,
681
- DoNotIncludeFunctionDefinedAsArrayFlatMapCallback: 1n << 70n
682
- };
683
- /**
684
- * Default component detection hint
685
- */
686
- const DEFAULT_COMPONENT_DETECTION_HINT = 0n | ComponentDetectionHint.DoNotIncludeJsxWithBigIntValue | ComponentDetectionHint.DoNotIncludeJsxWithBooleanValue | ComponentDetectionHint.DoNotIncludeJsxWithNumberValue | ComponentDetectionHint.DoNotIncludeJsxWithStringValue | ComponentDetectionHint.DoNotIncludeJsxWithUndefinedValue | ComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayFlatMapCallback | ComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayMapCallback | ComponentDetectionHint.DoNotIncludeFunctionDefinedInArrayExpression | ComponentDetectionHint.DoNotIncludeFunctionDefinedInArrayPattern | ComponentDetectionHint.RequireAllArrayElementsToBeJsx | ComponentDetectionHint.RequireBothBranchesOfConditionalExpressionToBeJsx | ComponentDetectionHint.RequireBothSidesOfLogicalExpressionToBeJsx;
687
-
688
- //#endregion
689
- //#region src/component/component-is.ts
712
+ //#region src/component/component-detection-legacy.ts
690
713
  /**
691
714
  * Check if a node is a React class component
692
715
  * @param node The AST node to check
@@ -717,6 +740,99 @@ function isPureComponent(node) {
717
740
  }
718
741
  return false;
719
742
  }
743
+ /**
744
+ * Create a lifecycle method checker function
745
+ * @param methodName The lifecycle method name
746
+ * @param isStatic Whether the method is static
747
+ */
748
+ function createLifecycleChecker(methodName, isStatic = false) {
749
+ return (node) => ast.isMethodOrProperty(node) && node.static === isStatic && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === methodName;
750
+ }
751
+ const isRender = createLifecycleChecker("render");
752
+ const isComponentDidCatch = createLifecycleChecker("componentDidCatch");
753
+ const isComponentDidMount = createLifecycleChecker("componentDidMount");
754
+ const isComponentDidUpdate = createLifecycleChecker("componentDidUpdate");
755
+ const isComponentWillMount = createLifecycleChecker("componentWillMount");
756
+ const isComponentWillReceiveProps = createLifecycleChecker("componentWillReceiveProps");
757
+ const isComponentWillUnmount = createLifecycleChecker("componentWillUnmount");
758
+ const isComponentWillUpdate = createLifecycleChecker("componentWillUpdate");
759
+ const isGetChildContext = createLifecycleChecker("getChildContext");
760
+ const isGetInitialState = createLifecycleChecker("getInitialState");
761
+ const isGetSnapshotBeforeUpdate = createLifecycleChecker("getSnapshotBeforeUpdate");
762
+ const isShouldComponentUpdate = createLifecycleChecker("shouldComponentUpdate");
763
+ const isUnsafeComponentWillMount = createLifecycleChecker("UNSAFE_componentWillMount");
764
+ const isUnsafeComponentWillReceiveProps = createLifecycleChecker("UNSAFE_componentWillReceiveProps");
765
+ const isUnsafeComponentWillUpdate = createLifecycleChecker("UNSAFE_componentWillUpdate");
766
+ const isGetDefaultProps = createLifecycleChecker("getDefaultProps", true);
767
+ const isGetDerivedStateFromProps = createLifecycleChecker("getDerivedStateFromProps", true);
768
+ const isGetDerivedStateFromError = createLifecycleChecker("getDerivedStateFromError", true);
769
+ /**
770
+ * Check if the given node is a componentDidMount callback
771
+ * @param node The node to check
772
+ * @returns True if the node is a componentDidMount callback, false otherwise
773
+ */
774
+ function isComponentDidMountCallback(node) {
775
+ return ast.isFunction(node) && isComponentDidMount(node.parent) && node.parent.value === node;
776
+ }
777
+ /**
778
+ * Check if the given node is a componentWillUnmount callback
779
+ * @param node The node to check
780
+ * @returns True if the node is a componentWillUnmount callback, false otherwise
781
+ */
782
+ function isComponentWillUnmountCallback(node) {
783
+ return ast.isFunction(node) && isComponentWillUnmount(node.parent) && node.parent.value === node;
784
+ }
785
+ /**
786
+ * Check whether given node is a render method of a class component
787
+ * @example
788
+ * ```tsx
789
+ * class Component extends React.Component {
790
+ * renderHeader = () => <div />;
791
+ * renderFooter = () => <div />;
792
+ * }
793
+ * ```
794
+ * @param node The AST node to check
795
+ * @returns `true` if node is a render function, `false` if not
796
+ */
797
+ function isRenderMethodLike(node) {
798
+ return ast.isMethodOrProperty(node) && node.key.type === AST_NODE_TYPES.Identifier && node.key.name.startsWith("render") && node.parent.parent.type === AST_NODE_TYPES.ClassDeclaration;
799
+ }
800
+ /**
801
+ * Check if the given node is a function within a render method of a class component
802
+ *
803
+ * @param node The AST node to check
804
+ * @returns `true` if the node is a render function inside a class component
805
+ *
806
+ * @example
807
+ * ```tsx
808
+ * class Component extends React.Component {
809
+ * renderHeader = () => <div />; // Returns true
810
+ * }
811
+ * ```
812
+ */
813
+ function isRenderMethodCallback(node) {
814
+ const parent = node.parent;
815
+ const greatGrandparent = parent.parent?.parent;
816
+ return greatGrandparent != null && isRenderMethodLike(parent) && isClassComponent(greatGrandparent);
817
+ }
818
+ /**
819
+ * Check whether the given node is a this.setState() call
820
+ * @param node The node to check
821
+ * @internal
822
+ */
823
+ function isThisSetState(node) {
824
+ const { callee } = node;
825
+ return callee.type === AST_NODE_TYPES.MemberExpression && ast.isThisExpressionLoose(callee.object) && callee.property.type === AST_NODE_TYPES.Identifier && callee.property.name === "setState";
826
+ }
827
+ /**
828
+ * Check whether the given node is an assignment to this.state
829
+ * @param node The node to check
830
+ * @internal
831
+ */
832
+ function isAssignmentToThisState(node) {
833
+ const { left } = node;
834
+ return left.type === AST_NODE_TYPES.MemberExpression && ast.isThisExpressionLoose(left.object) && ast.getPropertyName(left.property) === "state";
835
+ }
720
836
 
721
837
  //#endregion
722
838
  //#region src/component/component-wrapper.ts
@@ -814,75 +930,24 @@ function isFunctionWithLooseComponentName(context, fn, allowNone = false) {
814
930
  }
815
931
 
816
932
  //#endregion
817
- //#region src/component/component-render-method.ts
933
+ //#region src/component/component-detection.ts
818
934
  /**
819
- * Check whether given node is a render method of a class component
820
- * @example
821
- * ```tsx
822
- * class Component extends React.Component {
823
- * renderHeader = () => <div />;
824
- * renderFooter = () => <div />;
825
- * }
826
- * ```
827
- * @param node The AST node to check
828
- * @returns `true` if node is a render function, `false` if not
829
- */
830
- function isRenderMethodLike(node) {
831
- return ast.isMethodOrProperty(node) && node.key.type === AST_NODE_TYPES.Identifier && node.key.name.startsWith("render") && node.parent.parent.type === AST_NODE_TYPES.ClassDeclaration;
832
- }
833
-
834
- //#endregion
835
- //#region src/component/component-definition.ts
836
- /**
837
- * Check if the given node is a function within a render method of a class component.
838
- *
839
- * @param node The AST node to check
840
- * @returns `true` if the node is a render function inside a class component
841
- *
842
- * @example
843
- * ```tsx
844
- * class Component extends React.Component {
845
- * renderHeader = () => <div />; // Returns true
846
- * }
847
- * ```
848
- */
849
- function isRenderMethodCallback(node) {
850
- const parent = node.parent;
851
- const greatGrandparent = parent.parent?.parent;
852
- return greatGrandparent != null && isRenderMethodLike(parent) && isClassComponent(greatGrandparent);
853
- }
854
- /**
855
- * Check if a function node should be excluded based on provided detection hints
856
- *
857
- * @param node The function node to check
858
- * @param hint Component detection hints as bit flags
859
- * @returns `true` if the function matches an exclusion hint
935
+ * Hints for component collector
860
936
  */
861
- function shouldExcludeBasedOnHint(node, hint) {
862
- switch (true) {
863
- case hint & ComponentDetectionHint.DoNotIncludeFunctionDefinedOnObjectMethod && ast.isOneOf([AST_NODE_TYPES.ArrowFunctionExpression, AST_NODE_TYPES.FunctionExpression])(node) && node.parent.type === AST_NODE_TYPES.Property && node.parent.parent.type === AST_NODE_TYPES.ObjectExpression: return true;
864
- case hint & ComponentDetectionHint.DoNotIncludeFunctionDefinedOnClassMethod && ast.isOneOf([AST_NODE_TYPES.ArrowFunctionExpression, AST_NODE_TYPES.FunctionExpression])(node) && node.parent.type === AST_NODE_TYPES.MethodDefinition: return true;
865
- case hint & ComponentDetectionHint.DoNotIncludeFunctionDefinedOnClassProperty && ast.isOneOf([AST_NODE_TYPES.ArrowFunctionExpression, AST_NODE_TYPES.FunctionExpression])(node) && node.parent.type === AST_NODE_TYPES.Property: return true;
866
- case hint & ComponentDetectionHint.DoNotIncludeFunctionDefinedInArrayPattern && node.parent.type === AST_NODE_TYPES.ArrayPattern: return true;
867
- case hint & ComponentDetectionHint.DoNotIncludeFunctionDefinedInArrayExpression && node.parent.type === AST_NODE_TYPES.ArrayExpression: return true;
868
- case hint & ComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayMapCallback && node.parent.type === AST_NODE_TYPES.CallExpression && node.parent.callee.type === AST_NODE_TYPES.MemberExpression && node.parent.callee.property.type === AST_NODE_TYPES.Identifier && node.parent.callee.property.name === "map": return true;
869
- case hint & ComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayFlatMapCallback && node.parent.type === AST_NODE_TYPES.CallExpression && node.parent.callee.type === AST_NODE_TYPES.MemberExpression && node.parent.callee.property.type === AST_NODE_TYPES.Identifier && node.parent.callee.property.name === "flatMap": return true;
870
- }
871
- return false;
872
- }
937
+ const ComponentDetectionHint = {
938
+ ...JsxDetectionHint,
939
+ DoNotIncludeFunctionDefinedAsArrayFlatMapCallback: 1n << 17n,
940
+ DoNotIncludeFunctionDefinedAsArrayMapCallback: 1n << 16n,
941
+ DoNotIncludeFunctionDefinedInArrayExpression: 1n << 15n,
942
+ DoNotIncludeFunctionDefinedInArrayPattern: 1n << 14n,
943
+ DoNotIncludeFunctionDefinedOnClassMethod: 1n << 12n,
944
+ DoNotIncludeFunctionDefinedOnClassProperty: 1n << 13n,
945
+ DoNotIncludeFunctionDefinedOnObjectMethod: 1n << 11n
946
+ };
873
947
  /**
874
- * Determine if the node is an argument within `createElement`'s children list (3rd argument onwards)
875
- *
876
- * @param context The rule context
877
- * @param node The AST node to check
878
- * @returns `true` if the node is passed as a child to `createElement`
948
+ * Default component detection hint
879
949
  */
880
- function isChildrenOfCreateElement(context, node) {
881
- const parent = node.parent;
882
- if (parent?.type !== AST_NODE_TYPES.CallExpression) return false;
883
- if (!isCreateElementCall(context, parent)) return false;
884
- return parent.arguments.slice(2).some((arg) => arg === node);
885
- }
950
+ const DEFAULT_COMPONENT_DETECTION_HINT = 0n | ComponentDetectionHint.DoNotIncludeJsxWithBigIntValue | ComponentDetectionHint.DoNotIncludeJsxWithBooleanValue | ComponentDetectionHint.DoNotIncludeJsxWithNumberValue | ComponentDetectionHint.DoNotIncludeJsxWithStringValue | ComponentDetectionHint.DoNotIncludeJsxWithUndefinedValue | ComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayFlatMapCallback | ComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayMapCallback | ComponentDetectionHint.DoNotIncludeFunctionDefinedInArrayExpression | ComponentDetectionHint.DoNotIncludeFunctionDefinedInArrayPattern | ComponentDetectionHint.RequireAllArrayElementsToBeJsx | ComponentDetectionHint.RequireBothBranchesOfConditionalExpressionToBeJsx | ComponentDetectionHint.RequireBothSidesOfLogicalExpressionToBeJsx;
886
951
  /**
887
952
  * Determine if a function node represents a valid React component definition
888
953
  *
@@ -893,8 +958,33 @@ function isChildrenOfCreateElement(context, node) {
893
958
  */
894
959
  function isComponentDefinition(context, node, hint) {
895
960
  if (!isFunctionWithLooseComponentName(context, node, true)) return false;
896
- if (isChildrenOfCreateElement(context, node) || isRenderMethodCallback(node)) return false;
897
- if (shouldExcludeBasedOnHint(node, hint)) return false;
961
+ switch (true) {
962
+ case node.parent.type === AST_NODE_TYPES.CallExpression && isCreateElementCall(context, node.parent) && node.parent.arguments.slice(2).some((arg) => arg === node): return false;
963
+ case isRenderMethodCallback(node): return false;
964
+ }
965
+ switch (true) {
966
+ case ast.isOneOf([AST_NODE_TYPES.ArrowFunctionExpression, AST_NODE_TYPES.FunctionExpression])(node) && node.parent.type === AST_NODE_TYPES.Property && node.parent.parent.type === AST_NODE_TYPES.ObjectExpression:
967
+ if (hint & ComponentDetectionHint.DoNotIncludeFunctionDefinedOnObjectMethod) return false;
968
+ break;
969
+ case ast.isOneOf([AST_NODE_TYPES.ArrowFunctionExpression, AST_NODE_TYPES.FunctionExpression])(node) && node.parent.type === AST_NODE_TYPES.MethodDefinition:
970
+ if (hint & ComponentDetectionHint.DoNotIncludeFunctionDefinedOnClassMethod) return false;
971
+ break;
972
+ case ast.isOneOf([AST_NODE_TYPES.ArrowFunctionExpression, AST_NODE_TYPES.FunctionExpression])(node) && node.parent.type === AST_NODE_TYPES.Property:
973
+ if (hint & ComponentDetectionHint.DoNotIncludeFunctionDefinedOnClassProperty) return false;
974
+ break;
975
+ case node.parent.type === AST_NODE_TYPES.ArrayPattern:
976
+ if (hint & ComponentDetectionHint.DoNotIncludeFunctionDefinedInArrayPattern) return false;
977
+ break;
978
+ case node.parent.type === AST_NODE_TYPES.ArrayExpression:
979
+ if (hint & ComponentDetectionHint.DoNotIncludeFunctionDefinedInArrayExpression) return false;
980
+ break;
981
+ case node.parent.type === AST_NODE_TYPES.CallExpression && node.parent.callee.type === AST_NODE_TYPES.MemberExpression && node.parent.callee.property.type === AST_NODE_TYPES.Identifier && node.parent.callee.property.name === "map":
982
+ if (hint & ComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayMapCallback) return false;
983
+ break;
984
+ case node.parent.type === AST_NODE_TYPES.CallExpression && node.parent.callee.type === AST_NODE_TYPES.MemberExpression && node.parent.callee.property.type === AST_NODE_TYPES.Identifier && node.parent.callee.property.name === "flatMap":
985
+ if (hint & ComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayFlatMapCallback) return false;
986
+ break;
987
+ }
898
988
  const significantParent = ast.findParentNode(node, ast.isOneOf([
899
989
  AST_NODE_TYPES.JSXExpressionContainer,
900
990
  AST_NODE_TYPES.ArrowFunctionExpression,
@@ -913,15 +1003,12 @@ function isComponentDefinition(context, node, hint) {
913
1003
  * Component flag constants
914
1004
  */
915
1005
  const ComponentFlag = {
916
- None: 0n,
917
- PureComponent: 1n << 0n,
918
1006
  CreateElement: 1n << 1n,
1007
+ ForwardRef: 1n << 3n,
919
1008
  Memo: 1n << 2n,
920
- ForwardRef: 1n << 3n
1009
+ None: 0n,
1010
+ PureComponent: 1n << 0n
921
1011
  };
922
-
923
- //#endregion
924
- //#region src/component/component-init-path.ts
925
1012
  /**
926
1013
  * Get component flag from init path
927
1014
  * @param initPath The init path of the function component
@@ -936,7 +1023,7 @@ function getComponentFlagFromInitPath(initPath) {
936
1023
 
937
1024
  //#endregion
938
1025
  //#region src/component/component-collector.ts
939
- const idGen$1 = new IdGenerator("function_component_");
1026
+ const idGen$1 = new IdGenerator("function-component:");
940
1027
  /**
941
1028
  * Get a ctx and visitor object for the rule to collect function components
942
1029
  * @param context The ESLint rule context
@@ -963,7 +1050,6 @@ function useComponentCollector(context, options = {}) {
963
1050
  key,
964
1051
  kind: "function-component",
965
1052
  name,
966
- node,
967
1053
  directives,
968
1054
  displayName: unit,
969
1055
  flag: getComponentFlagFromInitPath(initPath),
@@ -973,6 +1059,7 @@ function useComponentCollector(context, options = {}) {
973
1059
  isComponentDefinition: isComponentDefinition(context, node, hint),
974
1060
  isExportDefault,
975
1061
  isExportDefaultDeclaration,
1062
+ node,
976
1063
  rets: []
977
1064
  };
978
1065
  functionEntries.push(entry);
@@ -1036,7 +1123,7 @@ function useComponentCollector(context, options = {}) {
1036
1123
 
1037
1124
  //#endregion
1038
1125
  //#region src/component/component-collector-legacy.ts
1039
- const idGen = new IdGenerator("class_component_");
1126
+ const idGen = new IdGenerator("class-component:");
1040
1127
  /**
1041
1128
  * Get a ctx and visitor object for the rule to collect class componentss
1042
1129
  * @param context The ESLint rule context
@@ -1059,11 +1146,11 @@ function useComponentCollectorLegacy(context) {
1059
1146
  key,
1060
1147
  kind: "class-component",
1061
1148
  name,
1062
- node,
1063
1149
  displayName: unit,
1064
1150
  flag,
1065
1151
  hint: 0n,
1066
- methods: []
1152
+ methods: [],
1153
+ node
1067
1154
  });
1068
1155
  };
1069
1156
  return {
@@ -1074,179 +1161,6 @@ function useComponentCollectorLegacy(context) {
1074
1161
  }
1075
1162
  };
1076
1163
  }
1077
- /**
1078
- * Check whether the given node is a this.setState() call
1079
- * @param node The node to check
1080
- * @internal
1081
- */
1082
- function isThisSetState(node) {
1083
- const { callee } = node;
1084
- return callee.type === AST_NODE_TYPES$1.MemberExpression && ast.isThisExpressionLoose(callee.object) && callee.property.type === AST_NODE_TYPES$1.Identifier && callee.property.name === "setState";
1085
- }
1086
- /**
1087
- * Check whether the given node is an assignment to this.state
1088
- * @param node The node to check
1089
- * @internal
1090
- */
1091
- function isAssignmentToThisState(node) {
1092
- const { left } = node;
1093
- return left.type === AST_NODE_TYPES$1.MemberExpression && ast.isThisExpressionLoose(left.object) && ast.getPropertyName(left.property) === "state";
1094
- }
1095
-
1096
- //#endregion
1097
- //#region src/component/component-method-is.ts
1098
- /**
1099
- * Create a lifecycle method checker function
1100
- * @param methodName The lifecycle method name
1101
- * @param isStatic Whether the method is static
1102
- */
1103
- function createLifecycleChecker(methodName, isStatic = false) {
1104
- return (node) => ast.isMethodOrProperty(node) && node.static === isStatic && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === methodName;
1105
- }
1106
- const isRender = createLifecycleChecker("render");
1107
- const isComponentDidCatch = createLifecycleChecker("componentDidCatch");
1108
- const isComponentDidMount = createLifecycleChecker("componentDidMount");
1109
- const isComponentDidUpdate = createLifecycleChecker("componentDidUpdate");
1110
- const isComponentWillMount = createLifecycleChecker("componentWillMount");
1111
- const isComponentWillReceiveProps = createLifecycleChecker("componentWillReceiveProps");
1112
- const isComponentWillUnmount = createLifecycleChecker("componentWillUnmount");
1113
- const isComponentWillUpdate = createLifecycleChecker("componentWillUpdate");
1114
- const isGetChildContext = createLifecycleChecker("getChildContext");
1115
- const isGetInitialState = createLifecycleChecker("getInitialState");
1116
- const isGetSnapshotBeforeUpdate = createLifecycleChecker("getSnapshotBeforeUpdate");
1117
- const isShouldComponentUpdate = createLifecycleChecker("shouldComponentUpdate");
1118
- const isUnsafeComponentWillMount = createLifecycleChecker("UNSAFE_componentWillMount");
1119
- const isUnsafeComponentWillReceiveProps = createLifecycleChecker("UNSAFE_componentWillReceiveProps");
1120
- const isUnsafeComponentWillUpdate = createLifecycleChecker("UNSAFE_componentWillUpdate");
1121
- const isGetDefaultProps = createLifecycleChecker("getDefaultProps", true);
1122
- const isGetDerivedStateFromProps = createLifecycleChecker("getDerivedStateFromProps", true);
1123
- const isGetDerivedStateFromError = createLifecycleChecker("getDerivedStateFromError", true);
1124
-
1125
- //#endregion
1126
- //#region src/component/component-method-callback.ts
1127
- /**
1128
- * Check if the given node is a componentDidMount callback
1129
- * @param node The node to check
1130
- * @returns True if the node is a componentDidMount callback, false otherwise
1131
- */
1132
- function isComponentDidMountCallback(node) {
1133
- return ast.isFunction(node) && isComponentDidMount(node.parent) && node.parent.value === node;
1134
- }
1135
- /**
1136
- * Check if the given node is a componentWillUnmount callback
1137
- * @param node The node to check
1138
- * @returns True if the node is a componentWillUnmount callback, false otherwise
1139
- */
1140
- function isComponentWillUnmountCallback(node) {
1141
- return ast.isFunction(node) && isComponentWillUnmount(node.parent) && node.parent.value === node;
1142
- }
1143
-
1144
- //#endregion
1145
- //#region src/component/component-render-prop.ts
1146
- /**
1147
- * Unsafe check whether given node is a render function
1148
- * ```tsx
1149
- * const renderRow = () => <div />
1150
- * ` ^^^^^^^^^^^^`
1151
- * _ = <Component renderRow={() => <div />} />
1152
- * ` ^^^^^^^^^^^^^ `
1153
- * ```
1154
- * @param context The rule context
1155
- * @param node The AST node to check
1156
- * @returns `true` if node is a render function, `false` if not
1157
- */
1158
- function isRenderFunctionLoose(context, node) {
1159
- if (!ast.isFunction(node)) return false;
1160
- const id = ast.getFunctionId(node);
1161
- switch (true) {
1162
- case id?.type === AST_NODE_TYPES.Identifier: return id.name.startsWith("render");
1163
- case id?.type === AST_NODE_TYPES.MemberExpression && id.property.type === AST_NODE_TYPES.Identifier: return id.property.name.startsWith("render");
1164
- case node.parent.type === AST_NODE_TYPES.JSXExpressionContainer && node.parent.parent.type === AST_NODE_TYPES.JSXAttribute && node.parent.parent.name.type === AST_NODE_TYPES.JSXIdentifier: return node.parent.parent.name.name.startsWith("render");
1165
- }
1166
- return false;
1167
- }
1168
- /**
1169
- * Unsafe check whether given JSXAttribute is a render prop
1170
- * ```tsx
1171
- * _ = <Component renderRow={() => <div />} />
1172
- * ` ^^^^^^^^^^^^^^^^^^^^^^^^^ `
1173
- * ```
1174
- * @param context The rule context
1175
- * @param node The AST node to check
1176
- * @returns `true` if node is a render prop, `false` if not
1177
- */
1178
- function isRenderPropLoose(context, node) {
1179
- if (node.name.type !== AST_NODE_TYPES.JSXIdentifier) return false;
1180
- return node.name.name.startsWith("render") && node.value?.type === AST_NODE_TYPES.JSXExpressionContainer && isRenderFunctionLoose(context, node.value.expression);
1181
- }
1182
- /**
1183
- * Unsafe check whether given node is declared directly inside a render property
1184
- * ```tsx
1185
- * const rows = { render: () => <div /> }
1186
- * ` ^^^^^^^^^^^^^ `
1187
- * _ = <Component rows={ [{ render: () => <div /> }] } />
1188
- * ` ^^^^^^^^^^^^^ `
1189
- * ```
1190
- * @internal
1191
- * @param node The AST node to check
1192
- * @returns `true` if component is declared inside a render property, `false` if not
1193
- */
1194
- function isDirectValueOfRenderPropertyLoose(node) {
1195
- const matching = (node) => {
1196
- return node.type === AST_NODE_TYPES.Property && node.key.type === AST_NODE_TYPES.Identifier && node.key.name.startsWith("render");
1197
- };
1198
- return matching(node) || node.parent != null && matching(node.parent);
1199
- }
1200
- /**
1201
- * Unsafe check whether given node is declared inside a render prop
1202
- * ```tsx
1203
- * _ = <Component renderRow={"node"} />
1204
- * ` ^^^^^^ `
1205
- * _ = <Component rows={ [{ render: "node" }] } />
1206
- * ` ^^^^^^ `
1207
- * ```
1208
- * @param node The AST node to check
1209
- * @returns `true` if component is declared inside a render prop, `false` if not
1210
- */
1211
- function isDeclaredInRenderPropLoose(node) {
1212
- if (isDirectValueOfRenderPropertyLoose(node)) return true;
1213
- const parent = ast.findParentNode(node, ast.is(AST_NODE_TYPES.JSXExpressionContainer))?.parent;
1214
- if (parent?.type !== AST_NODE_TYPES.JSXAttribute) return false;
1215
- return parent.name.type === AST_NODE_TYPES.JSXIdentifier && parent.name.name.startsWith("render");
1216
- }
1217
-
1218
- //#endregion
1219
- //#region src/hierarchy/find-enclosing-component-or-hook.ts
1220
- /**
1221
- * Find the enclosing React component or hook for a given AST node
1222
- * @param node The AST node to start the search from
1223
- * @param test Optional test function to customize component or hook identification
1224
- * @returns The enclosing component or hook node, or `null` if none is ASAST.
1225
- */
1226
- function findEnclosingComponentOrHook(node, test = (n, name) => {
1227
- if (name == null) return false;
1228
- return isComponentNameLoose(name) || isHookName(name);
1229
- }) {
1230
- const enclosingNode = ast.findParentNode(node, (n) => {
1231
- if (!ast.isFunction(n)) return false;
1232
- return test(n, match(ast.getFunctionId(n)).with({ type: AST_NODE_TYPES.Identifier }, (id) => id.name).with({
1233
- type: AST_NODE_TYPES.MemberExpression,
1234
- property: { type: AST_NODE_TYPES.Identifier }
1235
- }, (me) => me.property.name).otherwise(() => null));
1236
- });
1237
- return ast.isFunction(enclosingNode) ? enclosingNode : unit;
1238
- }
1239
-
1240
- //#endregion
1241
- //#region src/hierarchy/is-inside-component-or-hook.ts
1242
- /**
1243
- * Check if a given AST node is inside a React component or hook
1244
- * @param node The AST node to check
1245
- * @returns True if the node is inside a component or hook, false otherwise
1246
- */
1247
- function isInsideComponentOrHook(node) {
1248
- return findEnclosingComponentOrHook(node) != null;
1249
- }
1250
1164
 
1251
1165
  //#endregion
1252
1166
  //#region src/ref/ref-name.ts
@@ -1255,12 +1169,18 @@ function isInsideComponentOrHook(node) {
1255
1169
  * @param name The name to check
1256
1170
  * @returns True if the name is "ref" or ends with "Ref"
1257
1171
  */
1258
- function isRefName(name) {
1172
+ function isRefLikeName(name) {
1259
1173
  return name === "ref" || name.endsWith("Ref");
1260
1174
  }
1261
1175
 
1262
1176
  //#endregion
1263
- //#region src/ref/is-from-ref.ts
1177
+ //#region src/ref/ref-id.ts
1178
+ function isRefId(node) {
1179
+ return node.type === AST_NODE_TYPES.Identifier && isRefLikeName(node.name);
1180
+ }
1181
+
1182
+ //#endregion
1183
+ //#region src/ref/ref-init.ts
1264
1184
  /**
1265
1185
  * Check if the variable with the given name is initialized or derived from a ref
1266
1186
  * @param name The variable name
@@ -1282,7 +1202,7 @@ function getRefInit(name, initialScope) {
1282
1202
  const init = node.init;
1283
1203
  if (init == null) continue;
1284
1204
  switch (true) {
1285
- case init.type === AST_NODE_TYPES$1.MemberExpression && init.object.type === AST_NODE_TYPES$1.Identifier && isRefName(init.object.name): return init;
1205
+ case init.type === AST_NODE_TYPES$1.MemberExpression && init.object.type === AST_NODE_TYPES$1.Identifier && isRefLikeName(init.object.name): return init;
1286
1206
  case init.type === AST_NODE_TYPES$1.CallExpression && isUseRefCall(init): return init;
1287
1207
  }
1288
1208
  }
@@ -1290,4 +1210,4 @@ function getRefInit(name, initialScope) {
1290
1210
  }
1291
1211
 
1292
1212
  //#endregion
1293
- export { ComponentDetectionHint, ComponentFlag, DEFAULT_COMPONENT_DETECTION_HINT, DEFAULT_JSX_DETECTION_HINT, JsxDetectionHint, JsxEmit, REACT_BUILTIN_HOOK_NAMES, findEnclosingComponentOrHook, findParentJsxAttribute, getComponentFlagFromInitPath, getFunctionComponentId, getJsxAttribute, getJsxAttributeName, getJsxConfigFromAnnotation, getJsxConfigFromContext, getJsxElementType, getRefInit, isAssignmentToThisState, isCaptureOwnerStack, isCaptureOwnerStackCall, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isCloneElement, isCloneElementCall, isComponentDefinition, isComponentDidCatch, isComponentDidMount, isComponentDidMountCallback, isComponentDidUpdate, isComponentName, isComponentNameLoose, isComponentWillMount, isComponentWillReceiveProps, isComponentWillUnmount, isComponentWillUnmountCallback, isComponentWillUpdate, isComponentWrapperCall, isComponentWrapperCallLoose, isComponentWrapperCallback, isComponentWrapperCallbackLoose, isCreateContext, isCreateContextCall, isCreateElement, isCreateElementCall, isCreateRef, isCreateRefCall, isDeclaredInRenderPropLoose, isDirectValueOfRenderPropertyLoose, isForwardRef, isForwardRefCall, isFunctionWithLooseComponentName, isGetChildContext, isGetDefaultProps, isGetDerivedStateFromError, isGetDerivedStateFromProps, isGetInitialState, isGetSnapshotBeforeUpdate, isHook, isHookCall, isHookCallWithName, isHookId, isHookName, isInitializedFromReact, isInitializedFromReactNative, isInitializedFromRef, isInsideComponentOrHook, isJsxFragmentElement, isJsxHostElement, isJsxLike, isJsxText, isLazy, isLazyCall, isMemo, isMemoCall, isPureComponent, isReactAPI, isReactAPICall, isRefName, isRender, isRenderFunctionLoose, isRenderMethodLike, isRenderPropLoose, isShouldComponentUpdate, isThisSetState, isUnsafeComponentWillMount, isUnsafeComponentWillReceiveProps, isUnsafeComponentWillUpdate, isUseActionStateCall, isUseCall, isUseCallbackCall, isUseContextCall, isUseDebugValueCall, isUseDeferredValueCall, isUseEffectCall, isUseEffectCleanupCallback, isUseEffectLikeCall, isUseEffectSetupCallback, isUseFormStatusCall, isUseIdCall, isUseImperativeHandleCall, isUseInsertionEffectCall, isUseLayoutEffectCall, isUseMemoCall, isUseOptimisticCall, isUseReducerCall, isUseRefCall, isUseStateCall, isUseStateLikeCall, isUseSyncExternalStoreCall, isUseTransitionCall, resolveJsxAttributeValue, stringifyJsx, useComponentCollector, useComponentCollectorLegacy, useHookCollector };
1213
+ export { ComponentDetectionHint, ComponentFlag, DEFAULT_COMPONENT_DETECTION_HINT, DEFAULT_JSX_DETECTION_HINT, JsxDetectionHint, JsxEmit, REACT_BUILTIN_HOOK_NAMES, findImportSource, findParentJsxAttribute, getComponentFlagFromInitPath, getFunctionComponentId, getJsxAttribute, getJsxAttributeName, getJsxConfigFromAnnotation, getJsxConfigFromContext, getJsxElementType, getRefInit, isAssignmentToThisState, isCaptureOwnerStack, isCaptureOwnerStackCall, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isCloneElement, isCloneElementCall, isComponentDefinition, isComponentDidCatch, isComponentDidMount, isComponentDidMountCallback, isComponentDidUpdate, isComponentName, isComponentNameLoose, isComponentWillMount, isComponentWillReceiveProps, isComponentWillUnmount, isComponentWillUnmountCallback, isComponentWillUpdate, isComponentWrapperCall, isComponentWrapperCallLoose, isComponentWrapperCallback, isComponentWrapperCallbackLoose, isCreateContext, isCreateContextCall, isCreateElement, isCreateElementCall, isCreateRef, isCreateRefCall, isForwardRef, isForwardRefCall, isFunctionWithLooseComponentName, isGetChildContext, isGetDefaultProps, isGetDerivedStateFromError, isGetDerivedStateFromProps, isGetInitialState, isGetSnapshotBeforeUpdate, isHook, isHookCall, isHookCallWithName, isHookId, isHookName, isInitializedFromReact, isInitializedFromReactNative, isInitializedFromRef, isJsxFragmentElement, isJsxHostElement, isJsxLike, isJsxText, isLazy, isLazyCall, isMemo, isMemoCall, isPureComponent, isReactAPI, isReactAPICall, isRefId, isRefLikeName, isRender, isRenderMethodCallback, isRenderMethodLike, isShouldComponentUpdate, isThisSetState, isUnsafeComponentWillMount, isUnsafeComponentWillReceiveProps, isUnsafeComponentWillUpdate, isUseActionStateCall, isUseCall, isUseCallbackCall, isUseContextCall, isUseDebugValueCall, isUseDeferredValueCall, isUseEffectCall, isUseEffectCleanupCallback, isUseEffectLikeCall, isUseEffectSetupCallback, isUseFormStatusCall, isUseIdCall, isUseImperativeHandleCall, isUseInsertionEffectCall, isUseLayoutEffectCall, isUseMemoCall, isUseOptimisticCall, isUseReducerCall, isUseRefCall, isUseStateCall, isUseStateLikeCall, isUseSyncExternalStoreCall, isUseTransitionCall, resolveJsxAttributeValue, stringifyJsx, useComponentCollector, useComponentCollectorLegacy, useHookCollector };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eslint-react/core",
3
- "version": "3.0.0-next.6",
3
+ "version": "3.0.0-next.60",
4
4
  "description": "ESLint React's ESLint utility module for static analysis of React core APIs and patterns.",
5
5
  "homepage": "https://github.com/Rel1cx/eslint-react",
6
6
  "bugs": {
@@ -34,13 +34,13 @@
34
34
  "@typescript-eslint/types": "canary",
35
35
  "@typescript-eslint/utils": "canary",
36
36
  "ts-pattern": "^5.9.0",
37
- "@eslint-react/ast": "3.0.0-next.6",
38
- "@eslint-react/eff": "3.0.0-next.6",
39
- "@eslint-react/shared": "3.0.0-next.6",
40
- "@eslint-react/var": "3.0.0-next.6"
37
+ "@eslint-react/ast": "3.0.0-next.60",
38
+ "@eslint-react/eff": "3.0.0-next.60",
39
+ "@eslint-react/shared": "3.0.0-next.60",
40
+ "@eslint-react/var": "3.0.0-next.60"
41
41
  },
42
42
  "devDependencies": {
43
- "tsdown": "^0.20.3",
43
+ "tsdown": "^0.21.0-beta.2",
44
44
  "@local/configs": "0.0.0"
45
45
  },
46
46
  "peerDependencies": {
@@ -54,6 +54,6 @@
54
54
  "build": "tsdown --dts-resolve",
55
55
  "build:docs": "typedoc",
56
56
  "lint:publish": "publint",
57
- "lint:ts": "tsc --noEmit"
57
+ "lint:ts": "tsl"
58
58
  }
59
59
  }