Allow configuring the way CLI installs react-native

Summary:
- Added an option to override the install command in CI environments (see comment in code)
- Simplify the verbose mode
- Simplify how options are passed round

Test plan (only tested on Mac OS):

    react-native init TestApp
    react-native init TestApp --version 0.36

In both cases the app was generated, package.json contained the correct version (latest, or 0.36).

    react-native init TestApp --verbose

Saw progress bar.

    react-native init InstallCommandTest --installCommand "npm install bad-package-doesnt-exist"

404 error is printed to stdout correctly.

    react-native init TestApp --installCommand "npm install react-native"

The app was generated.

    react-native init InstallCommandTest --installCommand "npm install react-native --verbose" --verbose

Saw verbose output from npm.

Reviewed By: bestander

Differential Revision: D4284642

fbshipit-source-id: f2cdee52ab64831ae3ca064d50f23c5f73a0301f
This commit is contained in:
Martin Konicek 2016-12-06 13:21:35 -08:00 committed by Facebook Github Bot
parent d21aa92480
commit 76cd2f7bf2
2 changed files with 53 additions and 61 deletions

View File

@ -39,7 +39,6 @@ var fs = require('fs');
var path = require('path'); var path = require('path');
var exec = require('child_process').exec; var exec = require('child_process').exec;
var execSync = require('child_process').execSync; var execSync = require('child_process').execSync;
var spawn = require('child_process').spawn;
var chalk = require('chalk'); var chalk = require('chalk');
var prompt = require('prompt'); var prompt = require('prompt');
var semver = require('semver'); var semver = require('semver');
@ -55,7 +54,9 @@ var semver = require('semver');
* - "https://registry.npmjs.org/react-native/-/react-native-0.20.0.tgz" - a .tgz archive from any npm repo * - "https://registry.npmjs.org/react-native/-/react-native-0.20.0.tgz" - a .tgz archive from any npm repo
* - "/Users/home/react-native/react-native-0.22.0.tgz" - for package prepared with `npm pack`, useful for e2e tests * - "/Users/home/react-native/react-native-0.22.0.tgz" - for package prepared with `npm pack`, useful for e2e tests
*/ */
var argv = require('minimist')(process.argv.slice(2));
var options = require('minimist')(process.argv.slice(2));
checkForVersionArgument(options);
var CLI_MODULE_PATH = function() { var CLI_MODULE_PATH = function() {
return path.resolve( return path.resolve(
@ -102,20 +103,17 @@ function getYarnVersionIfAvailable() {
} }
} }
checkForVersionArgument();
var cli; var cli;
var cliPath = CLI_MODULE_PATH(); var cliPath = CLI_MODULE_PATH();
if (fs.existsSync(cliPath)) { if (fs.existsSync(cliPath)) {
cli = require(cliPath); cli = require(cliPath);
} }
// minimist api var commands = options._;
var commands = argv._;
if (cli) { if (cli) {
cli.run(); cli.run();
} else { } else {
if (argv._.length === 0 && (argv.h || argv.help)) { if (options._.length === 0 && (options.h || options.help)) {
console.log([ console.log([
'', '',
' Usage: react-native [command] [options]', ' Usage: react-native [command] [options]',
@ -149,8 +147,7 @@ if (cli) {
); );
process.exit(1); process.exit(1);
} else { } else {
const rnPackage = argv.version; init(commands[1], options);
init(commands[1], argv.verbose, rnPackage, argv.npm);
} }
break; break;
default: default:
@ -186,22 +183,22 @@ function validateProjectName(name) {
/** /**
* @param name Project name, e.g. 'AwesomeApp'. * @param name Project name, e.g. 'AwesomeApp'.
* @param verbose If true, will run 'npm install' in verbose mode (for debugging). * @param options.verbose If true, will run 'npm install' in verbose mode (for debugging).
* @param rnPackage Version of React Native to install, e.g. '0.38.0'. * @param options.version Version of React Native to install, e.g. '0.38.0'.
* @param forceNpmClient If true, always use the npm command line client, * @param options.npm If true, always use the npm command line client,
* don't use yarn even if available. * don't use yarn even if available.
*/ */
function init(name, verbose, rnPackage, forceNpmClient) { function init(name, options) {
validateProjectName(name); validateProjectName(name);
if (fs.existsSync(name)) { if (fs.existsSync(name)) {
createAfterConfirmation(name, verbose, rnPackage, forceNpmClient); createAfterConfirmation(name, options);
} else { } else {
createProject(name, verbose, rnPackage, forceNpmClient); createProject(name, options);
} }
} }
function createAfterConfirmation(name, verbose, rnPackage, forceNpmClient) { function createAfterConfirmation(name, options) {
prompt.start(); prompt.start();
var property = { var property = {
@ -214,7 +211,7 @@ function createAfterConfirmation(name, verbose, rnPackage, forceNpmClient) {
prompt.get(property, function (err, result) { prompt.get(property, function (err, result) {
if (result.yesno[0] === 'y') { if (result.yesno[0] === 'y') {
createProject(name, verbose, rnPackage, forceNpmClient); createProject(name, options);
} else { } else {
console.log('Project initialization canceled'); console.log('Project initialization canceled');
process.exit(); process.exit();
@ -222,7 +219,7 @@ function createAfterConfirmation(name, verbose, rnPackage, forceNpmClient) {
}); });
} }
function createProject(name, verbose, rnPackage, forceNpmClient) { function createProject(name, options) {
var root = path.resolve(name); var root = path.resolve(name);
var projectName = path.basename(root); var projectName = path.basename(root);
@ -246,11 +243,7 @@ function createProject(name, verbose, rnPackage, forceNpmClient) {
fs.writeFileSync(path.join(root, 'package.json'), JSON.stringify(packageJson)); fs.writeFileSync(path.join(root, 'package.json'), JSON.stringify(packageJson));
process.chdir(root); process.chdir(root);
if (verbose) { run(root, projectName, options);
runVerbose(root, projectName, rnPackage, forceNpmClient);
} else {
run(root, projectName, rnPackage, forceNpmClient);
}
} }
function getInstallPackage(rnPackage) { function getInstallPackage(rnPackage) {
@ -265,46 +258,45 @@ function getInstallPackage(rnPackage) {
return packageToInstall; return packageToInstall;
} }
function run(root, projectName, rnPackage, forceNpmClient) { function run(root, projectName, options) {
// E.g. '0.38' or '/path/to/archive.tgz'
const rnPackage = options.version;
const forceNpmClient = options.npm;
const yarnVersion = (!forceNpmClient) && getYarnVersionIfAvailable(); const yarnVersion = (!forceNpmClient) && getYarnVersionIfAvailable();
var installCommand; var installCommand;
if (options.installCommand) {
// In CI environments it can be useful to provide a custom command,
// to set up and use an offline mirror for installing dependencies, for example.
installCommand = options.installCommand;
} else {
if (yarnVersion) { if (yarnVersion) {
console.log('Using yarn v' + yarnVersion); console.log('Using yarn v' + yarnVersion);
console.log('Installing ' + getInstallPackage(rnPackage) + '...'); console.log('Installing ' + getInstallPackage(rnPackage) + '...');
installCommand = 'yarn add ' + getInstallPackage(rnPackage) + ' --exact'; installCommand = 'yarn add ' + getInstallPackage(rnPackage) + ' --exact';
if (options.verbose) {
installCommand += ' --verbose';
}
} else { } else {
console.log('Installing ' + getInstallPackage(rnPackage) + '...'); console.log('Installing ' + getInstallPackage(rnPackage) + '. This might take a while...');
if (!forceNpmClient) { if (!forceNpmClient) {
console.log('Consider installing yarn to make this faster: https://yarnpkg.com'); console.log('Consider installing yarn to make this faster: https://yarnpkg.com');
} }
installCommand = 'npm install --save --save-exact ' + getInstallPackage(rnPackage); installCommand = 'npm install --save --save-exact ' + getInstallPackage(rnPackage);
if (options.verbose) {
installCommand += ' --verbose';
} }
exec(installCommand, function(err, stdout, stderr) { }
if (err) { }
console.log(stdout); try {
console.error(stderr); execSync(installCommand, {stdio: 'inherit'});
} catch (err) {
console.error(err);
console.error('Command `' + installCommand + '` failed.'); console.error('Command `' + installCommand + '` failed.');
process.exit(1); process.exit(1);
} }
checkNodeVersion(); checkNodeVersion();
cli = require(CLI_MODULE_PATH()); cli = require(CLI_MODULE_PATH());
cli.init(root, projectName); cli.init(root, projectName);
});
}
function runVerbose(root, projectName, rnPackage, forceNpmClient) {
// Use npm client, yarn doesn't support --verbose yet
console.log('Installing ' + getInstallPackage(rnPackage) + ' from npm. This might take a while...');
var proc = spawn(/^win/.test(process.platform) ? 'npm.cmd' : 'npm', ['install', '--verbose', '--save', '--save-exact', getInstallPackage(rnPackage)], {stdio: 'inherit'});
proc.on('close', function (code) {
if (code !== 0) {
console.error('`npm install --save --save-exact react-native` failed');
return;
}
cli = require(CLI_MODULE_PATH());
cli.init(root, projectName);
});
} }
function checkNodeVersion() { function checkNodeVersion() {
@ -323,8 +315,8 @@ function checkNodeVersion() {
} }
} }
function checkForVersionArgument() { function checkForVersionArgument(options) {
if (argv._.length === 0 && (argv.v || argv.version)) { if (options._.length === 0 && (options.v || options.version)) {
console.log('react-native-cli: ' + require('./package.json').version); console.log('react-native-cli: ' + require('./package.json').version);
try { try {
console.log('react-native: ' + require(REACT_NATIVE_PACKAGE_JSON_PATH()).version); console.log('react-native: ' + require(REACT_NATIVE_PACKAGE_JSON_PATH()).version);

View File

@ -1,6 +1,6 @@
{ {
"name": "react-native-cli", "name": "react-native-cli",
"version": "1.3.0", "version": "2.0.0",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"description": "The React Native CLI tools", "description": "The React Native CLI tools",
"main": "index.js", "main": "index.js",