How Vite+ Handles Task Dependency Resolution with the Topological Flag in Monorepos
Vite+ automatically resolves task dependencies in monorepos by building a directed workspace graph from package.json relationships and applying topological sorting to ensure dependencies execute before dependents when running commands with --recursive or --filter.
The voidzero-dev/vite-plus toolchain introduces sophisticated task orchestration for JavaScript monorepos through its topological dependency resolution system. When executing commands across multiple packages, Vite+ analyzes workspace relationships defined in package.json to determine the optimal execution order. This ensures that build artifacts from dependency packages are available before dependent packages begin their tasks.
Workspace Graph Construction and Implicit Dependencies
Vite+ builds a workspace graph that represents each package as a node and the package.json relationships (dependencies, devDependencies, peerDependencies, etc.) as directed edges. When a command is run with --recursive or --filter, Vite+ must decide in which order to execute the selected packages.
Implicit Dependencies in Topological Mode
When the topological flag is enabled (the default for recursive runs), every workspace-to-workspace dependency edge is converted into an implicit task dependency. According to the project documentation in CLAUDE.md (lines 46-53), if package A depends on package B, then A#build automatically depends on B#build.
This behavior ensures that:
- Build tasks in dependency packages complete before downstream packages start
- Dev dependencies are prepared before their consumers execute
- Peer dependency constraints are respected during task execution
Default Topological Ordering for Recursive Commands
When executing vp exec -r … or vp run -r …, Vite+ automatically applies topological ordering. The RFC documented in rfcs/exec-command.md (lines 11-19) specifies that the topological sort uses petgraph::algo::toposort on the FilterResolution.package_subgraph, ensuring that dependencies are executed before dependents.
Implementation of the Topological Sort Algorithm
The core resolution logic resides in packages/cli/binding/src/exec/workspace.rs, where Vite+ implements a robust sorting mechanism with cycle detection.
Building the Workspace Graph
The function vite_workspace::load_package_graph constructs a DiGraphMap<PackageNodeIndex, ()> that stores all packages and their intra-workspace dependency edges. This directed graph serves as the foundation for all subsequent topological operations.
Subgraph Extraction and Filtering
After the user applies filters (--recursive, --filter, or defaults to the current package), the FilterResolution struct contains a package_subgraph field. This subgraph includes only the selected nodes and the edges relevant to those nodes, allowing targeted execution within large monorepos.
The topological_sort_packages Function
The critical sorting logic appears in lines 321-335 of workspace.rs. The topological_sort_packages function implements a two-phase approach:
fn topological_sort_packages(subgraph: &DiGraphMap<PackageNodeIndex, ()>) -> Vec<PackageNodeIndex> {
match petgraph::algo::toposort(subgraph, None) {
Ok(mut sorted) => {
sorted.reverse(); // deps-first order
sorted
}
Err(_cycle) => {
// Fallback for cycles – SCCs in reverse topological order
petgraph::algo::tarjan_scc(subgraph).into_iter().flatten().collect()
}
}
}
The function first attempts petgraph::algo::toposort, which returns nodes in dependents-first order. Vite+ reverses this list to ensure dependencies appear first. If the graph contains a cycle, the algorithm falls back to Tarjan's strongly-connected-components algorithm, yielding SCCs in reverse topological order to preserve correct dependencies-first ordering for everything outside the cycle.
Handling Circular Dependencies
When toposort detects a cycle, Vite+ does not fail. Instead, it uses petgraph::algo::tarjan_scc to identify strongly connected components. The RFC documentation explains that this preserves a correct dependencies-first order for acyclic portions of the graph, while the intra-cycle order is left arbitrary since no linear order can satisfy a circular dependency.
CLI Flags for Execution Order Control
Vite+ provides several flags to override the default topological behavior when running tasks across multiple packages.
Running Tasks in Parallel
The --parallel flag (defined in packages/cli/binding/src/exec/args.rs) skips topological ordering entirely and spawns all selected packages concurrently. This is useful for tasks that do not depend on build artifacts, though output may be interleaved:
# Execute command in all packages simultaneously
vp exec -r --parallel -- node -e "console.log(process.env.VITE_PLUS_PACKAGE_NAME)"
Reverse Topological Order
The --reverse flag reverses the computed topological order, causing dependents to execute before their dependencies. This is particularly useful for cleanup or teardown scripts:
# Run dependents first, then dependencies
vp exec -r --reverse -- node -e "console.log(process.env.VITE_PLUS_PACKAGE_NAME)"
Disabling Implicit Dependencies
The --no-topological flag (available on vp run commands) disables the creation of implicit dependencies from package.json relationships. When used, only explicit task dependencies defined in vite-task.json are honored:
# Ignore workspace dependencies, use only vite-task.json dependsOn
vp run build -r --no-topological
Practical Examples
The following commands demonstrate common topological execution patterns in Vite+ monorepos:
# Run a command in every workspace package, respecting package.json deps
vp exec -r -- node -e "console.log(process.env.VITE_PLUS_PACKAGE_NAME)"
# Packages are invoked in dependency-first order (e.g., lib → app)
# Reverse the order – useful for teardown scripts
vp exec -r --reverse -- node -e "console.log(process.env.VITE_PLUS_PACKAGE_NAME)"
# Dependents run first, then their dependencies
# Execute in parallel – ignore topological order
vp exec -r --parallel -- node -e "console.log(process.env.VITE_PLUS_PACKAGE_NAME)"
Summary
- Vite+ constructs a directed workspace graph from
package.jsondependencies to model package relationships. - The
topological_sort_packagesfunction inpackages/cli/binding/src/exec/workspace.rsusespetgraph::algo::toposortto establish a dependencies-first execution order. - When cycles are detected, the system falls back to Tarjan's SCC algorithm to maintain correct ordering for acyclic portions of the graph.
- The
--recursiveflag enables topological ordering by default, while--parallel,--reverse, and--no-topologicalprovide explicit control over execution semantics. - Implicit task dependencies are automatically derived from workspace package relationships unless explicitly disabled.
Frequently Asked Questions
What happens when Vite+ detects a circular dependency in the workspace?
When petgraph::algo::toposort detects a cycle in the workspace graph, Vite+ falls back to petgraph::algo::tarjan_scc as implemented in packages/cli/binding/src/exec/workspace.rs. This algorithm identifies strongly connected components and emits them in reverse topological order, preserving correct dependencies-first ordering for packages outside the cycle while handling the circular portion as a single unit.
How does the topological flag interact with explicit task dependencies in vite-task.json?
The topological flag creates implicit dependencies based on package.json relationships, while vite-task.json defines explicit dependencies through the dependsOn field. By default, both are respected. However, using --no-topological on vp run commands disables implicit dependencies, causing Vite+ to honor only the explicit dependsOn entries defined in vite-task.json.
Can I run dependent packages before their dependencies in Vite+?
Yes. Passing the --reverse flag to vp exec or vp run reverses the topological order, causing dependents to execute before their dependencies. This is useful for cleanup tasks, cache invalidation, or any operation where you need to process packages in the opposite direction of their dependency graph.
Does topological sorting apply to all Vite+ commands by default?
No. Topological sorting is automatically applied only when using --recursive (-r) or --filter flags that select multiple packages. Single-package execution does not trigger the topological sort. Additionally, you can explicitly disable topological ordering with --parallel (for concurrent execution) or --no-topological (to ignore workspace dependencies).
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →