Перейти к основному содержимому
Версия: 9.x

Как происходит разрешение peer depencies

Одной из лучших особенностей pnpm является то, что в одном проекте конкретная версия пакета всегда будет иметь один набор зависимостей. There is one exception from this rule, though - packages with peer dependencies.

Peer-зависимости разрешаются из зависимостей, установленных выше в графе зависимостей, пока их версии совпадают с версиями родительских пакетов. That means that if [email protected] has two peers (bar@^1 and baz@^1) then it might have multiple different sets of dependencies in the same project.

In the example above, [email protected] is installed for foo-parent-1 and foo-parent-2. Both packages have bar and baz as well, but they depend on different versions of baz. As a result, [email protected] has two different sets of dependencies: one with [email protected] and the other one with [email protected]. To support these use cases, pnpm has to hard link [email protected] as many times as there are different dependency sets.

Normally, if a package does not have peer dependencies, it is hard linked to a node_modules folder next to symlinks of its dependencies, like so:

node_modules
└── .pnpm
├── [email protected]
│ └── node_modules
│ ├── foo
│ ├── qux -> ../../[email protected]/node_modules/qux
│ └── plugh -> ../../[email protected]/node_modules/plugh
├── [email protected]
├── [email protected]

However, if foo has peer dependencies, there may be multiple sets of dependencies for it, so we create different sets for different peer dependency resolutions:

node_modules
└── .pnpm
├── [email protected][email protected][email protected]
│ └── node_modules
│ ├── foo
│ ├── bar -> ../../[email protected]/node_modules/bar
│ ├── baz -> ../../[email protected]/node_modules/baz
│ ├── qux -> ../../[email protected]/node_modules/qux
│ └── plugh -> ../../[email protected]/node_modules/plugh
├── [email protected][email protected][email protected]
│ └── node_modules
│ ├── foo
│ ├── bar -> ../../[email protected]/node_modules/bar
│ ├── baz -> ../../[email protected]/node_modules/baz
│ ├── qux -> ../../[email protected]/node_modules/qux
│ └── plugh -> ../../[email protected]/node_modules/plugh
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]

We create symlinks either to the foo that is inside [email protected][email protected][email protected] or to the one in [email protected][email protected][email protected]. Как следствие, загрузчик модулей в Node.js найдет правильные peer dependencies.

If a package has no peer dependencies but has dependencies with peers that are resolved higher in the graph, then that transitive package can appear in the project with different sets of dependencies. For instance, there's package [email protected] with a single dependency [email protected]. [email protected] has a peer dependency c@^1. [email protected] will never resolve the peers of [email protected], so it becomes dependent from the peers of [email protected] as well.

Here's how that structure will look in node_modules. In this example, [email protected] will need to appear twice in the project's node_modules - resolved once with [email protected] and again with [email protected].

node_modules
└── .pnpm
├── [email protected][email protected]
│ └── node_modules
│ ├── a
│ └── b -> ../../[email protected][email protected]/node_modules/b
├── [email protected][email protected]
│ └── node_modules
│ ├── a
│ └── b -> ../../[email protected][email protected]/node_modules/b
├── [email protected][email protected]
│ └── node_modules
│ ├── b
│ └── c -> ../../[email protected]/node_modules/c
├── [email protected][email protected]
│ └── node_modules
│ ├── b
│ └── c -> ../../[email protected]/node_modules/c
├── [email protected]
├── [email protected]