From f04f0ddabeea3bc330ba26dc26804d032be23ecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Thu, 27 Sep 2018 16:42:06 +0200 Subject: [PATCH 01/10] Adds the PnP plugin for Webpack to find dependencies when working under PnP --- .../react-scripts/config/webpack.config.dev.js | 17 +++++++++++++++++ .../react-scripts/config/webpack.config.prod.js | 17 +++++++++++++++++ packages/react-scripts/package.json | 1 + 3 files changed, 35 insertions(+) diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js index 2561ade86dd..a03db353626 100644 --- a/packages/react-scripts/config/webpack.config.dev.js +++ b/packages/react-scripts/config/webpack.config.dev.js @@ -10,6 +10,7 @@ const path = require('path'); const webpack = require('webpack'); +const PnpWebpackPlugin = require('pnp-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); @@ -149,6 +150,9 @@ module.exports = { 'react-native': 'react-native-web', }, plugins: [ + // Adds support for installing with Plug'n'Play, leading to faster installs and adding + // guards against forgotten dependencies and such. + PnpWebpackPlugin, // Prevents users from importing files from outside of src/ (or node_modules/). // This often causes confusion because we only process files within src/ with babel. // To fix this, we prevent you from importing files out of src/ -- if you'd like to, @@ -156,6 +160,19 @@ module.exports = { // Make sure your source files are compiled, as they will not be processed in any way. new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]), ], + // Plug'n'Play relies on symlink for its virtual paths (ie peer dependencies), which Webpack + // always resolve to the absolute path on disk by default. + symlinks: false, + }, + resolveLoader: { + plugins: [ + // Also related to Plug'n'Play, but this time it tells Webpack to load its loaders + // from the current package. + PnpWebpackPlugin.moduleLoader(module), + ], + // Plug'n'Play relies on symlink for its virtual paths (ie peer dependencies), which Webpack + // always resolve to the absolute path on disk by default. + symlinks: false, }, module: { strictExportPresence: true, diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js index 0defc5f44ab..578d28bd7be 100644 --- a/packages/react-scripts/config/webpack.config.prod.js +++ b/packages/react-scripts/config/webpack.config.prod.js @@ -10,6 +10,7 @@ const path = require('path'); const webpack = require('webpack'); +const PnpWebpackPlugin = require('pnp-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin'); const TerserPlugin = require('terser-webpack-plugin'); @@ -204,6 +205,9 @@ module.exports = { 'react-native': 'react-native-web', }, plugins: [ + // Adds support for installing with Plug'n'Play, leading to faster installs and adding + // guards against forgotten dependencies and such. + PnpWebpackPlugin, // Prevents users from importing files from outside of src/ (or node_modules/). // This often causes confusion because we only process files within src/ with babel. // To fix this, we prevent you from importing files out of src/ -- if you'd like to, @@ -211,6 +215,19 @@ module.exports = { // Make sure your source files are compiled, as they will not be processed in any way. new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]), ], + // Plug'n'Play relies on symlink for its virtual paths (ie peer dependencies), which Webpack + // always resolve to the absolute path on disk by default. + symlinks: false, + }, + resolveLoader: { + plugins: [ + // Also related to Plug'n'Play, but this time it tells Webpack to load its loaders + // from the current package. + PnpWebpackPlugin.moduleLoader(module), + ], + // Plug'n'Play relies on symlink for its virtual paths (ie peer dependencies), which Webpack + // always resolve to the absolute path on disk by default. + symlinks: false, }, module: { strictExportPresence: true, diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json index 3fd1bfd1251..67b1dda005c 100644 --- a/packages/react-scripts/package.json +++ b/packages/react-scripts/package.json @@ -49,6 +49,7 @@ "jest": "23.6.0", "mini-css-extract-plugin": "0.4.3", "optimize-css-assets-webpack-plugin": "5.0.1", + "pnp-webpack-plugin": "1.0.2", "postcss-flexbugs-fixes": "4.1.0", "postcss-loader": "3.0.0", "postcss-preset-env": "6.0.6", From 63152ef209dfe2e44fe584ddfc2d092c959e17dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Thu, 27 Sep 2018 17:20:14 +0200 Subject: [PATCH 02/10] Adds configuration for jest --- packages/react-scripts/package.json | 2 ++ packages/react-scripts/scripts/utils/createJestConfig.js | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json index 67b1dda005c..d527744a886 100644 --- a/packages/react-scripts/package.json +++ b/packages/react-scripts/package.json @@ -47,6 +47,8 @@ "html-webpack-plugin": "4.0.0-alpha.2", "identity-obj-proxy": "3.0.0", "jest": "23.6.0", + "jest-pnp-resolver": "^1.0.1", + "jest-resolve": "23.6.0", "mini-css-extract-plugin": "0.4.3", "optimize-css-assets-webpack-plugin": "5.0.1", "pnp-webpack-plugin": "1.0.2", diff --git a/packages/react-scripts/scripts/utils/createJestConfig.js b/packages/react-scripts/scripts/utils/createJestConfig.js index 22e52dbdca4..af085383f0b 100644 --- a/packages/react-scripts/scripts/utils/createJestConfig.js +++ b/packages/react-scripts/scripts/utils/createJestConfig.js @@ -22,7 +22,8 @@ module.exports = (resolve, rootDir, isEjecting) => { // in Jest configs. We need help from somebody with Windows to determine this. const config = { collectCoverageFrom: ['src/**/*.{js,jsx}'], - setupFiles: ['react-app-polyfill/jsdom'], + resolver: require.resolve('jest-pnp-resolver'), + setupFiles: [require.resolve('react-app-polyfill/jsdom')], setupTestFrameworkScriptFile: setupTestsFile, testMatch: [ '/src/**/__tests__/**/*.{js,jsx}', From 6eb3f33078035d3cfd106f535808b998cf714815 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Fri, 28 Sep 2018 17:55:14 +0200 Subject: [PATCH 03/10] Adds an e2e test for when using PnP --- .travis.yml | 2 +- appveyor.yml | 2 +- tasks/e2e-installs.sh | 12 ++++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index fd83ef7393e..e2e384a6f85 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ cache: directories: - .npm before_install: - - curl -o- -L https://yarnpkg.com/install.sh | bash + - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --nightly - export PATH="$HOME/.yarn/bin:$PATH" install: true script: diff --git a/appveyor.yml b/appveyor.yml index 4fc634b5331..b8420b71583 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -36,7 +36,7 @@ platform: install: - ps: Install-Product node $env:nodejs_version $env:platform - ps: | - (New-Object Net.WebClient).DownloadFile("https://yarnpkg.com/latest.msi", "$env:temp\yarn.msi") + (New-Object Net.WebClient).DownloadFile("https://nightly.yarnpkg.com/latest.msi", "$env:temp\yarn.msi") cmd /c start /wait msiexec.exe /i $env:temp\yarn.msi /quiet /qn /norestart build: off diff --git a/tasks/e2e-installs.sh b/tasks/e2e-installs.sh index 642e4f671a9..c0776c84efc 100755 --- a/tasks/e2e-installs.sh +++ b/tasks/e2e-installs.sh @@ -229,5 +229,17 @@ npx create-react-app test-app-nested-paths-t3/aa/bb/cc/dd cd test-app-nested-paths-t3/aa/bb/cc/dd yarn start --smoke-test +# ****************************************************************************** +# Test when PnP is enabled +# ****************************************************************************** +cd "$temp_app_path" +echo $OSTYPE +YARN_PLUGNPLAY_OVERRIDE=1 npx create-react-app test-app-pnp +cd test-app-pnp +! exists node_modules +exists .pnp.js +yarn start --smoke-test +yarn build + # Cleanup cleanup From 7c362b0a2a6c83988d5e52978a7d6d4eb81a8af4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Sat, 29 Sep 2018 01:05:35 +0200 Subject: [PATCH 04/10] Avoids cra from crashing at the engine check --- packages/create-react-app/createReactApp.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/create-react-app/createReactApp.js b/packages/create-react-app/createReactApp.js index 015417a2c2a..274783cfcbe 100755 --- a/packages/create-react-app/createReactApp.js +++ b/packages/create-react-app/createReactApp.js @@ -540,6 +540,11 @@ function checkNodeVersion(packageName) { packageName, 'package.json' ); + + if (!fs.existsSync(packageJsonPath)) { + return; + } + const packageJson = require(packageJsonPath); if (!packageJson.engines || !packageJson.engines.node) { return; From eae3d8203a47b9406e5404c3f3d1fc859c4412cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Sat, 29 Sep 2018 13:32:36 +0200 Subject: [PATCH 05/10] Avoids cra from crashing when initializing react-scripts --- packages/create-react-app/createReactApp.js | 44 ++++++++++++++++----- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/packages/create-react-app/createReactApp.js b/packages/create-react-app/createReactApp.js index 274783cfcbe..8fa53c6f217 100755 --- a/packages/create-react-app/createReactApp.js +++ b/packages/create-react-app/createReactApp.js @@ -340,19 +340,25 @@ function run( () => packageName ); }) - .then(packageName => { + .then(async packageName => { checkNodeVersion(packageName); setCaretRangeForRuntimeDeps(packageName); - const scriptsPath = path.resolve( - process.cwd(), - 'node_modules', - packageName, - 'scripts', - 'init.js' + const pnpPath = path.resolve(process.cwd(), '.pnp.js'); + + const nodeArgs = fs.existsSync(pnpPath) ? ['--require', pnpPath] : []; + + await executeNodeScript( + { + cwd: process.cwd(), + args: nodeArgs, + }, + [root, appName, verbose, originalDirectory, template], + ` + var init = require('${packageName}/scripts/init.js'); + init.apply(null, JSON.parse(process.argv[1])); + ` ); - const init = require(scriptsPath); - init(root, appName, verbose, originalDirectory, template); if (version === 'react-scripts@0.9.x') { console.log( @@ -799,3 +805,23 @@ function checkIfOnline(useYarn) { }); }); } + +function executeNodeScript({ cwd, args }, data, source) { + return new Promise((resolve, reject) => { + const child = spawn( + process.execPath, + [...args, '-e', source, '--', JSON.stringify(data)], + { stdio: 'inherit' } + ); + + child.on('close', code => { + if (code !== 0) { + reject({ + command: `${command} ${args.join(' ')}`, + }); + return; + } + resolve(); + }); + }); +} From 5bbe63a0cb7d300c6cd84c5cf58688d7fe8be84c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Sat, 29 Sep 2018 13:50:04 +0200 Subject: [PATCH 06/10] Makes the ownPath portable --- packages/react-scripts/scripts/init.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/react-scripts/scripts/init.js b/packages/react-scripts/scripts/init.js index 2904ae70cfe..7c65344337e 100644 --- a/packages/react-scripts/scripts/init.js +++ b/packages/react-scripts/scripts/init.js @@ -83,7 +83,9 @@ module.exports = function( ) { const ownPackageName = require(path.join(__dirname, '..', 'package.json')) .name; - const ownPath = path.join(appPath, 'node_modules', ownPackageName); + const ownPath = path.dirname( + require.resolve(path.join(__dirname, '..', 'package.json')) + ); const appPackage = require(path.join(appPath, 'package.json')); const useYarn = fs.existsSync(path.join(appPath, 'yarn.lock')); From 747ba0e1f1bd86ac1e73e8edec3eb91d401d4237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Sat, 29 Sep 2018 14:06:59 +0200 Subject: [PATCH 07/10] Fixes linting --- packages/create-react-app/createReactApp.js | 4 ++-- packages/react-scripts/scripts/init.js | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/create-react-app/createReactApp.js b/packages/create-react-app/createReactApp.js index 8fa53c6f217..8bd6c352937 100755 --- a/packages/create-react-app/createReactApp.js +++ b/packages/create-react-app/createReactApp.js @@ -811,13 +811,13 @@ function executeNodeScript({ cwd, args }, data, source) { const child = spawn( process.execPath, [...args, '-e', source, '--', JSON.stringify(data)], - { stdio: 'inherit' } + { cwd, stdio: 'inherit' } ); child.on('close', code => { if (code !== 0) { reject({ - command: `${command} ${args.join(' ')}`, + command: `node ${args.join(' ')}`, }); return; } diff --git a/packages/react-scripts/scripts/init.js b/packages/react-scripts/scripts/init.js index 7c65344337e..8ec24967528 100644 --- a/packages/react-scripts/scripts/init.js +++ b/packages/react-scripts/scripts/init.js @@ -81,8 +81,6 @@ module.exports = function( originalDirectory, template ) { - const ownPackageName = require(path.join(__dirname, '..', 'package.json')) - .name; const ownPath = path.dirname( require.resolve(path.join(__dirname, '..', 'package.json')) ); From 3609e33a7999533a81f464cfcd1a21ccbfe6e152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Sun, 30 Sep 2018 22:33:15 +0100 Subject: [PATCH 08/10] Bumps to pnp-webpack-plugin@1.1.0, removes symlinks: false --- packages/react-scripts/config/webpack.config.dev.js | 6 ------ packages/react-scripts/config/webpack.config.prod.js | 6 ------ packages/react-scripts/package.json | 2 +- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js index a03db353626..ac78a1b805d 100644 --- a/packages/react-scripts/config/webpack.config.dev.js +++ b/packages/react-scripts/config/webpack.config.dev.js @@ -160,9 +160,6 @@ module.exports = { // Make sure your source files are compiled, as they will not be processed in any way. new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]), ], - // Plug'n'Play relies on symlink for its virtual paths (ie peer dependencies), which Webpack - // always resolve to the absolute path on disk by default. - symlinks: false, }, resolveLoader: { plugins: [ @@ -170,9 +167,6 @@ module.exports = { // from the current package. PnpWebpackPlugin.moduleLoader(module), ], - // Plug'n'Play relies on symlink for its virtual paths (ie peer dependencies), which Webpack - // always resolve to the absolute path on disk by default. - symlinks: false, }, module: { strictExportPresence: true, diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js index 578d28bd7be..49ca399b11e 100644 --- a/packages/react-scripts/config/webpack.config.prod.js +++ b/packages/react-scripts/config/webpack.config.prod.js @@ -215,9 +215,6 @@ module.exports = { // Make sure your source files are compiled, as they will not be processed in any way. new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]), ], - // Plug'n'Play relies on symlink for its virtual paths (ie peer dependencies), which Webpack - // always resolve to the absolute path on disk by default. - symlinks: false, }, resolveLoader: { plugins: [ @@ -225,9 +222,6 @@ module.exports = { // from the current package. PnpWebpackPlugin.moduleLoader(module), ], - // Plug'n'Play relies on symlink for its virtual paths (ie peer dependencies), which Webpack - // always resolve to the absolute path on disk by default. - symlinks: false, }, module: { strictExportPresence: true, diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json index d527744a886..bfc24783dbe 100644 --- a/packages/react-scripts/package.json +++ b/packages/react-scripts/package.json @@ -51,7 +51,7 @@ "jest-resolve": "23.6.0", "mini-css-extract-plugin": "0.4.3", "optimize-css-assets-webpack-plugin": "5.0.1", - "pnp-webpack-plugin": "1.0.2", + "pnp-webpack-plugin": "1.1.0", "postcss-flexbugs-fixes": "4.1.0", "postcss-loader": "3.0.0", "postcss-preset-env": "6.0.6", From 68627139a59862cb7b4c1126c0d11cc05c67c232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Mon, 1 Oct 2018 15:13:55 +0100 Subject: [PATCH 09/10] Adds a --use-pnp option --- packages/create-react-app/createReactApp.js | 40 +++++++++++++++++---- tasks/e2e-installs.sh | 3 +- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/packages/create-react-app/createReactApp.js b/packages/create-react-app/createReactApp.js index 8bd6c352937..132d55a8373 100755 --- a/packages/create-react-app/createReactApp.js +++ b/packages/create-react-app/createReactApp.js @@ -76,6 +76,7 @@ const program = new commander.Command(packageJson.name) 'use a non-standard version of react-scripts' ) .option('--use-npm') + .option('--use-pnp') .allowUnknownOption() .on('--help', () => { console.log(` Only ${chalk.green('')} is required.`); @@ -178,10 +179,11 @@ createApp( program.verbose, program.scriptsVersion, program.useNpm, + program.usePnp, hiddenProgram.internalTestingTemplate ); -function createApp(name, verbose, version, useNpm, template) { +function createApp(name, verbose, version, useNpm, usePnp, template) { const root = path.resolve(name); const appName = path.basename(root); @@ -241,7 +243,16 @@ function createApp(name, verbose, version, useNpm, template) { version = 'react-scripts@0.9.x'; } } - run(root, appName, version, verbose, originalDirectory, template, useYarn); + run( + root, + appName, + version, + verbose, + originalDirectory, + template, + useYarn, + usePnp + ); } function shouldUseYarn() { @@ -253,7 +264,7 @@ function shouldUseYarn() { } } -function install(root, useYarn, dependencies, verbose, isOnline) { +function install(root, useYarn, usePnp, dependencies, verbose, isOnline) { return new Promise((resolve, reject) => { let command; let args; @@ -263,6 +274,9 @@ function install(root, useYarn, dependencies, verbose, isOnline) { if (!isOnline) { args.push('--offline'); } + if (usePnp) { + args.push('--enable-pnp'); + } [].push.apply(args, dependencies); // Explicitly set cwd() to work around issues like @@ -287,6 +301,12 @@ function install(root, useYarn, dependencies, verbose, isOnline) { '--loglevel', 'error', ].concat(dependencies); + + if (usePnp) { + console.log(chalk.yellow("NPM doesn't support PnP.")); + console.log(chalk.yellow('Falling back to the regular installs.')); + console.log(); + } } if (verbose) { @@ -313,7 +333,8 @@ function run( verbose, originalDirectory, template, - useYarn + useYarn, + usePnp ) { const packageToInstall = getInstallPackage(version, originalDirectory); const allDependencies = ['react', 'react-dom', packageToInstall]; @@ -336,9 +357,14 @@ function run( ); console.log(); - return install(root, useYarn, allDependencies, verbose, isOnline).then( - () => packageName - ); + return install( + root, + useYarn, + usePnp, + allDependencies, + verbose, + isOnline + ).then(() => packageName); }) .then(async packageName => { checkNodeVersion(packageName); diff --git a/tasks/e2e-installs.sh b/tasks/e2e-installs.sh index c0776c84efc..e402ee5c88a 100755 --- a/tasks/e2e-installs.sh +++ b/tasks/e2e-installs.sh @@ -233,8 +233,7 @@ yarn start --smoke-test # Test when PnP is enabled # ****************************************************************************** cd "$temp_app_path" -echo $OSTYPE -YARN_PLUGNPLAY_OVERRIDE=1 npx create-react-app test-app-pnp +npx create-react-app test-app-pnp --use-pnp cd test-app-pnp ! exists node_modules exists .pnp.js From 543f5f8f6949dfeb1e4ba8ebc00465391d419525 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 1 Oct 2018 15:52:34 +0100 Subject: [PATCH 10/10] Pin version --- packages/react-scripts/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json index bfc24783dbe..134b7de9779 100644 --- a/packages/react-scripts/package.json +++ b/packages/react-scripts/package.json @@ -47,7 +47,7 @@ "html-webpack-plugin": "4.0.0-alpha.2", "identity-obj-proxy": "3.0.0", "jest": "23.6.0", - "jest-pnp-resolver": "^1.0.1", + "jest-pnp-resolver": "1.0.1", "jest-resolve": "23.6.0", "mini-css-extract-plugin": "0.4.3", "optimize-css-assets-webpack-plugin": "5.0.1",