1import { getConfig } from '@expo/config'; 2import { AndroidConfig, IOSConfig } from '@expo/config-plugins'; 3import plist from '@expo/plist'; 4import fs from 'fs'; 5import resolveFrom from 'resolve-from'; 6 7import * as Log from '../log'; 8import { 9 hasRequiredAndroidFilesAsync, 10 hasRequiredIOSFilesAsync, 11} from '../prebuild/clearNativeFolder'; 12import { intersecting } from './array'; 13 14// sort longest to ensure uniqueness. 15// this might be undesirable as it causes the QR code to be longer. 16function sortLongest(obj: string[]): string[] { 17 return obj.sort((a, b) => b.length - a.length); 18} 19 20// TODO: Revisit and test after run code is merged. 21export async function getSchemesForIosAsync(projectRoot: string) { 22 try { 23 const configPath = IOSConfig.Paths.getInfoPlistPath(projectRoot); 24 const rawPlist = fs.readFileSync(configPath, 'utf8'); 25 const plistObject = plist.parse(rawPlist); 26 return sortLongest(IOSConfig.Scheme.getSchemesFromPlist(plistObject)); 27 } catch { 28 // No ios folder or some other error 29 return []; 30 } 31} 32 33// TODO: Revisit and test after run code is merged. 34export async function getSchemesForAndroidAsync(projectRoot: string) { 35 try { 36 const configPath = await AndroidConfig.Paths.getAndroidManifestAsync(projectRoot); 37 const manifest = await AndroidConfig.Manifest.readAndroidManifestAsync(configPath); 38 return sortLongest(await AndroidConfig.Scheme.getSchemesFromManifest(manifest)); 39 } catch { 40 // No android folder or some other error 41 return []; 42 } 43} 44 45// TODO: Revisit and test after run code is merged. 46async function getManagedDevClientSchemeAsync(projectRoot: string): Promise<string | null> { 47 const { exp } = getConfig(projectRoot); 48 try { 49 const getDefaultScheme = require(resolveFrom(projectRoot, 'expo-dev-client/getDefaultScheme')); 50 const scheme = getDefaultScheme(exp); 51 return scheme; 52 } catch { 53 Log.warn( 54 '\nDevelopment build: Unable to get the default URI scheme for the project. Please make sure the expo-dev-client package is installed.' 55 ); 56 return null; 57 } 58} 59 60// TODO: Revisit and test after run code is merged. 61export async function getOptionalDevClientSchemeAsync(projectRoot: string): Promise<string | null> { 62 const [hasIos, hasAndroid] = await Promise.all([ 63 hasRequiredIOSFilesAsync(projectRoot), 64 hasRequiredAndroidFilesAsync(projectRoot), 65 ]); 66 67 const [ios, android] = await Promise.all([ 68 getSchemesForIosAsync(projectRoot), 69 getSchemesForAndroidAsync(projectRoot), 70 ]); 71 72 // Allow managed projects 73 if (!hasIos && !hasAndroid) { 74 return getManagedDevClientSchemeAsync(projectRoot); 75 } 76 77 let matching: string; 78 // Allow for only one native project to exist. 79 if (!hasIos) { 80 matching = android[0]; 81 } else if (!hasAndroid) { 82 matching = ios[0]; 83 } else { 84 [matching] = intersecting(ios, android); 85 } 86 return matching ?? null; 87} 88