fix: integrate reviewed build and native development fixes#737
Merged
Conversation
FSExtra.rm passes through to the raw callback-style Node fs.rm on fs-extra
< 9, so `await FSExtra.rm(staticDir, { force, recursive })` crashes on
teardown ("callback is not a function", thrown async -> uncaught ->
process.exit(1)) when a consumer's tree hoists an older fs-extra above
one's nested 11.x. FSExtra.remove is promise-safe on every fs-extra
version and is already this file's own idiom.
REANIMATED_IGNORED_PATHS_REGEX was built with `s.replace(/\//g, '/')`, a no-op, so it only matched forward slashes. On Windows the module id uses backslashes, so react-native's own Animated files were not ignored and got pushed through the isolated reanimated babel pass (no JSX syntax), throwing "Support for the experimental syntax 'jsx' isn't currently enabled". Accept either separator (`[/\]`), mirroring SPEC_FILE_RE in the same file.
transform-classes was gated behind !dev in two spots, so dev native bundles transpiled class fields but left `class extends` as modern ES6. Hermes chokes on that half-transpiled class hierarchy at `new Subclass()` (TypeError: Cannot read property 'prototype' of undefined), red-screening the app at init on the Vite-native path. Make transform-classes unconditional (matching the fully-classed production bundle); keep only async-to-generator production-only.
On the new architecture, RN registers its real HMRClient via registerCallableModule during setUpBatchedBridge — before vxrn's bundle-splice stub runs — and the underlying std::unordered_map::emplace keeps the first registration. So RN's HMRClient.setup() survives, opens a MetroHMRClient to /hot, and red-boxes "unknown-message [object Object]" on every edit (metro's client can't parse vxrn's hmr:* frames). Alias react-native's Utilities/HMRClient to a no-op module at resolve time so RN registers the no-op as the first-and-only HMRClient — working with emplace, arch-agnostically. MetroHMRClient is never constructed (and tree-shakes out of the bundle), so the socket never opens and the error cannot occur. vxrn's own HMR (the rolldown-runtime WebSocket) is untouched. Also removes two now-dead workarounds that only chased the symptom: the emplace-losing bundle-splice stub, and a global ErrorUtils handler that matched 'HMRClient' (the thrown text is "unknown-message [object Object]", so it never caught this error).
RN's own component specs are Flow .js (e.g. RCTSafeAreaViewNativeComponent.js). @react-native/babel-plugin-codegen must replace codegenNativeComponent<Props>(...) with a static view-config at build time, but it never runs on them — so the runtime codegenNativeComponent executes and RN logs "Codegen didn't run for SafeAreaView. This will be an error in the future." on every render/hot reload. Two layers, both about Flow .js never reaching codegen with its type intact: 1. @vxrn/compiler's transformBabel adds a parser only for TS (.ts/.tsx via preset-typescript) and nothing for .js, so babel can't parse RN's Flow specs (import type, codegenNativeComponent<T>(...), casts). It throws, transformBabel returns undefined, and the file falls back to the SWC path (strips Flow, no codegen). Fix: parse Flow for non-TS files by appending @babel/plugin-transform-flow-strip-types (parses + strips), mirroring @react-native/babel-preset (TS -> preset-typescript, JS -> this plugin). The TS path is byte-identical. 2. On the native bundler, flowStripPlugin runs before the compiler and strips Flow from RN files, erasing the type argument codegen needs. Fix: in flowStripPlugin, when a file uses codegenNativeComponent, run @react-native/babel-plugin-codegen with a Flow parser first (while the type is present), then strip Flow. Graceful: any error falls through to the plain strip, never a broken build. Adds @babel/plugin-transform-flow-strip-types to @vxrn/compiler's deps (was only transitive) and @react-native/babel-plugin-codegen to vxrn's deps (now referenced by flowStripPlugin).
On the Vite-native dev path, editing a source file never updated the running app. React Refresh runs on device but can't repaint One's route components: useScreens re-wraps loadRoute()'s export (fromImport -> forwardRef -> getPageExport), so the mounted fiber isn't in the edited module's Refresh family. One ships a route-cache-refresh fallback, but on native three links were missing: 1. vxrn's rolldown runtime commits each HMR patch in applyUpdates() but never surfaced the updated module ids to a framework hook (the web one:route-update event has no equivalent on the native /hot transport). 2. ScreenComponent never subscribed to a route-hot signal, so nothing re-ran loadRoute(). 3. hmrImport.native was a rejecting stub, so resolve() fell back to the memoized original module. Fix the whole chain: - vxrn applyUpdates surfaces each committed boundary id to globalThis.__oneRouteHotUpdate (guarded; a no-op if nothing registered it). - useScreens registers that hook (evict the route cache via window.__oneRouteCache.clearFile, which bumps hmrVersion so resolve() re-imports, + bump a subscribable epoch) and subscribes ScreenComponent via useSyncExternalStore so it re-renders and re-runs loadRoute(). - hmrImport.native re-imports from the live rolldown runtime (__rolldown_runtime__.loadExports), replacing the rejecting stub. No dynamic import() (Hermes can't parse it); falls back to reject when the runtime is absent. All one-side changes are gated to TAMAGUI_TARGET === 'native', so the web path (one-hmr-update) is untouched.
Cover resolveId (aliases RN Utilities/HMRClient across both separators and optional js/ts/cjs extension; leaves near-misses like HMRClientRegistry alone) and load (no-op module exposing every method RN calls). Export the plugin factory so the test drives it directly.
…t drift The Hermes SWC downlevel include list was hand-copied in two call sites, and the original bug was transform-classes missing from one of them. Define the class set once (HERMES_CLASS_TRANSFORMS) plus the prod-only addend (HERMES_PROD_TRANSFORMS), consumed by both sites via getHermesSWCIncludes(dev). Behavior-identical; makes the "these move together" invariant a compile-time fact rather than a review-time hope. Unit-tested.
…ch on Windows The reanimated ignore-list (and the native optimizeDeps filter in index.ts) assumed forward-slash ids, but the native/patches path hands OS-native ids — so on Windows react-native's own files slipped past the ignore list into the reanimated babel pass (which has no JSX/TS parser), and optimizeDeps' node_modules filter dropped every file. Normalize the id once at getBabelOptions' entry (mirroring transformSWC.ts) so every path matcher can assume forward slashes; the reanimated regex reverts to a plain join. Fix the same-class filter in index.ts to accept either separator.
…rop the duplicate Running @react-native/babel-plugin-codegen inside flowStripPlugin was a layering violation — codegen ran in two packages with two divergent gates plus a silent catch. Instead, run vxrnCompilerPlugin BEFORE flowStripPlugin: now that the compiler parses Flow, react-native's Flow .js specs reach the codegen the compiler already owns with their type argument intact, and flowStripPlugin strips any remaining Flow downstream as the guaranteed safety net before oxc's core parse. Removes the flowStripPlugin codegen block, its silent catch, and vxrn's now-unused @react-native/babel-plugin-codegen dependency.
…date hook Move One's native route-HMR store out of useScreens.tsx into a dedicated routeHmr.native.ts (with a web no-op routeHmr.ts), matching the existing hmrImport.ts/.native.ts platform split -- removes the import-time global side effect from useScreens and makes the store unit-testable. Replace the ad-hoc globalThis.__oneRouteHotUpdate with a typed vxrn extension point, globalThis.__VXRN_ON_MODULE_UPDATED__, declared next to __VXRN_CUSTOM_HMR_HANDLER__: vxrn's applyUpdates surfaces each committed module id to it, and any framework (here One) registers a handler. Behavior unchanged. Adds unit tests for hmrImport.native (loadExports) and the routeHmr.native store.
Agentbus-Session: ab-mrmnqe8m-76176
Agentbus-Session: ab-mrmnqe8m-76176
Agentbus-Session: ab-mrmnqe8m-76176
|
🚅 Deployed to the one-pr-737 environment in onestack.dev
|
github-merge-queue
Bot
removed this pull request from the merge queue due to a conflict with the base branch
Jul 16, 2026
github-merge-queue
Bot
removed this pull request from the merge queue due to a conflict with the base branch
Jul 16, 2026
github-merge-queue
Bot
removed this pull request from the merge queue due to a conflict with the base branch
Jul 16, 2026
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.
Integrates the reviewed work from #731, #732, #733, #734, #735, and #736 on one branch, with conflicts resolved and combined-stack cleanup.
Additional fixes from integration review:
Validation:
bun run ci: 23/23 builds, 4/4 checks, 37/37 typecheck/lint tasks, 30/30 test tasks; main suites passed in dev, prod, and non-CLI dev modesPlease merge this PR with a merge commit so the original PR heads become ancestors of main.