1. pnpm 的 node_modules 结构
pnpm 是一个包管理工具,它通过符号链接(symlinks)来构建 node_modules 的结构。这种结构与传统的 npm 或 yarn 的方式不同,它更节省空间,也更严格地控制依赖的访问。
2. 硬链接和符号链接
- 硬链接:
node_modules中的每个包的文件实际上是存储在内容可寻址存储(content-addressable storage)中的硬链接。例如,foo@1.0.0和bar@1.0.0的文件会被硬链接到一个存储位置。 - 符号链接:pnpm 使用符号链接来构建依赖关系。符号链接是一个指向实际文件的快捷方式。
3. 示例结构
假设你安装了 foo@1.0.0,它依赖于 bar@1.0.0,pnpm 会创建以下结构:
node_modules
└── .pnpm
├── bar@1.0.0
│ └── node_modules
│ └── bar
│ ├── index.js -> <store>/001
│ └── package.json -> <store>/002
└── foo@1.0.0
└── node_modules
└── foo
├── index.js -> <store>/003
└── package.json -> <store>/004
node_modules/.pnpm是一个隐藏目录,用来存储每个包的实际文件。foo和bar的文件通过硬链接指向存储中的文件。
4. 构建依赖关系
接下来,pnpm 会通过符号链接来构建依赖关系:
node_modules
└── .pnpm
├── bar@1.0.0
│ └── node_modules
│ └── bar -> <store>
└── foo@1.0.0
└── node_modules
├── foo -> <store>
└── bar -> ../../bar@1.0.0/node_modules/bar
foo的node_modules文件夹中有一个符号链接bar,指向bar@1.0.0的实际位置。- 这样,
foo可以通过require('bar')正常访问bar。
5. 处理直接依赖
如果 foo 是项目的直接依赖,pnpm 会将 foo 符号链接到根目录的 node_modules 文件夹:
node_modules
├── foo -> ./.pnpm/foo@1.0.0/node_modules/foo
└── .pnpm
├── bar@1.0.0
│ └── node_modules
│ └── bar -> <store>
└── foo@1.0.0
└── node_modules
├── foo -> <store>
└── bar -> ../../bar@1.0.0/node_modules/bar
6. 添加更多依赖
假设 bar 和 foo 都依赖于 qar@2.0.0,pnpm 会继续使用符号链接来构建依赖关系:
node_modules
├── foo -> ./.pnpm/foo@1.0.0/node_modules/foo
└── .pnpm
├── bar@1.0.0
│ └── node_modules
│ ├── bar -> <store>
│ └── qar -> ../../qar@2.0.0/node_modules/qar
├── foo@1.0.0
│ └── node_modules
│ ├── foo -> <store>
│ ├── bar -> ../../bar@1.0.0/node_modules/bar
│ └── qar -> ../../qar@2.0.0/node_modules/qar
└── qar@2.0.0
└── node_modules
└── qar -> <store>