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

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, 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 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 (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 (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 (lines 38-48)
  • Locates the local vite-plus package using oxc_resolver via resolve_local_vite_plus
  • Executes the local 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 (devEngines.runtime)
  • Project runtime: Used when delegating to local installations, respecting project-specific configuration files like .node-version, engines.node in 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 (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, 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:

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 with its specific Node runtime.

In a plain npm project without Vite+:

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:

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 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.
  • 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 that lists vite-plus in dependencies or devDependencies using the has_vite_plus_dependency function in 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 (lines 38-48), checking .node-version, engines.node in 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 (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.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →