Перейти к основному содержимому

Плоская структура node_modules не является единственным способом

· 3 мин. читать

Новые пользователи pnpm часто спрашивают меня о странной структуре node_modules которую создает pnpm. Почему она не плоская? Где находятся все под-зависимости?

Я принял считать, что читатели этой статьи уже хорошо знакомы с плоской по структуре папкой node_modules, создаваемой npm и Yarn. Если вы не понимаете, почему в npm начиная с третьей версии начали использовать плоскую структуру node_modules, вы можете найти некоторую предысторию в Почему мы должны использовать pnpm?.

Так чем же необычна папка node_modules в pnpm? Давайте создадим две директории и запустим npm add express в одном из них, а pnpm add express в другом. Вот верхняя часть того, что вы получаете в первой директории node_modules:

.bin
accepts
array-flatten
body-parser
bytes
content-disposition
cookie-signature
cookie
debug
depd
destroy
ee-first
encodeurl
escape-html
etag
express

Вы можете увидеть всю директорию здесь.

А вот что вы получаете в папке node_modules, созданной pnpm:

.pnpm
.modules.yaml
express

Вы можете проверить это здесь.

Так где же все зависимости? В node_modules есть только одна папка .pnpm и символическая ссылка express. Что ж, мы установили только express, так что это единственный пакет, к которому ваше приложение должно иметь доступ

Узнайте больше о том, почему строгость pnpm — это хорошо здесь

Давайте посмотрим, что внутри express:

▾ node_modules
▸ .pnpm
▾ express
▸ lib
History.md
index.js
LICENSE
package.json
Readme.md
.modules.yaml

Внутри экспресс нет node_modules? Где все зависимости express?

Дело в том, что express — это просто символическая ссылка. When Node.js resolves dependencies, it uses their real locations, so it does not preserve symlinks. But where is the real location of express, you might ask?

Here: node_modules/.pnpm/express@4.17.1/node_modules/express.

OK, so now we know the purpose of the .pnpm/ folder. .pnpm/ stores all the packages in a flat folder structure, so every package can be found in a folder named by this pattern:

.pnpm/<name>@<version>/node_modules/<name>

We call it the virtual store directory.

This flat structure avoids the long path issues that were caused by the nested node_modules created by npm v2 but keeps packages isolated unlike the flat node_modules created by npm v3,4,5,6 or Yarn v1.

Now let's look into the real location of express:

  ▾ express
▸ lib
History.md
index.js
LICENSE
package.json
Readme.md

Is it a scam? It still lacks node_modules! The second trick of pnpm's node_modules structure is that the dependencies of packages are on the same directory level on which the real location of the dependent package. So dependencies of express are not in .pnpm/express@4.17.1/node_modules/express/node_modules/ but in .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

All the dependencies of express are symlinks to appropriate directories in node_modules/.pnpm/. Placing dependencies of express one level up allows avoiding circular symlinks.

So as you can see, even though pnpm's node_modules structure seems unusual at first:

  1. it is completely Node.js compatible
  2. пакеты хорошо сгруппированы с их зависимостями

The structure is a little bit more complex for packages with peer dependencies but the idea is the same: using symlinks to create a nesting with a flat directory structure.