Skip to content

Commit 1d4cbaf

Browse files
committed
[compiler][patch] Emit unary expressions instead of negative numbers
This is a babel bug + edge case. Babel compact mode produces invalid JavaScript (i.e. parse error) when given a `NumericLiteral` with a negative value. See https://codesandbox.io/p/devbox/5d47fr for repro. As a followup, we could change our test infra parse babel options (e.g. a babel transform options pragma) which could let us track regressions. We may add an "exhaustive" mode to the compiler test runner to test (1) different babel options and (2) commonly used versions.
1 parent 14094f8 commit 1d4cbaf

File tree

4 files changed

+83
-2
lines changed

4 files changed

+83
-2
lines changed

compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1726,7 +1726,7 @@ function codegenInstructionValue(
17261726
}
17271727
case 'UnaryExpression': {
17281728
value = t.unaryExpression(
1729-
instrValue.operator as 'throw', // todo
1729+
instrValue.operator,
17301730
codegenPlaceToExpression(cx, instrValue.value),
17311731
);
17321732
break;
@@ -2582,7 +2582,16 @@ function codegenValue(
25822582
value: boolean | number | string | null | undefined,
25832583
): t.Expression {
25842584
if (typeof value === 'number') {
2585-
return t.numericLiteral(value);
2585+
if (value < 0) {
2586+
/**
2587+
* Babel's code generator produces invalid JS for negative numbers when
2588+
* run with { compact: true }.
2589+
* See repro https://codesandbox.io/p/devbox/5d47fr
2590+
*/
2591+
return t.unaryExpression('-', t.numericLiteral(-value), false);
2592+
} else {
2593+
return t.numericLiteral(value);
2594+
}
25862595
} else if (typeof value === 'boolean') {
25872596
return t.booleanLiteral(value);
25882597
} else if (typeof value === 'string') {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
2+
## Input
3+
4+
```javascript
5+
import {Stringify} from 'shared-runtime';
6+
7+
function Repro(props) {
8+
const MY_CONST = -2;
9+
return <Stringify>{props.arg - MY_CONST}</Stringify>;
10+
}
11+
12+
export const FIXTURE_ENTRYPOINT = {
13+
fn: Repro,
14+
params: [
15+
{
16+
arg: 3,
17+
},
18+
],
19+
};
20+
21+
```
22+
23+
## Code
24+
25+
```javascript
26+
import { c as _c } from "react/compiler-runtime";
27+
import { Stringify } from "shared-runtime";
28+
29+
function Repro(props) {
30+
const $ = _c(2);
31+
32+
const t0 = props.arg - -2;
33+
let t1;
34+
if ($[0] !== t0) {
35+
t1 = <Stringify>{t0}</Stringify>;
36+
$[0] = t0;
37+
$[1] = t1;
38+
} else {
39+
t1 = $[1];
40+
}
41+
return t1;
42+
}
43+
44+
export const FIXTURE_ENTRYPOINT = {
45+
fn: Repro,
46+
params: [
47+
{
48+
arg: 3,
49+
},
50+
],
51+
};
52+
53+
```
54+
55+
### Eval output
56+
(kind: ok) <div>{"children":5}</div>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import {Stringify} from 'shared-runtime';
2+
3+
function Repro(props) {
4+
const MY_CONST = -2;
5+
return <Stringify>{props.arg - MY_CONST}</Stringify>;
6+
}
7+
8+
export const FIXTURE_ENTRYPOINT = {
9+
fn: Repro,
10+
params: [
11+
{
12+
arg: 3,
13+
},
14+
],
15+
};

compiler/packages/snap/src/compiler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ export async function transformFixtureInput(
242242
filename: virtualFilepath,
243243
highlightCode: false,
244244
retainLines: true,
245+
compact: true,
245246
plugins: [
246247
[plugin, options],
247248
'babel-plugin-fbt',

0 commit comments

Comments
 (0)