[No QA] Upgrade React Compiler to oxc-transform-react 0.145.0 - #98946
Draft
roryabraham wants to merge 3 commits into
Draft
[No QA] Upgrade React Compiler to oxc-transform-react 0.145.0#98946roryabraham wants to merge 3 commits into
roryabraham wants to merge 3 commits into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Explanation of Change
Upgrades the OXC React Compiler used by the web build from
oxc-transform@0.136.0tooxc-transform-react@0.145.0.As of 0.144.0 the React Compiler no longer ships inside
oxc-transform— it moved to a dedicatedoxc-transform-reactpackage.oxc-transform@0.145.0has noreactCompileroption at all, so this is a package swap rather than a version bump.oxc-transformhad no other callers in the repo, so it is removed outright.What changed, and why
config/rsbuild/loaders/oxc-react-compiler-loader.mjs— importsoxc-transform-react. Two things fall out of the new release:reactCompiler: false. Downgrade nonfatal compiler diagnostics fixed that: the compiler now skips the offending component and still emits code. Measured onsrc/, 16 files were paying for that second transform.severityand the newfatalresult flag instead of matching a[ReactCompiler]string prefix (see below).target: 'node20'is dropped —oxc-transform-reacthas notargetoption. Verified to be a no-op: transforming all 6,879src/files plus 800 files across the includednode_moduleswith and without it produced byte-identical output in every case.cwdis dropped for the same reason. Sourcemapsourcesentries are unchanged, because the loader passes absolute paths.config/reactCompiler/checkWithOxc.mjs— the shared analysis helper behind the compliance check and the ESLint processor. Rewritten against the new diagnostics; details in the section below.Two option defaults changed upstream and are pinned back to current behaviour:
eslintSuppressionRulesnow defaults to['react-hooks/exhaustive-deps', 'react-hooks/rules-of-hooks'], so a file carrying either suppression opts out of compilation.babel-plugin-react-compilersuppresses that default whenevervalidateExhaustiveMemoizationDependenciesandvalidateHooksUsageare both on, which is its own default, so web would have silently stopped memoizing 184 files that Metro/Jest still memoize. Set to[].sourcesnow defaults to skippingnode_modules. That would drop React Compiler memoization for theINCLUDED_NODE_MODULESallowlist (react-native-web,react-native-reanimated,@react-navigation/*, …) — 59 of 800 sampled files are memoized there today. Set to['']to keep compiling them.babel.config.js, which already excludesnode_modules, and would remove a real Babel/OXC divergence. That is a behaviour change on its own merits, so it is deliberately not bundled into a version upgrade.isDevis removed from the loader options — it was a realReactCompilerOptionsfield in 0.136 and no longer exists in 0.145, where it is silently ignored. Fast Refresh is driven byjsx.refresh, which is unchanged and verified working.React Compiler Compliance Check
Standardize diagnostics does break the check, and it fails open rather than closed. Both
checkWithOxc.mjsand the loader keyed off a[ReactCompiler]message prefix that 0.145 no longer emits, and severity moved too:Error·[ReactCompiler] Refs: Cannot access refs during renderWarning·Cannot access refs during renderWarning·[ReactCompiler] Todo: …Warning·…Running the pre-upgrade logic verbatim against 0.145 classifies both
RefViolation.tsxandHookOrderViolation.tsxasno-components— no diagnostic matches the prefix, so the OXC half of the check goes permanently green.The prefix carried a second job: the category (
RefsvsTodo) was what separated "this code violates the Rules of React" from "the compiler cannot handle this syntax yet". 0.145 reports both asWarning, and the category now only appears inside the renderedcodeframestring. Rather than parse that,checkWithOxc.mjsusespanicThreshold: 'critical_errors', which makes the compiler abort on a real violation (fatal: true, severityError) while leaving a limitation as a non-fatalWarning. That reproduces the old Error/Warning split from a structured signal, and keeps contributors from being blocked by gaps in the compiler itself. The loader keepspanicThreshold: 'none'so the build never fails on compiler diagnostics.It also keeps
memoizedconservative. UnderpanicThreshold: 'none', 0.145 emits partial memoization for a file where one function violates the rules;didBothCompilersMemoizeFilewould then report the file as fully memoized and let the ESLint processor suppress manual-memoization rules that are still needed.Across all 6,879
src/files, 25 (0.36%) change classification, all traceable to upstream compiler fixes: 20 movefailed→no-components(0.136 raisedMemoDependencies: Found missing memoization dependenciesas a hard error; 0.145 reportsExisting memoization could not be preservedas non-fatal), 3 improve tocompiled, 1 is newly detected as a genuine ref violation, and 1 gains memoization. None can produce a false CI failure: rule 1 only applies to newly added files, and rules 2 and 3 evaluate the base branch with the same checker, so grandfathering holds.Performance
Clean production web build, interleaved A/B on one machine (
rm -rf node_modules/.cache distbefore every run, alternating versions to cancel thermal drift):Median 18.68s → 17.03s (−8.8%), faster in 5 of 5 pairs. Non-interleaved batches of 3 runs each gave medians of 15.62s → 14.73s. Note the two batches disagree on absolute numbers by ~3s, which is why the paired result is the one worth reading.
The compiler is not the bottleneck in a full production build, so isolating the transform step is more informative. Transforming all 6,879
src/files single-threaded, 5 iterations, median:oxc-transform@0.136.0oxc-transform-react@0.145.04.46x faster. The native binary also shrinks from 7.3 MB to 4.0 MB (−45%).
Bundle output grows marginally: 55,910.3 kB → 55,946.7 kB raw (+0.07%), 22,004.2 kB → 22,019.0 kB gzipped (+0.07%), from the handful of components 0.145 successfully memoizes that 0.136 bailed on.
Fixed Issues
$
PROPOSAL:
Tests
npm installto pick upoxc-transform-reactand dropoxc-transform.npm run buildand verify it completes with noOxc transform errors.npm run web, open the app, and verify it loads and renders normally.npm run react-compiler-compliance-check check src/GlobalModals.tsxand verify OXC reportsCannot access refs during renderwith no[ReactCompiler]prefix.src/Probe.tsxcontaining a component that readsref.currentduring render, runnpm run react-compiler-compliance-check check-changed, and verify it fails withNew file fails to compile with the oxc React Compiler.npm run react-compiler-compliance-check check-changed, and verify it passes.src/Probe.tsx.npm run lintand verify it exits 0.Offline tests
N/A — this changes build tooling only. No runtime code, network behaviour, or Onyx interaction is affected.
QA Steps
N/A —
[No QA]. There is no user-facing change; the compiled output is equivalent apart from a handful of components that gain React Compiler memoization.PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectionAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
N/A — build tooling change, web build only.
Android: mWeb Chrome
N/A — build tooling change, web build only.
iOS: Native
N/A — build tooling change, web build only.
iOS: mWeb Safari
N/A — build tooling change, web build only.
MacOS: Chrome / Safari
Local CI parity run on this branch:
react-compiler-compliance-check check-changedreact-compiler-compliance-check check(synthetic violations)npm run lintnpm run typecheck(tsc, CI gate)npm run typecheck-tsgonpm test(jest)npm run test:bunnpm run fmt(oxfmt)npx knipscripts/findUnusedStyles.tsscripts/validatePatches.shnpm run build(production web)npm run lintneedsESLINT_CONCURRENCY=4on a 128 GB host, whichscripts/lint.tsalready documents; withautoits workers OOM. That is unrelated to this change — a memory probe over 1,500 files throughdidBothCompilersMemoizeFileshows this branch peaking at 507 MB RSS against 960 MB onmain.