Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Changelog

- **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"] }`.
- **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)).
- **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)).
- **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)).
- **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)).
Expand Down
6 changes: 2 additions & 4 deletions crates/vite_task/docs/task-query.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,10 +208,8 @@ Object-form entries select direct package dependencies from the declaring task:
```

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

Recursive expansion comes from dependency tasks declaring their own `dependsOn`
entries. For example, if `ui#build` also has `{ "task": "build", "from": "dependencies" }`,
Expand Down
51 changes: 4 additions & 47 deletions crates/vite_task_graph/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
use petgraph::{Direction, prelude::DiGraphMap, visit::EdgeRef};
use rustc_hash::{FxHashMap, FxHashSet};
use vite_str::Str;
use vite_workspace::PackageNodeIndex;
pub use vite_workspace::package_graph::{PackageQuery, PackageQueryResolveError};
use vite_workspace::{DependencyType, PackageNodeIndex};

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

Expand Down Expand Up @@ -166,47 +166,6 @@ impl IndexedTaskGraph {
.collect()
}

/// Resolve each package to the nearest reachable `task_name` task node.
///
/// Traversal starts from `packages`, follows package dependency edges whose
/// type is in `dependency_types`, and stops at a package once it defines the
/// requested task. This lets object-form `dependsOn` skip through direct
/// dependencies that lack the task without pulling in dependencies behind a
/// package that already has it.
fn resolve_nearest_packages_to_tasks(
&self,
packages: impl Iterator<Item = PackageNodeIndex>,
dependency_types: &[DependencyType],
task_name: &Str,
) -> FxHashMap<PackageNodeIndex, TaskNodeIndex> {
let package_graph = self.indexed_package_graph.package_graph();
let mut pkg_to_task = FxHashMap::default();
let mut seen = FxHashSet::default();
let mut frontier: Vec<_> = packages.collect();

while let Some(pkg) = frontier.pop() {
if !seen.insert(pkg) {
continue;
}
if let Some(&task_idx) = self
.node_indices_by_task_id
.get(&TaskId { package_index: pkg, task_name: task_name.clone() })
{
pkg_to_task.insert(pkg, task_idx);
continue;
}

frontier.extend(
package_graph
.edges(pkg)
.filter(|edge| dependency_types.contains(edge.weight()))
.map(|edge| edge.target()),
);
}

pkg_to_task
}

/// Map a package subgraph to a task execution graph.
///
/// For packages **with** the task: add the corresponding `TaskNodeIndex`.
Expand Down Expand Up @@ -327,15 +286,13 @@ impl IndexedTaskGraph {
let origin_package = from_task_id.package_index;
let package_graph = self.indexed_package_graph.package_graph();

// Select nearest dependency packages with `task_name`, starting from the
// origin's direct dependency packages whose edge matches one of the
// requested dependency fields.
let pkg_to_task = self.resolve_nearest_packages_to_tasks(
// Select the origin's direct dependency packages whose edge matches one of
// the requested dependency fields, mapped to their `task_name` task nodes.
let pkg_to_task = self.resolve_packages_to_tasks(
package_graph
.edges(origin_package)
.filter(|edge| entry.dependency_types.contains(edge.weight()))
.map(|edge| edge.target()),
&entry.dependency_types,
&entry.task_name,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ cwd = "packages/app"

[[plan]]
compact = true
name = "nearest_task_through_dependency_without_task"
name = "direct_dependency_without_task_is_skipped"
args = ["run", "@test/nearest-through-app#build"]

[[plan]]
compact = true
name = "nearest_task_stops_at_dependency_with_task"
name = "direct_dependency_with_task_is_selected"
args = ["run", "@test/nearest-stop-app#build"]

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

[[plan]]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// run @test/nearest-through-app#build
{
"packages/nearest-through-app#build": []
}

This file was deleted.

14 changes: 3 additions & 11 deletions docs/rfcs/depends-on-package-dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,6 @@ running `app#test` means:
1. Start at package `app`.
2. Select its direct workspace dependencies declared in `dependencies`.
3. In each selected package, run `build` if that package has a `build` task.
4. If a selected package lacks `build`, walk through its matching dependencies
until the nearest package or packages with `build` are found.

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

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

## Nearest Task Selection
## Not Recursive

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

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

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

If `@ui` did not define `build`, then `tokens#build` could be selected directly
from `app#test` by skipping through `@ui`.

## `from`

`from` names the package.json dependency fields used to select direct dependency packages
and to walk through packages that lack the requested task.
`from` names the package.json dependency fields used to select direct dependency packages.

```jsonc
{ "task": "build", "from": "dependencies" }
Expand Down
Loading