Skip to content

chore(rn-run): pin rnrun 0.2.3 - #29

Open
sanketsahu wants to merge 7 commits into
mainfrom
chore/rnrun-0.2.3
Open

chore(rn-run): pin rnrun 0.2.3#29
sanketsahu wants to merge 7 commits into
mainfrom
chore/rnrun-0.2.3

Conversation

@sanketsahu

Copy link
Copy Markdown
Contributor

Bumps the preset image's rnrun pin to 0.2.3 (ships browser-metro 1.3.0).

Why: 0.2.1 rendered every <Text> invisible on native in NativeWind apps. NativeWind styles components two ways — cssInterop() swaps the component (works, because app-level JSX resolves it), while remapProps() components (KeyboardAvoidingView, FlatList, ImageBackground) pass an opaque placeholder style down to their children. Resolving that placeholder needs the css-interop JSX wrapper active for the JSX inside the package chunk. Metro gets this free by aliasing react/jsx-runtime globally; rnrun's per-package chunks compile against the real runtime, so package-internal JSX bypassed the wrapper and every descendant Text lost its style — default black at no font size, invisible on a dark theme with zero layout height.

0.2.3 also adds a native-deps version to bundle hashes, which structurally prevents stale native bytes being served from any cache layer.

Verified on the box: rebuilt image reports [email protected] / browser-metro 1.3.0; served iOS bundles now contain jsx-runtime__original (the wrapper marker) and 437–507 __workletHash entries (correctly workletized reanimated, versus 8 in the poisoned bundles). Fleet migrated: suspended mobile containers removed so their next wake creates fresh, and the running ones restarted.

🤖 Generated with Claude Code

sanketsahu and others added 7 commits August 18, 2026 14:56
0.2.3 (with browser-metro 1.3.0) fixes invisible text on native: NativeWind's
remapProps components — KeyboardAvoidingView, FlatList, ImageBackground — pass
an opaque placeholder style to their children, and resolving it needs the
css-interop JSX wrapper active inside the package chunk. rnrun compiles those
chunks against the real react/jsx-runtime, so every descendant Text lost its
resolved style: default black at no font size, which on a dark theme renders as
nothing and collapses the layout.

0.2.3 also versions native dep hashes, so a server-side change to native output
mints new hashes and every cache layer misses instead of serving stale bytes.

Co-Authored-By: Claude Fable 5 <[email protected]>
0.2.5 fixes a deterministic device crash: any screen using FlatList redboxed
with "property is not configurable". esbuild lowered class fields with spec
semantics (defineProperty) while the RN ecosystem is compiled with loose class
fields (plain assignment), and VirtualizedList declares `state` where [[Define]]
is rejected but [[Set]] succeeds. Diagnostic signature: __publicField or
__defNormalProp frames in a device stack trace.

It also folds the native-deps version into /pkg and /prelude URLs and the
server's per-package cache keys, so a change to native bundling output is
unreachable through any stale cache layer by construction — no more hand-wiping
the on-box cache after a server fix.

--prewarm ios moves the cold build off the critical path: a scale-to-zero
container boots, idles, and today the first device to scan waits out the whole
build. iOS only, because a cold build is CPU-bound at the package server and
build time tracks concurrency more than dep count.

Co-Authored-By: Claude Fable 5 <[email protected]>
0.3.0 caches the assembled bundle to disk, keyed by platform, tool versions,
project sources, assets, inlined env and the bundler flags. Until now every
container start rebuilt the bundle from scratch, and because containers scale to
zero the QR scan was usually what started the container — so the phone waited
out a 30-90s rebuild of bytes the same container had produced minutes earlier.

Measurements that drove this (same project, warm package cache, 12.3MB bundle):
gVisor/4cpu/1GB 31.6s bundling and 86s wall; gVisor/8cpu/2GB 31.0s and 75s;
runc/8cpu/2GB 25.4s and 43s. Doubling CPU bought nothing, so the fix was to stop
redoing the work rather than to buy more machine.

0.3.0 also makes --prewarm exclusive: startup previously built a web bundle
unconditionally, so a container woken by a phone scan built web first and then
iOS. Web is now deferred to the first browser request.

The cache lives under HOME, which is the container's writable layer, and our
suspend is `docker stop` on the same container — so it survives a normal
scale-to-zero wake. Only container recreation loses it.

Co-Authored-By: Claude Fable 5 <[email protected]>
0.3.0 (and 0.2.7, which introduced the lean fetch) resolves the reanimated
worklets babel plugin by symlinking into rnrun's own node_modules. This image
installs rnrun with `npm install -g`, whose flattened layout has no such path,
so the fetch fails, the full-install fallback fails too, and every bundle is
built with the plugin disabled:

  [worklets] fetched plugin did not load; falling back to a full install
  [worklets] plugin install failed (ENOENT: ... lstat
    '/usr/local/lib/node_modules/rnrun/node_modules/react-native-worklets');
    reanimated worklets will not run.

All 8 live containers reported it, meaning every project would ship animations
that cannot run — worse than the slow wakes 0.3.0 was meant to fix. 0.2.5 uses
the older full-install path, which works here.

The disk bundle cache in 0.3.0 is otherwise verified good: a warm start serves
in 0.19s versus a 30-90s rebuild, --prewarm does not bypass it, and the web
build is correctly deferred. Re-pin once the plugin resolves against a global
install (or once the plugin can be baked via RNRUN_TOOLS_DIR).

Co-Authored-By: Claude Fable 5 <[email protected]>
Two corrections to the 0.3.0 roll, both found by measuring real production wakes
rather than fresh-container tests:

1. gVisor wipes the container writable layer on docker stop/start. rnrun's disk
   bundle cache lives under HOME=/cache in that layer, so it never survived a
   scale-to-zero wake — every wake rebuilt (measured: writable layer 114M before
   stop, 14M and 0 cache entries after start). Redirect the two expensive caches
   (RNRUN_BUNDLE_CACHE_DIR, RNRUN_TOOLS_DIR) to /data, the per-mobile-workload
   bind mount, which is real host storage. pkg-cache stays ephemeral: a
   bundle-cache hit serves instantly, only the background rebuild re-fetches.

2. Drop --prewarm. With a persistent cache, --prewarm is actively harmful: it
   rebuilds unconditionally on start, and a scan arriving mid-rebuild coalesces
   onto it (~32s) instead of reading the cache. Without it the serve path hits
   the cache directly.

Result on a live project: cold first build 38s (once), then a real suspend/wake
serves the iOS bundle in 0.67s (was 32-90s). Worklets enabled throughout.

0.3.1 also fixes the worklets plugin resolution under a global npm install that
made 0.2.7/0.3.0 unusable here (reanimated silently disabled).

Co-Authored-By: Claude Fable 5 <[email protected]>
0.3.2 makes the Fast-Refresh module-graph rebuild lazy (built on first edit)
instead of firing on every cache hit, so --prewarm ios is now a true no-op on a
warm cache — verified on a real stop/start wake: 0.19s serve, no rebuild.
Restore it so a genuinely-cold project warms during idle rather than making its
first-ever scan pay the ~38s build, without the per-wake penalty 0.3.1 had.

Also add RNRUN_PKG_CACHE_DIR=/data/.rnrun/pkg (new in 0.3.2): the package cache
was still under HOME in the ephemeral layer and re-fetched every wake. All three
rnrun caches now co-locate on the persistent /data mount.

Co-Authored-By: Claude Fable 5 <[email protected]>
0.3.3 adds the expo-web SPA deep-path fallback (reactnative-run #70): a browser
navigation (Accept: text/html) to a deep route with no file extension and not a
dev-server/manifest/asset path now serves the same index.html as /, so
expo-router resolves the route client-side instead of 404ing. Fixes /login etc.
on a primary rnrun web host rendering "Not found: /login".

Native/Expo-Go behavior unchanged; bare (non-navigation) requests to unknown
paths still 404 by design so real misses aren't masked. Also carries the
bun-install fail-fast, pre-warm, tailwindcss stub, chunk cache, and lazy graph
merged since 0.3.2.

Verified live on rapidnative-app-8zenm.rapidnative.app: /login and /some/deep/route
(Accept: text/html) -> 200 byte-identical to /; /login no-Accept -> 404;
/index.bundle -> 200 js. Worklets enabled, real builds unaffected (a project with
a syntax error in theme.ts errors identically on 0.3.2 and 0.3.3).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant