diff --git a/apps/oxlint/fixtures/tsgolint_type_error/.oxlintrc.json b/apps/oxlint/fixtures/tsgolint_type_error/.oxlintrc.json new file mode 100644 index 0000000000000..c3f3a5015e032 --- /dev/null +++ b/apps/oxlint/fixtures/tsgolint_type_error/.oxlintrc.json @@ -0,0 +1,9 @@ +{ + "categories": { + "correctness": "off" + }, + "rules": { + "typescript/no-floating-promises": "error", + "no-debugger": "error" + } +} diff --git a/apps/oxlint/fixtures/tsgolint_type_error/index.js b/apps/oxlint/fixtures/tsgolint_type_error/index.js new file mode 100644 index 0000000000000..6e77e5b57110c --- /dev/null +++ b/apps/oxlint/fixtures/tsgolint_type_error/index.js @@ -0,0 +1,2 @@ +"use strict"; +const foo = "42"; diff --git a/apps/oxlint/fixtures/tsgolint_type_error/index.ts b/apps/oxlint/fixtures/tsgolint_type_error/index.ts new file mode 100644 index 0000000000000..cfbf5f4f5e6aa --- /dev/null +++ b/apps/oxlint/fixtures/tsgolint_type_error/index.ts @@ -0,0 +1 @@ +const foo: number = "42"; diff --git a/apps/oxlint/fixtures/tsgolint_type_error/tsconfig.json b/apps/oxlint/fixtures/tsgolint_type_error/tsconfig.json new file mode 100644 index 0000000000000..4ac21667f3992 --- /dev/null +++ b/apps/oxlint/fixtures/tsgolint_type_error/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "es2016", + "lib": ["ES2024"], + "module": "commonjs", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + } +} diff --git a/apps/oxlint/src/command/lint.rs b/apps/oxlint/src/command/lint.rs index 40c193c785008..14ec437f0f09f 100644 --- a/apps/oxlint/src/command/lint.rs +++ b/apps/oxlint/src/command/lint.rs @@ -54,6 +54,10 @@ pub struct LintCommand { #[bpaf(switch, hide_usage)] pub type_aware: bool, + /// Enable experimental type checking (includes TypeScript compiler diagnostics) + #[bpaf(switch, hide_usage)] + pub type_check: bool, + #[bpaf(external)] pub inline_config_options: InlineConfigOptions, @@ -618,6 +622,14 @@ mod lint_options { let options = get_lint_options("."); assert!(!options.type_aware); } + + #[test] + fn type_check() { + let options = get_lint_options("--type-check"); + assert!(options.type_check); + let options = get_lint_options("."); + assert!(!options.type_check); + } } #[cfg(test)] diff --git a/apps/oxlint/src/lint.rs b/apps/oxlint/src/lint.rs index 677882a956bb5..579c6dbc5e7e0 100644 --- a/apps/oxlint/src/lint.rs +++ b/apps/oxlint/src/lint.rs @@ -380,6 +380,7 @@ impl CliRunner { // TODO: Add a warning message if `tsgolint` cannot be found, but type-aware rules are enabled let lint_runner = match LintRunner::builder(options, linter) .with_type_aware(self.options.type_aware) + .with_type_check(self.options.type_check) .with_silent(misc_options.silent) .with_fix_kind(fix_options.fix_kind()) .build() @@ -1361,6 +1362,13 @@ mod test { Tester::new().with_cwd("fixtures/tsgolint".into()).test_and_snapshot(args); } + #[test] + #[cfg(not(target_endian = "big"))] + fn test_tsgolint_type_error() { + let args = &["--type-aware", "--type-check"]; + Tester::new().with_cwd("fixtures/tsgolint_type_error".into()).test_and_snapshot(args); + } + #[test] #[cfg(not(target_endian = "big"))] fn test_tsgolint_no_typescript_files() { diff --git a/apps/oxlint/src/snapshots/fixtures__tsgolint_type_error_--type-aware --type-check@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__tsgolint_type_error_--type-aware --type-check@oxlint.snap new file mode 100644 index 0000000000000..44acd309dde12 --- /dev/null +++ b/apps/oxlint/src/snapshots/fixtures__tsgolint_type_error_--type-aware --type-check@oxlint.snap @@ -0,0 +1,19 @@ +--- +source: apps/oxlint/src/tester.rs +--- +########## +arguments: --type-aware --type-check +working directory: fixtures/tsgolint_type_error +---------- + + x typescript(TS2322): Type 'string' is not assignable to type 'number'. + ,-[index.ts:1:7] + 1 | const foo: number = "42"; + : ^^^ + `---- + +Found 0 warnings and 1 error. +Finished in ms on 2 files using 1 threads. +---------- +CLI result: LintFoundErrors +---------- diff --git a/crates/oxc_linter/src/lint_runner.rs b/crates/oxc_linter/src/lint_runner.rs index a9ad7718dced1..ec518b052c80d 100644 --- a/crates/oxc_linter/src/lint_runner.rs +++ b/crates/oxc_linter/src/lint_runner.rs @@ -126,6 +126,7 @@ impl Default for DirectivesStore { pub struct LintRunnerBuilder { regular_linter: Linter, type_aware_enabled: bool, + type_check: bool, lint_service_options: LintServiceOptions, silent: bool, fix_kind: FixKind, @@ -136,6 +137,7 @@ impl LintRunnerBuilder { Self { regular_linter: linter, type_aware_enabled: false, + type_check: false, lint_service_options, silent: false, fix_kind: FixKind::None, @@ -148,6 +150,12 @@ impl LintRunnerBuilder { self } + #[must_use] + pub fn with_type_check(mut self, enabled: bool) -> Self { + self.type_check = enabled; + self + } + #[must_use] pub fn with_silent(mut self, silent: bool) -> Self { self.silent = silent; @@ -171,7 +179,7 @@ impl LintRunnerBuilder { self.regular_linter.config.clone(), self.fix_kind, ) { - Ok(state) => Some(state.with_silent(self.silent)), + Ok(state) => Some(state.with_silent(self.silent).with_type_check(self.type_check)), Err(e) => return Err(e), } } else { diff --git a/crates/oxc_linter/src/tsgolint.rs b/crates/oxc_linter/src/tsgolint.rs index 1a0b00010280d..476f441c4549f 100644 --- a/crates/oxc_linter/src/tsgolint.rs +++ b/crates/oxc_linter/src/tsgolint.rs @@ -32,6 +32,8 @@ pub struct TsGoLintState { fix: bool, /// If `true`, request that suggestions be returned from `tsgolint`. fix_suggestions: bool, + /// If `true`, include TypeScript compiler syntactic and semantic diagnostics. + type_check: bool, } impl TsGoLintState { @@ -46,6 +48,7 @@ impl TsGoLintState { silent: false, fix: fix_kind.contains(FixKind::Fix), fix_suggestions: fix_kind.contains(FixKind::Suggestion), + type_check: false, } } @@ -67,6 +70,7 @@ impl TsGoLintState { silent: false, fix: fix_kind.contains(FixKind::Fix), fix_suggestions: fix_kind.contains(FixKind::Suggestion), + type_check: false, }) } @@ -80,6 +84,15 @@ impl TsGoLintState { self } + /// Set to `true` to include TypeScript compiler syntactic and semantic diagnostics. + /// + /// Default is `false`. + #[must_use] + pub fn with_type_check(mut self, yes: bool) -> Self { + self.type_check = yes; + self + } + /// # Panics /// - when `stdin` of subprocess cannot be opened /// - when `stdout` of subprocess cannot be opened @@ -548,6 +561,8 @@ impl TsGoLintState { }) .collect(), source_overrides, + report_syntactic: self.type_check, + report_semantic: self.type_check, } } } @@ -573,6 +588,8 @@ pub struct Payload { pub version: i32, pub configs: Vec, pub source_overrides: Option>, + pub report_syntactic: bool, + pub report_semantic: bool, } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/tasks/website/src/linter/snapshots/cli.snap b/tasks/website/src/linter/snapshots/cli.snap index 5192b50bfa71e..7cefcd95be589 100644 --- a/tasks/website/src/linter/snapshots/cli.snap +++ b/tasks/website/src/linter/snapshots/cli.snap @@ -159,6 +159,8 @@ Arguments: Disable the automatic loading of nested configuration files - **` --type-aware`** — Enable rules that require type information +- **` --type-check`** — + Enable experimental type checking (includes TypeScript compiler diagnostics) - **`-h`**, **`--help`** — Prints help information - **`-V`**, **`--version`** — diff --git a/tasks/website/src/linter/snapshots/cli_terminal.snap b/tasks/website/src/linter/snapshots/cli_terminal.snap index 7d13c8650a35c..c9b9bc15b8741 100644 --- a/tasks/website/src/linter/snapshots/cli_terminal.snap +++ b/tasks/website/src/linter/snapshots/cli_terminal.snap @@ -96,5 +96,7 @@ Available options: --lsp Start the language server --disable-nested-config Disable the automatic loading of nested configuration files --type-aware Enable rules that require type information + --type-check Enable experimental type checking (includes TypeScript compiler + diagnostics) -h, --help Prints help information -V, --version Prints version information