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] Locally-installed package should satisfy peer dependency #2199

Closed
btmills opened this issue Nov 19, 2020 · 4 comments · Fixed by npm/arborist#249
Closed

[BUG] Locally-installed package should satisfy peer dependency #2199

btmills opened this issue Nov 19, 2020 · 4 comments · Fixed by npm/arborist#249
Assignees
Labels
Bug thing that needs fixing Priority 0 will get attention right away Release 7.x work is associated with a specific npm 7 release

Comments

@btmills
Copy link

btmills commented Nov 19, 2020

Current Behavior:

npm install in the eslint/eslint repository fails when it tries to resolve a transitive peer dependency on eslint@>=5.0.0 and finds eslint@undefined.

We want to dogfood eslint in development, so we specify it in our devDependencies as "eslint": "file:.". We also depend on a third-party plugin, eslint-plugin-eslint-plugin, which lists eslint@>=5.0.0 in its peerDependencies.

Running npm install reports ERESOLVE unable to resolve dependency tree having found eslint@undefined, which fails the eslint@>=5.0.0 requirement. (Full output and eresolve-report.txt included in the repro steps.)

eslint@7.13.0`s package.json snippet
```json
{
	"name": "eslint",
	"version": "7.13.0",
	"devDependencies": {
		"eslint": "file:.",
		"eslint-plugin-eslint-plugin": "^2.2.1"
	}
}
```
eslint-plugin-eslint-plugin@2.3.0`s package.json snippet
```json
{
	"name": "eslint-plugin-eslint-plugin",
	"version": "2.3.0",
	"peerDependencies": {
		"eslint": ">=5.0.0"
	}
}
```

Expected Behavior:

Ideally, instead of finding eslint@undefined, eslint-plugin-eslint-plugin's eslint@>=5.0.0 peer dependency is satisifed by eslint@7.13.0, which is already "installed" from file:.. Install continues on as normal. (It won't succeed yet because I'm still working on upgrading another dependency to a newer version that lists eslint@^7.0.0 as a compatible peer dependency, but eslint-plugin-eslint-plugin at least should be happy.)

If that's not an option, our current solution so that we can test Node 15 in CI is to install with the --legacy-peer-deps flag. I also found that replacing file: dependencies with workspaces installs successfuly. At some point in the future when we can require at least npm v7, we could switch from --legacy-peer-deps to workspaces.

Steps To Reproduce:

$ node --version
v15.2.1
$ npm --version
7.0.10
$ git clone https://github.com/eslint/eslint.git
$ cd eslint
# Latest version as of writing in case a future commit no longer repros
$ git checkout v7.13.0
$ npm install
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: eslint@7.13.0
npm ERR! Found: eslint@undefined
npm ERR! node_modules/eslint
npm ERR!   dev eslint@"file:." from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer eslint@">=5.0.0" from eslint-plugin-eslint-plugin@2.3.0
npm ERR! node_modules/eslint-plugin-eslint-plugin
npm ERR!   dev eslint-plugin-eslint-plugin@"^2.2.1" 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 ~/.npm/eresolve-report.txt for a full report.

npm ERR! A complete log of this run can be found in:
npm ERR!     ~/.npm/_logs/2020-11-19T05_00_04_565Z-debug.log
~/.npm/eresolve-report.txt
# npm resolution error report

2020-11-19T05:00:04.563Z

While resolving: eslint@7.13.0
Found: eslint@undefined
node_modules/eslint
  dev eslint@"file:." from the root project

Could not resolve dependency:
peer eslint@">=5.0.0" from eslint-plugin-eslint-plugin@2.3.0
node_modules/eslint-plugin-eslint-plugin
  dev eslint-plugin-eslint-plugin@"^2.2.1" from the root project

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

Raw JSON explanation object:

{
  "code": "ERESOLVE",
  "current": {
    "name": "eslint",
    "errors": [
      {}
    ],
    "package": {},
    "whileInstalling": {
      "name": "eslint",
      "version": "7.13.0",
      "path": "~/code/eslint/eslint"
    },
    "location": "node_modules/eslint",
    "dependents": [
      {
        "type": "dev",
        "name": "eslint",
        "spec": "file:.",
        "from": {
          "location": "~/code/eslint/eslint"
        }
      }
    ]
  },
  "edge": {
    "type": "peer",
    "name": "eslint",
    "spec": ">=5.0.0",
    "error": "INVALID",
    "from": {
      "name": "eslint-plugin-eslint-plugin",
      "version": "2.3.0",
      "whileInstalling": {
        "name": "eslint",
        "version": "7.13.0",
        "path": "~/code/eslint/eslint"
      },
      "location": "node_modules/eslint-plugin-eslint-plugin",
      "dependents": [
        {
          "type": "dev",
          "name": "eslint-plugin-eslint-plugin",
          "spec": "^2.2.1",
          "from": {
            "location": "~/code/eslint/eslint"
          }
        }
      ]
    }
  },
  "peerConflict": null,
  "strictPeerDeps": false,
  "force": false
}

Environment:

  • OS: macOS 10.15.7
  • Node: 15.2.1
  • npm: 7.0.10
@btmills btmills 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 19, 2020
@RomanHotsiy
Copy link

I get a similar warning on the latest npm version:

  • npm --version
    7.3.0
  • node --version
    v15.5.1
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR! 
npm ERR! Found: type-fest@0.8.1
npm ERR! node_modules/type-fest
npm ERR!   type-fest@"^0.8.1" from boxen@4.2.0
npm ERR!   node_modules/boxen
npm ERR!     boxen@"^4.1.0" from @storybook/core@5.3.21
npm ERR!     node_modules/@storybook/core
npm ERR!       @storybook/core@"5.3.21" from @storybook/react@5.3.21
npm ERR!       node_modules/@storybook/react
npm ERR!         dev @storybook/react@"^5.2.0" from the root project
npm ERR!     boxen@"^4.2.0" from gatsby-telemetry@1.8.0
npm ERR!     node_modules/gatsby-telemetry
npm ERR!       gatsby-telemetry@"^1.8.0" from gatsby@2.30.1
npm ERR!       node_modules/gatsby
npm ERR!         gatsby@"2.30.1" from the root project
npm ERR!         17 more (@redocly/gatsby-plugin-favicon, ...)
npm ERR!       2 more (gatsby-cli, gatsby-plugin-page-creator)
npm ERR!   type-fest@"^0.8.0" from hasha@5.2.2
npm ERR!   node_modules/hasha
npm ERR!     hasha@"^5.2.0" from gatsby@2.30.1
npm ERR!     node_modules/gatsby
npm ERR!       gatsby@"2.30.1" from the root project
npm ERR!       17 more (@redocly/gatsby-plugin-favicon, ...)
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! peerOptional type-fest@"^0.13.1" from @pmmmwh/react-refresh-webpack-plugin@0.4.3
npm ERR! node_modules/@pmmmwh/react-refresh-webpack-plugin
npm ERR!   @pmmmwh/react-refresh-webpack-plugin@"^0.4.1" from gatsby@2.30.1
npm ERR!   node_modules/gatsby
npm ERR!     gatsby@"2.30.1" from the root project
npm ERR!     17 more (@redocly/gatsby-plugin-favicon, ...)
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.

@ljharb
Copy link
Collaborator

ljharb commented Jan 9, 2021

@RomanHotsiy try v7.4.0?

@RomanHotsiy
Copy link

@ljharb same.

@rhansen
Copy link

rhansen commented Feb 10, 2021

Shell script to reproduce this problem with the latest npm v7.x:

tmp=$(mktemp -d)
echo "tmp dir: $tmp"

mkdir "$tmp/npm"
cd "$tmp/npm"
npm i npm@^7.0.0
npm() { "$tmp"/npm/node_modules/.bin/npm "$@"; }

# Create a dummy ep_etherpad-lite package. Later, a real package
# will be installed that has a peer dependency on this package.
mkdir "$tmp/ep_etherpad-lite"
cd "$tmp/ep_etherpad-lite"
npm init -y
npm version major # Bump version to 2.0.0 (needed below).

# Create dummy main package that depends on the above
# ep_etherpad-lite package via 'file:'.
mkdir "$tmp/main"
cd "$tmp/main"
npm init -y
npm i ep_etherpad-lite@file:../ep_etherpad-lite

# Install ep_readonly_guest in main. ep_readonly_guest has a peer dependency on
# ep_etherpad-lite@>=1.8.7, which is why the version was bumped to 2.0.0 above.
# The installation of ep_readonly_guest fails because npm can't determine if the
# installed ep_etherpad-lite package satisfies ep_readonly_guests's peer
# dependency on ep_etherpad-lite.
cd "$tmp/main"
npm i ep_readonly_guest@1.0.28

rm -rf "$tmp"

rhansen added a commit to ether/etherpad-lite that referenced this issue Feb 16, 2021
rhansen added a commit to ether/etherpad-lite that referenced this issue Feb 16, 2021
The `--no-save` is so that npm v6 does not get confused when it is run
during Etherpad startup to discover plugins. The `--legacy-peer-deps`
flag works around npm/cli#2199.
rhansen added a commit to ether/etherpad-lite that referenced this issue Feb 16, 2021
The `--no-save` is so that npm v6 does not get confused when it is run
during Etherpad startup to discover plugins. The `--legacy-peer-deps`
flag works around npm/cli#2199.
rhansen added a commit to ether/etherpad-lite that referenced this issue Feb 17, 2021
The `--no-save` is so that npm v6 does not get confused when it is run
during Etherpad startup to discover plugins. The `--legacy-peer-deps`
flag works around npm/cli#2199.
rhansen added a commit to ether/etherpad-lite that referenced this issue Feb 17, 2021
The `--no-save` is so that npm v6 does not get confused when it is run
during Etherpad startup to discover plugins. The `--legacy-peer-deps`
flag works around npm/cli#2199.
rhansen added a commit to ether/etherpad-lite that referenced this issue Feb 18, 2021
See: npm/cli#2199

This flag is unknown to npm v6, but npm v6 silently ignores unknown
flags.
@darcyclarke darcyclarke added Bug thing that needs fixing Priority 0 will get attention right away and removed Priority 1 high priority issue Bug thing that needs fixing Enhancement new feature or improvement labels Mar 1, 2021
@darcyclarke darcyclarke removed this from the OSS - Sprint 25 milestone Mar 8, 2021
@darcyclarke darcyclarke added this to the OSS - Sprint 26 milestone Mar 8, 2021
isaacs added a commit to npm/arborist that referenced this issue Mar 9, 2021
Previously, we were not including link targets in the virtual trees
where peer dependency sets are calculated.  Additionally, we were still
using the path `/virtual-root` for the virtual node, even though this is
no longer load-bearing.  (And, as of the recent change to the Node
printable output, no longer necessary or particularly helpful for
debugging.)

As a result, a link dependency from the root node like `file:../../foo`
would get resolved against `/virtual-root`, resulting in `/foo`, which
of course does not match any Node in the virtual tree.  The outcome was
an ERESOVLVE error where the `current` Node is shown as having no name
or version (because it is an unsatisfied Link).

The solution is two-part.

First, the path of the virtual tree root now matches the path of the
Node that it is sourced from.

Second, any Link children of the source node have their targets mirrored
in the virtual tree, resulting in them being matched appropriately.

The result is that a Link dependency can now properly satisfy a
peerDependency.  Test shows an example of using a Link to a local Node
as a workaround for a peerSet that otherwise would not be resolveable.

This can of course be abused to get around valid peerDep contracts, but
if they user takes it on themselves to use a local fork of a dependency,
we should respect that in buildIdealTree as we do elsewhere.

Fix: npm/cli#2199
isaacs added a commit to npm/arborist that referenced this issue Mar 9, 2021
Previously, we were not including link targets in the virtual trees
where peer dependency sets are calculated.  Additionally, we were still
using the path `/virtual-root` for the virtual node, even though this is
no longer load-bearing.  (And, as of the recent change to the Node
printable output, no longer necessary or particularly helpful for
debugging.)

As a result, a link dependency from the root node like `file:../../foo`
would get resolved against `/virtual-root`, resulting in `/foo`, which
of course does not match any Node in the virtual tree.  The outcome was
an ERESOLVE error where the `current` Node is shown as having no name
or version (because it is an unsatisfied Link).

The solution is two-part.

First, the path of the virtual tree root now matches the path of the
Node that it is sourced from.

Second, any Link children of the source node have their targets mirrored
in the virtual tree, resulting in them being matched appropriately.

The result is that a Link dependency can now properly satisfy a
peerDependency.  Test shows an example of using a Link to a local Node
as a workaround for a peerSet that otherwise would not be resolveable.

This can of course be abused to get around valid peerDep contracts, but
if they user takes it on themselves to use a local fork of a dependency,
we should respect that in buildIdealTree as we do elsewhere.

Fix: npm/cli#2199
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug thing that needs fixing Priority 0 will get attention right away 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.

6 participants