@@ -6,24 +6,20 @@ import { coerce, compare, valid } from "semver";
66import { fileDoesNotExistOrIsDirectory } from "./utils/file-utils" ;
77
88const g2js = require ( "gradle-to-js/lib/parser" ) ;
9+ const REACT_NATIVE_HERMES_DEFAULT_VERSION = "0.70.0" ;
910
1011export function isValidVersion ( version : string ) : boolean {
1112 return ! ! valid ( version ) || / ^ \d + \. \d + $ / . test ( version ) ;
1213}
1314
1415export async function runHermesEmitBinaryCommand (
15- bundleName : string ,
16- outputFolder : string ,
17- sourcemapOutput : string ,
18- extraHermesFlags : string [ ] ,
19- gradleFile : string
16+ bundleName : string ,
17+ outputFolder : string ,
18+ sourcemapOutput : string ,
19+ extraHermesFlags : string [ ] ,
20+ gradleFile : string
2021) : Promise < void > {
2122 const hermesArgs : string [ ] = [ ] ;
22- const envNodeArgs : string = process . env . CODE_PUSH_NODE_ARGS ;
23-
24- if ( typeof envNodeArgs !== "undefined" ) {
25- Array . prototype . push . apply ( hermesArgs , envNodeArgs . trim ( ) . split ( / \s + / ) ) ;
26- }
2723
2824 Array . prototype . push . apply ( hermesArgs , [
2925 "-emit-binary" ,
@@ -43,6 +39,8 @@ export async function runHermesEmitBinaryCommand(
4339 console . log ( `${ hermesCommand } ${ hermesArgs . join ( " " ) } ` ) ;
4440
4541 return new Promise < void > ( ( resolve , reject ) => {
42+ let hermesProcessError : Error = null ;
43+
4644 hermesProcess . stdout . on ( "data" , ( data : Buffer ) => {
4745 console . log ( data . toString ( ) . trim ( ) ) ;
4846 } ) ;
@@ -51,23 +49,38 @@ export async function runHermesEmitBinaryCommand(
5149 console . error ( data . toString ( ) . trim ( ) ) ;
5250 } ) ;
5351
52+ hermesProcess . on ( "error" , ( err : Error ) => {
53+ hermesProcessError = err ;
54+ reject ( new Error ( `Failed to run Hermes compiler "${ hermesCommand } ": ${ err . message } ` ) ) ;
55+ } ) ;
56+
5457 hermesProcess . on ( "close" , ( exitCode : number , signal : string ) => {
58+ if ( hermesProcessError ) {
59+ return ;
60+ }
61+
5562 if ( exitCode !== 0 ) {
5663 reject ( new Error ( `"hermes" command failed (exitCode=${ exitCode } , signal=${ signal } ).` ) ) ;
64+ return ;
5765 }
66+
5867 // Copy HBC bundle to overwrite JS bundle
5968 const source = path . join ( outputFolder , bundleName + ".hbc" ) ;
6069 const destination = path . join ( outputFolder , bundleName ) ;
6170 fs . copyFile ( source , destination , ( err ) => {
6271 if ( err ) {
6372 console . error ( err ) ;
6473 reject ( new Error ( `Copying file ${ source } to ${ destination } failed. "hermes" previously exited with code ${ exitCode } .` ) ) ;
74+ return ;
6575 }
76+
6677 fs . unlink ( source , ( err ) => {
6778 if ( err ) {
6879 console . error ( err ) ;
6980 reject ( err ) ;
81+ return ;
7082 }
83+
7184 resolve ( null as void ) ;
7285 } ) ;
7386 } ) ;
@@ -125,11 +138,8 @@ export async function runHermesEmitBinaryCommand(
125138}
126139
127140function parseBuildGradleFile ( gradleFile : string ) {
128- let buildGradlePath : string = path . join ( "android" , "app" ) ;
129- if ( gradleFile ) {
130- buildGradlePath = gradleFile ;
131- }
132- if ( fs . lstatSync ( buildGradlePath ) . isDirectory ( ) ) {
141+ let buildGradlePath : string = getBuildGradlePath ( gradleFile ) ;
142+ if ( fs . existsSync ( buildGradlePath ) && fs . lstatSync ( buildGradlePath ) . isDirectory ( ) ) {
133143 buildGradlePath = path . join ( buildGradlePath , "build.gradle" ) ;
134144 }
135145
@@ -142,6 +152,19 @@ function parseBuildGradleFile(gradleFile: string) {
142152 } ) ;
143153}
144154
155+ function getBuildGradlePath ( gradleFile : string ) : string {
156+ let buildGradlePath : string = path . join ( "android" , "app" ) ;
157+ if ( gradleFile ) {
158+ buildGradlePath = gradleFile ;
159+ }
160+
161+ if ( fs . existsSync ( buildGradlePath ) && fs . lstatSync ( buildGradlePath ) . isDirectory ( ) ) {
162+ return path . join ( buildGradlePath , "build.gradle" ) ;
163+ }
164+
165+ return buildGradlePath ;
166+ }
167+
145168async function getHermesCommandFromGradle ( gradleFile : string ) : Promise < string > {
146169 const buildGradle : any = await parseBuildGradleFile ( gradleFile ) ;
147170 const hermesCommandProperty : any = Array . from ( buildGradle [ "project.ext.react" ] || [ ] ) . find ( ( prop : string ) =>
@@ -154,10 +177,43 @@ async function getHermesCommandFromGradle(gradleFile: string): Promise<string> {
154177 }
155178}
156179
157- export function getAndroidHermesEnabled ( gradleFile : string ) : boolean {
158- return parseBuildGradleFile ( gradleFile ) . then ( ( buildGradle : any ) => {
159- return Array . from ( buildGradle [ "project.ext.react" ] || [ ] ) . some ( ( line : string ) => / ^ e n a b l e H e r m e s \s { 0 , } : \s { 0 , } t r u e / . test ( line ) ) ;
160- } ) ;
180+ function getLegacyAndroidHermesEnabled ( buildGradle : any ) : boolean | null {
181+ const hermesEnabledProperty = Array . from ( buildGradle [ "project.ext.react" ] || [ ] ) . find ( ( line : string ) =>
182+ / ^ e n a b l e H e r m e s \s * : \s * ( t r u e | f a l s e ) \b / . test ( line )
183+ ) as string ;
184+
185+ if ( hermesEnabledProperty ) {
186+ return / : \s * t r u e \b / . test ( hermesEnabledProperty ) ;
187+ }
188+
189+ return null ;
190+ }
191+
192+ export async function getAndroidHermesEnabled ( gradleFile : string ) : Promise < boolean > {
193+ const gradlePropertiesPath = path . join ( "android" , "gradle.properties" ) ;
194+ if ( fs . existsSync ( gradlePropertiesPath ) ) {
195+ const gradlePropertiesContents = fs . readFileSync ( gradlePropertiesPath ) . toString ( ) ;
196+ const hermesEnabledMatch = gradlePropertiesContents . match ( / ^ \s * h e r m e s E n a b l e d \s * = \s * ( t r u e | f a l s e ) \s * $ / im) ;
197+ if ( hermesEnabledMatch ) {
198+ return hermesEnabledMatch [ 1 ] . toLowerCase ( ) === "true" ;
199+ }
200+ }
201+
202+ const buildGradlePath = getBuildGradlePath ( gradleFile ) ;
203+ if ( fs . existsSync ( buildGradlePath ) && fs . readFileSync ( buildGradlePath ) . toString ( ) . indexOf ( "enableHermes" ) !== - 1 ) {
204+ const buildGradle : any = await parseBuildGradleFile ( gradleFile ) ;
205+ const legacyHermesEnabled = getLegacyAndroidHermesEnabled ( buildGradle ) ;
206+ if ( legacyHermesEnabled !== null ) {
207+ return legacyHermesEnabled ;
208+ }
209+ }
210+
211+ const reactNativeVersion = coerce ( getReactNativeVersion ( ) ) ;
212+ if ( reactNativeVersion && compare ( reactNativeVersion . version , REACT_NATIVE_HERMES_DEFAULT_VERSION ) >= 0 ) {
213+ return true ;
214+ }
215+
216+ return false ;
161217}
162218
163219export function getiOSHermesEnabled ( podFile : string ) : boolean {
@@ -166,12 +222,26 @@ export function getiOSHermesEnabled(podFile: string): boolean {
166222 podPath = podFile ;
167223 }
168224 if ( fileDoesNotExistOrIsDirectory ( podPath ) ) {
169- throw new Error ( `Unable to find Podfile file "${ podPath } ".` ) ;
225+ if ( podFile ) {
226+ throw new Error ( `Unable to find Podfile file "${ podPath } ".` ) ;
227+ }
228+
229+ const reactNativeVersion = coerce ( getReactNativeVersion ( ) ) ;
230+ return ! ! reactNativeVersion && compare ( reactNativeVersion . version , REACT_NATIVE_HERMES_DEFAULT_VERSION ) >= 0 ;
170231 }
171232
172233 try {
173234 const podFileContents = fs . readFileSync ( podPath ) . toString ( ) ;
174- return / ( [ ^ # \n ] * : ? h e r m e s _ e n a b l e d ( \s + | \n + ) ? ( = > | : ) ( \s + | \n + ) ? t r u e ) / . test ( podFileContents ) ;
235+ if ( / ^ [ ^ # \n ] * : ? \b h e r m e s _ e n a b l e d \b \s * ( = > | : ) \s * f a l s e \b / im. test ( podFileContents ) ) {
236+ return false ;
237+ }
238+
239+ if ( / ^ [ ^ # \n ] * : ? \b h e r m e s _ e n a b l e d \b \s * ( = > | : ) \s * t r u e \b / im. test ( podFileContents ) ) {
240+ return true ;
241+ }
242+
243+ const reactNativeVersion = coerce ( getReactNativeVersion ( ) ) ;
244+ return ! ! reactNativeVersion && compare ( reactNativeVersion . version , REACT_NATIVE_HERMES_DEFAULT_VERSION ) >= 0 ;
175245 } catch ( error ) {
176246 throw error ;
177247 }
@@ -192,7 +262,8 @@ function getHermesOSBin(): string {
192262}
193263
194264function getHermesOSExe ( ) : string {
195- const react63orAbove = compare ( coerce ( getReactNativeVersion ( ) ) . version , "0.63.0" ) !== - 1 ;
265+ const reactNativeVersion = coerce ( getReactNativeVersion ( ) ) ;
266+ const react63orAbove = ! ! reactNativeVersion && compare ( reactNativeVersion . version , "0.63.0" ) !== - 1 ;
196267 const hermesExecutableName = react63orAbove ? "hermesc" : "hermes" ;
197268 switch ( process . platform ) {
198269 case "win32" :
@@ -210,23 +281,34 @@ async function getHermesCommand(gradleFile: string): Promise<string> {
210281 return false ;
211282 }
212283 } ;
284+
285+ const buildGradlePath = getBuildGradlePath ( gradleFile ) ;
286+
287+ if ( fs . existsSync ( buildGradlePath ) && fs . readFileSync ( buildGradlePath ) . toString ( ) . indexOf ( "hermesCommand" ) !== - 1 ) {
288+ const gradleHermesCommand = await getHermesCommandFromGradle ( gradleFile ) ;
289+ if ( gradleHermesCommand ) {
290+ return path . join ( "android" , "app" , gradleHermesCommand . replace ( "%OS-BIN%" , getHermesOSBin ( ) ) ) ;
291+ }
292+ }
293+
294+ const hermesCompilerExe = process . platform === "win32" ? "hermesc.exe" : "hermesc" ;
295+ const hermesCompiler = path . join ( "node_modules" , "hermes-compiler" , "hermesc" , getHermesOSBin ( ) , hermesCompilerExe ) ;
296+ if ( fileExists ( hermesCompiler ) ) {
297+ return hermesCompiler ;
298+ }
299+
213300 // Hermes is bundled with react-native since 0.69
214301 const bundledHermesEngine = path . join ( getReactNativePackagePath ( ) , "sdks" , "hermesc" , getHermesOSBin ( ) , getHermesOSExe ( ) ) ;
215302 if ( fileExists ( bundledHermesEngine ) ) {
216303 return bundledHermesEngine ;
217304 }
218305
219- const gradleHermesCommand = await getHermesCommandFromGradle ( gradleFile ) ;
220- if ( gradleHermesCommand ) {
221- return path . join ( "android" , "app" , gradleHermesCommand . replace ( "%OS-BIN%" , getHermesOSBin ( ) ) ) ;
222- } else {
223- // assume if hermes-engine exists it should be used instead of hermesvm
224- const hermesEngine = path . join ( "node_modules" , "hermes-engine" , getHermesOSBin ( ) , getHermesOSExe ( ) ) ;
225- if ( fileExists ( hermesEngine ) ) {
226- return hermesEngine ;
227- }
228- return path . join ( "node_modules" , "hermesvm" , getHermesOSBin ( ) , "hermes" ) ;
306+ // assume if hermes-engine exists it should be used instead of hermesvm
307+ const hermesEngine = path . join ( "node_modules" , "hermes-engine" , getHermesOSBin ( ) , getHermesOSExe ( ) ) ;
308+ if ( fileExists ( hermesEngine ) ) {
309+ return hermesEngine ;
229310 }
311+ return path . join ( "node_modules" , "hermesvm" , getHermesOSBin ( ) , "hermes" ) ;
230312}
231313
232314function getComposeSourceMapsPath ( ) : string {
@@ -240,7 +322,7 @@ function getComposeSourceMapsPath(): string {
240322
241323function getReactNativePackagePath ( ) : string {
242324 const result = childProcess . spawnSync ( "node" , [ "--print" , "require.resolve('react-native/package.json')" ] ) ;
243- const packagePath = path . dirname ( result . stdout . toString ( ) ) ;
325+ const packagePath = path . dirname ( result . stdout . toString ( ) . trim ( ) ) ;
244326 if ( result . status === 0 && directoryExistsSync ( packagePath ) ) {
245327 return packagePath ;
246328 }
@@ -276,8 +358,18 @@ export function getReactNativeVersion(): string {
276358 throw new Error ( `The "package.json" file in the CWD does not have the "name" field set.` ) ;
277359 }
278360
361+ try {
362+ const reactNativePackageJsonFilename = path . join ( getReactNativePackagePath ( ) , "package.json" ) ;
363+ const reactNativePackageJson = JSON . parse ( fs . readFileSync ( reactNativePackageJsonFilename , "utf-8" ) ) ;
364+ if ( reactNativePackageJson . version ) {
365+ return reactNativePackageJson . version ;
366+ }
367+ } catch ( error ) {
368+ // Fall back to the app package.json dependency range below.
369+ }
370+
279371 return (
280372 ( projectPackageJson . dependencies && projectPackageJson . dependencies [ "react-native" ] ) ||
281373 ( projectPackageJson . devDependencies && projectPackageJson . devDependencies [ "react-native" ] )
282374 ) ;
283- }
375+ }
0 commit comments