Skip to content

Commit 6b1a2c1

Browse files
authored
fix(react-compiler): optimize components declared with arrow function and implicit return and compilationMode: 'infer' (#31792)
fixes #31601 #31639 cc @josephsavona
1 parent de4aad5 commit 6b1a2c1

File tree

3 files changed

+71
-17
lines changed

3 files changed

+71
-17
lines changed

compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,31 +1008,39 @@ function callsHooksOrCreatesJsx(
10081008
return invokesHooks || createsJsx;
10091009
}
10101010

1011+
function isNonNode(node?: t.Expression | null): boolean {
1012+
if (!node) {
1013+
return true;
1014+
}
1015+
switch (node.type) {
1016+
case 'ObjectExpression':
1017+
case 'ArrowFunctionExpression':
1018+
case 'FunctionExpression':
1019+
case 'BigIntLiteral':
1020+
case 'ClassExpression':
1021+
case 'NewExpression': // technically `new Array()` is legit, but unlikely
1022+
return true;
1023+
}
1024+
return false;
1025+
}
1026+
10111027
function returnsNonNode(
10121028
node: NodePath<
10131029
t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
10141030
>,
10151031
): boolean {
1016-
let hasReturn = false;
10171032
let returnsNonNode = false;
1033+
if (
1034+
// node.traverse#ArrowFunctionExpression isn't called for the root node
1035+
node.type === 'ArrowFunctionExpression' &&
1036+
node.node.body.type !== 'BlockStatement'
1037+
) {
1038+
returnsNonNode = isNonNode(node.node.body);
1039+
}
10181040

10191041
node.traverse({
10201042
ReturnStatement(ret) {
1021-
hasReturn = true;
1022-
const argument = ret.node.argument;
1023-
if (argument == null) {
1024-
returnsNonNode = true;
1025-
} else {
1026-
switch (argument.type) {
1027-
case 'ObjectExpression':
1028-
case 'ArrowFunctionExpression':
1029-
case 'FunctionExpression':
1030-
case 'BigIntLiteral':
1031-
case 'ClassExpression':
1032-
case 'NewExpression': // technically `new Array()` is legit, but unlikely
1033-
returnsNonNode = true;
1034-
}
1035-
}
1043+
returnsNonNode = isNonNode(ret.node.argument);
10361044
},
10371045
// Skip traversing all nested functions and their return statements
10381046
ArrowFunctionExpression: skipNestedFunctions(node),
@@ -1041,7 +1049,7 @@ function returnsNonNode(
10411049
ObjectMethod: node => node.skip(),
10421050
});
10431051

1044-
return !hasReturn || returnsNonNode;
1052+
return returnsNonNode;
10451053
}
10461054

10471055
/*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
2+
## Input
3+
4+
```javascript
5+
// @compilationMode(infer)
6+
const Test = () => <div />;
7+
8+
export const FIXTURE_ENTRYPOINT = {
9+
fn: Test,
10+
params: [{}],
11+
};
12+
13+
```
14+
15+
## Code
16+
17+
```javascript
18+
import { c as _c } from "react/compiler-runtime"; // @compilationMode(infer)
19+
const Test = () => {
20+
const $ = _c(1);
21+
let t0;
22+
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
23+
t0 = <div />;
24+
$[0] = t0;
25+
} else {
26+
t0 = $[0];
27+
}
28+
return t0;
29+
};
30+
31+
export const FIXTURE_ENTRYPOINT = {
32+
fn: Test,
33+
params: [{}],
34+
};
35+
36+
```
37+
38+
### Eval output
39+
(kind: ok) <div></div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// @compilationMode(infer)
2+
const Test = () => <div />;
3+
4+
export const FIXTURE_ENTRYPOINT = {
5+
fn: Test,
6+
params: [{}],
7+
};

0 commit comments

Comments
 (0)