mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-24 04:16:00 +08:00
Prettier RN local-cli
Reviewed By: yungsters Differential Revision: D7962462 fbshipit-source-id: 0afe2092af8703895de91a6d1400315c3173aa6d
This commit is contained in:
committed by
Facebook Github Bot
parent
680fb50040
commit
aba4ec0c09
@@ -1,9 +1,12 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
* 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
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const child_process = require('child_process');
|
||||
@@ -13,7 +16,7 @@ const findXcodeProject = require('./findXcodeProject');
|
||||
const findReactNativeScripts = require('../util/findReactNativeScripts');
|
||||
const parseIOSDevicesList = require('./parseIOSDevicesList');
|
||||
const findMatchingSimulator = require('./findMatchingSimulator');
|
||||
const getBuildPath = function (configuration = 'Debug', appName, isDevice) {
|
||||
const getBuildPath = function(configuration = 'Debug', appName, isDevice) {
|
||||
let device;
|
||||
|
||||
if (isDevice) {
|
||||
@@ -29,7 +32,7 @@ const getBuildPath = function (configuration = 'Debug', appName, isDevice) {
|
||||
const xcprettyAvailable = function() {
|
||||
try {
|
||||
child_process.execSync('xcpretty --version', {
|
||||
stdio: [ 0, 'pipe', 'ignore', ]
|
||||
stdio: [0, 'pipe', 'ignore'],
|
||||
});
|
||||
} catch (error) {
|
||||
return false;
|
||||
@@ -44,11 +47,13 @@ function runIOS(argv, config, args) {
|
||||
child_process.spawnSync(
|
||||
reactNativeScriptsPath,
|
||||
['ios'].concat(process.argv.slice(1)),
|
||||
{stdio: 'inherit'}
|
||||
{stdio: 'inherit'},
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
throw new Error('iOS project folder not found. Are you sure this is a React Native project?');
|
||||
throw new Error(
|
||||
'iOS project folder not found. Are you sure this is a React Native project?',
|
||||
);
|
||||
}
|
||||
}
|
||||
process.chdir(args.projectPath);
|
||||
@@ -57,19 +62,38 @@ function runIOS(argv, config, args) {
|
||||
throw new Error('Could not find Xcode project files in ios folder');
|
||||
}
|
||||
|
||||
const inferredSchemeName = path.basename(xcodeProject.name, path.extname(xcodeProject.name));
|
||||
const inferredSchemeName = path.basename(
|
||||
xcodeProject.name,
|
||||
path.extname(xcodeProject.name),
|
||||
);
|
||||
const scheme = args.scheme || inferredSchemeName;
|
||||
console.log(`Found Xcode ${xcodeProject.isWorkspace ? 'workspace' : 'project'} ${xcodeProject.name}`);
|
||||
console.log(
|
||||
`Found Xcode ${xcodeProject.isWorkspace ? 'workspace' : 'project'} ${
|
||||
xcodeProject.name
|
||||
}`,
|
||||
);
|
||||
const devices = parseIOSDevicesList(
|
||||
child_process.execFileSync('xcrun', ['instruments', '-s'], {encoding: 'utf8'})
|
||||
child_process.execFileSync('xcrun', ['instruments', '-s'], {
|
||||
encoding: 'utf8',
|
||||
}),
|
||||
);
|
||||
if (args.device) {
|
||||
const selectedDevice = matchingDevice(devices, args.device);
|
||||
if (selectedDevice) {
|
||||
return runOnDevice(selectedDevice, scheme, xcodeProject, args.configuration, args.packager, args.verbose, args.port);
|
||||
return runOnDevice(
|
||||
selectedDevice,
|
||||
scheme,
|
||||
xcodeProject,
|
||||
args.configuration,
|
||||
args.packager,
|
||||
args.verbose,
|
||||
args.port,
|
||||
);
|
||||
} else {
|
||||
if (devices && devices.length > 0) {
|
||||
console.log('Could not find device with the name: "' + args.device + '".');
|
||||
console.log(
|
||||
'Could not find device with the name: "' + args.device + '".',
|
||||
);
|
||||
console.log('Choose one of the following:');
|
||||
printFoundDevices(devices);
|
||||
} else {
|
||||
@@ -86,7 +110,15 @@ function runIOS(argv, config, args) {
|
||||
function runOnDeviceByUdid(args, scheme, xcodeProject, devices) {
|
||||
const selectedDevice = matchingDeviceByUdid(devices, args.udid);
|
||||
if (selectedDevice) {
|
||||
return runOnDevice(selectedDevice, scheme, xcodeProject, args.configuration, args.packager, args.verbose, args.port);
|
||||
return runOnDevice(
|
||||
selectedDevice,
|
||||
scheme,
|
||||
xcodeProject,
|
||||
args.configuration,
|
||||
args.packager,
|
||||
args.verbose,
|
||||
args.port,
|
||||
);
|
||||
} else {
|
||||
if (devices && devices.length > 0) {
|
||||
console.log('Could not find device with the udid: "' + args.udid + '".');
|
||||
@@ -99,10 +131,14 @@ function runOnDeviceByUdid(args, scheme, xcodeProject, devices) {
|
||||
}
|
||||
|
||||
function runOnSimulator(xcodeProject, args, scheme) {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
try {
|
||||
var simulators = JSON.parse(
|
||||
child_process.execFileSync('xcrun', ['simctl', 'list', '--json', 'devices'], {encoding: 'utf8'})
|
||||
child_process.execFileSync(
|
||||
'xcrun',
|
||||
['simctl', 'list', '--json', 'devices'],
|
||||
{encoding: 'utf8'},
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
throw new Error('Could not parse the simulator list output');
|
||||
@@ -112,71 +148,110 @@ function runOnSimulator(xcodeProject, args, scheme) {
|
||||
if (!selectedSimulator) {
|
||||
throw new Error(`Could not find ${args.simulator} simulator`);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
child_process.execFileSync('open', [
|
||||
'/Applications/Xcode.app/Contents/Developer/Applications/Simulator.app',
|
||||
'--args',
|
||||
'-CurrentDeviceUDID',
|
||||
selectedSimulator.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.
|
||||
*/
|
||||
child_process.execFileSync('open', [
|
||||
'/Applications/Xcode.app/Contents/Developer/Applications/Simulator.app',
|
||||
'--args',
|
||||
'-CurrentDeviceUDID',
|
||||
selectedSimulator.udid,
|
||||
]);
|
||||
|
||||
if (!selectedSimulator.booted) {
|
||||
const simulatorFullName = formattedDeviceName(selectedSimulator);
|
||||
console.log(`Launching ${simulatorFullName}...`);
|
||||
try {
|
||||
child_process.spawnSync('xcrun', ['instruments', '-w', selectedSimulator.udid]);
|
||||
child_process.spawnSync('xcrun', [
|
||||
'instruments',
|
||||
'-w',
|
||||
selectedSimulator.udid,
|
||||
]);
|
||||
} catch (e) {
|
||||
// instruments always fail with 255 because it expects more arguments,
|
||||
// but we want it to only launch the simulator
|
||||
}
|
||||
}
|
||||
|
||||
buildProject(xcodeProject, selectedSimulator.udid, scheme, args.configuration, args.packager, args.verbose, args.port)
|
||||
.then((appName) => resolve({ udid: selectedSimulator.udid, appName }));
|
||||
})
|
||||
.then(({udid, appName}) => {
|
||||
buildProject(
|
||||
xcodeProject,
|
||||
selectedSimulator.udid,
|
||||
scheme,
|
||||
args.configuration,
|
||||
args.packager,
|
||||
args.verbose,
|
||||
args.port,
|
||||
).then(appName => resolve({udid: selectedSimulator.udid, appName}));
|
||||
}).then(({udid, appName}) => {
|
||||
if (!appName) {
|
||||
appName = scheme;
|
||||
}
|
||||
let appPath = getBuildPath(args.configuration, appName);
|
||||
console.log(`Installing ${appPath}`);
|
||||
child_process.spawnSync('xcrun', ['simctl', 'install', udid, appPath], {stdio: 'inherit'});
|
||||
child_process.spawnSync('xcrun', ['simctl', 'install', udid, appPath], {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
const bundleID = child_process.execFileSync(
|
||||
'/usr/libexec/PlistBuddy',
|
||||
['-c', 'Print:CFBundleIdentifier', path.join(appPath, 'Info.plist')],
|
||||
{encoding: 'utf8'}
|
||||
).trim();
|
||||
const bundleID = child_process
|
||||
.execFileSync(
|
||||
'/usr/libexec/PlistBuddy',
|
||||
['-c', 'Print:CFBundleIdentifier', path.join(appPath, 'Info.plist')],
|
||||
{encoding: 'utf8'},
|
||||
)
|
||||
.trim();
|
||||
|
||||
console.log(`Launching ${bundleID}`);
|
||||
child_process.spawnSync('xcrun', ['simctl', 'launch', udid, bundleID], {stdio: 'inherit'});
|
||||
child_process.spawnSync('xcrun', ['simctl', 'launch', udid, bundleID], {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function runOnDevice(selectedDevice, scheme, xcodeProject, configuration, launchPackager, verbose, port) {
|
||||
return buildProject(xcodeProject, selectedDevice.udid, scheme, configuration, launchPackager, verbose, port)
|
||||
.then((appName) => {
|
||||
function runOnDevice(
|
||||
selectedDevice,
|
||||
scheme,
|
||||
xcodeProject,
|
||||
configuration,
|
||||
launchPackager,
|
||||
verbose,
|
||||
port,
|
||||
) {
|
||||
return buildProject(
|
||||
xcodeProject,
|
||||
selectedDevice.udid,
|
||||
scheme,
|
||||
configuration,
|
||||
launchPackager,
|
||||
verbose,
|
||||
port,
|
||||
).then(appName => {
|
||||
if (!appName) {
|
||||
appName = scheme;
|
||||
}
|
||||
const iosDeployInstallArgs = [
|
||||
'--bundle', getBuildPath(configuration, appName, true),
|
||||
'--id' , selectedDevice.udid,
|
||||
'--justlaunch'
|
||||
'--bundle',
|
||||
getBuildPath(configuration, appName, true),
|
||||
'--id',
|
||||
selectedDevice.udid,
|
||||
'--justlaunch',
|
||||
];
|
||||
console.log(`installing and launching your app on ${selectedDevice.name}...`);
|
||||
const iosDeployOutput = child_process.spawnSync('ios-deploy', iosDeployInstallArgs, {encoding: 'utf8'});
|
||||
console.log(
|
||||
`installing and launching your app on ${selectedDevice.name}...`,
|
||||
);
|
||||
const iosDeployOutput = child_process.spawnSync(
|
||||
'ios-deploy',
|
||||
iosDeployInstallArgs,
|
||||
{encoding: 'utf8'},
|
||||
);
|
||||
if (iosDeployOutput.error) {
|
||||
console.log('');
|
||||
console.log('** INSTALLATION FAILED **');
|
||||
@@ -188,22 +263,42 @@ function runOnDevice(selectedDevice, scheme, xcodeProject, configuration, launch
|
||||
});
|
||||
}
|
||||
|
||||
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',
|
||||
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',
|
||||
];
|
||||
console.log(`Building using "xcodebuild ${xcodebuildArgs.join(' ')}"`);
|
||||
let xcpretty;
|
||||
if (!verbose) {
|
||||
xcpretty = xcprettyAvailable() && child_process.spawn('xcpretty', [], { stdio: ['pipe', process.stdout, process.stderr] });
|
||||
xcpretty =
|
||||
xcprettyAvailable() &&
|
||||
child_process.spawn('xcpretty', [], {
|
||||
stdio: ['pipe', process.stdout, process.stderr],
|
||||
});
|
||||
}
|
||||
const buildProcess = child_process.spawn('xcodebuild', xcodebuildArgs, getProcessOptions(launchPackager, port));
|
||||
const buildProcess = child_process.spawn(
|
||||
'xcodebuild',
|
||||
xcodebuildArgs,
|
||||
getProcessOptions(launchPackager, port),
|
||||
);
|
||||
let buildOutput = '';
|
||||
buildProcess.stdout.on('data', function(data) {
|
||||
buildOutput += data.toString();
|
||||
@@ -221,9 +316,15 @@ function buildProject(xcodeProject, udid, scheme, configuration = 'Debug', launc
|
||||
xcpretty.stdin.end();
|
||||
}
|
||||
//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"
|
||||
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
|
||||
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
|
||||
}
|
||||
return buildProcess.error ? reject(buildProcess.error) : resolve();
|
||||
});
|
||||
@@ -231,13 +332,19 @@ function buildProject(xcodeProject, udid, scheme, configuration = 'Debug', launc
|
||||
}
|
||||
|
||||
function matchingDevice(devices, deviceName) {
|
||||
if (deviceName === true && devices.length === 1)
|
||||
{
|
||||
console.log(`Using first available device ${devices[0].name} due to lack of name supplied.`);
|
||||
if (deviceName === true && devices.length === 1) {
|
||||
console.log(
|
||||
`Using first available device ${
|
||||
devices[0].name
|
||||
} due to lack of name supplied.`,
|
||||
);
|
||||
return devices[0];
|
||||
}
|
||||
for (let i = devices.length - 1; i >= 0; i--) {
|
||||
if (devices[i].name === deviceName || formattedDeviceName(devices[i]) === deviceName) {
|
||||
if (
|
||||
devices[i].name === deviceName ||
|
||||
formattedDeviceName(devices[i]) === deviceName
|
||||
) {
|
||||
return devices[i];
|
||||
}
|
||||
}
|
||||
@@ -264,12 +371,12 @@ function printFoundDevices(devices) {
|
||||
function getProcessOptions(launchPackager, port) {
|
||||
if (launchPackager) {
|
||||
return {
|
||||
env: { ...process.env, RCT_METRO_PORT: port }
|
||||
env: {...process.env, RCT_METRO_PORT: port},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
env: { ...process.env, RCT_NO_LAUNCH_PACKAGER: true },
|
||||
env: {...process.env, RCT_NO_LAUNCH_PACKAGER: true},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -278,53 +385,66 @@ module.exports = {
|
||||
description: 'builds your app and starts it on iOS simulator',
|
||||
func: runIOS,
|
||||
examples: [
|
||||
{
|
||||
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"',
|
||||
}
|
||||
{
|
||||
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),
|
||||
},
|
||||
],
|
||||
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),
|
||||
}],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user