1+ import assert from "node:assert/strict" ;
12import fs from "node:fs" ;
23import path from "node:path" ;
34
45import { Command } from "@commander-js/extra-typings" ;
56import { spawn , SpawnFailure } from "bufout" ;
67import { oraPromise } from "ora" ;
7- import { prettyPath } from "../path-utils " ;
8+ import { packageDirectorySync } from "pkg-dir " ;
89
9- const HERMES_PATH = path . resolve ( __dirname , "../../../hermes" ) ;
10+ import { getLatestMtime , prettyPath } from "../path-utils" ;
11+
12+ const HOST_PACKAGE_ROOT = path . resolve ( __dirname , "../../.." ) ;
13+ // FIXME: make this configurable with reasonable fallback before public release
1014const HERMES_GIT_URL = "https://github.com/kraenhansen/hermes.git" ;
1115const HERMES_GIT_TAG = "node-api-for-react-native-0.79.0" ;
12- const REACT_NATIVE_DIR = path . dirname (
13- require . resolve ( "react-native/package.json" )
14- ) ;
1516
1617export const command = new Command ( "vendor-hermes" )
18+ . argument ( "[from]" , "Path to a file inside the app package" , process . cwd ( ) )
1719 . option ( "--silent" , "Don't print anything except the final path" , false )
1820 . option (
1921 "--force" ,
2022 "Don't check timestamps of input files to skip unnecessary rebuilds" ,
2123 false
2224 )
23- . action ( async ( { force, silent } ) => {
25+ . action ( async ( from , { force, silent } ) => {
2426 try {
25- if ( force ) {
26- fs . rmSync ( HERMES_PATH , { recursive : true , force : true } ) ;
27+ const appPackageRoot = packageDirectorySync ( { cwd : from } ) ;
28+ assert ( appPackageRoot , "Failed to find package root" ) ;
29+ const hermesPath = path . join ( HOST_PACKAGE_ROOT , "hermes" ) ;
30+ if ( force && fs . existsSync ( hermesPath ) ) {
31+ await oraPromise (
32+ fs . promises . rm ( hermesPath , { recursive : true , force : true } ) ,
33+ {
34+ text : "Removing existing Hermes clone" ,
35+ successText : "Removed existing Hermes clone" ,
36+ failText : ( error ) =>
37+ `Failed to remove existing Hermes clone: ${ error . message } ` ,
38+ isEnabled : ! silent ,
39+ }
40+ ) ;
2741 }
28- if ( ! fs . existsSync ( HERMES_PATH ) ) {
42+ if ( ! fs . existsSync ( hermesPath ) ) {
2943 await oraPromise (
3044 spawn (
3145 "git" ,
@@ -37,27 +51,41 @@ export const command = new Command("vendor-hermes")
3751 "--branch" ,
3852 HERMES_GIT_TAG ,
3953 HERMES_GIT_URL ,
40- HERMES_PATH ,
54+ hermesPath ,
4155 ] ,
4256 {
4357 outputMode : "buffered" ,
4458 }
4559 ) ,
4660 {
47- text : `Cloning custom Hermes into ${ prettyPath ( HERMES_PATH ) } ` ,
61+ text : `Cloning custom Hermes into ${ prettyPath ( hermesPath ) } ` ,
4862 successText : "Cloned custom Hermes" ,
4963 failText : ( err ) => `Failed to clone custom Hermes: ${ err . message } ` ,
5064 isEnabled : ! silent ,
5165 }
5266 ) ;
67+ }
68+ const hermesJsiPath = path . join ( hermesPath , "API/jsi/jsi" ) ;
69+ const reactNativePath = path . dirname (
70+ require . resolve ( "react-native/package.json" , {
71+ // Ensures we'll be patching the React Native package actually used by the app
72+ paths : [ appPackageRoot ] ,
73+ } )
74+ ) ;
75+ const reactNativeJsiPath = path . join (
76+ reactNativePath ,
77+ "ReactCommon/jsi/jsi/"
78+ ) ;
79+
80+ if (
81+ fs . existsSync ( reactNativeJsiPath ) &&
82+ ( force ||
83+ getLatestMtime ( hermesJsiPath ) > getLatestMtime ( reactNativeJsiPath ) )
84+ ) {
5385 await oraPromise (
54- fs . promises . cp (
55- path . join ( HERMES_PATH , "API/jsi/jsi" ) ,
56- path . join ( REACT_NATIVE_DIR , "ReactCommon/jsi/jsi/" ) ,
57- {
58- recursive : true ,
59- }
60- ) ,
86+ fs . promises . cp ( hermesJsiPath , reactNativeJsiPath , {
87+ recursive : true ,
88+ } ) ,
6189 {
6290 text : `Copying JSI from Hermes to React Native` ,
6391 successText : "Copied JSI from Hermes to React Native" ,
@@ -67,7 +95,7 @@ export const command = new Command("vendor-hermes")
6795 }
6896 ) ;
6997 }
70- console . log ( HERMES_PATH ) ;
98+ console . log ( hermesPath ) ;
7199 } catch ( error ) {
72100 if ( error instanceof SpawnFailure ) {
73101 error . flushOutput ( "both" ) ;
0 commit comments