Skip to content

Commit ae9ffa4

Browse files
Use yarn when running in monorepo, facebook#3997
1 parent 3842c09 commit ae9ffa4

File tree

9 files changed

+84
-46
lines changed

9 files changed

+84
-46
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,
@@ -206,7 +206,7 @@ function createApp(name, verbose, version, useNpm, usePnp, template) {
206206
JSON.stringify(packageJson, null, 2) + os.EOL
207207
);
208208

209-
const useYarn = useNpm ? false : shouldUseYarn();
209+
const useYarn = useNpm ? false : shouldUseYarn(root);
210210
const originalDirectory = process.cwd();
211211
process.chdir(root);
212212
if (!useYarn && !checkThatNpmCanReadCwd()) {
@@ -277,7 +277,7 @@ function createApp(name, verbose, version, useNpm, usePnp, template) {
277277
);
278278
}
279279

280-
function shouldUseYarn() {
280+
function isYarnAvailable() {
281281
try {
282282
execSync('yarnpkg --version', { stdio: 'ignore' });
283283
return true;
@@ -286,6 +286,11 @@ function shouldUseYarn() {
286286
}
287287
}
288288

289+
function shouldUseYarn(appDir) {
290+
const mono = findMonorepo(appDir);
291+
return (mono.isYarnWs && mono.isAppIncluded) || isYarnAvailable();
292+
}
293+
289294
function install(root, useYarn, usePnp, dependencies, verbose, isOnline) {
290295
return new Promise((resolve, reject) => {
291296
let command;

packages/create-react-app/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"envinfo": "5.10.0",
2929
"fs-extra": "5.0.0",
3030
"hyperquest": "2.1.3",
31+
"react-dev-utils": "6.0.4",
3132
"semver": "5.5.1",
3233
"tar-pack": "3.4.1",
3334
"tmp": "0.0.33",

packages/react-dev-utils/package.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@
3838
"printHostingInstructions.js",
3939
"WatchMissingNodeModulesPlugin.js",
4040
"WebpackDevServerUtils.js",
41-
"webpackHotDevClient.js"
41+
"webpackHotDevClient.js",
42+
"workspaceUtils.js"
4243
],
4344
"dependencies": {
4445
"@babel/code-frame": "7.0.0",
@@ -49,8 +50,10 @@
4950
"detect-port-alt": "1.1.6",
5051
"escape-string-regexp": "1.0.5",
5152
"filesize": "3.6.1",
53+
"find-pkg": "1.0.0",
5254
"find-up": "3.0.0",
5355
"global-modules": "1.0.0",
56+
"globby": "7.1.1",
5457
"gzip-size": "5.0.0",
5558
"inquirer": "6.2.0",
5659
"is-root": "2.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-34
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
proxySetup: resolveApp('src/setupProxy.js'),
6462
appNodeModules: resolveApp('node_modules'),
@@ -81,7 +79,6 @@ module.exports = {
8179
appIndexJs: resolveApp('src/index.js'),
8280
appPackageJson: resolveApp('package.json'),
8381
appSrc: resolveApp('src'),
84-
yarnLockFile: resolveApp('yarn.lock'),
8582
testsSetup: resolveApp('src/setupTests.js'),
8683
proxySetup: resolveApp('src/setupProxy.js'),
8784
appNodeModules: resolveApp('node_modules'),
@@ -108,7 +105,6 @@ if (useTemplate) {
108105
appIndexJs: resolveOwn('template/src/index.js'),
109106
appPackageJson: resolveOwn('package.json'),
110107
appSrc: resolveOwn('template/src'),
111-
yarnLockFile: resolveOwn('template/yarn.lock'),
112108
testsSetup: resolveOwn('template/src/setupTests.js'),
113109
proxySetup: resolveOwn('template/src/setupProxy.js'),
114110
appNodeModules: resolveOwn('node_modules'),
@@ -123,37 +119,16 @@ if (useTemplate) {
123119

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

126-
const findPkgs = (rootPath, globPatterns) => {
127-
const globOpts = {
128-
cwd: rootPath,
129-
strict: true,
130-
absolute: true,
131-
};
132-
return globPatterns
133-
.reduce(
134-
(pkgs, pattern) =>
135-
pkgs.concat(globby.sync(path.join(pattern, 'package.json'), globOpts)),
136-
[]
137-
)
138-
.map(f => path.dirname(path.normalize(f)));
139-
};
140-
141-
const getMonorepoPkgPaths = () => {
142-
const monoPkgPath = findPkg.sync(path.resolve(appDirectory, '..'));
143-
if (monoPkgPath) {
144-
// get monorepo config from yarn workspace
145-
const pkgPatterns = require(monoPkgPath).workspaces;
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-
};
122+
module.exports.useYarn = fs.existsSync(
123+
path.join(module.exports.appPath, 'yarn.lock')
124+
);
154125

155126
if (checkForMonorepo) {
156127
// if app is in a monorepo (lerna or yarn workspace), treat other packages in
157128
// the monorepo as if they are app source
158-
Array.prototype.push.apply(module.exports.srcPaths, getMonorepoPkgPaths());
129+
const mono = findMonorepo(appDirectory);
130+
if (mono.isAppIncluded) {
131+
Array.prototype.push.apply(module.exports.srcPaths, mono.pkgs);
132+
}
133+
module.exports.useYarn = module.exports.useYarn || mono.isYarnWs;
159134
}

packages/react-scripts/package.json

-2
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,7 @@
4343
"eslint-plugin-jsx-a11y": "6.1.1",
4444
"eslint-plugin-react": "7.11.1",
4545
"file-loader": "2.0.0",
46-
"find-pkg": "1.0.0",
4746
"fs-extra": "7.0.0",
48-
"globby": "7.1.1",
4947
"html-webpack-plugin": "4.0.0-alpha.2",
5048
"identity-obj-proxy": "3.0.0",
5149
"jest": "23.6.0",

packages/react-scripts/scripts/build.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ const printBuildError = require('react-dev-utils/printBuildError');
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;
@@ -118,7 +117,7 @@ checkBrowsers(paths.appPath, isInteractive)
118117
publicUrl,
119118
publicPath,
120119
buildFolder,
121-
useYarn
120+
paths.useYarn
122121
);
123122
},
124123
err => {

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

+7-3
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
@@ -93,7 +91,13 @@ checkBrowsers(paths.appPath, isInteractive)
9391
const appName = require(paths.appPackageJson).name;
9492
const urls = prepareUrls(protocol, HOST, port);
9593
// Create a webpack compiler that is configured with custom messages.
96-
const compiler = createCompiler(webpack, config, appName, urls, useYarn);
94+
const compiler = createCompiler(
95+
webpack,
96+
config,
97+
appName,
98+
urls,
99+
paths.useYarn
100+
);
97101
// Load proxy config
98102
const proxySetting = require(paths.appPackageJson).proxy;
99103
const proxyConfig = prepareProxy(proxySetting, paths.appPublic);

0 commit comments

Comments
 (0)