Skip to content

Commit 7f55dee

Browse files
committed
refactor(utilities/jsx): improve the performance of 'isJSXValue'
1 parent e3bb72d commit 7f55dee

File tree

8 files changed

+77
-61
lines changed

8 files changed

+77
-61
lines changed

packages/core/docs/variables/ERComponentHint.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ hints for component collector
1616

1717
> `readonly` **None**: `0n`
1818
19+
### SkipBigIntLiteral
20+
21+
> `readonly` **SkipBigIntLiteral**: `bigint`
22+
1923
### SkipBooleanLiteral
2024

2125
> `readonly` **SkipBooleanLiteral**: `bigint`
@@ -64,9 +68,9 @@ hints for component collector
6468

6569
> `readonly` **SkipStringLiteral**: `bigint`
6670
67-
### SkipUndefinedLiteral
71+
### SkipUndefined
6872

69-
> `readonly` **SkipUndefinedLiteral**: `bigint`
73+
> `readonly` **SkipUndefined**: `bigint`
7074
7175
### StrictArray
7276

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export const DEFAULT_COMPONENT_HINT = 0n
2929
| ERComponentHint.SkipMapCallback
3030
| ERComponentHint.SkipNumberLiteral
3131
| ERComponentHint.SkipStringLiteral
32-
| ERComponentHint.SkipUndefinedLiteral
32+
| ERComponentHint.SkipUndefined
3333
| ERComponentHint.SkipEmptyArray
3434
| ERComponentHint.StrictArray
3535
| ERComponentHint.StrictConditional

packages/core/src/render-prop/is.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export function isRenderFunctionLoose(node: AST.TSESTreeFunction, context: RuleC
3131
getScope: (node: TSESTree.Node) => context.sourceCode.getScope(node),
3232
},
3333
JSX.JSXValueHint.SkipNullLiteral
34-
| JSX.JSXValueHint.SkipUndefinedLiteral
34+
| JSX.JSXValueHint.SkipUndefined
3535
| JSX.JSXValueHint.StrictLogical
3636
| JSX.JSXValueHint.StrictConditional,
3737
);

packages/plugins/eslint-plugin-react-x/src/rules/no-nested-components.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export default createRule<[], MessageID>({
4444
create(context) {
4545
const hint = ERComponentHint.SkipMapCallback
4646
| ERComponentHint.SkipNullLiteral
47-
| ERComponentHint.SkipUndefinedLiteral
47+
| ERComponentHint.SkipUndefined
4848
| ERComponentHint.SkipBooleanLiteral
4949
| ERComponentHint.SkipStringLiteral
5050
| ERComponentHint.SkipNumberLiteral

packages/types/docs/functions/typeOf.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
# Function: typeOf()
88

9-
> **typeOf**(`t`): `"object"` \| `string` & `object` \| `"array"`
9+
> **typeOf**(`t`): `"object"` \| `"array"` \| `string` & `object`
1010
1111
This is an enhanced version of the typeof operator to check the type of more complex values.
1212
In this case we just mind about arrays and objects. We can add more on demand.
@@ -21,6 +21,6 @@ the value to be checked
2121

2222
## Returns
2323

24-
`"object"` \| `string` & `object` \| `"array"`
24+
`"object"` \| `"array"` \| `string` & `object`
2525

2626
the type of the value

packages/types/src/index.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
export * from "./helpers";
2-
export type * from "./rule";
3-
export type * from "./rule-feature";
4-
export type * from "./rule-name";
5-
export type * from "./rule-namespace";
1+
export * from "./rule";
2+
export * from "./rule-feature";
3+
export * from "./rule-name";
4+
export * from "./rule-namespace";
5+
export * from "./utils";
File renamed without changes.

packages/utilities/jsx/src/is-jsx-value.ts

Lines changed: 61 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import * as VAR from "@eslint-react/var";
44
import type { Scope } from "@typescript-eslint/scope-manager";
55
import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
66
import type { TSESTree } from "@typescript-eslint/utils";
7-
import { match, P } from "ts-pattern";
87

98
// type ReactNode =
109
// | ReactElement
@@ -22,21 +21,22 @@ import { match, P } from "ts-pattern";
2221
/* eslint-disable perfectionist/sort-objects */
2322
export const JSXValueHint = {
2423
None: 0n,
25-
SkipNullLiteral: 1n << 0n,
26-
SkipUndefinedLiteral: 1n << 1n,
24+
SkipUndefined: 1n << 0n,
25+
SkipNullLiteral: 1n << 1n,
2726
SkipBooleanLiteral: 1n << 2n,
2827
SkipStringLiteral: 1n << 3n,
2928
SkipNumberLiteral: 1n << 4n,
30-
SkipCreateElement: 1n << 5n,
29+
SkipBigIntLiteral: 1n << 5n,
3130
SkipEmptyArray: 1n << 6n,
32-
StrictArray: 1n << 7n,
33-
StrictLogical: 1n << 8n,
34-
StrictConditional: 1n << 9n,
31+
SkipCreateElement: 1n << 7n,
32+
StrictArray: 1n << 8n,
33+
StrictLogical: 1n << 9n,
34+
StrictConditional: 1n << 10n,
3535
} as const;
3636
/* eslint-enable perfectionist/sort-objects */
3737

3838
export const DEFAULT_JSX_VALUE_HINT = 0n
39-
| JSXValueHint.SkipUndefinedLiteral
39+
| JSXValueHint.SkipUndefined
4040
| JSXValueHint.SkipBooleanLiteral;
4141

4242
/**
@@ -52,30 +52,45 @@ export function isJSXValue(
5252
jsxCtx: { getScope: (node: TSESTree.Node) => Scope },
5353
hint: bigint = DEFAULT_JSX_VALUE_HINT,
5454
): boolean {
55-
if (!node) {
56-
return false;
57-
}
58-
return match<typeof node, boolean>(node)
59-
.with({ type: T.JSXElement }, F.constTrue)
60-
.with({ type: T.JSXFragment }, F.constTrue)
61-
.with({ type: T.JSXMemberExpression }, F.constTrue)
62-
.with({ type: T.JSXNamespacedName }, F.constTrue)
63-
.with({ type: T.Literal }, (node) => {
64-
return match(node.value)
65-
.with(null, () => !(hint & JSXValueHint.SkipNullLiteral))
66-
.with(P.boolean, () => !(hint & JSXValueHint.SkipBooleanLiteral))
67-
.with(P.string, () => !(hint & JSXValueHint.SkipStringLiteral))
68-
.with(P.number, () => !(hint & JSXValueHint.SkipNumberLiteral))
69-
.otherwise(F.constFalse);
70-
})
71-
.with({ type: T.TemplateLiteral }, () => !(hint & JSXValueHint.SkipStringLiteral))
72-
.with({ type: T.ArrayExpression }, (node) => {
55+
switch (node?.type) {
56+
case T.JSXElement:
57+
case T.JSXFragment:
58+
case T.JSXMemberExpression:
59+
case T.JSXNamespacedName: {
60+
return true;
61+
}
62+
case T.Literal: {
63+
switch (typeof node.value) {
64+
case "boolean":
65+
return !(hint & JSXValueHint.SkipBooleanLiteral);
66+
case "string":
67+
return !(hint & JSXValueHint.SkipStringLiteral);
68+
case "number":
69+
return !(hint & JSXValueHint.SkipNumberLiteral);
70+
case "bigint":
71+
return !(hint & JSXValueHint.SkipBigIntLiteral);
72+
}
73+
if (node.value === null) {
74+
return !(hint & JSXValueHint.SkipNullLiteral);
75+
}
76+
return false;
77+
}
78+
case T.TemplateLiteral: {
79+
return !(hint & JSXValueHint.SkipStringLiteral);
80+
}
81+
case T.ArrayExpression: {
7382
if (hint & JSXValueHint.StrictArray) {
7483
return node.elements.every((n) => isJSXValue(n, jsxCtx, hint));
7584
}
7685
return node.elements.some((n) => isJSXValue(n, jsxCtx, hint));
77-
})
78-
.with({ type: T.ConditionalExpression }, (node) => {
86+
}
87+
case T.LogicalExpression: {
88+
if (hint & JSXValueHint.StrictLogical) {
89+
return isJSXValue(node.left, jsxCtx, hint) && isJSXValue(node.right, jsxCtx, hint);
90+
}
91+
return isJSXValue(node.left, jsxCtx, hint) || isJSXValue(node.right, jsxCtx, hint);
92+
}
93+
case T.ConditionalExpression: {
7994
function leftHasJSX(node: TSESTree.ConditionalExpression) {
8095
if (Array.isArray(node.consequent)) {
8196
if (node.consequent.length === 0) {
@@ -95,40 +110,37 @@ export function isJSXValue(
95110
return leftHasJSX(node) && rightHasJSX(node);
96111
}
97112
return leftHasJSX(node) || rightHasJSX(node);
98-
})
99-
.with({ type: T.LogicalExpression }, (node) => {
100-
if (hint & JSXValueHint.StrictLogical) {
101-
return isJSXValue(node.left, jsxCtx, hint) && isJSXValue(node.right, jsxCtx, hint);
102-
}
103-
return isJSXValue(node.left, jsxCtx, hint) || isJSXValue(node.right, jsxCtx, hint);
104-
})
105-
.with({ type: T.SequenceExpression }, (node) => {
113+
}
114+
case T.SequenceExpression: {
106115
const exp = node.expressions.at(-1);
107116
return isJSXValue(exp, jsxCtx, hint);
108-
})
109-
.with({ type: T.CallExpression }, (node) => {
117+
}
118+
case T.CallExpression: {
110119
if (hint & JSXValueHint.SkipCreateElement) {
111120
return false;
112121
}
113-
return match(node.callee)
114-
.with({ type: T.Identifier, name: "createElement" }, F.constTrue)
115-
.with({ type: T.MemberExpression, property: { name: "createElement" } }, F.constTrue)
116-
.otherwise(F.constFalse);
117-
})
118-
.with({ type: T.Identifier }, (node) => {
122+
switch (node.callee.type) {
123+
case T.Identifier:
124+
return node.callee.name === "createElement";
125+
case T.MemberExpression:
126+
return node.callee.property.type === T.Identifier && node.callee.property.name === "createElement";
127+
}
128+
return false;
129+
}
130+
case T.Identifier: {
119131
const { name } = node;
120132
if (name === "undefined") {
121-
return !(hint & JSXValueHint.SkipUndefinedLiteral);
133+
return !(hint & JSXValueHint.SkipUndefined);
122134
}
123135
if (AST.isJSXTagNameExpression(node)) {
124136
return true;
125137
}
126-
const initialScope = jsxCtx.getScope(node);
127138
return F.pipe(
128-
VAR.findVariable(name, initialScope),
139+
VAR.findVariable(name, jsxCtx.getScope(node)),
129140
O.flatMap(VAR.getVariableNode(0)),
130141
O.exists(n => isJSXValue(n, jsxCtx, hint)),
131142
);
132-
})
133-
.otherwise(F.constFalse);
143+
}
144+
}
145+
return false;
134146
}

0 commit comments

Comments
 (0)