Skip to content

Commit 189d2b3

Browse files
authored
refactor: move typeAware/typeCheck from CLI flags to vite.config.ts (#739)
oxlint natively reads `options.typeAware` and `options.typeCheck` from its config file, so there's no need for `vp check` to inject these as CLI flags when calling `vp lint`. - Remove `--no-type-aware` / `--no-type-check` flags from `vp check` - Add `options: { typeAware: true, typeCheck: true }` defaults in `vp lint --init`, `vp migrate`, and project root vite.config.ts - Strip `--type-aware` / `--type-check` from migrated scripts - Remove `oxc.typeAware` from VS Code settings (extension reads from config automatically) - Add `OxlintOptions` type with `typeAware` and `typeCheck` fields
1 parent 9121608 commit 189d2b3

44 files changed

Lines changed: 387 additions & 201 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 105 additions & 106 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/vite_global_cli/src/help.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -722,8 +722,6 @@ fn delegated_help_doc(command: &str) -> Option<HelpDoc> {
722722
row("--fix", "Auto-fix format and lint issues"),
723723
row("--no-fmt", "Skip format check"),
724724
row("--no-lint", "Skip lint check"),
725-
row("--no-type-aware", "Disable type-aware linting"),
726-
row("--no-type-check", "Disable TypeScript type checking"),
727725
row("-h, --help", "Print help"),
728726
],
729727
),

crates/vite_migration/src/package.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,13 @@ fn rewrite_script(script: &str, rules: &[RuleConfig<SupportLang>]) -> String {
2626
let result = ast_grep::apply_loaded_rules(&preprocessed, rules);
2727

2828
// Step 3: Replace cross-env marker back with "cross-env " (only if we replaced it)
29-
if has_cross_env { result.replace(CROSS_ENV_MARKER, CROSS_ENV_REPLACEMENT) } else { result }
29+
let result = if has_cross_env {
30+
result.replace(CROSS_ENV_MARKER, CROSS_ENV_REPLACEMENT)
31+
} else {
32+
result
33+
};
34+
35+
result
3036
}
3137

3238
/// Transform all script strings in a JSON object using the provided function.

crates/vite_migration/src/vite_config.rs

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
use std::path::Path;
1+
use std::{borrow::Cow, path::Path, sync::LazyLock};
22

33
use ast_grep_config::{GlobalRules, RuleConfig, from_yaml_string};
44
use ast_grep_language::{LanguageExt, SupportLang};
5+
use regex::Regex;
56
use vite_error::Error;
67

78
use crate::ast_grep;
@@ -100,15 +101,33 @@ fn merge_json_config_content(
100101
// Check if the config uses a function callback (for informational purposes)
101102
let uses_function_callback = check_function_callback(vite_config_content)?;
102103

104+
// Strip "$schema" property — it's a JSON Schema annotation not valid in OxlintConfig
105+
let ts_config = strip_schema_property(ts_config);
106+
103107
// Generate the ast-grep rules with the actual config
104-
let rule_yaml = generate_merge_rule(ts_config, config_key);
108+
let rule_yaml = generate_merge_rule(&ts_config, config_key);
105109

106110
// Apply the transformation
107111
let (content, updated) = ast_grep::apply_rules(vite_config_content, &rule_yaml)?;
108112

109113
Ok(MergeResult { content, updated, uses_function_callback })
110114
}
111115

116+
/// Regex to match `"$schema": "..."` lines (with optional trailing comma).
117+
static RE_SCHEMA: LazyLock<Regex> =
118+
LazyLock::new(|| Regex::new(r#"(?m)^\s*"\$schema"\s*:\s*"[^"]*"\s*,?\s*\n"#).unwrap());
119+
120+
/// Strip the `"$schema"` property from a JSON/JSONC config string.
121+
///
122+
/// JSON config files (`.oxlintrc.json`, `.oxfmtrc.json`) often contain a
123+
/// `"$schema"` annotation that is meaningful only for editor validation.
124+
/// When the JSON content is embedded into `vite.config.ts`, the `$schema`
125+
/// property causes a TypeScript type error because it is not part of
126+
/// `OxlintConfig` / `OxfmtConfig`.
127+
fn strip_schema_property(config: &str) -> Cow<'_, str> {
128+
RE_SCHEMA.replace_all(config, "")
129+
}
130+
112131
/// Check if the vite config uses a function callback pattern
113132
fn check_function_callback(vite_config_content: &str) -> Result<bool, Error> {
114133
// Match both sync and async arrow functions
@@ -870,6 +889,52 @@ export default defineConfig({
870889
);
871890
}
872891

892+
#[test]
893+
fn test_strip_schema_property() {
894+
// With trailing comma
895+
let input = r#"{
896+
"$schema": "https://raw.githubusercontent.com/nicolo-ribaudo/tc39-proposal-json-schema/refs/heads/main/schema.json",
897+
"rules": {
898+
"no-console": "warn"
899+
}
900+
}"#;
901+
let result = strip_schema_property(input);
902+
assert!(!result.contains("$schema"));
903+
assert!(result.contains(r#""no-console": "warn""#));
904+
905+
// Without trailing comma
906+
let input = r#"{
907+
"$schema": "https://example.com/schema.json"
908+
}"#;
909+
let result = strip_schema_property(input);
910+
assert!(!result.contains("$schema"));
911+
912+
// No $schema - unchanged
913+
let input = r#"{
914+
"rules": {}
915+
}"#;
916+
assert_eq!(strip_schema_property(input), input);
917+
}
918+
919+
#[test]
920+
fn test_merge_json_config_content_strips_schema() {
921+
let vite_config = r#"import { defineConfig } from 'vite';
922+
923+
export default defineConfig({});"#;
924+
925+
let oxlint_config = r#"{
926+
"$schema": "https://raw.githubusercontent.com/nicolo-ribaudo/tc39-proposal-json-schema/refs/heads/main/schema.json",
927+
"rules": {
928+
"no-console": "warn"
929+
}
930+
}"#;
931+
932+
let result = merge_json_config_content(vite_config, oxlint_config, "lint").unwrap();
933+
assert!(result.updated);
934+
assert!(!result.content.contains("$schema"));
935+
assert!(result.content.contains(r#""no-console": "warn""#));
936+
}
937+
873938
#[test]
874939
fn test_indent_multiline() {
875940
// Single line - no change

ecosystem-ci/repo.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"vue-mini": {
2929
"repository": "https://github.com/vue-mini/vue-mini.git",
3030
"branch": "master",
31-
"hash": "c51332662993dde44f665822bdea94cd0abf368b"
31+
"hash": "23df6ba49e29d3ea909ef55874f59b973916d177"
3232
},
3333
"vite-plugin-react": {
3434
"repository": "https://github.com/vitejs/vite-plugin-react.git",

packages/cli/binding/src/cli.rs

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,6 @@ pub enum SynthesizableSubcommand {
115115
/// Skip lint check
116116
#[arg(long = "no-lint")]
117117
no_lint: bool,
118-
/// Disable type-aware linting
119-
#[arg(long = "no-type-aware")]
120-
no_type_aware: bool,
121-
/// Disable TypeScript type checking
122-
#[arg(long = "no-type-check")]
123-
no_type_check: bool,
124118
/// File paths to check (passed through to fmt and lint)
125119
#[arg(trailing_var_arg = true)]
126120
paths: Vec<String>,
@@ -1040,14 +1034,7 @@ async fn execute_direct_subcommand(
10401034
let cwd_arc: Arc<AbsolutePath> = cwd.clone().into();
10411035

10421036
let status = match subcommand {
1043-
SynthesizableSubcommand::Check {
1044-
fix,
1045-
no_fmt,
1046-
no_lint,
1047-
no_type_aware,
1048-
no_type_check,
1049-
paths,
1050-
} => {
1037+
SynthesizableSubcommand::Check { fix, no_fmt, no_lint, paths } => {
10511038
if no_fmt && no_lint {
10521039
output::error("No checks enabled");
10531040
print_summary_line(
@@ -1140,13 +1127,6 @@ async fn execute_direct_subcommand(
11401127
if fix {
11411128
args.push("--fix".to_string());
11421129
}
1143-
if !no_type_aware {
1144-
args.push("--type-aware".to_string());
1145-
// --type-check requires --type-aware as prerequisite
1146-
if !no_type_check {
1147-
args.push("--type-check".to_string());
1148-
}
1149-
}
11501130
if has_paths {
11511131
args.extend(paths.iter().cloned());
11521132
}
@@ -1171,18 +1151,7 @@ async fn execute_direct_subcommand(
11711151

11721152
match analyze_lint_output(&combined_output) {
11731153
Some(Ok(success)) => {
1174-
let type_checks_enabled = !no_type_aware && !no_type_check;
1175-
let issue_label = if type_checks_enabled {
1176-
if fix {
1177-
"Found no warnings, lint errors, or type errors"
1178-
} else {
1179-
"Found no warnings, lint errors, or type errors"
1180-
}
1181-
} else if fix {
1182-
"Found no warnings or lint errors"
1183-
} else {
1184-
"Found no warnings or lint errors"
1185-
};
1154+
let issue_label = "Found no warnings, lint errors, or type errors";
11861155

11871156
let message = format!(
11881157
"{issue_label} in {}",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
{
2+
"ignoredPlatforms": ["win32"],
23
"commands": ["vp dlx -s cowsay hello # should work without package.json"]
34
}

packages/cli/snap-tests-global/create-from-nonworkspace-subdir/steps.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"ignoredPlatforms": ["win32"],
23
"commands": [
34
{
45
"command": "cd scripts && vp create --no-interactive vite:application # from non-monorepo subdir",

packages/cli/snap-tests-global/migration-auto-create-vite-config/snap.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ export default defineConfig({
3737
lint: {
3838
"rules": {
3939
"no-unused-vars": "error"
40+
},
41+
"options": {
42+
"typeAware": true,
43+
"typeCheck": true
4044
}
4145
},
4246
});
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"rules": {
3+
"no-unused-vars": "error"
4+
}
5+
}

0 commit comments

Comments
 (0)