Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] npm 7 makes incorrect suggestion when encountering peer deps issues #2123

Closed
billyjanitsch opened this issue Nov 5, 2020 · 9 comments
Labels
Bug thing that needs fixing Needs Triage needs review for next steps Release 7.x work is associated with a specific npm 7 release

Comments

@billyjanitsch
Copy link

Current Behavior:

When npm install fails due to peer dep incompatibilities, something like this is printed:

npm ERR! Found: webpack@5.4.0
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/react-refresh-webpack-plugin@0.4.3
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 webpack-dev-middleware@3.7.2
npm ERR! node_modules/webpack-dev-server/node_modules/webpack-dev-middleware
npm ERR!   webpack-dev-middleware@"^3.7.2" from webpack-dev-server@3.11.0
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.

Expected Behavior:

I would expect the suggested command to work, or I would expect it not to be suggested.

Steps To Reproduce:

  1. Run npm install webpack@5 webpack-dev-server@3.
  2. Run npm install --force webpack@5 webpack-dev-server@3, as suggested by the output of the first command.
  3. The second command fails with the same error, and the same suggestion to retry with --force.

Environment:

  • OS: macOS 10.15.7
  • Node: 14.14.0
  • npm: 7.0.8
@billyjanitsch billyjanitsch added Bug thing that needs fixing Needs Triage needs review for next steps Release 7.x work is associated with a specific npm 7 release labels Nov 5, 2020
@kapoko
Copy link

kapoko commented Nov 5, 2020

Reverting back to npm 6.14.8 solved this for me for now using npm install -g npm@latest

@ljharb
Copy link
Contributor

ljharb commented Nov 5, 2020

This isn’t a bug; it’s 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 —force is misleading, yes, because this isn’t programmatically fixable.

@billyjanitsch
Copy link
Author

This isn’t a bug; it’s 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 #2119 (comment), 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.

@ljharb
Copy link
Contributor

ljharb commented Nov 5, 2020

I don’t think any option should suppress the warnings, but i agree that if the suggestion won’t work, it shouldn’t be phrased as if it will :-)

@isaacs
Copy link
Contributor

isaacs commented Nov 11, 2020

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"
  }
}

@isaacs
Copy link
Contributor

isaacs commented Nov 11, 2020

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.

@billyjanitsch
Copy link
Author

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

isaacs added a commit to npm/arborist that referenced this issue Nov 12, 2020
Discovered an interesting issue with the following dependency set:

```json
{
  "devDependencies": {
    "webpack-dev-server": "^3.11.0",
    "@pmmmwh/react-refresh-webpack-plugin": "^0.4.3"
  }
}
```

`webpack-dev-server` here has a peerDependency on webpack v4.  But, the
dependencies of `@pmmmwh/react-refresh-webpack-plugin` have peer
dependencies on webpack at `4 || 5`.

So, a resolution _should_ be possible.  Since the
`@pmmmwh/react-refresh-webpack-plugin` package is alphabetically first,
it loads its deps, and ends up placing webpack@5 to satisfy the dep with
the latest and greatest.  Then when `webpack-dev-server` gets to its
eventual peer dep on webpack, it can't replace it, because `webpack@5`
has a dependency on `terser-webpack-plugin@^5.0.3`, which in turn has a
peer dependency on `webpack@5.4.0`.

When we check to see if webpack 5 can be replaced, we find it has a
dependent, and reject the replacement.  But that dependent is only
present as a dependency of the thing being replaced, so should not be
considered.

Second, while the source of the `terser-webpack-plugin` is `webpack`, it
cannot be installed there, because it has peer deps that are also peer
deps of webpack.  So, we _must_ place it in the root node_modules, but
were not attempting the "source" location overrides, because the root is
not the source.  This was a second issue resulting in `ERESOLVE` errors
even when `--force` was applied.

Fixes: #180
Fixes: npm/cli#2123
isaacs added a commit to npm/arborist that referenced this issue Nov 12, 2020
Discovered an interesting issue with the following dependency set:

```json
{
  "devDependencies": {
    "webpack-dev-server": "^3.11.0",
    "@pmmmwh/react-refresh-webpack-plugin": "^0.4.3"
  }
}
```

`webpack-dev-server` here has a peerDependency on webpack v4.  But, the
dependencies of `@pmmmwh/react-refresh-webpack-plugin` have peer
dependencies on webpack at `4 || 5`.

So, a resolution _should_ be possible.  Since the
`@pmmmwh/react-refresh-webpack-plugin` package is alphabetically first,
it loads its deps, and ends up placing webpack@5 to satisfy the dep with
the latest and greatest.  Then when `webpack-dev-server` gets to its
eventual peer dep on webpack, it can't replace it, because `webpack@5`
has a dependency on `terser-webpack-plugin@^5.0.3`, which in turn has a
peer dependency on `webpack@5.4.0`.

When we check to see if webpack 5 can be replaced, we find it has a
dependent, and reject the replacement.  But that dependent is only
present as a dependency of the thing being replaced, so should not be
considered.

Second, while the source of the `terser-webpack-plugin` is `webpack`, it
cannot be installed there, because it has peer deps that are also peer
deps of webpack.  So, we _must_ place it in the root node_modules, but
were not attempting the "source" location overrides, because the root is
not the source.  This was a second issue resulting in `ERESOLVE` errors
even when `--force` was applied, with this dependency set:

```json
{
  "devDependencies": {
    "webpack-dev-server": "^3.11.0",
    "webpack": "^5.0.0"
  }
}
```

Fixes: #180
Fixes: npm/cli#2123
isaacs added a commit to npm/arborist that referenced this issue Nov 13, 2020
Discovered an interesting issue with the following dependency set:

```json
{
  "devDependencies": {
    "webpack-dev-server": "^3.11.0",
    "@pmmmwh/react-refresh-webpack-plugin": "^0.4.3"
  }
}
```

`webpack-dev-server` here has a peerDependency on webpack v4.  But, the
dependencies of `@pmmmwh/react-refresh-webpack-plugin` have peer
dependencies on webpack at `4 || 5`.

So, a resolution _should_ be possible.  Since the
`@pmmmwh/react-refresh-webpack-plugin` package is alphabetically first,
it loads its deps, and ends up placing webpack@5 to satisfy the dep with
the latest and greatest.  Then when `webpack-dev-server` gets to its
eventual peer dep on webpack, it can't replace it, because `webpack@5`
has a dependency on `terser-webpack-plugin@^5.0.3`, which in turn has a
peer dependency on `webpack@5.4.0`.

When we check to see if webpack 5 can be replaced, we find it has a
dependent, and reject the replacement.  But that dependent is only
present as a dependency of the thing being replaced, so should not be
considered.

Second, while the source of the `terser-webpack-plugin` is `webpack`, it
cannot be installed there, because it has peer deps that are also peer
deps of webpack.  So, we _must_ place it in the root node_modules, but
were not attempting the "source" location overrides, because the root is
not the source.  This was a second issue resulting in `ERESOLVE` errors
even when `--force` was applied, with this dependency set:

```json
{
  "devDependencies": {
    "webpack-dev-server": "^3.11.0",
    "webpack": "^5.0.0"
  }
}
```

Fixes: #180
Fixes: npm/cli#2123

PR-URL: https://github.com/pull/182
Credit: @isaacs
Close: #182
Reviewed-by: @darcyclarke
isaacs added a commit that referenced this issue Nov 13, 2020
@darcyclarke darcyclarke added this to the OSS - Sprint 19 milestone Nov 16, 2020
@bigdaddy3211234
Copy link

Reverting back to npm 6.14.8 solved this for me for now using npm install -g npm@latest

Super bro.it worked👍

@kawcher-habib
Copy link

solved the problem using this command npm install -g npm@latest

Thank you kapoko

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug thing that needs fixing Needs Triage needs review for next steps Release 7.x work is associated with a specific npm 7 release
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants