From 511e61a3b92b94d5f5d0382cb5cd9714bb72db3c Mon Sep 17 00:00:00 2001 From: artus9033 Date: Thu, 17 Jul 2025 12:52:12 +0200 Subject: [PATCH 1/6] chore: postinstall script to build all packages --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 1216fa69..ddc5a126 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "test": "npm run test --workspace react-native-node-api --workspace cmake-rn --workspace gyp-to-cmake --workspace node-addon-examples", "bootstrap": "npm run build && npm run bootstrap --workspaces --if-present", "prerelease": "npm run build && npm run prerelease --workspaces --if-present", - "release": "changeset publish" + "release": "changeset publish", + "postinstall": "npm run build" }, "author": { "name": "Callstack", From 9f3ed85816df7aadb0742c0c13baee949ec5c41c Mon Sep 17 00:00:00 2001 From: artus9033 Date: Thu, 17 Jul 2025 12:53:02 +0200 Subject: [PATCH 2/6] fix: properly handle silent mode in hermes.ts --- packages/host/src/node/cli/hermes.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/host/src/node/cli/hermes.ts b/packages/host/src/node/cli/hermes.ts index a7c875a5..869b2972 100644 --- a/packages/host/src/node/cli/hermes.ts +++ b/packages/host/src/node/cli/hermes.ts @@ -55,7 +55,7 @@ export const command = new Command("vendor-hermes") successText: "Removed existing Hermes clone", failText: (error) => `Failed to remove existing Hermes clone: ${error.message}`, - isEnabled: !silent, + isSilent: silent, } ); } @@ -84,7 +84,7 @@ export const command = new Command("vendor-hermes") successText: "Cloned custom Hermes", failText: (err) => `Failed to clone custom Hermes: ${err.message}`, - isEnabled: !silent, + isSilent: silent, } ); } catch (error) { @@ -117,7 +117,7 @@ export const command = new Command("vendor-hermes") successText: "Copied JSI from patched Hermes to React Native", failText: (err) => `Failed to copy JSI from Hermes to React Native: ${err.message}`, - isEnabled: !silent, + isSilent: silent, } ); console.log(hermesPath); From 4c0cc1dab3f0398bd5ceef870c03bbccaf8bf155 Mon Sep 17 00:00:00 2001 From: artus9033 Date: Thu, 17 Jul 2025 15:13:25 +0200 Subject: [PATCH 3/6] chore: fix node version on last supported (22) due to by-default-cjs script execution --- .nvmrc | 1 + 1 file changed, 1 insertion(+) create mode 100644 .nvmrc diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..2bd5a0a9 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 From 6b6284884643c3d95834d5fc218e5a60f6366b40 Mon Sep 17 00:00:00 2001 From: artus9033 Date: Thu, 17 Jul 2025 16:56:45 +0200 Subject: [PATCH 4/6] chore: simplify settings.gradle script, extend docs --- apps/test-app/android/settings.gradle | 4 +++ docs/ANDROID.md | 27 +++++++++++++++++-- docs/IOS.md | 7 +++++ package-lock.json | 2 ++ packages/host/android/consumerSettings.gradle | 22 +++++++++++++++ 5 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 docs/IOS.md create mode 100644 packages/host/android/consumerSettings.gradle diff --git a/apps/test-app/android/settings.gradle b/apps/test-app/android/settings.gradle index 04d27aae..c85805f4 100644 --- a/apps/test-app/android/settings.gradle +++ b/apps/test-app/android/settings.gradle @@ -19,3 +19,7 @@ apply(from: { throw new GradleException("Could not find `react-native-test-app`"); }()) applyTestAppSettings(settings) + +// customize the path to match your node_modules location +apply(from: "../../../node_modules/react-native-node-api/android/consumerSettings.gradle") +applyNodeAPISettings(settings) diff --git a/docs/ANDROID.md b/docs/ANDROID.md index d946cf29..4ae711ea 100644 --- a/docs/ANDROID.md +++ b/docs/ANDROID.md @@ -1,13 +1,36 @@ # Android support -## Building Hermes from source +## Android setup + +### Step 1: `settings.gradle` + +Gradle needs special handling to build React Native from source. In your app's `settings.gradle` please include the below: + +```groovy +// customize the path to match your node_modules location +apply(from: "../../../node_modules/react-native-node-api/android/consumerSettings.gradle") +applyNodeAPISettings(settings) +``` + +### Step 2: script for adjusting environment variables + +To integrate automatic setup of Hermes engine, a special env variable (`REACT_NATIVE_OVERRIDE_HERMES_DIR`) must be set to a proper path. Since Gradle does not really support loading `.env` files, this must be automated by the consumer. We provide the script `react-native-node-api vendor-hermes --silent` which will output a single line, the path to Hermes directory. + +Each time you run Android Studio, make sure this is in place. + +### How it works: building Hermes from source Because we're using a version of Hermes patched with Node-API support, we need to build React Native from source. +Alternatively, if for whatever reason you want to do it manually, you can do so by exporting this environment variable before Gradle invocation: + ``` -export REACT_NATIVE_OVERRIDE_HERMES_DIR=`npx react-native-node-api vendor-hermes --silent` +export REACT_NATIVE_OVERRIDE_HERMES_DIR="$(npx react-native-node-api vendor-hermes --silent)" ``` +> [!TIP] +> This above automatically done by our script. If you run it from postinstall, there is no need to do this manually. + ## Cleaning your React Native build folders If you've accidentally built your app without Hermes patched, you can clean things up by deleting the `ReactAndroid` build folder. diff --git a/docs/IOS.md b/docs/IOS.md new file mode 100644 index 00000000..e47c68e8 --- /dev/null +++ b/docs/IOS.md @@ -0,0 +1,7 @@ +# iOS support + +## iOS setup: script for adjusting environment variables + +To integrate automatic setup of Hermes engine, a special env variable (`REACT_NATIVE_OVERRIDE_HERMES_DIR`) must be set to a proper path. We provide the script `react-native-node-api vendor-hermes --silent` which will output a single line, the path to Hermes directory. + +Each time you run XCode, make sure this is in place. diff --git a/package-lock.json b/package-lock.json index 9774e28a..91f031e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "packages": { "": { "name": "@react-native-node-api/root", + "hasInstallScript": true, "license": "MIT", "workspaces": [ "apps/test-app", @@ -33,6 +34,7 @@ "apps/test-app": { "name": "@react-native-node-api/test-app", "version": "0.1.1", + "hasInstallScript": true, "dependencies": { "@babel/core": "^7.26.10", "@babel/preset-env": "^7.26.9", diff --git a/packages/host/android/consumerSettings.gradle b/packages/host/android/consumerSettings.gradle new file mode 100644 index 00000000..0db8f0d6 --- /dev/null +++ b/packages/host/android/consumerSettings.gradle @@ -0,0 +1,22 @@ +import org.gradle.initialization.DefaultSettings + + +ext.applyNodeAPISettings = { DefaultSettings settings -> + def searchDirectory = rootDir.toPath() + do { + def p = searchDirectory.resolve("node_modules/react-native") + if (p.toFile().exists()) { + println "[RN-NAPI] Found React Native in ${p.toRealPath().toString()}" + + includeBuild(p.toRealPath().toString()) { + dependencySubstitution { + substitute(module("com.facebook.react:react-android")).using(project(":packages:react-native:ReactAndroid")) + substitute(module("com.facebook.react:react-native")).using(project(":packages:react-native:ReactAndroid")) + substitute(module("com.facebook.react:hermes-android")).using(project(":packages:react-native:ReactAndroid:hermes-engine")) + substitute(module("com.facebook.react:hermes-engine")).using(project(":packages:react-native:ReactAndroid:hermes-engine")) + } + } + break + } + } while (searchDirectory = searchDirectory.getParent()) +} From c755b1021721c9dc9191c7833f9a3787f7559bb6 Mon Sep 17 00:00:00 2001 From: artus9033 Date: Thu, 17 Jul 2025 17:22:33 +0200 Subject: [PATCH 5/6] chore: added changeset --- .changeset/salty-mugs-judge.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/salty-mugs-judge.md diff --git a/.changeset/salty-mugs-judge.md b/.changeset/salty-mugs-judge.md new file mode 100644 index 00000000..febf9381 --- /dev/null +++ b/.changeset/salty-mugs-judge.md @@ -0,0 +1,6 @@ +--- +'@react-native-node-api/test-app': patch +'react-native-node-api': patch +--- + +Improvements to setup scripts, updated docs From 9077adf965bbff0e11729a8d3f222b562f48b12b Mon Sep 17 00:00:00 2001 From: artus9033 Date: Fri, 18 Jul 2025 10:18:58 +0200 Subject: [PATCH 6/6] refactor: changes after CR --- README.md | 4 ++++ apps/test-app/android/settings.gradle | 2 +- docs/ANDROID.md | 22 +++++++++---------- docs/IOS.md | 6 +---- package-lock.json | 2 -- package.json | 3 +-- ...merSettings.gradle => app-settings.gradle} | 2 +- 7 files changed, 18 insertions(+), 23 deletions(-) rename packages/host/android/{consumerSettings.gradle => app-settings.gradle} (88%) diff --git a/README.md b/README.md index 34af256b..550d2acd 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,10 @@ See the document on ["how it works"](./docs/HOW-IT-WORKS.md) for a detailed description of what it's like to write native modules using this package. +### Quick start + +To get started, first install the dependencies with `npm i` and then build all packages using `npm run build`. + ## Packages This mono-repository hosts the development of a few packages: diff --git a/apps/test-app/android/settings.gradle b/apps/test-app/android/settings.gradle index c85805f4..ec4aeb3e 100644 --- a/apps/test-app/android/settings.gradle +++ b/apps/test-app/android/settings.gradle @@ -21,5 +21,5 @@ apply(from: { applyTestAppSettings(settings) // customize the path to match your node_modules location -apply(from: "../../../node_modules/react-native-node-api/android/consumerSettings.gradle") +apply(from: "../../../node_modules/react-native-node-api/android/app-settings.gradle") applyNodeAPISettings(settings) diff --git a/docs/ANDROID.md b/docs/ANDROID.md index 4ae711ea..4745d1bf 100644 --- a/docs/ANDROID.md +++ b/docs/ANDROID.md @@ -1,35 +1,33 @@ # Android support -## Android setup +## Building Hermes from source ### Step 1: `settings.gradle` -Gradle needs special handling to build React Native from source. In your app's `settings.gradle` please include the below: +Gradle needs specific configuration to build React Native from source: dependency substitutions for React Native & Hermes modules, and modifications to default logic in RN build scripts, which is done by setting an environment variable, as described in this section. + +In your app's `settings.gradle` please include the below: ```groovy // customize the path to match your node_modules location -apply(from: "../../../node_modules/react-native-node-api/android/consumerSettings.gradle") +apply(from: "../../../node_modules/react-native-node-api/android/app-settings.gradle") applyNodeAPISettings(settings) ``` ### Step 2: script for adjusting environment variables -To integrate automatic setup of Hermes engine, a special env variable (`REACT_NATIVE_OVERRIDE_HERMES_DIR`) must be set to a proper path. Since Gradle does not really support loading `.env` files, this must be automated by the consumer. We provide the script `react-native-node-api vendor-hermes --silent` which will output a single line, the path to Hermes directory. - -Each time you run Android Studio, make sure this is in place. - -### How it works: building Hermes from source +> [!IMPORTANT] +> Each time you run Android Studio or build the Android app from a terminal, make sure the below is in place. -Because we're using a version of Hermes patched with Node-API support, we need to build React Native from source. +Because we're using a version of Hermes patched with Node-API support, we need to build React Native from source. A special environment variable (`REACT_NATIVE_OVERRIDE_HERMES_DIR`) must be set to the path of a Hermes engine with Node-API support. Since Gradle does not support loading `.env` files directly, this must be automated by the consumer. We provide the `react-native-node-api vendor-hermes --silent` command, which will download Hermes and output the path to Hermes directory path as its only output. -Alternatively, if for whatever reason you want to do it manually, you can do so by exporting this environment variable before Gradle invocation: +You can configure the environment variable using the following command: ``` export REACT_NATIVE_OVERRIDE_HERMES_DIR="$(npx react-native-node-api vendor-hermes --silent)" ``` -> [!TIP] -> This above automatically done by our script. If you run it from postinstall, there is no need to do this manually. +This either needs to be done each time before Gradle / Android Studio invocation, or permanently in a shell init script such as `~/.zshrc` on Zsh (MacOS) or `~/.bashrc` on Bash (Linux). ## Cleaning your React Native build folders diff --git a/docs/IOS.md b/docs/IOS.md index e47c68e8..23bce4a9 100644 --- a/docs/IOS.md +++ b/docs/IOS.md @@ -1,7 +1,3 @@ # iOS support -## iOS setup: script for adjusting environment variables - -To integrate automatic setup of Hermes engine, a special env variable (`REACT_NATIVE_OVERRIDE_HERMES_DIR`) must be set to a proper path. We provide the script `react-native-node-api vendor-hermes --silent` which will output a single line, the path to Hermes directory. - -Each time you run XCode, make sure this is in place. +Because we're using a version of Hermes patched with Node-API support, we need to build React Native from source. Special environment variables (`REACT_NATIVE_OVERRIDE_HERMES_DIR`, `BUILD_FROM_SOURCE`) must be set to the path of a Hermes engine with Node-API support. The podspec of the iOS Pod already includes instrumentation to configure React Native appropriately via [`patch-hermes.rb`](../packages/host/scripts/patch-hermes.rb). diff --git a/package-lock.json b/package-lock.json index 91f031e0..9774e28a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,7 +5,6 @@ "packages": { "": { "name": "@react-native-node-api/root", - "hasInstallScript": true, "license": "MIT", "workspaces": [ "apps/test-app", @@ -34,7 +33,6 @@ "apps/test-app": { "name": "@react-native-node-api/test-app", "version": "0.1.1", - "hasInstallScript": true, "dependencies": { "@babel/core": "^7.26.10", "@babel/preset-env": "^7.26.9", diff --git a/package.json b/package.json index ddc5a126..1216fa69 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,7 @@ "test": "npm run test --workspace react-native-node-api --workspace cmake-rn --workspace gyp-to-cmake --workspace node-addon-examples", "bootstrap": "npm run build && npm run bootstrap --workspaces --if-present", "prerelease": "npm run build && npm run prerelease --workspaces --if-present", - "release": "changeset publish", - "postinstall": "npm run build" + "release": "changeset publish" }, "author": { "name": "Callstack", diff --git a/packages/host/android/consumerSettings.gradle b/packages/host/android/app-settings.gradle similarity index 88% rename from packages/host/android/consumerSettings.gradle rename to packages/host/android/app-settings.gradle index 0db8f0d6..071727d4 100644 --- a/packages/host/android/consumerSettings.gradle +++ b/packages/host/android/app-settings.gradle @@ -6,7 +6,7 @@ ext.applyNodeAPISettings = { DefaultSettings settings -> do { def p = searchDirectory.resolve("node_modules/react-native") if (p.toFile().exists()) { - println "[RN-NAPI] Found React Native in ${p.toRealPath().toString()}" + println "[Node-API] !!! PATCHING HERMES WITH NODE-API SUPPORT !!! Found React Native in ${p.toRealPath().toString()}" includeBuild(p.toRealPath().toString()) { dependencySubstitution {