1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 | 'use strict';
|
10 |
|
11 | const chalk = require('react-dev-utils/chalk');
|
12 | const fs = require('fs');
|
13 | const semver = require('semver');
|
14 | const path = require('path');
|
15 |
|
16 |
|
17 |
|
18 |
|
19 | function verifyPackageTree() {
|
20 | const depsToCheck = [
|
21 |
|
22 |
|
23 |
|
24 | 'babel-eslint',
|
25 | 'babel-jest',
|
26 | 'babel-loader',
|
27 | 'eslint',
|
28 | 'jest',
|
29 | 'webpack',
|
30 | 'webpack-dev-server',
|
31 | ];
|
32 |
|
33 |
|
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 |
|
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 |
|
52 | let currentDir = __dirname;
|
53 |
|
54 | while (true) {
|
55 | const previousDir = currentDir;
|
56 | currentDir = path.resolve(currentDir, '..');
|
57 | if (currentDir === previousDir) {
|
58 |
|
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 |
|
163 | module.exports = verifyPackageTree;
|