pnpm: Problem with peer dependencies when using `pnpm link`

When a package that has peer dependencies is linked to some destination, the node_modules of that package are included in the symbolic link. This causes the dependencies of the package to come from another source than the destination’s node_modules/.pnpm folder. This becomes a problem with peer dependencies, because they end up not pointing to the same exact file. Even when the version on both side is an exact match, this can lead to problems when exact references (e.g.: singleton) are needed.

The simple example is for a React package, where React is marked as a peer dependency. When a consumer package links the first library, there will be a missing in the imported files, leading to countless problem on runtime.

pnpm version:

8.6.3

Code to reproduce the issue:

I’ve created a reproduction repository that includes a README.md file with all the instructions needed in order to get a better feel of the issue.

https://github.com/kevinpastor/pnpm-link-peers-issue

Expected behavior:

When linking a package that has peer dependencies to some destination, we should expect that the peer dependencies are respected.

Actual behavior:

The peer dependencies of the package behave more like dependencies. The peer dependencies are consumed from the package’s original location and not from the destination’s node_modules.

Additional information:

About this issue

  • Original URL
  • State: open
  • Created a year ago
  • Reactions: 5
  • Comments: 16 (6 by maintainers)

Most upvoted comments

I know, we need better docs. The most upvoted issue is actually related to linking: https://github.com/pnpm/pnpm/issues/4640

Dependencies added via the file: protocol are already hard linked. So if you edit a file, it will be changed in the consumer. But if you build your package, then the files in dist are probably replaced, so it doesn’t matter that they were hard links.

You cannot solve this with pnpm link. What you can do is use a file: protocol. If it is inside a workspace, you could also use injected dependencies.

In your case, if you need this for local development and the projects are in separate directories, probably the file: protocol would be the way. You can put it into an override like so:

{
  "name": "consumer",
  "version": "1.0.0",
  "dependencies": {
    "dep": "1.0.0"
  },
  "pnpm": {
    "overrides": {
      "dep": "file:../dep"
    }
  }
}