How Vite+ Implements the `--no-*` Boolean Flag Pattern in Its CLI
Vite+ defines explicit negative flags (e.g., --no-fmt, --no-lint) using Clap's derive macros, parsing them into standard bool fields that command executors check to conditionally skip specific tooling steps.
The Vite+ monorepo provides a unified CLI for formatting, linting, and type-checking. To give users granular control over which checks run, the toolkit implements a consistent negatable flag pattern across its Rust-based command structure. This approach leverages the Clap crate to declare --no-* variants explicitly, ensuring type-safe boolean parsing and automatic help generation without complex custom logic.
Declaring Negatable Flags with Clap Derive Macros
Vite+ defines CLI arguments through Rust structs using Clap's derive API. For any feature that users might want to disable, the codebase declares an explicit negative flag using the #[arg(long = "no-<name>")] attribute. This creates a standard boolean field that defaults to false and flips to true only when the flag appears on the command line.
In packages/cli/binding/src/cli.rs, the Check command variant demonstrates this pattern for disabling the formatter and linter:
/// Run format, lint, and type checks
Check {
/// Auto-fix format and lint issues
#[arg(long)]
fix: bool,
/// Skip format check
#[arg(long = "no-fmt")]
no_fmt: bool,
/// Skip lint check
#[arg(long = "no-lint")]
no_lint: bool,
/// File paths to check (passed through to fmt and lint)
#[arg(trailing_var_arg = true)]
paths: Vec<String>,
},
Source: packages/cli/binding/src/cli.rs (lines 112–119)【112†L112-L119】
By declaring no_fmt and no_lint as distinct boolean fields rather than using optional value parsing (e.g., --fmt=false), Vite+ ensures that the generated argument parser treats these as simple presence flags. This eliminates ambiguity and allows Clap to enforce correct usage automatically.
Handling --no-* Flags in Command Execution
The command dispatcher reads these boolean fields to determine which subsystems to execute. In packages/cli/binding/src/cli.rs, the execute_direct_subcommand function matches on the Check variant and implements validation logic to prevent users from disabling all available checks simultaneously.
SynthesizableSubcommand::Check { fix, no_fmt, no_lint, paths } => {
if no_fmt && no_lint {
output::error("No checks enabled");
print_summary_line(
"`vp check` did not run because both `--no-fmt` and `--no-lint` were set",
);
return Ok(ExitStatus(1));
}
// ... later in the function ...
if !no_fmt {
// run formatting step
}
if !no_lint {
// run linting step
}
}
Source: packages/cli/binding/src/cli.rs (lines 1010–1030)【1010†L1010-L1030】
This implementation pattern separates argument parsing from business logic. The Clap-generated code handles string-to-bool conversion and validation (e.g., rejecting unknown flags), while the executor function simply branches on the boolean state. When both --no-fmt and --no-lint are present, the function uses output::error from crates/vite_shared/src/output.rs to print a diagnostic message and returns ExitStatus(1) to signal failure.
Help Output and User Interface
Because Clap derives the argument parser from struct definitions, it automatically generates help text entries for each --no-* flag. Vite+ augments this standard output with a unified help formatter located in crates/vite_global_cli/src/help.rs, which ensures consistent styling across the CLI surface.
The generated help entries appear alongside other options:
--no-fmt Skip format check
--no-lint Skip lint check
Source: crates/vite_global_cli/src/help.rs (row definition)【760†L760-L762】
This approach guarantees that documentation stays synchronized with code. When developers add new negatable flags to the struct, Clap updates the help output automatically, requiring no manual edits to string tables or markdown files.
Summary
- Explicit declaration: Negatable flags use
#[arg(long = "no-*")]attributes in Clap derive macros, creating clear, self-documenting argument names. - Boolean parsing: Flags map directly to
boolfields (true when present, false otherwise), avoiding complex value parsing logic. - Conditional execution: Command executors in
packages/cli/binding/src/cli.rscheck these booleans to skip specific tooling steps. - Validation logic: The implementation prevents invalid state combinations (e.g., disabling all checks) with early exits and descriptive error messages.
- Automatic documentation: Clap generates help text from struct definitions, ensuring flags appear correctly in
--helpoutput without manual maintenance.
Frequently Asked Questions
Why does Vite+ use --no-* flags instead of --flag=false?
Explicit negative flags provide unambiguous UX and avoid parsing edge cases. Clap handles --no-fmt as a simple boolean switch, whereas --fmt=false would require value parsing and potential type conversion errors. The --no-* pattern also aligns with conventions established by tools like Git and npm.
Can multiple --no-* flags be combined in a single command?
Yes, users can combine flags (e.g., vp check --no-fmt --no-lint). However, the executor validates that at least one check remains enabled. If both formatting and linting are disabled, the CLI prints "No checks enabled" and exits with status code 1, preventing no-op executions.
How does Clap generate help text for these negatable flags?
Clap introspects the struct field names and #[arg] attributes to build the help table automatically. The long = "no-fmt" string becomes the documented flag name, while the doc comment above the field becomes the description. Custom formatting in crates/vite_global_cli/src/help.rs ensures consistent indentation and styling across all subcommands.
Where is the validation logic for conflicting negatable flags?
The validation resides in the execute_direct_subcommand function within packages/cli/binding/src/cli.rs. Specifically, the code checks if no_fmt && no_lint immediately after destructuring the command variant, returning an ExitStatus(1) before any tooling processes execute.
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 →