Skip to content

Commit

Permalink
Use yarn when running inside yarn workspace. (#3997)
Browse files Browse the repository at this point in the history
* Run yarn after ejecting.

* On eject, choose to run yarn instead of npm if yarn is available.

* Move monorepo to react-dev-utils.

* Fix lint.

* Rename monorepo to workspaceUtils.

* Add react-dev-utils dep for create-react-app.

* getMonorepo -> findMonorepo
  • Loading branch information
bradfordlemley authored and gaearon committed Feb 10, 2018
1 parent c342677 commit 2c34d5b
Show file tree
Hide file tree
Showing 9 changed files with 87 additions and 50 deletions.
11 changes: 8 additions & 3 deletions packages/create-react-app/createReactApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const url = require('url');
const hyperquest = require('hyperquest');
const envinfo = require('envinfo');
const os = require('os');

const findMonorepo = require('react-dev-utils/workspaceUtils').findMonorepo;
const packageJson = require('./package.json');

// These files should be allowed to remain on a failed install,
Expand Down Expand Up @@ -185,7 +185,7 @@ function createApp(name, verbose, version, useNpm, template) {
JSON.stringify(packageJson, null, 2) + os.EOL
);

const useYarn = useNpm ? false : shouldUseYarn();
const useYarn = useNpm ? false : shouldUseYarn(root);
const originalDirectory = process.cwd();
process.chdir(root);
if (!useYarn && !checkThatNpmCanReadCwd()) {
Expand Down Expand Up @@ -221,7 +221,7 @@ function createApp(name, verbose, version, useNpm, template) {
run(root, appName, version, verbose, originalDirectory, template, useYarn);
}

function shouldUseYarn() {
function isYarnAvailable() {
try {
execSync('yarnpkg --version', { stdio: 'ignore' });
return true;
Expand All @@ -230,6 +230,11 @@ function shouldUseYarn() {
}
}

function shouldUseYarn(appDir) {
const mono = findMonorepo(appDir);
return (mono.isYarnWs && mono.isAppIncluded) || isYarnAvailable();
}

function install(root, useYarn, dependencies, verbose, isOnline) {
return new Promise((resolve, reject) => {
let command;
Expand Down
1 change: 1 addition & 0 deletions packages/create-react-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"envinfo": "3.4.2",
"fs-extra": "^5.0.0",
"hyperquest": "^2.1.2",
"react-dev-utils": "^5.0.0",
"semver": "^5.0.3",
"tar-pack": "^3.4.0",
"tmp": "0.0.33",
Expand Down
5 changes: 4 additions & 1 deletion packages/react-dev-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
"printHostingInstructions.js",
"WatchMissingNodeModulesPlugin.js",
"WebpackDevServerUtils.js",
"webpackHotDevClient.js"
"webpackHotDevClient.js",
"workspaceUtils.js"
],
"dependencies": {
"@babel/code-frame": "7.0.0-beta.38",
Expand All @@ -45,7 +46,9 @@
"detect-port-alt": "1.1.5",
"escape-string-regexp": "1.0.5",
"filesize": "3.5.11",
"find-pkg": "1.0.0",
"global-modules": "1.0.0",
"globby": "7.1.1",
"gzip-size": "4.1.0",
"inquirer": "5.0.0",
"is-root": "1.0.0",
Expand Down
53 changes: 53 additions & 0 deletions packages/react-dev-utils/workspaceUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* Copyright (c) 2018-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.
*/

'use strict';
const fs = require('fs');
const path = require('path');
const findPkg = require('find-pkg');
const globby = require('globby');

const findPkgs = (rootPath, globPatterns) => {
if (!globPatterns) {
return [];
}
const globOpts = {
cwd: rootPath,
strict: true,
absolute: true,
};
return globPatterns
.reduce(
(pkgs, pattern) =>
pkgs.concat(globby.sync(path.join(pattern, 'package.json'), globOpts)),
[]
)
.map(f => path.dirname(path.normalize(f)));
};

const findMonorepo = appDir => {
const monoPkgPath = findPkg.sync(path.resolve(appDir, '..'));
const monoPkg = monoPkgPath && require(monoPkgPath);
const patterns = monoPkg && monoPkg.workspaces;
const isYarnWs = Boolean(patterns);
const allPkgs = patterns && findPkgs(path.dirname(monoPkgPath), patterns);
const isIncluded = dir => allPkgs && allPkgs.indexOf(dir) !== -1;
const isAppIncluded = isIncluded(appDir);
const pkgs = allPkgs
? allPkgs.filter(f => fs.realpathSync(f) !== appDir)
: [];

return {
isAppIncluded,
isYarnWs,
pkgs,
};
};

module.exports = {
findMonorepo,
};
46 changes: 9 additions & 37 deletions packages/react-scripts/config/paths.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
const path = require('path');
const fs = require('fs');
const url = require('url');
const findPkg = require('find-pkg');
const globby = require('globby');
const findMonorepo = require('react-dev-utils/workspaceUtils').findMonorepo;

// Make sure any symlinks in the project folder are resolved:
// https://github.com/facebook/create-react-app/issues/637
Expand Down Expand Up @@ -58,7 +57,6 @@ module.exports = {
appIndexJs: resolveApp('src/index.js'),
appPackageJson: resolveApp('package.json'),
appSrc: resolveApp('src'),
yarnLockFile: resolveApp('yarn.lock'),
testsSetup: resolveApp('src/setupTests.js'),
appNodeModules: resolveApp('node_modules'),
publicUrl: getPublicUrl(resolveApp('package.json')),
Expand All @@ -80,7 +78,6 @@ module.exports = {
appIndexJs: resolveApp('src/index.js'),
appPackageJson: resolveApp('package.json'),
appSrc: resolveApp('src'),
yarnLockFile: resolveApp('yarn.lock'),
testsSetup: resolveApp('src/setupTests.js'),
appNodeModules: resolveApp('node_modules'),
publicUrl: getPublicUrl(resolveApp('package.json')),
Expand All @@ -106,7 +103,6 @@ if (useTemplate) {
appIndexJs: resolveOwn('template/src/index.js'),
appPackageJson: resolveOwn('package.json'),
appSrc: resolveOwn('template/src'),
yarnLockFile: resolveOwn('template/yarn.lock'),
testsSetup: resolveOwn('template/src/setupTests.js'),
appNodeModules: resolveOwn('node_modules'),
publicUrl: getPublicUrl(resolveOwn('package.json')),
Expand All @@ -120,40 +116,16 @@ if (useTemplate) {

module.exports.srcPaths = [module.exports.appSrc];

const findPkgs = (rootPath, globPatterns) => {
const globOpts = {
cwd: rootPath,
strict: true,
absolute: true,
};
return globPatterns
.reduce(
(pkgs, pattern) =>
pkgs.concat(globby.sync(path.join(pattern, 'package.json'), globOpts)),
[]
)
.map(f => path.dirname(path.normalize(f)));
};

const getMonorepoPkgPaths = () => {
const monoPkgPath = findPkg.sync(path.resolve(appDirectory, '..'));
if (monoPkgPath) {
// get monorepo config from yarn workspace
const pkgPatterns = require(monoPkgPath).workspaces;
if (pkgPatterns == null) {
return [];
}
const pkgPaths = findPkgs(path.dirname(monoPkgPath), pkgPatterns);
// only include monorepo pkgs if app itself is included in monorepo
if (pkgPaths.indexOf(appDirectory) !== -1) {
return pkgPaths.filter(f => fs.realpathSync(f) !== appDirectory);
}
}
return [];
};
module.exports.useYarn = fs.existsSync(
path.join(module.exports.appPath, 'yarn.lock')
);

if (checkForMonorepo) {
// if app is in a monorepo (lerna or yarn workspace), treat other packages in
// the monorepo as if they are app source
Array.prototype.push.apply(module.exports.srcPaths, getMonorepoPkgPaths());
const mono = findMonorepo(appDirectory);
if (mono.isAppIncluded) {
Array.prototype.push.apply(module.exports.srcPaths, mono.pkgs);
}
module.exports.useYarn = module.exports.useYarn || mono.isYarnWs;
}
2 changes: 0 additions & 2 deletions packages/react-scripts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,7 @@
"eslint-plugin-react": "7.5.1",
"extract-text-webpack-plugin": "3.0.2",
"file-loader": "1.1.6",
"find-pkg": "1.0.0",
"fs-extra": "5.0.0",
"globby": "7.1.1",
"graphql": "0.12.3",
"graphql-tag": "2.6.1",
"html-webpack-plugin": "2.30.1",
Expand Down
3 changes: 1 addition & 2 deletions packages/react-scripts/scripts/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ const { printBrowsers } = require('react-dev-utils/browsersHelper');
const measureFileSizesBeforeBuild =
FileSizeReporter.measureFileSizesBeforeBuild;
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
const useYarn = fs.existsSync(paths.yarnLockFile);

// These sizes are pretty large. We'll warn for bundles exceeding them.
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
Expand Down Expand Up @@ -112,7 +111,7 @@ checkBrowsers(paths.appPath)
publicUrl,
publicPath,
buildFolder,
useYarn
paths.useYarn
);
printBrowsers(paths.appPath);
},
Expand Down
2 changes: 1 addition & 1 deletion packages/react-scripts/scripts/eject.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ inquirer
}
}

if (fs.existsSync(paths.yarnLockFile)) {
if (paths.useYarn) {
const windowsCmdFilePath = path.join(
appPath,
'node_modules',
Expand Down
14 changes: 10 additions & 4 deletions packages/react-scripts/scripts/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ if (process.env.SKIP_PREFLIGHT_CHECK !== 'true') {
}
// @remove-on-eject-end

const fs = require('fs');
const chalk = require('chalk');
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
Expand All @@ -46,7 +45,6 @@ const paths = require('../config/paths');
const config = require('../config/webpack.config.dev');
const createDevServerConfig = require('../config/webpackDevServer.config');

const useYarn = fs.existsSync(paths.yarnLockFile);
const isInteractive = process.stdout.isTTY;

// Warn and crash if required files are missing
Expand All @@ -69,7 +67,9 @@ if (process.env.HOST) {
console.log(
`If this was unintentional, check that you haven't mistakenly set it in your shell.`
);
console.log(`Learn more here: ${chalk.yellow('http://bit.ly/CRA-advanced-config')}`);
console.log(
`Learn more here: ${chalk.yellow('http://bit.ly/CRA-advanced-config')}`
);
console.log();
}

Expand All @@ -91,7 +91,13 @@ checkBrowsers(paths.appPath)
const appName = require(paths.appPackageJson).name;
const urls = prepareUrls(protocol, HOST, port);
// Create a webpack compiler that is configured with custom messages.
const compiler = createCompiler(webpack, config, appName, urls, useYarn);
const compiler = createCompiler(
webpack,
config,
appName,
urls,
paths.useYarn
);
// Load proxy config
const proxySetting = require(paths.appPackageJson).proxy;
const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
Expand Down

0 comments on commit 2c34d5b

Please sign in to comment.