Skip to content

Commit 6287a29

Browse files
wan9chicodex
andcommitted
Revert "fix(task-graph): limit package dependsOn to direct deps (#469)"
This reverts commit ed454ce. Co-authored-by: GPT-5 Codex <codex@openai.com>
1 parent 734d36b commit 6287a29

9 files changed

Lines changed: 73 additions & 17 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Changelog
22

3-
- **Added** `dependsOn` can now run the specified task in each direct workspace package listed in `dependencies`, `devDependencies`, or `peerDependencies`; for example, `{ "task": "build", "from": ["dependencies", "devDependencies"] }` runs `build` in each direct dependency and dev dependency ([#467](https://github.com/voidzero-dev/vite-task/pull/467), [#469](https://github.com/voidzero-dev/vite-task/pull/469)).
3+
- **Added** Object-form `dependsOn` entries can now run a task in direct workspace dependency packages selected by package.json fields, e.g. `{ "task": "build", "from": ["dependencies", "devDependencies"] }`.
44
- **Added** First-party support for caching `vite build` with zero cache config, giving Vite projects correct cache hits out of the box ([vitejs/vite#22453](https://github.com/vitejs/vite/pull/22453)).
55
- **Added** [`@voidzero-dev/vite-task-client`](https://npmx.dev/package/@voidzero-dev/vite-task-client), allowing tools to report cache information to Vite Task at runtime so users do not need to configure it manually ([#441](https://github.com/voidzero-dev/vite-task/pull/441), [#454](https://github.com/voidzero-dev/vite-task/pull/454), [#449](https://github.com/voidzero-dev/vite-task/pull/449), [#450](https://github.com/voidzero-dev/vite-task/pull/450), [#458](https://github.com/voidzero-dev/vite-task/pull/458), [#431](https://github.com/voidzero-dev/vite-task/pull/431), [#459](https://github.com/voidzero-dev/vite-task/pull/459), [#472](https://github.com/voidzero-dev/vite-task/pull/472)).
66
- **Changed** Cached tasks now restore automatically tracked output files by default; use `output: []` to disable restoration ([#460](https://github.com/voidzero-dev/vite-task/pull/460), [#461](https://github.com/voidzero-dev/vite-task/pull/461)).

crates/vite_task/docs/task-query.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,10 @@ Object-form entries select direct package dependencies from the declaring task:
208208
```
209209

210210
For `app#test`, this runs `build` in direct workspace dependency packages selected
211-
by the listed package.json fields. Packages without `build` are skipped. Supported
212-
fields are `dependencies`, `devDependencies`, and `peerDependencies`.
211+
by the listed package.json fields. If a selected package lacks `build`, expansion
212+
walks through its matching dependency edges until it finds the nearest packages
213+
with `build`; it stops at packages that already have `build`. Supported fields
214+
are `dependencies`, `devDependencies`, and `peerDependencies`.
213215

214216
Recursive expansion comes from dependency tasks declaring their own `dependsOn`
215217
entries. For example, if `ui#build` also has `{ "task": "build", "from": "dependencies" }`,

crates/vite_task_graph/src/query/mod.rs

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
use petgraph::{Direction, prelude::DiGraphMap, visit::EdgeRef};
2222
use rustc_hash::{FxHashMap, FxHashSet};
2323
use vite_str::Str;
24-
use vite_workspace::PackageNodeIndex;
2524
pub use vite_workspace::package_graph::{PackageQuery, PackageQueryResolveError};
25+
use vite_workspace::{DependencyType, PackageNodeIndex};
2626

2727
use crate::{IndexedTaskGraph, PackageDependencyEntry, TaskDependencyType, TaskId, TaskNodeIndex};
2828

@@ -166,6 +166,47 @@ impl IndexedTaskGraph {
166166
.collect()
167167
}
168168

169+
/// Resolve each package to the nearest reachable `task_name` task node.
170+
///
171+
/// Traversal starts from `packages`, follows package dependency edges whose
172+
/// type is in `dependency_types`, and stops at a package once it defines the
173+
/// requested task. This lets object-form `dependsOn` skip through direct
174+
/// dependencies that lack the task without pulling in dependencies behind a
175+
/// package that already has it.
176+
fn resolve_nearest_packages_to_tasks(
177+
&self,
178+
packages: impl Iterator<Item = PackageNodeIndex>,
179+
dependency_types: &[DependencyType],
180+
task_name: &Str,
181+
) -> FxHashMap<PackageNodeIndex, TaskNodeIndex> {
182+
let package_graph = self.indexed_package_graph.package_graph();
183+
let mut pkg_to_task = FxHashMap::default();
184+
let mut seen = FxHashSet::default();
185+
let mut frontier: Vec<_> = packages.collect();
186+
187+
while let Some(pkg) = frontier.pop() {
188+
if !seen.insert(pkg) {
189+
continue;
190+
}
191+
if let Some(&task_idx) = self
192+
.node_indices_by_task_id
193+
.get(&TaskId { package_index: pkg, task_name: task_name.clone() })
194+
{
195+
pkg_to_task.insert(pkg, task_idx);
196+
continue;
197+
}
198+
199+
frontier.extend(
200+
package_graph
201+
.edges(pkg)
202+
.filter(|edge| dependency_types.contains(edge.weight()))
203+
.map(|edge| edge.target()),
204+
);
205+
}
206+
207+
pkg_to_task
208+
}
209+
169210
/// Map a package subgraph to a task execution graph.
170211
///
171212
/// For packages **with** the task: add the corresponding `TaskNodeIndex`.
@@ -286,13 +327,15 @@ impl IndexedTaskGraph {
286327
let origin_package = from_task_id.package_index;
287328
let package_graph = self.indexed_package_graph.package_graph();
288329

289-
// Select the origin's direct dependency packages whose edge matches one of
290-
// the requested dependency fields, mapped to their `task_name` task nodes.
291-
let pkg_to_task = self.resolve_packages_to_tasks(
330+
// Select nearest dependency packages with `task_name`, starting from the
331+
// origin's direct dependency packages whose edge matches one of the
332+
// requested dependency fields.
333+
let pkg_to_task = self.resolve_nearest_packages_to_tasks(
292334
package_graph
293335
.edges(origin_package)
294336
.filter(|edge| entry.dependency_types.contains(edge.weight()))
295337
.map(|edge| edge.target()),
338+
&entry.dependency_types,
296339
&entry.task_name,
297340
);
298341

crates/vite_task_plan/tests/plan_snapshots/fixtures/depends_on_package_dependencies/snapshots.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ cwd = "packages/app"
3232

3333
[[plan]]
3434
compact = true
35-
name = "direct_dependency_without_task_is_skipped"
35+
name = "nearest_task_through_dependency_without_task"
3636
args = ["run", "@test/nearest-through-app#build"]
3737

3838
[[plan]]
3939
compact = true
40-
name = "direct_dependency_with_task_is_selected"
40+
name = "nearest_task_stops_at_dependency_with_task"
4141
args = ["run", "@test/nearest-stop-app#build"]
4242

4343
# A selected package (`nearest-order-direct`) transitively depends on another
@@ -46,7 +46,7 @@ args = ["run", "@test/nearest-stop-app#build"]
4646
# stay unordered because there is no direct package edge between them.
4747
[[plan]]
4848
compact = true
49-
name = "direct_dependency_tasks_are_not_transitively_ordered"
49+
name = "nearest_task_no_transitive_ordering"
5050
args = ["run", "@test/nearest-order-app#build"]
5151

5252
[[plan]]

crates/vite_task_plan/tests/plan_snapshots/fixtures/depends_on_package_dependencies/snapshots/query_direct_dependency_without_task_is_skipped.jsonc

Lines changed: 0 additions & 4 deletions
This file was deleted.

crates/vite_task_plan/tests/plan_snapshots/fixtures/depends_on_package_dependencies/snapshots/query_direct_dependency_tasks_are_not_transitively_ordered.jsonc renamed to crates/vite_task_plan/tests/plan_snapshots/fixtures/depends_on_package_dependencies/snapshots/query_nearest_task_no_transitive_ordering.jsonc

File renamed without changes.

crates/vite_task_plan/tests/plan_snapshots/fixtures/depends_on_package_dependencies/snapshots/query_direct_dependency_with_task_is_selected.jsonc renamed to crates/vite_task_plan/tests/plan_snapshots/fixtures/depends_on_package_dependencies/snapshots/query_nearest_task_stops_at_dependency_with_task.jsonc

File renamed without changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// run @test/nearest-through-app#build
2+
{
3+
"packages/nearest-through-app#build": [
4+
"packages/nearest-through-bar#build"
5+
],
6+
"packages/nearest-through-bar#build": []
7+
}

docs/rfcs/depends-on-package-dependencies.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ running `app#test` means:
7070
1. Start at package `app`.
7171
2. Select its direct workspace dependencies declared in `dependencies`.
7272
3. In each selected package, run `build` if that package has a `build` task.
73+
4. If a selected package lacks `build`, walk through its matching dependencies
74+
until the nearest package or packages with `build` are found.
7375

7476
The source package itself is not selected by the object entry.
7577

@@ -97,9 +99,11 @@ In this example:
9799
- `tokens#build` is not selected by this entry because `@tokens` is not a direct dependency of `@app`.
98100
- `app#test` does not imply `app#build`; same-package dependencies use string form.
99101

100-
## Not Recursive
102+
## Nearest Task Selection
101103

102-
An object entry is not recursive. It selects only direct dependency packages.
104+
An object entry is not recursive past packages that define the requested task.
105+
It starts from direct dependency packages and stops at the nearest matching task
106+
on each dependency path.
103107

104108
In the graph above, `tokens#build` is not selected by `app#test` because `@tokens` is a dependency of `@ui`, not `@app`.
105109

@@ -121,9 +125,13 @@ flowchart LR
121125
uiTask --> tokensTask["tokens#build"]
122126
```
123127

128+
If `@ui` did not define `build`, then `tokens#build` could be selected directly
129+
from `app#test` by skipping through `@ui`.
130+
124131
## `from`
125132

126-
`from` names the package.json dependency fields used to select direct dependency packages.
133+
`from` names the package.json dependency fields used to select direct dependency packages
134+
and to walk through packages that lack the requested task.
127135

128136
```jsonc
129137
{ "task": "build", "from": "dependencies" }

0 commit comments

Comments
 (0)