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:
Martin Konicek
2016-12-12 11:36:37 +00:00
parent 70d4023856
commit ca403f0a5f
4 changed files with 125 additions and 45 deletions
+61 -42
View File
@@ -18,6 +18,7 @@ const TerminalAdapter = require('yeoman-environment/lib/adapter');
const log = require('npmlog');
const rimraf = require('rimraf');
const semver = require('semver');
const yarn = require('./yarn');
const {
checkDeclaredVersion,
@@ -31,8 +32,7 @@ log.heading = 'git-upgrade';
/**
* Promisify the callback-based shelljs function exec
* @param command
* @param logOutput
* @param logOutput If true, log the stdout of the command.
* @returns {Promise}
*/
function exec(command, logOutput) {
@@ -62,42 +62,37 @@ stdout: ${stdout}`));
})
}
/**
+ * Returns two objects:
+ * - Parsed node_modules/react-native/package.json
+ * - Parsed package.json
+ */
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'
);
function parseJsonFile(path, useYarn) {
const installHint = useYarn ?
'Make sure you ran "yarn" and that you are inside a React Native project.' :
'Make sure you ran "npm install" and that you are inside a React Native project.';
let fileContents;
try {
const reactNativeNodeModulesPak = JSON.parse(fs.readFileSync(reactNativeNodeModulesPakPath, 'utf8'));
const reactNodeModulesPak = JSON.parse(fs.readFileSync(reactNodeModulesPakPath, 'utf8'));
const pak = JSON.parse(fs.readFileSync(pakPath, 'utf8'));
return {reactNativeNodeModulesPak, reactNodeModulesPak, pak};
fileContents = fs.readFileSync(path, 'utf8');
} catch (err) {
throw new Error(
'Unable to find one of "' + pakPath + '", "' + rnPakPath + '" or "' + reactPakPath + '". ' +
'Make sure you ran "npm install" and that you are inside a React Native project.'
)
throw new Error('Cannot find "' + path + '". ' + installHint);
}
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'.
* `undefined` if no argument passed.
@@ -204,8 +214,10 @@ async function run(requestedVersion, cliArgs) {
try {
await checkForUpdates();
const useYarn = shouldUseYarn(cliArgs, path.resolve(process.cwd()));
log.info('Read package.json files');
const {reactNativeNodeModulesPak, reactNodeModulesPak, pak} = readPackageFiles();
const {reactNativeNodeModulesPak, reactNodeModulesPak, pak} = readPackageFiles(useYarn);
const appName = pak.name;
const currentVersion = reactNativeNodeModulesPak.version;
const currentReactVersion = reactNodeModulesPak.version;
@@ -218,7 +230,7 @@ async function run(requestedVersion, cliArgs) {
checkDeclaredVersion(declaredVersion);
log.info('Check matching versions');
checkMatchingVersions(currentVersion, declaredVersion);
checkMatchingVersions(currentVersion, declaredVersion, useYarn);
log.info('Check React peer dependency');
checkReactPeerDependency(currentVersion, declaredReactVersion);
@@ -231,6 +243,8 @@ async function run(requestedVersion, cliArgs) {
const viewOutput = await exec(viewCommand, verbose).then(JSON.parse);
const newVersion = viewOutput.version;
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');
checkNewVersionValid(newVersion, requestedVersion);
@@ -264,9 +278,14 @@ async function run(requestedVersion, cliArgs) {
await exec('git commit -m "Old version" --allow-empty', verbose);
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;
if (!semver.satisfies(currentReactVersion, newReactVersionRange)) {
if (newReactVersionRange && !semver.satisfies(currentReactVersion, newReactVersionRange)) {
// Install React as well to avoid unmet peer dependency
installCommand += ' react@' + newReactVersionRange;
}
@@ -296,8 +315,8 @@ async function run(requestedVersion, cliArgs) {
await exec(`git apply --3way ${patchPath}`, true);
} catch (err) {
log.warn(
'The upgrade process succeeded but there might be conflicts to be resolved.\n' +
'See above for the list of files that had merge conflicts.');
'The upgrade process succeeded but there might be conflicts to be resolved. ' +
'See above for the list of files that have merge conflicts.');
} finally {
log.info('Upgrade done');
if (cliArgs.verbose) {