# Vite+ CLI Command Delegation System: Two-Layer Architecture Explained

> Explore Vite+ CLI command delegation architecture. Learn how it routes commands locally or via package managers for efficient project command execution. Discover the two-layer system.

- Repository: [VoidZero/vite-plus](https://github.com/voidzero-dev/vite-plus)
- Tags: architecture
- Published: 2026-03-16

---

**Vite+ uses a two-layer delegation architecture that automatically routes commands to a project-local installation when available, falling back to package manager execution when no local dependency is detected.**

The `voidzero-dev/vite-plus` repository implements a sophisticated command routing mechanism that determines at runtime whether to execute commands through the global `vp` binary or delegate to a project-specific installation. This architecture ensures that developers always use the correct Vite+ version for their project while maintaining a seamless global CLI experience. The system is implemented in Rust within the `vite_global_cli` crate and handles Node.js runtime resolution, environment setup, and package manager fallbacks automatically.

## How the Delegation Decision Works

The core logic resides in [`crates/vite_global_cli/src/commands/run_or_delegate.rs`](https://github.com/voidzero-dev/vite-plus/blob/main/crates/vite_global_cli/src/commands/run_or_delegate.rs), where the CLI evaluates the project context before executing any command.

### Detecting Local Vite+ Dependencies

The system begins by walking up the directory tree to locate a [`package.json`](https://github.com/voidzero-dev/vite-plus/blob/main/package.json) containing `vite-plus` in either `dependencies` or `devDependencies`. This detection happens in the `has_vite_plus_dependency` function within [`crates/vite_global_cli/src/commands/mod.rs`](https://github.com/voidzero-dev/vite-plus/blob/main/crates/vite_global_cli/src/commands/mod.rs) (lines 48-66).

When a local dependency is found, the CLI delegates execution to the project's own Vite+ installation rather than using the global binary.

### The Delegation Flow

If `has_vite_plus_dependency` returns true, the command flows through `delegate::execute` in [`crates/vite_global_cli/src/commands/delegate.rs`](https://github.com/voidzero-dev/vite-plus/blob/main/crates/vite_global_cli/src/commands/delegate.rs) (lines 10-19). The heavy lifting occurs in `JsExecutor::delegate_to_local_cli`, which:

- Resolves the project-specific Node.js runtime using `ensure_project_runtime` in [`crates/vite_global_cli/src/js_executor.rs`](https://github.com/voidzero-dev/vite-plus/blob/main/crates/vite_global_cli/src/js_executor.rs) (lines 38-48)
- Locates the local `vite-plus` package using `oxc_resolver` via `resolve_local_vite_plus`
- Executes the local [`dist/bin.js`](https://github.com/voidzero-dev/vite-plus/blob/main/dist/bin.js) entry point through `run_js_entry` (lines 66-86)

## Runtime Management and Node.js Resolution

Vite+ maintains separate runtime contexts for global and project-local execution, ensuring version compatibility across different projects.

### Project-Specific vs. Global Runtimes

The `JsExecutor` struct caches two distinct runtimes:

- **CLI runtime**: Used for global commands and internal operations, resolved from the CLI's own [`package.json`](https://github.com/voidzero-dev/vite-plus/blob/main/package.json) (`devEngines.runtime`)
- **Project runtime**: Used when delegating to local installations, respecting project-specific configuration files like `.node-version`, `engines.node` in [`package.json`](https://github.com/voidzero-dev/vite-plus/blob/main/package.json), or `devEngines.runtime`

This separation allows each project to pin its own Node.js version while the global CLI operates independently.

### Environment Variable Injection

Before spawning processes, the executor prepares the environment through `create_js_command`. It injects `VITE_PLUS_CLI_BIN` (pointing to the `vp` binary path) and prepends the selected Node binary's directory to `PATH` using `vite_shared::prepend_to_path_env` with deduplication options (`PrependOptions { dedupe_anywhere: true }`).

## Package Manager Fallback Strategy

When no local Vite+ dependency exists, the system falls back to standard package manager execution. This path, implemented in [`run_or_delegate.rs`](https://github.com/voidzero-dev/vite-plus/blob/main/run_or_delegate.rs) (lines 13-22), prepends the managed Node runtime's binary directory to `PATH` via `prepend_js_runtime_to_path_env`, then constructs a `PackageManager` instance through `commands::build_package_manager` ([`mod.rs`](https://github.com/voidzero-dev/vite-plus/blob/main/mod.rs), lines 84-119) to execute commands like `npm run build` or `pnpm run dev`.

## Practical Command Examples

The following examples demonstrate how the delegation system behaves in different project contexts.

When running inside a project with `vite-plus` as a dependency:

```bash
vp run dev

```

The flow proceeds through `run_or_delegate::execute` → `has_vite_plus_dependency` detects the dependency → `delegate::execute` → `JsExecutor::delegate_to_local_cli` executes the project's [`dist/bin.js`](https://github.com/voidzero-dev/vite-plus/blob/main/dist/bin.js) with its specific Node runtime.

In a plain npm project without Vite+:

```bash
vp run build

```

Here, `has_vite_plus_dependency` returns false, triggering `prepend_js_runtime_to_path_env` to modify `PATH`, followed by `PackageManager::run_script_command` executing `npm run build`.

For internal operations requiring explicit global delegation:

```rust
use vite_global_cli::commands::delegate;

async fn call_global() -> Result<ExitStatus, Error> {
    delegate::execute_global(
        AbsolutePathBuf::new("/my/project".into())?,
        "version",
        &[]
    ).await
}

```

This uses `JsExecutor::delegate_to_global_cli`, which always runs the bundled [`dist/bin.js`](https://github.com/voidzero-dev/vite-plus/blob/main/dist/bin.js) with the CLI's own Node runtime regardless of project configuration.

## Summary

- **Two-layer architecture**: Vite+ automatically chooses between project-local and global execution contexts based on dependency detection in [`package.json`](https://github.com/voidzero-dev/vite-plus/blob/main/package.json).
- **Smart runtime resolution**: The system respects `.node-version`, `engines.node`, and `devEngines.runtime` configurations when selecting Node.js versions for local projects.
- **Automatic fallback**: When no local Vite+ installation exists, commands transparently fall back to the system's package manager with proper `PATH` configuration.
- **Environment isolation**: Each execution context receives appropriate environment variables including `VITE_PLUS_CLI_BIN` and modified `PATH` settings through `vite_shared::prepend_to_path_env`.

## Frequently Asked Questions

### How does Vite+ detect whether to use a local or global installation?

The CLI walks up the directory tree from the current working directory looking for a [`package.json`](https://github.com/voidzero-dev/vite-plus/blob/main/package.json) that lists `vite-plus` in `dependencies` or `devDependencies` using the `has_vite_plus_dependency` function in [`crates/vite_global_cli/src/commands/mod.rs`](https://github.com/voidzero-dev/vite-plus/blob/main/crates/vite_global_cli/src/commands/mod.rs). If found, it delegates to the local installation via `JsExecutor::delegate_to_local_cli`; otherwise, it falls back to package manager execution.

### What Node.js version does Vite+ use when delegating to a local project?

When delegating locally, Vite+ resolves the project-specific Node.js runtime through `ensure_project_runtime` in [`js_executor.rs`](https://github.com/voidzero-dev/vite-plus/blob/main/js_executor.rs) (lines 38-48), checking `.node-version`, `engines.node` in [`package.json`](https://github.com/voidzero-dev/vite-plus/blob/main/package.json), `devEngines.runtime`, or a user-configured default. This project runtime is cached separately from the global CLI runtime within the `JsExecutor` struct.

### How does the global CLI handle projects that don't have Vite+ installed?

If no local `vite-plus` dependency is detected, the system falls back to executing commands through the detected package manager (npm, pnpm, yarn, etc.). It prepends the managed Node runtime's binary directory to `PATH` via `prepend_js_runtime_to_path_env` and runs the equivalent package manager command, as implemented in [`run_or_delegate.rs`](https://github.com/voidzero-dev/vite-plus/blob/main/run_or_delegate.rs) (lines 13-22).

### What environment variables does Vite+ set during command execution?

The CLI sets `VITE_PLUS_CLI_BIN` to the absolute path of the `vp` binary and ensures the selected Node.js binary's directory is prepended to `PATH` using `vite_shared::prepend_to_path_env` with deduplication enabled (`PrependOptions { dedupe_anywhere: true }`), ensuring spawned processes can locate the correct runtime.