mirror of
https://github.com/zhigang1992/create-react-app.git
synced 2026-04-28 09:25:42 +08:00
Add preflight check to guard against wrong versions of webpack/eslint/jest higher up the tree (#3771)
* Run real scripts in local development * Add preflight check warning * I know what I am doing * Move preflight check into individual scripts This ensures we don't try to filter NODE_PATH twice, accidentally removing the now-absolute path. * Slightly tweak the wording * Fix lint
This commit is contained in:
7
packages/react-scripts/bin/react-scripts.js
vendored
7
packages/react-scripts/bin/react-scripts.js
vendored
@@ -8,6 +8,13 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
// Makes the script crash on unhandled rejections instead of silently
|
||||
// ignoring them. In the future, promise rejections that are not handled will
|
||||
// terminate the Node.js process with a non-zero exit code.
|
||||
process.on('unhandledRejection', err => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
const spawn = require('react-dev-utils/crossSpawn');
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
|
||||
7
packages/react-scripts/scripts/build.js
vendored
7
packages/react-scripts/scripts/build.js
vendored
@@ -21,6 +21,13 @@ process.on('unhandledRejection', err => {
|
||||
|
||||
// Ensure environment variables are read.
|
||||
require('../config/env');
|
||||
// @remove-on-eject-begin
|
||||
// Do the preflight check (only happens before eject).
|
||||
const verifyPackageTree = require('./utils/verifyPackageTree');
|
||||
if (process.env.SKIP_PREFLIGHT_CHECK !== 'true') {
|
||||
verifyPackageTree();
|
||||
}
|
||||
// @remove-on-eject-end
|
||||
|
||||
const path = require('path');
|
||||
const chalk = require('chalk');
|
||||
|
||||
7
packages/react-scripts/scripts/start.js
vendored
7
packages/react-scripts/scripts/start.js
vendored
@@ -21,6 +21,13 @@ process.on('unhandledRejection', err => {
|
||||
|
||||
// Ensure environment variables are read.
|
||||
require('../config/env');
|
||||
// @remove-on-eject-begin
|
||||
// Do the preflight check (only happens before eject).
|
||||
const verifyPackageTree = require('./utils/verifyPackageTree');
|
||||
if (process.env.SKIP_PREFLIGHT_CHECK !== 'true') {
|
||||
verifyPackageTree();
|
||||
}
|
||||
// @remove-on-eject-end
|
||||
|
||||
const fs = require('fs');
|
||||
const chalk = require('chalk');
|
||||
|
||||
7
packages/react-scripts/scripts/test.js
vendored
7
packages/react-scripts/scripts/test.js
vendored
@@ -22,6 +22,13 @@ process.on('unhandledRejection', err => {
|
||||
|
||||
// Ensure environment variables are read.
|
||||
require('../config/env');
|
||||
// @remove-on-eject-begin
|
||||
// Do the preflight check (only happens before eject).
|
||||
const verifyPackageTree = require('./utils/verifyPackageTree');
|
||||
if (process.env.SKIP_PREFLIGHT_CHECK !== 'true') {
|
||||
verifyPackageTree();
|
||||
}
|
||||
// @remove-on-eject-end
|
||||
|
||||
const jest = require('jest');
|
||||
const argv = process.argv.slice(2);
|
||||
|
||||
153
packages/react-scripts/scripts/utils/verifyPackageTree.js
vendored
Normal file
153
packages/react-scripts/scripts/utils/verifyPackageTree.js
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
// @remove-file-on-eject
|
||||
/**
|
||||
* Copyright (c) 2015-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 chalk = require('chalk');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// We assume that having wrong versions of these
|
||||
// in the tree will likely break your setup.
|
||||
// This is a relatively low-effort way to find common issues.
|
||||
function verifyPackageTree() {
|
||||
const depsToCheck = [
|
||||
// These are packages most likely to break in practice.
|
||||
// See https://github.com/facebookincubator/create-react-app/issues/1795 for reasons why.
|
||||
// I have not included Babel here because plugins typically don't import Babel (so it's not affected).
|
||||
'eslint',
|
||||
'jest',
|
||||
'webpack',
|
||||
'webpack-dev-server',
|
||||
];
|
||||
// Inlined from semver-regex, MIT license.
|
||||
// Don't want to make this a dependency after ejecting.
|
||||
const getSemverRegex = () =>
|
||||
/\bv?(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[\da-z-]+(?:\.[\da-z-]+)*)?(?:\+[\da-z-]+(?:\.[\da-z-]+)*)?\b/gi;
|
||||
const ownPackageJson = require('../../package.json');
|
||||
const expectedVersionsByDep = {};
|
||||
// Gather wanted deps
|
||||
depsToCheck.forEach(dep => {
|
||||
const expectedVersion = ownPackageJson.dependencies[dep];
|
||||
if (!expectedVersion) {
|
||||
throw new Error('This dependency list is outdated, fix it.');
|
||||
}
|
||||
if (!getSemverRegex().test(expectedVersion)) {
|
||||
throw new Error(
|
||||
`The ${dep} package should be pinned, instead got version ${expectedVersion}.`
|
||||
);
|
||||
}
|
||||
expectedVersionsByDep[dep] = expectedVersion;
|
||||
});
|
||||
// Verify we don't have other versions up the tree
|
||||
let currentDir = __dirname;
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
const previousDir = currentDir;
|
||||
currentDir = path.resolve(currentDir, '..');
|
||||
if (currentDir === previousDir) {
|
||||
// We've reached the root.
|
||||
break;
|
||||
}
|
||||
const maybeNodeModules = path.resolve(currentDir, 'node_modules');
|
||||
if (!fs.existsSync(maybeNodeModules)) {
|
||||
continue;
|
||||
}
|
||||
depsToCheck.forEach(dep => {
|
||||
const maybeDep = path.resolve(maybeNodeModules, dep);
|
||||
if (!fs.existsSync(maybeDep)) {
|
||||
return;
|
||||
}
|
||||
const maybeDepPackageJson = path.resolve(maybeDep, 'package.json');
|
||||
if (!fs.existsSync(maybeDepPackageJson)) {
|
||||
return;
|
||||
}
|
||||
const depPackageJson = JSON.parse(
|
||||
fs.readFileSync(maybeDepPackageJson, 'utf8')
|
||||
);
|
||||
const expectedVersion = expectedVersionsByDep[dep];
|
||||
if (depPackageJson.version !== expectedVersion) {
|
||||
console.error(
|
||||
chalk.red(
|
||||
`\nThere might be a problem with the project dependency tree.\n` +
|
||||
`It is likely ${chalk.bold(
|
||||
'not'
|
||||
)} a bug in Create React App, but something you need to fix locally.\n\n`
|
||||
) +
|
||||
`The ${chalk.bold(
|
||||
ownPackageJson.name
|
||||
)} package provided by Create React App requires a dependency:\n\n` +
|
||||
chalk.green(
|
||||
` "${chalk.bold(dep)}": "${chalk.bold(expectedVersion)}"\n\n`
|
||||
) +
|
||||
`Don't try to install it manually: your package manager does it automatically.\n` +
|
||||
`However, a different version of ${chalk.bold(
|
||||
dep
|
||||
)} was detected higher up in the tree:\n\n` +
|
||||
` ${chalk.bold(chalk.red(maybeDep))} (version: ${chalk.bold(
|
||||
chalk.red(depPackageJson.version)
|
||||
)}) \n\n` +
|
||||
`Manually installing incompatible versions is known to cause hard-to-debug issues.\n` +
|
||||
`To fix the dependency tree, try following the steps below in the exact order:\n\n` +
|
||||
` ${chalk.cyan('1.')} Delete ${chalk.bold(
|
||||
'package-lock.json'
|
||||
)} (${chalk.underline('not')} ${chalk.bold(
|
||||
'package.json'
|
||||
)}!) and/or ${chalk.bold(
|
||||
'yarn.lock'
|
||||
)} in your project folder.\n\n` +
|
||||
` ${chalk.cyan('2.')} Delete ${chalk.bold(
|
||||
'node_modules'
|
||||
)} in your project folder.\n\n` +
|
||||
` ${chalk.cyan('3.')} Remove "${chalk.bold(
|
||||
dep
|
||||
)}" from ${chalk.bold('dependencies')} and/or ${chalk.bold(
|
||||
'devDependencies'
|
||||
)} in the ${chalk.bold(
|
||||
'package.json'
|
||||
)} file in your project folder.\n\n` +
|
||||
` ${chalk.cyan('4.')} Run ${chalk.bold(
|
||||
'npm install'
|
||||
)} or ${chalk.bold(
|
||||
'yarn'
|
||||
)}, depending on the package manager you use.\n\n` +
|
||||
`In most cases, this should be enough to fix the problem.\n` +
|
||||
`If this has not helped, there are a few other things you can try:\n\n` +
|
||||
` ${chalk.cyan('5.')} If you used ${chalk.bold(
|
||||
'npm'
|
||||
)}, install ${chalk.bold(
|
||||
'yarn'
|
||||
)} (http://yarnpkg.com/) and repeat the above steps with it instead.\n` +
|
||||
` This may help because npm has known issues with package hoisting which may get resolved in future versions.\n\n` +
|
||||
` ${chalk.cyan('6.')} Check if ${chalk.bold(
|
||||
maybeDep
|
||||
)} is outside your project directory.\n` +
|
||||
` For example, you might have accidentally installed something in your home folder.\n\n` +
|
||||
` ${chalk.cyan('7.')} Try running ${chalk.bold(
|
||||
`npm ls ${dep}`
|
||||
)} in your project folder.\n` +
|
||||
` This will tell you which ${chalk.underline(
|
||||
'other'
|
||||
)} package (apart from the expected ${chalk.bold(
|
||||
ownPackageJson.name
|
||||
)}) installed ${chalk.bold(dep)}.\n\n` +
|
||||
`If nothing else helps, add ${chalk.bold(
|
||||
'SKIP_PREFLIGHT_CHECK=true'
|
||||
)} to an ${chalk.bold('.env')} file in your project.\n` +
|
||||
`That would permanently disable this preflight check in case you want to proceed anyway.\n\n` +
|
||||
chalk.cyan(
|
||||
`P.S. We know this message is long but please read the steps above :-) We hope you find them helpful!\n`
|
||||
)
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = verifyPackageTree;
|
||||
Reference in New Issue
Block a user