n1ro°
RU

Текст и данные · Инструкция

pnpm --filter: как запускать команды по части monorepo

pnpm --filter позволяет запускать install, test, build и другие команды не по всему monorepo, а по выбранному пакету.

Редакция N1RO · Проверено

Короткий ответ

Для одного workspace-пакета используйте `pnpm --filter <name> <command>`. Если нужны его зависимости — селектор вида `<name>..`, если зависимые проекты — `..<name>`; для CI полезно добавлять `--fail-if-no-match`, чтобы опечатка в фильтре не превращалась в тихий успешный запуск без работы.

Что именно фильтрует pnpm

Фильтр pnpm работает на уровне workspace-проектов, а не отдельных файлов внутри пакета. Это важно: команда `pnpm --filter app test` сначала выбирает проект `app`, а уже затем запускает команду в его контексте. Официальная документация поддерживает несколько видов селекторов, которые можно комбинировать: точное или glob-подобное имя пакета, зависимости и dependents через многоточие, расположение проекта через `{path}`, а также изменённые проекты через `[git-ref]`. Несколько `--filter` можно передать одновременно; исключение задаётся отрицательным селектором с `!`. Такая модель удобна для больших monorepo, но ошибки возникают, когда разработчик мысленно смешивает «зависимости пакета» и «пакеты, которые зависят от него». Перед автоматизацией CI полезно сначала выполнить безопасную команду вроде `pnpm --filter .. list` или иной читающей операции, чтобы увидеть, какие проекты действительно выбраны.

Совет: Если имя workspace-пакета не уникально или вы используете scope, фильтруйте полным именем вроде `@scope/pkg`. Это делает CI менее зависимым от совпадений имён.

Как построить фильтр без сюрпризов

  1. Определите единицу работы: Решите, нужен один пакет, его dependency-граф, dependents или набор по пути. Не начинайте с длинной комбинации селекторов.
  2. Проверьте базовый селектор: Запустите читающую команду по выбранному фильтру и убедитесь, что имя пакета совпадает с workspace manifest.
  3. Добавьте направление графа: Если нужны зависимости, используйте многоточие после имени; если dependents — перед именем. Каретка перед многоточием исключает сам исходный пакет.
  4. При необходимости ограничьте путь или Git-изменения: Используйте `{path}` для расположения workspace и `[ref]` для изменений относительно Git-ref. Их можно сочетать с другими частями селектора.
  5. Добавьте исключения: Отрицательным фильтром `!selector` уберите docs, examples или другой проект, который не должен участвовать.
  6. В CI включите ошибку при пустом результате: Опция `--fail-if-no-match` полезна там, где отсутствие совпадений должно ломать job, а не выглядеть как успешный запуск.

Важно: Оборачивайте фильтры с `!`, `{}`, `[]` и glob-символами в кавычки, если ваша оболочка может интерпретировать их сама. Иначе pnpm может получить уже изменённый аргумент.

Dependencies и dependents: самая частая путаница

Предположим, `web` импортирует `ui`, а `ui` импортирует `tokens`. Для селектора `web..` pnpm выбирает `web` и его зависимости по графу, то есть также `ui` и `tokens`. Это удобно, когда вы хотите сначала собрать всё, что требуется приложению. Селектор `..ui`, наоборот, идёт в обратную сторону: он выбирает `ui` и проекты, которые зависят от `ui`, например `web`. Такой фильтр полезен для проверки влияния изменения библиотеки на потребителей. Варианты с `^..` и `..^` исключают исходный пакет из результата, что удобно, когда его команда будет запущена отдельно. Не делайте вывод по расположению каталогов: dependency-граф строится по workspace-зависимостям, а не по тому, лежат ли проекты рядом. Если после изменения shared-библиотеки вы хотите протестировать потенциально затронутые приложения, обычно думаете в сторону dependents, а не dependencies.

Фильтрация по изменениям и директориям

Селектор с квадратными скобками позволяет выбрать проекты, изменённые относительно Git-ref, например `[origin/main]`. В больших репозиториях это удобная база для сокращения CI, но важно понимать границу: итог зависит от того, какие изменения pnpm относит к workspace-проектам и какой ref доступен в checkout. Если CI делает shallow clone и нужного ref нет локально, сначала обеспечьте доступ к истории/ветке, иначе сам фильтр не решит проблему. Фильтр пути в фигурных скобках выбирает проекты по расположению, например `{packages/**}`. Его можно сочетать с графовыми модификаторами и Git-частью, чтобы выразить более точную область. Держите сложные селекторы читаемыми: две последовательные команды иногда проще сопровождать, чем один «магический» фильтр. Перед внедрением сохраните несколько тестовых сценариев в CI, чтобы изменения структуры monorepo не сделали фильтр слишком широким или слишком узким.

Предупреждение: Для оптимизации CI сначала измерьте длительность job. Фильтр уменьшает число пакетов, но сложная выборка сама по себе не гарантирует более быстрый pipeline, если основное время уходит на общий install или cache miss.

Практические команды

Для одного пакета типичная форма выглядит как `pnpm --filter @acme/web build`. Чтобы собрать пакет вместе с workspace-зависимостями, можно использовать `pnpm --filter "@acme/web.." build`. Для запуска тестов у потребителей библиотеки — `pnpm --filter "..@acme/ui" test`. Фильтр по каталогу может выглядеть как `pnpm --filter "{packages/**}" lint`, а изменённые проекты — как `pnpm --filter "[origin/main]" test`. Если нужно исключить docs из более широкого набора, добавьте второй `--filter "!@acme/docs"`. Конкретная команда после фильтра может быть `run`, `test`, `build`, `exec` или другая поддерживаемая операция; фильтр отвечает только за выбор workspace-проектов. В CI добавляйте `--fail-if-no-match`, когда нулевой набор является ошибкой конфигурации. После изменения фильтра сравните список выбранных пакетов с ожидаемым хотя бы на нескольких типовых pull request.

Как проверить сложный фильтр до включения в CI

Сложный selector лучше проверять как отдельный контракт, а не сразу использовать в команде, которая публикует пакет или меняет артефакты. Возьмите несколько заранее известных сценариев: изменение только одного leaf-package, изменение общей библиотеки, изменение документации и изменение файла в корне. Для каждого запишите ожидаемый набор workspace-проектов и сравните его с фактическим результатом фильтра. Если используется `[origin/main]`, убедитесь, что CI действительно получил этот ref; при shallow checkout нужной точки сравнения может не быть локально. Для фильтров с dependents отдельно проверьте изменение shared-пакета, потому что именно здесь легко перепутать `foo..` и `..foo`. В production job включайте `--fail-if-no-match`, если пустой набор означает ошибку, но не делайте это автоматически для задач, где отсутствие затронутых пакетов допустимо. Такой небольшой набор тестовых случаев защищает pipeline лучше, чем комментарий с длинным selector без проверяемых примеров.

Проверка фильтра перед CI

  • Используется корректное package name из manifest
  • Понятно, нужны dependencies или dependents
  • Спецсимволы защищены от shell
  • Git-ref существует в checkout, если используется `[ref]`
  • Исключения не убирают нужные проекты
  • `--fail-if-no-match` включён там, где пустой набор должен быть ошибкой

Что учитывать

Условия меняются. Страница отражает состояние на 2026-09-21; при расхождении с официальной документацией приоритет у первоисточника.

Источники и проверка

Фактическая часть сверена по первичным источникам (в т.ч. Filtering). Пример и формулировки — редакция N1RO на 2026-09-21.