Skip to content

Instantly share code, notes, and snippets.

@zetavg
Last active June 22, 2024 14:42
Show Gist options
  • Save zetavg/eacd8f8e6ec46213570ab6d1431672be to your computer and use it in GitHub Desktop.
Save zetavg/eacd8f8e6ec46213570ab6d1431672be to your computer and use it in GitHub Desktop.

How React Native Bundles JS on Build

We want to know how React Native will be bundling the JavaScript code and assets during the build of iOS or Android native apps.

Updated on June 2024.

Apple

In the Xcode project, there’s a “Bundle React Native code and images” build phase, which will execute something like the following:

`"$NODE_BINARY" --print "require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'"`

The react-native-xcode.sh lives in the react-native package, located at packages/react-native/scripts/react-native-xcode.sh inside the react-native repo.

It executes the bundle command of React Native CLI (CLI entry point at packages/react-native/cli.js), and Hermes to emit the binary from the bundled JS file.

Check the Shared section to continue on what will happen afterwards.

Android

In the android/app/build.gradle file, there’s something like the following:

apply plugin: "com.facebook.react"

We can do that because in the android/build.gradle file, there’s something like the following:

buildscript {
    // ...
    dependencies {
        // ...
        classpath('com.facebook.react:react-native-gradle-plugin')
    }
}

And also, this in settings.gradle:

includeBuild('../node_modules/@react-native/gradle-plugin')

(Reference: https://reactnative.dev/docs/0.74/integration-with-existing-apps#configuring-gradle, see the “Configuring Gradle” section under Android)

That React Native Gradle Plugin is located at packages/react-native-gradle-plugin in the react-native repo.

TODO: I don’t know how the createBundleReleaseJsAndAssets task (listed in ./gradlew tasks --all) is being called during build. Seems that the createBundle${targetName}JsAndAssets task is defined here.

In BundleHermesCTask.kt it will run the bundle command, where the cliFile is passed from TaskConfiguration.kt and determined here in PathUtils.kt, which will normally be react-native/cli.js.

It also runs the Hermes command to emit a binary, if Hermes is enabled.

Check the Shared section to continue on what will happen afterwards.

Shared

Bundling JS and assets

Things start from react-native/cli.js, where it just forwards stuff to @react-native-community/cli - entry point is the run function in index.ts.

The available commands (such as run-ios, build-ios, run-android, etc.), including the bundle command that we care about, is loaded via the loadConfig function, which located in @react-native-community/cli-config.

The loadConfig starts its work by reading user config (react-native.config.js, or .ts) from disk, then merge it with dependencies.

The findDependencies function is used to get a list of dependencies to consider, it does it’s work by listing all dependencies and devDependencies in the RN project’s package.json. The result will be an array like this: ['react', 'react-native', '@babel/core', '@babel/preset-env', '@babel/runtime', '@react-native/babel-preset', '@react-native/eslint-config', '@react-native/metro-config', ...].

For each dependency, readDependencyConfigFromDisk is used to load the dependency config, under the hood cosmiconfig is used to search for react-native.config.js, or .ts inside the package. For example, there’s a react-native.config.js file under the react-native package, and there’s where the bundle commend is defined.

Note that if there’re duplicate commands defined, the removeDuplicateCommands function will be used to remove them.

To find the bundle command, check react-native/react-native.config.js and we’ll find out that it’s from the @react-native/community-cli-plugin package, where the source is located under packages/community-cli-plugin inside the react-native repo.

So the bundle command lives here.

Inside it, bundleImpl defaults to metroBundle.

bundleImpl.build, which is metroBundle.build, is actually packagerClient.build, where packagerClient is the server.

And server is const server = new Server(config), so the code for bundling is here.

TODO: explore internals.

Hermes

TODO


References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment