Текст и данные · Инструкция
pnpm catalogs: как задать общие версии зависимостей в monorepo
pnpm catalogs позволяют убрать десятки одинаковых версий из package.
Короткий ответ
pnpm catalogs позволяют вынести общие версии зависимостей monorepo в `pnpm-workspace.yaml`, а в `package.json` ссылаться на них через `catalog:` или именованный `catalog:<name>`. Это уменьшает расхождения версий между workspace-пакетами и делает обновление одной зависимости централизованным.
Зачем pnpm catalogs нужны в monorepo
В большом workspace одна и та же библиотека часто встречается в десятках `package.json`. Если каждый пакет хранит собственный диапазон версии, обновление React, TypeScript, ESLint или внутреннего SDK превращается в серию однотипных правок, а после пары месяцев появляются случайные расхождения. pnpm catalogs решают именно эту задачу: версия задаётся один раз на уровне workspace, а манифесты используют логическое имя каталога. Официальная документация pnpm поддерживает как каталог по умолчанию `catalog`, так и именованные `catalogs`. Важное отличие от dedupe: каталог управляет тем, что записано как желаемая версия в манифестах, а не пытается после установки схлопнуть уже разрешённое дерево зависимостей.
Важно: Catalog — это источник версии для манифестов, а не замена lockfile. `pnpm-lock.yaml` всё равно фиксирует разрешённый результат установки.
Как настроить общий каталог версий
- Откройте `pnpm-workspace.yaml` в корне monorepo. Именно здесь pnpm ожидает определения catalog и named catalogs.
- Добавьте раздел `catalog` и перечислите общие зависимости с нужными диапазонами версий. Например: `react: ^19.0.0`, `typescript: ^5.9.0` — конкретные версии выбирайте по требованиям проекта, а не копируйте пример вслепую.
- В `package.json` workspace-пакета замените повторяющийся диапазон на ссылку вроде `"typescript": "catalog:"`. Для именованного каталога используется форма `catalog:<name>`.
- Запустите обычную установку pnpm и проверьте diff `package.json` и `pnpm-lock.yaml`. Не коммитьте массовое изменение lockfile без просмотра: переход на каталог может выявить несовместимые peer requirements.
- Переводите зависимости группами, а не весь monorepo одной огромной правкой. Удобно начать с tooling-пакетов, затем перейти к runtime-зависимостям.
- Добавьте проверку в CI, чтобы изменения workspace-файла и lockfile проходили тот же install/check, что и обычное обновление зависимостей.
Совет: При миграции сначала выберите одну зависимость, используемую во многих пакетах. Малый пилот быстрее показывает, как выбранная версия влияет на peer dependencies.
Пример `pnpm-workspace.yaml` и ссылок из package.json
- В workspace-файле: `catalog: { typescript: ^5.9.0, eslint: ^9.0.0 }`.
- Именованный вариант: `catalogs: { react19: { react: ^19.0.0, react-dom: ^19.0.0 } }`.
- В `package.json`: `"typescript": "catalog:"` использует каталог по умолчанию.
- В `package.json`: `"react": "catalog:react19"` использует именованный каталог.
Где catalog protocol работает и что происходит при публикации
Смысл `catalog:` был бы ограниченным, если бы такие строки уходили в npm registry как нестандартные версии. Поэтому pnpm при упаковке и публикации заменяет catalog-ссылки на реальные specifier-ы из конфигурации. Официальная документация также указывает использование catalog references в обычных секциях зависимостей, включая dependencies, devDependencies, peerDependencies и optionalDependencies; поведение отдельных сценариев всё равно стоит проверять на текущей версии pnpm. Для внутренних непубликуемых пакетов это удобно само по себе, а для библиотек важно дополнительно посмотреть итог `pnpm pack`, чтобы убедиться, какой `package.json` увидит потребитель.
Предупреждение: Перед публикацией библиотеки откройте содержимое tarball после `pnpm pack`. Проверка фактического манифеста надёжнее предположений о том, как протокол будет преобразован.
Именованные каталоги: когда они полезны, а когда создают лишнюю сложность
Один каталог подходит, если весь monorepo движется синхронно. Именованные каталоги полезны во время управляемой миграции: например, часть приложений остаётся на одной major-версии фреймворка, а экспериментальная ветка workspace использует следующую. Тогда названия `react18` и `react19` явно показывают выбранную линию. Но превращать каждую зависимость в отдельный именованный каталог бессмысленно — получится ещё один слой конфигурации без пользы. Хороший каталог выражает осознанную политику версий: общие инструменты, стабильный набор runtime-зависимостей или несколько поддерживаемых платформенных линий.
Catalogs и похожие механизмы — не одно и то же
- pnpm catalogs. Централизовать specifier версий в workspace. Одинаковые зависимости во многих package.json
- pnpm dedupe. Сократить дублирование разрешённых версий. После установки есть совместимые повторяющиеся версии
- overrides. Принудительно изменить версию в графе зависимостей. Контроль транзитивной зависимости или временный security fix
- workspace: protocol. Ссылаться на локальные workspace-пакеты. Связи между собственными пакетами monorepo
Проверка миграции на catalogs
- В workspace-файле нет двух конкурирующих источников версии для одной и той же политики.
- Все заменённые `package.json` используют правильный `catalog:` или `catalog:<name>`.
- Lockfile пересоздан контролируемо и прошёл review.
- Peer dependency warnings не проигнорированы.
- Тесты и build выполнены минимум для затронутых workspace-пакетов.
- Для публикуемых пакетов проверен результат `pnpm pack`.
Как внедрять catalogs без большого рискованного diff
Практичная стратегия — сначала инвентаризировать зависимости, которые повторяются хотя бы в нескольких workspace-пакетах, затем выбрать одну с понятной совместимостью. Создайте каталог, замените specifier в двух-трёх пакетах, выполните install и CI, только после этого расширяйте миграцию. Не смешивайте в тот же коммит обновление major-версии, рефакторинг конфигурации и переход на catalog protocol: при регрессии будет трудно определить причину. Когда схема прижилась, можно формализовать правило code review — общие версии меняются в каталоге, а локальные исключения должны иметь объяснение. Так catalog становится частью архитектуры monorepo, а не декоративной YAML-секцией.
Какие поля поддерживает catalog protocol и что проверить в текущем pnpm
В актуальной документации pnpm catalog references разрешены не только в `dependencies`, но также в `devDependencies`, `peerDependencies` и `optionalDependencies`; в `pnpm-workspace.yaml` протокол может использоваться и в `overrides`. Это удобно, но не означает, что один каталог должен механически управлять всеми типами зависимостей: для peer dependency диапазон является частью публичного контракта библиотеки и требует отдельного review. Перед миграцией проверьте версию pnpm, которой реально пользуются разработчики и CI, затем зафиксируйте её через принятый в проекте механизм. После `pnpm install` просмотрите lockfile и выполните `pnpm pack` для публикуемых пакетов: при pack/publish pnpm заменяет `catalog:` на обычный specifier. Если команда использует новый `catalogMode`, включайте его осознанно — режим `strict` меняет правила добавления зависимостей и способен превратить обычный `pnpm add` в policy-check.
Как понять, что dependency не стоит выносить в общий catalog
Не каждая повторяющаяся строка версии должна становиться общей политикой. Если один workspace сознательно тестирует следующую major-версию, библиотека публикует широкий peer range или пакет имеет платформенную привязку, принудительная ссылка на единый catalog может скрыть полезное различие. В таких случаях оставьте локальный specifier либо заведите понятный named catalog с названием линии миграции, а не маскируйте исключение случайным диапазоном. Хороший критерий простой: изменение записи в catalog должно означать осознанное обновление группы пакетов, которые действительно должны двигаться вместе. Если команда не может объяснить эту группу, централизация добавит связанность вместо порядка.
Что учитывать
Условия меняются. Страница отражает состояние на 2026-09-21; при расхождении с официальной документацией приоритет у первоисточника.
Источники и проверка
Фактическая часть сверена по первичным источникам (в т.ч. Catalogs). Пример и формулировки — редакция N1RO на 2026-09-21.