From 1a61cd452e8f391d676d1b6b47f163bab745c427 Mon Sep 17 00:00:00 2001
From: Dan Abramov <dan.abramov@gmail.com>
Date: Mon, 30 Oct 2017 21:24:06 +0000
Subject: [PATCH] Add preflight CWD check for npm (#3355)

* Add preflight CWD check for npm

* Typos
---
 packages/create-react-app/createReactApp.js | 79 ++++++++++++++++++++-
 1 file changed, 76 insertions(+), 3 deletions(-)

diff --git a/packages/create-react-app/createReactApp.js b/packages/create-react-app/createReactApp.js
index 161beab50..9ff7c08cf 100755
--- a/packages/create-react-app/createReactApp.js
+++ b/packages/create-react-app/createReactApp.js
@@ -158,8 +158,13 @@ function createApp(name, verbose, version, template) {
     path.join(root, 'package.json'),
     JSON.stringify(packageJson, null, 2)
   );
+
+  const useYarn = shouldUseYarn();
   const originalDirectory = process.cwd();
   process.chdir(root);
+  if (!useYarn && !checkThatNpmCanReadCwd()) {
+    process.exit(1);
+  }
 
   if (!semver.satisfies(process.version, '>=6.0.0')) {
     console.log(
@@ -172,7 +177,6 @@ function createApp(name, verbose, version, template) {
     version = 'react-scripts@0.9.x';
   }
 
-  const useYarn = shouldUseYarn();
   if (!useYarn) {
     const npmInfo = checkNpmVersion();
     if (!npmInfo.hasMinNpm) {
@@ -200,7 +204,7 @@ function shouldUseYarn() {
   }
 }
 
-function install(useYarn, dependencies, verbose, isOnline) {
+function install(root, useYarn, dependencies, verbose, isOnline) {
   return new Promise((resolve, reject) => {
     let command;
     let args;
@@ -212,6 +216,14 @@ function install(useYarn, dependencies, verbose, isOnline) {
       }
       [].push.apply(args, dependencies);
 
+      // Explicitly set cwd() to work around issues like
+      // https://github.com/facebookincubator/create-react-app/issues/3326.
+      // Unfortunately we can only do this for Yarn because npm support for
+      // equivalent --prefix flag doesn't help with this issue.
+      // This is why for npm, we run checkThatNpmCanReadCwd() early instead.
+      args.push('--cwd');
+      args.push(root);
+
       if (!isOnline) {
         console.log(chalk.yellow('You appear to be offline.'));
         console.log(chalk.yellow('Falling back to the local Yarn cache.'));
@@ -275,7 +287,7 @@ function run(
       );
       console.log();
 
-      return install(useYarn, allDependencies, verbose, isOnline).then(
+      return install(root, useYarn, allDependencies, verbose, isOnline).then(
         () => packageName
       );
     })
@@ -606,6 +618,67 @@ function isSafeToCreateProjectIn(root, name) {
   return false;
 }
 
+function checkThatNpmCanReadCwd() {
+  const cwd = process.cwd();
+  let childOutput = null;
+  try {
+    // Note: intentionally using spawn over exec since
+    // the problem doesn't reproduce otherwise.
+    // `npm config list` is the only reliable way I could find
+    // to reproduce the wrong path. Just printing process.cwd()
+    // in a Node process was not enough.
+    childOutput = spawn.sync('npm', ['config', 'list']).output.join('');
+  } catch (err) {
+    // Something went wrong spawning node.
+    // Not great, but it means we can't do this check.
+    // We might fail later on, but let's continue.
+    return true;
+  }
+  if (typeof childOutput !== 'string') {
+    return true;
+  }
+  const lines = childOutput.split('\n');
+  // `npm config list` output includes the following line:
+  // "; cwd = C:\path\to\current\dir" (unquoted)
+  // I couldn't find an easier way to get it.
+  const prefix = '; cwd = ';
+  const line = lines.find(line => line.indexOf(prefix) === 0);
+  if (typeof line !== 'string') {
+    // Fail gracefully. They could remove it.
+    return true;
+  }
+  const npmCWD = line.substring(prefix.length);
+  if (npmCWD === cwd) {
+    return true;
+  }
+  console.error(
+    chalk.red(
+      `Could not start an npm process in the right directory.\n\n` +
+        `The current directory is: ${chalk.bold(cwd)}\n` +
+        `However, a newly started npm process runs in: ${chalk.bold(
+          npmCWD
+        )}\n\n` +
+        `This is probably caused by a misconfigured system terminal shell.`
+    )
+  );
+  if (process.platform === 'win32') {
+    console.error(
+      chalk.red(`On Windows, this can usually be fixed by running:\n\n`) +
+        `  ${chalk.cyan(
+          'reg'
+        )} delete "HKCU\\Software\\Microsoft\\Command Processor" /v AutoRun /f\n` +
+        `  ${chalk.cyan(
+          'reg'
+        )} delete "HKLM\\Software\\Microsoft\\Command Processor" /v AutoRun /f\n\n` +
+        chalk.red(`Try to run the above two lines in the terminal.\n`) +
+        chalk.red(
+          `To learn more about this problem, read: https://blogs.msdn.microsoft.com/oldnewthing/20071121-00/?p=24433/`
+        )
+    );
+  }
+  return false;
+}
+
 function checkIfOnline(useYarn) {
   if (!useYarn) {
     // Don't ping the Yarn registry.
-- 
GitLab