UNPKG

6.8 kBJavaScriptView Raw
1// @remove-file-on-eject
2/**
3 * Copyright (c) 2015-present, Facebook, Inc.
4 *
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8
9'use strict';
10
11const chalk = require('react-dev-utils/chalk');
12const fs = require('fs');
13const semver = require('semver');
14const path = require('path');
15
16// We assume that having wrong versions of these
17// in the tree will likely break your setup.
18// This is a relatively low-effort way to find common issues.
19function verifyPackageTree() {
20 const depsToCheck = [
21 // These are packages most likely to break in practice.
22 // See https://github.com/facebook/create-react-app/issues/1795 for reasons why.
23 // I have not included Babel here because plugins typically don't import Babel (so it's not affected).
24 'babel-eslint',
25 'babel-jest',
26 'babel-loader',
27 'eslint',
28 'jest',
29 'webpack',
30 'webpack-dev-server',
31 ];
32 // Inlined from semver-regex, MIT license.
33 // Don't want to make this a dependency after ejecting.
34 const getSemverRegex = () =>
35 /\bv?(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[\da-z-]+(?:\.[\da-z-]+)*)?(?:\+[\da-z-]+(?:\.[\da-z-]+)*)?\b/gi;
36 const ownPackageJson = require('../../package.json');
37 const expectedVersionsByDep = {};
38 // Gather wanted deps
39 depsToCheck.forEach(dep => {
40 const expectedVersion = ownPackageJson.dependencies[dep];
41 if (!expectedVersion) {
42 throw new Error('This dependency list is outdated, fix it.');
43 }
44 if (!getSemverRegex().test(expectedVersion)) {
45 throw new Error(
46 `The ${dep} package should be pinned, instead got version ${expectedVersion}.`
47 );
48 }
49 expectedVersionsByDep[dep] = expectedVersion;
50 });
51 // Verify we don't have other versions up the tree
52 let currentDir = __dirname;
53 // eslint-disable-next-line no-constant-condition
54 while (true) {
55 const previousDir = currentDir;
56 currentDir = path.resolve(currentDir, '..');
57 if (currentDir === previousDir) {
58 // We've reached the root.
59 break;
60 }
61 const maybeNodeModules = path.resolve(currentDir, 'node_modules');
62 if (!fs.existsSync(maybeNodeModules)) {
63 continue;
64 }
65 depsToCheck.forEach(dep => {
66 const maybeDep = path.resolve(maybeNodeModules, dep);
67 if (!fs.existsSync(maybeDep)) {
68 return;
69 }
70 const maybeDepPackageJson = path.resolve(maybeDep, 'package.json');
71 if (!fs.existsSync(maybeDepPackageJson)) {
72 return;
73 }
74 const depPackageJson = JSON.parse(
75 fs.readFileSync(maybeDepPackageJson, 'utf8')
76 );
77 const expectedVersion = expectedVersionsByDep[dep];
78 if (!semver.satisfies(depPackageJson.version, expectedVersion)) {
79 console.error(
80 chalk.red(
81 `\nThere might be a problem with the project dependency tree.\n` +
82 `It is likely ${chalk.bold(
83 'not'
84 )} a bug in Create React App, but something you need to fix locally.\n\n`
85 ) +
86 `The ${chalk.bold(
87 ownPackageJson.name
88 )} package provided by Create React App requires a dependency:\n\n` +
89 chalk.green(
90 ` "${chalk.bold(dep)}": "${chalk.bold(expectedVersion)}"\n\n`
91 ) +
92 `Don't try to install it manually: your package manager does it automatically.\n` +
93 `However, a different version of ${chalk.bold(
94 dep
95 )} was detected higher up in the tree:\n\n` +
96 ` ${chalk.bold(chalk.red(maybeDep))} (version: ${chalk.bold(
97 chalk.red(depPackageJson.version)
98 )}) \n\n` +
99 `Manually installing incompatible versions is known to cause hard-to-debug issues.\n\n` +
100 chalk.red(
101 `If you would prefer to ignore this check, add ${chalk.bold(
102 'SKIP_PREFLIGHT_CHECK=true'
103 )} to an ${chalk.bold('.env')} file in your project.\n` +
104 `That will permanently disable this message but you might encounter other issues.\n\n`
105 ) +
106 `To ${chalk.green(
107 'fix'
108 )} the dependency tree, try following the steps below in the exact order:\n\n` +
109 ` ${chalk.cyan('1.')} Delete ${chalk.bold(
110 'package-lock.json'
111 )} (${chalk.underline('not')} ${chalk.bold(
112 'package.json'
113 )}!) and/or ${chalk.bold('yarn.lock')} in your project folder.\n` +
114 ` ${chalk.cyan('2.')} Delete ${chalk.bold(
115 'node_modules'
116 )} in your project folder.\n` +
117 ` ${chalk.cyan('3.')} Remove "${chalk.bold(
118 dep
119 )}" from ${chalk.bold('dependencies')} and/or ${chalk.bold(
120 'devDependencies'
121 )} in the ${chalk.bold(
122 'package.json'
123 )} file in your project folder.\n` +
124 ` ${chalk.cyan('4.')} Run ${chalk.bold(
125 'npm install'
126 )} or ${chalk.bold(
127 'yarn'
128 )}, depending on the package manager you use.\n\n` +
129 `In most cases, this should be enough to fix the problem.\n` +
130 `If this has not helped, there are a few other things you can try:\n\n` +
131 ` ${chalk.cyan('5.')} If you used ${chalk.bold(
132 'npm'
133 )}, install ${chalk.bold(
134 'yarn'
135 )} (http://yarnpkg.com/) and repeat the above steps with it instead.\n` +
136 ` This may help because npm has known issues with package hoisting which may get resolved in future versions.\n\n` +
137 ` ${chalk.cyan('6.')} Check if ${chalk.bold(
138 maybeDep
139 )} is outside your project directory.\n` +
140 ` For example, you might have accidentally installed something in your home folder.\n\n` +
141 ` ${chalk.cyan('7.')} Try running ${chalk.bold(
142 `npm ls ${dep}`
143 )} in your project folder.\n` +
144 ` This will tell you which ${chalk.underline(
145 'other'
146 )} package (apart from the expected ${chalk.bold(
147 ownPackageJson.name
148 )}) installed ${chalk.bold(dep)}.\n\n` +
149 `If nothing else helps, add ${chalk.bold(
150 'SKIP_PREFLIGHT_CHECK=true'
151 )} to an ${chalk.bold('.env')} file in your project.\n` +
152 `That would permanently disable this preflight check in case you want to proceed anyway.\n\n` +
153 chalk.cyan(
154 `P.S. We know this message is long but please read the steps above :-) We hope you find them helpful!\n`
155 )
156 );
157 process.exit(1);
158 }
159 });
160 }
161}
162
163module.exports = verifyPackageTree;