Перевод проекта Node с npm на pnpm без нарушения работы команды
npm против pnpm: структура диска, фантомные зависимости и файлы блокировки; затем миграция команды Node.js с использованием pnpm import, опции only-allow и поля packageManager с фиксированным значением.
Источники: Switching from npm to pnpm
Менеджеры пакетов определяют, как будут сохраняться зависимости на диске, какой файл блокировки следует коммитить и насколько строгими будут процедуры импорта в случае возникновения проблем. Для многих проектов на Node по умолчанию остается npm. Команды, которые поддерживают несколько репозиториев, постоянно сталкиваются с потоком рекомендаций по безопасности, требующих обновления версий. Dependabot может автоматически выполнять некоторые из этих обновлений; другие требуют ручного вмешательства из-за конфликтов при использовании косвенных исправлений или потому, что единственным решением является полная замена пакета.
Примерно в то же время GitHub изменил работу Dependabot так, что PR по обновлению версии, не связанные с безопасностью, ожидают три дня перед автоматическим открытием. Это изменение политики в основном не связано с выбором менеджера пакетов, но оно показывает, насколько шумными стали автоматические обновления.
Тем временем инциденты в цепочках поставок продолжают попадать в новости. Поврежденные или уязвимые пакеты находятся внутри приложений, которые загружают наборы иконок, инструменты аутентификации и утилиты токенов, поэтому зона поражения расширяется с каждым промежуточным звеном. Сообщения об угрозах безопасности редко поступают по одному. Ноутбук, использующий несколько сервисов, может получить уведомления как о проблемах с парсерами, так и с интерфейсными комплектами и промежуточными модулями аутентификации в тот же неделю. Автоматические боты помогают, когда проблема связана с техническими аспектами, но они бессильны, когда новая версия изменяет API или когда две зависимости не согласовывают действия при работе с общим ресурсом. В такие периоды вполне уместно задаться вопросом, может ли сам инсталлятор снизить риски — отклоняя ненужные зависимости, храня контент на диске так, чтобы при аудите видна была только одна копия версии, и замедляя публикации совершенно новых версий до тех пор, пока экосистема не оценит ситуацию.
Одно из часто высказываемых предложений — заменить npm на pnpm: схожие повседневные команды, более строгая модель установки и, как правило, меньшая нагрузка на диск. В следующих разделах приводится сравнение этих двух инструментов без указания абсолютного победителя — только достаточно информации, чтобы решить, стоит ли переход за счет связанных с миграцией затрат.
Общая информация о npm / pnpm
Kраткие основные сведения об каждом инструменте:
npm
- Является стандартным менеджером пакетов в Node.js.
- Отвечает за установку зависимостей и выполнение скриптов из
package.json. - К числу повседневных команд относятся
npm install(установка из файла lock/manifest),npm install <name>(добавление зависимости),npm run <script>иnpm uninstall <name>. - Создает файл
package-lock.json. - Хранит установленные пакеты в каталоге
node_modules.
npm также поставляет npx, который запускает пакет без его добавления в проект или глобальной установки. Инструменты для создания структуры проекта, такие как npx create-vite@latest project-name, временно загружаются, выполняются и не изменяют файл package.json. Различие с npm install преднамеренно: одноразовое выполнение против постоянной зависимости.
pnpm
- Управляющий пакетами от сторонних разработчиков с аналогичными функциями — управление зависимостями и скриптами — реализованный по-другому на диске.
- Знакомые команды имеют схожую нумерацию:
pnpm install,pnpm add <name>,pnpm run <script>,pnpm remove <name>. - Обычно занимает меньше места на диске и быстрее выполняет установки, храня каждую версию только один раз.
- Ограничивает импорты тем, что указано в файле
package.json.
pnpm-lock.yaml вместо package-lock.json.Одноразовый запускатель, аналогичный npx, — это pnpm dlx; он полезен, например, при создании структуры проекта с использованием create-vite в последней версии. Многие генераторы также поддерживают pnpm create. Для бинарных файлов, уже находящихся в node_modules, в качестве локального запускателя используется pnpm exec.
Плюсы и минусы pnpm
Компромиссы важны не меньше, чем слоганы:
- npm копирует пакеты в
node_modulesкаждого проекта, что приводит к дублированию данных между репозиториями; pnpm хранит одну копию каждой версии с адресной идентификацией и создает ссылки из проектов. - Такая структура обычно сокращает использование диска и ускоряет повторные установки.
- Благодаря более простой системе разрешения в npm код может требовать пакета, который никогда не объявлялся; pnpm отклоняет такие незаявленные импорты, что сокращает возможности для случайных атак.
- Задержка в принятии совершенно новых версий у pnpm снижает риск мгновенного использования скомпрометированных версий пакетов.
- Со временем npm также получил соответствующие функции, но некоторые из них остаются факультативными, а не стандартными.
- Для pnpm требуется отдельный шаг установки; npm поставляется вместе с Node.
- Старые плагины, предполагающие структуру npm, могут потребовать корректировок из-за более строгой системы связей в pnpm.
- У команд все еще преобладает привычка к npm, поэтому адаптация может занять некоторое время.
Различия в структуре диска проявляются быстро на серверах CI, где плохо хранится кэш node_modules, а также на рабочих станциях разработчиков, где установлено десятки приложений. Хранилище с адресным управлением контентом позволяет второму и третьему проектам, нуждающимся в lodash@4.x, создавать лишь ссылки вместо полной копии структуры проекта. Более строгая видимость зависимостей также влияет на отладку: когда импорт не удается из-за того, что пакет никогда не был объявлен, ошибка проявляется сразу во время установки или проверки типов, а не как неожиданная ошибка во время выполнения программы, возникшая лишь потому, что другой пакет случайно его подключил.
Переход на pnpm
Минимальная глобальная установка выглядит следующим образом:
// Installation command (once)
npm install -g pnpm
// Usage
pnpm install
pnpm add [package name]
pnpm run [script name]
pnpm remove [package name]
После первой успешной установки в проекте ожидайте появления файла pnpm-lock.yaml рядом с (а со временем вместо) файла package-lock.json.
Существуют и другие способы установки, включая автономные скрипты; они перечислены в документации по установке pnpm.
Миграция существующего проекта npm обычно подразумевает удаление файлов node_modules и package-lock.json, а затем выполнение команды pnpm install. В большинстве случаев всё работает без проблем; однако неудобные нативные дополнения или устаревшие резолверы могут потребовать временных решений.
Чтобы задать версии на основе существующего файла lockfile, выполните команду pnpm import. Чтобы устранить остатки данных npm:
// For macOS/Linux/Git Bash:
rm -rf node_modules package-lock.json
// For Windows PowerShell:
rm -Recurse -Force node_modules, package-lock.json
Чтобы предотвратить случайную установку через npm install в будущем, добавьте фильтр preinstall:
"scripts": {
"preinstall": "npx only-allow pnpm"
}
Затем добавьте этот инструмент в качестве зависимости для разработки:
pnpm add -D only-allow
Для фиксации конкретной версии CLI для сотрудников используется поле packageManager (замените строку с версией по мере необходимости):
{
"packageManager": "pnpm@11.22.0"
}
Этот фикс гарантирует совместимость CI-систем и ноутбуков с одной версией CLI; не забывайте умышленно обновлять его при апгрейде командой.
Проверка и автономное обновление выполняются следующим образом:
pnpm --version
pnpm self-update
Если автономное обновление работает некорректно, переустановите пакет глобально с помощью команды npm install -g pnpm и перезапустите оболочку.
После успешной замены вы должны увидеть:
- Отсутствие файла
package-lock.json. - Наличие файла
pnpm-lock.yaml. - Файл
package.jsonостается неизменным, за исключением добавленных фиксов или скриптов. - Папка
node_modulesпереписывается с использованием инструмента линковки pnpm (или создаётся заново при выполнении командыpnpm install). - Команды повседневного использования отличаются в основном названием используемого инструмента в начале.
- Одноразовые запуски через команду
pnpm dlxвместо ранее использовавшейсяnpx.
Официальная документация по-прежнему является источником информации для решения крайних случаев; приведенные выше заметки достаточны для оценки процесса миграции и проведения стандартной замены.
Рассматривайте первый мигрированный репозиторий как репетицию: запустите набор тестов, убедитесь, что сборки в Docker по-прежнему работают, и проверьте, что все нативные дополнения компилируются с использованием нового линкера. Как только этот процесс станет рутинным, использование одинаковых настроек packageManager и механизма защиты only-allow во всех связанных сервисах поможет сохранить единообразие во всей системе. Если какой-то устаревший инструмент действительно не может работать с структурой pnpm, изолируйте его, вместо того чтобы отказываться от более строгих стандартов в остальных частях системы.