From 7c4c16710e6be0fc6ca86f8aead8330fca6b995a Mon Sep 17 00:00:00 2001 From: Jirat Ki Date: Thu, 23 Feb 2017 10:55:41 +0700 Subject: [PATCH] Install react, react-dom, and react-scripts at the same time (#1253) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Install react and react-dom along with react-scripts - Install react, react-dom and react-script in a same time - Move react-scripts to devDependencies. * Check if react, react-dom has been already installed - To backward compatibility with old CRA’s cli - In case old CRA doesn’t install react, react-don along with react-scripts * Use packageName to find script dependency - use packageName to find dependency - fix pathExists.sync * Check dependencies.react in package.json instead of actual files * Process exit when dependencies not found - Show error and exit when dependencies not found. - Log install show custom package name * Remove template string * Install dependencies if template is preseted * Remove dangling comma Resolves #1239 --- packages/create-react-app/index.js | 53 +++++++++++--- packages/react-scripts/scripts/init.js | 98 ++++++++++++++------------ 2 files changed, 97 insertions(+), 54 deletions(-) diff --git a/packages/create-react-app/index.js b/packages/create-react-app/index.js index 14fce0a9549..d8d28496cbf 100755 --- a/packages/create-react-app/index.js +++ b/packages/create-react-app/index.js @@ -40,7 +40,7 @@ var chalk = require('chalk'); -var currentNodeVersion = process.versions.node +var currentNodeVersion = process.versions.node; if (currentNodeVersion.split('.')[0] < 4) { console.error( chalk.red( @@ -124,7 +124,7 @@ function createApp(name, verbose, version, template) { var packageJson = { name: appName, version: '0.1.0', - private: true, + private: true }; fs.writeFileSync( path.join(root, 'package.json'), @@ -133,10 +133,6 @@ function createApp(name, verbose, version, template) { var originalDirectory = process.cwd(); process.chdir(root); - console.log('Installing packages. This might take a couple minutes.'); - console.log('Installing ' + chalk.cyan('react-scripts') + '...'); - console.log(); - run(root, appName, version, verbose, originalDirectory, template); } @@ -149,15 +145,15 @@ function shouldUseYarn() { } } -function install(packageToInstall, verbose, callback) { +function install(dependencies, verbose, callback) { var command; var args; if (shouldUseYarn()) { command = 'yarnpkg'; - args = [ 'add', '--dev', '--exact', packageToInstall]; + args = [ 'add', '--exact'].concat(dependencies); } else { command = 'npm'; - args = ['install', '--save-dev', '--save-exact', packageToInstall]; + args = ['install', '--save', '--save-exact'].concat(dependencies); } if (verbose) { @@ -174,7 +170,13 @@ function run(root, appName, version, verbose, originalDirectory, template) { var packageToInstall = getInstallPackage(version); var packageName = getPackageName(packageToInstall); - install(packageToInstall, verbose, function(code, command, args) { + var allDependencies = ['react', 'react-dom', packageToInstall]; + + console.log('Installing packages. This might take a couple minutes.'); + console.log('Installing ' + chalk.cyan('react, react-dom, ' + packageName) + '...'); + console.log(); + + install(allDependencies, verbose, function(code, command, args) { if (code !== 0) { console.error(chalk.cyan(command + ' ' + args.join(' ')) + ' failed'); process.exit(1); @@ -182,6 +184,10 @@ function run(root, appName, version, verbose, originalDirectory, template) { checkNodeVersion(packageName); + // Since react-scripts has been installed with --save + // We need to move it into devDependencies and rewrite package.json + moveReactScriptsToDev(packageName); + var scriptsPath = path.resolve( process.cwd(), 'node_modules', @@ -273,6 +279,33 @@ function checkAppName(appName) { } } +function moveReactScriptsToDev(packageName) { + var packagePath = path.join(process.cwd(), 'package.json'); + var packageJson = require(packagePath); + + if (typeof packageJson.dependencies === 'undefined') { + console.error( + chalk.red('Missing dependencies in package.json') + ); + process.exit(1); + } + + var packageVersion = packageJson.dependencies[packageName]; + + if (typeof packageVersion === 'undefined') { + console.error( + chalk.red('Unable to find ' + packageName + ' in package.json') + ); + process.exit(1); + } + + packageJson.devDependencies = packageJson.devDependencies || {}; + packageJson.devDependencies[packageName] = packageVersion; + delete packageJson.dependencies[packageName]; + + fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2)); +} + // If project only contains files generated by GH, it’s safe. // We also special case IJ-based products .idea because it integrates with CRA: // https://github.com/facebookincubator/create-react-app/pull/368#issuecomment-243446094 diff --git a/packages/react-scripts/scripts/init.js b/packages/react-scripts/scripts/init.js index 864005ecac2..aa62265ccc5 100644 --- a/packages/react-scripts/scripts/init.js +++ b/packages/react-scripts/scripts/init.js @@ -64,8 +64,6 @@ module.exports = function(appPath, appName, verbose, originalDirectory, template } }); - // Run yarn or npm for react and react-dom - // TODO: having to do two npm/yarn installs is bad, can we avoid it? var command; var args; @@ -92,53 +90,65 @@ module.exports = function(appPath, appName, verbose, originalDirectory, template fs.unlinkSync(templateDependenciesPath); } - console.log('Installing react and react-dom using ' + command + '...'); - console.log(); + // Install react and react-dom for backward compatibility with old CRA cli + // which doesn't install react and react-dom along with react-scripts + // or template is presetend (via --internal-testing-template) + if (!isReactInstalled(appPackage) || template) { + console.log('Installing react and react-dom using ' + command + '...'); + console.log(); - var proc = spawn(command, args, {stdio: 'inherit'}); - proc.on('close', function (code) { - if (code !== 0) { + var proc = spawn.sync(command, args, {stdio: 'inherit'}); + if (proc.status !== 0) { console.error('`' + command + ' ' + args.join(' ') + '` failed'); return; } + } - // Display the most elegant way to cd. - // This needs to handle an undefined originalDirectory for - // backward compatibility with old global-cli's. - var cdpath; - if (originalDirectory && - path.join(originalDirectory, appName) === appPath) { - cdpath = appName; - } else { - cdpath = appPath; - } + // Display the most elegant way to cd. + // This needs to handle an undefined originalDirectory for + // backward compatibility with old global-cli's. + var cdpath; + if (originalDirectory && + path.join(originalDirectory, appName) === appPath) { + cdpath = appName; + } else { + cdpath = appPath; + } + console.log(); + console.log('Success! Created ' + appName + ' at ' + appPath); + console.log('Inside that directory, you can run several commands:'); + console.log(); + console.log(chalk.cyan(' ' + command + ' start')); + console.log(' Starts the development server.'); + console.log(); + console.log(chalk.cyan(' ' + command + ' run build')); + console.log(' Bundles the app into static files for production.'); + console.log(); + console.log(chalk.cyan(' ' + command + ' test')); + console.log(' Starts the test runner.'); + console.log(); + console.log(chalk.cyan(' ' + command + ' run eject')); + console.log(' Removes this tool and copies build dependencies, configuration files'); + console.log(' and scripts into the app directory. If you do this, you can’t go back!'); + console.log(); + console.log('We suggest that you begin by typing:'); + console.log(); + console.log(chalk.cyan(' cd'), cdpath); + console.log(' ' + chalk.cyan(command + ' start')); + if (readmeExists) { console.log(); - console.log('Success! Created ' + appName + ' at ' + appPath); - console.log('Inside that directory, you can run several commands:'); - console.log(); - console.log(chalk.cyan(' ' + command + ' start')); - console.log(' Starts the development server.'); - console.log(); - console.log(chalk.cyan(' ' + command + ' run build')); - console.log(' Bundles the app into static files for production.'); - console.log(); - console.log(chalk.cyan(' ' + command + ' test')); - console.log(' Starts the test runner.'); - console.log(); - console.log(chalk.cyan(' ' + command + ' run eject')); - console.log(' Removes this tool and copies build dependencies, configuration files'); - console.log(' and scripts into the app directory. If you do this, you can’t go back!'); - console.log(); - console.log('We suggest that you begin by typing:'); - console.log(); - console.log(chalk.cyan(' cd'), cdpath); - console.log(' ' + chalk.cyan(command + ' start')); - if (readmeExists) { - console.log(); - console.log(chalk.yellow('You had a `README.md` file, we renamed it to `README.old.md`')); - } - console.log(); - console.log('Happy hacking!'); - }); + console.log(chalk.yellow('You had a `README.md` file, we renamed it to `README.old.md`')); + } + console.log(); + console.log('Happy hacking!'); }; + +function isReactInstalled(appPackage) { + var dependencies = appPackage.dependencies || {}; + + return ( + typeof dependencies.react !== 'undefined' && + typeof dependencies['react-dom'] !== 'undefined' + ) +}