Skip to content

Commit 76e3b16

Browse files
Rel1cxCopilot
andauthored
refactor: replace construction detection with object type detection and remove unused code (#1292)
Signed-off-by: REL1CX <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent 4cab622 commit 76e3b16

File tree

5 files changed

+134
-106
lines changed

5 files changed

+134
-106
lines changed

packages/plugins/eslint-plugin-react-x/src/rules/no-unstable-context-value.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { getJsxElementType, isReactHookCall, useComponentCollector } from "@esli
33
import { getOrElseUpdate } from "@eslint-react/eff";
44
import type { RuleContext, RuleFeature } from "@eslint-react/shared";
55
import { getSettingsFromContext } from "@eslint-react/shared";
6-
import { type Construction, getConstruction } from "@eslint-react/var";
6+
import { type ObjectType, getObjectType } from "@eslint-react/var";
77
import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
88
import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
99
import { compare } from "compare-versions";
@@ -39,7 +39,7 @@ export function create(context: RuleContext<MessageID, []>): RuleListener {
3939
const { version } = getSettingsFromContext(context);
4040
const isReact18OrBelow = compare(version, "19.0.0", "<");
4141
const { ctx, listeners } = useComponentCollector(context);
42-
const constructions = new WeakMap<AST.TSESTreeFunction, Construction[]>();
42+
const constructions = new WeakMap<AST.TSESTreeFunction, ObjectType[]>();
4343

4444
return {
4545
...listeners,
@@ -61,7 +61,7 @@ export function create(context: RuleContext<MessageID, []>): RuleListener {
6161
if (value?.type !== T.JSXExpressionContainer) return;
6262
const valueExpression = value.expression;
6363
const initialScope = context.sourceCode.getScope(valueExpression);
64-
const construction = getConstruction(valueExpression, initialScope);
64+
const construction = getObjectType(valueExpression, initialScope);
6565
if (construction == null) return;
6666
if (isReactHookCall(construction.node)) {
6767
return;
@@ -73,7 +73,7 @@ export function create(context: RuleContext<MessageID, []>): RuleListener {
7373
for (const { node: component } of components) {
7474
for (const construction of constructions.get(component) ?? []) {
7575
const { kind, node: constructionNode } = construction;
76-
const suggestion = kind.startsWith("Function")
76+
const suggestion = kind === "function"
7777
? "Consider wrapping it in a useCallback hook."
7878
: "Consider wrapping it in a useMemo hook.";
7979
context.report({

packages/plugins/eslint-plugin-react-x/src/rules/no-unstable-default-props.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as AST from "@eslint-react/ast";
22
import { isReactHookCall, useComponentCollector } from "@eslint-react/core";
33
import { getOrElseUpdate } from "@eslint-react/eff";
44
import { type RuleContext, type RuleFeature } from "@eslint-react/shared";
5-
import { ConstructionDetectionHint, getConstruction } from "@eslint-react/var";
5+
import { getObjectType } from "@eslint-react/var";
66
import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
77
import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
88
import type { CamelCase } from "string-ts";
@@ -72,10 +72,9 @@ export function create(context: RuleContext<MessageID, []>): RuleListener {
7272
const { value } = prop;
7373
const { right } = value;
7474
const initialScope = context.sourceCode.getScope(value);
75-
const construction = getConstruction(
75+
const construction = getObjectType(
7676
value,
7777
initialScope,
78-
ConstructionDetectionHint.StrictCallExpression,
7978
);
8079
if (construction == null) {
8180
continue;

packages/utilities/var/src/construction-detection.ts

Lines changed: 0 additions & 98 deletions
This file was deleted.
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import { unit } from "@eslint-react/eff";
2+
import type { Scope } from "@typescript-eslint/scope-manager";
3+
import type { TSESTree } from "@typescript-eslint/types";
4+
5+
import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
6+
import { getVariableDefinitionNode } from "./get-variable-definition-node";
7+
8+
export type ObjectType =
9+
| {
10+
kind: "jsx";
11+
node:
12+
| TSESTree.JSXElement
13+
| TSESTree.JSXFragment;
14+
}
15+
| {
16+
kind: "array";
17+
node: TSESTree.ArrayExpression;
18+
}
19+
| {
20+
kind: "plain";
21+
node: TSESTree.ObjectExpression;
22+
}
23+
| {
24+
kind: "class";
25+
node: TSESTree.ClassExpression;
26+
}
27+
| {
28+
kind: "instance";
29+
node:
30+
| TSESTree.NewExpression
31+
| TSESTree.ThisExpression;
32+
}
33+
| {
34+
kind: "function";
35+
node:
36+
| TSESTree.FunctionDeclaration
37+
| TSESTree.FunctionExpression
38+
| TSESTree.ArrowFunctionExpression;
39+
}
40+
| {
41+
kind: "regexp";
42+
node: TSESTree.RegExpLiteral;
43+
}
44+
| {
45+
kind: "unknown";
46+
node: TSESTree.Node;
47+
// Reason for why the type is unknown
48+
reason: "call-expression" | "unsupported-node";
49+
};
50+
51+
/**
52+
* Detects the ObjectType of a given node
53+
* @param node The node to check
54+
* @param initialScope The initial scope to check for variable declarations
55+
* @returns The ObjectType of the node, or undefined if not detected
56+
*/
57+
export function getObjectType(
58+
node: TSESTree.Node | unit,
59+
initialScope: Scope,
60+
): ObjectType | unit {
61+
if (node == null) return unit;
62+
switch (node.type) {
63+
case T.JSXElement:
64+
case T.JSXFragment:
65+
return { kind: "jsx", node } as const;
66+
case T.ArrayExpression:
67+
return { kind: "array", node } as const;
68+
case T.ObjectExpression:
69+
return { kind: "plain", node } as const;
70+
case T.ClassExpression:
71+
return { kind: "class", node } as const;
72+
case T.NewExpression:
73+
case T.ThisExpression:
74+
return { kind: "instance", node } as const;
75+
case T.FunctionDeclaration:
76+
case T.FunctionExpression:
77+
case T.ArrowFunctionExpression:
78+
return { kind: "function", node } as const;
79+
case T.Literal: {
80+
if ("regex" in node) {
81+
return { kind: "regexp", node } as const;
82+
}
83+
return unit;
84+
}
85+
case T.Identifier: {
86+
if (!("name" in node) || typeof node.name !== "string") {
87+
return unit;
88+
}
89+
const variable = initialScope.set.get(node.name);
90+
const variableNode = getVariableDefinitionNode(variable, -1);
91+
return getObjectType(variableNode, initialScope);
92+
}
93+
case T.MemberExpression: {
94+
if (!("object" in node)) return unit;
95+
return getObjectType(node.object, initialScope);
96+
}
97+
case T.AssignmentExpression:
98+
case T.AssignmentPattern: {
99+
if (!("right" in node)) return unit;
100+
return getObjectType(node.right, initialScope);
101+
}
102+
case T.LogicalExpression: {
103+
return getObjectType(node.right, initialScope);
104+
}
105+
case T.ConditionalExpression: {
106+
return getObjectType(node.consequent, initialScope) ?? getObjectType(node.alternate, initialScope);
107+
}
108+
case T.SequenceExpression: {
109+
if (node.expressions.length === 0) {
110+
return unit;
111+
}
112+
return getObjectType(
113+
node.expressions[node.expressions.length - 1],
114+
initialScope,
115+
);
116+
}
117+
case T.CallExpression: {
118+
return { kind: "unknown", node, reason: "call-expression" } as const;
119+
}
120+
default: {
121+
if (!("expression" in node) || typeof node.expression !== "object") {
122+
return unit;
123+
}
124+
return getObjectType(node.expression, initialScope);
125+
}
126+
}
127+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
export * from "./construction-detection";
21
export * from "./find-assignment-target";
32
export * from "./find-property";
43
export * from "./get-child-scopes";
4+
export * from "./get-object-type";
55
export * from "./get-variable-definition-node";
66
export * from "./get-variables-from-scope";
77
export * from "./is-node-value-equal";

0 commit comments

Comments
 (0)