[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.
This commit is contained in:
Mofei Zhang
2025-06-02 11:31:52 -04:00
parent 14094f80cb
commit 1d4cbaffb5
4 changed files with 83 additions and 2 deletions
@@ -1726,7 +1726,7 @@ function codegenInstructionValue(
}
case 'UnaryExpression': {
value = t.unaryExpression(
instrValue.operator as 'throw', // todo
instrValue.operator,
codegenPlaceToExpression(cx, instrValue.value),
);
break;
@@ -2582,7 +2582,16 @@ function codegenValue(
value: boolean | number | string | null | undefined,
): t.Expression {
if (typeof value === 'number') {
return t.numericLiteral(value);
if (value < 0) {
/**
* Babel's code generator produces invalid JS for negative numbers when
* run with { compact: true }.
* See repro https://codesandbox.io/p/devbox/5d47fr
*/
return t.unaryExpression('-', t.numericLiteral(-value), false);
} else {
return t.numericLiteral(value);
}
} else if (typeof value === 'boolean') {
return t.booleanLiteral(value);
} else if (typeof value === 'string') {
@@ -0,0 +1,56 @@
## Input
```javascript
import {Stringify} from 'shared-runtime';
function Repro(props) {
const MY_CONST = -2;
return <Stringify>{props.arg - MY_CONST}</Stringify>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Repro,
params: [
{
arg: 3,
},
],
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import { Stringify } from "shared-runtime";
function Repro(props) {
const $ = _c(2);
const t0 = props.arg - -2;
let t1;
if ($[0] !== t0) {
t1 = <Stringify>{t0}</Stringify>;
$[0] = t0;
$[1] = t1;
} else {
t1 = $[1];
}
return t1;
}
export const FIXTURE_ENTRYPOINT = {
fn: Repro,
params: [
{
arg: 3,
},
],
};
```
### Eval output
(kind: ok) <div>{"children":5}</div>
@@ -0,0 +1,15 @@
import {Stringify} from 'shared-runtime';
function Repro(props) {
const MY_CONST = -2;
return <Stringify>{props.arg - MY_CONST}</Stringify>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Repro,
params: [
{
arg: 3,
},
],
};
+1
View File
@@ -242,6 +242,7 @@ export async function transformFixtureInput(
filename: virtualFilepath,
highlightCode: false,
retainLines: true,
compact: true,
plugins: [
[plugin, options],
'babel-plugin-fbt',