Рядом с Git · Про Git тут ни слова

npm и pnpm: как зависимости попадают на диск

Почему из пяти строк вырастает полторы тысячи папок, зачем нужен файл-замок и в чём разница между двумя менеджерами, которые делают одно и то же.

~12 минутничего не требуетвводить в терминал ничего не надо

Зачем это в твоей миссии

К Git отношения не имеет, но пробел тот же: слова мелькают каждый день, а картинки за ними нет. Агент говорит «ставлю зависимости» — полезно понимать, что при этом происходит с диском, почему нельзя разрешать ему удалять файл-замок и почему свежая папка проекта не запускается.

Начнём с цифры

В твоём проекте GildiyaChat в списке нужного перечислено 5 пакетов. А на диске в папке зависимостей лежит 1432 пакета общим весом 992 мегабайта.

Пять и полторы тысячи. Разберёмся, откуда берётся разница — а заодно станет понятно всё остальное.

Три файла, и всё встанет на места

package.json

Рецепт. Пишете его вы с агентом. Здесь сказано, что проекту нужно: «библиотека для веб-сервера, версия пятая или новее». Формулировки нарочно с люфтом.

файл-замок

Протокол. Пишет менеджер, а не ты. Что именно встало: версия до последней цифры, адрес, контрольная сумма. Люфта тут нет вообще.

node_modules

Результат. Сами файлы библиотек. В снимках Git не хранится: полностью восстанавливается из первых двух.

Файл-замок называется по-разному в зависимости от менеджера: package-lock.json у npm, pnpm-lock.yaml у pnpm. Смысл один.

Откуда полторы тысячи из пяти

Проверил на живом примере. Поставил один пакет — популярный express. На диск приехало 65 пакетов.

Причина простая: у каждой библиотеки есть свои зависимости, у тех — свои. Ты просишь одну коробку, приезжает фура. Это называется транзитивными зависимостями, и это нормальный порядок вещей, а не чья-то ошибка.

Поэтому пять строк в рецепте и полторы тысячи папок на диске — не противоречие. Просто ты видишь верхушку, а менеджер разворачивает всё дерево целиком.

Зачем нужен файл-замок

Вот здесь прячется настоящая практическая польза, ради которой стоит дочитать.

В рецепте написано «версия пятая или новее» — это удобно, но означает, что в разные дни установка даст разный результат. Сегодня встанет 5.2, через месяц выйдет 5.3 и встанет она.

А теперь представь: у тебя на маке стоит 5.2, потому что ставил в июле. На сервере разворачивают проект в августе — и туда приезжает 5.3, где что-то поменялось. Приложение падает только на сервере. Ты смотришь на один и тот же код и не понимаешь, в чём дело.

Файл-замок это отсекает. В нём записано «ровно 5.2.1, вот с такой контрольной суммой», и на любой машине встанет то же самое.

Отсюда правило, которое стоит держать в голове

Когда установка не идёт, у агента есть соблазн удалить файл-замок — после этого она обычно проходит. Сиюминутная проблема решена, воспроизводимость сломана. Правильный ответ агенту: «не удаляй, покажи саму ошибку».

Теперь про два менеджера

npm и pnpm ставят одни и те же пакеты из одного и того же места. Отличаются они только тем, как раскладывают их на диске.

npm — каждому проекту личная копия:

    проект-1/node_modules/express/…   ← файлы
    проект-2/node_modules/express/…   ← те же файлы ещё раз
    проект-3/node_modules/express/…   ← и ещё раз


pnpm — один склад, а в проектах ссылки на него:

    склад/express@5.2.1/…             ← файлы, один раз
    проект-1/node_modules/express  ──┐
    проект-2/node_modules/express  ──┼──▶ туда же
    проект-3/node_modules/express  ──┘

Замерил на одном и том же пакете:

npmpnpm
один проект3,8 МБ4,3 МБ
два проекта с тем же пакетом7,5 МБ5,1 МБ

Обрати внимание на первую строку: на одном проекте pnpm даже чуть тяжелее. Никакого чуда нет — служебная структура склада тоже занимает место. Выигрыш начинается со второго проекта: у npm вес удвоился, у pnpm вырос на восемьсот килобайт. На двадцати проектах разница уже в гигабайтах.

Как это работает: второе имя, а не ярлык

Механизм называется жёсткой ссылкой, и его стоит отличать от обычного ярлыка:

Это видно напрямую. У файла есть счётчик: сколько имён на него указывает. В проверке выше файл, поставленный через npm, имел одно имя — личная копия. Тот же файл в раскладке pnpm — семнадцать имён, потому что он же используется в других проектах на этой машине.

Отсюда и главное свойство: экономия без потери независимости. Проекты могут держать разные версии одной библиотеки и не мешать друг другу — просто одинаковые куски не дублируются.

При чём тут worktrees

Прямо при том. В уроке про worktrees была засада: свежее рабочее дерево приезжает без зависимостей, и их приходится ставить заново.

Если проект на pnpm, эта установка почти бесплатна: файлы уже на складе, создаются только ссылки. Если на npm — каждое новое дерево честно копирует свой гигабайт.

Одно условие

Склад должен лежать на том же диске, что и проекты — жёсткие ссылки не умеют пересекать границу между дисками. Если склад окажется на другом, pnpm молча начнёт копировать, и весь смысл пропадёт. По умолчанию он сам заводит по складу на каждый диск, так что обычно об этом можно не думать.

Как сказать агенту

Обычная установка

Поставь зависимости тем менеджером, чей файл-замок лежит в проекте.

Одна фраза снимает целый класс проблем. Агент, пришедший в чужой проект, иногда ставит привычным ему менеджером — и рядом появляется второй файл-замок.

Установка не идёт

Не удаляй файл-замок. Покажи мне саму ошибку целиком.

Удаление замка выглядит как решение и является сокрытием проблемы. Ошибка почти всегда говорит что-то конкретное про версию или про сеть.

Новый проект

Заводи проект на pnpm.

Дальше это будет экономить и место, и время на каждом новом worktree. Переводить существующие проекты при этом не нужно — там уже всё установлено и работает.

Проверь себя

Первоисточники к этому уроку

pnpm → Motivation — объяснение от самих авторов, зачем понадобился ещё один менеджер. Там же про склад и жёсткие ссылки. Про склад на другом диске — в разделе вопросов.

Документация npm про package-lock.json — официальное описание файла-замка и того, зачем он существует.

Если что-то не сошлось — назови конкретное место. Например: «не понял, почему на одном проекте pnpm тяжелее» или «не понял про семнадцать имён». Разберу отдельно.