Bundle native builds with Re.Pack + OXC transform pipeline - #99407
Draft
elirangoshen wants to merge 30 commits into
Draft
Bundle native builds with Re.Pack + OXC transform pipeline#99407elirangoshen wants to merge 30 commits into
elirangoshen wants to merge 30 commits into
Conversation
# Conflicts: # ios/Podfile.lock
# Conflicts: # ios/Podfile.lock # package-lock.json
# Conflicts: # ios/Podfile.lock
hermes-parser takes ~95s to parse this minified single-line browser bundle, serializing the whole cold build. It is plain ESM with no RN-specific syntax, so rspack parses it natively. Cold prod bundle drops from ~120s to ~48s; dev server cold start from 146s to 78s.
Split the node_modules rule in two: packages that genuinely need babel + hermes-parser (Flow-typed RN core, codegenNativeComponent libs, Re.Pack's own runtime) stay on babel-swc-loader; everything else goes through the same OXC chain as app source (oxc -> worklets -> CJS lowering), with parallel workers and Fast Refresh disabled for third-party code. The cjs-inline-requires loader gains two options: sourcemap (prebuilt bundles ship multi-source maps that remapping() rejects) and hermesLowering (Hermes cannot parse async generators). Same-day numbers vs Metro: prod cold 85s -> 50s, dev cold (--platform ios) 54s -> 53s, dev warm 8s -> 4s, minified JS -3.5%. Boot-verified on iOS standalone, Android emulator, and HybridApp simulator builds.
…ie-hermes-parse # Conflicts: # ios/Podfile.lock # tsconfig.json
Contributor
|
|
elirangoshen
commented
Aug 25, 2026
| * Flow) and packages calling `codegenNativeComponent` in shipped JS, which needs the RN preset's | ||
| * codegen plugin to register Fabric view configs — without it the app crashes on boot. | ||
| */ | ||
| const BABEL_PACKAGES = [ |
Contributor
Author
There was a problem hiding this comment.
This is why the CI script in the post merge pr is crucial - because when there will be deps or new packages that meet those conditions in the comment and won't added to this allowlist if will crush on boot
…ie-hermes-parse # Conflicts: # ios/Podfile.lock # package-lock.json # tsconfig.json
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
elirangoshen
force-pushed
the
eliran/2733-dotlottie-hermes-parse
branch
from
August 26, 2026 13:54
01cfc69 to
dbfc762
Compare
40 tasks
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
This PR switches native (iOS/Android) JS bundling from Metro to Re.Pack (Rspack-based), with a Rust transform pipeline (OXC + SWC) for app source and almost all of
node_modules.What changed
pluginRepack(rock.config.mjs), with the bundler config atconfig/repack/rspack.config.mjsand a rootrspack.config.mjsre-export.node_modulesoutside a small allowlist are transformed by the same Rust pipeline:oxc-transform(React Compiler disabled for third-party code; Fast Refresh injection disabled fornode_modules, since Re.Pack's refresh runtime does not wrap them), thenworklets-loader(Reanimated worklets text-sniff), thencjs-inline-requires-loader(SWC CJS lowering + Metro-style inlined requires). All loaders run withparallel: trueworker pools.BABEL_PACKAGESallowlist (~23 packages) stays on Re.Pack'sbabel-swc-loaderwithhermes-parser: Flow-typed React Native core packages, packages that callcodegenNativeComponentin shipped JS (they need the RN Babel preset's codegen plugin to register Fabric view configs), and@callstack/repack's own runtime.cjs-inline-requires-loadergained two options:sourcemap: false: prebuilt packages ship multi-source maps that@jridgewell/remappingcannot compose.hermesLowering: true: addstransform-async-to-generator+transform-async-generator-functions, because Hermes cannot parse async generators. The full RN-preset lowering set was measured and rejected as pure size bloat.main(React Native 0.86,oxc-transform-react0.145).Why
Re.Pack previously ran
hermes-parseron every.jsfile. That parser is pathological on prebuilt, minified single-line files: 95s for one 0.5MB file vs 0.1s with@babel/parser. Metro never pays this cost because itshermesParseroption defaults tofalse.Results (same machine, same day; cold = cache wiped):
Hermes bytecode grew 3.5%. The likely cause is duplicated import wrappers from the inline-requires step; a contained loader fix is tracked as a follow-up, and staging ManualAppStartup telemetry will show whether it affects startup.
Known follow-ups
@babel/parserto match Metro's default.codegenNativeComponent/ Flow syntax on dependency bumps, so theBABEL_PACKAGESallowlist can't silently go stale.Fixed Issues
$ N/A (internal build-infrastructure improvement; no public GitHub issue tracks this work)
PROPOSAL: N/A (build tooling change, not a contributor proposal)
Automated Tests
No new automated tests: this PR changes the native bundling pipeline only, not runtime app code. Existing CI (typecheck, lint, Jest) covers the unchanged sources; the bundle itself is verified by building and booting each platform (see Tests).
Tests
developmentDebug)callstack-repackpod; verify the ScriptManager TurboModule loads on the hybrid RN host)hermesc -O(bytecode builds without errors).Offline tests
Bundling change only; no runtime network behavior is affected. Launch the app once online, then relaunch offline and verify it boots from the bundled JS and renders cached data normally.
QA Steps
Same as Tests (steps 1–2): build and boot each native platform, sign in, verify the LHN loads, open a report, and send a chat message.
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
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari