Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export default extendConfig(
{ text: 'Run', link: '/config/run' },
{ text: 'Format', link: '/config/fmt' },
{ text: 'Lint', link: '/config/lint' },
{ text: 'Check', link: '/config/check' },
{ text: 'Test', link: '/config/test' },
{ text: 'Build', link: '/config/build' },
{ text: 'Pack', link: '/config/pack' },
Expand Down
35 changes: 35 additions & 0 deletions docs/config/check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Check Config

`vp check` runs format, lint, and type checks together. The `check` block in `vite.config.ts` sets defaults for the composite command, mirroring the `--no-fmt` and `--no-lint` CLI flags.

This is useful when a project wants to keep most of the toolchain but skip one step by default. For example, a team that lints but does not format can disable `check.fmt` so a plain `vp check` (the command agents and contributors run most) only lints, without anyone needing to remember `--no-fmt`.

## Example

```ts [vite.config.ts]
import { defineConfig } from 'vite-plus';

export default defineConfig({
check: {
// Skip the format step in `vp check`. Defaults to true.
fmt: false,
// Skip lint rules in `vp check`. Type-check still runs when both
// `lint.options.typeAware` and `lint.options.typeCheck` are enabled.
// Defaults to true.
lint: true,
},
});
```

When a step is disabled here, `vp check` prints a short `note:` line so it is clear why the step did not run. With the `check.fmt: false` config above:

```bash
$ vp check
note: Format skipped (check.fmt: false in vite.config.ts)
pass: Found no warnings or lint errors in 1 file (12ms, 8 threads)
```

## Scope and precedence

- These options only affect the composite `vp check`. Standalone [`vp fmt`](/config/fmt) and [`vp lint`](/config/lint) are unaffected, so you can still run a disabled tool directly when you need it once. Note that any `vp check` invocation honors these defaults, including one run from a pre-commit hook: if your [`staged`](/config/staged) tasks call `vp check`, that step is skipped there too.
- A step is skipped if the config disables it **or** the matching CLI flag is passed. There is no flag to re-enable a step disabled in config; run `vp fmt` or `vp lint` directly instead.
2 changes: 2 additions & 0 deletions docs/config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default defineConfig({
run: {},
fmt: {},
lint: {},
check: {},
test: {},
pack: {},
staged: {},
Expand All @@ -28,6 +29,7 @@ Vite+ extends the basic Vite configuration with these additions:
- [`run`](/config/run) for Vite Task
- [`fmt`](/config/fmt) for Oxfmt
- [`lint`](/config/lint) for Oxlint
- [`check`](/config/check) for `vp check` defaults
- [`test`](/config/test) for Vitest
- [`pack`](/config/pack) for tsdown
- [`staged`](/config/staged) for staged-file checks
16 changes: 16 additions & 0 deletions docs/guide/check.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,19 @@ export default defineConfig({
},
});
```

### Disabling a step by default

To make `vp check` skip formatting or linting without passing a flag every time, set the [`check`](/config/check) block in `vite.config.ts`. This is handy when a project wants the rest of the toolchain but not, say, formatting:

```ts [vite.config.ts]
import { defineConfig } from 'vite-plus';

export default defineConfig({
check: {
fmt: false, // `vp check` lints (and type-checks) but does not format
},
});
```

These options only affect `vp check`; standalone `vp fmt` and `vp lint` still run normally. A step is skipped if it is disabled in config or the matching `--no-fmt` / `--no-lint` flag is passed. Because the defaults apply to every `vp check` run, a pre-commit hook that calls `vp check` will skip the disabled step too. See [Check config](/config/check) for the full reference.
38 changes: 28 additions & 10 deletions packages/cli/binding/src/check/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,13 @@ impl LintMessageKind {
/// analysis must be on for TypeScript diagnostics to surface.
pub(super) fn lint_config_type_check_enabled(lint_config: Option<&serde_json::Value>) -> bool {
let options = lint_config.and_then(|config| config.get("options"));
let type_aware = options
.and_then(|options| options.get("typeAware"))
.and_then(serde_json::Value::as_bool)
.unwrap_or(false);
let type_check = options
.and_then(|options| options.get("typeCheck"))
.and_then(serde_json::Value::as_bool)
.unwrap_or(false);
type_aware && type_check
json_bool(options, "typeAware", false) && json_bool(options, "typeCheck", false)
}

/// Read a boolean `key` from a JSON object, falling back to `default` when the
/// object is absent, the key is missing, or the value is not a boolean.
pub(super) fn json_bool(value: Option<&serde_json::Value>, key: &str, default: bool) -> bool {
value.and_then(|value| value.get(key)).and_then(serde_json::Value::as_bool).unwrap_or(default)
}

fn parse_check_summary(line: &str) -> Option<CheckSummary> {
Expand Down Expand Up @@ -247,7 +245,7 @@ pub(super) fn analyze_lint_output(output: &str) -> Option<Result<LintSuccess, Li
mod tests {
use serde_json::json;

use super::{LintMessageKind, lint_config_type_check_enabled};
use super::{LintMessageKind, json_bool, lint_config_type_check_enabled};

#[test]
fn lint_message_kind_defaults_to_lint_only_without_typecheck() {
Expand Down Expand Up @@ -296,6 +294,26 @@ mod tests {
}))));
}

#[test]
fn json_bool_falls_back_for_absent_or_non_bool() {
// An absent object, an absent key, or a non-bool value all use the default.
assert!(json_bool(None, "fmt", true));
assert!(json_bool(Some(&json!({})), "fmt", true));
assert!(json_bool(Some(&json!({ "lint": false })), "fmt", true));
assert!(json_bool(Some(&json!({ "fmt": "false" })), "fmt", true));
assert!(json_bool(Some(&json!({ "fmt": 0 })), "fmt", true));
assert!(json_bool(Some(&json!({ "fmt": null })), "fmt", true));
assert!(!json_bool(None, "fmt", false));
}

#[test]
fn json_bool_respects_explicit_booleans() {
assert!(!json_bool(Some(&json!({ "fmt": false })), "fmt", true));
assert!(json_bool(Some(&json!({ "fmt": true })), "fmt", false));
assert!(!json_bool(Some(&json!({ "lint": false })), "lint", true));
assert!(json_bool(Some(&json!({ "lint": true })), "lint", false));
}

#[test]
fn lint_config_type_check_requires_type_aware_prerequisite() {
assert!(!lint_config_type_check_enabled(Some(&json!({
Expand Down
25 changes: 20 additions & 5 deletions packages/cli/binding/src/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use vite_task::ExitStatus;

use self::analysis::{
LintMessageKind, analyze_fmt_check_output, analyze_lint_output, format_count, format_elapsed,
lint_config_type_check_enabled, print_error_block, print_pass_line, print_stdout_block,
print_summary_line,
json_bool, lint_config_type_check_enabled, print_error_block, print_pass_line,
print_stdout_block, print_summary_line,
};
use crate::cli::{
CapturedCommandOutput, SubcommandResolver, SynthesizableSubcommand, resolve_and_capture_output,
Expand All @@ -21,8 +21,8 @@ use crate::cli::{
pub(crate) async fn execute_check(
resolver: &SubcommandResolver,
fix: bool,
no_fmt: bool,
no_lint: bool,
no_fmt_flag: bool,
no_lint_flag: bool,
no_error_on_unmatched_pattern: bool,
paths: Vec<String>,
envs: &Arc<FxHashMap<Arc<OsStr>, Arc<OsStr>>>,
Expand All @@ -38,14 +38,29 @@ pub(crate) async fn execute_check(
let mut deferred_lint_pass: Option<(String, String)> = None;
let resolved_vite_config = resolver.resolve_universal_vite_config().await?;

// A step is skipped when either the CLI flag is passed OR `check.fmt`/
// `check.lint` is disabled in vite.config.ts. The skip note is printed only
// when CONFIG (not the CLI flag) turned a step off, so existing `--no-fmt` /
// `--no-lint` output stays byte-identical.
let config_fmt_off = !json_bool(resolved_vite_config.check.as_ref(), "fmt", true);
let config_lint_off = !json_bool(resolved_vite_config.check.as_ref(), "lint", true);
if config_fmt_off && !no_fmt_flag {
output::note("Format skipped (check.fmt: false in vite.config.ts)");
}
if config_lint_off && !no_lint_flag {
output::note("Lint skipped (check.lint: false in vite.config.ts)");
}
let no_fmt = no_fmt_flag || config_fmt_off;
let no_lint = no_lint_flag || config_lint_off;

let type_check_enabled = lint_config_type_check_enabled(resolved_vite_config.lint.as_ref());
let lint_enabled = !no_lint;
let run_lint_phase = lint_enabled || type_check_enabled;

if no_fmt && !run_lint_phase {
output::error("No checks enabled");
print_summary_line(
"Enable `lint.options.typeCheck` in vite.config.ts to use `vp check --no-fmt --no-lint` for type-check only, or drop a flag to re-enable fmt/lint.",
"Enable `lint.options.typeCheck` in vite.config.ts for type-check only, drop a `--no-fmt`/`--no-lint` flag, or re-enable `check.fmt`/`check.lint` in vite.config.ts.",
Comment thread
fengmk2 marked this conversation as resolved.
);
return Ok(ExitStatus(1));
}
Expand Down
1 change: 1 addition & 0 deletions packages/cli/binding/src/cli/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub(crate) struct ResolvedUniversalViteConfig {
pub(crate) config_file: Option<String>,
pub(crate) lint: Option<serde_json::Value>,
pub(crate) fmt: Option<serde_json::Value>,
pub(crate) check: Option<serde_json::Value>,
pub(crate) run: Option<serde_json::Value>,
}

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/snap-tests/check-all-skipped/snap.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[1]> vp check --no-fmt --no-lint
error: No checks enabled

Enable `lint.options.typeCheck` in vite.config.ts to use `vp check --no-fmt --no-lint` for type-check only, or drop a flag to re-enable fmt/lint.
Enable `lint.options.typeCheck` in vite.config.ts for type-check only, drop a `--no-fmt`/`--no-lint` flag, or re-enable `check.fmt`/`check.lint` in vite.config.ts.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "check-config-flag-precedence",
"version": "0.0.0",
"private": true
}
2 changes: 2 additions & 0 deletions packages/cli/snap-tests/check-config-flag-precedence/snap.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
> vp check --no-fmt
pass: Found no warnings or lint errors in 2 files (<variable>ms, <variable> threads)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function hello() {
return "hello";
}

export { hello };
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"env": {
"VITE_DISABLE_AUTO_INSTALL": "1"
},
"commands": ["vp check --no-fmt"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default {
check: {
fmt: true,
},
};
5 changes: 5 additions & 0 deletions packages/cli/snap-tests/check-config-no-fmt/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "check-config-no-fmt",
"version": "0.0.0",
"private": true
}
3 changes: 3 additions & 0 deletions packages/cli/snap-tests/check-config-no-fmt/snap.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
> vp check
note: Format skipped (check.fmt: false in vite.config.ts)
pass: Found no warnings or lint errors in 2 files (<variable>ms, <variable> threads)
5 changes: 5 additions & 0 deletions packages/cli/snap-tests/check-config-no-fmt/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function hello() {
return "hello";
}

export { hello };
6 changes: 6 additions & 0 deletions packages/cli/snap-tests/check-config-no-fmt/steps.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"env": {
"VITE_DISABLE_AUTO_INSTALL": "1"
},
"commands": ["vp check"]
}
5 changes: 5 additions & 0 deletions packages/cli/snap-tests/check-config-no-fmt/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default {
check: {
fmt: false,
},
};
5 changes: 5 additions & 0 deletions packages/cli/snap-tests/check-config-no-lint/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "check-config-no-lint",
"version": "0.0.0",
"private": true
}
3 changes: 3 additions & 0 deletions packages/cli/snap-tests/check-config-no-lint/snap.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
> vp check
note: Lint skipped (check.lint: false in vite.config.ts)
pass: All 4 files are correctly formatted (<variable>ms, <variable> threads)
5 changes: 5 additions & 0 deletions packages/cli/snap-tests/check-config-no-lint/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function hello() {
return "hello";
}

export { hello };
6 changes: 6 additions & 0 deletions packages/cli/snap-tests/check-config-no-lint/steps.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"env": {
"VITE_DISABLE_AUTO_INSTALL": "1"
},
"commands": ["vp check"]
}
5 changes: 5 additions & 0 deletions packages/cli/snap-tests/check-config-no-lint/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default {
check: {
lint: false,
},
};
20 changes: 20 additions & 0 deletions packages/cli/src/define-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,26 @@ declare module '@voidzero-dev/vite-plus-core' {

fmt?: OxfmtConfig;

/**
* Defaults for the `vp check` composite command. Each flag mirrors the
* matching CLI option (`--no-fmt` / `--no-lint`) and only affects
* `vp check`; standalone `vp fmt` / `vp lint` are unaffected.
*/
check?: {
/**
* Run the format step in `vp check`.
* @default true
*/
fmt?: boolean;

/**
* Run the lint step in `vp check`. Type-check still runs when both
* `lint.options.typeAware` and `lint.options.typeCheck` are enabled.
* @default true
*/
lint?: boolean;
};

pack?: PackUserConfig | PackUserConfig[];

run?: RunConfig;
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/resolve-vite-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export async function resolveUniversalViteConfig(err: null | Error, viteConfigCw
configFile: config.configFile,
lint: config.lint,
fmt: config.fmt,
check: config.check,
run: config.run,
staged: config.staged,
});
Expand Down
Loading