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

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

· 3 хв читання

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

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

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