@eslint-react/core 3.0.0-next.7 → 3.0.0-next.71

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 +342 -320
  2. package/dist/index.js +523 -516
  3. package/package.json +7 -7
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
- import { constFalse, constTrue, dual, flip, getOrElseUpdate, identity, unit } from "@eslint-react/eff";
2
+ import { constFalse, dual, flip, getOrElseUpdate, identity } from "@eslint-react/eff";
4
3
  import { AST_NODE_TYPES } from "@typescript-eslint/types";
5
- 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
- import { getStaticValue } from "@typescript-eslint/utils/ast-utils";
4
+ import { findVariable, getStaticValue } from "@typescript-eslint/utils/ast-utils";
7
5
  import { P, match } from "ts-pattern";
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";
7
+ import { resolve } from "@eslint-react/var";
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 null 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 null if not found
35
+ */
36
+ function findImportSource(name, initialScope) {
37
+ const latestDef = findVariable(initialScope, name)?.defs.at(-1);
38
+ if (latestDef == null) return null;
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 null;
46
+ return arg0.value;
47
+ }
48
+ if (parent?.type === AST_NODE_TYPES.ImportDeclaration) return parent.source.value;
49
+ return null;
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
@@ -270,7 +313,7 @@ function useHookCollector(context) {
270
313
  const hooks = /* @__PURE__ */ new Map();
271
314
  const functionEntries = [];
272
315
  const getText = (n) => context.sourceCode.getText(n);
273
- const getCurrentEntry = () => functionEntries.at(-1);
316
+ const getCurrentEntry = () => functionEntries.at(-1) ?? null;
274
317
  const onFunctionEnter = (node) => {
275
318
  const id = ast.getFunctionId(node);
276
319
  const key = idGen$2.next();
@@ -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 = () => {
@@ -315,150 +358,6 @@ function useHookCollector(context) {
315
358
  };
316
359
  }
317
360
 
318
- //#endregion
319
- //#region src/jsx/jsx-stringify.ts
320
- /**
321
- * Incomplete but sufficient stringification of JSX nodes for common use cases
322
- *
323
- * @param node JSX node from TypeScript ESTree
324
- * @returns String representation of the JSX node
325
- */
326
- function stringifyJsx(node) {
327
- switch (node.type) {
328
- case AST_NODE_TYPES.JSXIdentifier: return node.name;
329
- case AST_NODE_TYPES.JSXNamespacedName: return `${node.namespace.name}:${node.name.name}`;
330
- case AST_NODE_TYPES.JSXMemberExpression: return `${stringifyJsx(node.object)}.${stringifyJsx(node.property)}`;
331
- case AST_NODE_TYPES.JSXText: return node.value;
332
- case AST_NODE_TYPES.JSXOpeningElement: return `<${stringifyJsx(node.name)}>`;
333
- case AST_NODE_TYPES.JSXClosingElement: return `</${stringifyJsx(node.name)}>`;
334
- case AST_NODE_TYPES.JSXOpeningFragment: return "<>";
335
- case AST_NODE_TYPES.JSXClosingFragment: return "</>";
336
- }
337
- }
338
-
339
- //#endregion
340
- //#region src/jsx/jsx-attribute-name.ts
341
- /**
342
- * Get the stringified name of a JSX attribute
343
- * @param context The ESLint rule context
344
- * @param node The JSX attribute node
345
- * @returns The name of the attribute
346
- */
347
- function getJsxAttributeName(context, node) {
348
- return stringifyJsx(node.name);
349
- }
350
-
351
- //#endregion
352
- //#region src/jsx/jsx-attribute.ts
353
- /**
354
- * Creates a helper function to find a specific JSX attribute by name
355
- * Handles direct attributes and spread attributes (variables or object literals)
356
- * @param context The ESLint rule context
357
- * @param node The JSX element node
358
- * @param initialScope (Optional) The initial scope to use for variable resolution
359
- */
360
- function getJsxAttribute(context, node, initialScope) {
361
- const scope = initialScope ?? context.sourceCode.getScope(node);
362
- const attributes = node.openingElement.attributes;
363
- /**
364
- * Finds the last occurrence of a specific attribute
365
- * @param name The attribute name to search for
366
- */
367
- return (name) => {
368
- return attributes.findLast((attr) => {
369
- if (attr.type === AST_NODE_TYPES.JSXAttribute) return getJsxAttributeName(context, attr) === name;
370
- switch (attr.argument.type) {
371
- 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;
374
- return false;
375
- }
376
- case AST_NODE_TYPES.ObjectExpression: return findProperty(name, attr.argument.properties, scope) != null;
377
- }
378
- return false;
379
- });
380
- };
381
- }
382
-
383
- //#endregion
384
- //#region src/jsx/jsx-attribute-value.ts
385
- /**
386
- * Resolve the static value of a JSX attribute or spread attribute
387
- *
388
- * @param context - The ESLint rule context
389
- * @param attribute - The JSX attribute node to resolve
390
- * @returns An object containing the value kind, the node (if applicable), and a `toStatic` helper
391
- */
392
- function resolveJsxAttributeValue(context, attribute) {
393
- const initialScope = context.sourceCode.getScope(attribute);
394
- /**
395
- * Handles standard JSX attributes (e.g., prop="value", prop={value}, prop)
396
- * @param node The JSX attribute node
397
- */
398
- function handleJsxAttribute(node) {
399
- if (node.value == null) return {
400
- kind: "boolean",
401
- toStatic() {
402
- return true;
403
- }
404
- };
405
- switch (node.value.type) {
406
- case AST_NODE_TYPES.Literal: {
407
- const staticValue = node.value.value;
408
- return {
409
- kind: "literal",
410
- node: node.value,
411
- toStatic() {
412
- return staticValue;
413
- }
414
- };
415
- }
416
- case AST_NODE_TYPES.JSXExpressionContainer: {
417
- const expr = node.value.expression;
418
- return {
419
- kind: "expression",
420
- node: expr,
421
- toStatic() {
422
- return getStaticValue(expr, initialScope)?.value;
423
- }
424
- };
425
- }
426
- case AST_NODE_TYPES.JSXElement: return {
427
- kind: "element",
428
- node: node.value,
429
- toStatic() {
430
- return unit;
431
- }
432
- };
433
- case AST_NODE_TYPES.JSXSpreadChild: return {
434
- kind: "spreadChild",
435
- node: node.value.expression,
436
- toStatic() {
437
- return unit;
438
- }
439
- };
440
- }
441
- }
442
- /**
443
- * Handles JSX spread attributes (e.g., {...props})
444
- * @param node The JSX spread attribute node
445
- */
446
- function handleJsxSpreadAttribute(node) {
447
- return {
448
- kind: "spreadProps",
449
- node: node.argument,
450
- toStatic(name) {
451
- if (name == null) return unit;
452
- return match(getStaticValue(node.argument, initialScope)?.value).with({ [name]: P.select(P.any) }, identity).otherwise(() => unit);
453
- }
454
- };
455
- }
456
- switch (attribute.type) {
457
- case AST_NODE_TYPES.JSXAttribute: return handleJsxAttribute(attribute);
458
- case AST_NODE_TYPES.JSXSpreadAttribute: return handleJsxSpreadAttribute(attribute);
459
- }
460
- }
461
-
462
361
  //#endregion
463
362
  //#region src/jsx/jsx-config.ts
464
363
  const JsxEmit = {
@@ -531,25 +430,15 @@ const JsxDetectionHint = {
531
430
  */
532
431
  const DEFAULT_JSX_DETECTION_HINT = 0n | JsxDetectionHint.DoNotIncludeJsxWithNumberValue | JsxDetectionHint.DoNotIncludeJsxWithBigIntValue | JsxDetectionHint.DoNotIncludeJsxWithBooleanValue | JsxDetectionHint.DoNotIncludeJsxWithStringValue | JsxDetectionHint.DoNotIncludeJsxWithUndefinedValue;
533
432
  /**
534
- * Check if a node is a `JSXText` or a `Literal` node
535
- * @param node The AST node to check
536
- * @returns `true` if the node is a `JSXText` or a `Literal` node
537
- */
538
- function isJsxText(node) {
539
- if (node == null) return false;
540
- return node.type === AST_NODE_TYPES.JSXText || node.type === AST_NODE_TYPES.Literal;
541
- }
542
- /**
543
433
  * Determine if a node represents JSX-like content based on heuristics
544
434
  * Supports configuration through hint flags to customize detection behavior
545
435
  *
546
- * @param code The source code with scope lookup capability
547
- * @param code.getScope The function to get the scope of a node
436
+ * @param context The rule context with scope lookup capability
548
437
  * @param node The AST node to analyze
549
438
  * @param hint The configuration flags to adjust detection behavior
550
439
  * @returns boolean Whether the node is considered JSX-like
551
440
  */
552
- function isJsxLike(code, node, hint = DEFAULT_JSX_DETECTION_HINT) {
441
+ function isJsxLike(context, node, hint = DEFAULT_JSX_DETECTION_HINT) {
553
442
  if (node == null) return false;
554
443
  if (ast.isJSX(node)) return true;
555
444
  switch (node.type) {
@@ -565,27 +454,27 @@ function isJsxLike(code, node, hint = DEFAULT_JSX_DETECTION_HINT) {
565
454
  case AST_NODE_TYPES.TemplateLiteral: return !(hint & JsxDetectionHint.DoNotIncludeJsxWithStringValue);
566
455
  case AST_NODE_TYPES.ArrayExpression:
567
456
  if (node.elements.length === 0) return !(hint & JsxDetectionHint.DoNotIncludeJsxWithEmptyArrayValue);
568
- if (hint & JsxDetectionHint.RequireAllArrayElementsToBeJsx) return node.elements.every((n) => isJsxLike(code, n, hint));
569
- return node.elements.some((n) => isJsxLike(code, n, hint));
457
+ if (hint & JsxDetectionHint.RequireAllArrayElementsToBeJsx) return node.elements.every((n) => isJsxLike(context, n, hint));
458
+ return node.elements.some((n) => isJsxLike(context, n, hint));
570
459
  case AST_NODE_TYPES.LogicalExpression:
571
- if (hint & JsxDetectionHint.RequireBothSidesOfLogicalExpressionToBeJsx) return isJsxLike(code, node.left, hint) && isJsxLike(code, node.right, hint);
572
- return isJsxLike(code, node.left, hint) || isJsxLike(code, node.right, hint);
460
+ if (hint & JsxDetectionHint.RequireBothSidesOfLogicalExpressionToBeJsx) return isJsxLike(context, node.left, hint) && isJsxLike(context, node.right, hint);
461
+ return isJsxLike(context, node.left, hint) || isJsxLike(context, node.right, hint);
573
462
  case AST_NODE_TYPES.ConditionalExpression: {
574
463
  function leftHasJSX(node) {
575
464
  if (Array.isArray(node.consequent)) {
576
465
  if (node.consequent.length === 0) return !(hint & JsxDetectionHint.DoNotIncludeJsxWithEmptyArrayValue);
577
- if (hint & JsxDetectionHint.RequireAllArrayElementsToBeJsx) return node.consequent.every((n) => isJsxLike(code, n, hint));
578
- return node.consequent.some((n) => isJsxLike(code, n, hint));
466
+ if (hint & JsxDetectionHint.RequireAllArrayElementsToBeJsx) return node.consequent.every((n) => isJsxLike(context, n, hint));
467
+ return node.consequent.some((n) => isJsxLike(context, n, hint));
579
468
  }
580
- return isJsxLike(code, node.consequent, hint);
469
+ return isJsxLike(context, node.consequent, hint);
581
470
  }
582
471
  function rightHasJSX(node) {
583
- return isJsxLike(code, node.alternate, hint);
472
+ return isJsxLike(context, node.alternate, hint);
584
473
  }
585
474
  if (hint & JsxDetectionHint.RequireBothBranchesOfConditionalExpressionToBeJsx) return leftHasJSX(node) && rightHasJSX(node);
586
475
  return leftHasJSX(node) || rightHasJSX(node);
587
476
  }
588
- case AST_NODE_TYPES.SequenceExpression: return isJsxLike(code, node.expressions.at(-1), hint);
477
+ case AST_NODE_TYPES.SequenceExpression: return isJsxLike(context, node.expressions.at(-1) ?? null, hint);
589
478
  case AST_NODE_TYPES.CallExpression:
590
479
  if (hint & JsxDetectionHint.DoNotIncludeJsxWithCreateElementValue) return false;
591
480
  switch (node.callee.type) {
@@ -593,100 +482,321 @@ function isJsxLike(code, node, hint = DEFAULT_JSX_DETECTION_HINT) {
593
482
  case AST_NODE_TYPES.MemberExpression: return node.callee.property.type === AST_NODE_TYPES.Identifier && node.callee.property.name === "createElement";
594
483
  }
595
484
  return false;
596
- case AST_NODE_TYPES.Identifier: {
597
- const { name } = node;
598
- if (name === "undefined") return !(hint & JsxDetectionHint.DoNotIncludeJsxWithUndefinedValue);
485
+ case AST_NODE_TYPES.Identifier:
486
+ if (node.name === "undefined") return !(hint & JsxDetectionHint.DoNotIncludeJsxWithUndefinedValue);
599
487
  if (ast.isJSXTagNameExpression(node)) return true;
600
- return isJsxLike(code, getVariableDefinitionNode(findVariable(name, code.getScope(node)), 0), hint);
601
- }
488
+ return isJsxLike(context, resolve(context, node), hint);
602
489
  }
603
490
  return false;
604
491
  }
605
492
 
606
493
  //#endregion
607
- //#region src/jsx/jsx-element-type.ts
494
+ //#region src/jsx/jsx-stringify.ts
608
495
  /**
609
- * Extracts the element type name from a JSX element or fragment
610
- * For JSX elements, returns the stringified name (e.g., "div", "Button", "React.Fragment")
611
- * For JSX fragments, returns an empty string
496
+ * Incomplete but sufficient stringification of JSX nodes for common use cases
612
497
  *
613
- * @param context ESLint rule context
614
- * @param node JSX element or fragment node
615
- * @returns String representation of the element type
498
+ * @param node JSX node from TypeScript ESTree
499
+ * @returns String representation of the JSX node
616
500
  */
617
- function getJsxElementType(context, node) {
618
- if (node.type === AST_NODE_TYPES.JSXFragment) return "";
619
- return stringifyJsx(node.openingElement.name);
501
+ function stringifyJsx(node) {
502
+ switch (node.type) {
503
+ case AST_NODE_TYPES.JSXIdentifier: return node.name;
504
+ case AST_NODE_TYPES.JSXNamespacedName: return `${node.namespace.name}:${node.name.name}`;
505
+ case AST_NODE_TYPES.JSXMemberExpression: return `${stringifyJsx(node.object)}.${stringifyJsx(node.property)}`;
506
+ case AST_NODE_TYPES.JSXText: return node.value;
507
+ case AST_NODE_TYPES.JSXOpeningElement: return `<${stringifyJsx(node.name)}>`;
508
+ case AST_NODE_TYPES.JSXClosingElement: return `</${stringifyJsx(node.name)}>`;
509
+ case AST_NODE_TYPES.JSXOpeningFragment: return "<>";
510
+ case AST_NODE_TYPES.JSXClosingFragment: return "</>";
511
+ }
620
512
  }
621
513
 
622
514
  //#endregion
623
- //#region src/jsx/jsx-element-is.ts
515
+ //#region src/jsx/jsx-inspector.ts
624
516
  /**
625
- * Determine if a JSX element is a host element
626
- * Host elements in React start with lowercase letters (e.g., div, span)
517
+ * A stateful helper that binds an ESLint `RuleContext` once and exposes
518
+ * ergonomic methods for the most common JSX inspection tasks that rules need.
627
519
  *
628
- * @param context ESLint rule context
629
- * @param node AST node to check
630
- * @returns boolean indicating if the element is a host element
631
- */
632
- function isJsxHostElement(context, node) {
633
- return node.type === AST_NODE_TYPES.JSXElement && node.openingElement.name.type === AST_NODE_TYPES.JSXIdentifier && /^[a-z]/u.test(node.openingElement.name.name);
634
- }
635
- /**
636
- * Determine if a JSX element is a React Fragment
637
- * Fragments can be imported from React and used like <Fragment> or <React.Fragment>
520
+ * ### Typical usage inside a rule's `create` function
638
521
  *
639
- * @param context ESLint rule context
640
- * @param node AST node to check
641
- * @param jsxConfig Optional JSX configuration
642
- * @param jsxConfig.jsxFragmentFactory Name of the fragment factory (e.g., React.Fragment)
643
- * @returns boolean indicating if the element is a Fragment
644
- */
645
- function isJsxFragmentElement(context, node, jsxConfig) {
646
- if (node.type !== AST_NODE_TYPES.JSXElement) return false;
647
- const fragment = jsxConfig?.jsxFragmentFactory?.split(".").at(-1) ?? "Fragment";
648
- return getJsxElementType(context, node).split(".").at(-1) === fragment;
649
- }
650
-
651
- //#endregion
652
- //#region src/jsx/jsx-hierarchy.ts
653
- /**
654
- * Traverses up the AST to find a parent JSX attribute node that matches a given test
522
+ * ```ts
523
+ * export function create(context: RuleContext) {
524
+ * const jsx = JsxInspector.from(context);
655
525
  *
656
- * @param node The starting AST node
657
- * @param test Optional predicate function to test if the attribute meets criteria
658
- * Defaults to always returning true (matches any attribute)
659
- * @returns The first matching JSX attribute node found when traversing upwards, or undefined
526
+ * return defineRuleListener({
527
+ * JSXElement(node) {
528
+ * // element type
529
+ * const type = jsx.getElementType(node); // "div" | "React.Fragment" |
530
+ *
531
+ * // attribute lookup + value resolution in one step
532
+ * const val = jsx.getAttributeValue(node, "sandbox");
533
+ * if (typeof val?.getStatic() === "string") { … }
534
+ *
535
+ * // simple boolean checks
536
+ * if (jsx.isHostElement(node)) { … }
537
+ * if (jsx.isFragmentElement(node)) { … }
538
+ * if (jsx.hasAttribute(node, "key")) { … }
539
+ * },
540
+ * });
541
+ * }
542
+ * ```
660
543
  */
661
- function findParentJsxAttribute(node, test = constTrue) {
662
- const guard = (node) => {
663
- return node.type === AST_NODE_TYPES.JSXAttribute && test(node);
664
- };
665
- return ast.findParentNode(node, guard);
666
- }
544
+ var JsxInspector = class JsxInspector {
545
+ context;
546
+ /**
547
+ * Merged JSX configuration (tsconfig compiler options + pragma annotations).
548
+ * The result is lazily computed and cached for the lifetime of this inspector.
549
+ */
550
+ get jsxConfig() {
551
+ return this.#jsxConfig ??= {
552
+ ...getJsxConfigFromContext(this.context),
553
+ ...getJsxConfigFromAnnotation(this.context)
554
+ };
555
+ }
556
+ /**
557
+ * Lazily resolved & cached JSX configuration (merged from tsconfig +
558
+ * pragma annotations). Use {@link jsxConfig} to access.
559
+ */
560
+ #jsxConfig;
561
+ constructor(context) {
562
+ this.context = context;
563
+ }
564
+ /**
565
+ * Walk **up** the AST from `node` to find the nearest ancestor that is a
566
+ * `JSXAttribute` and passes the optional `test` predicate.
567
+ * @param node The starting node for the search.
568
+ * @param test A predicate function to test each ancestor node.
569
+ */
570
+ static findParentAttribute(node, test = () => true) {
571
+ const guard = (n) => {
572
+ return n.type === AST_NODE_TYPES.JSXAttribute && test(n);
573
+ };
574
+ return ast.findParentNode(node, guard);
575
+ }
576
+ /**
577
+ * Create a new `JsxInspector` bound to the given rule context.
578
+ * @param context The ESLint rule context to bind to this inspector instance.
579
+ */
580
+ static from(context) {
581
+ return new JsxInspector(context);
582
+ }
583
+ /**
584
+ * Whether the node is a `JSXText` or a `Literal` node.
585
+ * @param node The node to check.
586
+ */
587
+ static isJsxText(node) {
588
+ if (node == null) return false;
589
+ return node.type === AST_NODE_TYPES.JSXText || node.type === AST_NODE_TYPES.Literal;
590
+ }
591
+ /**
592
+ * Find a JSX attribute (or spread attribute containing the property) by name
593
+ * on a given element.
594
+ *
595
+ * Returns the **last** matching attribute (to mirror React's behaviour where
596
+ * later props win), or `undefined` if not found.
597
+ * @param node The JSX element to search for the attribute.
598
+ * @param name The name of the attribute to find (e.g. `"className"`).
599
+ */
600
+ findAttribute(node, name) {
601
+ return node.openingElement.attributes.findLast((attr) => {
602
+ if (attr.type === AST_NODE_TYPES.JSXAttribute) return stringifyJsx(attr.name) === name;
603
+ switch (attr.argument.type) {
604
+ case AST_NODE_TYPES.Identifier: {
605
+ const initNode = resolve(this.context, attr.argument);
606
+ if (initNode?.type === AST_NODE_TYPES.ObjectExpression) return ast.findProperty(initNode.properties, name) != null;
607
+ return false;
608
+ }
609
+ case AST_NODE_TYPES.ObjectExpression: return ast.findProperty(attr.argument.properties, name) != null;
610
+ }
611
+ return false;
612
+ });
613
+ }
614
+ /**
615
+ * Get the stringified name of a `JSXAttribute` node
616
+ * (e.g. `"className"`, `"aria-label"`, `"xml:space"`).
617
+ * @param node The `JSXAttribute` node to extract the name from.
618
+ * @returns The stringified name of the attribute.
619
+ */
620
+ getAttributeName(node) {
621
+ return stringifyJsx(node.name);
622
+ }
623
+ /**
624
+ * Resolve the static value of an attribute, automatically handling the
625
+ * `spreadProps` case by extracting the named property.
626
+ *
627
+ * This eliminates the repetitive pattern:
628
+ * ```ts
629
+ * const v = core.resolveJsxAttributeValue(ctx, attr);
630
+ * const s = v.kind === "spreadProps" ? v.getProperty(name) : v.toStatic();
631
+ * ```
632
+ *
633
+ * Returns `undefined` when the attribute is not present or its value
634
+ * cannot be statically determined.
635
+ * @param node The JSX element to search for the attribute.
636
+ * @param name The name of the attribute to resolve (e.g. `"className"`).
637
+ * @returns The static value of the attribute, or `undefined` if not found or not statically resolvable.
638
+ */
639
+ getAttributeStaticValue(node, name) {
640
+ const attr = this.findAttribute(node, name);
641
+ if (attr == null) return void 0;
642
+ const resolved = this.resolveAttributeValue(attr);
643
+ if (resolved.kind === "spreadProps") return resolved.getProperty(name);
644
+ return resolved.toStatic();
645
+ }
646
+ /**
647
+ * **All-in-one helper** – find an attribute by name on an element *and*
648
+ * resolve its value in a single call.
649
+ *
650
+ * Returns `undefined` when the attribute is not present.
651
+ * @param node The JSX element to search for the attribute.
652
+ * @param name The name of the attribute to find and resolve (e.g. `"className"`).
653
+ * @returns A descriptor of the attribute's value that can be further inspected, or `undefined` if the attribute is not found.
654
+ */
655
+ getAttributeValue(node, name) {
656
+ const attr = this.findAttribute(node, name);
657
+ if (attr == null) return void 0;
658
+ return this.resolveAttributeValue(attr);
659
+ }
660
+ /**
661
+ * Get the **self name** (last segment) of a JSX element type.
662
+ *
663
+ * - `<Foo.Bar.Baz>` → `"Baz"`
664
+ * - `<div>` → `"div"`
665
+ * - `<></>` → `""`
666
+ * @param node The JSX element or fragment to extract the self name from.
667
+ */
668
+ getElementSelfName(node) {
669
+ return this.getElementType(node).split(".").at(-1) ?? "";
670
+ }
671
+ /**
672
+ * Get the string representation of a JSX element's type.
673
+ *
674
+ * - `<div>` → `"div"`
675
+ * - `<Foo.Bar>` → `"Foo.Bar"`
676
+ * - `<React.Fragment>` → `"React.Fragment"`
677
+ * - `<></>` (JSXFragment) → `""`
678
+ * @param node The JSX element or fragment to extract the type from.
679
+ */
680
+ getElementType(node) {
681
+ if (node.type === AST_NODE_TYPES.JSXFragment) return "";
682
+ return stringifyJsx(node.openingElement.name);
683
+ }
684
+ /**
685
+ * Shorthand: check whether an attribute exists on the element.
686
+ * @param node The JSX element to check for the attribute.
687
+ * @param name The name of the attribute to check for (e.g. `"className"`).
688
+ * @returns `true` if the attribute exists on the element, `false` otherwise.
689
+ */
690
+ hasAttribute(node, name) {
691
+ return this.findAttribute(node, name) != null;
692
+ }
693
+ /**
694
+ * Whether the node is a React **Fragment** element (either `<Fragment>` /
695
+ * `<React.Fragment>` or the shorthand `<>` syntax).
696
+ *
697
+ * The check honours the configured `jsxFragmentFactory`.
698
+ * @param node The node to check.
699
+ */
700
+ isFragmentElement(node) {
701
+ if (node.type === AST_NODE_TYPES.JSXFragment) return true;
702
+ if (node.type !== AST_NODE_TYPES.JSXElement) return false;
703
+ const fragment = this.jsxConfig.jsxFragmentFactory.split(".").at(-1) ?? "Fragment";
704
+ return this.getElementType(node).split(".").at(-1) === fragment;
705
+ }
706
+ /**
707
+ * Whether the node is a **host** (intrinsic / DOM) element – i.e. its tag
708
+ * name starts with a lowercase letter.
709
+ * @param node The node to check.
710
+ */
711
+ isHostElement(node) {
712
+ return node.type === AST_NODE_TYPES.JSXElement && node.openingElement.name.type === AST_NODE_TYPES.JSXIdentifier && /^[a-z]/u.test(node.openingElement.name.name);
713
+ }
714
+ /**
715
+ * Resolve the *value* of a JSX attribute (or spread attribute) into a
716
+ * descriptor that can be inspected further.
717
+ *
718
+ * See {@link JsxAttributeValue} for the full set of `kind` discriminants.
719
+ * @param attribute The attribute node to resolve the value of.
720
+ * @returns A descriptor of the attribute's value that can be further inspected.
721
+ */
722
+ resolveAttributeValue(attribute) {
723
+ if (attribute.type === AST_NODE_TYPES.JSXAttribute) return this.#resolveJsxAttribute(attribute);
724
+ return this.#resolveJsxSpreadAttribute(attribute);
725
+ }
726
+ #resolveJsxAttribute(node) {
727
+ const scope = this.context.sourceCode.getScope(node);
728
+ if (node.value == null) return {
729
+ kind: "boolean",
730
+ toStatic() {
731
+ return true;
732
+ }
733
+ };
734
+ switch (node.value.type) {
735
+ case AST_NODE_TYPES.Literal: {
736
+ const staticValue = node.value.value;
737
+ return {
738
+ kind: "literal",
739
+ node: node.value,
740
+ toStatic() {
741
+ return staticValue;
742
+ }
743
+ };
744
+ }
745
+ case AST_NODE_TYPES.JSXExpressionContainer: {
746
+ const expr = node.value.expression;
747
+ if (expr.type === AST_NODE_TYPES.JSXEmptyExpression) return {
748
+ kind: "missing",
749
+ node: expr,
750
+ toStatic() {
751
+ return null;
752
+ }
753
+ };
754
+ return {
755
+ kind: "expression",
756
+ node: expr,
757
+ toStatic() {
758
+ return getStaticValue(expr, scope)?.value;
759
+ }
760
+ };
761
+ }
762
+ case AST_NODE_TYPES.JSXElement: return {
763
+ kind: "element",
764
+ node: node.value,
765
+ toStatic() {
766
+ return null;
767
+ }
768
+ };
769
+ case AST_NODE_TYPES.JSXSpreadChild:
770
+ node.value.expression;
771
+ return {
772
+ kind: "spreadChild",
773
+ node: node.value.expression,
774
+ toStatic() {
775
+ return null;
776
+ },
777
+ getChildren(_at) {
778
+ return null;
779
+ }
780
+ };
781
+ }
782
+ }
783
+ #resolveJsxSpreadAttribute(node) {
784
+ const scope = this.context.sourceCode.getScope(node);
785
+ return {
786
+ kind: "spreadProps",
787
+ node: node.argument,
788
+ toStatic() {
789
+ return null;
790
+ },
791
+ getProperty(name) {
792
+ return match(getStaticValue(node.argument, scope)?.value).with({ [name]: P.select(P.any) }, identity).otherwise(() => null);
793
+ }
794
+ };
795
+ }
796
+ };
667
797
 
668
798
  //#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
799
+ //#region src/component/component-detection-legacy.ts
690
800
  /**
691
801
  * Check if a node is a React class component
692
802
  * @param node The AST node to check
@@ -717,6 +827,99 @@ function isPureComponent(node) {
717
827
  }
718
828
  return false;
719
829
  }
830
+ /**
831
+ * Create a lifecycle method checker function
832
+ * @param methodName The lifecycle method name
833
+ * @param isStatic Whether the method is static
834
+ */
835
+ function createLifecycleChecker(methodName, isStatic = false) {
836
+ return (node) => ast.isMethodOrProperty(node) && node.static === isStatic && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === methodName;
837
+ }
838
+ const isRender = createLifecycleChecker("render");
839
+ const isComponentDidCatch = createLifecycleChecker("componentDidCatch");
840
+ const isComponentDidMount = createLifecycleChecker("componentDidMount");
841
+ const isComponentDidUpdate = createLifecycleChecker("componentDidUpdate");
842
+ const isComponentWillMount = createLifecycleChecker("componentWillMount");
843
+ const isComponentWillReceiveProps = createLifecycleChecker("componentWillReceiveProps");
844
+ const isComponentWillUnmount = createLifecycleChecker("componentWillUnmount");
845
+ const isComponentWillUpdate = createLifecycleChecker("componentWillUpdate");
846
+ const isGetChildContext = createLifecycleChecker("getChildContext");
847
+ const isGetInitialState = createLifecycleChecker("getInitialState");
848
+ const isGetSnapshotBeforeUpdate = createLifecycleChecker("getSnapshotBeforeUpdate");
849
+ const isShouldComponentUpdate = createLifecycleChecker("shouldComponentUpdate");
850
+ const isUnsafeComponentWillMount = createLifecycleChecker("UNSAFE_componentWillMount");
851
+ const isUnsafeComponentWillReceiveProps = createLifecycleChecker("UNSAFE_componentWillReceiveProps");
852
+ const isUnsafeComponentWillUpdate = createLifecycleChecker("UNSAFE_componentWillUpdate");
853
+ const isGetDefaultProps = createLifecycleChecker("getDefaultProps", true);
854
+ const isGetDerivedStateFromProps = createLifecycleChecker("getDerivedStateFromProps", true);
855
+ const isGetDerivedStateFromError = createLifecycleChecker("getDerivedStateFromError", true);
856
+ /**
857
+ * Check if the given node is a componentDidMount callback
858
+ * @param node The node to check
859
+ * @returns True if the node is a componentDidMount callback, false otherwise
860
+ */
861
+ function isComponentDidMountCallback(node) {
862
+ return ast.isFunction(node) && isComponentDidMount(node.parent) && node.parent.value === node;
863
+ }
864
+ /**
865
+ * Check if the given node is a componentWillUnmount callback
866
+ * @param node The node to check
867
+ * @returns True if the node is a componentWillUnmount callback, false otherwise
868
+ */
869
+ function isComponentWillUnmountCallback(node) {
870
+ return ast.isFunction(node) && isComponentWillUnmount(node.parent) && node.parent.value === node;
871
+ }
872
+ /**
873
+ * Check whether given node is a render method of a class component
874
+ * @example
875
+ * ```tsx
876
+ * class Component extends React.Component {
877
+ * renderHeader = () => <div />;
878
+ * renderFooter = () => <div />;
879
+ * }
880
+ * ```
881
+ * @param node The AST node to check
882
+ * @returns `true` if node is a render function, `false` if not
883
+ */
884
+ function isRenderMethodLike(node) {
885
+ return ast.isMethodOrProperty(node) && node.key.type === AST_NODE_TYPES.Identifier && node.key.name.startsWith("render") && node.parent.parent.type === AST_NODE_TYPES.ClassDeclaration;
886
+ }
887
+ /**
888
+ * Check if the given node is a function within a render method of a class component
889
+ *
890
+ * @param node The AST node to check
891
+ * @returns `true` if the node is a render function inside a class component
892
+ *
893
+ * @example
894
+ * ```tsx
895
+ * class Component extends React.Component {
896
+ * renderHeader = () => <div />; // Returns true
897
+ * }
898
+ * ```
899
+ */
900
+ function isRenderMethodCallback(node) {
901
+ const parent = node.parent;
902
+ const greatGrandparent = parent.parent?.parent;
903
+ return greatGrandparent != null && isRenderMethodLike(parent) && isClassComponent(greatGrandparent);
904
+ }
905
+ /**
906
+ * Check whether the given node is a this.setState() call
907
+ * @param node The node to check
908
+ * @internal
909
+ */
910
+ function isThisSetState(node) {
911
+ const { callee } = node;
912
+ return callee.type === AST_NODE_TYPES.MemberExpression && ast.isThisExpressionLoose(callee.object) && callee.property.type === AST_NODE_TYPES.Identifier && callee.property.name === "setState";
913
+ }
914
+ /**
915
+ * Check whether the given node is an assignment to this.state
916
+ * @param node The node to check
917
+ * @internal
918
+ */
919
+ function isAssignmentToThisState(node) {
920
+ const { left } = node;
921
+ return left.type === AST_NODE_TYPES.MemberExpression && ast.isThisExpressionLoose(left.object) && ast.getPropertyName(left.property) === "state";
922
+ }
720
923
 
721
924
  //#endregion
722
925
  //#region src/component/component-wrapper.ts
@@ -771,7 +974,7 @@ function isComponentWrapperCallbackLoose(context, node) {
771
974
  * Get function component identifier from `const Component = memo(() => {});`
772
975
  * @param context The rule context
773
976
  * @param node The function node to analyze
774
- * @returns The function identifier or `unit` if not found
977
+ * @returns The function identifier or `null` if not found
775
978
  */
776
979
  function getFunctionComponentId(context, node) {
777
980
  const functionId = ast.getFunctionId(node);
@@ -779,7 +982,7 @@ function getFunctionComponentId(context, node) {
779
982
  const { parent } = node;
780
983
  if (parent.type === AST_NODE_TYPES.CallExpression && isComponentWrapperCallLoose(context, parent) && parent.parent.type === AST_NODE_TYPES.VariableDeclarator) return parent.parent.id;
781
984
  if (parent.type === AST_NODE_TYPES.CallExpression && isComponentWrapperCallLoose(context, parent) && parent.parent.type === AST_NODE_TYPES.CallExpression && isComponentWrapperCallLoose(context, parent.parent) && parent.parent.parent.type === AST_NODE_TYPES.VariableDeclarator) return parent.parent.parent.id;
782
- return unit;
985
+ return null;
783
986
  }
784
987
 
785
988
  //#endregion
@@ -814,75 +1017,24 @@ function isFunctionWithLooseComponentName(context, fn, allowNone = false) {
814
1017
  }
815
1018
 
816
1019
  //#endregion
817
- //#region src/component/component-render-method.ts
1020
+ //#region src/component/component-detection.ts
818
1021
  /**
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
1022
+ * Hints for component collector
860
1023
  */
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
- }
1024
+ const ComponentDetectionHint = {
1025
+ ...JsxDetectionHint,
1026
+ DoNotIncludeFunctionDefinedAsArrayFlatMapCallback: 1n << 17n,
1027
+ DoNotIncludeFunctionDefinedAsArrayMapCallback: 1n << 16n,
1028
+ DoNotIncludeFunctionDefinedInArrayExpression: 1n << 15n,
1029
+ DoNotIncludeFunctionDefinedInArrayPattern: 1n << 14n,
1030
+ DoNotIncludeFunctionDefinedOnClassMethod: 1n << 12n,
1031
+ DoNotIncludeFunctionDefinedOnClassProperty: 1n << 13n,
1032
+ DoNotIncludeFunctionDefinedOnObjectMethod: 1n << 11n
1033
+ };
873
1034
  /**
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`
1035
+ * Default component detection hint
879
1036
  */
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
- }
1037
+ 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
1038
  /**
887
1039
  * Determine if a function node represents a valid React component definition
888
1040
  *
@@ -893,8 +1045,33 @@ function isChildrenOfCreateElement(context, node) {
893
1045
  */
894
1046
  function isComponentDefinition(context, node, hint) {
895
1047
  if (!isFunctionWithLooseComponentName(context, node, true)) return false;
896
- if (isChildrenOfCreateElement(context, node) || isRenderMethodCallback(node)) return false;
897
- if (shouldExcludeBasedOnHint(node, hint)) return false;
1048
+ switch (true) {
1049
+ case node.parent.type === AST_NODE_TYPES.CallExpression && isCreateElementCall(context, node.parent) && node.parent.arguments.slice(2).some((arg) => arg === node): return false;
1050
+ case isRenderMethodCallback(node): return false;
1051
+ }
1052
+ switch (true) {
1053
+ 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:
1054
+ if (hint & ComponentDetectionHint.DoNotIncludeFunctionDefinedOnObjectMethod) return false;
1055
+ break;
1056
+ case ast.isOneOf([AST_NODE_TYPES.ArrowFunctionExpression, AST_NODE_TYPES.FunctionExpression])(node) && node.parent.type === AST_NODE_TYPES.MethodDefinition:
1057
+ if (hint & ComponentDetectionHint.DoNotIncludeFunctionDefinedOnClassMethod) return false;
1058
+ break;
1059
+ case ast.isOneOf([AST_NODE_TYPES.ArrowFunctionExpression, AST_NODE_TYPES.FunctionExpression])(node) && node.parent.type === AST_NODE_TYPES.Property:
1060
+ if (hint & ComponentDetectionHint.DoNotIncludeFunctionDefinedOnClassProperty) return false;
1061
+ break;
1062
+ case node.parent.type === AST_NODE_TYPES.ArrayPattern:
1063
+ if (hint & ComponentDetectionHint.DoNotIncludeFunctionDefinedInArrayPattern) return false;
1064
+ break;
1065
+ case node.parent.type === AST_NODE_TYPES.ArrayExpression:
1066
+ if (hint & ComponentDetectionHint.DoNotIncludeFunctionDefinedInArrayExpression) return false;
1067
+ break;
1068
+ 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":
1069
+ if (hint & ComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayMapCallback) return false;
1070
+ break;
1071
+ 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":
1072
+ if (hint & ComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayFlatMapCallback) return false;
1073
+ break;
1074
+ }
898
1075
  const significantParent = ast.findParentNode(node, ast.isOneOf([
899
1076
  AST_NODE_TYPES.JSXExpressionContainer,
900
1077
  AST_NODE_TYPES.ArrowFunctionExpression,
@@ -913,15 +1090,12 @@ function isComponentDefinition(context, node, hint) {
913
1090
  * Component flag constants
914
1091
  */
915
1092
  const ComponentFlag = {
916
- None: 0n,
917
- PureComponent: 1n << 0n,
918
1093
  CreateElement: 1n << 1n,
1094
+ ForwardRef: 1n << 3n,
919
1095
  Memo: 1n << 2n,
920
- ForwardRef: 1n << 3n
1096
+ None: 0n,
1097
+ PureComponent: 1n << 0n
921
1098
  };
922
-
923
- //#endregion
924
- //#region src/component/component-init-path.ts
925
1099
  /**
926
1100
  * Get component flag from init path
927
1101
  * @param initPath The init path of the function component
@@ -936,7 +1110,7 @@ function getComponentFlagFromInitPath(initPath) {
936
1110
 
937
1111
  //#endregion
938
1112
  //#region src/component/component-collector.ts
939
- const idGen$1 = new IdGenerator("function_component_");
1113
+ const idGen$1 = new IdGenerator("function-component:");
940
1114
  /**
941
1115
  * Get a ctx and visitor object for the rule to collect function components
942
1116
  * @param context The ESLint rule context
@@ -948,14 +1122,14 @@ function useComponentCollector(context, options = {}) {
948
1122
  const functionEntries = [];
949
1123
  const components = /* @__PURE__ */ new Map();
950
1124
  const getText = (n) => context.sourceCode.getText(n);
951
- const getCurrentEntry = () => functionEntries.at(-1);
1125
+ const getCurrentEntry = () => functionEntries.at(-1) ?? null;
952
1126
  const onFunctionEnter = (node) => {
953
1127
  const key = idGen$1.next();
954
1128
  const exp = ast.findParentNode(node, (n) => n.type === AST_NODE_TYPES.ExportDefaultDeclaration);
955
1129
  const isExportDefault = exp != null;
956
1130
  const isExportDefaultDeclaration = exp != null && ast.getUnderlyingExpression(exp.declaration) === node;
957
1131
  const id = getFunctionComponentId(context, node);
958
- const name = id == null ? unit : ast.getFullyQualifiedName(id, getText);
1132
+ const name = id == null ? null : ast.getFullyQualifiedName(id, getText);
959
1133
  const initPath = ast.getFunctionInitPath(node);
960
1134
  const directives = ast.getFunctionDirectives(node);
961
1135
  const entry = {
@@ -963,9 +1137,8 @@ function useComponentCollector(context, options = {}) {
963
1137
  key,
964
1138
  kind: "function-component",
965
1139
  name,
966
- node,
967
1140
  directives,
968
- displayName: unit,
1141
+ displayName: null,
969
1142
  flag: getComponentFlagFromInitPath(initPath),
970
1143
  hint,
971
1144
  hookCalls: [],
@@ -973,6 +1146,7 @@ function useComponentCollector(context, options = {}) {
973
1146
  isComponentDefinition: isComponentDefinition(context, node, hint),
974
1147
  isExportDefault,
975
1148
  isExportDefaultDeclaration,
1149
+ node,
976
1150
  rets: []
977
1151
  };
978
1152
  functionEntries.push(entry);
@@ -1002,13 +1176,13 @@ function useComponentCollector(context, options = {}) {
1002
1176
  if (body.type === AST_NODE_TYPES.BlockStatement) return;
1003
1177
  entry.rets.push(body);
1004
1178
  if (!entry.isComponentDefinition) return;
1005
- if (!components.has(entry.key) && !isJsxLike(context.sourceCode, body, hint)) return;
1179
+ if (!components.has(entry.key) && !isJsxLike(context, body, hint)) return;
1006
1180
  components.set(entry.key, entry);
1007
1181
  },
1008
1182
  ...collectDisplayName ? { [ast.SEL_DISPLAY_NAME_ASSIGNMENT_EXPRESSION](node) {
1009
1183
  const { left, right } = node;
1010
1184
  if (left.type !== AST_NODE_TYPES.MemberExpression) return;
1011
- const componentName = left.object.type === AST_NODE_TYPES.Identifier ? left.object.name : unit;
1185
+ const componentName = left.object.type === AST_NODE_TYPES.Identifier ? left.object.name : null;
1012
1186
  const component = [...components.values()].findLast(({ name }) => name != null && name === componentName);
1013
1187
  if (component == null) return;
1014
1188
  component.displayName = right;
@@ -1027,7 +1201,7 @@ function useComponentCollector(context, options = {}) {
1027
1201
  entry.rets.push(node.argument);
1028
1202
  if (!entry.isComponentDefinition) return;
1029
1203
  const { argument } = node;
1030
- if (!components.has(entry.key) && !isJsxLike(context.sourceCode, argument, hint)) return;
1204
+ if (!components.has(entry.key) && !isJsxLike(context, argument, hint)) return;
1031
1205
  components.set(entry.key, entry);
1032
1206
  }
1033
1207
  }
@@ -1036,7 +1210,7 @@ function useComponentCollector(context, options = {}) {
1036
1210
 
1037
1211
  //#endregion
1038
1212
  //#region src/component/component-collector-legacy.ts
1039
- const idGen = new IdGenerator("class_component_");
1213
+ const idGen = new IdGenerator("class-component:");
1040
1214
  /**
1041
1215
  * Get a ctx and visitor object for the rule to collect class componentss
1042
1216
  * @param context The ESLint rule context
@@ -1052,18 +1226,18 @@ function useComponentCollectorLegacy(context) {
1052
1226
  if (!isClassComponent(node)) return;
1053
1227
  const id = ast.getClassId(node);
1054
1228
  const key = idGen.next();
1055
- const name = id == null ? unit : ast.getFullyQualifiedName(id, getText);
1229
+ const name = id == null ? null : ast.getFullyQualifiedName(id, getText);
1056
1230
  const flag = isPureComponent(node) ? ComponentFlag.PureComponent : ComponentFlag.None;
1057
1231
  components.set(key, {
1058
1232
  id,
1059
1233
  key,
1060
1234
  kind: "class-component",
1061
1235
  name,
1062
- node,
1063
- displayName: unit,
1236
+ displayName: null,
1064
1237
  flag,
1065
1238
  hint: 0n,
1066
- methods: []
1239
+ methods: [],
1240
+ node
1067
1241
  });
1068
1242
  };
1069
1243
  return {
@@ -1074,179 +1248,6 @@ function useComponentCollectorLegacy(context) {
1074
1248
  }
1075
1249
  };
1076
1250
  }
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
1251
 
1251
1252
  //#endregion
1252
1253
  //#region src/ref/ref-name.ts
@@ -1255,12 +1256,18 @@ function isInsideComponentOrHook(node) {
1255
1256
  * @param name The name to check
1256
1257
  * @returns True if the name is "ref" or ends with "Ref"
1257
1258
  */
1258
- function isRefName(name) {
1259
+ function isRefLikeName(name) {
1259
1260
  return name === "ref" || name.endsWith("Ref");
1260
1261
  }
1261
1262
 
1262
1263
  //#endregion
1263
- //#region src/ref/is-from-ref.ts
1264
+ //#region src/ref/ref-id.ts
1265
+ function isRefId(node) {
1266
+ return node.type === AST_NODE_TYPES.Identifier && isRefLikeName(node.name);
1267
+ }
1268
+
1269
+ //#endregion
1270
+ //#region src/ref/ref-init.ts
1264
1271
  /**
1265
1272
  * Check if the variable with the given name is initialized or derived from a ref
1266
1273
  * @param name The variable name
@@ -1274,20 +1281,20 @@ function isInitializedFromRef(name, initialScope) {
1274
1281
  * Get the init expression of a ref variable
1275
1282
  * @param name The variable name
1276
1283
  * @param initialScope The initial scope
1277
- * @returns The init expression node if the variable is derived from a ref, or undefined otherwise
1284
+ * @returns The init expression node if the variable is derived from a ref, or null otherwise
1278
1285
  */
1279
1286
  function getRefInit(name, initialScope) {
1280
- for (const { node } of findVariable(initialScope)(name)?.defs ?? []) {
1287
+ for (const { node } of findVariable(initialScope, name)?.defs ?? []) {
1281
1288
  if (node.type !== AST_NODE_TYPES$1.VariableDeclarator) continue;
1282
1289
  const init = node.init;
1283
1290
  if (init == null) continue;
1284
1291
  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;
1292
+ case init.type === AST_NODE_TYPES$1.MemberExpression && init.object.type === AST_NODE_TYPES$1.Identifier && isRefLikeName(init.object.name): return init;
1286
1293
  case init.type === AST_NODE_TYPES$1.CallExpression && isUseRefCall(init): return init;
1287
1294
  }
1288
1295
  }
1289
- return unit;
1296
+ return null;
1290
1297
  }
1291
1298
 
1292
1299
  //#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 };
1300
+ export { ComponentDetectionHint, ComponentFlag, DEFAULT_COMPONENT_DETECTION_HINT, DEFAULT_JSX_DETECTION_HINT, JsxDetectionHint, JsxEmit, JsxInspector, REACT_BUILTIN_HOOK_NAMES, findImportSource, getComponentFlagFromInitPath, getFunctionComponentId, getJsxConfigFromAnnotation, getJsxConfigFromContext, 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, isJsxLike, 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, useComponentCollector, useComponentCollectorLegacy, useHookCollector };