From f1018cfdeddb4f45f9efdf0b3c9976ccb9e63cdc Mon Sep 17 00:00:00 2001 From: Bradford Lemley Date: Thu, 8 Feb 2018 15:07:45 -0700 Subject: [PATCH 1/7] Run yarn after ejecting. --- tasks/e2e-monorepos.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tasks/e2e-monorepos.sh b/tasks/e2e-monorepos.sh index 635badcf219..0b52f7eb3bb 100755 --- a/tasks/e2e-monorepos.sh +++ b/tasks/e2e-monorepos.sh @@ -120,6 +120,7 @@ verifyTest # Test eject echo yes | npm run eject +yarn verifyBuild yarn start --smoke-test verifyTest From 869413f7d35d345be69b8b5ead111148c7aebc5b Mon Sep 17 00:00:00 2001 From: Bradford Lemley Date: Fri, 9 Feb 2018 04:00:47 -0700 Subject: [PATCH 2/7] On eject, choose to run yarn instead of npm if yarn is available. --- packages/react-scripts/scripts/eject.js | 11 ++++++++++- tasks/e2e-monorepos.sh | 1 - 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/react-scripts/scripts/eject.js b/packages/react-scripts/scripts/eject.js index e8c64a77853..99bc3e02a13 100644 --- a/packages/react-scripts/scripts/eject.js +++ b/packages/react-scripts/scripts/eject.js @@ -38,6 +38,15 @@ function getGitStatus() { } } +function isYarnAvailable() { + try { + execSync('yarnpkg --version', { stdio: 'ignore' }); + return true; + } catch (e) { + return false; + } +} + inquirer .prompt({ type: 'confirm', @@ -224,7 +233,7 @@ inquirer } } - if (fs.existsSync(paths.yarnLockFile)) { + if (fs.existsSync(paths.yarnLockFile) || isYarnAvailable()) { const windowsCmdFilePath = path.join( appPath, 'node_modules', diff --git a/tasks/e2e-monorepos.sh b/tasks/e2e-monorepos.sh index 0b52f7eb3bb..635badcf219 100755 --- a/tasks/e2e-monorepos.sh +++ b/tasks/e2e-monorepos.sh @@ -120,7 +120,6 @@ verifyTest # Test eject echo yes | npm run eject -yarn verifyBuild yarn start --smoke-test verifyTest From c792a985b70ae8a386951d2b63a027555bbf1171 Mon Sep 17 00:00:00 2001 From: Bradford Lemley Date: Fri, 9 Feb 2018 09:44:15 -0700 Subject: [PATCH 3/7] Move monorepo to react-dev-utils. --- packages/create-react-app/createReactApp.js | 11 +++-- packages/react-dev-utils/monorepo.js | 51 +++++++++++++++++++++ packages/react-dev-utils/package.json | 3 ++ packages/react-scripts/config/paths.js | 46 ++++--------------- packages/react-scripts/package.json | 2 - packages/react-scripts/scripts/build.js | 3 +- packages/react-scripts/scripts/eject.js | 11 +---- packages/react-scripts/scripts/start.js | 13 ++++-- 8 files changed, 83 insertions(+), 57 deletions(-) create mode 100644 packages/react-dev-utils/monorepo.js diff --git a/packages/create-react-app/createReactApp.js b/packages/create-react-app/createReactApp.js index 16acace8fa2..03c3c77bb98 100755 --- a/packages/create-react-app/createReactApp.js +++ b/packages/create-react-app/createReactApp.js @@ -49,7 +49,7 @@ const url = require('url'); const hyperquest = require('hyperquest'); const envinfo = require('envinfo'); const os = require('os'); - +const monorepo = require('react-dev-utils/monorepo'); const packageJson = require('./package.json'); // These files should be allowed to remain on a failed install, @@ -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()) { @@ -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; @@ -230,6 +230,11 @@ function shouldUseYarn() { } } +function shouldUseYarn(appDir) { + const mono = monorepo(appDir); + return (mono.isYarnWs && mono.isAppIncluded) || isYarnAvailable(); +} + function install(root, useYarn, dependencies, verbose, isOnline) { return new Promise((resolve, reject) => { let command; diff --git a/packages/react-dev-utils/monorepo.js b/packages/react-dev-utils/monorepo.js new file mode 100644 index 00000000000..c12a68bc83b --- /dev/null +++ b/packages/react-dev-utils/monorepo.js @@ -0,0 +1,51 @@ +/** + * 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 getMonorepo = 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 = getMonorepo; diff --git a/packages/react-dev-utils/package.json b/packages/react-dev-utils/package.json index 05bddedae05..31379483a4a 100644 --- a/packages/react-dev-utils/package.json +++ b/packages/react-dev-utils/package.json @@ -28,6 +28,7 @@ "launchEditor.js", "launchEditorEndpoint.js", "ModuleScopePlugin.js", + "monorepo.js", "noopServiceWorkerMiddleware.js", "openBrowser.js", "openChrome.applescript", @@ -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", diff --git a/packages/react-scripts/config/paths.js b/packages/react-scripts/config/paths.js index 5d8c7aaa51e..a8c7eacc617 100644 --- a/packages/react-scripts/config/paths.js +++ b/packages/react-scripts/config/paths.js @@ -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 monorepo = require('react-dev-utils/monorepo'); // Make sure any symlinks in the project folder are resolved: // https://github.com/facebook/create-react-app/issues/637 @@ -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')), @@ -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')), @@ -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')), @@ -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 = monorepo(appDirectory); + if (mono.isAppIncluded) { + Array.prototype.push.apply(module.exports.srcPaths, mono.pkgs); + } + module.exports.useYarn = module.exports.useYarn || mono.isYarnWs; } diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json index d9e904d3ad9..a22878af921 100644 --- a/packages/react-scripts/package.json +++ b/packages/react-scripts/package.json @@ -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", diff --git a/packages/react-scripts/scripts/build.js b/packages/react-scripts/scripts/build.js index cca5ff28313..f69400dcf63 100644 --- a/packages/react-scripts/scripts/build.js +++ b/packages/react-scripts/scripts/build.js @@ -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; @@ -112,7 +111,7 @@ checkBrowsers(paths.appPath) publicUrl, publicPath, buildFolder, - useYarn + paths.useYarn ); printBrowsers(paths.appPath); }, diff --git a/packages/react-scripts/scripts/eject.js b/packages/react-scripts/scripts/eject.js index 99bc3e02a13..98863b915f2 100644 --- a/packages/react-scripts/scripts/eject.js +++ b/packages/react-scripts/scripts/eject.js @@ -38,15 +38,6 @@ function getGitStatus() { } } -function isYarnAvailable() { - try { - execSync('yarnpkg --version', { stdio: 'ignore' }); - return true; - } catch (e) { - return false; - } -} - inquirer .prompt({ type: 'confirm', @@ -233,7 +224,7 @@ inquirer } } - if (fs.existsSync(paths.yarnLockFile) || isYarnAvailable()) { + if (paths.useYarn) { const windowsCmdFilePath = path.join( appPath, 'node_modules', diff --git a/packages/react-scripts/scripts/start.js b/packages/react-scripts/scripts/start.js index 83e4bb630ed..8420923b033 100644 --- a/packages/react-scripts/scripts/start.js +++ b/packages/react-scripts/scripts/start.js @@ -46,7 +46,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 @@ -69,7 +68,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(); } @@ -91,7 +92,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); From a34a610e4666962ff3c470ba41add85272df70ec Mon Sep 17 00:00:00 2001 From: Bradford Lemley Date: Fri, 9 Feb 2018 10:35:48 -0700 Subject: [PATCH 4/7] Fix lint. --- packages/react-scripts/scripts/start.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react-scripts/scripts/start.js b/packages/react-scripts/scripts/start.js index 8420923b033..95502bc2a24 100644 --- a/packages/react-scripts/scripts/start.js +++ b/packages/react-scripts/scripts/start.js @@ -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'); From 48176519cad2705eb9ce4c8d9f09fb32ef53cfe5 Mon Sep 17 00:00:00 2001 From: Bradford Lemley Date: Fri, 9 Feb 2018 10:59:20 -0700 Subject: [PATCH 5/7] Rename monorepo to workspaceUtils. --- packages/create-react-app/createReactApp.js | 4 ++-- packages/react-dev-utils/package.json | 4 ++-- packages/react-dev-utils/{monorepo.js => workspaceUtils.js} | 4 +++- packages/react-scripts/config/paths.js | 4 ++-- 4 files changed, 9 insertions(+), 7 deletions(-) rename packages/react-dev-utils/{monorepo.js => workspaceUtils.js} (97%) diff --git a/packages/create-react-app/createReactApp.js b/packages/create-react-app/createReactApp.js index 03c3c77bb98..69b8ec4bac5 100755 --- a/packages/create-react-app/createReactApp.js +++ b/packages/create-react-app/createReactApp.js @@ -49,7 +49,7 @@ const url = require('url'); const hyperquest = require('hyperquest'); const envinfo = require('envinfo'); const os = require('os'); -const monorepo = require('react-dev-utils/monorepo'); +const getMonorepo = require('react-dev-utils/workspaceUtils').getMonorepo; const packageJson = require('./package.json'); // These files should be allowed to remain on a failed install, @@ -231,7 +231,7 @@ function isYarnAvailable() { } function shouldUseYarn(appDir) { - const mono = monorepo(appDir); + const mono = getMonorepo(appDir); return (mono.isYarnWs && mono.isAppIncluded) || isYarnAvailable(); } diff --git a/packages/react-dev-utils/package.json b/packages/react-dev-utils/package.json index 31379483a4a..d66e92c9daf 100644 --- a/packages/react-dev-utils/package.json +++ b/packages/react-dev-utils/package.json @@ -28,14 +28,14 @@ "launchEditor.js", "launchEditorEndpoint.js", "ModuleScopePlugin.js", - "monorepo.js", "noopServiceWorkerMiddleware.js", "openBrowser.js", "openChrome.applescript", "printHostingInstructions.js", "WatchMissingNodeModulesPlugin.js", "WebpackDevServerUtils.js", - "webpackHotDevClient.js" + "webpackHotDevClient.js", + "workspaceUtils.js" ], "dependencies": { "@babel/code-frame": "7.0.0-beta.38", diff --git a/packages/react-dev-utils/monorepo.js b/packages/react-dev-utils/workspaceUtils.js similarity index 97% rename from packages/react-dev-utils/monorepo.js rename to packages/react-dev-utils/workspaceUtils.js index c12a68bc83b..66da98267fa 100644 --- a/packages/react-dev-utils/monorepo.js +++ b/packages/react-dev-utils/workspaceUtils.js @@ -48,4 +48,6 @@ const getMonorepo = appDir => { }; }; -module.exports = getMonorepo; +module.exports = { + getMonorepo, +}; diff --git a/packages/react-scripts/config/paths.js b/packages/react-scripts/config/paths.js index a8c7eacc617..012f4fbb28a 100644 --- a/packages/react-scripts/config/paths.js +++ b/packages/react-scripts/config/paths.js @@ -11,7 +11,7 @@ const path = require('path'); const fs = require('fs'); const url = require('url'); -const monorepo = require('react-dev-utils/monorepo'); +const getMonorepo = require('react-dev-utils/workspaceUtils').getMonorepo; // Make sure any symlinks in the project folder are resolved: // https://github.com/facebook/create-react-app/issues/637 @@ -123,7 +123,7 @@ module.exports.useYarn = fs.existsSync( if (checkForMonorepo) { // if app is in a monorepo (lerna or yarn workspace), treat other packages in // the monorepo as if they are app source - const mono = monorepo(appDirectory); + const mono = getMonorepo(appDirectory); if (mono.isAppIncluded) { Array.prototype.push.apply(module.exports.srcPaths, mono.pkgs); } From c664c11c083ea3ff7b54791a48417793d234544d Mon Sep 17 00:00:00 2001 From: Bradford Lemley Date: Fri, 9 Feb 2018 11:13:00 -0700 Subject: [PATCH 6/7] Add react-dev-utils dep for create-react-app. --- packages/create-react-app/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/create-react-app/package.json b/packages/create-react-app/package.json index 1a66c6a7f3c..86bd258f736 100644 --- a/packages/create-react-app/package.json +++ b/packages/create-react-app/package.json @@ -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", From 41fb5b06b9ac9ed7ae369a1dd829dbec8e7b6cb5 Mon Sep 17 00:00:00 2001 From: Bradford Lemley Date: Sat, 10 Feb 2018 04:43:00 -0700 Subject: [PATCH 7/7] getMonorepo -> findMonorepo --- packages/create-react-app/createReactApp.js | 4 ++-- packages/react-dev-utils/workspaceUtils.js | 4 ++-- packages/react-scripts/config/paths.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/create-react-app/createReactApp.js b/packages/create-react-app/createReactApp.js index 69b8ec4bac5..1bf3bb0649d 100755 --- a/packages/create-react-app/createReactApp.js +++ b/packages/create-react-app/createReactApp.js @@ -49,7 +49,7 @@ const url = require('url'); const hyperquest = require('hyperquest'); const envinfo = require('envinfo'); const os = require('os'); -const getMonorepo = require('react-dev-utils/workspaceUtils').getMonorepo; +const findMonorepo = require('react-dev-utils/workspaceUtils').findMonorepo; const packageJson = require('./package.json'); // These files should be allowed to remain on a failed install, @@ -231,7 +231,7 @@ function isYarnAvailable() { } function shouldUseYarn(appDir) { - const mono = getMonorepo(appDir); + const mono = findMonorepo(appDir); return (mono.isYarnWs && mono.isAppIncluded) || isYarnAvailable(); } diff --git a/packages/react-dev-utils/workspaceUtils.js b/packages/react-dev-utils/workspaceUtils.js index 66da98267fa..159580e6066 100644 --- a/packages/react-dev-utils/workspaceUtils.js +++ b/packages/react-dev-utils/workspaceUtils.js @@ -29,7 +29,7 @@ const findPkgs = (rootPath, globPatterns) => { .map(f => path.dirname(path.normalize(f))); }; -const getMonorepo = appDir => { +const findMonorepo = appDir => { const monoPkgPath = findPkg.sync(path.resolve(appDir, '..')); const monoPkg = monoPkgPath && require(monoPkgPath); const patterns = monoPkg && monoPkg.workspaces; @@ -49,5 +49,5 @@ const getMonorepo = appDir => { }; module.exports = { - getMonorepo, + findMonorepo, }; diff --git a/packages/react-scripts/config/paths.js b/packages/react-scripts/config/paths.js index 012f4fbb28a..8cd5cf439d7 100644 --- a/packages/react-scripts/config/paths.js +++ b/packages/react-scripts/config/paths.js @@ -11,7 +11,7 @@ const path = require('path'); const fs = require('fs'); const url = require('url'); -const getMonorepo = require('react-dev-utils/workspaceUtils').getMonorepo; +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 @@ -123,7 +123,7 @@ module.exports.useYarn = fs.existsSync( if (checkForMonorepo) { // if app is in a monorepo (lerna or yarn workspace), treat other packages in // the monorepo as if they are app source - const mono = getMonorepo(appDirectory); + const mono = findMonorepo(appDirectory); if (mono.isAppIncluded) { Array.prototype.push.apply(module.exports.srcPaths, mono.pkgs); }