Skip to main content
Version: 9.x

如何解决对等依赖

pnpm 的最佳功能之一是,在一个项目中,包的特定版本始终具有一组依赖。不过,这一规则有一个例外 - packages with 对等依赖

¥One of the best features of pnpm is that in one project, a specific version of a package will always have one set of dependencies. There is one exception from this rule, though - packages with peer dependencies.

对等依赖是从依赖图中较高位置安装的依赖解析的,因为它们与其父项共享相同的版本。这意味着,如果 foo@1.0.0 有两个对等依赖(bar@^1baz@^1),那么它可能在同一项目中具有多个不同的依赖集。

¥Peer dependencies are resolved from dependencies installed higher in the dependency graph, since they share the same version as their parent. That means that if foo@1.0.0 has two peers (bar@^1 and baz@^1) then it might have multiple different sets of dependencies in the same project.

- foo-parent-1
- bar@1.0.0
- baz@1.0.0
- foo@1.0.0
- foo-parent-2
- bar@1.0.0
- baz@1.1.0
- foo@1.0.0

在上例中,为 foo-parent-1foo-parent-2 安装了 foo@1.0.0。这两个软件包也都有 barbaz,但它们依赖于不同版本的 baz。因此,foo@1.0.0 有两组不同的依赖:一个带有 baz@1.0.0,另一个带有 baz@1.1.0。为了支持这些用例,pnpm 必须硬链接 foo@1.0.0 次数,次数与不同依赖集的次数相同。

¥In the example above, foo@1.0.0 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, foo@1.0.0 has two different sets of dependencies: one with baz@1.0.0 and the other one with baz@1.1.0. To support these use cases, pnpm has to hard link foo@1.0.0 as many times as there are different dependency sets.

通常,如果一个包没有对等依赖,它会硬链接到其依赖符号链接旁边的 node_modules 文件夹,如下所示:

¥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
├── foo@1.0.0
│ └── node_modules
│ ├── foo
│ ├── qux -> ../../qux@1.0.0/node_modules/qux
│ └── plugh -> ../../plugh@1.0.0/node_modules/plugh
├── qux@1.0.0
├── plugh@1.0.0

但是,如果 foo 具有对等依赖,则它可能有多个依赖集,因此我们为不同的对等依赖解析创建不同的依赖集:

¥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
├── foo@1.0.0_bar@1.0.0+baz@1.0.0
│ └── node_modules
│ ├── foo
│ ├── bar -> ../../bar@1.0.0/node_modules/bar
│ ├── baz -> ../../baz@1.0.0/node_modules/baz
│ ├── qux -> ../../qux@1.0.0/node_modules/qux
│ └── plugh -> ../../plugh@1.0.0/node_modules/plugh
├── foo@1.0.0_bar@1.0.0+baz@1.1.0
│ └── node_modules
│ ├── foo
│ ├── bar -> ../../bar@1.0.0/node_modules/bar
│ ├── baz -> ../../baz@1.1.0/node_modules/baz
│ ├── qux -> ../../qux@1.0.0/node_modules/qux
│ └── plugh -> ../../plugh@1.0.0/node_modules/plugh
├── bar@1.0.0
├── baz@1.0.0
├── baz@1.1.0
├── qux@1.0.0
├── plugh@1.0.0

我们创建指向 foo@1.0.0_bar@1.0.0+baz@1.0.0 内的 foofoo@1.0.0_bar@1.0.0+baz@1.1.0 内的符号链接。因此,Node.js 模块解析器将找到正确的对等依赖。

¥We create symlinks either to the foo that is inside foo@1.0.0_bar@1.0.0+baz@1.0.0 or to the one in foo@1.0.0_bar@1.0.0+baz@1.1.0. As a consequence, the Node.js module resolver will find the correct peers.

如果某个包没有对等依赖,但与图中解析较高的对等项有依赖,则该传递包可能会以不同的依赖集出现在项目中。例如,包 a@1.0.0 具有单个依赖 b@1.0.0b@1.0.0 具有对等依赖 c@^1a@1.0.0 永远不会解析 b@1.0.0 的对等依赖,因此它也依赖于 b@1.0.0 的对等依赖。

¥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 a@1.0.0 with a single dependency b@1.0.0. b@1.0.0 has a peer dependency c@^1. a@1.0.0 will never resolve the peers of b@1.0.0, so it becomes dependent from the peers of b@1.0.0 as well.

该结构在 node_modules 中的外观如下。在此示例中,a@1.0.0 将需要在项目的 node_modules 中出现两次 - 用 c@1.0.0 解决一次,用 c@1.1.0 解决一次。

¥Here's how that structure will look in node_modules. In this example, a@1.0.0 will need to appear twice in the project's node_modules - resolved once with c@1.0.0 and again with c@1.1.0.

node_modules
└── .pnpm
├── a@1.0.0_c@1.0.0
│ └── node_modules
│ ├── a
│ └── b -> ../../b@1.0.0_c@1.0.0/node_modules/b
├── a@1.0.0_c@1.1.0
│ └── node_modules
│ ├── a
│ └── b -> ../../b@1.0.0_c@1.1.0/node_modules/b
├── b@1.0.0_c@1.0.0
│ └── node_modules
│ ├── b
│ └── c -> ../../c@1.0.0/node_modules/c
├── b@1.0.0_c@1.1.0
│ └── node_modules
│ ├── b
│ └── c -> ../../c@1.1.0/node_modules/c
├── c@1.0.0
├── c@1.1.0