Главная / Статьи

Перевод проекта Node с npm на pnpm без нарушения работы команды

npm против pnpm: структура диска, фантомные зависимости и файлы блокировки; затем миграция команды Node.js с использованием pnpm import, опции only-allow и поля packageManager с фиксированным значением.

Издатель: REACTAPP.TOP6 мин чтения1317 слов

Источники: 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, изолируйте его, вместо того чтобы отказываться от более строгих стандартов в остальных частях системы.