diff --git a/.changeset/chatty-states-build.md b/.changeset/chatty-states-build.md new file mode 100644 index 00000000..1be4d07f --- /dev/null +++ b/.changeset/chatty-states-build.md @@ -0,0 +1,5 @@ +--- +"react-native-node-api": patch +--- + +Add --react-native-package option to "vendor-hermes" command, allowing caller to choose the package to download hermes into diff --git a/packages/host/scripts/patch-hermes.rb b/packages/host/scripts/patch-hermes.rb index 9e1faaf5..a6cb11f7 100644 --- a/packages/host/scripts/patch-hermes.rb +++ b/packages/host/scripts/patch-hermes.rb @@ -4,13 +4,20 @@ raise "React Native Node-API cannot reliably patch JSI when React Native Core is prebuilt." end -VENDORED_HERMES_DIR ||= `npx react-native-node-api 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 - raise "Hermes patching failed. Please check the output above for errors." +if ENV['REACT_NATIVE_OVERRIDE_HERMES_DIR'].nil? + VENDORED_HERMES_DIR ||= `npx react-native-node-api vendor-hermes --silent '#{Pod::Config.instance.installation_root}'`.strip + # Signal the patched Hermes to React Native + ENV['BUILD_FROM_SOURCE'] = 'true' + ENV['REACT_NATIVE_OVERRIDE_HERMES_DIR'] = VENDORED_HERMES_DIR +elsif Dir.exist?(ENV['REACT_NATIVE_OVERRIDE_HERMES_DIR']) + # Setting an override path implies building from source + ENV['BUILD_FROM_SOURCE'] = 'true' end -# Signal the patched Hermes to React Native -ENV['BUILD_FROM_SOURCE'] = 'true' -ENV['REACT_NATIVE_OVERRIDE_HERMES_DIR'] = VENDORED_HERMES_DIR +if !ENV['REACT_NATIVE_OVERRIDE_HERMES_DIR'].empty? + if Dir.exist?(ENV['REACT_NATIVE_OVERRIDE_HERMES_DIR']) + Pod::UI.info "[Node-API] Using overridden Hermes in #{ENV['REACT_NATIVE_OVERRIDE_HERMES_DIR'].inspect}" + else + raise "Hermes patching failed: Expected override to exist in #{ENV['REACT_NATIVE_OVERRIDE_HERMES_DIR'].inspect}" + end +end diff --git a/packages/host/src/node/cli/hermes.ts b/packages/host/src/node/cli/hermes.ts index 541a324e..6bd3daa3 100644 --- a/packages/host/src/node/cli/hermes.ts +++ b/packages/host/src/node/cli/hermes.ts @@ -5,18 +5,24 @@ import path from "node:path"; import { chalk, Command, + Option, oraPromise, spawn, UsageError, wrapAction, prettyPath, } from "@react-native-node-api/cli-utils"; -import { packageDirectorySync } from "pkg-dir"; +import { packageDirectory } from "pkg-dir"; +import { readPackage } from "read-pkg"; -const HOST_PACKAGE_ROOT = path.resolve(__dirname, "../../.."); // FIXME: make this configurable with reasonable fallback before public release const HERMES_GIT_URL = "https://github.com/kraenhansen/hermes.git"; +const platformOption = new Option( + "--react-native-package ", + "The React Native package to vendor Hermes into", +).default("react-native"); + export const command = new Command("vendor-hermes") .argument("[from]", "Path to a file inside the app package", process.cwd()) .option("--silent", "Don't print anything except the final path", false) @@ -25,12 +31,20 @@ export const command = new Command("vendor-hermes") "Don't check timestamps of input files to skip unnecessary rebuilds", false, ) + .addOption(platformOption) .action( - wrapAction(async (from, { force, silent }) => { - const appPackageRoot = packageDirectorySync({ cwd: from }); + wrapAction(async (from, { force, silent, reactNativePackage }) => { + const appPackageRoot = await packageDirectory({ cwd: from }); assert(appPackageRoot, "Failed to find package root"); + + const { dependencies = {} } = await readPackage({ cwd: appPackageRoot }); + assert( + Object.keys(dependencies).includes(reactNativePackage), + `Expected app to have a dependency on the '${reactNativePackage}' package`, + ); + const reactNativePath = path.dirname( - require.resolve("react-native/package.json", { + require.resolve(reactNativePackage + "/package.json", { // Ensures we'll be patching the React Native package actually used by the app paths: [appPackageRoot], }), @@ -40,6 +54,11 @@ export const command = new Command("vendor-hermes") "sdks", ".hermesversion", ); + assert( + fs.existsSync(hermesVersionPath), + `Expected a file with a Hermes version at ${prettyPath(hermesVersionPath)}`, + ); + const hermesVersion = fs.readFileSync(hermesVersionPath, "utf8").trim(); if (!silent) { console.log(`Using Hermes version: ${hermesVersion}`); @@ -50,7 +69,7 @@ export const command = new Command("vendor-hermes") "ReactCommon/jsi/jsi/", ); - const hermesPath = path.join(HOST_PACKAGE_ROOT, "hermes"); + const hermesPath = path.join(reactNativePath, "sdks", "node-api-hermes"); if (force && fs.existsSync(hermesPath)) { await oraPromise( fs.promises.rm(hermesPath, { recursive: true, force: true }),