@eslint-react/ast 5.17.2 → 5.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,47 +1,87 @@
1
1
  import { AST_NODE_TYPES, TSESTree } from "@typescript-eslint/types";
2
2
  //#region src/types.d.ts
3
+ /** Union of function-like node types. */
3
4
  type TSESTreeFunction = TSESTree.ArrowFunctionExpression | TSESTree.FunctionDeclaration | TSESTree.FunctionExpression;
5
+ /** Union of class-like node types. */
4
6
  type TSESTreeClass = TSESTree.ClassDeclaration | TSESTree.ClassExpression;
7
+ /** Union of method and property definition node types. */
5
8
  type TSESTreeMethodOrPropertyDefinition = TSESTree.PropertyDefinition | TSESTree.MethodDefinition;
9
+ /** Union of all JSX-related node types. */
6
10
  type TSESTreeJSX = TSESTree.JSXAttribute | TSESTree.JSXClosingElement | TSESTree.JSXClosingFragment | TSESTree.JSXElement | TSESTree.JSXEmptyExpression | TSESTree.JSXExpressionContainer | TSESTree.JSXFragment | TSESTree.JSXIdentifier | TSESTree.JSXMemberExpression | TSESTree.JSXNamespacedName | TSESTree.JSXOpeningElement | TSESTree.JSXOpeningFragment | TSESTree.JSXSpreadAttribute | TSESTree.JSXSpreadChild | TSESTree.JSXText;
11
+ /** Union of JSX element-like node types (element or fragment). */
7
12
  type TSESTreeJSXElementLike = TSESTree.JSXElement | TSESTree.JSXFragment;
13
+ /** Union of JSX attribute-like node types (attribute or spread attribute). */
8
14
  type TSESTreeJSXAttributeLike = TSESTree.JSXAttribute | TSESTree.JSXSpreadAttribute;
15
+ /** An expression statement that is a directive (ex: `"use strict"`). */
9
16
  type TSESTreeDirective = TSESTree.ExpressionStatement & {
10
17
  directive: string;
11
18
  expression: TSESTree.StringLiteral;
12
19
  };
20
+ /** Union of TypeScript type expression node types. */
13
21
  type TSESTreeTypeExpression = TSESTree.TSAsExpression | TSESTree.TSTypeAssertion | TSESTree.TSNonNullExpression | TSESTree.TSSatisfiesExpression | TSESTree.TSInstantiationExpression;
14
22
  declare namespace check_d_exports {
15
- export { is, isClass, isDirective, isExpression, isFunction, isIdentifier, isJSX, isJSXElement, isJSXElementOrFragment, isJSXFragment, isJSXTagNameExpression, isOneOf, isProperty, isPropertyOrMethod, isTypeAssertionExpression, isTypeExpression };
23
+ export { is, isClass, isConditional, isDirective, isExpression, isFunction, isIdentifier, isJSX, isJSXElement, isJSXElementOrFragment, isJSXFragment, isJSXTagNameExpression, isOneOf, isProperty, isPropertyOrMethod, isTypeAssertionExpression, isTypeExpression };
16
24
  }
25
+ /** Check if a node is of the given type. */
17
26
  declare const is: <NodeType extends AST_NODE_TYPES>(nodeType: NodeType) => (node: TSESTree.Node | null | undefined) => node is Extract<TSESTree.Node, {
18
27
  type: NodeType;
19
28
  }>;
29
+ /** Check if a node is one of the given types. */
20
30
  declare const isOneOf: <NodeTypes extends readonly AST_NODE_TYPES[]>(nodeTypes: NodeTypes) => (node: TSESTree.Node | null | undefined) => node is Extract<TSESTree.Node, {
21
31
  type: NodeTypes[number];
22
32
  }>;
33
+ /**
34
+ * Check if a node is a directive statement (ex: `"use client"`), optionally matching a specific directive name.
35
+ * @param node The node to check.
36
+ * @param name The directive name to match. When omitted, any directive matches.
37
+ * @returns `true` if the node is a matching directive statement.
38
+ */
23
39
  declare function isDirective(node: TSESTree.Node, name?: string): node is TSESTreeDirective;
40
+ /**
41
+ * Check if a node is an identifier, optionally matching a specific name.
42
+ * @param node The node to check.
43
+ * @param name The identifier name to match. When omitted, any identifier matches.
44
+ * @returns `true` if the node is a matching identifier.
45
+ */
24
46
  declare function isIdentifier(node: TSESTree.Node, name?: string): node is TSESTree.Identifier;
47
+ /** Check if a node is a class declaration or class expression. */
25
48
  declare const isClass: (node: TSESTree.Node | null | undefined) => node is TSESTree.ClassDeclarationWithName | TSESTree.ClassDeclarationWithOptionalName | TSESTree.ClassExpression;
49
+ /** Check if a node is a function declaration, function expression, or arrow function expression. */
26
50
  declare const isFunction: (node: TSESTree.Node | null | undefined) => node is TSESTree.ArrowFunctionExpressionWithBlockBody | TSESTree.ArrowFunctionExpressionWithExpressionBody | TSESTree.FunctionDeclarationWithName | TSESTree.FunctionDeclarationWithOptionalName | TSESTree.FunctionExpression;
51
+ /** Check if a node is a property-like node (property definition, index signature, parameter property, or property signature). */
27
52
  declare const isProperty: (node: TSESTree.Node | null | undefined) => node is TSESTree.PropertyDefinitionComputedName | TSESTree.PropertyDefinitionNonComputedName | TSESTree.TSIndexSignature | TSESTree.TSParameterProperty | TSESTree.TSPropertySignatureComputedName | TSESTree.TSPropertySignatureNonComputedName;
53
+ /** Check if a node is a property or method definition. */
28
54
  declare const isPropertyOrMethod: (node: TSESTree.Node | null | undefined) => node is TSESTree.MethodDefinitionComputedName | TSESTree.MethodDefinitionNonComputedName | TSESTree.PropertyDefinitionComputedName | TSESTree.PropertyDefinitionNonComputedName;
55
+ /** Check if a node is a JSX element. */
29
56
  declare const isJSXElement: (node: TSESTree.Node | null | undefined) => node is TSESTree.JSXElement;
57
+ /** Check if a node is a JSX fragment. */
30
58
  declare const isJSXFragment: (node: TSESTree.Node | null | undefined) => node is TSESTree.JSXFragment;
59
+ /** Check if a node is a JSX element or JSX fragment. */
31
60
  declare const isJSXElementOrFragment: (node: TSESTree.Node | null | undefined) => node is TSESTree.JSXElement | TSESTree.JSXFragment;
61
+ /** Check if a node can appear as a JSX tag name. */
32
62
  declare const isJSXTagNameExpression: (node: TSESTree.Node | null | undefined) => node is TSESTree.JSXIdentifier | TSESTree.JSXMemberExpression | TSESTree.JSXNamespacedName;
63
+ /** Check if a node is any JSX-related node. */
33
64
  declare const isJSX: (node: TSESTree.Node | null | undefined) => node is TSESTree.JSXAttribute | TSESTree.JSXClosingElement | TSESTree.JSXClosingFragment | TSESTree.JSXElement | TSESTree.JSXEmptyExpression | TSESTree.JSXExpressionContainer | TSESTree.JSXFragment | TSESTree.JSXIdentifier | TSESTree.JSXMemberExpression | TSESTree.JSXNamespacedName | TSESTree.JSXOpeningElement | TSESTree.JSXOpeningFragment | TSESTree.JSXSpreadAttribute | TSESTree.JSXSpreadChild | TSESTree.JSXText;
65
+ /** Check if a node is a TypeScript type expression (assertion, non-null, satisfies, or instantiation). */
34
66
  declare const isTypeExpression: (node: TSESTree.Node | null | undefined) => node is TSESTree.TSAsExpression | TSESTree.TSInstantiationExpression | TSESTree.TSNonNullExpression | TSESTree.TSSatisfiesExpression | TSESTree.TSTypeAssertion;
67
+ /** Check if a node is a TypeScript type assertion-like expression (as, assertion, non-null, or satisfies). */
35
68
  declare const isTypeAssertionExpression: (node: TSESTree.Node | null | undefined) => node is TSESTree.TSAsExpression | TSESTree.TSNonNullExpression | TSESTree.TSSatisfiesExpression | TSESTree.TSTypeAssertion;
69
+ /** Check if a node is an expression node. */
36
70
  declare const isExpression: (node: TSESTree.Node | null | undefined) => node is TSESTree.ArrayExpression | TSESTree.ArrayPattern | TSESTree.ArrowFunctionExpressionWithBlockBody | TSESTree.ArrowFunctionExpressionWithExpressionBody | TSESTree.AssignmentExpression | TSESTree.AwaitExpression | TSESTree.PrivateInExpression | TSESTree.SymmetricBinaryExpression | TSESTree.CallExpression | TSESTree.ChainExpression | TSESTree.ClassExpression | TSESTree.ConditionalExpression | TSESTree.FunctionExpression | TSESTree.Identifier | TSESTree.ImportExpression | TSESTree.JSXElement | TSESTree.JSXFragment | TSESTree.BigIntLiteral | TSESTree.BooleanLiteral | TSESTree.NullLiteral | TSESTree.NumberLiteral | TSESTree.RegExpLiteral | TSESTree.StringLiteral | TSESTree.LogicalExpression | TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedName | TSESTree.MetaProperty | TSESTree.NewExpression | TSESTree.ObjectExpression | TSESTree.ObjectPattern | TSESTree.SequenceExpression | TSESTree.Super | TSESTree.TaggedTemplateExpression | TSESTree.TemplateLiteral | TSESTree.ThisExpression | TSESTree.TSAsExpression | TSESTree.TSInstantiationExpression | TSESTree.TSNonNullExpression | TSESTree.TSSatisfiesExpression | TSESTree.TSTypeAssertion | TSESTree.UnaryExpressionBitwiseNot | TSESTree.UnaryExpressionDelete | TSESTree.UnaryExpressionMinus | TSESTree.UnaryExpressionNot | TSESTree.UnaryExpressionPlus | TSESTree.UnaryExpressionTypeof | TSESTree.UnaryExpressionVoid | TSESTree.UpdateExpression | TSESTree.YieldNoStarExpression | TSESTree.YieldStarExpression;
71
+ /**
72
+ * Check if a node is a conditional expression or a control flow statement.
73
+ * @param node The node to check.
74
+ * @returns `true` if the node is conditional.
75
+ */
76
+ declare const isConditional: (node: TSESTree.Node | null | undefined) => node is TSESTree.ConditionalExpression | TSESTree.DoWhileStatement | TSESTree.ForInStatement | TSESTree.ForOfStatement | TSESTree.ForStatement | TSESTree.IfStatement | TSESTree.LogicalExpression | TSESTree.SwitchStatement | TSESTree.WhileStatement;
37
77
  declare namespace compare_d_exports {
38
78
  export { isEqual };
39
79
  }
40
80
  /**
41
- * Check if two nodes are equal.
42
- * @param a node to compare.
43
- * @param b node to compare.
44
- * @returns `true` if node equal.
81
+ * Check if two nodes are structurally equal.
82
+ * @param a The first node to compare.
83
+ * @param b The second node to compare.
84
+ * @returns `true` if the nodes are equal.
45
85
  * @see https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/src/util/isNodeEqual.ts
46
86
  */
47
87
  declare const isEqual: {
@@ -49,18 +89,60 @@ declare const isEqual: {
49
89
  (a: TSESTree.Node, b: TSESTree.Node): boolean;
50
90
  };
51
91
  declare namespace extract_d_exports {
52
- export { getCalleeName, getFullyQualifiedName, getRootIdentifier, getStaticPropertyName, unwrap };
92
+ export { findProperty, getCalleeName, getFullyQualifiedName, getIdentifierAt, getPropertyName, unwrap };
53
93
  }
94
+ /**
95
+ * Recursively unwrap TypeScript type expressions and chain expressions to get the underlying expression.
96
+ * @param node The node to unwrap.
97
+ * @returns The innermost non-type-expression node.
98
+ */
54
99
  declare function unwrap(node: TSESTree.Node): Exclude<TSESTree.Node, TSESTreeTypeExpression>;
55
- declare function getRootIdentifier(node: TSESTree.Expression | TSESTree.PrivateIdentifier): TSESTree.Identifier | null;
100
+ /**
101
+ * Find a property by name in a list of object literal properties, recursing into spread object expressions.
102
+ * @param properties The object literal properties to search.
103
+ * @param name The property name to look for.
104
+ * @returns The matching `Property` node, or `null` when not found.
105
+ */
106
+ declare function findProperty(properties: TSESTree.ObjectLiteralElement[], name: string): TSESTree.Property | null;
107
+ /**
108
+ * Get the name of the callee of a call expression.
109
+ * @param node The call expression to inspect.
110
+ * @returns The callee name (ex: `"useState"`), or `null` when it cannot be statically determined.
111
+ */
56
112
  declare function getCalleeName(node: TSESTree.CallExpression): string | null;
57
- declare function getStaticPropertyName(prop: TSESTree.Property): string | null;
113
+ /**
114
+ * Get the static name of an object property's key.
115
+ * @param property The property to inspect.
116
+ * @param effort `"min"` only matches plain identifiers; `"max"` also resolves string literals and simple template literals.
117
+ * @returns The property name, or `null` when it cannot be statically determined.
118
+ */
119
+ declare function getPropertyName(property: TSESTree.Property, effort?: "min" | "max"): string | null;
120
+ /**
121
+ * Get the fully qualified name of a node (ex: `React.useState`), falling back to source text when needed.
122
+ * @param node The node to inspect.
123
+ * @param getText A function returning the source text of a node.
124
+ * @returns The fully qualified name.
125
+ */
58
126
  declare function getFullyQualifiedName(node: TSESTree.Node, getText: (node: TSESTree.Node) => string): string;
127
+ /**
128
+ * Get the identifier at a given position in a member expression chain (ex: position `0` in `a.b.c` is `a`).
129
+ * @param node The expression to walk.
130
+ * @param position The position of the identifier to return.
131
+ * @returns The identifier at the given position, or `null` when there is none.
132
+ */
133
+ declare function getIdentifierAt(node: TSESTree.Expression | TSESTree.PrivateIdentifier, position: number): TSESTree.Identifier | null;
59
134
  declare namespace traverse_d_exports {
60
135
  export { findParent };
61
136
  }
62
137
  type Predicate<T extends TSESTree.Node> = (node: TSESTree.Node) => node is T;
63
138
  type NodePredicate = (node: TSESTree.Node) => boolean;
139
+ /**
140
+ * Walk up the AST from `node` to find the nearest ancestor matching a predicate.
141
+ * @param node The starting node for the upward search.
142
+ * @param test The predicate a candidate ancestor must satisfy.
143
+ * @param stop An optional predicate that aborts the search when it returns `true`.
144
+ * @returns The first matching ancestor, or `null` when none is found.
145
+ */
64
146
  declare function findParent<T extends TSESTree.Node>(node: TSESTree.Node | null, test: Predicate<T>, stop?: NodePredicate): T | null;
65
147
  declare function findParent(node: TSESTree.Node | null, test: NodePredicate, stop?: NodePredicate): TSESTree.Node | null;
66
148
  //#endregion
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ import { ASTUtils } from "@typescript-eslint/utils";
6
6
  var check_exports = /* @__PURE__ */ __exportAll({
7
7
  is: () => is,
8
8
  isClass: () => isClass,
9
+ isConditional: () => isConditional,
9
10
  isDirective: () => isDirective,
10
11
  isExpression: () => isExpression,
11
12
  isFunction: () => isFunction,
@@ -21,35 +22,58 @@ var check_exports = /* @__PURE__ */ __exportAll({
21
22
  isTypeAssertionExpression: () => isTypeAssertionExpression,
22
23
  isTypeExpression: () => isTypeExpression
23
24
  });
25
+ /** Check if a node is of the given type. */
24
26
  const is = ASTUtils.isNodeOfType;
27
+ /** Check if a node is one of the given types. */
25
28
  const isOneOf = ASTUtils.isNodeOfTypes;
29
+ /**
30
+ * Check if a node is a directive statement (ex: `"use client"`), optionally matching a specific directive name.
31
+ * @param node The node to check.
32
+ * @param name The directive name to match. When omitted, any directive matches.
33
+ * @returns `true` if the node is a matching directive statement.
34
+ */
26
35
  function isDirective(node, name) {
27
36
  return node.type === AST_NODE_TYPES.ExpressionStatement && (name == null || node.directive === name);
28
37
  }
38
+ /**
39
+ * Check if a node is an identifier, optionally matching a specific name.
40
+ * @param node The node to check.
41
+ * @param name The identifier name to match. When omitted, any identifier matches.
42
+ * @returns `true` if the node is a matching identifier.
43
+ */
29
44
  function isIdentifier(node, name) {
30
45
  return node.type === AST_NODE_TYPES.Identifier && (name == null || node.name === name);
31
46
  }
47
+ /** Check if a node is a class declaration or class expression. */
32
48
  const isClass = isOneOf([AST_NODE_TYPES.ClassDeclaration, AST_NODE_TYPES.ClassExpression]);
49
+ /** Check if a node is a function declaration, function expression, or arrow function expression. */
33
50
  const isFunction = isOneOf([
34
51
  AST_NODE_TYPES.ArrowFunctionExpression,
35
52
  AST_NODE_TYPES.FunctionDeclaration,
36
53
  AST_NODE_TYPES.FunctionExpression
37
54
  ]);
55
+ /** Check if a node is a property-like node (property definition, index signature, parameter property, or property signature). */
38
56
  const isProperty = isOneOf([
39
57
  AST_NODE_TYPES.PropertyDefinition,
40
58
  AST_NODE_TYPES.TSIndexSignature,
41
59
  AST_NODE_TYPES.TSParameterProperty,
42
60
  AST_NODE_TYPES.TSPropertySignature
43
61
  ]);
62
+ /** Check if a node is a property or method definition. */
44
63
  const isPropertyOrMethod = isOneOf([AST_NODE_TYPES.PropertyDefinition, AST_NODE_TYPES.MethodDefinition]);
64
+ /** Check if a node is a JSX element. */
45
65
  const isJSXElement = is(AST_NODE_TYPES.JSXElement);
66
+ /** Check if a node is a JSX fragment. */
46
67
  const isJSXFragment = is(AST_NODE_TYPES.JSXFragment);
68
+ /** Check if a node is a JSX element or JSX fragment. */
47
69
  const isJSXElementOrFragment = isOneOf([AST_NODE_TYPES.JSXElement, AST_NODE_TYPES.JSXFragment]);
70
+ /** Check if a node can appear as a JSX tag name. */
48
71
  const isJSXTagNameExpression = isOneOf([
49
72
  AST_NODE_TYPES.JSXIdentifier,
50
73
  AST_NODE_TYPES.JSXMemberExpression,
51
74
  AST_NODE_TYPES.JSXNamespacedName
52
75
  ]);
76
+ /** Check if a node is any JSX-related node. */
53
77
  const isJSX = isOneOf([
54
78
  AST_NODE_TYPES.JSXAttribute,
55
79
  AST_NODE_TYPES.JSXClosingElement,
@@ -67,6 +91,7 @@ const isJSX = isOneOf([
67
91
  AST_NODE_TYPES.JSXSpreadChild,
68
92
  AST_NODE_TYPES.JSXText
69
93
  ]);
94
+ /** Check if a node is a TypeScript type expression (assertion, non-null, satisfies, or instantiation). */
70
95
  const isTypeExpression = isOneOf([
71
96
  AST_NODE_TYPES.TSAsExpression,
72
97
  AST_NODE_TYPES.TSTypeAssertion,
@@ -74,12 +99,14 @@ const isTypeExpression = isOneOf([
74
99
  AST_NODE_TYPES.TSSatisfiesExpression,
75
100
  AST_NODE_TYPES.TSInstantiationExpression
76
101
  ]);
102
+ /** Check if a node is a TypeScript type assertion-like expression (as, assertion, non-null, or satisfies). */
77
103
  const isTypeAssertionExpression = isOneOf([
78
104
  AST_NODE_TYPES.TSAsExpression,
79
105
  AST_NODE_TYPES.TSTypeAssertion,
80
106
  AST_NODE_TYPES.TSNonNullExpression,
81
107
  AST_NODE_TYPES.TSSatisfiesExpression
82
108
  ]);
109
+ /** Check if a node is an expression node. */
83
110
  const isExpression = isOneOf([
84
111
  AST_NODE_TYPES.ArrayExpression,
85
112
  AST_NODE_TYPES.ArrayPattern,
@@ -117,6 +144,22 @@ const isExpression = isOneOf([
117
144
  AST_NODE_TYPES.UpdateExpression,
118
145
  AST_NODE_TYPES.YieldExpression
119
146
  ]);
147
+ /**
148
+ * Check if a node is a conditional expression or a control flow statement.
149
+ * @param node The node to check.
150
+ * @returns `true` if the node is conditional.
151
+ */
152
+ const isConditional = isOneOf([
153
+ AST_NODE_TYPES.DoWhileStatement,
154
+ AST_NODE_TYPES.ForInStatement,
155
+ AST_NODE_TYPES.ForOfStatement,
156
+ AST_NODE_TYPES.ForStatement,
157
+ AST_NODE_TYPES.WhileStatement,
158
+ AST_NODE_TYPES.IfStatement,
159
+ AST_NODE_TYPES.SwitchStatement,
160
+ AST_NODE_TYPES.LogicalExpression,
161
+ AST_NODE_TYPES.ConditionalExpression
162
+ ]);
120
163
 
121
164
  //#endregion
122
165
  //#region ../../.pkgs/eff/dist/index.js
@@ -595,35 +638,69 @@ const takeWhile = dual(2, (xs, pred) => {
595
638
  //#endregion
596
639
  //#region src/extract.ts
597
640
  var extract_exports = /* @__PURE__ */ __exportAll({
641
+ findProperty: () => findProperty,
598
642
  getCalleeName: () => getCalleeName,
599
643
  getFullyQualifiedName: () => getFullyQualifiedName,
600
- getRootIdentifier: () => getRootIdentifier,
601
- getStaticPropertyName: () => getStaticPropertyName,
644
+ getIdentifierAt: () => getIdentifierAt,
645
+ getPropertyName: () => getPropertyName,
602
646
  unwrap: () => unwrap
603
647
  });
648
+ /**
649
+ * Recursively unwrap TypeScript type expressions and chain expressions to get the underlying expression.
650
+ * @param node The node to unwrap.
651
+ * @returns The innermost non-type-expression node.
652
+ */
604
653
  function unwrap(node) {
605
654
  if (isTypeExpression(node) || node.type === AST_NODE_TYPES.ChainExpression) return unwrap(node.expression);
606
655
  return node;
607
656
  }
608
- function getRootIdentifier(node) {
609
- const expr = unwrap(node);
610
- if (expr.type === AST_NODE_TYPES.Identifier) return expr;
611
- if (expr.type === AST_NODE_TYPES.MemberExpression) return getRootIdentifier(expr.object);
657
+ /**
658
+ * Find a property by name in a list of object literal properties, recursing into spread object expressions.
659
+ * @param properties The object literal properties to search.
660
+ * @param name The property name to look for.
661
+ * @returns The matching `Property` node, or `null` when not found.
662
+ */
663
+ function findProperty(properties, name) {
664
+ for (const property of properties) {
665
+ if (property.type === AST_NODE_TYPES.Property && getPropertyName(property) === name) return property;
666
+ if (property.type === AST_NODE_TYPES.SpreadElement && property.argument.type === AST_NODE_TYPES.ObjectExpression) {
667
+ const found = findProperty(property.argument.properties, name);
668
+ if (found != null) return found;
669
+ }
670
+ }
612
671
  return null;
613
672
  }
673
+ /**
674
+ * Get the name of the callee of a call expression.
675
+ * @param node The call expression to inspect.
676
+ * @returns The callee name (ex: `"useState"`), or `null` when it cannot be statically determined.
677
+ */
614
678
  function getCalleeName(node) {
615
679
  const callee = unwrap(node.callee);
616
680
  if (callee.type === AST_NODE_TYPES.Identifier) return callee.name;
617
681
  if (callee.type === AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === AST_NODE_TYPES.Identifier) return callee.property.name;
618
682
  return null;
619
683
  }
620
- function getStaticPropertyName(prop) {
621
- const key = unwrap(prop.key);
622
- if (key.type === AST_NODE_TYPES.Identifier && !prop.computed) return key.name;
684
+ /**
685
+ * Get the static name of an object property's key.
686
+ * @param property The property to inspect.
687
+ * @param effort `"min"` only matches plain identifiers; `"max"` also resolves string literals and simple template literals.
688
+ * @returns The property name, or `null` when it cannot be statically determined.
689
+ */
690
+ function getPropertyName(property, effort = "min") {
691
+ const key = unwrap(property.key);
692
+ if (key.type === AST_NODE_TYPES.Identifier && !property.computed) return key.name;
693
+ if (effort === "min") return null;
623
694
  if (key.type === AST_NODE_TYPES.Literal && typeof key.value === "string") return key.value;
624
695
  if (key.type === AST_NODE_TYPES.TemplateLiteral && key.expressions.length === 0) return key.quasis[0]?.value.cooked ?? key.quasis[0]?.value.raw ?? null;
625
696
  return null;
626
697
  }
698
+ /**
699
+ * Get the fully qualified name of a node (ex: `React.useState`), falling back to source text when needed.
700
+ * @param node The node to inspect.
701
+ * @param getText A function returning the source text of a node.
702
+ * @returns The fully qualified name.
703
+ */
627
704
  function getFullyQualifiedName(node, getText) {
628
705
  const expr = unwrap(node);
629
706
  switch (expr.type) {
@@ -640,15 +717,32 @@ function getFullyQualifiedName(node, getText) {
640
717
  default: return getText(expr);
641
718
  }
642
719
  }
720
+ /**
721
+ * Get the identifier at a given position in a member expression chain (ex: position `0` in `a.b.c` is `a`).
722
+ * @param node The expression to walk.
723
+ * @param position The position of the identifier to return.
724
+ * @returns The identifier at the given position, or `null` when there is none.
725
+ */
726
+ function getIdentifierAt(node, position) {
727
+ const identifiers = [];
728
+ let current = unwrap(node);
729
+ while (current.type === AST_NODE_TYPES.MemberExpression) {
730
+ const property = unwrap(current.property);
731
+ identifiers.unshift(property.type === AST_NODE_TYPES.Identifier ? property : null);
732
+ current = unwrap(current.object);
733
+ }
734
+ identifiers.unshift(current.type === AST_NODE_TYPES.Identifier ? current : null);
735
+ return identifiers.at(position) ?? null;
736
+ }
643
737
 
644
738
  //#endregion
645
739
  //#region src/compare.ts
646
740
  var compare_exports = /* @__PURE__ */ __exportAll({ isEqual: () => isEqual });
647
741
  /**
648
- * Check if two nodes are equal.
649
- * @param a node to compare.
650
- * @param b node to compare.
651
- * @returns `true` if node equal.
742
+ * Check if two nodes are structurally equal.
743
+ * @param a The first node to compare.
744
+ * @param b The second node to compare.
745
+ * @returns `true` if the nodes are equal.
652
746
  * @see https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/src/util/isNodeEqual.ts
653
747
  */
654
748
  const isEqual = dual(2, (a, b) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eslint-react/ast",
3
- "version": "5.17.2",
3
+ "version": "5.18.0",
4
4
  "description": "ESLint React's TSESTree AST utility module.",
5
5
  "homepage": "https://github.com/Rel1cx/eslint-react",
6
6
  "bugs": {
@@ -29,20 +29,20 @@
29
29
  "dist"
30
30
  ],
31
31
  "dependencies": {
32
- "@typescript-eslint/types": "^8.64.0",
33
- "@typescript-eslint/typescript-estree": "^8.64.0",
34
- "@typescript-eslint/utils": "^8.64.0",
32
+ "@typescript-eslint/types": "^8.65.0",
33
+ "@typescript-eslint/typescript-estree": "^8.65.0",
34
+ "@typescript-eslint/utils": "^8.65.0",
35
35
  "string-ts": "^2.3.1"
36
36
  },
37
37
  "devDependencies": {
38
- "@typescript-eslint/parser": "^8.64.0",
38
+ "@typescript-eslint/parser": "^8.65.0",
39
39
  "eslint": "^10.7.0",
40
- "tsdown": "^0.22.9",
40
+ "tsdown": "^0.22.13",
41
41
  "typescript": "6.0.3",
42
42
  "vitest": "^4.1.10",
43
+ "@local/eff": "0.0.0",
43
44
  "@local/configs": "0.0.0",
44
- "@local/testkit": "0.0.0",
45
- "@local/eff": "0.0.0"
45
+ "@local/testkit": "0.0.0"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "eslint": "*",