Skip to content

Commit be5fe35

Browse files
authored
refactor: use Check.isIdentifier and Extract.unwrap consistently (#1938)
1 parent 9b8e5fa commit be5fe35

53 files changed

Lines changed: 205 additions & 163 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/ast/src/compare.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ export const isEqual: {
5151
}
5252
return true;
5353
}
54-
case a.type === AST.Identifier
55-
&& b.type === AST.Identifier:
54+
case Check.isIdentifier(a)
55+
&& Check.isIdentifier(b):
5656
return a.name === b.name;
5757
case a.type === AST.PrivateIdentifier
5858
&& b.type === AST.PrivateIdentifier:

packages/ast/src/extract.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ export function findProperty(properties: TSESTree.ObjectLiteralElement[], name:
4040
*/
4141
export function getCalleeName(node: TSESTree.CallExpression): string | null {
4242
const callee = unwrap(node.callee);
43-
if (callee.type === AST.Identifier) {
43+
if (Check.isIdentifier(callee)) {
4444
return callee.name;
4545
}
46-
if (callee.type === AST.MemberExpression && !callee.computed && callee.property.type === AST.Identifier) {
46+
if (callee.type === AST.MemberExpression && !callee.computed && Check.isIdentifier(callee.property)) {
4747
return callee.property.name;
4848
}
4949
return null;
@@ -73,7 +73,7 @@ export function getInnermostCall(node: TSESTree.CallExpression): TSESTree.CallEx
7373
*/
7474
export function getPropertyName(property: TSESTree.Property, effort: "min" | "max" = "min"): string | null {
7575
const key = unwrap(property.key);
76-
if (key.type === AST.Identifier && !property.computed) return key.name;
76+
if (Check.isIdentifier(key) && !property.computed) return key.name;
7777
if (effort === "min") return null;
7878
if (key.type === AST.Literal && typeof key.value === "string") return key.value;
7979
if (key.type === AST.TemplateLiteral && key.expressions.length === 0) {
@@ -123,9 +123,9 @@ export function getIdentifierAt(node: TSESTree.Expression | TSESTree.PrivateIden
123123
let current: TSESTree.Node = unwrap(node);
124124
while (current.type === AST.MemberExpression) {
125125
const property = unwrap(current.property);
126-
identifiers.unshift(property.type === AST.Identifier ? property : null);
126+
identifiers.unshift(Check.isIdentifier(property) ? property : null);
127127
current = unwrap(current.object);
128128
}
129-
identifiers.unshift(current.type === AST.Identifier ? current : null);
129+
identifiers.unshift(Check.isIdentifier(current) ? current : null);
130130
return identifiers.at(position) ?? null;
131131
}

packages/core/src/class-component.test.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,19 @@ describe("isThisSetStateCall", () => {
208208
}, true);
209209
expect(result).toBe(false);
210210
});
211+
212+
it("should return true when this is wrapped in TSAsExpression", () => {
213+
const code = "(this as any).setState({})";
214+
let result = false;
215+
simpleTraverse(parseCode(code).ast, {
216+
enter(node) {
217+
if (node.type === AST.CallExpression) {
218+
result = isThisSetStateCall(node);
219+
}
220+
},
221+
}, true);
222+
expect(result).toBe(true);
223+
});
211224
});
212225

213226
describe("isAssignmentToThisState", () => {
@@ -288,4 +301,43 @@ describe("isAssignmentToThisState", () => {
288301
}, true);
289302
expect(result).toBe(false);
290303
});
304+
305+
it("should return true for (this as any).state = {}", () => {
306+
const code = "(this as any).state = {}";
307+
let result = false;
308+
simpleTraverse(parseCode(code).ast, {
309+
enter(node) {
310+
if (node.type === AST.AssignmentExpression) {
311+
result = isAssignmentToThisState(node);
312+
}
313+
},
314+
}, true);
315+
expect(result).toBe(true);
316+
});
317+
318+
it("should return true for (this as any).state.foo = 'baz'", () => {
319+
const code = "(this as any).state.foo = 'baz'";
320+
let result = false;
321+
simpleTraverse(parseCode(code).ast, {
322+
enter(node) {
323+
if (node.type === AST.AssignmentExpression) {
324+
result = isAssignmentToThisState(node);
325+
}
326+
},
327+
}, true);
328+
expect(result).toBe(true);
329+
});
330+
331+
it("should return true for this!.state = {}", () => {
332+
const code = "this!.state = {}";
333+
let result = false;
334+
simpleTraverse(parseCode(code).ast, {
335+
enter(node) {
336+
if (node.type === AST.AssignmentExpression) {
337+
result = isAssignmentToThisState(node);
338+
}
339+
},
340+
}, true);
341+
expect(result).toBe(true);
342+
});
291343
});

packages/core/src/class-component.ts

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ export function isClassComponent(node: TSESTree.Node): node is TSESTreeClass {
3737
if ("superClass" in node && node.superClass != null) {
3838
const re = /^(?:Pure)?Component$/u;
3939
switch (true) {
40-
case node.superClass.type === AST.Identifier:
40+
case Check.isIdentifier(node.superClass):
4141
return re.test(node.superClass.name);
4242
case node.superClass.type === AST.MemberExpression
43-
&& node.superClass.property.type === AST.Identifier:
43+
&& Check.isIdentifier(node.superClass.property):
4444
return re.test(node.superClass.property.name);
4545
}
4646
}
@@ -57,10 +57,10 @@ export function isPureComponent(node: TSESTree.Node) {
5757
if ("superClass" in node && node.superClass != null) {
5858
const re = /^PureComponent$/u;
5959
switch (true) {
60-
case node.superClass.type === AST.Identifier:
60+
case Check.isIdentifier(node.superClass):
6161
return re.test(node.superClass.name);
6262
case node.superClass.type === AST.MemberExpression
63-
&& node.superClass.property.type === AST.Identifier:
63+
&& Check.isIdentifier(node.superClass.property):
6464
return re.test(node.superClass.property.name);
6565
}
6666
}
@@ -75,7 +75,7 @@ function createLifecycleChecker(methodName: string, isStatic = false) {
7575
return (node: TSESTree.Node): node is TSESTreeMethodOrPropertyDefinition => (
7676
Check.isPropertyOrMethod(node)
7777
&& node.static === isStatic
78-
&& node.key.type === AST.Identifier
78+
&& Check.isIdentifier(node.key)
7979
&& node.key.name === methodName
8080
);
8181
}
@@ -110,7 +110,6 @@ export const isUnsafeComponentWillMount = createLifecycleChecker("UNSAFE_compone
110110
export const isUnsafeComponentWillReceiveProps = createLifecycleChecker("UNSAFE_componentWillReceiveProps");
111111
/** @deprecated Class components are legacy. */
112112
export const isUnsafeComponentWillUpdate = createLifecycleChecker("UNSAFE_componentWillUpdate");
113-
114113
/** @deprecated Class components are legacy. */
115114
export const isGetDefaultProps = createLifecycleChecker("getDefaultProps", true);
116115
/** @deprecated Class components are legacy. */
@@ -130,7 +129,7 @@ export const isGetDerivedStateFromError = createLifecycleChecker("getDerivedStat
130129
*/
131130
export function isRenderMethodLike(node: TSESTree.Node): node is TSESTreeMethodOrPropertyDefinition {
132131
return Check.isPropertyOrMethod(node)
133-
&& node.key.type === AST.Identifier
132+
&& Check.isIdentifier(node.key)
134133
&& node.key.name.startsWith("render")
135134
&& Check.isOneOf([AST.ClassDeclaration, AST.ClassExpression])(node.parent.parent);
136135
}
@@ -144,9 +143,8 @@ export function isRenderMethodCallback(node: TSESTreeFunction) {
144143
const parent = node.parent;
145144
const grandparent = parent.parent;
146145
const greatGrandparent = grandparent?.parent;
147-
return greatGrandparent != null
148-
&& isRenderMethodLike(parent)
149-
&& isClassComponent(greatGrandparent);
146+
if (greatGrandparent == null) return false;
147+
return isRenderMethodLike(parent) && isClassComponent(greatGrandparent);
150148
}
151149

152150
// #endregion
@@ -163,7 +161,7 @@ export function isThisSetStateCall(node: TSESTree.CallExpression) {
163161
const callee = Extract.unwrap(node.callee);
164162
return (
165163
callee.type === AST.MemberExpression
166-
&& callee.object.type === AST.ThisExpression
164+
&& Extract.unwrap(callee.object).type === AST.ThisExpression
167165
&& Extract.getCalleeName(node) === "setState"
168166
);
169167
}
@@ -178,11 +176,12 @@ export function isAssignmentToThisState(node: TSESTree.AssignmentExpression) {
178176
const { left } = node;
179177
let current: TSESTree.Node = Extract.unwrap(left);
180178
while (current.type === AST.MemberExpression) {
181-
const { object, property } = current;
182-
if (object.type === AST.ThisExpression && property.type === AST.Identifier && property.name === "state") {
179+
const object = Extract.unwrap(current.object);
180+
const property = current.property;
181+
if (object.type === AST.ThisExpression && Check.isIdentifier(property, "state")) {
183182
return true;
184183
}
185-
current = Extract.unwrap(object);
184+
current = object;
186185
}
187186
return false;
188187
}

packages/core/src/function-component.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ function createMockContext(): RuleContext {
2424
return {
2525
sourceCode: {
2626
getText: (node: TSESTree.Node) => {
27-
if (node.type === AST.Identifier) {
27+
if (Check.isIdentifier(node)) {
2828
return node.name;
2929
}
3030
return "";
@@ -180,7 +180,7 @@ describe("getFunctionComponentId", () => {
180180
if (Check.isFunction(node) && node.type === expectedType) {
181181
const id = getFunctionComponentId(context, node);
182182
expect(id).not.toBeNull();
183-
if (id?.type === AST.Identifier) {
183+
if (id != null && Check.isIdentifier(id)) {
184184
expect(id.name).toBe(expectedName);
185185
}
186186
found = true;

packages/core/src/function-component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,10 +179,10 @@ export function isFunctionComponentNameLoose(name: string) {
179179
export function isFunctionWithLooseComponentName(context: RuleContext, fn: TSESTreeFunction, allowNone = false) {
180180
const id = getFunctionComponentId(context, fn);
181181
if (id == null) return allowNone;
182-
if (id.type === AST.Identifier) {
182+
if (Check.isIdentifier(id)) {
183183
return isFunctionComponentNameLoose(id.name);
184184
}
185-
if (id.type === AST.MemberExpression && id.property.type === AST.Identifier) {
185+
if (id.type === AST.MemberExpression && Check.isIdentifier(id.property)) {
186186
return isFunctionComponentNameLoose(id.property.name);
187187
}
188188
return false;

packages/core/src/function.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ describe("getFunctionId", () => {
9595
if (Check.isFunction(node) && node.type === expectedType) {
9696
const id = getFunctionId(node);
9797
expect(id).not.toBeNull();
98-
if (id?.type === AST.Identifier) {
98+
if (id != null && Check.isIdentifier(id)) {
9999
expect(id.name).toBe(expectedName);
100100
}
101101
found = true;

packages/core/src/function.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ export function isFunctionHasCallInInitPath(callName: string, initPath: Function
230230
const callee = Extract.unwrap(node.callee);
231231

232232
// Check direct function calls: memo(...)
233-
if (callee.type === AST.Identifier) {
233+
if (Check.isIdentifier(callee)) {
234234
return callee.name === callName;
235235
}
236236

packages/jsx/src/attribute-find.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type TSESTreeJSXAttributeLike, Traverse } from "@eslint-react/ast";
1+
import { Check, type TSESTreeJSXAttributeLike, Traverse } from "@eslint-react/ast";
22
import type { RuleContext } from "@eslint-react/eslint";
33
import { resolve } from "@eslint-react/var";
44
import { AST_NODE_TYPES as AST, type TSESTree } from "@typescript-eslint/types";
@@ -75,11 +75,11 @@ export function findSpreadProperty(
7575
seen: Set<TSESTree.Node> = new Set(),
7676
): TSESTree.Property | undefined {
7777
let objectExpression: TSESTree.ObjectExpression | undefined;
78-
if (argument.type === AST.Identifier) {
78+
if (Check.isIdentifier(argument)) {
7979
// Follow identifier aliases (`const b = a`) until a non-identifier
8080
// initializer is reached, mirroring `getStaticValue`'s identifier tracking.
8181
let initNode: TSESTree.Node | null = resolve(context, argument);
82-
while (initNode != null && initNode.type === AST.Identifier && !seen.has(initNode)) {
82+
while (initNode != null && Check.isIdentifier(initNode) && !seen.has(initNode)) {
8383
seen.add(initNode);
8484
initNode = resolve(context, initNode);
8585
}
@@ -103,7 +103,7 @@ export function findSpreadProperty(
103103
if (getStaticValue(key, keyScope)?.value === name) return property;
104104
continue;
105105
}
106-
if (key.type === AST.Identifier && key.name === name) return property;
106+
if (Check.isIdentifier(key, name)) return property;
107107
if (key.type === AST.Literal && key.value === name) return property;
108108
continue;
109109
}

packages/var/src/get-require-expression-arguments.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Extract } from "@eslint-react/ast";
1+
import { Check, Extract } from "@eslint-react/ast";
22
import { AST_NODE_TYPES as AST, type TSESTree } from "@typescript-eslint/types";
33

44
/**
@@ -11,7 +11,7 @@ export function getRequireExpressionArguments(node: TSESTree.Node) {
1111
const unwrapped = Extract.unwrap(node);
1212
if (unwrapped.type === AST.CallExpression) {
1313
const callee = Extract.unwrap(unwrapped.callee);
14-
if (callee.type === AST.Identifier && callee.name === "require") {
14+
if (Check.isIdentifier(callee, "require")) {
1515
return unwrapped.arguments;
1616
}
1717
}

0 commit comments

Comments
 (0)