Skip to content

Commit 605f99f

Browse files
committed
refactor: improve code eeadability
1 parent 9bdbabd commit 605f99f

24 files changed

+311
-472
lines changed

packages/core/docs/functions/useComponentCollector.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@
2828
2929
#### ctx.getCurrentFunction()
3030

31-
> **getCurrentFunction**: () => `Option`\<\[`string`, `TSESTreeFunction`, `boolean`, `CallExpression`[]\]\>
31+
> **getCurrentFunction**: () => `Option`\<\{ `hookCalls`: `CallExpression`[]; `isComponent`: `boolean`; `key`: `string`; `node`: `TSESTreeFunction`; \}\>
3232
3333
##### Returns
3434

35-
`Option`\<\[`string`, `TSESTreeFunction`, `boolean`, `CallExpression`[]\]\>
35+
`Option`\<\{ `hookCalls`: `CallExpression`[]; `isComponent`: `boolean`; `key`: `string`; `node`: `TSESTreeFunction`; \}\>
3636

3737
#### ctx.getAllComponents()
3838

@@ -50,7 +50,7 @@
5050

5151
##### Returns
5252

53-
\[`string`, `TSESTreeFunction`, `boolean`, `CallExpression`[]\][]
53+
`object`[]
5454

5555
### listeners
5656

@@ -72,11 +72,11 @@
7272

7373
#### listeners.:function\[type\]:exit()
7474

75-
> `readonly` **:function\[type\]:exit**: () => `undefined` \| \[`string`, `TSESTreeFunction`, `boolean`, `CallExpression`[]\] = `onFunctionExit`
75+
> `readonly` **:function\[type\]:exit**: () => `undefined` \| \{ `hookCalls`: `CallExpression`[]; `isComponent`: `boolean`; `key`: `string`; `node`: `TSESTreeFunction`; \} = `onFunctionExit`
7676
7777
##### Returns
7878

79-
`undefined` \| \[`string`, `TSESTreeFunction`, `boolean`, `CallExpression`[]\]
79+
`undefined` \| \{ `hookCalls`: `CallExpression`[]; `isComponent`: `boolean`; `key`: `string`; `node`: `TSESTreeFunction`; \}
8080

8181
#### listeners.ArrowFunctionExpression\[type\]\[body.type!='BlockStatement'\]()
8282

@@ -90,7 +90,7 @@
9090

9191
###### node
9292

93-
`Node`
93+
`AssignmentExpression` & `object`
9494

9595
##### Returns
9696

packages/core/src/component/component-collector.ts

Lines changed: 82 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -66,25 +66,25 @@ export function useComponentCollector(
6666
) {
6767
const jsxCtx = { getScope: (node: TSESTree.Node) => context.sourceCode.getScope(node) } as const;
6868
const components = new Map<string, ERFunctionComponent>();
69-
const functionStack: [
70-
key: string,
71-
node: AST.TSESTreeFunction,
72-
isComponent: boolean,
73-
hookCalls: TSESTree.CallExpression[],
74-
][] = [];
69+
const functionStack: {
70+
key: string;
71+
node: AST.TSESTreeFunction;
72+
hookCalls: TSESTree.CallExpression[];
73+
isComponent: boolean;
74+
}[] = [];
7575
const getCurrentFunction = () => O.fromNullable(functionStack.at(-1));
7676
const onFunctionEnter = (node: AST.TSESTreeFunction) => {
7777
const key = getId();
78-
functionStack.push([key, node, false, []]);
78+
functionStack.push({ key, node, hookCalls: [], isComponent: false });
7979
};
8080
const onFunctionExit = () => {
81-
const [key, fn, isComponent] = functionStack.at(-1) ?? [];
82-
if (!key || !fn || !isComponent) return functionStack.pop();
83-
const shouldDrop = AST.getNestedReturnStatements(fn.body)
81+
const { key, node, isComponent } = functionStack.at(-1) ?? {};
82+
if (!key || !node || !isComponent) return functionStack.pop();
83+
const shouldDrop = AST.getNestedReturnStatements(node.body)
8484
.slice()
8585
.reverse()
8686
.some(r => {
87-
return context.sourceCode.getScope(r).block === fn
87+
return context.sourceCode.getScope(r).block === node
8888
&& r.argument !== null
8989
&& !JSX.isJSXValue(r.argument, jsxCtx, hint);
9090
});
@@ -106,83 +106,90 @@ export function useComponentCollector(
106106
":function[type]": onFunctionEnter,
107107
":function[type]:exit": onFunctionExit,
108108
"ArrowFunctionExpression[type][body.type!='BlockStatement']"() {
109-
const maybeCurrentFn = getCurrentFunction();
110-
if (O.isNone(maybeCurrentFn)) return;
111-
const [_key, currentFn, _isComponent, hookCalls] = maybeCurrentFn.value;
112-
const { body } = currentFn;
113-
const isComponent = hasNoneOrValidComponentName(currentFn, context)
114-
&& JSX.isJSXValue(body, jsxCtx, hint)
115-
&& hasValidHierarchy(currentFn, context, hint);
116-
if (!isComponent) return;
117-
const initPath = AST.getFunctionInitPath(currentFn);
118-
const id = getFunctionComponentIdentifier(currentFn, context);
119-
const name = O.flatMapNullable(id, getComponentNameFromIdentifier);
120-
const key = getId();
121-
components.set(key, {
122-
_: key,
123-
id,
124-
kind: "function",
125-
name,
126-
node: currentFn,
127-
displayName: O.none(),
128-
flag: getComponentFlag(initPath),
129-
hint,
130-
hookCalls,
131-
initPath,
109+
O.match(getCurrentFunction(), {
110+
onNone() {},
111+
onSome(a) {
112+
const { body } = a.node;
113+
const isComponent = hasNoneOrValidComponentName(a.node, context)
114+
&& JSX.isJSXValue(body, jsxCtx, hint)
115+
&& hasValidHierarchy(a.node, context, hint);
116+
if (!isComponent) return;
117+
const initPath = AST.getFunctionInitPath(a.node);
118+
const id = getFunctionComponentIdentifier(a.node, context);
119+
const name = O.flatMapNullable(id, getComponentNameFromIdentifier);
120+
const key = getId();
121+
components.set(key, {
122+
_: key,
123+
id,
124+
kind: "function",
125+
name,
126+
node: a.node,
127+
displayName: O.none(),
128+
flag: getComponentFlag(initPath),
129+
hint,
130+
hookCalls: a.hookCalls,
131+
initPath,
132+
});
133+
},
132134
});
133135
},
134136
"AssignmentExpression[type][operator='='][left.type='MemberExpression'][left.property.name='displayName']"(
135-
node: TSESTree.Node,
137+
node: TSESTree.AssignmentExpression & { left: TSESTree.MemberExpression },
136138
) {
137-
if (node.type !== AST_NODE_TYPES.AssignmentExpression) return;
138139
const { left, right } = node;
139-
if (left.type !== AST_NODE_TYPES.MemberExpression) return;
140-
const maybeComponentName = match(left.object)
140+
const componentName = match(left.object)
141141
.with({ type: AST_NODE_TYPES.Identifier }, n => O.some(n.name))
142142
.otherwise(O.none);
143-
if (O.isNone(maybeComponentName)) return;
144-
const component = Array
145-
.from(components.values())
146-
.findLast(({ name }) => O.exists(name, n => n === maybeComponentName.value));
147-
if (!component) return;
148-
components.set(component._, {
149-
...component,
150-
displayName: O.some(right),
143+
O.match(componentName, {
144+
onNone() {},
145+
onSome(a) {
146+
const component = Array
147+
.from(components.values())
148+
.findLast(({ name }) => O.exists(name, n => n === a));
149+
if (!component) return;
150+
components.set(component._, {
151+
...component,
152+
displayName: O.some(right),
153+
});
154+
},
151155
});
152156
},
153157
"CallExpression[type]:exit"(node: TSESTree.CallExpression) {
154158
if (!isReactHookCall(node)) return;
155-
const maybeCurrentFn = getCurrentFunction();
156-
if (O.isNone(maybeCurrentFn)) return;
157-
const [key, currentFn, isComponent, hookCalls] = maybeCurrentFn.value;
158-
functionStack.pop();
159-
functionStack.push([key, currentFn, isComponent, [...hookCalls, node]]);
159+
O.match(getCurrentFunction(), {
160+
onNone() {},
161+
onSome(a) {
162+
functionStack.pop();
163+
functionStack.push({ ...a, hookCalls: [...a.hookCalls, node] });
164+
},
165+
});
160166
},
161167
"ReturnStatement[type]"(node: TSESTree.ReturnStatement) {
162-
const maybeCurrentFn = getCurrentFunction();
163-
if (O.isNone(maybeCurrentFn)) return;
164-
const [key, currentFn, isKnown, hookCalls] = maybeCurrentFn.value;
165-
if (isKnown) return;
166-
const isComponent = hasNoneOrValidComponentName(currentFn, context)
167-
&& JSX.isJSXValue(node.argument, jsxCtx, hint)
168-
&& hasValidHierarchy(currentFn, context, hint);
169-
if (!isComponent) return;
170-
functionStack.pop();
171-
functionStack.push([key, currentFn, true, []]);
172-
const initPath = AST.getFunctionInitPath(currentFn);
173-
const id = getFunctionComponentIdentifier(currentFn, context);
174-
const name = O.flatMapNullable(id, getComponentNameFromIdentifier);
175-
components.set(key, {
176-
_: key,
177-
id,
178-
kind: "function",
179-
name,
180-
node: currentFn,
181-
displayName: O.none(),
182-
flag: getComponentFlag(initPath),
183-
hint,
184-
hookCalls,
185-
initPath,
168+
O.match(getCurrentFunction(), {
169+
onNone() {},
170+
onSome(a) {
171+
const isComponent = hasNoneOrValidComponentName(a.node, context)
172+
&& JSX.isJSXValue(node.argument, jsxCtx, hint)
173+
&& hasValidHierarchy(a.node, context, hint);
174+
if (!isComponent) return;
175+
functionStack.pop();
176+
functionStack.push({ ...a, isComponent });
177+
const initPath = AST.getFunctionInitPath(a.node);
178+
const id = getFunctionComponentIdentifier(a.node, context);
179+
const name = O.flatMapNullable(id, getComponentNameFromIdentifier);
180+
components.set(a.key, {
181+
_: a.key,
182+
id,
183+
kind: "function",
184+
name,
185+
node: a.node,
186+
displayName: O.none(),
187+
flag: getComponentFlag(initPath),
188+
hint,
189+
hookCalls: a.hookCalls,
190+
initPath,
191+
});
192+
},
186193
});
187194
},
188195
} as const satisfies ESLintUtils.RuleListener;

packages/plugins/eslint-plugin-react-dom/src/rules/no-dangerously-set-innerhtml.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,16 @@ export default createRule<[], MessageID>({
3131
return {
3232
JSXElement(node) {
3333
const initialScope = context.sourceCode.getScope(node);
34-
const maybeDanger = JSX.findPropInAttributes(node.openingElement.attributes, initialScope)(
35-
"dangerouslySetInnerHTML",
36-
);
37-
if (O.isSome(maybeDanger)) {
38-
context.report({
39-
messageId: "noDangerouslySetInnerhtml",
40-
node,
41-
});
42-
}
34+
const prop = JSX.findPropInAttributes(node.openingElement.attributes, initialScope)("dangerouslySetInnerHTML");
35+
O.match(prop, {
36+
onNone() {},
37+
onSome(a) {
38+
context.report({
39+
messageId: "noDangerouslySetInnerhtml",
40+
node: a,
41+
});
42+
},
43+
});
4344
},
4445
};
4546
},

packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-button-type.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,14 @@ export default createRule<[], MessageID>({
3535
if (elementName !== "button") return;
3636
const { attributes } = node.openingElement;
3737
const initialScope = context.sourceCode.getScope(node);
38-
const maybeTypeAttribute = JSX.findPropInAttributes(attributes, initialScope)("type");
39-
if (O.isSome(maybeTypeAttribute)) return;
40-
context.report({
41-
messageId: "noMissingButtonType",
42-
node: node.openingElement,
38+
O.match(JSX.findPropInAttributes(attributes, initialScope)("type"), {
39+
onNone() {
40+
context.report({
41+
messageId: "noMissingButtonType",
42+
node: node.openingElement,
43+
});
44+
},
45+
onSome() {},
4346
});
4447
},
4548
};

packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-iframe-sandbox.ts

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -54,25 +54,26 @@ export default createRule<[], MessageID>({
5454
if (elementName !== "iframe") return;
5555
const { attributes } = node.openingElement;
5656
const initialScope = context.sourceCode.getScope(node);
57-
const maybeSandboxAttribute = JSX.findPropInAttributes(attributes, initialScope)("sandbox");
58-
if (O.isNone(maybeSandboxAttribute)) {
59-
context.report({
60-
messageId: "noMissingIframeSandbox",
61-
node: node.openingElement,
62-
});
63-
return;
64-
}
65-
const sandboxAttribute = maybeSandboxAttribute.value;
66-
const hasValidSandbox = F.pipe(
67-
JSX.getPropValue(sandboxAttribute, context.sourceCode.getScope(sandboxAttribute)),
68-
O.filter(isString),
69-
O.map((value) => value.split(" ")),
70-
O.exists((values) => values.every((value) => validTypes.some((validType) => validType === value))),
71-
);
72-
if (hasValidSandbox) return;
73-
context.report({
74-
messageId: "noMissingIframeSandbox",
75-
node: sandboxAttribute,
57+
O.match(JSX.findPropInAttributes(attributes, initialScope)("sandbox"), {
58+
onNone() {
59+
context.report({
60+
messageId: "noMissingIframeSandbox",
61+
node: node.openingElement,
62+
});
63+
},
64+
onSome(a) {
65+
const hasValidSandbox = F.pipe(
66+
JSX.getPropValue(a, context.sourceCode.getScope(a)),
67+
O.filter(isString),
68+
O.map((value) => value.split(" ")),
69+
O.exists((values) => values.every((value) => validTypes.some((validType) => validType === value))),
70+
);
71+
if (hasValidSandbox) return;
72+
context.report({
73+
messageId: "noMissingIframeSandbox",
74+
node: a,
75+
});
76+
},
7677
});
7778
},
7879
};

packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-iframe-sandbox.ts

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -41,26 +41,29 @@ export default createRule<[], MessageID>({
4141
if (elementName !== "iframe") return;
4242
const { attributes } = node.openingElement;
4343
const initialScope = context.sourceCode.getScope(node);
44-
const maybeSandboxAttribute = JSX.findPropInAttributes(attributes, initialScope)("sandbox");
45-
if (O.isNone(maybeSandboxAttribute)) return;
46-
const isSafeSandboxValue = !F.pipe(
47-
JSX.getPropValue(maybeSandboxAttribute.value, context.sourceCode.getScope(maybeSandboxAttribute.value)),
48-
O.flatMapNullable(v =>
49-
match(v)
50-
.with(P.string, F.identity)
51-
.with({ sandbox: P.string }, ({ sandbox }) => sandbox)
52-
.otherwise(F.constNull)
53-
),
54-
O.filter(isString),
55-
O.map((value) => value.split(" ")),
56-
O.exists(values =>
57-
unsafeCombinations.some(combinations => combinations.every(unsafeValue => values.includes(unsafeValue)))
58-
),
59-
);
60-
if (isSafeSandboxValue) return;
61-
context.report({
62-
messageId: "noUnsafeIframeSandbox",
63-
node: maybeSandboxAttribute.value,
44+
O.match(JSX.findPropInAttributes(attributes, initialScope)("sandbox"), {
45+
onNone() {},
46+
onSome(a) {
47+
const isSafeSandboxValue = !F.pipe(
48+
JSX.getPropValue(a, context.sourceCode.getScope(a)),
49+
O.flatMapNullable(v =>
50+
match(v)
51+
.with(P.string, F.identity)
52+
.with({ sandbox: P.string }, ({ sandbox }) => sandbox)
53+
.otherwise(F.constNull)
54+
),
55+
O.filter(isString),
56+
O.map((value) => value.split(" ")),
57+
O.exists(values =>
58+
unsafeCombinations.some(combinations => combinations.every(unsafeValue => values.includes(unsafeValue)))
59+
),
60+
);
61+
if (isSafeSandboxValue) return;
62+
context.report({
63+
messageId: "noUnsafeIframeSandbox",
64+
node: a,
65+
});
66+
},
6467
});
6568
},
6669
};

0 commit comments

Comments
 (0)