Skip to content

Latest commit

 

History

History
310 lines (216 loc) · 11.5 KB

File metadata and controls

310 lines (216 loc) · 11.5 KB

RFC: vp check Command

Summary

Add vp check as a built-in command that runs format verification, linting, and type checking in a single invocation. This provides a single "fast check" command for CI and local development, distinct from "slow checks" like test suites.

Motivation

Currently, running a full code quality check requires chaining multiple commands:

# From the monorepo template's "ready" script:
vp fmt && vp lint --type-aware && vp run -r test && vp run -r build

Pain points:

  • No single command for the most common pre-commit/CI check: "is my code correct?"
  • Users must remember to pass --type-aware and --type-check to lint
  • The && chaining pattern is fragile and verbose
  • No standardized "check" workflow across projects

Fast vs Slow Checks

  • Fast checks (seconds): type checking + linting + formatting — static analysis, no code execution
  • Slow checks (minutes): test suites (Vitest) — code execution

vp check targets the fast checks category. Tests are explicitly excluded — use vp test for that.

Command Syntax

# Run all fast checks (fmt --check + lint --type-aware --type-check)
vp check

# Auto-fix format and lint issues
vp check --fix
vp check --fix --no-lint    # Only fix formatting

# Disable specific checks
vp check --no-fmt
vp check --no-lint
vp check --no-type-aware
vp check --no-type-check

Options

Flag Default Description
--fix OFF Auto-fix format and lint issues
--fmt / --no-fmt ON Run format check (vp fmt --check)
--lint / --no-lint ON Run lint check (vp lint)
--type-aware / --no-type-aware ON Enable type-aware lint rules (oxlint --type-aware)
--type-check / --no-type-check ON Enable TypeScript type checking (oxlint --type-check)
--no-error-on-unmatched-pattern OFF Do not exit with error when pattern is unmatched

Flag dependency: --type-check requires --type-aware as a prerequisite.

  • --type-aware enables lint rules that use type information (e.g., no-floating-promises)
  • --type-check enables experimental TypeScript compiler-level type checking (requires type-aware)
  • If --no-type-aware is set, --type-check is also implicitly disabled

Both are enabled by default in vp check to provide comprehensive static analysis.

File Path Arguments

vp check accepts optional trailing file paths, which are passed through to fmt and lint:

# Check only specific files
vp check --fix src/index.ts src/utils.ts

When file paths are provided:

  • Paths are appended to both fmt and lint sub-commands
  • In --fix mode, --no-error-on-unmatched-pattern is implicitly enabled for both fmt and lint, preventing errors when all provided paths are excluded by ignorePatterns. This is the common lint-staged use case where staged files may not match tool-specific patterns.
  • Without --fix, unmatched patterns are reported as errors unless --no-error-on-unmatched-pattern is explicitly passed. Both oxfmt and oxlint support this flag natively.

This enables lint-staged integration:

"lint-staged": {
  "*.@(js|ts|tsx)": "vp check --fix"
}

lint-staged appends staged file paths automatically, so vp check --fix becomes e.g. vp check --fix src/a.ts src/b.ts.

Behavior

Commands run sequentially with fail-fast semantics:

1. vp fmt --check                          (verify formatting, don't auto-fix)
2. vp lint --type-aware --type-check       (lint + type checking)

If any step fails, vp check exits immediately with a non-zero exit code.

CLI Output

vp check should print completion summaries only for successful phases:

pass: All 989 files are correctly formatted (423ms, 16 threads)
pass: Found no warnings, lint errors, or type errors in 150 files (452ms, 16 threads)

Output rules:

  • Do not print delegated commands such as vp fmt --check or vp lint --type-aware --type-check
  • Print one pass: line only after a phase completes successfully
  • Mention type checks in the lint success line only when --type-check is enabled
  • On failure, print a human-readable error: line, then raw diagnostics, then a blank line and a final summary sentence
  • Treat vp check --no-fmt --no-lint as an error instead of silent success

Representative failure output:

error: Formatting issues found
src/index.js
steps.json

Found formatting issues in 2 files (105ms, 16 threads). Run `vp check --fix` to fix them.
error: Lint or type issues found
...diagnostics...

Found 3 errors and 1 warning in 2 files (452ms, 16 threads)

Decisions

Dual mode: verify and fix

By default, vp check is a read-only verification command. It never modifies files:

  • vp fmt --check reports unformatted files (doesn't auto-format)
  • vp lint --type-aware --type-check reports issues (doesn't auto-fix)

This keeps vp check safe for CI and predictable for local dev.

With --fix, vp check switches to auto-fix mode:

  • vp fmt auto-formats files
  • vp lint --fix --type-aware --type-check auto-fixes lint issues

This replaces the manual vp fmt && vp lint --fix workflow with a single command.

No tests

vp check does not run Vitest. The distinction is intentional:

  • vp check = fast static analysis (seconds)
  • vp test = test execution (minutes)

Implementation Architecture

Rust Global CLI

Add Check variant to Commands enum in crates/vite_global_cli/src/cli.rs:

#[command(disable_help_flag = true)]
Check {
    #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
    args: Vec<String>,
},

Route via delegation:

Commands::Check { args } => commands::delegate::execute(cwd, "check", &args).await,

NAPI Binding

The Check variant is defined in SynthesizableSubcommand in packages/cli/binding/src/cli.rs. The check command's orchestration logic lives in its own module at packages/cli/binding/src/check/, following the same directory-per-command pattern as exec/:

  • check/mod.rsexecute_check() orchestration (runs fmt + lint sequentially, handles --fix re-formatting)
  • check/analysis.rs — Output analysis types (CheckSummary, LintMessageKind, etc.), parsers, and formatting helpers

The check module reuses SubcommandResolver and resolve_and_capture_output from cli.rs to resolve and run the underlying fmt/lint commands.

TypeScript Side

No new resolver needed — vp check reuses existing resolve-lint.ts and resolve-fmt.ts.

Key Files

  1. crates/vite_global_cli/src/cli.rsCheck command variant and routing
  2. packages/cli/binding/src/cli.rsSynthesizableSubcommand::Check definition, delegates to check module
  3. packages/cli/binding/src/check/mod.rs — Check command orchestration (execute_check)
  4. packages/cli/binding/src/check/analysis.rs — Output parsing and analysis types

CLI Help Output

Run format, lint, and type checks

Usage: vp check [OPTIONS]

Options:
      --fmt              Run format check [default: true]
      --lint             Run lint check [default: true]
      --type-aware       Enable type-aware linting [default: true]
      --type-check       Enable TypeScript type checking [default: true]
      --no-error-on-unmatched-pattern  Do not exit with error when no files match
  -h, --help             Print help

Relationship to Existing Commands

Command Purpose Speed
vp fmt Format code (auto-fix) Fast
vp fmt --check Verify formatting Fast
vp lint Lint code Fast
vp lint --type-aware --type-check Lint + full type checking Fast
vp test Run test suite Slow
vp build Build project Slow
vp check fmt --check + lint --type-aware --type-check Fast
vp check --fix fmt + lint --fix --type-aware --type-check Fast

With vp check, the monorepo template's "ready" script simplifies to:

"ready": "vp check && vp run -r test && vp run -r build"

Caching

When vp check is used as a package.json script (e.g., "check": "vp check") and executed via vp run check, it supports task runner caching like other synthesized commands (vp build, vp lint, vp fmt).

Configuration

Enable caching in vite.config.ts:

export default {
  run: {
    cache: true,
  },
};

With caching enabled, the second vp run check replays cached output when inputs haven't changed:

$ vp check ◉ cache hit, replaying
pass: All 4 files are correctly formatted (105ms, 16 threads)
pass: Found no warnings or lint errors in 2 files (452ms, 16 threads)

Cache key

The check command's cache fingerprint includes:

  • Environment variable: OXLINT_TSGOLINT_PATH (affects lint behavior)
  • Input files: Auto-tracked via fspy, excluding:
    • node_modules/.vite-temp/** — config compilation cache (read+written by the vp CLI subprocess)
    • node_modules/.vite/task-cache/** — task runner state files that change after each run

These exclusions are defined by check_cache_inputs() in cli.rs.

How it differs from vp fmt / vp lint

When vp fmt or vp lint appear in task scripts, the command handler resolves them to their underlying binaries (e.g., node path/to/oxfmt.mjs). The vp check command is different — it runs as a full vp check subprocess because it's a composite command that orchestrates both fmt and lint internally. This means the vp CLI process itself is tracked by fspy, which is why the .vite-temp and .vite/task-cache exclusions are necessary.

Comparison with Other Tools

Tool Scope
cargo check Type checking only
cargo clippy Lint only
biome check Format + lint (closest analog)
deno check Type checking only

Snap Tests

packages/cli/snap-tests/check-basic/
  package.json
  steps.json     # { "steps": [{ "command": "vp check" }] }
  src/index.ts   # Clean file that passes all checks
  snap.txt

packages/cli/snap-tests/check-fmt-fail/
  package.json
  steps.json     # { "steps": [{ "command": "vp check" }] }
  src/index.ts   # Badly formatted file
  snap.txt       # Shows fmt --check failure, lint doesn't run (fail-fast)

packages/cli/snap-tests/check-no-fmt/
  package.json
  steps.json     # { "steps": [{ "command": "vp check --no-fmt" }] }
  snap.txt       # Only lint runs

packages/cli/snap-tests/check-cache-enabled/
  package.json   # { "scripts": { "check": "vp check" } }
  vite.config.ts # { run: { cache: true } }
  steps.json     # Runs vp run check twice, expects cache hit on second run
  src/index.js
  snap.txt