How the `vp staged` Command Works with Git Pre-Commit Hooks in Vite-Plus

The vp staged command executes the lint-staged pipeline defined in your vite.config.ts and serves as the entry point for Git pre-commit hooks in voidzero-dev/vite-plus projects.

When working with the Vite-Plus toolchain, the vp staged command bridges Git's native hook mechanism with your project's linting configuration. This integration allows the same staged-file processing to run both manually via CLI and automatically during the Git commit workflow, ensuring consistent code quality checks across your development team.

The Complete Execution Flow

The integration between Git hooks and Vite-Plus follows a deterministic sequence from installation to execution:

  1. Hook Installation: The vp config --hooks command creates the .vite-hooks/_ directory structure and registers it as Git's core.hooksPath.
  2. Pre-Commit Trigger: When git commit executes, Git invokes .vite-hooks/pre-commit, which sources a shim script to locate the local vp binary in node_modules/.bin.
  3. Command Delegation: The hook invokes vp staged, which the Rust CLI at crates/vite_global_cli/src/commands/staged.rs delegates to the JavaScript implementation.
  4. Pipeline Execution: The JavaScript entry in packages/cli/src/staged/bin.ts loads vite.config.ts, extracts the staged object, and passes it to the lint-staged programmatic API.

Hook Installation and Shim Creation

In packages/cli/src/config/hooks.ts, the installation process determines the Git root using git rev-parse --show-prefix, then writes two critical components to .vite-hooks/_/:

  • A central shim script (h) that prepends the project's node_modules/.bin to PATH and executes the actual hook script
  • Individual hook files (e.g., pre-commit) that source the shim via . "$(dirname "$0")/h"

This architecture ensures the hooks remain portable across different environments while always resolving to the project's local Vite-Plus installation. The shim also sources optional initialization files from ~/.config/vite-plus/hooks-init.sh for user-level customization.

Pre-Commit Hook Execution

When Git triggers the pre-commit hook, the shim at .vite-hooks/h resolves the real script at .vite-hooks/_/pre-commit and executes it via sh -e "$s" "$@". The generated pre-commit script is intentionally minimal:

#!/usr/bin/env sh
. "$(dirname "$0")/h"

This indirection allows the shim to configure the environment—including the critical PATH modification—before delegating to the logic that ultimately calls vp staged.

Configuration Resolution

The resolveViteConfig() function in packages/cli/src/resolve-vite-config.ts loads the project's vite.config.ts and extracts the staged configuration block. This object follows the standard lint-staged format, mapping glob patterns to shell commands that will execute against staged files only.

Lint-Staged Pipeline Execution

The main implementation in packages/cli/src/staged/bin.ts parses CLI flags using mri, constructs a lint-staged Configuration object from the viteConfig.staged property, and invokes the programmatic API:

const success = await lintStaged(options);
process.exit(success ? 0 : 1);

The options object supports all lint-staged flags including --allow-empty, --concurrent, and --debug. If any configured task fails, the process exits with code 1, aborting the Git commit.

Deep Dive into Hook Architecture

The .vite-hooks Directory Structure

The installation creates a hidden directory structure that separates the portable shim from concrete hook implementations:

  • .vite-hooks/h: The central shim that configures PATH and executes target scripts
  • .vite-hooks/_/pre-commit: The concrete pre-commit script generated by Vite-Plus
  • .vite-hooks/_/pre-push: Additional hooks as needed for other Git lifecycle events

This design allows users to maintain custom initialization logic while keeping repository-specific hooks functional regardless of where the repository is cloned.

Rust-to-JavaScript Command Delegation

The Rust CLI wrapper at crates/vite_global_cli/src/commands/staged.rs provides a thin forwarding layer that ensures consistent behavior across platforms:

pub async fn execute(cwd: AbsolutePathBuf, args: &[String]) -> Result<ExitStatus, Error> {
    super::delegate::execute(cwd, "staged", args).await
}

This delegates to the Node.js-based CLI implementation, maintaining a unified interface whether running through Git hooks or directly via the command line.

Configuring the Staged Pipeline in vite.config.ts

The staged block in your Vite configuration defines which commands run against which staged files:

import { defineConfig } from 'vite-plus';

export default defineConfig({
  staged: {
    '*.{js,ts,tsx,vue,svelte}': 'vp check --fix',
    '*.css': 'stylelint --fix',
  },
});

Each glob pattern maps to a shell command that lint-staged executes only on files that Git reports as staged. The configuration supports multiple patterns, command chaining with &&, and all standard lint-staged features.

Practical Usage Examples

Installing Git Hooks

Initialize the hook infrastructure once per repository:

vp config --hooks

This creates .vite-hooks/_, sets git config core.hooksPath .vite-hooks/_, and generates the shim and hook scripts required for automatic execution.

Running Staged Checks Manually

Debug the pipeline without triggering a commit:

vp staged --debug

This executes the same lint-staged configuration that the pre-commit hook uses, loading the staged block from vite.config.ts and reporting which files match your configured globs.

Typical Commit Workflow

git add src/**/*.ts
git commit -m "Apply lint fixes"

The commit triggers .vite-hooks/pre-commit → shim → vp staged → lint-staged. If vp check --fix modifies files or exits with an error, the commit aborts and must be retried after restaging the fixes.

Summary

  • The vp staged command in packages/cli/src/staged/bin.ts serves as the primary entry point for running lint-staged against Git staged files in Vite-Plus projects.
  • Hook installation via packages/cli/src/config/hooks.ts creates a portable shim system in .vite-hooks/_ that configures PATH before executing commands.
  • The Rust wrapper at crates/vite_global_cli/src/commands/staged.rs delegates to the JavaScript implementation for cross-platform consistency.
  • Configuration is defined in the staged block of vite.config.ts and passed directly to the lint-staged programmatic API.
  • Exit codes propagate to Git, aborting commits when linting fails and allowing them when all tasks succeed.

Frequently Asked Questions

What is the .vite-hooks directory and why is it hidden?

The .vite-hooks directory contains the shim scripts and concrete hook implementations that Vite-Plus installs when you run vp config --hooks. It is prefixed with a dot to indicate it contains generated infrastructure rather than user-editable code, while the _ subdirectory contains the actual executable scripts that source the central shim.

Can I use vp staged without installing Git hooks?

Yes. Running vp staged manually from the command line executes the same lint-staged pipeline defined in your vite.config.ts without requiring Git hooks to be installed. This is useful for testing configurations or running checks on demand before attempting a commit.

How does vp staged differ from running lint-staged directly?

vp staged automatically resolves your vite.config.ts and extracts the staged configuration block, passing it as inline options to lint-staged. This eliminates the need for a separate .lintstagedrc file and ensures your linting configuration lives alongside your other Vite tooling in a single configuration file.

Where does the pre-commit hook script get generated?

The pre-commit script is generated in packages/cli/src/config/hooks.ts and written to .vite-hooks/_/pre-commit. This script simply sources the shim h, which then configures the environment to find the local vp binary before executing the actual pre-commit logic.

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 →