Skip to content

Commit a3c9848

Browse files
authored
refactor(utilities/jsx): improve the performance of 'isJSXValue' (#911)
1 parent e3bb72d commit a3c9848

File tree

10 files changed

+75
-58
lines changed

10 files changed

+75
-58
lines changed

eslint.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ const enableTypeCheckedRules = {
7575
...eslintPluginSafeTypeScript.configs.recommended.rules,
7676
"@susisu/safe-typescript/no-unsafe-object-property-check": "off",
7777
"@susisu/safe-typescript/no-unsafe-object-property-overwrite": "off",
78+
"@typescript-eslint/consistent-type-exports": "error",
7879
"@typescript-eslint/strict-boolean-expressions": ["warn", { allowNullableBoolean: true, allowNullableString: true }],
7980
} as const;
8081

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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
export * from "./helpers";
21
export type * from "./rule";
32
export type * from "./rule-feature";
43
export type * from "./rule-name";
54
export type * from "./rule-namespace";
5+
export * from "./utils";
File renamed without changes.

packages/utilities/ast/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,5 @@ export * from "./is-string-literal";
2727
export * from "./is-this-expression";
2828
export * from "./to-readable-node-name";
2929
export * from "./to-readable-node-type";
30-
export * from "./types";
30+
export type * from "./types";
3131
export * from "./unwrap-type-expression";

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)