From bd60db831b60e7c38ed20b2c092a9bc99440a97d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Thu, 15 May 2025 12:08:05 +0200 Subject: [PATCH 1/4] Make "vender-hermes" command clone into the app directory --- apps/test-app/.gitignore | 2 ++ .../scripts/patch-hermes.rb | 2 +- .../src/node/cli/hermes.ts | 22 ++++++++++++------- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/apps/test-app/.gitignore b/apps/test-app/.gitignore index b0dd8f88..4550bc8f 100644 --- a/apps/test-app/.gitignore +++ b/apps/test-app/.gitignore @@ -13,3 +13,5 @@ msbuild.binlog # Ignoring the Podfile.lock as the `react-native-node-api-modules` hash updates too frequently Podfile.lock + +hermes/ diff --git a/packages/react-native-node-api-modules/scripts/patch-hermes.rb b/packages/react-native-node-api-modules/scripts/patch-hermes.rb index 93737ffc..6e200250 100644 --- a/packages/react-native-node-api-modules/scripts/patch-hermes.rb +++ b/packages/react-native-node-api-modules/scripts/patch-hermes.rb @@ -1,6 +1,6 @@ Pod::UI.warn "!!! PATCHING HERMES WITH NODE-API SUPPORT !!!" -VENDORED_HERMES_DIR ||= `npx react-native-node-api-modules vendor-hermes --silent`.strip +VENDORED_HERMES_DIR ||= `npx react-native-node-api-modules vendor-hermes --silent '#{Pod::Config.instance.installation_root}'`.strip if Dir.exist?(VENDORED_HERMES_DIR) Pod::UI.info "Hermes vendored into #{VENDORED_HERMES_DIR.inspect}" else diff --git a/packages/react-native-node-api-modules/src/node/cli/hermes.ts b/packages/react-native-node-api-modules/src/node/cli/hermes.ts index 50e184a1..5c00d203 100644 --- a/packages/react-native-node-api-modules/src/node/cli/hermes.ts +++ b/packages/react-native-node-api-modules/src/node/cli/hermes.ts @@ -1,12 +1,14 @@ +import assert from "node:assert/strict"; import fs from "node:fs"; import path from "node:path"; import { Command } from "@commander-js/extra-typings"; import { spawn, SpawnFailure } from "bufout"; import { oraPromise } from "ora"; +import { packageDirectorySync } from "pkg-dir"; + import { prettyPath } from "../path-utils"; -const HERMES_PATH = path.resolve(__dirname, "../../../hermes"); const HERMES_GIT_URL = "https://github.com/kraenhansen/hermes.git"; const HERMES_GIT_TAG = "node-api-for-react-native-0.79.0"; const REACT_NATIVE_DIR = path.dirname( @@ -14,18 +16,22 @@ const REACT_NATIVE_DIR = path.dirname( ); export const command = new Command("vendor-hermes") + .argument("[from]", "Path to a file inside the package", process.cwd()) .option("--silent", "Don't print anything except the final path", false) .option( "--force", "Don't check timestamps of input files to skip unnecessary rebuilds", false ) - .action(async ({ force, silent }) => { + .action(async (from, { force, silent }) => { try { + const packageRoot = packageDirectorySync({ cwd: from }); + assert(packageRoot, "Failed to find package root"); + const hermesPath = path.join(packageRoot, "hermes"); if (force) { - fs.rmSync(HERMES_PATH, { recursive: true, force: true }); + fs.rmSync(hermesPath, { recursive: true, force: true }); } - if (!fs.existsSync(HERMES_PATH)) { + if (!fs.existsSync(hermesPath)) { await oraPromise( spawn( "git", @@ -37,14 +43,14 @@ export const command = new Command("vendor-hermes") "--branch", HERMES_GIT_TAG, HERMES_GIT_URL, - HERMES_PATH, + hermesPath, ], { outputMode: "buffered", } ), { - text: `Cloning custom Hermes into ${prettyPath(HERMES_PATH)}`, + text: `Cloning custom Hermes into ${prettyPath(hermesPath)}`, successText: "Cloned custom Hermes", failText: (err) => `Failed to clone custom Hermes: ${err.message}`, isEnabled: !silent, @@ -52,7 +58,7 @@ export const command = new Command("vendor-hermes") ); await oraPromise( fs.promises.cp( - path.join(HERMES_PATH, "API/jsi/jsi"), + path.join(hermesPath, "API/jsi/jsi"), path.join(REACT_NATIVE_DIR, "ReactCommon/jsi/jsi/"), { recursive: true, @@ -67,7 +73,7 @@ export const command = new Command("vendor-hermes") } ); } - console.log(HERMES_PATH); + console.log(hermesPath); } catch (error) { if (error instanceof SpawnFailure) { error.flushOutput("both"); From 08e959c86f09177286a09bb811a841a82f714a2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Sat, 17 May 2025 13:35:46 +0200 Subject: [PATCH 2/4] Clean hermes using async fs --- .../src/node/cli/hermes.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/react-native-node-api-modules/src/node/cli/hermes.ts b/packages/react-native-node-api-modules/src/node/cli/hermes.ts index 5c00d203..5d5d6112 100644 --- a/packages/react-native-node-api-modules/src/node/cli/hermes.ts +++ b/packages/react-native-node-api-modules/src/node/cli/hermes.ts @@ -28,8 +28,17 @@ export const command = new Command("vendor-hermes") const packageRoot = packageDirectorySync({ cwd: from }); assert(packageRoot, "Failed to find package root"); const hermesPath = path.join(packageRoot, "hermes"); - if (force) { - fs.rmSync(hermesPath, { recursive: true, force: true }); + if (force && fs.existsSync(hermesPath)) { + await oraPromise( + fs.promises.rm(hermesPath, { recursive: true, force: true }), + { + text: "Removing existing Hermes clone", + successText: "Removed existing Hermes clone", + failText: (error) => + `Failed to remove existing Hermes clone: ${error.message}`, + isEnabled: !silent, + } + ); } if (!fs.existsSync(hermesPath)) { await oraPromise( From f9a3ea6d7477cac418506ee44cf8516bba1b65a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Sat, 17 May 2025 13:55:05 +0200 Subject: [PATCH 3/4] Make the reactNativeDir resolve from the app package root --- .../src/node/cli/hermes.ts | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/packages/react-native-node-api-modules/src/node/cli/hermes.ts b/packages/react-native-node-api-modules/src/node/cli/hermes.ts index 5d5d6112..7fe3beed 100644 --- a/packages/react-native-node-api-modules/src/node/cli/hermes.ts +++ b/packages/react-native-node-api-modules/src/node/cli/hermes.ts @@ -7,8 +7,9 @@ import { spawn, SpawnFailure } from "bufout"; import { oraPromise } from "ora"; import { packageDirectorySync } from "pkg-dir"; -import { prettyPath } from "../path-utils"; +import { getLatestMtime, prettyPath } from "../path-utils"; +const HOST_PACKAGE_ROOT = path.resolve(__dirname, "../../.."); const HERMES_GIT_URL = "https://github.com/kraenhansen/hermes.git"; const HERMES_GIT_TAG = "node-api-for-react-native-0.79.0"; const REACT_NATIVE_DIR = path.dirname( @@ -65,14 +66,28 @@ export const command = new Command("vendor-hermes") isEnabled: !silent, } ); + } + const hermesJsiPath = path.join(hermesPath, "API/jsi/jsi"); + const reactNativePath = path.dirname( + require.resolve("react-native/package.json", { + // Ensures we'll be patching the React Native package actually used by the app + paths: [appPackageRoot], + }) + ); + const reactNativeJsiPath = path.join( + reactNativePath, + "ReactCommon/jsi/jsi/" + ); + + if ( + fs.existsSync(reactNativeJsiPath) && + (force || + getLatestMtime(hermesJsiPath) > getLatestMtime(reactNativeJsiPath)) + ) { await oraPromise( - fs.promises.cp( - path.join(hermesPath, "API/jsi/jsi"), - path.join(REACT_NATIVE_DIR, "ReactCommon/jsi/jsi/"), - { - recursive: true, - } - ), + fs.promises.cp(hermesJsiPath, reactNativeJsiPath, { + recursive: true, + }), { text: `Copying JSI from Hermes to React Native`, successText: "Copied JSI from Hermes to React Native", From d761a2b48a1cbc5eb6b4c033e44ddb7a0661dbc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Sat, 17 May 2025 13:55:44 +0200 Subject: [PATCH 4/4] Make the use of app root conditional --- .../src/node/cli/hermes.ts | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/packages/react-native-node-api-modules/src/node/cli/hermes.ts b/packages/react-native-node-api-modules/src/node/cli/hermes.ts index 7fe3beed..c40fdfb8 100644 --- a/packages/react-native-node-api-modules/src/node/cli/hermes.ts +++ b/packages/react-native-node-api-modules/src/node/cli/hermes.ts @@ -12,23 +12,28 @@ import { getLatestMtime, prettyPath } from "../path-utils"; const HOST_PACKAGE_ROOT = path.resolve(__dirname, "../../.."); const HERMES_GIT_URL = "https://github.com/kraenhansen/hermes.git"; const HERMES_GIT_TAG = "node-api-for-react-native-0.79.0"; -const REACT_NATIVE_DIR = path.dirname( - require.resolve("react-native/package.json") -); export const command = new Command("vendor-hermes") - .argument("[from]", "Path to a file inside the package", process.cwd()) + .argument("[from]", "Path to a file inside the app package", process.cwd()) .option("--silent", "Don't print anything except the final path", false) .option( "--force", "Don't check timestamps of input files to skip unnecessary rebuilds", false ) - .action(async (from, { force, silent }) => { + .option( + "--use-app-root", + "Clone Hermes into the app root instead of the host package root", + false + ) + .action(async (from, { force, silent, useAppRoot }) => { try { - const packageRoot = packageDirectorySync({ cwd: from }); - assert(packageRoot, "Failed to find package root"); - const hermesPath = path.join(packageRoot, "hermes"); + const appPackageRoot = packageDirectorySync({ cwd: from }); + assert(appPackageRoot, "Failed to find package root"); + const hermesPath = path.join( + useAppRoot ? appPackageRoot : HOST_PACKAGE_ROOT, + "hermes" + ); if (force && fs.existsSync(hermesPath)) { await oraPromise( fs.promises.rm(hermesPath, { recursive: true, force: true }),