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:
- Hook Installation: The
vp config --hookscommand creates the.vite-hooks/_directory structure and registers it as Git'score.hooksPath. - Pre-Commit Trigger: When
git commitexecutes, Git invokes.vite-hooks/pre-commit, which sources a shim script to locate the localvpbinary innode_modules/.bin. - Command Delegation: The hook invokes
vp staged, which the Rust CLI atcrates/vite_global_cli/src/commands/staged.rsdelegates to the JavaScript implementation. - Pipeline Execution: The JavaScript entry in
packages/cli/src/staged/bin.tsloadsvite.config.ts, extracts thestagedobject, 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'snode_modules/.bintoPATHand 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 configuresPATHand 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 stagedcommand inpackages/cli/src/staged/bin.tsserves 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.tscreates a portable shim system in.vite-hooks/_that configuresPATHbefore executing commands. - The Rust wrapper at
crates/vite_global_cli/src/commands/staged.rsdelegates to the JavaScript implementation for cross-platform consistency. - Configuration is defined in the
stagedblock ofvite.config.tsand 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →