"Пласкі" node_modules — не єдиний спосіб
Нові користувачі pnpm часто запитують мене про дивну структуру node_modules
, яку створює pnpm. Чому вона не пласка? Куди поділись усі залежності (sub-dependency)?
Я припускаю, що читачі статті вже знайомі з пласкими
node_modules
, створеними npm і Yarn. Якщо ви не розумієте, чому npm 3 повинен був почати використовувати пласкі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
спочатку виглядає незвичною:
- вона є повністю сумісною з Node.js
- пакунки добре згрупованні разом з їх залежностями
Структура трохи складніша для пакунків з одноранговими залежностями, але ідея та сама: використання символьних посилань для створення вкладеності з пласкою структурою тек.