Skip to content

Commit 9e074bb

Browse files
arcanisgaearon
authored andcommitted
Plug'n'Play support (facebook#5136)
* Adds the PnP plugin for Webpack to find dependencies when working under PnP * Adds configuration for jest * Adds an e2e test for when using PnP * Avoids cra from crashing at the engine check * Avoids cra from crashing when initializing react-scripts * Makes the ownPath portable * Fixes linting * Bumps to pnp-webpack-plugin@1.1.0, removes symlinks: false * Adds a --use-pnp option * Pin version
1 parent f59165f commit 9e074bb

File tree

9 files changed

+116
-22
lines changed

9 files changed

+116
-22
lines changed

.travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ cache:
99
directories:
1010
- .npm
1111
before_install:
12-
- curl -o- -L https://yarnpkg.com/install.sh | bash
12+
- curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --nightly
1313
- export PATH="$HOME/.yarn/bin:$PATH"
1414
install: true
1515
script:

appveyor.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ platform:
3737
install:
3838
- ps: Install-Product node $env:nodejs_version $env:platform
3939
- ps: |
40-
(New-Object Net.WebClient).DownloadFile("https://yarnpkg.com/latest.msi", "$env:temp\yarn.msi")
40+
(New-Object Net.WebClient).DownloadFile("https://nightly.yarnpkg.com/latest.msi", "$env:temp\yarn.msi")
4141
cmd /c start /wait msiexec.exe /i $env:temp\yarn.msi /quiet /qn /norestart
4242
4343
build: off

packages/create-react-app/createReactApp.js

+73-16
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ const program = new commander.Command(packageJson.name)
7676
'use a non-standard version of react-scripts'
7777
)
7878
.option('--use-npm')
79+
.option('--use-pnp')
7980
.allowUnknownOption()
8081
.on('--help', () => {
8182
console.log(` Only ${chalk.green('<project-directory>')} is required.`);
@@ -178,10 +179,11 @@ createApp(
178179
program.verbose,
179180
program.scriptsVersion,
180181
program.useNpm,
182+
program.usePnp,
181183
hiddenProgram.internalTestingTemplate
182184
);
183185

184-
function createApp(name, verbose, version, useNpm, template) {
186+
function createApp(name, verbose, version, useNpm, usePnp, template) {
185187
const root = path.resolve(name);
186188
const appName = path.basename(root);
187189

@@ -241,7 +243,16 @@ function createApp(name, verbose, version, useNpm, template) {
241243
version = 'react-scripts@0.9.x';
242244
}
243245
}
244-
run(root, appName, version, verbose, originalDirectory, template, useYarn);
246+
run(
247+
root,
248+
appName,
249+
version,
250+
verbose,
251+
originalDirectory,
252+
template,
253+
useYarn,
254+
usePnp
255+
);
245256
}
246257

247258
function shouldUseYarn() {
@@ -253,7 +264,7 @@ function shouldUseYarn() {
253264
}
254265
}
255266

256-
function install(root, useYarn, dependencies, verbose, isOnline) {
267+
function install(root, useYarn, usePnp, dependencies, verbose, isOnline) {
257268
return new Promise((resolve, reject) => {
258269
let command;
259270
let args;
@@ -263,6 +274,9 @@ function install(root, useYarn, dependencies, verbose, isOnline) {
263274
if (!isOnline) {
264275
args.push('--offline');
265276
}
277+
if (usePnp) {
278+
args.push('--enable-pnp');
279+
}
266280
[].push.apply(args, dependencies);
267281

268282
// Explicitly set cwd() to work around issues like
@@ -287,6 +301,12 @@ function install(root, useYarn, dependencies, verbose, isOnline) {
287301
'--loglevel',
288302
'error',
289303
].concat(dependencies);
304+
305+
if (usePnp) {
306+
console.log(chalk.yellow("NPM doesn't support PnP."));
307+
console.log(chalk.yellow('Falling back to the regular installs.'));
308+
console.log();
309+
}
290310
}
291311

292312
if (verbose) {
@@ -313,7 +333,8 @@ function run(
313333
verbose,
314334
originalDirectory,
315335
template,
316-
useYarn
336+
useYarn,
337+
usePnp
317338
) {
318339
const packageToInstall = getInstallPackage(version, originalDirectory);
319340
const allDependencies = ['react', 'react-dom', packageToInstall];
@@ -336,23 +357,34 @@ function run(
336357
);
337358
console.log();
338359

339-
return install(root, useYarn, allDependencies, verbose, isOnline).then(
340-
() => packageName
341-
);
360+
return install(
361+
root,
362+
useYarn,
363+
usePnp,
364+
allDependencies,
365+
verbose,
366+
isOnline
367+
).then(() => packageName);
342368
})
343-
.then(packageName => {
369+
.then(async packageName => {
344370
checkNodeVersion(packageName);
345371
setCaretRangeForRuntimeDeps(packageName);
346372

347-
const scriptsPath = path.resolve(
348-
process.cwd(),
349-
'node_modules',
350-
packageName,
351-
'scripts',
352-
'init.js'
373+
const pnpPath = path.resolve(process.cwd(), '.pnp.js');
374+
375+
const nodeArgs = fs.existsSync(pnpPath) ? ['--require', pnpPath] : [];
376+
377+
await executeNodeScript(
378+
{
379+
cwd: process.cwd(),
380+
args: nodeArgs,
381+
},
382+
[root, appName, verbose, originalDirectory, template],
383+
`
384+
var init = require('${packageName}/scripts/init.js');
385+
init.apply(null, JSON.parse(process.argv[1]));
386+
`
353387
);
354-
const init = require(scriptsPath);
355-
init(root, appName, verbose, originalDirectory, template);
356388

357389
if (version === 'react-scripts@0.9.x') {
358390
console.log(
@@ -540,6 +572,11 @@ function checkNodeVersion(packageName) {
540572
packageName,
541573
'package.json'
542574
);
575+
576+
if (!fs.existsSync(packageJsonPath)) {
577+
return;
578+
}
579+
543580
const packageJson = require(packageJsonPath);
544581
if (!packageJson.engines || !packageJson.engines.node) {
545582
return;
@@ -794,3 +831,23 @@ function checkIfOnline(useYarn) {
794831
});
795832
});
796833
}
834+
835+
function executeNodeScript({ cwd, args }, data, source) {
836+
return new Promise((resolve, reject) => {
837+
const child = spawn(
838+
process.execPath,
839+
[...args, '-e', source, '--', JSON.stringify(data)],
840+
{ cwd, stdio: 'inherit' }
841+
);
842+
843+
child.on('close', code => {
844+
if (code !== 0) {
845+
reject({
846+
command: `node ${args.join(' ')}`,
847+
});
848+
return;
849+
}
850+
resolve();
851+
});
852+
});
853+
}

packages/react-scripts/config/webpack.config.dev.js

+11
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
const path = require('path');
1212
const webpack = require('webpack');
13+
const PnpWebpackPlugin = require('pnp-webpack-plugin');
1314
const HtmlWebpackPlugin = require('html-webpack-plugin');
1415
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
1516
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
@@ -150,6 +151,9 @@ module.exports = {
150151
'react-native': 'react-native-web',
151152
},
152153
plugins: [
154+
// Adds support for installing with Plug'n'Play, leading to faster installs and adding
155+
// guards against forgotten dependencies and such.
156+
PnpWebpackPlugin,
153157
// Prevents users from importing files from outside of src/ (or node_modules/).
154158
// This often causes confusion because we only process files within src/ with babel.
155159
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
@@ -158,6 +162,13 @@ module.exports = {
158162
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
159163
],
160164
},
165+
resolveLoader: {
166+
plugins: [
167+
// Also related to Plug'n'Play, but this time it tells Webpack to load its loaders
168+
// from the current package.
169+
PnpWebpackPlugin.moduleLoader(module),
170+
],
171+
},
161172
module: {
162173
strictExportPresence: true,
163174
rules: [

packages/react-scripts/config/webpack.config.prod.js

+11
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
const path = require('path');
1212
const webpack = require('webpack');
13+
const PnpWebpackPlugin = require('pnp-webpack-plugin');
1314
const HtmlWebpackPlugin = require('html-webpack-plugin');
1415
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
1516
const TerserPlugin = require('terser-webpack-plugin');
@@ -214,6 +215,9 @@ module.exports = {
214215
'react-native': 'react-native-web',
215216
},
216217
plugins: [
218+
// Adds support for installing with Plug'n'Play, leading to faster installs and adding
219+
// guards against forgotten dependencies and such.
220+
PnpWebpackPlugin,
217221
// Prevents users from importing files from outside of src/ (or node_modules/).
218222
// This often causes confusion because we only process files within src/ with babel.
219223
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
@@ -222,6 +226,13 @@ module.exports = {
222226
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
223227
],
224228
},
229+
resolveLoader: {
230+
plugins: [
231+
// Also related to Plug'n'Play, but this time it tells Webpack to load its loaders
232+
// from the current package.
233+
PnpWebpackPlugin.moduleLoader(module),
234+
],
235+
},
225236
module: {
226237
strictExportPresence: true,
227238
rules: [

packages/react-scripts/package.json

+3
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,11 @@
4747
"html-webpack-plugin": "4.0.0-alpha.2",
4848
"identity-obj-proxy": "3.0.0",
4949
"jest": "23.6.0",
50+
"jest-pnp-resolver": "1.0.1",
51+
"jest-resolve": "23.6.0",
5052
"mini-css-extract-plugin": "0.4.3",
5153
"optimize-css-assets-webpack-plugin": "5.0.1",
54+
"pnp-webpack-plugin": "1.1.0",
5255
"postcss-flexbugs-fixes": "4.1.0",
5356
"postcss-loader": "3.0.0",
5457
"postcss-preset-env": "6.0.6",

packages/react-scripts/scripts/init.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,9 @@ module.exports = function(
8181
originalDirectory,
8282
template
8383
) {
84-
const ownPackageName = require(path.join(__dirname, '..', 'package.json'))
85-
.name;
86-
const ownPath = path.join(appPath, 'node_modules', ownPackageName);
84+
const ownPath = path.dirname(
85+
require.resolve(path.join(__dirname, '..', 'package.json'))
86+
);
8787
const appPackage = require(path.join(appPath, 'package.json'));
8888
const useYarn = fs.existsSync(path.join(appPath, 'yarn.lock'));
8989

packages/react-scripts/scripts/utils/createJestConfig.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ module.exports = (resolve, rootDir, isEjecting) => {
2222
// in Jest configs. We need help from somebody with Windows to determine this.
2323
const config = {
2424
collectCoverageFrom: ['src/**/*.{js,jsx}'],
25-
setupFiles: ['react-app-polyfill/jsdom'],
25+
resolver: require.resolve('jest-pnp-resolver'),
26+
setupFiles: [require.resolve('react-app-polyfill/jsdom')],
2627
setupTestFrameworkScriptFile: setupTestsFile,
2728
testMatch: [
2829
'<rootDir>/src/**/__tests__/**/*.{js,jsx}',

tasks/e2e-installs.sh

+11
Original file line numberDiff line numberDiff line change
@@ -229,5 +229,16 @@ npx create-react-app test-app-nested-paths-t3/aa/bb/cc/dd
229229
cd test-app-nested-paths-t3/aa/bb/cc/dd
230230
yarn start --smoke-test
231231

232+
# ******************************************************************************
233+
# Test when PnP is enabled
234+
# ******************************************************************************
235+
cd "$temp_app_path"
236+
npx create-react-app test-app-pnp --use-pnp
237+
cd test-app-pnp
238+
! exists node_modules
239+
exists .pnp.js
240+
yarn start --smoke-test
241+
yarn build
242+
232243
# Cleanup
233244
cleanup

0 commit comments

Comments
 (0)