xdl
Version:
The Expo Development Library
302 lines (293 loc) • 10.3 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.CoreSimulatorError = void 0;
exports.devicePlistToSimulatorDevice = devicePlistToSimulatorDevice;
exports.getBootedDeviceAsync = getBootedDeviceAsync;
exports.getContainerPathAsync = getContainerPathAsync;
exports.getDeviceInfoAsync = getDeviceInfoAsync;
exports.isEnabled = isEnabled;
exports.listDevicesAsync = listDevicesAsync;
function _fs() {
const data = _interopRequireDefault(require("fs"));
_fs = function () {
return data;
};
return data;
}
function _getenv() {
const data = require("getenv");
_getenv = function () {
return data;
};
return data;
}
function _glob() {
const data = require("glob");
_glob = function () {
return data;
};
return data;
}
function _os() {
const data = _interopRequireDefault(require("os"));
_os = function () {
return data;
};
return data;
}
function _path() {
const data = _interopRequireDefault(require("path"));
_path = function () {
return data;
};
return data;
}
function _internal() {
const data = require("../internal");
_internal = function () {
return data;
};
return data;
}
function _parseBinaryPlistAsync() {
const data = require("../utils/parseBinaryPlistAsync");
_parseBinaryPlistAsync = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// Enable this to test the JS version of simctl
const EXPO_USE_CORE_SIM = (0, _getenv().boolish)('EXPO_USE_CORE_SIM', false);
function isEnabled() {
return EXPO_USE_CORE_SIM;
}
var DeviceState;
(function (DeviceState) {
DeviceState[DeviceState["BOOTED"] = 3] = "BOOTED";
DeviceState[DeviceState["SHUTDOWN"] = 1] = "SHUTDOWN";
})(DeviceState || (DeviceState = {}));
class CoreSimulatorError extends Error {
constructor(message, code) {
super(message);
this.message = message;
this.code = code;
}
}
/**
* CoreSimulator devices folder.
*
* @returns /Users/evanbacon/Library/Developer/CoreSimulator/Devices
*/
exports.CoreSimulatorError = CoreSimulatorError;
function getDevicesDirectory() {
return _path().default.join(_os().default.homedir(), '/Library/Developer/CoreSimulator/Devices/');
}
/**
* CoreSimulator device folder, asserts when the device is invalid.
*
* @param props.udid device udid. Cannot be `booted`.
* @returns /Users/evanbacon/Library/Developer/CoreSimulator/Devices/EFEEA6EF-E3F5-4EDE-9B72-29EAFA7514AE/
*/
async function getDirectoryForDeviceAsync(udid) {
const deviceFolder = _path().default.join(getDevicesDirectory(), udid);
// Invalid udid (no matching device)
if (!_fs().default.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;
_internal().Logger.global.debug('Resolved booted device: ' + udid);
}
return udid;
}
async function listDevicesAsync() {
const devicesDirectory = getDevicesDirectory();
const devices = await getDirectoriesAsync(devicesDirectory);
return (await Promise.all(devices.map(async device => {
const plistPath = _path().default.join(devicesDirectory, device, 'device.plist');
if (!_fs().default.existsSync(plistPath)) return null;
// The plist is stored in binary format
const data = await (0, _parseBinaryPlistAsync().parseBinaryPlistAsync)(plistPath);
return devicePlistToSimulatorDevice(devicesDirectory, data);
}))).filter(Boolean);
}
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().default.join(deviceDirectory, 'device.plist');
// The plist is stored in binary format
const data = await (0, _parseBinaryPlistAsync().parseBinaryPlistAsync)(plistPath);
return devicePlistToSimulatorDevice(deviceDirectory, data);
}
function devicePlistToSimulatorDevice(deviceDirectory, data) {
const runtimeSuffix = data.runtime.split('com.apple.CoreSimulator.SimRuntime.').pop();
// Create an array [tvOS, 13, 4]
const [osType, ...osVersionComponents] = runtimeSuffix.split('-');
// Join the end components [13, 4] -> '13.4'
const osVersion = osVersionComponents.join('.');
return {
...data,
/**
* '/Users/name/Library/Developer/CoreSimulator/Devices/00E55DC0-0364-49DF-9EC6-77BE587137D4/data'
*/
dataPath: _path().default.join(deviceDirectory, 'data'),
/**
* '/Users/name/Library/Logs/CoreSimulator/00E55DC0-0364-49DF-9EC6-77BE587137D4'
*/
logPath: _path().default.join(_os().default.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,
/**
* '13.4'
*/
osVersion,
/**
* 'iPhone 11 (13.6)'
*/
windowName: `${data.name} (${osVersion})`,
// Compare state stored under `state` to 3 (booted)
isBooted: data.state === DeviceState.BOOTED
};
}
/**
* Get UDID for the first booted simulator. It's unclear if this is the exact method used by `xcrun simctl` to determine which device is "booted".
*
* @returns EFEEA6EF-E3F5-4EDE-9B72-29EAFA7514AE
*/
async function getBootedDeviceAsync() {
const devicesDirectory = getDevicesDirectory();
const devices = await getDirectoriesAsync(devicesDirectory);
// parallelize searching for the matching app
return new Promise(async (resolve, reject) => {
let complete = false;
try {
await Promise.all(devices.map(async device => {
if (complete) return;
const plistPath = _path().default.join(devicesDirectory, device, 'device.plist');
// The plist is stored in binary format
const data = await (0, _parseBinaryPlistAsync().parseBinaryPlistAsync)(plistPath);
// Compare state stored under `state` to 3 (booted)
if (data.state === DeviceState.BOOTED) {
complete = true;
resolve(data);
}
}));
if (!complete) {
resolve(null);
}
} catch (error) {
if (!complete) {
reject(error);
}
}
});
}
/**
* Returns the local path for the installed binary.app on a given Apple simulator. Returns null when the app isn't installed.
*
* This can be used as a replacement for `xcrun simctl get_app_container <udid> <bundleIdentifier>` but it's over 200x faster.
*
* @param props.udid device udid.
* @param props.bundleIdentifier bundle identifier for app
* @returns local file path to installed app binary, e.g. '/Users/evanbacon/Library/Developer/CoreSimulator/Devices/EFEEA6EF-E3F5-4EDE-9B72-29EAFA7514AE/data/Containers/Bundle/Application/FA43A0C6-C2AD-442D-B8B1-EAF3E88CF3BF/Exponent-2.21.3.tar.app'
*/
async function getContainerPathAsync({
udid,
bundleIdentifier
}) {
udid = await resolveUdidAsync(udid);
// Like: `/Users/evanbacon/Library/Developer/CoreSimulator/Devices/EFEEA6EF-E3F5-4EDE-9B72-29EAFA7514AE/data/Containers/Bundle/Application/`
// TODO: Maybe shallow glob for `.com.apple.mobile_container_manager.metadata.plist` to find apps faster
const appsFolder = _path().default.join(await getDirectoryForDeviceAsync(udid), 'data/Containers/Bundle/Application');
// Get all apps for a device
// Like: `['FA43A0C6-C2AD-442D-B8B1-EAF3E88CF3BF']`
const apps = await getDirectoriesAsync(appsFolder);
// parallelize searching for the matching app
return new Promise(async (resolve, reject) => {
let complete = false;
try {
await Promise.all(apps.map(async app => {
if (complete) return;
const appFolder = _path().default.join(appsFolder, app);
const plistPath = _path().default.join(appFolder, '.com.apple.mobile_container_manager.metadata.plist');
// The plist is stored in binary format
const data = await (0, _parseBinaryPlistAsync().parseBinaryPlistAsync)(plistPath);
// Compare bundle identifier stored under `MCMMetadataIdentifier`
if (data.MCMMetadataIdentifier === bundleIdentifier) {
// Find .app file in the app folder
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) {
// Find .app file in the app folder
const binaryPath = (0, _glob().sync)('*.app', {
absolute: true,
cwd: folder
})[0];
return binaryPath || null;
}
async function getDirectoriesAsync(directory) {
return (await _fs().default.promises.readdir(directory, {
withFileTypes: true
}).catch(() => [])).filter(device => device.isDirectory()).map(device => device.name);
}
//# sourceMappingURL=CoreSimulator.js.map
;