Everyone heard about Lerna, which is “A tool for managing JavaScript projects with multiple packages.” A lot fewer devs heard about pnpm, which is a fast, disk space efficient package manager for JavaScript. Both Lerna and pnpm try to improve tooling for multi-package repositories (MPR). For Lerna it was the reason for creation. For pnpm, MPR support is a nice bonus feature that is implemented via a set of commands called recursive. Of course, there are many differences between how Lerna manages a multi-package repo vs pnpm. In this article, I want to compare one seemingly simple aspect: filtering.

Filtering in an MPR is important because, during development, changes are mainly made inside one or two packages. It wouldn’t make sense to run commands on the whole repository if only a few packages were modified.

Filtering in Lerna

Filtering in Lerna (as of v3.2.1 ) is achieved by the following flags:

scope - Include only packages with names matching the given glob.

- Include only packages with names matching the given glob. include-filtered-dependents - Include all transitive dependents when running a command regardless of --scope , --ignore , or --since .

- Include all transitive dependents when running a command regardless of , , or . include-filtered-dependencies - Include all transitive dependencies when running a command regardless of --scope , --ignore , or --since .

- Include all transitive dependencies when running a command regardless of , , or . ignore - Exclude packages with names matching the given glob.

- Exclude packages with names matching the given glob. private - Include private packages. Pass --no-private to exclude private packages.

- Include private packages. Pass --no-private to exclude private packages. since - Only include packages that have been updated since the specified [ref]. If no ref is passed, it defaults to the most recent tag.

These flags make filtering in Lerna quite powerful. However, they are pretty hard to type. Let’s say you downloaded a repository and want to work only on login-page component. You'd want to run installation for login-page and any of its dependencies:

lerna bootstrap --scope login-page --include-filtered-dependencies

Or maybe you changed a component called site-header and would like to run tests on all the dependent packages:

lerna run test --scope site-header --include-filtered-dependents

These flags are not only hard to type but also hard to remember and easy to mix up.

Filtering in pnpm

Unlike Lerna, pnpm uses a special package selector syntax to restrict its commands. So instead of memorizing a set of long flag names, you should only remember a very easy-to-remember selector syntax.

As of version 2.15.0 , these are the selectors that pnpm supports:

<pattern> - Restricts the scope to package names matching the given pattern. E.g.: foo , @bar/*

- Restricts the scope to package names matching the given pattern. E.g.: , <pattern>... - Includes all direct and indirect dependencies of the matched packages. E.g.: foo...

- Includes all direct and indirect dependencies of the matched packages. E.g.: ...<pattern> - Includes all direct and indirect dependents of the matched packages. E.g.: ...foo , ...@bar/*

- Includes all direct and indirect dependents of the matched packages. E.g.: , ./<directory> - Includes all packages that are inside a given subdirectory. E.g.: ./components

- Includes all packages that are inside a given subdirectory. E.g.: . - Includes all packages that are under the current working directory.

These filters may be specified either via the --filter flag or after a -- at the end of the command.

Note: as of v2.15.0 , filters after -- are not supported by run , exec , test

So if you want to bootstrap login-page and all of its dependencies, this is the way to do it with pnpm:

pnpm recursive install -- login-page...

And if you want to run tests of site-header and all of its dependents, use the ...<pattern> selector:

pnpm recursive test --filter ...site-header

Of course, you can combine as many selectors as you’d like:

pnpm recursive update -- ...site-header login-page... ./utils @style/*

The command above updates dependencies in: