When npm install
fails due to peer dep incompatibilities, something like this is printed:
npm ERR! Found: [email protected]
npm ERR! node_modules/webpack
npm ERR! dev webpack@"^5.4.0" from the root project
npm ERR! peer webpack@">=4.43.0 <6.0.0" from @pmmmwh/[email protected]
npm ERR! node_modules/@pmmmwh/react-refresh-webpack-plugin
npm ERR! dev @pmmmwh/react-refresh-webpack-plugin@"^0.4.3" from the root project
npm ERR! 7 more (babel-loader, file-loader, html-webpack-plugin, ...)
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer webpack@"^4.0.0" from [email protected]
npm ERR! node_modules/webpack-dev-server/node_modules/webpack-dev-middleware
npm ERR! webpack-dev-middleware@"^3.7.2" from [email protected]
npm ERR! node_modules/webpack-dev-server
npm ERR! dev webpack-dev-server@"^3.11.0" from the root project
npm ERR! 1 more (@pmmmwh/react-refresh-webpack-plugin)
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.
The suggestion to "retry this command with --force" doesn't appear to work. npm install --force
prints exactly the same output, with only the addition of:
npm WARN using --force Recommended protections disabled.
I would expect the suggested command to work, or I would expect it not to be suggested.
npm install webpack@5 webpack-dev-server@3
.npm install --force webpack@5 webpack-dev-server@3
, as suggested by the output of the first command.--force
.Reverting back to npm 6.14.8 solved this for me for now using npm install -g npm@latest
This isn鈥檛 a bug; it鈥檚 a correct warning because your dependency graph is invalid.
you have webpack 5, but v3.7.2 of webpack-dev-middleware requires webpack 4. Your choices are to downgrade to webpack 4, upgrade to a version (of available) of webpack-dev-middleware that supports webpack 5, or remove webpack-dev-middleware entirely.
The message telling you to use 鈥攆orce is misleading, yes, because this isn鈥檛 programmatically fixable.
This isn鈥檛 a bug; it鈥檚 a correct warning because your dependency graph is invalid.
@ljharb I think you misread the issue. I'm not claiming that either the failed installation or the warning message is a bug. See https://github.com/npm/cli/issues/2119#issuecomment-722086100, from which I borrowed to use as an example of an install that contains a peer dependency incompatibility.
This issue is entirely about the --force
suggestion that npm makes, which as you said is somewhere between misleading and incorrect. The error states: "retry this command with --force, or --legacy-peer-deps to accept an incorrect (and potentially broken) dependency resolution." There's a subject ambiguity here about whether --force
will accept the incorrect resolution or whether that only applies to --legacy-peer-deps
. But either way that you read this, it suggests that rerunning the command with --force
will produce a different output. This isn't true.
It's not exactly clear to me what the --force
modifier actually does on install, but IMO it's not unreasonable to guess that it might mean "ignore peer dependency incompatibilities wrt exit behavior", mirroring the npm@6 behavior. (I know that you hate the thought of this. 馃檪) If that's the intention, then there's a bug in the implementation. Or, more likely, this is _not_ the intention in which case it seems that npm shouldn't be making this suggestion at all.
I don鈥檛 think any option should suppress the warnings, but i agree that if the suggestion won鈥檛 work, it shouldn鈥檛 be phrased as if it will :-)
Well, yeah, this is a bug. It should either not tell you to use --force
or (ideally) --force
should provide an override that makes it produce a mostly (but not entirely) correct package tree.
Rather than roll back to npm v6, I'd suggest using --legacy-peer-deps
or putting legacy-peer-deps = true
in your project-level .npmrc
file. There are a lot of other advantages to using v7.
Here's the minimal reproduction package.json file that I'm using:
{
"devDependencies": {
"webpack-dev-server": "^3.11.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.4.3"
}
}
IMO it's not unreasonable to guess that it might mean "ignore peer dependency incompatibilities wrt exit behavior", mirroring the npm@6 behavior.
The npm@6 behavior is "ignore peerDependencies entirely". That's what --legacy-peer-deps
does.
--force
is either slightly better or much worse in this sort of case. It attempts to infer a reasonable best guess at what version _should_ be placed there, based on prioritizing non-peer dependencies, and failing that, accepting invalid peer deps and moving on.
You've found a case where we don't do the right thing (or at least, the _intended_ thing, fraught though it may be) in the --force
case.
That bit where it says "recommended protections disabled", this is one of those "recommended protections". npm v7 tries to not ever give you a tree that violates the stated dependency contracts of any packages.
--force is either slightly better or much worse in this sort of case. It attempts to infer a reasonable best guess at what version should be placed there, based on prioritizing non-peer dependencies, and failing that, accepting invalid peer deps and moving on.
Ah, that's what I had originally expected it to do! I like this behavior, because --legacy-peer-deps
doesn't work if you've come to rely on npm@7's automatic installation of peer deps for other peer deps, unrelated to the one(s) that are incompatible.
For example, if you have something like this, where @babel/cli
peer-depends on @babel/core
:
{
"scripts": {
"build": "babel index.js"
},
"devDependencies": {
"@babel/cli": "^7.12.0",
"webpack-dev-server": "^3.11.0",
"webpack": "^5.0.0"
}
}
Using --legacy-peer-deps
would ignore the webpack incompatibility, but it would fail to install @babel/core
, so npm run build
would fail. Whereas, if I understand the intention of --force
correctly, it would install @babel/core
while also "ignoring" the webpack incompatibility.
(I understand that this is all danger zone territory, but it does help to understand the intention of --force
in the rare case where it's needed. For example: when you're waiting on a third-party package to update a peer dep and you want to test whether it happens to work locally in the meantime. It's nice to have a method to force npm to complete the install rather than bailing from populating node_modules entirely.)