Перейти до основного змісту

"Плоскі" node_modules — не єдиний спосіб

· 3 хв читання

Нові користувачі pnpm часто запитують мене про дивну структуру node_modules, яку створює pnpm. Чому вона не плоска? Куди поділись усі залежності (sub-dependency)?

Я припускаю, що читачі статті вже знайомі з плоскими node_modules, створеними npm і Yarn. Якщо ви не розумієте, чому npm 3 повинен був почати використовувати flat node_modules у версії 3, ви можете знайти деякі передісторії в Чому ми повинні використовувати pnpm?.

Отже, чому pnpm node_modules незвичний? Створімо дві директорії та запустимо 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

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, створеними npm v2, але зберігає пакети ізольованими на відміну від плоских node_modules, створених npm v3,4,5,6 або Yarn v1.

Тепер давайте подивимося на справжнє місце розташування 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 спочатку виглядає незвичною:

  1. повністю сумісна з Node.js
  2. пакети добре згрупованні з їх залежностями

Структура трохи складніша для пакунків з одноранговими залежностями, але ідея та сама: використання символьних посилань для створення вкладеності з плоскою структурою каталогів.