对等体如何被解析
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@^1 和 baz@^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-1 和 foo-parent-2 安装的。两个软件包也都有 bar 和 baz,但它们依赖于不同版本的 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.0。b@1.0.0 有一个同伴依赖 c@^1。a@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