@samitouri / QOS-React / commits / 81e1ee7476

[compiler] Support inline enums (flow/ts), type declarations (#33747)

Supports inline enum declarations in both Flow and TS by treating the node as pass-through (enums can't capture values mutably). Related, this PR extends the set of type-related declarations that we ignore. Previously we threw a todo for things like DeclareClass or DeclareVariable, but these are type related and can simply be dropped just like we dropped TypeAlias. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33747). * #33753 * #33752 * #33751 * #33750 * #33748 * __->__ #33747

Joseph Savona committed Jul 9, 2025 at 22:21 UTC 81e1ee7476a68fdf13c63d3002e5ef1b699b6842
7 files changed +179 -17
compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts
+16 -9
@@ -1388,10 +1388,13 @@ function lowerStatement(
1388 });
1389 return;
1390 }
1391 - case 'TypeAlias':
1392 - case 'TSInterfaceDeclaration':
1393 - case 'TSTypeAliasDeclaration': {
1394 - // We do not preserve type annotations/syntax through transformation
1391 + case 'EnumDeclaration':
1392 + case 'TSEnumDeclaration': {
1393 + lowerValueToTemporary(builder, {
1394 + kind: 'UnsupportedNode',
1395 + loc: stmtPath.node.loc ?? GeneratedSource,
1396 + node: stmtPath.node,
1397 + });
1398 return;
1399 }
1400 case 'DeclareClass':
@@ -1404,15 +1407,19 @@ function lowerStatement(
1407 case 'DeclareOpaqueType':
1408 case 'DeclareTypeAlias':
1409 case 'DeclareVariable':
1407 - case 'EnumDeclaration':
1410 + case 'InterfaceDeclaration':
1411 + case 'OpaqueType':
1412 + case 'TSDeclareFunction':
1413 + case 'TSInterfaceDeclaration':
1414 + case 'TSTypeAliasDeclaration':
1415 + case 'TypeAlias': {
1416 + // We do not preserve type annotations/syntax through transformation
1417 + return;
1418 + }
1419 case 'ExportAllDeclaration':
1420 case 'ExportDefaultDeclaration':
1421 case 'ExportNamedDeclaration':
1422 case 'ImportDeclaration':
1412 - case 'InterfaceDeclaration':
1413 - case 'OpaqueType':
1414 - case 'TSDeclareFunction':
1415 - case 'TSEnumDeclaration':
1423 case 'TSExportAssignment':
1424 case 'TSImportEqualsDeclaration':
1425 case 'TSModuleDeclaration':
compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts
+1 -2
@@ -5,7 +5,6 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import generate from '@babel/generator';
8 import {CompilerError} from '../CompilerError';
9 import {printReactiveScopeSummary} from '../ReactiveScopes/PrintReactiveFunction';
10 import DisjointSet from '../Utils/DisjointSet';
@@ -466,7 +465,7 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
465 break;
466 }
467 case 'UnsupportedNode': {
469 - value = `UnsupportedNode(${generate(instrValue.node).code})`;
468 + value = `UnsupportedNode ${instrValue.node.type}`;
469 break;
470 }
471 case 'LoadLocal': {
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneNonEscapingScopes.ts
+8 -6
@@ -829,12 +829,14 @@ class CollectDependenciesVisitor extends ReactiveFunctionVisitor<
829 };
830 }
831 case 'UnsupportedNode': {
832 - CompilerError.invariant(false, {
833 - reason: `Unexpected unsupported node`,
834 - description: null,
835 - loc: value.loc,
836 - suggestions: null,
837 - });
832 + const lvalues = [];
833 + if (lvalue !== null) {
834 + lvalues.push({place: lvalue, level: MemoizationLevel.Never});
835 + }
836 + return {
837 + lvalues,
838 + rvalues: [],
839 + };
840 }
841 default: {
842 assertExhaustive(
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/flow-enum-inline.expect.md new
+60
@@ -0,0 +1,60 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @flow
6 +function Component(props) {
7 + enum Bool {
8 + True = 'true',
9 + False = 'false',
10 + }
11 +
12 + let bool: Bool = Bool.False;
13 + if (props.value) {
14 + bool = Bool.True;
15 + }
16 + return <div>{bool}</div>;
17 +}
18 +
19 +export const FIXTURE_ENTRYPOINT = {
20 + fn: Component,
21 + params: [{value: true}],
22 +};
23 +
24 +```
25 +
26 +## Code
27 +
28 +```javascript
29 +import { c as _c } from "react/compiler-runtime";
30 +function Component(props) {
31 + const $ = _c(2);
32 + enum Bool {
33 + True = "true",
34 + False = "false",
35 + }
36 +
37 + let bool = Bool.False;
38 + if (props.value) {
39 + bool = Bool.True;
40 + }
41 + let t0;
42 + if ($[0] !== bool) {
43 + t0 = <div>{bool}</div>;
44 + $[0] = bool;
45 + $[1] = t0;
46 + } else {
47 + t0 = $[1];
48 + }
49 + return t0;
50 +}
51 +
52 +export const FIXTURE_ENTRYPOINT = {
53 + fn: Component,
54 + params: [{ value: true }],
55 +};
56 +
57 +```
58 +
59 +### Eval output
60 +(kind: exception) Bool is not defined
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/flow-enum-inline.js new
+18
@@ -0,0 +1,18 @@
1 +// @flow
2 +function Component(props) {
3 + enum Bool {
4 + True = 'true',
5 + False = 'false',
6 + }
7 +
8 + let bool: Bool = Bool.False;
9 + if (props.value) {
10 + bool = Bool.True;
11 + }
12 + return <div>{bool}</div>;
13 +}
14 +
15 +export const FIXTURE_ENTRYPOINT = {
16 + fn: Component,
17 + params: [{value: true}],
18 +};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ts-enum-inline.expect.md new
+59
@@ -0,0 +1,59 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + enum Bool {
7 + True = 'true',
8 + False = 'false',
9 + }
10 +
11 + let bool: Bool = Bool.False;
12 + if (props.value) {
13 + bool = Bool.True;
14 + }
15 + return <div>{bool}</div>;
16 +}
17 +
18 +export const FIXTURE_ENTRYPOINT = {
19 + fn: Component,
20 + params: [{value: true}],
21 +};
22 +
23 +```
24 +
25 +## Code
26 +
27 +```javascript
28 +import { c as _c } from "react/compiler-runtime";
29 +function Component(props) {
30 + const $ = _c(2);
31 + enum Bool {
32 + True = "true",
33 + False = "false",
34 + }
35 +
36 + let bool = Bool.False;
37 + if (props.value) {
38 + bool = Bool.True;
39 + }
40 + let t0;
41 + if ($[0] !== bool) {
42 + t0 = <div>{bool}</div>;
43 + $[0] = bool;
44 + $[1] = t0;
45 + } else {
46 + t0 = $[1];
47 + }
48 + return t0;
49 +}
50 +
51 +export const FIXTURE_ENTRYPOINT = {
52 + fn: Component,
53 + params: [{ value: true }],
54 +};
55 +
56 +```
57 +
58 +### Eval output
59 +(kind: ok) <div>true</div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ts-enum-inline.tsx new
+17
@@ -0,0 +1,17 @@
1 +function Component(props) {
2 + enum Bool {
3 + True = 'true',
4 + False = 'false',
5 + }
6 +
7 + let bool: Bool = Bool.False;
8 + if (props.value) {
9 + bool = Bool.True;
10 + }
11 + return <div>{bool}</div>;
12 +}
13 +
14 +export const FIXTURE_ENTRYPOINT = {
15 + fn: Component,
16 + params: [{value: true}],
17 +};