12k
All articles

Как устроен pnpm, переписанный на Rust

Узнайте о переписывании pnpm 12 на Rust, результатах тестов, совместимости с pnpm 11 и рисках обновления, которые могут повлиять на CI.

OpenReplay Team
OpenReplay Team
Как устроен pnpm, переписанный на Rust

pnpm 12 заменяет кодовую базу pnpm на TypeScript нативной реализацией на Rust. При этом сохраняются команды, флаги, настройки и формат lock-файла pnpm 11, поэтому большинство проектов можно обновить без правки конфигурации.

Если вы используете pnpm в монорепозитории с загруженным парком CI-раннеров, вы наверняка видели в заголовках «на 90% быстрее» и задавались вопросом, в чём подвох. Новая мажорная версия инструмента, который формирует ваш lock-файл, заслуживает более пристального внимания, чем график бенчмарков.

В этой статье разберём, почему пакетный менеджер на JavaScript вообще работает медленно, что изменил движок на Rust и чего это стоило, кто и как получил каждую цифру, а также какие изменения в поведении могут затронуть ваш пайплайн.

Ключевые выводы

  • Стабильная версия pnpm 12.0.0 вышла 26 августа 2026 года. Она сохраняет команды, флаги, настройки и формат lock-файла pnpm 11, за исключением короткого списка задокументированных отличий.
  • На официальной странице бенчмарков pnpm (pnpm 11.27.1 против 12.7.0) «тёплая» повторная установка ускоряется с 563 мс до 18 мс, а чистая установка — лишь с 8,4 с до 4,4 с, поскольку при холодной установке основное время уходит на передачу данных по сети и распаковку.
  • По замерам Vercel, установка в её workspace из 1670 пакетов на pnpm 12 занимает на 64,4–90,5% меньше времени, чем на pnpm 10.28. Однако запуск через Corepack без кэша стал на 11,1% медленнее из-за большего размера нативного дистрибутива.
  • Изменение, которое с наибольшей вероятностью сломает CI, — удаление pnpm install --resolution-only. Вместо него используйте pnpm peers check.

Исходное ограничение: тот же pnpm, другой движок

pnpm 12 создавался так, чтобы обновление не ощущалось как миграция. Именно эта цель заявлена в анонсе релиза pnpm 12.0, а руководство по совместимости подтверждает: за исключением короткого списка отличий, pnpm 12 сохраняет команды, флаги, настройки и формат lock-файла pnpm 11. Кроме того, в pnpm 12 осталось контентно-адресуемое хранилище, благодаря которому проекты разделяют файлы пакетов, а не копируют их. По данным InfoQ, структура node_modules также не изменилась.

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

Почему пакетный менеджер на JavaScript работает медленно?

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

Установка проходит примерно через следующие этапы:

  1. Получение метаданных пакетов из реестра.
  2. Разрешение графа зависимостей.
  3. Загрузка tarball-архивов.
  4. Распаковка их в хранилище.
  5. Линковка пакетов в node_modules.

Первая издержка фиксированная. Прежде чем pnpm 11 мог приступить к реальной работе, должен был запуститься Node.js. pnpm 12 публикуется в виде нативных бинарных файлов — по одному пакету @pnpm/exe.<platform>-<arch> на каждую платформу. Документация по self-update подтверждает, что Node.js предварительно не запускается, так что эта издержка на старте исчезла.

Вторая издержка растёт вместе с объёмом работы. Этапы 4 и 5 включают распаковку tarball-архивов и создание жёстких ссылок на тысячи файлов, и каждая такая операция проходит через среду выполнения JavaScript.

Этот принцип применим к любому инструменту: устранение фиксированных накладных расходов даёт наибольший эффект, когда прочей работы почти не остаётся. Если установке практически нечего делать, запуск занимает бо́льшую часть общего времени выполнения. Если же нужно скачать сотни мегабайт, время запуска почти незаметно.

Насколько быстрее pnpm 12?

Прирост скорости в pnpm 12 реален, но неравномерен: «тёплая» повторная установка ускоряется с 563 мс до 18 мс, а чистая — лишь с 8,4 с до 4,4 с. Данные взяты из двух независимых наборов измерений с разными базовыми версиями. Не сводите их к одной цифре.

СценарийДоpnpm 12Кто измерялБазовая версия
«Тёплая» повторная установка563 мс18 мсСтраница бенчмарков pnpm (pnpm 12.7.0)pnpm 11.27.1
Чистая установка8,43 с4,42 сСтраница бенчмарков pnpm (pnpm 12.7.0)pnpm 11.27.1
Workspace из 1670 пакетов, шесть сценариев (медиана)н/дна 64,4–90,5% меньше времениVercel (pnpm 12.0.0)pnpm 10.28.0
Запуск через Corepack без кэшан/дна 11,1% медленнееVercel (pnpm 12.0.0)pnpm 10.28.0
Запуск через Corepack с кэшемн/дна 74,7% быстрееVercel (pnpm 12.0.0)pnpm 10.28.0

Данные pnpm относятся к проекту alotta-files на странице бенчмарков pnpm и сравнивают pnpm 11.27.1 с pnpm 12.7.0. Тесты на этой странице регулярно перезапускаются и всегда отражают новейшую версию каждого инструмента, поэтому цифры будут меняться. Разрыв между двумя строками pnpm — наглядная иллюстрация предыдущего раздела. «Тёплая» установка ускоряется примерно в 30 раз — скорее всего, потому, что фиксированные издержки вроде запуска составляли значительную долю её времени выполнения. Чистая установка ускоряется лишь примерно в 1,9 раза, поскольку основное время уходит на передачу данных по сети и распаковку tarball-архивов — независимо от того, на каком языке написан движок.

Собственные замеры Vercel показывают ту же закономерность. При уже существующей node_modules, «тёплом» хранилище и отключённых скриптах время установки сократилось с 1,476 с до 142 мс. Полностью холодная установка с включёнными lifecycle-скриптами ускорилась с 9,850 с до 3,472 с. Каждое медианное значение получено по 20 запускам каждой версии на одной Linux-машине в workspace Turborepo из 21 проекта.

У нативной сборки pnpm 12 есть своя цена. По данным Vercel, размер загрузки pnpm 12 через Corepack составляет 47,3 МБ против 17,5 МБ у pnpm 10.28.0, и именно с этим увеличением компания связывает замедление запуска без кэша. CI-раннеры, не кэширующие Corepack, скорее всего, будут платить эту цену в каждом задании.

Детерминированная обработка циклических зависимостей, появившаяся вместе с новым движком, — отдельное улучшение. В руководстве по совместимости pnpm снижение потребления памяти примерно на 25% и ускорение разрешения peer-зависимостей в 2–3 раза в workspace с большим количеством циклов связываются именно с этим изменением, а не с движком на Rust как таковым.

Публичная страница бенчмарков pnpm теперь сравнивает pnpm 12 только с npm и pnpm 11. Как сообщает InfoQ, pnpm исключил Bun и Yarn из сравнения после того, как проблемы с конфигурацией бенчмарков сделали их результаты ненадёжными.

Почему формат lock-файла pnpm должен был остаться прежним?

Формат lock-файла в pnpm 12 нужно было сохранить, потому что его несовместимость расколола бы команду посреди обновления. Если бы pnpm 12 записывал файл в новом формате, все ноутбуки и CI-раннеры пришлось бы переключить в один день. Иначе в одном репозитории конкурировали бы два «диалекта» lock-файла, и каждый pull request содержал бы шум от той версии, которая запускалась последней.

Сохранение формата призвано позволить команде обновляться постепенно. Это не значит, что содержимое файла никогда не изменится. Формат и содержимое — разные вещи:

  • Существующие lock-файлы продолжают работать. В руководстве отмечается, что pnpm не трогает существующие записи, пока что-либо не заставит его разрешить их заново.
  • Повторное разрешение может изменить записи. pnpm 12 записывает зависимости из GitHub, GitLab и Bitbucket с HTTPS-адресом, а не SSH. Кроме того, он всегда разрывает циклы зависимостей в одном и том же месте, благодаря чему lock-файлы в workspace с большим количеством циклов становятся меньше.

Просматривайте diff после первой установки с повторным разрешением зависимостей на pnpm 12 как отдельный коммит.

Что ломается при обновлении до pnpm 12?

Если после обновления до pnpm 12 ломается задание в CI, вероятнее всего, причина в скрипте, который всё ещё вызывает pnpm install --resolution-only. v12 отклоняет этот флаг. Его функцию — вывод информации о peer-зависимостях — теперь выполняет pnpm peers check:

# pnpm 11
pnpm install --resolution-only

# pnpm 12
pnpm peers check

Кроме того, v12 отклоняет pnpm install --frozen-lockfile false. Чтобы отключить режим frozen-lockfile, используйте --no-frozen-lockfile, а чтобы включить — просто --frozen-lockfile.

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

ИзменениеКого затрагиваетЧто делать
Git-зависимости с GitHub/GitLab/Bitbucket разрешаются через HTTPSПриватные репозитории с доступом по SSHНастроить перезапись Git URL на машине
О неизвестных ключах в pnpm-workspace.yaml выводится сообщение, а если проект фиксирует версию pnpm, которой удовлетворяет запущенный pnpm, команда завершается ошибкой ERR_PNPM_UNRECOGNIZED_WORKSPACE_SETTINGSКонфигурации с опечаткамиИсправить или удалить ключ
Команды, изменяющие глобальную установку, завершаются ошибкой ERR_PNPM_SUDO_NOT_SUPPORTED при запуске через sudosudo pnpm self-update и аналогичныеЗапускать их без sudo
При включённом engineStrict несовместимый engine в обычных dependencies приводит к ошибке установки, даже если есть запись в optionalDependenciesПроекты, использующие engineStrictБыть готовыми к тому, что установки, ранее выдававшие предупреждение, будут завершаться ошибкой
В Linux packageImportMethod: auto пробует жёсткие ссылки раньше reflinkПользователи LinuxКак правило, ничего
Глобальные node, deno или bun следуют версии, зафиксированной в проектеМашины с глобально установленными средами выполненияОжидать зафиксированную версию

Release notes v12.0.0 объясняют, почему важна проверка workspace. В pnpm 11 при опечатке в настройке вроде minimumReleaseAge pnpm молча пропускал ключ, и правило, которое он должен был задавать, так и не применялось:

# pnpm-workspace.yaml
packages:
  - "apps/*"
minimumReleseAge:   # typo: pnpm 12 reports this key

Всего в руководстве по совместимости перечислено восемь отличий. Шесть из них меняют результат, а два отклоняют синтаксис командной строки, который принимал pnpm 11: --resolution-only и --frozen-lockfile false. Приведённая выше таблица охватывает не все отличия. В неё не вошло, как pnpm 12 обрабатывает имена пакетных менеджеров, например yarn, в pnpm add, а строки о ключах workspace и sudo взяты из release notes, а не из руководства. Перед обновлением прочитайте руководство целиком.

Стоит ли обновляться до pnpm 12?

Как правило, да, и обновление проходит без сюрпризов — такова была цель разработчиков. Начиная с pnpm 11.10 и новее выполните:

pnpm self-update

В проекте, где версия pnpm зафиксирована через packageManager, self-update просто обновляет версию в этом поле, а не устанавливает pnpm глобально. Затем pnpm загрузит новую версию при следующем запуске любой команды. Закоммитьте изменение, чтобы CI использовал ту же версию:

{
  "packageManager": "pnpm@12.8.1"
}

Используйте последний релиз 12.x. Если ваш процесс установки опирается на менее распространённые возможности, такие как pnpm deploy или определённые режимы линковщика, сначала опробуйте обновление в отдельной ветке. Командам, которые всё ещё используют npm, стоит прочитать статью о том, имеет ли смысл переходить с npm на pnpm, прежде чем браться за оба изменения одновременно.

Заключение

pnpm 12 сохранил всё, чем вы пользуетесь каждый день, — команды, формат lock-файла и модель хранилища, — и перестроил стоящий за ними движок. Переписывание окупилось, потому что версию на JavaScript ограничивали два фактора: запуск Node.js при каждом вызове и файловый ввод-вывод, проходящий через единственную среду выполнения. Перед обновлением поищите в конфигурации CI --resolution-only и --frozen-lockfile false, проверьте наличие приватных Git-зависимостей с доступом по SSH и закоммитьте первый lock-файл с повторно разрешёнными зависимостями отдельно, чтобы можно было просмотреть diff.

Часто задаваемые вопросы

Нужен ли Node.js для работы pnpm 12?

Нет. После установки pnpm 12 работает как нативная программа, поэтому Node.js не требуется. Автономному скрипту установки Node.js также не нужен — даже на этапе установки. Единственное исключение — установка pnpm 12 через npm: в этом случае установщику требуется Node.js 22.13 или новее. Если для вашей платформы нет готового бинарного файла pnpm 12, документация pnpm рекомендует использовать pnpm 11 на JavaScript.

Как продолжить использовать SSH для приватных Git-зависимостей в pnpm 12?

pnpm 12 загружает зависимости с GitHub, GitLab и Bitbucket по HTTPS-адресу соответствующего хостинга. Чтобы продолжить использовать SSH, настройте перезапись Git URL, например командой git config --global url.'git@github.com:'.insteadOf https://github.com/. pnpm под капотом вызывает git, поэтому это правило распространяется на все Git-команды, которые выполняет pnpm. Хосты, которые pnpm не распознаёт, и URL с учётными данными остаются ровно в том виде, в каком вы их указали, включая SSH.

Почему pnpm 12 завершается ошибкой из-за нераспознанной настройки в pnpm-workspace.yaml?

Ошибка возникает, только если в проекте зафиксирована версия pnpm и запущенный pnpm ей соответствует. В этом случае pnpm считает неизвестный ключ ошибкой и останавливается с ERR_PNPM_UNRECOGNIZED_WORKSPACE_SETTINGS. Если подходящей фиксации версии нет, выводится предупреждение, и команда продолжает работу. Если ключ похож на опечатку, pnpm подскажет, какую настройку вы, вероятно, имели в виду. Команды pnpm config по-прежнему работают с файлом, содержащим некорректный ключ, поэтому с их помощью можно найти и исправить ошибку.

Какие команды pnpm 12 завершаются ошибкой при запуске через sudo?

При запуске через sudo команды pnpm setup, pnpm self-update и все команды, изменяющие глобальную установку, например pnpm add --global, останавливаются с ошибкой ERR_PNPM_SUDO_NOT_SUPPORTED. Предыдущие версии в такой ситуации молча записывали данные в домашний каталог root. Глобальные пакеты и настройки хранятся в вашем собственном домашнем каталоге, поэтому ни одной из этих команд права root не нужны. Команды, работающие только на чтение, например pnpm bin --global, по-прежнему выполняются через sudo.

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.