原文链接:https://pnpm.io/blog/2020/05/27/flat-node-modules-is-not-the-only-way
Flat node_modules is not the only way
新的 pnpm 用户经常问我关于 pnpm 生成的奇怪的 node_modules 结构。为什么它不是平铺的?它的所有子依赖项去哪了?
我将假设这篇文章的读者已经熟悉为什么 npm 或 yarn 生成的node_modules 结构是扁平化的。如果你不理解为什么 npm 3 开始使用扁平化的 node_modules,你可以寻找以前的历史在Why should we use pnpm?
所以为什么 pnpm 的 node_modules 是不寻常的?让我们创建两个目录,一个执行 npm add express,一个执行 pnpm add express。
执行 npm 的目录长这样:
.bin
accepts
array-flatten
body-parser
bytes
content-disposition
cookie-signature
cookie
debug
depd
destroy
ee-first
encodeurl
escape-html
etag
express
执行 pnpm 的长这样:
.pnpm
.modules.yaml
express
所以所有的依赖去哪了?在 node_modules 里面仅仅有 .pnpm 目录,一个符号链接 express。是的,我们仅仅下载了 express,所以它是你的项目仅能获取的包。
Let’s see what is inside express:
让我们看看符号链接 express 里面都有些什么:
▾ node_modules
▸ .pnpm
▾ express
▸ lib
History.md
index.js
LICENSE
package.json
Readme.md
.modules.yaml
express 没有 node_modules ?那 express 的所有依赖去哪了?
这只不过是一个戏法。express 仅仅是一个符号链接。当 Node.js 解析依赖时,它会使用它们真实的位置,所以它不会保存符号链接。但你可能会问 express 真实的位置在哪。
在这里:
node_modules/.pnpm/express@4.17.1/node_modules/express.
好的,那么现在我们知道了 .pnpm 文件夹的意图了。.pnpm/ 下以扁平的方式存放着所有的包,所以每一个包你都能通过这个路径格式在这个目录下找到。
.pnpm/<name>@<version>/node_modules/<name>
我们叫它虚拟存储目录。
这个扁平化的结构避免了因为嵌套的 node_modules 而导致的长路径问题,但是依旧保持了包之间的隔离。
现在我们看看 express 的真实位置里面:
▾ express
▸ lib
History.md
index.js
LICENSE
package.json
Readme.md
这是一个骗局吗?这里依旧缺少 node_modules。第二个 pnpm 的 node_modules 结构诀窍就是,这个包的依赖和这个包的真实位置在一个相同级别的目录位置下。
所以 express 的依赖没有在 .pnpm/express@4.17.1/node_modules/express/node_modules/
,而是在 .pnpm/express@4.17.1/node_modules/:
▾ node_modules
▾ .pnpm
▸ accepts@1.3.5
▸ array-flatten@1.1.1
...
▾ express@4.16.3
▾ node_modules
▸ accepts
▸ array-flatten
▸ body-parser
▸ content-disposition
...
▸ etag
▾ express
▸ lib
History.md
index.js
LICENSE
package.json
Readme.md
express 的所有依赖都是符号链接,指向在 node_modules/.pnpm 下相应的目录。把 express 的依赖安放在一个级别提升的目录允许避免循环的符号链接。
所以正如你所看到的,即使 pnpm 的 node_modules 结构在一开始开起来不寻常:
- 但它完全兼容 Node.js
- 包和它们的依赖被很好的分组管理
带有 peer dependencies 的包结构会比较复杂一点。但是思想都是相同的:使用符号链接生成一个带有扁平目录的嵌套结构。