Skip to content

chore(🐙): let a release build of the example run without Metro - #560

Open
YevheniiKotyrlo wants to merge 5 commits into
wcandillon:mainfrom
YevheniiKotyrlo:chore/example-release-build
Open

YevheniiKotyrlo wants to merge 5 commits into
wcandillon:mainfrom
YevheniiKotyrlo:chore/example-release-build

Conversation

@YevheniiKotyrlo

@YevheniiKotyrlo YevheniiKotyrlo commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #196.

An Android release build of the example could not run: its APK carried no JavaScript bundle, and the example fetched its images from URLs only Metro serves.

Problem

react-native-test-app turns off React Native's bundling task and packages the bundles app.json lists under resources, which name one for iOS, macOS and visionOS but none for Android, so assembleRelease packaged no bundle. A bundle packaged that way also skips the Hermes bytecode compile React Native's release build runs after bundling. With one packaged, useAssets fetched Image.resolveAssetSource(mod).uri, which without Metro is the image's drawable resource name with no scheme (src_assets_di3d). fetch() cannot read that, and useImageData dropped the rejected load, so App rendered nothing and logged nothing: the screen stayed blank and an e2e run waited out its connection timeout.

Solution

yarn bundle:android (from apps/example/) bundles as React Native's release build does for Hermes (--dev false --minify false, then hermesc -O), writes the bytecode to dist/main.android.jsbundle and the assets into dist/res, and app.json lists the bundle beside dist/res. It bundles with --reset-cache, as CI starts Metro, so CI and E2E_PORT are inlined with the values it runs with. scripts/compile-hermes-bytecode.mjs runs the compiler react-native compiles with: its hermes-compiler dependency, else the hermesc in its sdks/hermesc. Another package here installs a hermes-compiler for Hermes 1.0, and its bytecode makes the app abort with Wrong bytecode version. Expected 96 but got 98.

resolveImageUrl turns a resource name into android.resource://<application id>/drawable/<name>, which React Native's fetch() reads through the content resolver; Metro's URLs and iOS's file URLs pass through unchanged. fetchAsset, decodeImage and the Tests screen's fTexture use it. The application id is app.json's android.package, else react-native-test-app's default, as react-native-test-app derives it.

useImageData keeps each load's state and throws a failed load while rendering, so it reaches the error screen in development and the crash log in a release build.

CONTRIBUTING.md describes the release build and the e2e run on it. The ThreeJS scenes still load their models from Metro.

Verification

yarn lint · yarn tsc · yarn turbo run build --filter='./packages/*' · the Expo template's web build · yarn test:plugin · yarn check-dawn · yarn build:docs. CI's Android e2e step against a release build instead of Metro (CI=true yarn bundle:android, then assembleRelease) on an Android 15 emulator (API 35): 42 of 44 suites and 219 of 221 tests passed, the same as main's debug build with Metro on that emulator image. The two failures in both are YUV external-texture cases (ExternalTextureColorSpace, NativeBufferWrapper), which render on the host's GPU there. With main's asset loading, the same release build stays blank and the run ends with No device connected to the test server. With every image URL broken on purpose, a release build with main's useImageData stays blank with nothing in logcat, and with this change React reports TypeError: Network request failed with the component stack. The bytecode script gives byte-identical output on Windows, Linux and macOS, and picks hermes-compiler 0.14.1 under react-native 0.83. On devices the whole suite passes, 221 of 221: on a Galaxy S23 (Android 14), and on an iPad Pro 12.9-inch (4th generation, iOS 27.0.1) built and run as CI's iOS job does on a simulator (Debug build, Metro with CI=true). main passes 221 of 221 on both.

The Android release build packaged no JavaScript bundle, and the example
fetched its images from URLs only Metro serves. `yarn bundle:android`
bundles from a fresh transform cache, so that CI and E2E_PORT take the
values it runs with, compiles the bundle to Hermes bytecode as React
Native's release build does, and writes it where app.json now lists it.
An image whose source names an Android resource is read through an
android.resource:// URL.
@YevheniiKotyrlo

Copy link
Copy Markdown
Contributor Author

With main's app.json, assembleRelease packages no JavaScript bundle. With the bundle listed, release builds (x86_64) bundled with CI=true E2E_PORT=4247 yarn bundle:android, through CI's Android e2e step with no Metro on an Android 15 emulator (API 35):

Asset loading Result
main's blank screen; No device connected to the test server on port 4247 within 600s
this change, Hermes bytecode Test Suites: 2 failed, 42 passed, 44 total · Tests: 2 failed, 219 passed, 221 total

main's debug build with Metro gives the same counts and the same two failures on that emulator image: ExternalTextureColorSpace's and NativeBufferWrapper's YUV cases. A release build bundled without CI opens the demo list, and Image Blur draws its image read from the APK. The same bundle compiled by the Hermes 1.0 hermes-compiler another package installs aborts at launch: Compiling JS failed: Wrong bytecode version. Expected 96 but got 98.

…ing nothing

useImageData awaited decodeImage in an effect with no rejection handler, so
a load that failed left useAssets returning null for good: the app rendered
nothing, and a release build logged nothing. The hook now keeps the load's
state and throws a failed load while rendering, so it reaches the error
screen in development and the crash log in a release build.
@YevheniiKotyrlo
YevheniiKotyrlo marked this pull request as ready for review October 17, 2026 05:44

This branch has not been deployed

No deployments
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.

How to build an example app as release build?

1 participant