verifyPackageTree.js 6.6 KB

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