Як вирішуються конфлікти прямих залежностей
Однією з найкращих особливостей pnpm є те, що в одному проєкті певна версія пакунка завжди матиме один набір залежностей. З цього правила є один виняток — пакунки з прямими залежностями.
Прямі залежності вирішуються із залежностей, встановлених вище на графі залежностей, оскільки вони мають ту саму версію, що й їхні батьки. Це означає, що якщо foo@1.0.0
має дві прямі залежності (bar@^1
і baz@^1
), то він може мати декілька різних наборів залежностей в одному проєкті.
- foo-parent-1
- bar@1.0.0
- baz@1.0.0
- foo@1.0.0
- foo-parent-2
- bar@1.0.0
- baz@1.1.0
- foo@1.0.0
У наведеному вище прикладі foo@1.0.0
встановлено для foo-parent-1
та foo-parent-2
. Обидва пакунки також мають bar
та baz
, але вони залежать від різних версій baz
. Як наслідок, foo@1.0.0
має два різних набори залежностей: один з baz@1.0.0
, а інший з baz@1.1.0
. Для підтримки цих варіантів використання pnpm має створити жорстке посилання foo@1.0.0
стільки разів, скільки існує різних наборів залежностей.
Зазвичай, якщо пакунок не має прямих залежностей, він привʼязується до теки node_modules
жорстким посиланням поруч із символічними посиланнями його залежностей, наприклад, так:
node_modules
└── .pnpm
├── foo@1.0.0
│ └── node_modules
│ ├── foo
│ ├── qux -> ../../qux@1.0.0/node_modules/qux
│ └── plugh -> ../../plugh@1.0.0/node_modules/plugh
├── qux@1.0.0
├── plugh@1.0.0
Однак, якщо foo
має прямі залежності, для нього може існувати декілька наборів залежностей, тому ми створюємо різні набори для різних резолюцій прямих залежностей:
node_modules
└── .pnpm
├── foo@1.0.0_bar@1.0.0+baz@1.0.0
│ └── node_modules
│ ├── foo
│ ├── bar -> ../../bar@1.0.0/node_modules/bar
│ ├── baz -> ../../baz@1.0.0/node_modules/baz
│ ├── qux -> ../../qux@1.0.0/node_modules/qux
│ └── plugh -> ../../plugh@1.0.0/node_modules/plugh
├── foo@1.0.0_bar@1.0.0+baz@1.1.0
│ └── node_modules
│ ├── foo
│ ├── bar -> ../../bar@1.0.0/node_modules/bar
│ ├── baz -> ../../baz@1.1.0/node_modules/baz
│ ├── qux -> ../../qux@1.0.0/node_modules/qux
│ └── plugh -> ../../plugh@1.0.0/node_modules/plugh
├── bar@1.0.0
├── baz@1.0.0
├── baz@1.1.0
├── qux@1.0.0
├── plugh@1.0.0
Ми створюємо символічні посилання або на foo
, що знаходиться всередині foo@1.0.0_bar@1.0.0+baz@1.0.0
, або на той, що знаходиться в foo@1.0.0_bar@1.0.0+baz@1.1.0
. Як наслідок, модуль Node.js знайде правильні прямі залежності.
Якщо пакунок не має залежностей від однорангових пакунків, але має залежності в ід однорангових пакунків, що розташовані вище на графі, то цей транзитивний пакунок може зʼявитися у проєкті з різними наборами залежностей. Наприклад, є пакунок a@1.0.0
з єдиною залежністю b@1.0.0
. b@1.0.0
має пряму залежність c@^1
. a@1.0.0
ніколи не розпізнає прямих залежностей b@1.0.0
, тому він також стає залежним від залежностей b@1.0.0
.
Ось як ця структура буде виглядати у node_modules
. В цьому прикладі a@1.0.0
має зʼявитися двічі у node_modules
проєкту — один раз за допомогою c@1.0.0
, а другий — за допомогою c@1.1.0
.
node_modules
└── .pnpm
├── a@1.0.0_c@1.0.0
│ └── node_modules
│ ├── a
│ └─ ─ b -> ../../b@1.0.0_c@1.0.0/node_modules/b
├── a@1.0.0_c@1.1.0
│ └── node_modules
│ ├── a
│ └── b -> ../../b@1.0.0_c@1.1.0/node_modules/b
├── b@1.0.0_c@1.0.0
│ └── node_modules
│ ├── b
│ └── c -> ../../c@1.0.0/node_modules/c
├── b@1.0.0_c@1.1.0
│ └── node_modules
│ ├── b
│ └── c -> ../../c@1.1.0/node_modules/c
├── c@1.0.0
├── c@1.1.0