|
21 | 21 | use petgraph::{Direction, prelude::DiGraphMap, visit::EdgeRef}; |
22 | 22 | use rustc_hash::{FxHashMap, FxHashSet}; |
23 | 23 | use vite_str::Str; |
24 | | -use vite_workspace::PackageNodeIndex; |
25 | 24 | pub use vite_workspace::package_graph::{PackageQuery, PackageQueryResolveError}; |
| 25 | +use vite_workspace::{DependencyType, PackageNodeIndex}; |
26 | 26 |
|
27 | 27 | use crate::{IndexedTaskGraph, PackageDependencyEntry, TaskDependencyType, TaskId, TaskNodeIndex}; |
28 | 28 |
|
@@ -166,6 +166,47 @@ impl IndexedTaskGraph { |
166 | 166 | .collect() |
167 | 167 | } |
168 | 168 |
|
| 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 | + |
169 | 210 | /// Map a package subgraph to a task execution graph. |
170 | 211 | /// |
171 | 212 | /// For packages **with** the task: add the corresponding `TaskNodeIndex`. |
@@ -286,13 +327,15 @@ impl IndexedTaskGraph { |
286 | 327 | let origin_package = from_task_id.package_index; |
287 | 328 | let package_graph = self.indexed_package_graph.package_graph(); |
288 | 329 |
|
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( |
292 | 334 | package_graph |
293 | 335 | .edges(origin_package) |
294 | 336 | .filter(|edge| entry.dependency_types.contains(edge.weight())) |
295 | 337 | .map(|edge| edge.target()), |
| 338 | + &entry.dependency_types, |
296 | 339 | &entry.task_name, |
297 | 340 | ); |
298 | 341 |
|
|
0 commit comments