Commit 9e1287e
feat: filter packages with
## Summary
Add pnpm-compatible `--filter` flag to `vp run` for selecting which
packages to run tasks in.
Supports:
- **Package name**: exact (`--filter @test/app`) and glob (`--filter
@test/*`)
- **Directory**: exact (`--filter ./packages/app`), glob (`--filter
./packages/*`), relative (`.`, `..`)
- **Braced paths**: `{./path}` for traversal on paths, `name{./dir}` for
name + directory intersection
- **Dependency traversal**: `foo...` (dependencies), `...foo`
(dependents), `^` to exclude self, `...foo...` (both)
- **Exclusion**: `!foo` to exclude packages from the result
- **Multiple filters**: `--filter a --filter b` (union); `--filter "a
b"` (whitespace split, pnpm compat)
- **`-w` / `--workspace-root`**: select workspace root; additive with
`--filter`, redundant with `-r`, supports `-t` for root + transitive
deps
- **Nested expansion**: `vp run --filter .... build` inside package
scripts is expanded in the plan
### Architecture: `PackageQueryArgs` → `PackageQuery` →
`FilterResolution`
Package selection lives in `vite_workspace`, not in the task runner. Any
command that needs to pick packages can reuse it.
**Step 1 — Parse CLI flags into a query.**
`PackageQueryArgs` is a clap struct with `-r`, `-t`, `-w`, and
`--filter`. Embed it via `#[clap(flatten)]`, then call
`into_package_query()`:
```rust
// in any command definition (e.g. `vp exec`, `vp run`)
#[derive(clap::Args)]
struct MyCommand {
#[clap(flatten)]
packages: PackageQueryArgs,
}
// at runtime — package_name comes from `pkg#task` syntax (e.g. "app" in `vp run app#build`).
// commands that don't use `pkg#task` (like `vp exec`) pass None.
let query: PackageQuery = args.packages.into_package_query(package_name, &cwd)?;
```
`into_package_query` validates flag combinations (e.g. `--filter` + `-r`
is an error), splits whitespace, parses each `--filter` token, and
returns an opaque `PackageQuery`.
**Step 2 — Resolve the query against the package graph.**
```rust
let resolution: FilterResolution = indexed_package_graph.resolve_query(&query);
// resolution.package_subgraph — the selected packages + edges between them
// resolution.unmatched_selectors — filter strings that matched nothing (for warnings)
```
`PackageQuery` is opaque — callers don't inspect its internals. They
just pass it to `resolve_query` and get back the selected subgraph.
Future commands like `vp exec` only need steps 1 and 2 — they get the
selected packages without depending on the task graph at all.
**Step 3 (task runner only) — Map packages to tasks.**
The task-graph layer maps the package subgraph to task nodes,
reconnecting across packages that lack the requested task. Only `vp run`
needs this step.
### `unmatched_selectors` tracks original filter strings
Each `--filter` token is stored as a `source: Option<Str>` inside the
parsed filter. Synthetic filters (implicit cwd, `-w`) get `source: None`
since the user didn't type them. When a filter matches nothing,
`resolve_query` collects the original string into `unmatched_selectors:
Vec<Str>` so the caller can show a warning like:
```
warn: --filter "typo-pkg" matched no packages
```
This works correctly even with whitespace splitting (`--filter "a b"` →
two filters, each with its own source string).
## Test plan
- [x] 48 unit tests in `package_filter` for `parse_filter`,
`into_package_query`, `resolve_directory_pattern`, and source tracking —
covering all selector types, traversal modes, flag combinations, path
normalization, and error cases
- [x] 40 plan snapshot tests across `filter-workspace` (35) and
`transitive-skip-intermediate` (5) fixtures
- [x] Existing plan and e2e snapshot tests still pass
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>--filter (#176)1 parent 1128edc commit 9e1287e
107 files changed
Lines changed: 4278 additions & 900 deletions
File tree
- crates
- vite_glob/src
- vite_graph_ser/src
- vite_task_bin/tests/e2e_snapshots
- fixtures
- filter-unmatched
- packages
- app
- lib
- snapshots
- task-select/snapshots
- vite_task_graph
- src
- query
- vite_task_plan
- src
- tests/plan_snapshots
- fixtures
- comprehensive-task-graph/snapshots
- conflict-test/snapshots
- cycle-dependency/snapshots
- dependency-both-topo-and-explicit/snapshots
- duplicate-package-names
- packages
- pkg-a
- pkg-b
- snapshots
- empty-package-test/snapshots
- explicit-deps-workspace/snapshots
- filter-workspace
- packages
- app
- cli
- core
- lib
- utils
- snapshots
- recursive-topological-workspace/snapshots
- transitive-skip-intermediate
- packages
- bottom
- middle
- top
- snapshots
- vite_task
- docs
- src
- cli
- session
- vite_workspace
- src
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
91 | 91 | | |
92 | 92 | | |
93 | 93 | | |
| 94 | + | |
94 | 95 | | |
95 | 96 | | |
96 | 97 | | |
| |||
145 | 146 | | |
146 | 147 | | |
147 | 148 | | |
148 | | - | |
| 149 | + | |
149 | 150 | | |
150 | 151 | | |
151 | 152 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
| 7 | + | |
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
20 | | - | |
21 | | - | |
| 20 | + | |
| 21 | + | |
22 | 22 | | |
23 | 23 | | |
24 | | - | |
| 24 | + | |
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| 28 | + | |
| 29 | + | |
28 | 30 | | |
29 | 31 | | |
30 | | - | |
| 32 | + | |
31 | 33 | | |
32 | 34 | | |
33 | 35 | | |
| |||
45 | 47 | | |
46 | 48 | | |
47 | 49 | | |
48 | | - | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | | - | |
53 | | - | |
| 50 | + | |
54 | 51 | | |
55 | 52 | | |
56 | 53 | | |
57 | | - | |
| 54 | + | |
58 | 55 | | |
59 | | - | |
| 56 | + | |
60 | 57 | | |
61 | 58 | | |
62 | 59 | | |
63 | 60 | | |
64 | | - | |
| 61 | + | |
65 | 62 | | |
66 | | - | |
| 63 | + | |
67 | 64 | | |
68 | 65 | | |
69 | 66 | | |
| |||
101 | 98 | | |
102 | 99 | | |
103 | 100 | | |
104 | | - | |
| 101 | + | |
105 | 102 | | |
106 | 103 | | |
107 | 104 | | |
108 | 105 | | |
109 | | - | |
| 106 | + | |
110 | 107 | | |
111 | 108 | | |
112 | 109 | | |
113 | 110 | | |
114 | | - | |
115 | | - | |
116 | | - | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
117 | 114 | | |
118 | 115 | | |
119 | 116 | | |
| |||
123 | 120 | | |
124 | 121 | | |
125 | 122 | | |
126 | | - | |
| 123 | + | |
127 | 124 | | |
128 | 125 | | |
129 | 126 | | |
130 | 127 | | |
131 | | - | |
| 128 | + | |
132 | 129 | | |
133 | 130 | | |
134 | 131 | | |
| |||
0 commit comments