main
md 198 lines 8.89 KB
Rendered Raw
1 # Rust port: e2e parity TODO
2
3 Status snapshot (after the current stack lands):
4
5 | Variant | Score | Failures |
6 | ------- | ------------ | -------- |
7 | Babel | 1792 / 1802 | 10 |
8 | SWC | 1786 / 1802 | 16 |
9 | OXC | 1704 / 1795 | 91 |
10
11 The corpus grew by the three `ts-*` module-interop fixtures (1799 →
12 1802 on this branch). The Babel/SWC rows are measured on this branch
13 and their failure sets are byte-identical to the pre-stack baseline;
14 the OXC row predates the fixtures and has not been re-measured.
15
16 `cargo test --workspace`: 84 passed, 0 failed.
17
18 ## SWC
19
20 (Historical, pre-ts-interop-stack triage on the old staging base; current
21 snapshot at top.)
22
23 The 15 remaining SWC e2e failures fall into three groups. Each line names the
24 fixture and the failure mode; the group it sits in dictates the appropriate
25 fix.
26
27 ### Group A: Fixture maintenance, not Rust bugs
28
29 SWC compiles code that TS rejects, or vice versa, in ways where Rust's
30 behavior is arguably correct. The fix is to rename the fixture (drop the
31 `error.` prefix) and update the `.expect.md` snapshot so the suite stops
32 asserting the TS-specific output.
33
34 - `error.bug-invariant-local-or-context-references.js` — TS fires
35 `CompilerError::invariant` ("expected all references ... consistently
36 local or context"). Rust handles the same code without tripping the
37 invariant.
38 - `error.todo-jsx-intrinsic-tag-matches-local-binding.js` — SWC pipeline
39 emits a Todo bailout (`[hoisting] EnterSSA: Expected identifier to be
40 defined before being used`) that the Babel path does not.
41 - `error.todo-repro-named-function-with-shadowed-local-same-name.js`
42 Babel errors; SWC compiles.
43 - `new-mutability/error.todo-repro-named-function-with-shadowed-local-same-name.js`
44 — same as above with the new mutation-aliasing model enabled.
45 - `error.todo-rust-as-expression-assignment-target.tsx` — Babel errors;
46 SWC compiles.
47 - `fbt/error.todo-locally-require-fbt.js` — Babel emits the
48 `Invariant: <fbt> tags should be module-level imports` shape; SWC emits
49 `Todo: Local variables named 'fbt' may conflict with the fbt plugin`.
50 Different categories, both reasonable.
51
52 ### Group B: External dependency
53
54 - `use-no-forget-multiple-with-eslint-suppression.js` — spurious
55 `import { c as _c }` in the TS reference output. Fixed on `main` by
56 [react#36500](https://github.com/facebook/react/pull/36500) (merged).
57 Will pass automatically once `pr-36173` rebases onto `main`; until then
58 the TS dist built from `pr-36173` still emits the unused import.
59
60 ### Group C: Real SWC frontend bugs
61
62 Each line names the failure mode and a sketch of where to look.
63
64 - `fbt/fbt-param-with-quotes.js` — SWC codegen emits double quotes
65 (`"fbt"`) and reformats multi-line JSX into a single line; Babel uses
66 single quotes and preserves the source layout. Semantically equivalent
67 output; the fix is either an SWC codegen flag for quote style or a
68 post-emit pass. Low impact, high effort.
69
70 - `lone-surrogate-string-values.js` — TS preserves lone surrogates
71 (`\uD83E`); SWC emits `\uFFFD` because `Wtf8Atom::to_string_lossy()` in
72 `react_compiler_swc/src/convert_ast.rs::wtf8_to_string` replaces invalid
73 UTF-8 sequences. Real WTF-8 handling work that touches every call site
74 using that helper. Probably needs to detect lone surrogates and emit
75 `\uXXXX` escapes before they hit `String`.
76
77 - `many-scopes-no-stack-overflow.js` — TS memoizes the function
78 (`const $ = _c(401);` with 401 memo slots); SWC pipeline bails out and
79 returns the uncompiled source. The fixture exists to test that the
80 compiler handles many sequential reactive scopes without stack overflow,
81 so the SWC variant should compile. Root cause unclear — needs
82 investigation in the SWC pipeline or the compiler core to see where the
83 bail happens.
84
85 - `pattern4_bare_type.js` — Two unrelated bugs in one fixture:
86 1. Operator-precedence stripping. `Math.round((x - y) * 1000)` becomes
87 `Math.round(x - y * 1000)`. SWC codegen drops the parentheses around
88 the subtraction. Probably in `convert_ast_reverse.rs`'s
89 BinaryExpression handling.
90 2. Method return type annotation. `formatMetrics(): Metrics` becomes
91 `formatMetrics()`. The TS-type-on-binding-ident fix in commit
92 cc1ba1e1 only covered binding identifiers; class method signatures
93 are a separate code path. Same shape of fix; different
94 `convert_binding_ident`-equivalent call site.
95
96 - `reduce-reactive-deps/hoist-deps-diff-ssa-instance1.tsx`
97 `(x as HasA).a.value + 2` becomes `(x as HasA.a.value) + 2`. The member
98 expression's property chain gets absorbed into the type annotation when
99 `convert_ast_reverse` emits the cast. Likely a parenthesization /
100 precedence bug in the reverse converter or the SWC printer's handling
101 of `TSAsExpression` as the object of a `MemberExpression`.
102
103 - `todo-round2_unicode_string.js` (prefixed `todo-`) — Hex escape format
104 (`\xC5`) vs unicode escape (`\u00C5`) for bytes 0x80-0xFF. Both valid JS
105 literals; codegen format choice in SWC's string printer.
106
107 - `todo-round3_promote_used_temps.js` (prefixed `todo-`) — Class body
108 codegen. TS emits the class with fields and constructor; SWC emits an
109 empty class body and pulls fields/methods out into separate assignments.
110 Likely an interaction between SWC codegen and the compiler's
111 `promote_used_temps` pass.
112
113 - `ts-non-null-expression-default-value.tsx` — Generic type parameter
114 support. `const x: ReadonlyMap<string, string> = ...` becomes
115 `const x = ...` (annotation dropped entirely). Our
116 `convert_ts_type_to_json` helper in cc1ba1e1 explicitly guards against
117 `TsTypeRef` with `type_params` to avoid silently emitting
118 `ReadonlyMap` without the params. The proper fix needs serialization of
119 `TSTypeParameterInstantiation` in `convert_ast.rs` AND deserialization
120 in `convert_ast_reverse.rs::convert_ts_type_from_json`.
121
122 ## Cross-frontend: TypeScript module interop statements
123
124 Three `ts-*` fixtures pin how TS module-interop statements
125 (`import x = require(...)`, `export = x`, `export as namespace X`) must
126 behave: the statement is preserved in output and the file's functions
127 still compile.
128
129 - **Babel/NAPI** and **SWC** now preserve these end to end. Both flow
130 the statements through `Statement::Unknown` (the raw Babel-shaped
131 carrier in `react_compiler_ast`); the SWC frontend rebuilds the swc
132 module declarations in `convert_ast_reverse.rs` and works around an
133 upstream swc_ecma_codegen bug that prints `TsNamespaceExportDecl`
134 as `export = X` (`react_compiler_swc/src/ts_namespace_export_fixup.rs`,
135 which also carries the guard test that flags when the upstream fix
136 lands). Fixtures renamed from `todo-ts-*` to `ts-*`; the
137 `SproutTodoFilter` entry for the namespace fixture remains (sprout's
138 evaluator cannot evaluate `export as namespace`).
139 - **OXC** remains deferred: `todo!()` panics in
140 `react_compiler_oxc/src/convert_ast.rs` (arms
141 `TSImportEqualsDeclaration` / `TSExportAssignment` /
142 `TSNamespaceExportDeclaration`; the sibling `TSGlobalDeclaration`
143 arm is also unmodeled but unreachable from Babel-parsed fixtures,
144 which represent `declare global` as `TSModuleDeclaration`).
145
146 - `ts-import-equals-declaration.ts`
147 - `ts-export-assignment.ts`
148 - `ts-namespace-export-declaration.ts`
149
150 ## Babel
151
152 (Historical, pre-ts-interop-stack numbers; current snapshot at top.)
153
154 **TODO: scope this out.** Babel is at 1788 / 1795 (7 failures). These have
155 been the baseline throughout the SWC parity stack and were not touched, so the
156 failure list is whatever was on `pr-36173` before this work landed.
157
158 Next step is to enumerate the failures by fixture and bucket them the same
159 way as SWC (fixture maintenance / external dependency / real bugs). Run:
160
161 ```bash
162 bash compiler/scripts/test-e2e.sh --no-color --variant babel
163 ```
164
165 …and triage the resulting failures into A/B/C groups under this section.
166
167 ## OXC
168
169 (Historical, pre-ts-interop-stack numbers; current snapshot at top.)
170
171 **TODO: scope this out.** OXC is at 1704 / 1795 (91 failures). The CLI
172 `filename` fix in commit c30f0d6f bumped this by +2 from the 1702 baseline,
173 but everything else is unaddressed.
174
175 Next step is to enumerate failures and identify OXC-specific clusters
176 (likely AST conversion gaps in `react_compiler_oxc` analogous to the SWC
177 work in this stack). Run:
178
179 ```bash
180 bash compiler/scripts/test-e2e.sh --no-color --variant oxc
181 ```
182
183 …and bucket the resulting failures into A/B/C groups under this section.
184 Expect significant overlap with the SWC Group C bugs (cast wrappers,
185 type annotations, UTF-16/WTF-8 handling) since both frontends share the
186 post-conversion pipeline.
187
188 ## How this stack got here
189
190 (Historical, pre-ts-interop-stack numbers; current snapshot at top.)
191
192 - `compiler/scripts/test-e2e.sh --variant swc` baseline was 1742 / 1795
193 (53 failures) before this stack.
194 - 9 commits in the current stack reduce that to 1780 / 1795 (15 failures,
195 -38 fixtures, 72% reduction).
196 - Babel variant: 1788 / 1795 throughout (no regressions).
197 - OXC variant: 1702 → 1704 (the CLI filename commit also benefited OXC).
198 - `cargo test --workspace`: 56 passed, 0 failed throughout.