UNPKG

create-expo-cljs-app

Version:

Create a react native application with Expo and Shadow-CLJS!

507 lines (397 loc) 16.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; function _child_process() { const data = _interopRequireDefault(require("child_process")); _child_process = function () { return data; }; return data; } function _fs() { const data = _interopRequireDefault(require("fs")); _fs = function () { return data; }; return data; } function _path() { const data = _interopRequireDefault(require("path")); _path = function () { return data; }; return data; } function _chalk() { const data = _interopRequireDefault(require("chalk")); _chalk = function () { return data; }; return data; } var _findXcodeProject = _interopRequireDefault(require("./findXcodeProject")); var _parseIOSDevicesList = _interopRequireDefault(require("./parseIOSDevicesList")); var _parseXctraceIOSDevicesList = _interopRequireDefault(require("./parseXctraceIOSDevicesList")); var _findMatchingSimulator = _interopRequireDefault(require("./findMatchingSimulator")); var _warnAboutManuallyLinkedLibs = _interopRequireDefault(require("../../link/warnAboutManuallyLinkedLibs")); var _warnAboutPodInstall = _interopRequireDefault(require("../../link/warnAboutPodInstall")); function _cliTools() { const data = require("@react-native-community/cli-tools"); _cliTools = function () { return data; }; return data; } function _ora() { const data = _interopRequireDefault(require("ora")); _ora = function () { return data; }; return data; } function _execa() { const data = _interopRequireDefault(require("execa")); _execa = function () { return data; }; return data; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * */ function runIOS(_, ctx, args) { if (!_fs().default.existsSync(args.projectPath)) { throw new (_cliTools().CLIError)('iOS project folder not found. Are you sure this is a React Native project?'); } (0, _warnAboutManuallyLinkedLibs.default)(ctx); (0, _warnAboutPodInstall.default)(ctx); process.chdir(args.projectPath); const xcodeProject = (0, _findXcodeProject.default)(_fs().default.readdirSync('.')); if (!xcodeProject) { throw new (_cliTools().CLIError)(`Could not find Xcode project files in "${args.projectPath}" folder`); } const inferredSchemeName = _path().default.basename(xcodeProject.name, _path().default.extname(xcodeProject.name)); const scheme = args.scheme || inferredSchemeName; _cliTools().logger.info(`Found Xcode ${xcodeProject.isWorkspace ? 'workspace' : 'project'} "${_chalk().default.bold(xcodeProject.name)}"`); // No need to load all available devices if (!args.device && !args.udid) { return runOnSimulator(xcodeProject, scheme, args); } if (args.device && args.udid) { return _cliTools().logger.error('The `device` and `udid` options are mutually exclusive.'); } let devices; try { const out = _execa().default.sync('xcrun', ['xctrace', 'list', 'devices']); devices = (0, _parseXctraceIOSDevicesList.default)( // Xcode 12.5 introduced a change to output the list to stdout instead of stderr out.stderr === '' ? out.stdout : out.stderr); } catch (e) { _cliTools().logger.warn('Support for Xcode 11 and older is deprecated. Please upgrade to Xcode 12.'); devices = (0, _parseIOSDevicesList.default)(_execa().default.sync('xcrun', ['instruments', '-s']).stdout); } if (args.udid) { const device = devices.find(d => d.udid === args.udid); if (!device) { return _cliTools().logger.error(`Could not find a device with udid: "${_chalk().default.bold(args.udid)}". ${printFoundDevices(devices)}`); } if (device.type === 'simulator') { return runOnSimulator(xcodeProject, scheme, args); } else { return runOnDevice(device, scheme, xcodeProject, args); } } else { const physicalDevices = devices.filter(d => d.type !== 'simulator'); const device = matchingDevice(physicalDevices, args.device); if (device) { return runOnDevice(device, scheme, xcodeProject, args); } } } async function runOnSimulator(xcodeProject, scheme, args) { let simulators; try { simulators = JSON.parse(_child_process().default.execFileSync('xcrun', ['simctl', 'list', '--json', 'devices'], { encoding: 'utf8' })); } catch (error) { throw new (_cliTools().CLIError)('Could not get the simulator list from Xcode. Please open Xcode and try running project directly from there to resolve the remaining issues.', error); } /** * If provided simulator does not exist, try simulators in following order * - iPhone 12 * - iPhone 11 * - iPhone 8 */ const fallbackSimulators = ['iPhone 12', 'iPhone 11', 'iPhone 8']; const selectedSimulator = fallbackSimulators.reduce((simulator, fallback) => { return simulator || (0, _findMatchingSimulator.default)(simulators, { simulator: fallback }); }, (0, _findMatchingSimulator.default)(simulators, args)); if (!selectedSimulator) { throw new (_cliTools().CLIError)(`No simulator available with ${args.simulator ? `name "${args.simulator}"` : `udid "${args.udid}"`}`); } /** * Booting simulator through `xcrun simctl boot` will boot it in the `headless` mode * (running in the background). * * In order for user to see the app and the simulator itself, we have to make sure * that the Simulator.app is running. * * We also pass it `-CurrentDeviceUDID` so that when we launch it for the first time, * it will not boot the "default" device, but the one we set. If the app is already running, * this flag has no effect. */ const activeDeveloperDir = _child_process().default.execFileSync('xcode-select', ['-p'], { encoding: 'utf8' }).trim(); _child_process().default.execFileSync('open', [`${activeDeveloperDir}/Applications/Simulator.app`, '--args', '-CurrentDeviceUDID', selectedSimulator.udid]); if (!selectedSimulator.booted) { bootSimulator(selectedSimulator); } const buildOutput = await buildProject(xcodeProject, selectedSimulator.udid, scheme, args); const appPath = getBuildPath(xcodeProject, args.configuration, buildOutput, scheme); _cliTools().logger.info(`Installing "${_chalk().default.bold(appPath)}"`); _child_process().default.spawnSync('xcrun', ['simctl', 'install', selectedSimulator.udid, appPath], { stdio: 'inherit' }); const bundleID = _child_process().default.execFileSync('/usr/libexec/PlistBuddy', ['-c', 'Print:CFBundleIdentifier', _path().default.join(appPath, 'Info.plist')], { encoding: 'utf8' }).trim(); _cliTools().logger.info(`Launching "${_chalk().default.bold(bundleID)}"`); const result = _child_process().default.spawnSync('xcrun', ['simctl', 'launch', selectedSimulator.udid, bundleID]); if (result.status === 0) { _cliTools().logger.success('Successfully launched the app on the simulator'); } else { _cliTools().logger.error('Failed to launch the app on simulator', result.stderr); } } async function runOnDevice(selectedDevice, scheme, xcodeProject, args) { const isIOSDeployInstalled = _child_process().default.spawnSync('ios-deploy', ['--version'], { encoding: 'utf8' }); if (isIOSDeployInstalled.error) { throw new (_cliTools().CLIError)(`Failed to install the app on the device because we couldn't execute the "ios-deploy" command. Please install it by running "${_chalk().default.bold('npm install -g ios-deploy')}" and try again.`); } const buildOutput = await buildProject(xcodeProject, selectedDevice.udid, scheme, args); const iosDeployInstallArgs = ['--bundle', getBuildPath(xcodeProject, args.configuration, buildOutput, scheme), '--id', selectedDevice.udid, '--justlaunch']; _cliTools().logger.info(`Installing and launching your app on ${selectedDevice.name}`); const iosDeployOutput = _child_process().default.spawnSync('ios-deploy', iosDeployInstallArgs, { encoding: 'utf8' }); if (iosDeployOutput.error) { throw new (_cliTools().CLIError)(`Failed to install the app on the device. We've encountered an error in "ios-deploy" command: ${iosDeployOutput.error.message}`); } return _cliTools().logger.success('Installed the app on the device.'); } function buildProject(xcodeProject, udid, scheme, args) { return new Promise((resolve, reject) => { const xcodebuildArgs = [xcodeProject.isWorkspace ? '-workspace' : '-project', xcodeProject.name, '-configuration', args.configuration, '-scheme', scheme, '-destination', `id=${udid}`]; const loader = (0, _ora().default)(); _cliTools().logger.info(`Building ${_chalk().default.dim(`(using "xcodebuild ${xcodebuildArgs.join(' ')}")`)}`); let xcpretty; if (!args.verbose) { xcpretty = xcprettyAvailable() && _child_process().default.spawn('xcpretty', [], { stdio: ['pipe', process.stdout, process.stderr] }); } const buildProcess = _child_process().default.spawn('xcodebuild', xcodebuildArgs, getProcessOptions(args)); let buildOutput = ''; let errorOutput = ''; buildProcess.stdout.on('data', data => { const stringData = data.toString(); buildOutput += stringData; if (xcpretty) { xcpretty.stdin.write(data); } else { if (_cliTools().logger.isVerbose()) { _cliTools().logger.debug(stringData); } else { loader.start(`Building the app${'.'.repeat(buildOutput.length % 10)}`); } } }); buildProcess.stderr.on('data', data => { errorOutput += data; }); buildProcess.on('close', code => { if (xcpretty) { xcpretty.stdin.end(); } else { loader.stop(); } if (code !== 0) { reject(new (_cliTools().CLIError)(` Failed to build iOS project. We ran "xcodebuild" command but it exited with error code ${code}. To debug build logs further, consider building your app with Xcode.app, by opening ${xcodeProject.name}. `, buildOutput + '\n' + errorOutput)); return; } _cliTools().logger.success('Successfully built the app'); resolve(buildOutput); }); }); } function bootSimulator(selectedSimulator) { const simulatorFullName = formattedDeviceName(selectedSimulator); _cliTools().logger.info(`Launching ${simulatorFullName}`); try { _child_process().default.spawnSync('xcrun', ['instruments', '-w', selectedSimulator.udid]); } catch (_ignored) {// instruments always fail with 255 because it expects more arguments, // but we want it to only launch the simulator } } function getTargetPaths(buildSettings) { const settings = JSON.parse(buildSettings); // Find app in all building settings - look for WRAPPER_EXTENSION: 'app', for (const i in settings) { const wrapperExtension = settings[i].buildSettings.WRAPPER_EXTENSION; if (wrapperExtension === 'app') { return { targetBuildDir: settings[i].buildSettings.TARGET_BUILD_DIR, executableFolderPath: settings[i].buildSettings.EXECUTABLE_FOLDER_PATH }; } } return {}; } function getBuildPath(xcodeProject, configuration, buildOutput, scheme) { const buildSettings = _child_process().default.execFileSync('xcodebuild', [xcodeProject.isWorkspace ? '-workspace' : '-project', xcodeProject.name, '-scheme', scheme, '-sdk', getPlatformName(buildOutput), '-configuration', configuration, '-showBuildSettings', '-json'], { encoding: 'utf8' }); const { targetBuildDir, executableFolderPath } = getTargetPaths(buildSettings); if (!targetBuildDir) { throw new (_cliTools().CLIError)('Failed to get the target build directory.'); } if (!executableFolderPath) { throw new (_cliTools().CLIError)('Failed to get the app name.'); } return `${targetBuildDir}/${executableFolderPath}`; } function getPlatformName(buildOutput) { // Xcode can sometimes escape `=` with a backslash or put the value in quotes const platformNameMatch = /export PLATFORM_NAME\\?="?(\w+)"?$/m.exec(buildOutput); if (!platformNameMatch) { throw new (_cliTools().CLIError)('Couldn\'t find "PLATFORM_NAME" variable in xcodebuild output. Please report this issue and run your project with Xcode instead.'); } return platformNameMatch[1]; } function xcprettyAvailable() { try { _child_process().default.execSync('xcpretty --version', { stdio: [0, 'pipe', 'ignore'] }); } catch (error) { return false; } return true; } function matchingDevice(devices, deviceName) { if (deviceName === true) { const firstIOSDevice = devices.find(d => d.type === 'device'); if (firstIOSDevice) { _cliTools().logger.info(`Using first available device named "${_chalk().default.bold(firstIOSDevice.name)}" due to lack of name supplied.`); return firstIOSDevice; } else { _cliTools().logger.error('No iOS devices connected.'); return undefined; } } const deviceByName = devices.find(device => device.name === deviceName || formattedDeviceName(device) === deviceName); if (!deviceByName) { _cliTools().logger.error(`Could not find a device named: "${_chalk().default.bold(String(deviceName))}". ${printFoundDevices(devices)}`); } return deviceByName; } function formattedDeviceName(simulator) { return simulator.version ? `${simulator.name} (${simulator.version})` : simulator.name; } function printFoundDevices(devices) { return ['Available devices:', ...devices.map(device => ` - ${device.name} (${device.udid})`)].join('\n'); } function getProcessOptions({ packager, terminal, port }) { if (packager) { return { env: { ...process.env, RCT_TERMINAL: terminal, RCT_METRO_PORT: port.toString() } }; } return { env: { ...process.env, RCT_TERMINAL: terminal, RCT_NO_LAUNCH_PACKAGER: 'true' } }; } var _default = { name: 'run-ios', description: 'builds your app and starts it on iOS simulator', func: runIOS, examples: [{ desc: 'Run on a different simulator, e.g. iPhone SE (1st generation)', cmd: 'react-native run-ios --simulator "iPhone SE (1st generation)"' }, { desc: 'Pass a non-standard location of iOS directory', cmd: 'react-native run-ios --project-path "./app/ios"' }, { desc: "Run on a connected device, e.g. Max's iPhone", cmd: 'react-native run-ios --device "Max\'s iPhone"' }, { desc: 'Run on the AppleTV simulator', cmd: 'react-native run-ios --simulator "Apple TV" --scheme "helloworld-tvOS"' }], options: [{ name: '--simulator <string>', description: 'Explicitly set simulator to use. Optionally include iOS version between ' + 'parenthesis at the end to match an exact version: "iPhone 6 (10.0)"', default: 'iPhone 12' }, { name: '--configuration <string>', description: 'Explicitly set the scheme configuration to use', default: 'Debug' }, { name: '--scheme <string>', description: 'Explicitly set Xcode scheme to use' }, { name: '--project-path <string>', description: 'Path relative to project root where the Xcode project ' + '(.xcodeproj) lives.', default: 'ios' }, { name: '--device [string]', description: 'Explicitly set device to use by name. The value is not required if you have a single device connected.' }, { name: '--udid <string>', description: 'Explicitly set device to use by udid' }, { name: '--no-packager', description: 'Do not launch packager while building' }, { name: '--verbose', description: 'Do not use xcpretty even if installed' }, { name: '--port <number>', default: process.env.RCT_METRO_PORT || 8081, parse: Number }, { name: '--terminal <string>', description: 'Launches the Metro Bundler in a new window using the specified terminal path.', default: _cliTools().getDefaultUserTerminal }] }; exports.default = _default; //# sourceMappingURL=index.js.map