UNPKG

@expo/xdl

Version:
1,110 lines (874 loc) 29.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isPlatformSupported = isPlatformSupported; exports.ensureXcodeInstalledAsync = ensureXcodeInstalledAsync; exports.isSimulatorInstalledAsync = isSimulatorInstalledAsync; exports.ensureSimulatorOpenAsync = ensureSimulatorOpenAsync; exports.isSimulatorBootedAsync = isSimulatorBootedAsync; exports.activateSimulatorWindowAsync = activateSimulatorWindowAsync; exports.closeSimulatorAppAsync = closeSimulatorAppAsync; exports.isExpoClientInstalledOnSimulatorAsync = isExpoClientInstalledOnSimulatorAsync; exports.waitForExpoClientInstalledOnSimulatorAsync = waitForExpoClientInstalledOnSimulatorAsync; exports.waitForExpoClientUninstalledOnSimulatorAsync = waitForExpoClientUninstalledOnSimulatorAsync; exports.expoVersionOnSimulatorAsync = expoVersionOnSimulatorAsync; exports.doesExpoClientNeedUpdatedAsync = doesExpoClientNeedUpdatedAsync; exports._downloadSimulatorAppAsync = _downloadSimulatorAppAsync; exports.installExpoOnSimulatorAsync = installExpoOnSimulatorAsync; exports.uninstallExpoAppFromSimulatorAsync = uninstallExpoAppFromSimulatorAsync; exports.upgradeExpoAsync = upgradeExpoAsync; exports.openProjectAsync = openProjectAsync; exports.openWebProjectAsync = openWebProjectAsync; exports.sortDefaultDeviceToBeginningAsync = sortDefaultDeviceToBeginningAsync; exports.promptForSimulatorAsync = promptForSimulatorAsync; function _config() { const data = require("@expo/config"); _config = function () { return data; }; return data; } function osascript() { const data = _interopRequireWildcard(require("@expo/osascript")); osascript = function () { return data; }; return data; } function _spawnAsync() { const data = _interopRequireDefault(require("@expo/spawn-async")); _spawnAsync = function () { return data; }; return data; } function _chalk() { const data = _interopRequireDefault(require("chalk")); _chalk = function () { return data; }; return data; } function _fsExtra() { const data = _interopRequireDefault(require("fs-extra")); _fsExtra = function () { return data; }; return data; } function _path() { const data = _interopRequireDefault(require("path")); _path = function () { return data; }; return data; } function _progress() { const data = _interopRequireDefault(require("progress")); _progress = function () { return data; }; return data; } function _prompts() { const data = _interopRequireDefault(require("prompts")); _prompts = function () { return data; }; return data; } function _semver() { const data = _interopRequireDefault(require("semver")); _semver = function () { return data; }; return data; } function _Analytics() { const data = _interopRequireDefault(require("./Analytics")); _Analytics = function () { return data; }; return data; } function _Api() { const data = _interopRequireDefault(require("./Api")); _Api = function () { return data; }; return data; } function _BundleIdentifier() { const data = require("./BundleIdentifier"); _BundleIdentifier = function () { return data; }; return data; } function _Logger() { const data = _interopRequireDefault(require("./Logger")); _Logger = function () { return data; }; return data; } function _NotificationCode() { const data = _interopRequireDefault(require("./NotificationCode")); _NotificationCode = function () { return data; }; return data; } function Prompts() { const data = _interopRequireWildcard(require("./Prompts")); Prompts = function () { return data; }; return data; } function SimControl() { const data = _interopRequireWildcard(require("./SimControl")); SimControl = function () { return data; }; return data; } function UrlUtils() { const data = _interopRequireWildcard(require("./UrlUtils")); UrlUtils = function () { return data; }; return data; } function _UserSettings() { const data = _interopRequireDefault(require("./UserSettings")); _UserSettings = function () { return data; }; return data; } function Versions() { const data = _interopRequireWildcard(require("./Versions")); Versions = function () { return data; }; return data; } function _Webpack() { const data = require("./Webpack"); _Webpack = function () { return data; }; return data; } function Xcode() { const data = _interopRequireWildcard(require("./Xcode")); Xcode = function () { return data; }; return data; } function _TerminalLink() { const data = require("./logs/TerminalLink"); _TerminalLink = function () { return data; }; return data; } function _delayAsync() { const data = require("./utils/delayAsync"); _delayAsync = function () { return data; }; return data; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } let _lastUrl = null; let _lastUdid = null; const SUGGESTED_XCODE_VERSION = `${Xcode().minimumVersion}.0`; const INSTALL_WARNING_TIMEOUT = 60 * 1000; function isPlatformSupported() { return process.platform === 'darwin'; } /** * Ensure Xcode is installed an recent enough to be used with Expo. * * @return true when Xcode is installed, false when the process should end. */ async function ensureXcodeInstalledAsync() { const promptToOpenAppStoreAsync = async message => { // This prompt serves no purpose accept informing the user what to do next, we could just open the App Store but it could be confusing if they don't know what's going on. const confirm = await Prompts().confirmAsync({ initial: true, message }); if (confirm) { _Logger().default.global.info(`Going to the App Store, re-run Expo when Xcode is finished installing.`); await Xcode().openAppStoreAsync(Xcode().appStoreId); } }; const version = await Xcode().getXcodeVersionAsync(); if (!version) { // Almost certainly Xcode isn't installed. await promptToOpenAppStoreAsync(`Xcode needs to be installed (don't worry, you won't have to use it), would you like to continue to the App Store?`); return false; } if (!_semver().default.valid(version)) { // Not sure why this would happen, if it does we should add a more confident error message. console.error(`Xcode version is in an unknown format: ${version}`); return false; } if (_semver().default.lt(version, SUGGESTED_XCODE_VERSION)) { // Xcode version is too old. await promptToOpenAppStoreAsync(`Xcode (${version}) needs to be updated to at least version ${Xcode().minimumVersion}, would you like to continue to the App Store?`); return false; } return true; } async function ensureXcodeCommandLineToolsInstalledAsync() { if (!(await ensureXcodeInstalledAsync())) { // Need Xcode to install the CLI afaict return false; } else if (await SimControl().isXcrunInstalledAsync()) { // Run this second to ensure the Xcode version check is run. return true; } async function pendingAsync() { if (await SimControl().isXcrunInstalledAsync()) { return true; } else { await (0, _delayAsync().delayAsync)(100); return await pendingAsync(); } } // This prompt serves no purpose accept informing the user what to do next, we could just open the App Store but it could be confusing if they don't know what's going on. const confirm = await Prompts().confirmAsync({ initial: true, message: `Xcode ${_chalk().default.bold`Command Line Tools`} needs to be installed (requires ${_chalk().default.bold`sudo`}), continue?` }); if (!confirm) { return false; } try { await (0, _spawnAsync().default)('sudo', ['xcode-select', '--install' // TODO: Is there any harm in skipping this? // '--switch', '/Applications/Xcode.app' ]); // Most likely the user will cancel the process, but if they don't this will continue checking until the CLI is available. await pendingAsync(); return true; } catch (error) {// TODO: Figure out why this might get called (cancel early, network issues, server problems) // TODO: Handle me } return false; } class TimeoutError extends Error {} // Simulator installed async function isSimulatorInstalledAsync() { // Check to ensure Xcode and its CLI are installed and up to date. if (!(await ensureXcodeCommandLineToolsInstalledAsync())) { return false; } // TODO: extract into ensureSimulatorInstalled method let result; try { result = (await osascript().execAsync('id of app "Simulator"')).trim(); } catch (e) { // This error may occur in CI where the users intends to install just the simulators but no Xcode. console.error("Can't determine id of Simulator app; the Simulator is most likely not installed on this machine. Run `sudo xcode-select -s /Applications/Xcode.app`", e); return false; } if (result !== 'com.apple.iphonesimulator' && result !== 'com.apple.CoreSimulator.SimulatorTrampoline') { // TODO: FYI console.warn("Simulator is installed but is identified as '" + result + "'; don't know what that is."); return false; } // make sure we can run simctl try { await SimControl().simctlAsync(['help']); } catch (e) { if (e.isXDLError) { _Logger().default.global.error(e.toString()); } else { console.warn(`Unable to run simctl: ${e.toString()}`); _Logger().default.global.error('xcrun may not be configured correctly. Try running `sudo xcode-select --reset` and running this again.'); } return false; } return true; } /** * Ensure a simulator is booted and the Simulator app is opened. * This is where any timeout related error handling should live. */ async function ensureSimulatorOpenAsync({ udid } = {}, tryAgain = true) { // Yes, simulators can be booted even if the app isn't running, obviously we'd never want this. if (!(await SimControl().isSimulatorAppRunningAsync())) { _Logger().default.global.info(`Opening the iOS simulator, this might take a moment.`); // In theory this would ensure the correct simulator is booted as well. // This isn't theory though, this is Xcode. await SimControl().openSimulatorAppAsync({ udid }); if (!(await waitForSimulatorAppToStart())) { throw new TimeoutError(`Simulator app did not open fast enough. Try opening Simulator first, then running your app.`); } } // Use a default simulator if none was specified if (!udid) { const simulatorOpenedByApp = await isSimulatorBootedAsync({ udid }); // This should prevent opening a second simulator in the chance that default // simulator doesn't match what the Simulator app would open by default. if (simulatorOpenedByApp === null || simulatorOpenedByApp === void 0 ? void 0 : simulatorOpenedByApp.udid) { udid = simulatorOpenedByApp.udid; } else { var _await$_getDefaultSim; udid = (_await$_getDefaultSim = await _getDefaultSimulatorDeviceUDIDAsync()) !== null && _await$_getDefaultSim !== void 0 ? _await$_getDefaultSim : (await getFirstAvailableDeviceAsync()).udid; } } const bootedDevice = await waitForDeviceToBootAsync({ udid }); if (!bootedDevice) { // Give it a second chance, this might not be needed but it could potentially lead to a better UX on slower devices. if (tryAgain) { return await ensureSimulatorOpenAsync({ udid }, false); } // TODO: We should eliminate all needs for a timeout error, it's bad UX to get an error about the simulator not starting while the user can clearly see it starting on their slow computer. throw new TimeoutError(`Simulator didn't boot fast enough. Try opening Simulator first, then running your app.`); } return bootedDevice; } /** * Get all simulators supported by Expo (iOS only). */ async function getSelectableSimulatorsAsync() { const simulators = await getSimulatorsAsync(); return simulators.filter(device => device.isAvailable && device.osType === 'iOS'); } async function getSimulatorsAsync() { const simulatorDeviceInfo = await SimControl().listAsync('devices'); return Object.values(simulatorDeviceInfo.devices).reduce((prev, runtime) => { return prev.concat(runtime); }, []); } async function getBootedSimulatorsAsync() { const simulators = await getSimulatorsAsync(); return simulators.filter(device => device.state === 'Booted'); } async function isSimulatorBootedAsync({ udid } = {}) { // Simulators can be booted even if the app isn't running :( const devices = await getBootedSimulatorsAsync(); if (udid) { var _devices$find; return (_devices$find = devices.find(bootedDevice => bootedDevice.udid === udid)) !== null && _devices$find !== void 0 ? _devices$find : null; } else { var _devices$; return (_devices$ = devices[0]) !== null && _devices$ !== void 0 ? _devices$ : null; } } async function _getDefaultSimulatorDeviceUDIDAsync() { try { const { stdout: defaultDeviceUDID } = await (0, _spawnAsync().default)('defaults', ['read', 'com.apple.iphonesimulator', 'CurrentDeviceUDID']); return defaultDeviceUDID.trim(); } catch (e) { return null; } } async function getFirstAvailableDeviceAsync() { const simulators = await getSelectableSimulatorsAsync(); if (!simulators.length) { // TODO: Prompt to install the simulators throw new Error('No iPhone devices available in Simulator.'); } return simulators[0]; } async function waitForActionAsync({ action, interval = 100, maxWaitTime = 20000 }) { let complete; const start = Date.now(); do { await (0, _delayAsync().delayAsync)(interval); complete = await action(); if (Date.now() - start > maxWaitTime) { break; } } while (!complete); return complete; } async function waitForSimulatorAppToStart() { return waitForActionAsync({ action: SimControl().isSimulatorAppRunningAsync }); } async function waitForDeviceToBootAsync({ udid }) { return waitForActionAsync({ action: () => { return SimControl().bootAsync({ udid }); } }); } async function activateSimulatorWindowAsync() { // TODO: Focus the individual window return await osascript().execAsync(`tell application "Simulator" to activate`); } async function closeSimulatorAppAsync() { return await osascript().execAsync('tell application "Simulator" to quit'); } async function isExpoClientInstalledOnSimulatorAsync({ udid }) { return !!(await SimControl().getContainerPathAsync(udid, 'host.exp.Exponent')); } async function waitForExpoClientInstalledOnSimulatorAsync({ udid }) { if (await isExpoClientInstalledOnSimulatorAsync({ udid })) { return true; } else { await (0, _delayAsync().delayAsync)(100); return await waitForExpoClientInstalledOnSimulatorAsync({ udid }); } } async function waitForExpoClientUninstalledOnSimulatorAsync({ udid }) { if (!(await isExpoClientInstalledOnSimulatorAsync({ udid }))) { return true; } else { await (0, _delayAsync().delayAsync)(100); return await waitForExpoClientInstalledOnSimulatorAsync({ udid }); } } async function expoVersionOnSimulatorAsync({ udid }) { const localPath = await SimControl().getContainerPathAsync(udid, 'host.exp.Exponent'); if (!localPath) { return null; } const regex = /Exponent-([0-9.]+).*\.app$/; const regexMatch = regex.exec(localPath); if (!regexMatch) { return null; } let matched = regexMatch[1]; // If the value is matched like 1.0.0. then remove the trailing dot. if (matched.endsWith('.')) { matched = matched.substr(0, matched.length - 1); } return matched; } async function doesExpoClientNeedUpdatedAsync(simulator, sdkVersion) { var _clientForSdk$version; // Test that upgrading works by returning true // return true; const versions = await Versions().versionsAsync(); const clientForSdk = await getClientForSDK(sdkVersion); const latestVersionForSdk = (_clientForSdk$version = clientForSdk === null || clientForSdk === void 0 ? void 0 : clientForSdk.version) !== null && _clientForSdk$version !== void 0 ? _clientForSdk$version : versions.iosVersion; const installedVersion = await expoVersionOnSimulatorAsync(simulator); if (installedVersion && _semver().default.lt(installedVersion, latestVersionForSdk)) { return true; } return false; } // If specific URL given just always download it and don't use cache async function _downloadSimulatorAppAsync(url, downloadProgressCallback) { if (!url) { const versions = await Versions().versionsAsync(); url = versions.iosUrl; } const filename = _path().default.parse(url).name; const dir = _path().default.join(simulatorCacheDirectory(), `${filename}.app`); if (await _fsExtra().default.pathExists(dir)) { const filesInDir = await _fsExtra().default.readdir(dir); if (filesInDir.length > 0) { return dir; } else { _fsExtra().default.removeSync(dir); } } _fsExtra().default.mkdirpSync(dir); try { await _Api().default.downloadAsync(url, dir, { extract: true }, downloadProgressCallback); } catch (e) { _fsExtra().default.removeSync(dir); throw e; } return dir; } // url: Optional URL of Exponent.app tarball to download async function installExpoOnSimulatorAsync({ url, simulator, version }) { const bar = new (_progress().default)(`Installing the Expo Go app on ${simulator.name} [:bar] :percent :etas`, { total: 100, width: 64, complete: '=', incomplete: ' ' }); let warningTimer; const setWarningTimer = () => { if (warningTimer) { clearTimeout(warningTimer); } return setTimeout(() => { _Logger().default.global.info(''); _Logger().default.global.info('This download is taking longer than expected. You can also try downloading the clients from the website at https://expo.io/tools'); }, INSTALL_WARNING_TIMEOUT); }; _Logger().default.notifications.info({ code: _NotificationCode().default.START_LOADING }); warningTimer = setWarningTimer(); const dir = await _downloadSimulatorAppAsync(url, progress => bar.tick(1, progress)); _Logger().default.notifications.info({ code: _NotificationCode().default.STOP_LOADING }); if (version) { _Logger().default.global.info(`Installing Expo client ${version} on ${simulator.name}`); } else { _Logger().default.global.info(`Installing Expo client on ${simulator.name}`); } _Logger().default.notifications.info({ code: _NotificationCode().default.START_LOADING }); warningTimer = setWarningTimer(); const result = await SimControl().installAsync({ udid: simulator.udid, dir }); _Logger().default.notifications.info({ code: _NotificationCode().default.STOP_LOADING }); clearTimeout(warningTimer); return result; } async function uninstallExpoAppFromSimulatorAsync({ udid } = {}) { try { _Logger().default.global.info('Uninstalling Expo client from iOS simulator.'); await SimControl().uninstallAsync({ udid, bundleIdentifier: 'host.exp.Exponent' }); } catch (e) { var _e$message; if (!((_e$message = e.message) === null || _e$message === void 0 ? void 0 : _e$message.includes('No devices are booted.'))) { console.error(e); throw e; } } } function simulatorCacheDirectory() { const dotExpoHomeDirectory = _UserSettings().default.dotExpoHomeDirectory(); const dir = _path().default.join(dotExpoHomeDirectory, 'ios-simulator-app-cache'); _fsExtra().default.mkdirpSync(dir); return dir; } async function upgradeExpoAsync(options = {}) { if (!(await isSimulatorInstalledAsync())) { return false; } const simulator = await ensureSimulatorOpenAsync(options); await uninstallExpoAppFromSimulatorAsync(simulator); const installResult = await installExpoOnSimulatorAsync({ url: options.url, version: options.version, simulator }); if (installResult.status !== 0) { return false; } if (_lastUrl) { _Logger().default.global.info(`Opening ${_chalk().default.underline(_lastUrl)} in Expo`); await SimControl().openURLAsync({ udid: simulator.udid, url: _lastUrl }); _lastUrl = null; } return true; } async function openUrlInSimulatorSafeAsync({ url, udid, isDetached = false, sdkVersion, devClient = false, projectRoot, exp = (0, _config().getConfig)(projectRoot, { skipSDKVersionRequirement: true }).exp }) { if (!(await isSimulatorInstalledAsync())) { return { success: false, msg: 'Unable to verify Xcode and Simulator installation.' }; } let simulator = null; try { simulator = await ensureSimulatorOpenAsync({ udid }); } catch (error) { return { success: false, msg: error.message }; } try { if (devClient) { const bundleIdentifier = await (0, _BundleIdentifier().configureBundleIdentifierAsync)(projectRoot, exp); await assertDevClientInstalledAsync(simulator, bundleIdentifier); } else if (!isDetached) { await ensureExpoClientInstalledAsync(simulator, sdkVersion); _lastUrl = url; } _Logger().default.global.info(`Opening ${_chalk().default.underline(url)} on ${_chalk().default.bold(simulator.name)}`); await SimControl().openURLAsync({ url, udid: simulator.udid }); } catch (e) { if (e.isXDLError) { // Hit some internal error, don't try again. // This includes Xcode license errors _Logger().default.global.error(e.message); return { success: false, msg: `${e.toString()}` }; } if (isDetached) { _Logger().default.global.error(`Error running app. Have you installed the app already using Xcode? Since you are detached you must build manually. ${e.toString()}`); } else { _Logger().default.global.error(e.message); } return { success: false, msg: `${e.toString()}` }; } _Analytics().default.logEvent('Open Url on Device', { platform: 'ios' }); return { success: true }; } async function assertDevClientInstalledAsync(simulator, bundleIdentifier) { if (!(await SimControl().getContainerPathAsync(simulator.udid, bundleIdentifier))) { throw new Error(`The development client (${bundleIdentifier}) for this project is not installed. ` + `Please build and install the client on the simulator first.\n${(0, _TerminalLink().learnMore)('https://docs.expo.io/clients/distribution-for-ios/#building-for-ios')}`); } } // Keep a list of simulator UDIDs so we can prevent asking multiple times if a user wants to upgrade. // This can prevent annoying interactions when they don't want to upgrade for whatever reason. const hasPromptedToUpgrade = {}; async function ensureExpoClientInstalledAsync(simulator, sdkVersion) { let isInstalled = await isExpoClientInstalledOnSimulatorAsync(simulator); if (isInstalled) { if (!hasPromptedToUpgrade[simulator.udid] && (await doesExpoClientNeedUpdatedAsync(simulator, sdkVersion))) { // Only prompt once per simulator in a single run. hasPromptedToUpgrade[simulator.udid] = true; const confirm = await Prompts().confirmAsync({ initial: true, message: `Expo client on ${simulator.name} is outdated, would you like to upgrade?` }); if (confirm) { // TODO: Is there any downside to skipping the uninstall step? // await uninstallExpoAppFromSimulatorAsync(simulator); // await waitForExpoClientUninstalledOnSimulatorAsync(simulator); isInstalled = false; } } } // If it's still "not installed" then install it (again). if (!isInstalled) { const iosClient = await getClientForSDK(sdkVersion); await installExpoOnSimulatorAsync({ simulator, ...iosClient }); await waitForExpoClientInstalledOnSimulatorAsync(simulator); } } async function getClientForSDK(sdkVersionString) { if (!sdkVersionString) { return null; } const sdkVersion = (await Versions().sdkVersionsAsync())[sdkVersionString]; return { url: sdkVersion.iosClientUrl, version: sdkVersion.iosClientVersion }; } async function openProjectAsync({ projectRoot, shouldPrompt, devClient }) { const projectUrl = await UrlUtils().constructDeepLinkAsync(projectRoot, { hostType: 'localhost' }); const { exp } = (0, _config().getConfig)(projectRoot, { skipSDKVersionRequirement: true }); let device = null; if (shouldPrompt) { const devices = await getSelectableSimulatorsAsync(); device = await promptForSimulatorAsync(devices); } else { var _lastUdid2; device = await ensureSimulatorOpenAsync({ udid: (_lastUdid2 = _lastUdid) !== null && _lastUdid2 !== void 0 ? _lastUdid2 : undefined }); } if (!device) { return { success: false, error: 'escaped' }; } _lastUdid = device.udid; const result = await openUrlInSimulatorSafeAsync({ udid: device.udid, url: projectUrl, sdkVersion: exp.sdkVersion, isDetached: !!exp.isDetached, devClient, exp, projectRoot }); if (result.success) { await activateSimulatorWindowAsync(); return { success: true, url: projectUrl }; } return { success: result.success, error: result.msg }; } async function openWebProjectAsync({ projectRoot, shouldPrompt }) { const projectUrl = await (0, _Webpack().getUrlAsync)(projectRoot); if (projectUrl === null) { return { success: false, error: `The web project has not been started yet` }; } let device = null; if (shouldPrompt) { const devices = await getSelectableSimulatorsAsync(); device = await promptForSimulatorAsync(devices); } else { var _lastUdid3; device = await ensureSimulatorOpenAsync({ udid: (_lastUdid3 = _lastUdid) !== null && _lastUdid3 !== void 0 ? _lastUdid3 : undefined }); } if (!device) { return { success: false, error: 'escaped' }; } _lastUdid = device.udid; const result = await openUrlInSimulatorSafeAsync({ url: projectUrl, udid: device.udid, isDetached: true, projectRoot }); if (result.success) { await activateSimulatorWindowAsync(); return { success: true, url: projectUrl }; } return { success: result.success, error: result.msg }; } /** * Sort the devices so the last simulator that was opened (user's default) is the first suggested. * * @param devices */ async function sortDefaultDeviceToBeginningAsync(devices) { var _await$_getDefaultSim2; const defaultUdid = (_await$_getDefaultSim2 = await _getDefaultSimulatorDeviceUDIDAsync()) !== null && _await$_getDefaultSim2 !== void 0 ? _await$_getDefaultSim2 : (await getFirstAvailableDeviceAsync()).udid; if (defaultUdid) { let iterations = 0; while (devices[0].udid !== defaultUdid && iterations < devices.length) { devices.push(devices.shift()); iterations++; } } return devices; } async function promptForSimulatorAsync(devices) { devices = await sortDefaultDeviceToBeginningAsync(devices); // TODO: Bail on non-interactive const results = await promptForDeviceAsync(devices); return results ? devices.find(({ udid }) => results === udid) : null; } async function promptForDeviceAsync(devices) { // TODO: provide an option to add or download more simulators // TODO: Add support for physical devices too. // Pause interactions on the TerminalUI Prompts().pauseInteractions(); const { value } = await (0, _prompts().default)({ type: 'autocomplete', name: 'value', limit: 11, message: 'Select a simulator', choices: devices.map(item => { const isActive = item.state === 'Booted'; const format = isActive ? _chalk().default.bold : text => text; return { title: `${format(item.name)} ${_chalk().default.dim(`(${item.osVersion})`)}`, value: item.udid }; }), suggest: (input, choices) => { const regex = new RegExp(input, 'i'); return choices.filter(choice => regex.test(choice.title)); } }); // Resume interactions on the TerminalUI Prompts().resumeInteractions(); return value; } //# sourceMappingURL=__sourcemaps__/Simulator.js.map