Как происходит разрешение peer depencies
Одной из лучших особенностей pnpm является то, что в одном проекте конкретная версия пакета всегда будет иметь один набор зависимостей. There is one exception from this rule, though - packages with peer dependencies.
Peer-зависимости разрешаются из зависимостей, установленных выше в графе зависимостей, пока их версии совпадают с версиями родительских пакетов. Это означает, что если у [email protected] есть две peer-зависимости (bar@^1 и baz@^1), то у него может быть разный набор зависимостей в одном проекте.
- foo-parent-1
- [email protected]
- [email protected]
- [email protected]
- foo-parent-2
- [email protected]
- [email protected]
- [email protected]
В приведенном выше примере [email protected] устанавливается для foo-parent-1 и foo-parent-2. Both packages have bar and baz as well, but they depend on different versions of baz. В результате [email protected] имеет два разных набора из зависимостей: один с [email protected], а другой с [email protected]. Чтобы это продолжало нормально работать, pnpm создаёт жёсткие ссылки на пакет [email protected] столько раз, сколько он попадается в разных наборах зависимостей.
Обычно, если пакет не имеет peer dependencies, он связан жёсткой ссылкой с папкой node_modules рядом с символическими ссылками на его зависимости, например:
node_modules
└── .pnpm
├── [email protected]
│ └── node_modules
│ ├── foo
│ ├── qux -> ../../[email protected]/node_modules/qux
│ └── plugh -> ../../[email protected]/node_modules/plugh
├── [email protected]
├── [email protected]
Однако, если пакет foo содержит peer dependencies, для него может быть несколько наборов зависимостей, поэтому мы создаем разные наборы зависимостей для отличающихся peer dependencies:
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]
Мы создаем символические ссылки либо на foo внутри [email protected][email protected][email protected], либо на [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]