@@ -7,15 +7,17 @@ import { createHash } from 'crypto';
7
7
import {
8
8
red , green , yellow , bold ,
9
9
} from 'chalk' ;
10
- import { execSync } from 'child_process' ;
10
+ import { exec } from 'child_process' ;
11
11
import * as Path from 'node:path' ;
12
+ import * as util from 'util' ;
12
13
import stableStringify from 'fast-safe-stringify' ;
13
- import { readFileSync } from 'fs' ;
14
14
15
15
import '@total-typescript/ts-reset' ;
16
16
17
17
import type { ExpoConfig , Android , IOS } from '@expo/config-types' ;
18
18
19
+ const execAsync = util . promisify ( exec ) ;
20
+
19
21
export type Module = {
20
22
name : string ,
21
23
path : string ,
@@ -86,15 +88,26 @@ export const hashFiles = async (rootDir: string, relativeFilePaths: string[], ve
86
88
return hashIt ( nativeFilesHashes . join ( ',' ) ) ;
87
89
} ;
88
90
89
- export const isGitDirty = ( rootDir : string ) => {
90
- const gitDiffOutput = execSync ( 'git diff HEAD' , { cwd : rootDir , encoding : 'utf8' } ) . toString ( ) ;
91
+ export const isGitDirty = async ( rootDir : string ) => {
92
+ const gitDiffOutput = ( await execAsync ( 'git diff HEAD' , { cwd : rootDir , encoding : 'utf8' } ) ) . stdout ;
91
93
return gitDiffOutput . length > 0 ;
92
94
} ;
93
95
94
96
export const getFolderHash = async ( platform : Platform , rootDir : string , verbose = false ) => {
95
- const gitFiles = execSync ( 'git ls-tree -r HEAD --name-only' , { cwd : rootDir , encoding : 'utf8' , env : process . env } ) ;
97
+ const hasAndroidOrIOSFolders = await hasNativeVersion ( platform , rootDir ) ;
98
+
99
+ // if there are no native folders, we don't need to hash anything
100
+ if ( ! hasAndroidOrIOSFolders ) {
101
+ return '' ;
102
+ }
103
+
104
+ const gitFiles = await execAsync ( 'git ls-tree -r HEAD --name-only' , {
105
+ cwd : rootDir ,
106
+ encoding : 'utf8' ,
107
+ env : process . env ,
108
+ } ) ;
96
109
97
- const nativeFiles = gitFiles
110
+ const nativeFiles = gitFiles . stdout
98
111
. split ( '\n' )
99
112
. filter ( ( f ) => {
100
113
const includeBecauseIos = platform !== Platform . android && ( f . startsWith ( 'ios' ) || f . endsWith ( '.podspec' ) ) ;
@@ -107,9 +120,9 @@ export const getFolderHash = async (platform: Platform, rootDir: string, verbose
107
120
} ;
108
121
109
122
export const getAppPluginHash = async ( rootDir : string , verbose = false ) => {
110
- const gitFiles = execSync ( 'git ls-tree -r HEAD --name-only' , { cwd : rootDir , encoding : 'utf8' , env : process . env } ) ;
123
+ const gitFiles = await execAsync ( 'git ls-tree -r HEAD --name-only' , { cwd : rootDir , encoding : 'utf8' , env : process . env } ) ;
111
124
112
- const nativeFiles = gitFiles
125
+ const nativeFiles = gitFiles . stdout
113
126
. split ( '\n' )
114
127
. filter ( ( f ) => {
115
128
const includeBecauseAppPlugin = f . endsWith ( 'plugin.js' ) || f . endsWith ( 'plugin.ts' ) ;
@@ -212,9 +225,9 @@ export const getModules = async (rootDir = '.') => {
212
225
}
213
226
} ;
214
227
215
- export const readExpoConfig = ( rootDir = '.' ) => {
216
- const appJsonStr = execSync ( 'npx expo config --json --full --type prebuild' , { cwd : rootDir , encoding : 'utf-8' , env : process . env } ) ;
217
- const appJson = JSON . parse ( appJsonStr ) as { exp : ExpoConfig } ;
228
+ export const readExpoConfig = async ( rootDir = '.' ) => {
229
+ const appJsonStr = await execAsync ( 'npx expo config --json --full --type prebuild' , { cwd : rootDir , encoding : 'utf-8' , env : process . env } ) ;
230
+ const appJson = JSON . parse ( appJsonStr . stdout ) as { exp : ExpoConfig } ;
218
231
return appJson . exp ;
219
232
} ;
220
233
@@ -255,11 +268,11 @@ const expoPropsToHash: Partial<Record<keyof ExpoConfig, boolean>> = {
255
268
jsEngine : true ,
256
269
} ;
257
270
258
- const getAppJsonHash = (platform = Platform.all, rootDir = '.', verbose = false) => {
271
+ const getAppJsonHash = async (platform = Platform.all, rootDir = '.', verbose = false) => {
259
272
let appJsonContent = '' ;
260
273
261
274
try {
262
- const appJson = readExpoConfig ( rootDir ) ;
275
+ const appJson = await readExpoConfig ( rootDir ) ;
263
276
264
277
Object . keys ( appJson || { } ) . forEach ( ( key ) => {
265
278
if ( ! expoPropsToHash [ key ] ) {
@@ -286,12 +299,14 @@ const getAppJsonHash = (platform = Platform.all, rootDir = '.', verbose = false)
286
299
delete appJson . ios ?. [ key ] ;
287
300
} else if ( key === 'entitlements' ) {
288
301
Object . keys ( appJson . ios ?. entitlements || { } ) . forEach ( ( entitlementKey ) => {
289
- if ( appJson . ios ?. entitlements ?. [ entitlementKey ] ) {
302
+ // eslint-disable-next-line max-len
303
+ const entitlementValue = appJson . ios ?. entitlements ?. [ entitlementKey ] as string | undefined ;
304
+ if ( entitlementValue && typeof entitlementValue === 'string' && bundleIdentifier && appJson ?. ios ?. entitlements ) {
290
305
// some entitlements have the bundleIdentifier in them,
291
306
// lets ignore it to avoid unnecessary rebuilds
292
307
// eslint-disable-next-line max-len
293
308
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
294
- appJson . ios . entitlements [ entitlementKey ] = appJson . ios . entitlements [ entitlementKey ] . replace ( bundleIdentifier , '' ) ;
309
+ appJson . ios . entitlements [ entitlementKey ] = entitlementValue . replace ( bundleIdentifier , '' ) ;
295
310
}
296
311
} ) ;
297
312
}
@@ -332,7 +347,7 @@ export const getCurrentHash = async (platform: Platform, {
332
347
} : GenerateHashOptions = GENERATE_HASH_DEFAULTS) => {
333
348
const localNativeFoldersHash = await getFolderHash ( platform , rootDir , verbose ) ;
334
349
335
- const appJsonContent = skipAppJson ? '' : getAppJsonHash ( platform , rootDir , verbose ) ;
350
+ const appJsonContent = skipAppJson ? '' : await getAppJsonHash ( platform , rootDir , verbose ) ;
336
351
337
352
const nativeModules = skipNodeModules ? [ ] : await getModulesForPlatform ( platform , rootDir ) ;
338
353
@@ -380,15 +395,15 @@ export async function verifyExpoApp(
380
395
rootDir : string ;
381
396
} ,
382
397
) {
383
- if ( verbose ) { console . info ( `getting depenency hash for native dependencies in: ${ rootDir } ` ) ; }
398
+ if ( verbose ) { console . info ( `getting dependency hash for native dependencies in: ${ rootDir } ` ) ; }
384
399
385
400
const { ios , android , all } = await generateHashes({ rootDir , verbose } );
386
401
387
402
let valueExists = false;
388
403
let hasChanged = false;
389
404
390
405
try {
391
- const expoConfig = readExpoConfig ( rootDir ) ;
406
+ const expoConfig = await readExpoConfig ( rootDir ) ;
392
407
393
408
if ( expoConfig . runtimeVersion ) {
394
409
valueExists = true ;
@@ -441,7 +456,7 @@ export async function updateExpoApp(
441
456
const { ios, android, all } = await generateHashes ( { rootDir, verbose } ) ;
442
457
443
458
try {
444
- const fileStr = readFileSync ( Path . join ( rootDir , 'app.json' ) , 'utf8' ) ;
459
+ const fileStr = await readFile ( Path . join ( rootDir , 'app.json' ) , 'utf8' ) ;
445
460
const prevJson = JSON . parse ( fileStr ) as { expo : ExpoConfig } ;
446
461
447
462
prevJson . expo . runtimeVersion = all ;
@@ -465,7 +480,7 @@ export async function verifyLibrary(
465
480
rootDir : string ;
466
481
} ,
467
482
) {
468
- if ( verbose ) { console . info ( `getting depenency hash for native dependencies in: ${ rootDir } ` ) ; }
483
+ if ( verbose ) { console . info ( `getting dependency hash for native dependencies in: ${ rootDir } ` ) ; }
469
484
470
485
const { ios , android , all } = await generateHashes ( {
471
486
rootDir, verbose, skipNodeModules : true , skipAppJson : true ,
0 commit comments