8

When running react-scripts build (Create-React-App) in a sub folder (c:\Repos\web_app1\api_ui) with it's own package.json, node_modules folder, etc. I get the following error:

 react-scripts build


There might be a problem with the project dependency tree.
It is likely not a bug in Create React App, but something you need to fix locally.

The react-scripts package provided by Create React App requires a dependency:

  "babel-loader": "8.0.4"

Don't try to install it manually: your package manager does it automatically.
However, a different version of babel-loader was detected higher up in the tree:

  c:\Repos\web_app1\node_modules\babel-loader (version: 7.1.4)
  • Updating the parent folder's (c:\Repos\web_app1) babel-loader to v8.0.4 is not an option as web_app1 depends on babel-loader v7.1.4
  • Deleting the node_modules in c:\Repos\web_app1 is not an option. This is the parent application and needs its own node_modules.
  • My fix was adding SKIP_PREFLIGHT_CHECK=true to and .env file. This seems like a hack and I would like another fix that involves building through the preflight check.
  • The package-lock.json in the sub folder (c:\Repos\web_app1\api_ui) has the correct babel-loader version (8.0.4), so why is it going to the parent folder?

Is there a way to ignore parent folder or higher tree node_modules when building react-scripts in a sub folder?

Badro Niaimi
  • 959
  • 1
  • 14
  • 28
Griffin Pair
  • 105
  • 6
  • I have the same problem, did you manage to solve it? – shem Nov 23 '21 at 08:43
  • Does this answer your question? [react-script problem while having a node\_module folder in the parent directory](https://stackoverflow.com/questions/53282430/react-script-problem-while-having-a-node-module-folder-in-the-parent-directory) – Bamse Dec 30 '21 at 02:20

1 Answers1

1

This puzzled me a lot too and I did some research, looking in to the source code of react-scripts version 4.0.3. This version requires babel-loader v. 8.1.0. With storybook installed (requiring version ^8.0.0), we get a different babel-loader in the top level node_modules so that we end up with:

|-src
|-node_modules
  |
  |-storybook 6.3.12
  |-babel-loader 8.2.6
  |-react-scripts 4.0.3
    |
    |-node_modules
      |
      |-babel-loader 8.1.0

I get the same error message as a lot of people have seen, and I think to myself, shouldn't any use of babel-loader in react-scripts get the babel-loader in its own node_modules, namely version 8.1.0? I can delete node_modules and package-lock.json any number of times and it seems the problem always persists.

How you describe it in the question is in fact how the package managers (at least npm) works, e.g. importing babel-loader from react-scripts WOULD give it version 8.1.0 from its own node_modules... but we forget about two things, that combined will cause problems:

  • Package hoisting
  • Transitive dependencies

Package hoisting

I won't go into details on when package hoisting occurs, but it happens. Basically, it amounts to a dependency package B of a dependency A being added to the project root node_modules (or some other parent node_modules) instead of to the node_modules of A.

So instead of

|-src
|-node_modules
  |
  |-A
    |
    |-node_modules
      |
      |-B

... we get ...

|-src
|-node_modules
  |
  |-A
    |
    |-node_modules (might exist anyway)
  |-B

Due to how npm works (and as you correctly understood it in the question), if B cannot be found in node_modules/A/node_modules, it will look in node_modules for the package, which makes this work. Hoisting can be an optimization when many packages all need compatible dependencies so that we, instead of storing N similar versions of the dependency, can store only one. It also simplifies the folder structure of the project root node_modules which is then easier to debug. As a matter of fact, the default is to hoist any packages that can be hoisted, e.g. that don't already exist in the node_modules to hoist to.

Usually, fallback to the naive strategy, with "all dependencies for a package in its own node_modules", is used whenever we have conflicting package versions. This is what has happened in the top example with two different versions of babel-loader; storybook really consists of several packages and multiple of them uses babel-loader version ^8.0.0. When storybook is installed before react-scripts, it takes the most recent version of babel-loader which meets the constraint, and it hoists it to the top-level node_modules. This then causes the familiar problems when react-scripts is installed. I have another project with the same setup, but where react-scripts was installed before storybook. There, babel-loader version 8.1.0 is hoisted instead, and since this package also meets the requirements of storybook (^8.0.0), no more babel-loader's are needed. Here, create-react-scripts does not complain. It is of course desirable that npm could figure this order out by itself, which is also optimal from a storage perspective (only one babel-loader instead of two), but as far as I know, npm uses alphabetical order when processing dependencies.

Transitive dependencies

Even though package A is a dependency of my project, A can have dependencies of its own. These are transitive dependencies with respect to my project.

Why doesn't it work?

In the source for react-script version 4.0.3, the file verifyPackageTree is responsible for running the check hat results in the boring error message in the question. This file is no longer around in later prereleases, but in it at the top, we find the comment:

// 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.

A few rows down it says:

// These are packages most likely to break in practice.
// See https://github.com/facebook/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).

Looking at the referenced thread, there is a discussion about why create-react-app breaks when non-compatible dependencies are installed with the same question as above... WHY doesn't it work?

The final conclusion seems to be that if, for example, react-scripts imports a package X that has been hoisted, this package won't reside in the node_modules under react-scripts but instead in the root node_modules, where (with respect to the topmost example) also babel-loader version 8.2.6 resides (as opposed to version 8.1.0 in the node_modules folder of react-scripts). Now if X were to import babel-loader, it would get the wrong babel-loader, e.g. NOT version 8.1.0 as expected but instead 8.2.6. In the thread this is considered a bug in npm even though I don't exactly know why it would be a bug. Perhaps, there could be some flag indicating whether it is ok to hoist a package or not because if hoisting altogether is considered a bug, it seems like as step backwards.

Because the writers of create-react-app can't know whether some dependency is hoisted or not, they have implemented this simple check which throws a warning. For advanced users they give a chance to opt out of the check with the SKIP_PREFLIGHT_CHECK flag. Also, it is good to remember that the error comes from create-react-app, not npm, so it really doesn't say anything about how npm works, only what the creators of create-react-app considers a problem (which was indeed equally confusing initially).

fast-reflexes
  • 4,891
  • 4
  • 31
  • 44