Use Yarn if available
Summary: **Motivation** If the project is using yarn to manage its dependencies, we should be running 'yarn add' to upgrade the React Native dependency, rather than 'npm install'. **Test plan (required)** Running in a project that's in a bad state: Error: react-native version in "package.json" (0.29.2) doesn't match the installed version in "node_modules" (0.38.0). Try running "yarn" to fix this. Removed yarn.lock file, ran again: Error: react-native version in "package.json" (0.29.2) doesn't match the installed version in "node_modules" (0.38.0). Try running "npm install" to fix this. Running inside a folder that doesn't contain a `package.json` file: Error: Unable to find "/Users/mkonicek/Zero29App/package.json" or "/Users/mkonicek/Zero29App/node_modules/react-native/package.json". Make sure you ran "yarn" and that you are inside a React Native project. Removed yarn.lock file, ran again: Error: Unable to find "/Users/mkonicek/Zero29App/package.json" or "/Users/ Closes https://github.com/facebook/react-native/pull/11225 Differential Revision: D4261102 Pulled By: mkonicek fbshipit-source-id: b44ae91fe46f2c81b14616ca2868cc171692c895
This commit is contained in:
parent
23423774ba
commit
6bee16a63d
|
@ -19,12 +19,14 @@ function checkDeclaredVersion(declaredVersion) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkMatchingVersions(currentVersion, declaredVersion) {
|
function checkMatchingVersions(currentVersion, declaredVersion, useYarn) {
|
||||||
if (!semver.satisfies(currentVersion, declaredVersion)) {
|
if (!semver.satisfies(currentVersion, declaredVersion)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'react-native version in "package.json" (' + declaredVersion + ') doesn\'t match ' +
|
'react-native version in "package.json" (' + declaredVersion + ') doesn\'t match ' +
|
||||||
'the installed version in "node_modules" (' + currentVersion + ').\n' +
|
'the installed version in "node_modules" (' + currentVersion + ').\n' +
|
||||||
'Try running "npm install" to fix this.'
|
(useYarn ?
|
||||||
|
'Try running "yarn" to fix this.' :
|
||||||
|
'Try running "npm install" to fix this.')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ const TerminalAdapter = require('yeoman-environment/lib/adapter');
|
||||||
const log = require('npmlog');
|
const log = require('npmlog');
|
||||||
const rimraf = require('rimraf');
|
const rimraf = require('rimraf');
|
||||||
const semver = require('semver');
|
const semver = require('semver');
|
||||||
|
const yarn = require('./yarn');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
checkDeclaredVersion,
|
checkDeclaredVersion,
|
||||||
|
@ -31,8 +32,7 @@ log.heading = 'git-upgrade';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Promisify the callback-based shelljs function exec
|
* Promisify the callback-based shelljs function exec
|
||||||
* @param command
|
* @param logOutput If true, log the stdout of the command.
|
||||||
* @param logOutput
|
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
function exec(command, logOutput) {
|
function exec(command, logOutput) {
|
||||||
|
@ -62,42 +62,37 @@ stdout: ${stdout}`));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function parseJsonFile(path, useYarn) {
|
||||||
+ * Returns two objects:
|
const installHint = useYarn ?
|
||||||
+ * - Parsed node_modules/react-native/package.json
|
'Make sure you ran "yarn" and that you are inside a React Native project.' :
|
||||||
+ * - Parsed package.json
|
'Make sure you ran "npm install" and that you are inside a React Native project.';
|
||||||
+ */
|
let fileContents;
|
||||||
function readPackageFiles() {
|
|
||||||
const reactNativeNodeModulesPakPath = path.resolve(
|
|
||||||
process.cwd(),
|
|
||||||
'node_modules',
|
|
||||||
'react-native',
|
|
||||||
'package.json'
|
|
||||||
);
|
|
||||||
|
|
||||||
const reactNodeModulesPakPath = path.resolve(
|
|
||||||
process.cwd(),
|
|
||||||
'node_modules',
|
|
||||||
'react',
|
|
||||||
'package.json'
|
|
||||||
);
|
|
||||||
|
|
||||||
const pakPath = path.resolve(
|
|
||||||
process.cwd(),
|
|
||||||
'package.json'
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const reactNativeNodeModulesPak = JSON.parse(fs.readFileSync(reactNativeNodeModulesPakPath, 'utf8'));
|
fileContents = fs.readFileSync(path, 'utf8');
|
||||||
const reactNodeModulesPak = JSON.parse(fs.readFileSync(reactNodeModulesPakPath, 'utf8'));
|
|
||||||
const pak = JSON.parse(fs.readFileSync(pakPath, 'utf8'));
|
|
||||||
|
|
||||||
return {reactNativeNodeModulesPak, reactNodeModulesPak, pak};
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error(
|
throw new Error('Cannot find "' + path + '". ' + installHint);
|
||||||
'Unable to find one of "' + pakPath + '", "' + rnPakPath + '" or "' + reactPakPath + '". ' +
|
}
|
||||||
'Make sure you ran "npm install" and that you are inside a React Native project.'
|
try {
|
||||||
)
|
return JSON.parse(fileContents);
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error('Cannot parse "' + path + '": ' + err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function readPackageFiles(useYarn) {
|
||||||
|
const reactNativeNodeModulesPakPath = path.resolve(
|
||||||
|
process.cwd(), 'node_modules', 'react-native', 'package.json'
|
||||||
|
);
|
||||||
|
const reactNodeModulesPakPath = path.resolve(
|
||||||
|
process.cwd(), 'node_modules', 'react', 'package.json'
|
||||||
|
);
|
||||||
|
const pakPath = path.resolve(
|
||||||
|
process.cwd(), 'package.json'
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
reactNativeNodeModulesPak: parseJsonFile(reactNativeNodeModulesPakPath),
|
||||||
|
reactNodeModulesPak: parseJsonFile(reactNodeModulesPakPath),
|
||||||
|
pak: parseJsonFile(pakPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,6 +186,21 @@ async function checkForUpdates() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, use yarn instead of the npm client to upgrade the project.
|
||||||
|
*/
|
||||||
|
function shouldUseYarn(cliArgs, projectDir) {
|
||||||
|
if (cliArgs && cliArgs.npm) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const yarnVersion = yarn.getYarnVersionIfAvailable();
|
||||||
|
if (yarnVersion && yarn.isProjectUsingYarn(projectDir)) {
|
||||||
|
log.info('Using yarn ' + yarnVersion);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param requestedVersion The version argument, e.g. 'react-native-git-upgrade 0.38'.
|
* @param requestedVersion The version argument, e.g. 'react-native-git-upgrade 0.38'.
|
||||||
* `undefined` if no argument passed.
|
* `undefined` if no argument passed.
|
||||||
|
@ -204,8 +214,10 @@ async function run(requestedVersion, cliArgs) {
|
||||||
try {
|
try {
|
||||||
await checkForUpdates();
|
await checkForUpdates();
|
||||||
|
|
||||||
|
const useYarn = shouldUseYarn(cliArgs, path.resolve(process.cwd()));
|
||||||
|
|
||||||
log.info('Read package.json files');
|
log.info('Read package.json files');
|
||||||
const {reactNativeNodeModulesPak, reactNodeModulesPak, pak} = readPackageFiles();
|
const {reactNativeNodeModulesPak, reactNodeModulesPak, pak} = readPackageFiles(useYarn);
|
||||||
const appName = pak.name;
|
const appName = pak.name;
|
||||||
const currentVersion = reactNativeNodeModulesPak.version;
|
const currentVersion = reactNativeNodeModulesPak.version;
|
||||||
const currentReactVersion = reactNodeModulesPak.version;
|
const currentReactVersion = reactNodeModulesPak.version;
|
||||||
|
@ -218,7 +230,7 @@ async function run(requestedVersion, cliArgs) {
|
||||||
checkDeclaredVersion(declaredVersion);
|
checkDeclaredVersion(declaredVersion);
|
||||||
|
|
||||||
log.info('Check matching versions');
|
log.info('Check matching versions');
|
||||||
checkMatchingVersions(currentVersion, declaredVersion);
|
checkMatchingVersions(currentVersion, declaredVersion, useYarn);
|
||||||
|
|
||||||
log.info('Check React peer dependency');
|
log.info('Check React peer dependency');
|
||||||
checkReactPeerDependency(currentVersion, declaredReactVersion);
|
checkReactPeerDependency(currentVersion, declaredReactVersion);
|
||||||
|
@ -231,6 +243,8 @@ async function run(requestedVersion, cliArgs) {
|
||||||
const viewOutput = await exec(viewCommand, verbose).then(JSON.parse);
|
const viewOutput = await exec(viewCommand, verbose).then(JSON.parse);
|
||||||
const newVersion = viewOutput.version;
|
const newVersion = viewOutput.version;
|
||||||
const newReactVersionRange = viewOutput['peerDependencies.react'];
|
const newReactVersionRange = viewOutput['peerDependencies.react'];
|
||||||
|
// Print which versions we're upgrading to
|
||||||
|
log.info('Upgrading to React Native ' + newVersion + (newReactVersionRange ? ', React ' + newReactVersionRange : ''));
|
||||||
|
|
||||||
log.info('Check new version');
|
log.info('Check new version');
|
||||||
checkNewVersionValid(newVersion, requestedVersion);
|
checkNewVersionValid(newVersion, requestedVersion);
|
||||||
|
@ -264,9 +278,14 @@ async function run(requestedVersion, cliArgs) {
|
||||||
await exec('git commit -m "Old version" --allow-empty', verbose);
|
await exec('git commit -m "Old version" --allow-empty', verbose);
|
||||||
|
|
||||||
log.info('Install the new version');
|
log.info('Install the new version');
|
||||||
let installCommand = 'npm install --save --color=always';
|
let installCommand;
|
||||||
|
if (useYarn) {
|
||||||
|
installCommand = 'yarn add';
|
||||||
|
} else {
|
||||||
|
installCommand = 'npm install --save --color=always';
|
||||||
|
}
|
||||||
installCommand += ' react-native@' + newVersion;
|
installCommand += ' react-native@' + newVersion;
|
||||||
if (!semver.satisfies(currentReactVersion, newReactVersionRange)) {
|
if (newReactVersionRange && !semver.satisfies(currentReactVersion, newReactVersionRange)) {
|
||||||
// Install React as well to avoid unmet peer dependency
|
// Install React as well to avoid unmet peer dependency
|
||||||
installCommand += ' react@' + newReactVersionRange;
|
installCommand += ' react@' + newReactVersionRange;
|
||||||
}
|
}
|
||||||
|
@ -296,8 +315,8 @@ async function run(requestedVersion, cliArgs) {
|
||||||
await exec(`git apply --3way ${patchPath}`, true);
|
await exec(`git apply --3way ${patchPath}`, true);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log.warn(
|
log.warn(
|
||||||
'The upgrade process succeeded but there might be conflicts to be resolved.\n' +
|
'The upgrade process succeeded but there might be conflicts to be resolved. ' +
|
||||||
'See above for the list of files that had merge conflicts.');
|
'See above for the list of files that have merge conflicts.');
|
||||||
} finally {
|
} finally {
|
||||||
log.info('Upgrade done');
|
log.info('Upgrade done');
|
||||||
if (cliArgs.verbose) {
|
if (cliArgs.verbose) {
|
||||||
|
|
|
@ -27,7 +27,8 @@ if (argv._.length === 0 && (argv.h || argv.help)) {
|
||||||
'',
|
'',
|
||||||
' -h, --help output usage information',
|
' -h, --help output usage information',
|
||||||
' -v, --version output the version number',
|
' -v, --version output the version number',
|
||||||
' --verbose output',
|
' --verbose output debugging info',
|
||||||
|
' --npm force using the npm client even if your project uses yarn',
|
||||||
'',
|
'',
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const semver = require('semver');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use Yarn if available, it's much faster than the npm client.
|
||||||
|
* Return the version of yarn installed on the system, null if yarn is not available.
|
||||||
|
*/
|
||||||
|
function getYarnVersionIfAvailable() {
|
||||||
|
let yarnVersion;
|
||||||
|
try {
|
||||||
|
// execSync returns a Buffer -> convert to string
|
||||||
|
if (process.platform.startsWith('win')) {
|
||||||
|
yarnVersion = (execSync('yarn --version').toString() || '').trim();
|
||||||
|
} else {
|
||||||
|
yarnVersion = (execSync('yarn --version 2>/dev/null').toString() || '').trim();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// yarn < 0.16 has a 'missing manifest' bug
|
||||||
|
try {
|
||||||
|
if (semver.gte(yarnVersion, '0.16.0')) {
|
||||||
|
return yarnVersion;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Cannot parse yarn version: ' + yarnVersion);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that 'react-native init' itself used yarn to install React Native.
|
||||||
|
* When using an old global react-native-cli@1.0.0 (or older), we don't want
|
||||||
|
* to install React Native with npm, and React + Jest with yarn.
|
||||||
|
* Let's be safe and not mix yarn and npm in a single project.
|
||||||
|
* @param projectDir e.g. /Users/martin/AwesomeApp
|
||||||
|
*/
|
||||||
|
function isProjectUsingYarn(projectDir) {
|
||||||
|
return fs.existsSync(path.join(projectDir, 'yarn.lock'));
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getYarnVersionIfAvailable: getYarnVersionIfAvailable,
|
||||||
|
isProjectUsingYarn: isProjectUsingYarn,
|
||||||
|
};
|
Loading…
Reference in New Issue