workspaces-filter
Version:
A companion for filtering monorepo workspaces, by package name or package dir. Because all package manager's are weird. Useful for running scripts on a subset of workspaces. The primary reason is because Bun's "--filter" feature is buggy, but it's also us
247 lines (168 loc) • 7.9 kB
Markdown
# workspaces-filter
> A companion for filtering monorepo workspaces, by package name or package dir. Because all package
> manager's are weird. Useful for running scripts on a subset of workspaces. The primary reason is
> because Bun's --filter feature is buggy, but it's also useful for other package manger, it also
> runs shell scripts using the `execa` package.
<!-- prettier-ignore-start -->
[![][npm-version-src]][npm-version-href]
[![][github-actions-src]][github-actions-href]
[![][codecov-src]][codecov-href]
[![][npm-downloads-src]][npm-downloads-href]
[![][codestyle-src]][codestyle-href]
[![][license-src]][license-href]
<!-- prettier-ignore-end -->
<!-- [![][npm-weekly-img]][npmv-url] -->
<!-- [![][npm-monthly-img]][npmv-url] -->
<!-- [![][npm-yearly-img]][npmv-url] -->
<!-- [![][npm-alltime-img]][npmv-url] -->
## Highlights
The library is especially useful when you need to:
- Programmatically find and filter workspace packages
- Run commands or scripts on a subset of packages
- Execute package manager commands across filtered workspaces
- Run shell commands in specific workspace directories
## Install
```sh
bun add workspaces-filter
npm install workspaces-filter
```
or use the CLI directly:
```sh
bunx workspaces-filter
npx workspaces-filter
pnpm dlx workspaces-filter
# or install globally
npm install -g workspaces-filter
```
## Usage as CLI
```sh
workspaces-filter/0.8.1
Usage:
$ workspaces-filter <pattern> [...command]
Commands:
<pattern> [...command] Select by package name or workspace directory
For more info, run any command with the `--help` flag:
$ workspaces-filter --help
Options:
--print <mode> Print the names/folders of selected packages, without running command
--cwd <dir> Current working directory (default: /home/charlike/code/mid-april-2025/workspaces-filter)
--pm, --package-manager <pm> The package manager to use. Defaults to packageManager from root package.json, or Bun
-v, --version Display version number
-h, --help Display this message
Examples:
workspaces-filter . build # run in all packages of all workspaces
workspaces-filter _ build # because the "*" would not work if raw
workspaces-filter '*' build # should be quoted to avoid shell globbing
workspaces-filter "*preset*" build
workspaces-filter "*preset*" add foo-pkg barry-pkg
workspaces-filter "*preset*" add --dev typescript
workspaces-filter "./packages/foo" -- echo "Hello, World!"
workspaces-filter "./packages/*preset*" -- pwd
workspaces-filter "*preset*" --print names
workspaces-filter "*preset*" --print json
workspaces-filter "*preset*" --print dirs
```
> [!NOTE]
>
> To run a shell command in selected/filtered packages, use `--` right after the pattern!
> [!CAUTION]
>
> Keep in mind that if a workspace package has a script called `add`, it would run it while you may want to run `bun add`, `npm add` or `pnpm add`.
> Just don't name your scripts like that or expect buggy behaviors. It's easy to assume it would have conflicts. Or use `--` to run a shell command in selected package dirs.
### Examples
```sh
npx workspaces-filter '*preset*' build
pnpm dlx workspaces-filter '*preset*' add foo-pkg
bunx workspaces-filter '*preset*' add --dev typescript
```
It checks if there is a script in package's scripts field (thus runs it with `bun run`, `npm run` or
`pnpm run`), if not runs the package manager command (`bun add`, `npm add`), or a shell command if
`_` or `sh` is provided right after the pattern, like so
```sh
bunx workspaces-filter './packages/foo' -- echo 'Hello, World!' # runs `echo 'Hello, World!'` in the `./packages/foo` workspace
bunx workspaces-filter './packages/*preset*' -- pwd # runs `pwd` in each workspace
```
You can run `pnpm dlx` like so
```sh
pnpx workspaces-filter '*preset*' dlx esmc
```
## Using as a Library
The package can also be used programmatically in your Node.js/TypeScript applications:
### API
<!-- prettier-ignore-start -->
<!-- docks-start -->
_Generated using [docks](https://github.com/tunnckoCore/workspaces-filter/blob/master/docks.ts)._
### [filter](./src/index.ts#L51)
Filters workspace packages based on provided glob patterns and search patterns.
<span id="filter-params"></span>
#### Params
- `wsGlobs` **{Array<string>}** - Array of workspace glob patterns to search for package.json files.
- `pattern` **{Array<string>}** - String or array of strings to filter workspaces by name or directory.
- `cwd` - Optional current working directory (defaults to `process.cwd()`).
<span id="filter-throws"></span>
#### Throws
- **{Error}** - When no workspace globs are provided.
- **{Error}** - When no pattern is provided.
<span id="filter-returns"></span>
#### Returns
- **{Promise<Graph>}** - Resolving to a Graph object containing filtered workspace metadata.
<span id="filter-examples"></span>
#### Examples
```ts
import { filter } from 'workspaces-filter';
// Filter workspaces matching 'pkg-*' pattern
const graph = await filter(['packages/*'], 'pkg-*');
// Filter multiple patterns
const graph = await filter(['packages/*'], ['pkg-1', 'pkg-2']);
// Filter with package dirs
const graph = await filter(['packages/*'], ['packages/foo']);
// Filter with custom working directory
const graph = await filter(['packages/*'], '*', '/path/to/project');
```
### [runCommandOn](./src/index.ts#L167)
Executes a shell command or a package script in the context of each package in the graph.
<span id="runcommandon-params"></span>
#### Params
- `args` **{Array<string>}** - Arguments to pass to the command.
- `graph` **{Graph}** - Graph object containing package metadata.
- `options` **{RunCommandOnOptions}** - Optional configuration for running the command.
<span id="runcommandon-returns"></span>
#### Returns
- **{Promise<Graph>}** - Resolving to the input graph object.
<span id="runcommandon-examples"></span>
#### Examples
```ts
import { filter, runCommandOn } from 'workspaces-filter';
const graph = await filter(['packages/*'], ['@scope/*']);
console.log(graph);
type RunCommandOnOptions = {
cwd?: string;
isShell?: boolean;
packageManager?: string;
onTestCallback?: (_err: any, _ok: any) => void | Promise<void>;
};
// Run a shell command in each package
await runCommandOn(['echo', 'Hello, World!'], graph, { isShell: true } as RunCommandOnOptions);
// Run a package script in each package
await runCommandOn(['build'], graph);
```
<!-- docks-end -->
<!-- prettier-ignore-end -->
## License
Licensed under the [MIT License](https://opensource.org/licenses/MIT)
<!-- prettier-ignore-start -->
[npm-version-src]: https://img.shields.io/npm/v/workspaces-filter?style=flat&logo=npm
[npm-version-href]: https://npmjs.com/package/workspaces-filter
[npm-downloads-src]: https://img.shields.io/npm/dm/workspaces-filter?style=flat&logo=npm
[npm-downloads-href]: https://npmjs.com/package/workspaces-filter
[github-actions-src]: https://img.shields.io/github/actions/workflow/status/tunnckoCore/workspaces-filter/ci.yml?style=flat&logo=github
[github-actions-href]: https://github.com/tunnckocore/workspaces-filter/actions?query=workflow%3Aci
[codecov-src]: https://img.shields.io/codecov/c/gh/tunnckocore/workspaces-filter/master?style=flat&logo=codecov
[codecov-href]: https://codecov.io/gh/tunnckocore/workspaces-filter
[bundle-src]: https://img.shields.io/bundlephobia/minzip/workspaces-filter?style=flat
[bundle-href]: https://bundlephobia.com/result?p=workspaces-filter
[license-src]: https://img.shields.io/npm/l/workspaces-filter?style=flat&colorB=blue
[license-href]: https://github.com/tunnckocore/workspaces-filter/blob/master/LICENSE
[codestyle-src]: https://badgen.net/badge/code%20style/xaxa/44cc11?icon=airbnb
[codestyle-href]: https://github.com/tunnckoCore/eslint-config-xaxa
<!-- prettier-ignore-end -->