Skip to content

Commit 22f34e0

Browse files
bradfordlemleyakstuhl
authored andcommitted
Use yarn when running inside yarn workspace. (facebook#3997)
* 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
1 parent 84cc1ad commit 22f34e0

File tree

9 files changed

+87
-50
lines changed

9 files changed

+87
-50
lines changed

packages/create-react-app/createReactApp.js

+8-3
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const url = require('url');
4949
const hyperquest = require('hyperquest');
5050
const envinfo = require('envinfo');
5151
const os = require('os');
52-
52+
const findMonorepo = require('react-dev-utils/workspaceUtils').findMonorepo;
5353
const packageJson = require('./package.json');
5454

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

188-
const useYarn = useNpm ? false : shouldUseYarn();
188+
const useYarn = useNpm ? false : shouldUseYarn(root);
189189
const originalDirectory = process.cwd();
190190
process.chdir(root);
191191
if (!useYarn && !checkThatNpmCanReadCwd()) {
@@ -221,7 +221,7 @@ function createApp(name, verbose, version, useNpm, template) {
221221
run(root, appName, version, verbose, originalDirectory, template, useYarn);
222222
}
223223

224-
function shouldUseYarn() {
224+
function isYarnAvailable() {
225225
try {
226226
execSync('yarnpkg --version', { stdio: 'ignore' });
227227
return true;
@@ -230,6 +230,11 @@ function shouldUseYarn() {
230230
}
231231
}
232232

233+
function shouldUseYarn(appDir) {
234+
const mono = findMonorepo(appDir);
235+
return (mono.isYarnWs && mono.isAppIncluded) || isYarnAvailable();
236+
}
237+
233238
function install(root, useYarn, dependencies, verbose, isOnline) {
234239
return new Promise((resolve, reject) => {
235240
let command;

packages/create-react-app/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"envinfo": "3.4.2",
2828
"fs-extra": "^5.0.0",
2929
"hyperquest": "^2.1.2",
30+
"react-dev-utils": "^5.0.0",
3031
"semver": "^5.0.3",
3132
"tar-pack": "^3.4.0",
3233
"tmp": "0.0.33",

packages/react-dev-utils/package.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
"printHostingInstructions.js",
3535
"WatchMissingNodeModulesPlugin.js",
3636
"WebpackDevServerUtils.js",
37-
"webpackHotDevClient.js"
37+
"webpackHotDevClient.js",
38+
"workspaceUtils.js"
3839
],
3940
"dependencies": {
4041
"@babel/code-frame": "7.0.0-beta.38",
@@ -45,7 +46,9 @@
4546
"detect-port-alt": "1.1.5",
4647
"escape-string-regexp": "1.0.5",
4748
"filesize": "3.5.11",
49+
"find-pkg": "1.0.0",
4850
"global-modules": "1.0.0",
51+
"globby": "7.1.1",
4952
"gzip-size": "4.1.0",
5053
"inquirer": "5.0.0",
5154
"is-root": "1.0.0",
+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* Copyright (c) 2018-present, Facebook, Inc.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
'use strict';
9+
const fs = require('fs');
10+
const path = require('path');
11+
const findPkg = require('find-pkg');
12+
const globby = require('globby');
13+
14+
const findPkgs = (rootPath, globPatterns) => {
15+
if (!globPatterns) {
16+
return [];
17+
}
18+
const globOpts = {
19+
cwd: rootPath,
20+
strict: true,
21+
absolute: true,
22+
};
23+
return globPatterns
24+
.reduce(
25+
(pkgs, pattern) =>
26+
pkgs.concat(globby.sync(path.join(pattern, 'package.json'), globOpts)),
27+
[]
28+
)
29+
.map(f => path.dirname(path.normalize(f)));
30+
};
31+
32+
const findMonorepo = appDir => {
33+
const monoPkgPath = findPkg.sync(path.resolve(appDir, '..'));
34+
const monoPkg = monoPkgPath && require(monoPkgPath);
35+
const patterns = monoPkg && monoPkg.workspaces;
36+
const isYarnWs = Boolean(patterns);
37+
const allPkgs = patterns && findPkgs(path.dirname(monoPkgPath), patterns);
38+
const isIncluded = dir => allPkgs && allPkgs.indexOf(dir) !== -1;
39+
const isAppIncluded = isIncluded(appDir);
40+
const pkgs = allPkgs
41+
? allPkgs.filter(f => fs.realpathSync(f) !== appDir)
42+
: [];
43+
44+
return {
45+
isAppIncluded,
46+
isYarnWs,
47+
pkgs,
48+
};
49+
};
50+
51+
module.exports = {
52+
findMonorepo,
53+
};

packages/react-scripts/config/paths.js

+9-37
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111
const path = require('path');
1212
const fs = require('fs');
1313
const url = require('url');
14-
const findPkg = require('find-pkg');
15-
const globby = require('globby');
14+
const findMonorepo = require('react-dev-utils/workspaceUtils').findMonorepo;
1615

1716
// Make sure any symlinks in the project folder are resolved:
1817
// https://github.com/facebook/create-react-app/issues/637
@@ -58,7 +57,6 @@ module.exports = {
5857
appIndexJs: resolveApp('src/index.js'),
5958
appPackageJson: resolveApp('package.json'),
6059
appSrc: resolveApp('src'),
61-
yarnLockFile: resolveApp('yarn.lock'),
6260
testsSetup: resolveApp('src/setupTests.js'),
6361
appNodeModules: resolveApp('node_modules'),
6462
publicUrl: getPublicUrl(resolveApp('package.json')),
@@ -80,7 +78,6 @@ module.exports = {
8078
appIndexJs: resolveApp('src/index.js'),
8179
appPackageJson: resolveApp('package.json'),
8280
appSrc: resolveApp('src'),
83-
yarnLockFile: resolveApp('yarn.lock'),
8481
testsSetup: resolveApp('src/setupTests.js'),
8582
appNodeModules: resolveApp('node_modules'),
8683
publicUrl: getPublicUrl(resolveApp('package.json')),
@@ -106,7 +103,6 @@ if (useTemplate) {
106103
appIndexJs: resolveOwn('template/src/index.js'),
107104
appPackageJson: resolveOwn('package.json'),
108105
appSrc: resolveOwn('template/src'),
109-
yarnLockFile: resolveOwn('template/yarn.lock'),
110106
testsSetup: resolveOwn('template/src/setupTests.js'),
111107
appNodeModules: resolveOwn('node_modules'),
112108
publicUrl: getPublicUrl(resolveOwn('package.json')),
@@ -120,40 +116,16 @@ if (useTemplate) {
120116

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

123-
const findPkgs = (rootPath, globPatterns) => {
124-
const globOpts = {
125-
cwd: rootPath,
126-
strict: true,
127-
absolute: true,
128-
};
129-
return globPatterns
130-
.reduce(
131-
(pkgs, pattern) =>
132-
pkgs.concat(globby.sync(path.join(pattern, 'package.json'), globOpts)),
133-
[]
134-
)
135-
.map(f => path.dirname(path.normalize(f)));
136-
};
137-
138-
const getMonorepoPkgPaths = () => {
139-
const monoPkgPath = findPkg.sync(path.resolve(appDirectory, '..'));
140-
if (monoPkgPath) {
141-
// get monorepo config from yarn workspace
142-
const pkgPatterns = require(monoPkgPath).workspaces;
143-
if (pkgPatterns == null) {
144-
return [];
145-
}
146-
const pkgPaths = findPkgs(path.dirname(monoPkgPath), pkgPatterns);
147-
// only include monorepo pkgs if app itself is included in monorepo
148-
if (pkgPaths.indexOf(appDirectory) !== -1) {
149-
return pkgPaths.filter(f => fs.realpathSync(f) !== appDirectory);
150-
}
151-
}
152-
return [];
153-
};
119+
module.exports.useYarn = fs.existsSync(
120+
path.join(module.exports.appPath, 'yarn.lock')
121+
);
154122

155123
if (checkForMonorepo) {
156124
// if app is in a monorepo (lerna or yarn workspace), treat other packages in
157125
// the monorepo as if they are app source
158-
Array.prototype.push.apply(module.exports.srcPaths, getMonorepoPkgPaths());
126+
const mono = findMonorepo(appDirectory);
127+
if (mono.isAppIncluded) {
128+
Array.prototype.push.apply(module.exports.srcPaths, mono.pkgs);
129+
}
130+
module.exports.useYarn = module.exports.useYarn || mono.isYarnWs;
159131
}

packages/react-scripts/package.json

-2
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,7 @@
4444
"eslint-plugin-react": "7.5.1",
4545
"extract-text-webpack-plugin": "3.0.2",
4646
"file-loader": "1.1.6",
47-
"find-pkg": "1.0.0",
4847
"fs-extra": "5.0.0",
49-
"globby": "7.1.1",
5048
"graphql": "0.12.3",
5149
"graphql-tag": "2.6.1",
5250
"html-webpack-plugin": "2.30.1",

packages/react-scripts/scripts/build.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ const { printBrowsers } = require('react-dev-utils/browsersHelper');
4545
const measureFileSizesBeforeBuild =
4646
FileSizeReporter.measureFileSizesBeforeBuild;
4747
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
48-
const useYarn = fs.existsSync(paths.yarnLockFile);
4948

5049
// These sizes are pretty large. We'll warn for bundles exceeding them.
5150
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
@@ -112,7 +111,7 @@ checkBrowsers(paths.appPath)
112111
publicUrl,
113112
publicPath,
114113
buildFolder,
115-
useYarn
114+
paths.useYarn
116115
);
117116
printBrowsers(paths.appPath);
118117
},

packages/react-scripts/scripts/eject.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ inquirer
224224
}
225225
}
226226

227-
if (fs.existsSync(paths.yarnLockFile)) {
227+
if (paths.useYarn) {
228228
const windowsCmdFilePath = path.join(
229229
appPath,
230230
'node_modules',

packages/react-scripts/scripts/start.js

+10-4
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ if (process.env.SKIP_PREFLIGHT_CHECK !== 'true') {
2929
}
3030
// @remove-on-eject-end
3131

32-
const fs = require('fs');
3332
const chalk = require('chalk');
3433
const webpack = require('webpack');
3534
const WebpackDevServer = require('webpack-dev-server');
@@ -46,7 +45,6 @@ const paths = require('../config/paths');
4645
const config = require('../config/webpack.config.dev');
4746
const createDevServerConfig = require('../config/webpackDevServer.config');
4847

49-
const useYarn = fs.existsSync(paths.yarnLockFile);
5048
const isInteractive = process.stdout.isTTY;
5149

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

@@ -91,7 +91,13 @@ checkBrowsers(paths.appPath)
9191
const appName = require(paths.appPackageJson).name;
9292
const urls = prepareUrls(protocol, HOST, port);
9393
// Create a webpack compiler that is configured with custom messages.
94-
const compiler = createCompiler(webpack, config, appName, urls, useYarn);
94+
const compiler = createCompiler(
95+
webpack,
96+
config,
97+
appName,
98+
urls,
99+
paths.useYarn
100+
);
95101
// Load proxy config
96102
const proxySetting = require(paths.appPackageJson).proxy;
97103
const proxyConfig = prepareProxy(proxySetting, paths.appPublic);

0 commit comments

Comments
 (0)