261

I'm trying to understand how recreating the node_modules directory for deployment works.

We're using npm ci instead of npm install to ensure a clean slate during deployment. However, when we run it without any flags, we get the following error:

Fix the upstream dependency conflict, or retry this command with --force, or --legacy-peer-deps to accept an incorrect (and potentially broken) dependency resolution.

The documentation for npm install for --force is as follows (there are no flags on npm ci's page):

The -f or --force argument will force npm to fetch remote resources even if a local copy exists on disk.

Meanwhile, the documentation for --legacy-peer-deps says:

--legacy-peer-deps: ignore all peerDependencies when installing, in the style of npm version 4 through version 6.

It seems that both flags will let npm ci generate the node_modules directory without any issues, but I am still unclear about the differences between the two.

From what I understand, --force sounds like it will be on a last-dependency-downloaded-wins basis and will overwrite any previously downloaded dependencies. Meanwhile, --legacy-peer-deps sounds like it will always skip peer dependencies (whatever those are) during installation even if there are no issues.

What are the differences between the two flags, and when should we use them?

1
  • none of the above is said to be true to the original question Commented Feb 5 at 10:14

6 Answers 6

199

In the new version of npm (v7), by default, npm install will fail when it encounters conflicting peerDependencies. It was not like that before.

Take a look here for more info about peer dependencies in npm v7.

The differences between the two are below -

  • --legacy-peer-deps: ignore all peerDependencies when installing, in the style of npm version 4 through version 6.

  • --strict-peer-deps: fail and abort the install process for any conflicting peerDependencies when encountered. By default, npm will only crash for peerDependencies conflicts caused by the direct dependencies of the root project.

  • --force: will force npm to fetch remote resources even if a local copy exists on disk.

Sign up to request clarification or add additional context in comments.

3 Comments

and what of --force per OP? Are there performance benefits with one over the other or overall deduped packages, etc?
--force still pins many dependency versions while --legacy-peer-deps ignore peer dependencies entirely. See example from @HongboMiao
The question is about the when to use what but I don't see that in the answer. Can you elaborate more about what are the differences between the 2?
92

In the article npm 7 is now generally available!,

You have the option to retry with --force to bypass the conflict or --legacy-peer-deps command to ignore peer dependencies entirely (this behavior is similar to versions 4-6).

I agree this sentence is not really clear, but "ignore peer dependencies entirely" does not sound good. Let's use a real example:

Here is a peer dependency error I met when I npm install:

npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: mobile@undefined
npm ERR! Found: [email protected]
npm ERR! node_modules/react
npm ERR!   react@"17.0.1" from the root project
npm ERR!   peer react@">=16.0.0" from @testing-library/[email protected]
npm ERR!   node_modules/@testing-library/react-native
npm ERR!     dev @testing-library/react-native@"7.2.0" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer react@"16.13.1" from [email protected]
npm ERR! node_modules/react-native
npm ERR!   react-native@"https://github.com/expo/react-native/archive/sdk-39.0.4.tar.gz" from the root project
npm ERR!   peer react-native@">=0.59" from @testing-library/[email protected]
npm ERR!   node_modules/@testing-library/react-native
npm ERR!     dev @testing-library/react-native@"7.2.0" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR!
npm ERR! See /Users/me/.npm/eresolve-report.txt for a full report.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/me/.npm/_logs/2021-03-13T00_10_33_813Z-debug.log
npm ERR! code 1
npm ERR! path /Users/me/my-app
npm ERR! command failed
npm ERR! command sh -c sh ./bin/setup.sh

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/me/.npm/_logs/2021-03-13T00_10_33_860Z-debug.log

Below is the package-lock.json difference between --legacy-peer-deps and --force.

  1. If I run npm install --legacy-peer-deps, it adds this in my package-lock.json:
"node_modules/@unimodules/react-native-adapter": {
  "version": "5.7.0",
  "resolved": "https://registry.npmjs.org/@unimodules/react-native-adapter/-/react-native-adapter-5.7.0.tgz",
  "integrity": "sha512-L557/+sc8ZKJVgo1734HF1QNCxrt/fpqdmdNgySJT+kErux/AJNfPq3flsK0fyJduVmniTutYIMyW48cFoPKDA==",
  "dependencies": {
    "invariant": "^2.2.4",
    "lodash": "^4.5.0"
  },
  "peerDependencies": {
    "react-native": "*",
    "react-native-web": "~0.13.7"
  }
},

...

"@unimodules/react-native-adapter": {
  "version": "5.7.0",
  "resolved": "https://registry.npmjs.org/@unimodules/react-native-adapter/-/react-native-adapter-5.7.0.tgz",
  "integrity": "sha512-L557/+sc8ZKJVgo1734HF1QNCxrt/fpqdmdNgySJT+kErux/AJNfPq3flsK0fyJduVmniTutYIMyW48cFoPKDA==",
  "requires": {
    "invariant": "^2.2.4",
    "lodash": "^4.5.0"
  }
},
  1. If I use npm install --force, instead, it adds
"node_modules/expo/node_modules/@unimodules/react-native-adapter": {
  "version": "5.7.0",
  "resolved": "https://registry.npmjs.org/@unimodules/react-native-adapter/-/react-native-adapter-5.7.0.tgz",
  "integrity": "sha512-L557/+sc8ZKJVgo1734HF1QNCxrt/fpqdmdNgySJT+kErux/AJNfPq3flsK0fyJduVmniTutYIMyW48cFoPKDA==",
  "dependencies": {
    "invariant": "^2.2.4",
    "lodash": "^4.5.0"
  },
  "peerDependencies": {
    "react-native": "*",
    "react-native-web": "~0.13.7"
  }
},
"node_modules/expo/node_modules/inline-style-prefixer": {
  "version": "5.1.2",
  "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-5.1.2.tgz",
  "integrity": "sha512-PYUF+94gDfhy+LsQxM0g3d6Hge4l1pAqOSOiZuHWzMvQEGsbRQ/ck2WioLqrY2ZkHyPgVUXxn+hrkF7D6QUGbA==",
  "peer": true,
  "dependencies": {
    "css-in-js-utils": "^2.0.0"
  }
},
"node_modules/expo/node_modules/react-native-web": {
  "version": "0.13.18",
  "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.13.18.tgz",
  "integrity": "sha512-WR/0ECAmwLQ2+2cL2Ur+0/swXFAtcSM0URoADJmG6D4MnY+wGc91JO8LoOTlgY0USBOY+qG/beRrjFa+RAuOiA==",
  "peer": true,
  "dependencies": {
    "array-find-index": "^1.0.2",
    "create-react-class": "^15.6.2",
    "deep-assign": "^3.0.0",
    "fbjs": "^1.0.0",
    "hyphenate-style-name": "^1.0.3",
    "inline-style-prefixer": "^5.1.0",
    "normalize-css-color": "^1.0.2",
    "prop-types": "^15.6.0",
    "react-timer-mixin": "^0.13.4"
  },
  "peerDependencies": {
    "react": ">=16.5.1",
    "react-dom": ">=16.5.1"
  }
},

...

  "dependencies": {
    "@unimodules/react-native-adapter": {
      "version": "5.7.0",
      "resolved": "https://registry.npmjs.org/@unimodules/react-native-adapter/-/react-native-adapter-5.7.0.tgz",
      "integrity": "sha512-L557/+sc8ZKJVgo1734HF1QNCxrt/fpqdmdNgySJT+kErux/AJNfPq3flsK0fyJduVmniTutYIMyW48cFoPKDA==",
      "requires": {
        "invariant": "^2.2.4",
        "lodash": "^4.5.0"
      }
    },
    "inline-style-prefixer": {
      "version": "5.1.2",
      "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-5.1.2.tgz",
      "integrity": "sha512-PYUF+94gDfhy+LsQxM0g3d6Hge4l1pAqOSOiZuHWzMvQEGsbRQ/ck2WioLqrY2ZkHyPgVUXxn+hrkF7D6QUGbA==",
      "peer": true,
      "requires": {
        "css-in-js-utils": "^2.0.0"
      }
    },
    "react-native-web": {
      "version": "0.13.18",
      "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.13.18.tgz",
      "integrity": "sha512-WR/0ECAmwLQ2+2cL2Ur+0/swXFAtcSM0URoADJmG6D4MnY+wGc91JO8LoOTlgY0USBOY+qG/beRrjFa+RAuOiA==",
      "peer": true,
      "requires": {
        "array-find-index": "^1.0.2",
        "create-react-class": "^15.6.2",
        "deep-assign": "^3.0.0",
        "fbjs": "^1.0.0",
        "hyphenate-style-name": "^1.0.3",
        "inline-style-prefixer": "^5.1.0",
        "normalize-css-color": "^1.0.2",
        "prop-types": "^15.6.0",
        "react-timer-mixin": "^0.13.4"
      }
    }
  }
},

As you see, npm install --force still pins many dependency versions which is stricter.

5 Comments

When I'm using yarn instead of npm install it does not seem to get any errors and it installs the packages without prompting any errors? Is there any problem using yarn instead of npm?
This is specific to npm
I don't quite understand, with --legacy-peer-deps it just doesn't install any of the peer dependencies?
@Plumpie Yes --legacy-peer-deps ignores peer dependencies entirely which may result in an incorrect/broken dependency resolution.
@Dillon so if I ignore peer dependencies entirely, the peer dependencies won't be installed automatically. That must mean that almost every package that has peerDependencies should not work correctly, because it relies on peer packages that are not installed. Am I right?
58

If you’re wondering which one is safer, the answer is --legacy-peer-deps.

Both flags skip the normal peer dependancy checks. But --legacy-peer-deps still enforces other safeguards that --force skips as well. In fact, --legacy-peer-deps simply replicates the functionality of npm before v7, when extra checks were added (hence its name).

Flag Peer-Dependency Checks Other Safeguards Enforced Notes
None Enforced Version resolution, lockfile consistency, integrity Default, catches peer or real-dep mismatches
--legacy-peer-deps Skipped Version resolution, lockfile consistency, integrity Masks only peer-dep warnings—safest bypass hack
--force Skipped None (all errors/warnings ignored) May produce broken, non-reproducible, bloated tree

So when is --force useful? One usecase is invalidating a corrupted cache, as it will force npm to reinstall all packages. Another is if you just really want to get a build working. But I would not recommend using it because it can introduce bugs and make things a nightmare later down the line.

Comments

1

Use this commands npm config set legacy-peer-deps true then do npm install it will install automatically. Form The next time, I will not repeat to you. it's allowed to install without failing.

Comments

1

In my understanding,

The -f or --force argument will force npm to fetch remote resources even if a local copy exists on disk.

npm i -f will force installation of all peer dependencies even if it conflicts with any of your existing package i.e your project might end up having multiple versions of the same package.

Causes npm to completely ignore peerDependencies when building a package tree, as in npm versions 3 through 6.

npm i --legacy-peer-deps ignores peer dependencies completely, so no peer dependencies (conflicting or non-conflicting) will be installed at all i.e. your project will not have multiple versions of a package caused due to peer dependency installation but an already installed version might not be compatible with all the packages in your project. Now, it doesn't guarantee that there will no multiple versions of a package because in project we have transitive dependencies (dependencies of dependencies) too but atleast the chances are less. Also, it can lead to missing packages which would have been there if peer dependencies were not ignored but this problem is solvable.

While using --legacy-peer-deps you are essentially telling npm that some/all of the packages in your project have not updated their peer dependencies so are still relying on legacy peer dependencies, and hence please ignore all peer dependencies and install.

--force : multiple versions of a package to ensure compatibility with all the packages

--legacy-peer-deps : single version of a package but might be incompatible with some of the packages

So IMHO, --legacy-peer-deps is generally safer than --force because possibility of incompatible packages is less than possibility of facing problems due to multiple versions of a package.

By default, conflicting peerDependencies deep in the dependency graph will be resolved using the nearest non-peer dependency specification, even if doing so will result in some packages receiving a peer dependency outside the range set in their package's peerDependencies object.

Also note that now, npm by default itself handles conflicting peer dependencies that are deep in the dependency graphs in a potentially incompatible way, which hints that using --legacy-peer-deps approach is safer than --force.

If you want npm to ensure peer dependency resolutions at all levels use --strict-peer-deps.
Going further read about npm dedupe as well which helps to remove duplicate dependencies in a project.

Comments

-1

If you want a temporary workaround to get rid of npm peer dependency errors or ignore peer-dependencies, use --legacy-peer-deps but to set a different peer dependency tree than the conflicting one, use --force. Find more info here

3 Comments

Which is safer?
It's usually better to address dependency conflicts for long-term stability. @AmanGodara
Sorry I couldn't understand which one is safer from your comment.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.