2016-01-06 01:11:53 +00:00
/ * *
2018-05-11 19:43:49 +00:00
* Copyright ( c ) 2015 - present , Facebook , Inc .
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree .
*
* @ format
* /
2016-01-06 01:11:53 +00:00
'use strict' ;
const child _process = require ( 'child_process' ) ;
const fs = require ( 'fs' ) ;
const path = require ( 'path' ) ;
const findXcodeProject = require ( './findXcodeProject' ) ;
2017-08-03 18:55:40 +00:00
const findReactNativeScripts = require ( '../util/findReactNativeScripts' ) ;
2016-09-06 15:00:00 +00:00
const parseIOSDevicesList = require ( './parseIOSDevicesList' ) ;
const findMatchingSimulator = require ( './findMatchingSimulator' ) ;
2018-05-11 19:43:49 +00:00
const getBuildPath = function ( configuration = 'Debug' , appName , isDevice ) {
2018-01-29 21:20:48 +00:00
let device ;
if ( isDevice ) {
device = 'iphoneos' ;
} else if ( appName . toLowerCase ( ) . includes ( 'tvos' ) ) {
device = 'appletvsimulator' ;
} else {
device = 'iphonesimulator' ;
}
Fix installing step of `run-ios` command
Summary:
To date if you create a new `react-native@0.55.0` project and try to build/run it for iOS via CLI, e.g. by running:
```
$ react-native init test
$ cd test
$ react-native run-ios --no-packager
```
the build would succeed, but installing will fail afterwards:
```
** BUILD SUCCEEDED **
Installing Build/Products/Debug-iphonesimulator/test.app
An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=2):
Failed to install the requested application
An application bundle was not found at the provided path.
Provide a valid path to the desired application bundle.
Print: Entry, ":CFBundleIdentifier", Does Not Exist
Command failed: /usr/libexec/PlistBuddy -c Print:CFBundleIdentifier Build/Products/Debug-iphonesimulator/test.app/Info.plist
Print: Entry, ":CFBundleIdentifier", Does Not Exist
```
This fail happens because `/usr/libexec/PlistBuddy` can't find `Info.plist` file at the provided path.
This is a regression introduced by changes from PR #17963 (accepted in https://github.com/facebook/react-native/commit/5447ca67076a110e2b0df03b014f53d1df4646ab). If you execute test plan from that PR, it would fail.
As per why:
By default, `run-ios` process's working directory is `$PROJECT_DIR/ios`.
According to [this line in `runIOS.js`](https://github.com/facebook/react-native/blob/3cd2b4342653d0cc6edfc9e7d436d73bfb4f139f/local-cli/runIOS/runIOS.js#L184), `xcodebuild` places all artifacts in `build` directory.
And the default Xcode paths for products is `Build/Products` (at least of Xcode 9.2 which I use, and Xcode 9.3 which I tested this with also).
So, the required `Info.plist` file is actually being created at `$PROJECT_DIR/ios/build/Build/Products/Debug-iphonesimulator/test.app/Info.plist` (with double `build`, the first from `derivedDataPath` key, the second from default products path). Relatively to `run-ios` process's working directory, the path of the file is `build/Build/Products/Debug-iphonesimulator/test.app/Info.plist`.
PR #17963 changed correct path to incorrect, thus introducing this regression.
If changes from that PR are reverted, CLI doesn't fail on install step.
I catch this error on both existing project and a freshly created test project. I can build/run an app from Xcode just fine, but running from CLI still would fail. The other workaround is to change path of products artifacts in Xcode, which is user settings and therefore can't be commited to a project's repo with VCS.
Run:
```
$ react-native init test
$ cd test
$ react-native run-ios --no-packager
```
Ensure that it doesn't fail on install step and produce output similar to this:
```
Installing build/Build/Products/Debug-iphonesimulator/test.app
Launching org.reactjs.native.example.test
```
[CLI][BUGFIX][local-cli/runIOS/runIOS.js] - Fix failing of `run-ios` command on install step
Closes https://github.com/facebook/react-native/pull/18700
Differential Revision: D7555096
Pulled By: hramos
fbshipit-source-id: d877b867e89256f4356f22781d78308affbb9d9c
2018-04-09 19:24:39 +00:00
return ` build/Build/Products/ ${ configuration } - ${ device } / ${ appName } .app ` ;
2016-11-10 17:54:25 +00:00
} ;
2017-09-08 16:57:22 +00:00
const xcprettyAvailable = function ( ) {
try {
child _process . execSync ( 'xcpretty --version' , {
2018-05-11 19:43:49 +00:00
stdio : [ 0 , 'pipe' , 'ignore' ] ,
2017-09-08 16:57:22 +00:00
} ) ;
} catch ( error ) {
return false ;
}
return true ;
} ;
2016-01-06 01:11:53 +00:00
2016-07-30 15:59:16 +00:00
function runIOS ( argv , config , args ) {
2017-08-03 18:55:40 +00:00
if ( ! fs . existsSync ( args . projectPath ) ) {
const reactNativeScriptsPath = findReactNativeScripts ( ) ;
if ( reactNativeScriptsPath ) {
child _process . spawnSync (
reactNativeScriptsPath ,
[ 'ios' ] . concat ( process . argv . slice ( 1 ) ) ,
2018-05-11 19:43:49 +00:00
{ stdio : 'inherit' } ,
2017-08-03 18:55:40 +00:00
) ;
return ;
} else {
2018-05-11 19:43:49 +00:00
throw new Error (
'iOS project folder not found. Are you sure this is a React Native project?' ,
) ;
2017-08-03 18:55:40 +00:00
}
}
2016-07-30 15:59:16 +00:00
process . chdir ( args . projectPath ) ;
2016-01-06 01:11:53 +00:00
const xcodeProject = findXcodeProject ( fs . readdirSync ( '.' ) ) ;
if ( ! xcodeProject ) {
2016-07-30 15:59:16 +00:00
throw new Error ( 'Could not find Xcode project files in ios folder' ) ;
2016-01-06 01:11:53 +00:00
}
2018-05-11 19:43:49 +00:00
const inferredSchemeName = path . basename (
xcodeProject . name ,
path . extname ( xcodeProject . name ) ,
) ;
2016-04-01 15:02:40 +00:00
const scheme = args . scheme || inferredSchemeName ;
2018-05-11 19:43:49 +00:00
console . log (
` Found Xcode ${ xcodeProject . isWorkspace ? 'workspace' : 'project' } ${
xcodeProject . name
} ` ,
) ;
2016-09-06 15:00:00 +00:00
const devices = parseIOSDevicesList (
2018-05-11 19:43:49 +00:00
child _process . execFileSync ( 'xcrun' , [ 'instruments' , '-s' ] , {
encoding : 'utf8' ,
} ) ,
2016-01-06 01:11:53 +00:00
) ;
2016-09-06 15:00:00 +00:00
if ( args . device ) {
const selectedDevice = matchingDevice ( devices , args . device ) ;
2017-08-23 06:33:21 +00:00
if ( selectedDevice ) {
2018-05-11 19:43:49 +00:00
return runOnDevice (
selectedDevice ,
scheme ,
xcodeProject ,
args . configuration ,
args . packager ,
args . verbose ,
args . port ,
) ;
2016-09-06 15:00:00 +00:00
} else {
2017-08-23 06:33:21 +00:00
if ( devices && devices . length > 0 ) {
2018-05-11 19:43:49 +00:00
console . log (
'Could not find device with the name: "' + args . device + '".' ,
) ;
2016-09-06 15:00:00 +00:00
console . log ( 'Choose one of the following:' ) ;
printFoundDevices ( devices ) ;
} else {
console . log ( 'No iOS devices connected.' ) ;
}
}
} else if ( args . udid ) {
2016-11-10 17:54:25 +00:00
return runOnDeviceByUdid ( args , scheme , xcodeProject , devices ) ;
2016-09-06 15:00:00 +00:00
} else {
2017-04-19 18:19:05 +00:00
return runOnSimulator ( xcodeProject , args , scheme ) ;
2016-09-06 15:00:00 +00:00
}
}
2016-11-10 17:54:25 +00:00
function runOnDeviceByUdid ( args , scheme , xcodeProject , devices ) {
const selectedDevice = matchingDeviceByUdid ( devices , args . udid ) ;
2017-08-23 06:33:21 +00:00
if ( selectedDevice ) {
2018-05-11 19:43:49 +00:00
return runOnDevice (
selectedDevice ,
scheme ,
xcodeProject ,
args . configuration ,
args . packager ,
args . verbose ,
args . port ,
) ;
2016-09-06 15:00:00 +00:00
} else {
2017-08-23 06:33:21 +00:00
if ( devices && devices . length > 0 ) {
2016-11-10 17:54:25 +00:00
console . log ( 'Could not find device with the udid: "' + args . udid + '".' ) ;
2016-09-06 15:00:00 +00:00
console . log ( 'Choose one of the following:' ) ;
printFoundDevices ( devices ) ;
} else {
console . log ( 'No iOS devices connected.' ) ;
}
}
}
2017-08-23 06:33:21 +00:00
function runOnSimulator ( xcodeProject , args , scheme ) {
2018-05-11 19:43:49 +00:00
return new Promise ( resolve => {
2016-10-14 21:42:14 +00:00
try {
var simulators = JSON . parse (
2018-05-11 19:43:49 +00:00
child _process . execFileSync (
'xcrun' ,
[ 'simctl' , 'list' , '--json' , 'devices' ] ,
{ encoding : 'utf8' } ,
) ,
2016-10-14 21:42:14 +00:00
) ;
} catch ( e ) {
throw new Error ( 'Could not parse the simulator list output' ) ;
}
2016-06-22 10:30:30 +00:00
2016-10-14 21:42:14 +00:00
const selectedSimulator = findMatchingSimulator ( simulators , args . simulator ) ;
if ( ! selectedSimulator ) {
2016-11-22 19:48:36 +00:00
throw new Error ( ` Could not find ${ args . simulator } simulator ` ) ;
2016-10-14 21:42:14 +00:00
}
2018-05-11 19:43:49 +00:00
2018-04-06 21:10:12 +00:00
/ * *
2018-05-11 19:43:49 +00:00
* 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 .
* /
2018-06-18 05:52:00 +00:00
const activeDeveloperDir = child _process
. execFileSync ( 'xcode-select' , [ '-p' ] , { encoding : 'utf8' } )
. trim ( ) ;
2018-05-11 19:43:49 +00:00
child _process . execFileSync ( 'open' , [
2018-06-18 05:52:00 +00:00
` ${ activeDeveloperDir } /Applications/Simulator.app ` ,
2018-05-11 19:43:49 +00:00
'--args' ,
'-CurrentDeviceUDID' ,
selectedSimulator . udid ,
] ) ;
2016-01-06 01:11:53 +00:00
2018-02-27 22:03:50 +00:00
if ( ! selectedSimulator . booted ) {
const simulatorFullName = formattedDeviceName ( selectedSimulator ) ;
Fix launching iOS simulator regression
Summary:
PR #17284 (accepted in 2ad34075f1d048bebb08ef30799ac0d081073150) introduced a couple of regressions.
~1. There's the code:~
```
.then((appName) => resolve(selectedSimulator.udid, appName));
/* ... */
.then((udid, appName) => {
```
~~This makes `appName` to be always `undefined` as per `resolve` accepts only 1 argument. This regression causes issues if an app name differs from a scheme name.~
~This PR fixes this by wrapping both values in an array.~
This was fixed in 589eae1432cc4bbc16221f841e27b038099fd128.
2. The code
```
child_process.execFileSync('xcrun', ['simctl', 'boot', selectedSimulator.udid]);
```
makes a simulator *boot*, but the simulator *doesn't launch*. That's a regression, which forces developers to launch simulators by other means (by running a number of elaborate console commands, by running Xcode, or by running a simulator manually).
This PR reverts that part of changes.
Create a blank project with a name that differs from scheme name. Try to `react-native run-ios` in it. See that a simulator is launched and installing succeeds. Without this changes simulator wouldn't launch, and installing step would fail because of app name mismatch.
[CLI][BUGFIX][local-cli/runIOS/runIOS.js] - Fix running on multiple simulators feature regressions
Closes https://github.com/facebook/react-native/pull/18711
Differential Revision: D7535150
Pulled By: hramos
fbshipit-source-id: 5c714231e9977c0c829b6f8c793497cd31cd46b5
2018-04-06 16:49:56 +00:00
console . log ( ` Launching ${ simulatorFullName } ... ` ) ;
2018-02-27 22:03:50 +00:00
try {
2018-05-11 19:43:49 +00:00
child _process . spawnSync ( 'xcrun' , [
'instruments' ,
'-w' ,
selectedSimulator . udid ,
] ) ;
2018-02-27 22:03:50 +00:00
} catch ( e ) {
Fix launching iOS simulator regression
Summary:
PR #17284 (accepted in 2ad34075f1d048bebb08ef30799ac0d081073150) introduced a couple of regressions.
~1. There's the code:~
```
.then((appName) => resolve(selectedSimulator.udid, appName));
/* ... */
.then((udid, appName) => {
```
~~This makes `appName` to be always `undefined` as per `resolve` accepts only 1 argument. This regression causes issues if an app name differs from a scheme name.~
~This PR fixes this by wrapping both values in an array.~
This was fixed in 589eae1432cc4bbc16221f841e27b038099fd128.
2. The code
```
child_process.execFileSync('xcrun', ['simctl', 'boot', selectedSimulator.udid]);
```
makes a simulator *boot*, but the simulator *doesn't launch*. That's a regression, which forces developers to launch simulators by other means (by running a number of elaborate console commands, by running Xcode, or by running a simulator manually).
This PR reverts that part of changes.
Create a blank project with a name that differs from scheme name. Try to `react-native run-ios` in it. See that a simulator is launched and installing succeeds. Without this changes simulator wouldn't launch, and installing step would fail because of app name mismatch.
[CLI][BUGFIX][local-cli/runIOS/runIOS.js] - Fix running on multiple simulators feature regressions
Closes https://github.com/facebook/react-native/pull/18711
Differential Revision: D7535150
Pulled By: hramos
fbshipit-source-id: 5c714231e9977c0c829b6f8c793497cd31cd46b5
2018-04-06 16:49:56 +00:00
// instruments always fail with 255 because it expects more arguments,
// but we want it to only launch the simulator
2018-02-27 22:03:50 +00:00
}
2016-10-14 21:42:14 +00:00
}
2018-02-27 22:03:50 +00:00
2018-05-11 19:43:49 +00:00
buildProject (
xcodeProject ,
selectedSimulator . udid ,
scheme ,
args . configuration ,
args . packager ,
args . verbose ,
args . port ,
) . then ( appName => resolve ( { udid : selectedSimulator . udid , appName } ) ) ;
} ) . then ( ( { udid , appName } ) => {
2016-10-14 21:42:14 +00:00
if ( ! appName ) {
2017-04-19 18:19:05 +00:00
appName = scheme ;
2016-10-14 21:42:14 +00:00
}
2016-11-10 17:54:25 +00:00
let appPath = getBuildPath ( args . configuration , appName ) ;
2016-10-14 21:42:14 +00:00
console . log ( ` Installing ${ appPath } ` ) ;
2018-05-11 19:43:49 +00:00
child _process . spawnSync ( 'xcrun' , [ 'simctl' , 'install' , udid , appPath ] , {
stdio : 'inherit' ,
} ) ;
2016-10-14 21:42:14 +00:00
2018-05-11 19:43:49 +00:00
const bundleID = child _process
. execFileSync (
'/usr/libexec/PlistBuddy' ,
[ '-c' , 'Print:CFBundleIdentifier' , path . join ( appPath , 'Info.plist' ) ] ,
{ encoding : 'utf8' } ,
)
. trim ( ) ;
2016-10-14 21:42:14 +00:00
console . log ( ` Launching ${ bundleID } ` ) ;
2018-05-11 19:43:49 +00:00
child _process . spawnSync ( 'xcrun' , [ 'simctl' , 'launch' , udid , bundleID ] , {
stdio : 'inherit' ,
} ) ;
2017-09-08 16:57:22 +00:00
} ) ;
2016-01-06 01:11:53 +00:00
}
2018-05-11 19:43:49 +00:00
function runOnDevice (
selectedDevice ,
scheme ,
xcodeProject ,
configuration ,
launchPackager ,
verbose ,
port ,
) {
return buildProject (
xcodeProject ,
selectedDevice . udid ,
scheme ,
configuration ,
launchPackager ,
verbose ,
port ,
) . then ( appName => {
2016-10-14 21:42:14 +00:00
if ( ! appName ) {
appName = scheme ;
}
const iosDeployInstallArgs = [
2018-05-11 19:43:49 +00:00
'--bundle' ,
getBuildPath ( configuration , appName , true ) ,
'--id' ,
selectedDevice . udid ,
'--justlaunch' ,
2016-10-14 21:42:14 +00:00
] ;
2018-05-11 19:43:49 +00:00
console . log (
` installing and launching your app on ${ selectedDevice . name } ... ` ,
) ;
const iosDeployOutput = child _process . spawnSync (
'ios-deploy' ,
iosDeployInstallArgs ,
{ encoding : 'utf8' } ,
) ;
2016-10-14 21:42:14 +00:00
if ( iosDeployOutput . error ) {
console . log ( '' ) ;
console . log ( '** INSTALLATION FAILED **' ) ;
console . log ( 'Make sure you have ios-deploy installed globally.' ) ;
console . log ( '(e.g "npm install -g ios-deploy")' ) ;
} else {
console . log ( '** INSTALLATION SUCCEEDED **' ) ;
}
} ) ;
2016-09-06 15:00:00 +00:00
}
2018-05-11 19:43:49 +00:00
function buildProject (
xcodeProject ,
udid ,
scheme ,
configuration = 'Debug' ,
launchPackager = false ,
verbose ,
port ,
) {
return new Promise ( ( resolve , reject ) => {
var xcodebuildArgs = [
xcodeProject . isWorkspace ? '-workspace' : '-project' ,
xcodeProject . name ,
'-configuration' ,
configuration ,
'-scheme' ,
scheme ,
'-destination' ,
` id= ${ udid } ` ,
'-derivedDataPath' ,
'build' ,
2016-10-14 21:42:14 +00:00
] ;
console . log ( ` Building using "xcodebuild ${ xcodebuildArgs . join ( ' ' ) } " ` ) ;
2017-09-08 16:57:22 +00:00
let xcpretty ;
if ( ! verbose ) {
2018-05-11 19:43:49 +00:00
xcpretty =
xcprettyAvailable ( ) &&
child _process . spawn ( 'xcpretty' , [ ] , {
stdio : [ 'pipe' , process . stdout , process . stderr ] ,
} ) ;
2017-09-08 16:57:22 +00:00
}
2018-05-11 19:43:49 +00:00
const buildProcess = child _process . spawn (
'xcodebuild' ,
xcodebuildArgs ,
getProcessOptions ( launchPackager , port ) ,
) ;
2017-09-08 16:57:22 +00:00
let buildOutput = '' ;
2016-10-14 21:42:14 +00:00
buildProcess . stdout . on ( 'data' , function ( data ) {
buildOutput += data . toString ( ) ;
2017-09-08 16:57:22 +00:00
if ( xcpretty ) {
xcpretty . stdin . write ( data ) ;
} else {
console . log ( data . toString ( ) ) ;
}
2016-10-14 21:42:14 +00:00
} ) ;
buildProcess . stderr . on ( 'data' , function ( data ) {
console . error ( data . toString ( ) ) ;
} ) ;
buildProcess . on ( 'close' , function ( code ) {
2017-09-08 16:57:22 +00:00
if ( xcpretty ) {
xcpretty . stdin . end ( ) ;
}
2016-10-14 21:42:14 +00:00
//FULL_PRODUCT_NAME is the actual file name of the app, which actually comes from the Product Name in the build config, which does not necessary match a scheme name, example output line: export FULL_PRODUCT_NAME="Super App Dev.app"
2018-05-11 19:43:49 +00:00
let productNameMatch = /export FULL_PRODUCT_NAME="?(.+).app"?$/m . exec (
buildOutput ,
) ;
if (
productNameMatch &&
productNameMatch . length &&
productNameMatch . length > 1
) {
return resolve ( productNameMatch [ 1 ] ) ; //0 is the full match, 1 is the app name
2016-10-14 21:42:14 +00:00
}
2017-09-08 16:57:22 +00:00
return buildProcess . error ? reject ( buildProcess . error ) : resolve ( ) ;
2016-10-14 21:42:14 +00:00
} ) ;
} ) ;
2016-09-06 15:00:00 +00:00
}
function matchingDevice ( devices , deviceName ) {
2018-05-11 19:43:49 +00:00
if ( deviceName === true && devices . length === 1 ) {
console . log (
` Using first available device ${
devices [ 0 ] . name
} due to lack of name supplied . ` ,
) ;
2016-10-14 21:42:14 +00:00
return devices [ 0 ] ;
2016-11-10 17:54:25 +00:00
}
2016-09-06 15:00:00 +00:00
for ( let i = devices . length - 1 ; i >= 0 ; i -- ) {
2018-05-11 19:43:49 +00:00
if (
devices [ i ] . name === deviceName ||
formattedDeviceName ( devices [ i ] ) === deviceName
) {
2016-09-06 15:00:00 +00:00
return devices [ i ] ;
}
}
}
function matchingDeviceByUdid ( devices , udid ) {
for ( let i = devices . length - 1 ; i >= 0 ; i -- ) {
if ( devices [ i ] . udid === udid ) {
return devices [ i ] ;
2016-01-06 01:11:53 +00:00
}
}
}
2016-09-06 15:00:00 +00:00
function formattedDeviceName ( simulator ) {
2016-07-05 16:46:22 +00:00
return ` ${ simulator . name } ( ${ simulator . version } ) ` ;
}
2017-08-23 06:33:21 +00:00
function printFoundDevices ( devices ) {
2016-09-06 15:00:00 +00:00
for ( let i = devices . length - 1 ; i >= 0 ; i -- ) {
console . log ( devices [ i ] . name + ' Udid: ' + devices [ i ] . udid ) ;
}
}
2018-01-05 04:02:37 +00:00
function getProcessOptions ( launchPackager , port ) {
2017-01-09 11:25:27 +00:00
if ( launchPackager ) {
2018-01-05 04:02:37 +00:00
return {
2018-05-11 19:43:49 +00:00
env : { ... process . env , RCT _METRO _PORT : port } ,
2018-01-05 04:02:37 +00:00
} ;
2017-01-09 11:25:27 +00:00
}
return {
2018-05-11 19:43:49 +00:00
env : { ... process . env , RCT _NO _LAUNCH _PACKAGER : true } ,
2017-01-09 11:25:27 +00:00
} ;
}
2016-07-30 15:59:16 +00:00
module . exports = {
name : 'run-ios' ,
description : 'builds your app and starts it on iOS simulator' ,
func : runIOS ,
examples : [
2018-05-11 19:43:49 +00:00
{
desc : 'Run on a different simulator, e.g. iPhone 5' ,
cmd : 'react-native run-ios --simulator "iPhone 5"' ,
} ,
{
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 : [
{
command : '--simulator [string]' ,
description : 'Explicitly set simulator to use' ,
default : 'iPhone 6' ,
} ,
{
command : '--configuration [string]' ,
description : 'Explicitly set the scheme configuration to use' ,
} ,
{
command : '--scheme [string]' ,
description : 'Explicitly set Xcode scheme to use' ,
} ,
{
command : '--project-path [string]' ,
description :
'Path relative to project root where the Xcode project ' +
"(.xcodeproj) lives. The default is 'ios'." ,
default : 'ios' ,
} ,
{
command : '--device [string]' ,
description :
'Explicitly set device to use by name. The value is not required if you have a single device connected.' ,
} ,
{
command : '--udid [string]' ,
description : 'Explicitly set device to use by udid' ,
} ,
{
command : '--no-packager' ,
description : 'Do not launch packager while building' ,
} ,
{
command : '--verbose' ,
description : 'Do not use xcpretty even if installed' ,
} ,
{
command : '--port [number]' ,
default : process . env . RCT _METRO _PORT || 8081 ,
parse : ( val : string ) => Number ( val ) ,
} ,
2016-07-30 15:59:16 +00:00
] ,
} ;