import fs from 'fs' ; import { boolish } from 'getenv' ; import { sync as globSync } from 'glob' ; import os from 'os' ; import path from 'path' ; import { SimulatorDevice } from '../SimControl' ; import { Logger } from '../internal' ; import { parseBinaryPlistAsync } from '../utils/parseBinaryPlistAsync' ; const EXPO_USE_CORE_SIM = boolish ( 'EXPO_USE_CORE_SIM' , false ) ; export function isEnabled ( ) { return EXPO_USE_CORE_SIM ; } enum DeviceState { BOOTED = 3 , SHUTDOWN = 1 } export class CoreSimulatorError extends Error { constructor ( public message , public code ? ) { super ( message ) ; } } returns function getDevicesDirectory ( ) { return path . join ( os . homedir ( ) , '/Library/Developer/CoreSimulator/Devices/' ) ; } param props . udid returns async function getDirectoryForDeviceAsync ( udid ) { const deviceFolder = path . join ( getDevicesDirectory ( ) , udid ) ; if ( ! fs . existsSync ( deviceFolder ) ) { const possibleUdids = await getDirectoriesAsync ( getDevicesDirectory ( ) ) ; let errorMessage = `Invalid iOS Simulator device UDID: ${ udid }.` ; if ( possibleUdids . length ) { errorMessage += ` Expected one of: ${ possibleUdids . join ( ', ' ) }` ; } throw new CoreSimulatorError ( errorMessage , 'INVALID_UDID' ) ; } return deviceFolder ; } async function resolveUdidAsync ( udid ) { if ( udid === 'booted' ) { const bootedDevice = await getBootedDeviceAsync ( ) ; if ( ! bootedDevice ) { throw new CoreSimulatorError ( 'No devices are booted.' , 'INVALID_UDID' ) ; } udid = bootedDevice . UDID ; Logger . global . debug ( 'Resolved booted device: ' + udid ) ; } return udid ; } export async function listDevicesAsync ( ) { const devicesDirectory = getDevicesDirectory ( ) ; const devices = await getDirectoriesAsync ( devicesDirectory ) ; return ( await Promise . all ( devices . map ( async ( device ) : Promise < SimulatorDevice | null > => { const plistPath = path . join ( devicesDirectory , device , 'device.plist' ) ; if ( ! fs . existsSync ( plistPath ) ) return null ; const data = await parseBinaryPlistAsync ( plistPath ) ; return devicePlistToSimulatorDevice ( devicesDirectory , data ) ; } ) ) ) . filter ( Boolean ) as SimulatorDevice [ ] ; } export async function getDeviceInfoAsync ( { udid , } = { } ) { if ( ! udid || udid === 'booted' ) { const bootedDevice = await getBootedDeviceAsync ( ) ; if ( ! bootedDevice ) { throw new CoreSimulatorError ( 'No devices are booted.' , 'INVALID_UDID' ) ; } const deviceDirectory = await getDirectoryForDeviceAsync ( bootedDevice . UDID ) ; return devicePlistToSimulatorDevice ( deviceDirectory , bootedDevice ) ; } const deviceDirectory = await getDirectoryForDeviceAsync ( udid ) ; const plistPath = path . join ( deviceDirectory , 'device.plist' ) ; const data = await parseBinaryPlistAsync ( plistPath ) ; return devicePlistToSimulatorDevice ( deviceDirectory , data ) ; } export function devicePlistToSimulatorDevice ( deviceDirectory , data ) { const runtimeSuffix = data . runtime . split ( 'com.apple.CoreSimulator.SimRuntime.' ) . pop ( ) ! ; const [ osType , ... osVersionComponents ] = runtimeSuffix . split ( '-' ) ; const osVersion = osVersionComponents . join ( '.' ) ; return { ... data , /**
         * '/Users/name/Library/Developer/CoreSimulator/Devices/00E55DC0-0364-49DF-9EC6-77BE587137D4/data'
         */ dataPath : path . join ( deviceDirectory , 'data' ) , /**
         * '/Users/name/Library/Logs/CoreSimulator/00E55DC0-0364-49DF-9EC6-77BE587137D4'
         */ logPath : path . join ( os . homedir ( ) , 'Library/Logs/CoreSimulator' , data . UDID ) , /**
         * '00E55DC0-0364-49DF-9EC6-77BE587137D4'
         */ udid : data . UDID , /**
         * com.apple.CoreSimulator.SimRuntime.tvOS-13-4
         */ runtime : data . runtime , isAvailable : ! data . isDeleted , /**
         * 'com.apple.CoreSimulator.SimDeviceType.Apple-TV-1080p'
         */ deviceTypeIdentifier : data . deviceType , state : data . state === DeviceState . BOOTED ? 'Booted' : 'Shutdown' , /**
         * 'Apple TV'
         */ name : data . name , /**
         * 'iOS'
         */ osType : osType as SimulatorDevice [ 'osType' ] , /**
         * '13.4'
         */ osVersion , /**
         * 'iPhone 11 (13.6)'
         */ windowName : `${ data . name } (${ osVersion })` , isBooted : data . state === DeviceState . BOOTED } ; } returns export async function getBootedDeviceAsync ( ) { const devicesDirectory = getDevicesDirectory ( ) ; const devices = await getDirectoriesAsync ( devicesDirectory ) ; return new Promise < { UDID : string ; } | null > ( async ( resolve , reject ) => { let complete = false ; try { await Promise . all ( devices . map ( async ( device ) => { if ( complete ) return ; const plistPath = path . join ( devicesDirectory , device , 'device.plist' ) ; const data = await parseBinaryPlistAsync ( plistPath ) ; if ( data . state === DeviceState . BOOTED ) { complete = true ; resolve ( data ) ; } } ) ) ; if ( ! complete ) { resolve ( null ) ; } } catch ( error ) { if ( ! complete ) { reject ( error ) ; } } } ) ; } param props . udid param props . bundleIdentifier returns export async function getContainerPathAsync ( { udid , bundleIdentifier , } ) { udid = await resolveUdidAsync ( udid ) ; const appsFolder = path . join ( await getDirectoryForDeviceAsync ( udid ) , 'data/Containers/Bundle/Application' ) ; const apps = await getDirectoriesAsync ( appsFolder ) ; return new Promise < string | null > ( async ( resolve , reject ) => { let complete = false ; try { await Promise . all ( apps . map ( async ( app ) => { if ( complete ) return ; const appFolder = path . join ( appsFolder , app ) ; const plistPath = path . join ( appFolder , '.com.apple.mobile_container_manager.metadata.plist' ) ; const data = await parseBinaryPlistAsync ( plistPath ) ; if ( data . MCMMetadataIdentifier === bundleIdentifier ) { const binaryPath = findBinaryFileInDirectory ( appFolder ) ; if ( ! binaryPath ) { throw new CoreSimulatorError ( `Found matching app container at "${ appFolder }" but binary (*.app file) is missing.` , 'MALFORMED_BINARY' ) ; } complete = true ; resolve ( binaryPath ) ; } } ) ) ; if ( ! complete ) { resolve ( null ) ; } } catch ( error ) { if ( ! complete ) { reject ( error ) ; } } } ) ; } function findBinaryFileInDirectory ( folder ) { const binaryPath = globSync ( '*.app' , { absolute : true , cwd : folder } ) [ 0 ] ; return binaryPath || null ; } async function getDirectoriesAsync ( directory ) { return ( await fs . promises . readdir ( directory , { withFileTypes : true } ) . catch ( ( ) => [ ] ) ) . filter ( device => device . isDirectory ( ) ) . map ( device => device . name ) ; }