Skip to main content
Version: 11.x

对等体如何被解析

pnpm 的一个最佳特性是,在一个项目中,某个特定版本的包始终会有一套固定的依赖。不过,这条规则有一个例外——具有【同行依赖】的包。

🌐 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@1.0.0 是为 foo-parent-1foo-parent-2 安装的。两个软件包也都有 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 有 peer 依赖,它可能会有多组依赖,因此我们会为不同的 peer 依赖解析创建不同的依赖集合:

🌐 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 内的 foo,要么指向 foo@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 的同伴依赖。

以下是在 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