diff --git a/crates/oxc_cli/src/command/mod.rs b/crates/oxc_cli/src/command/mod.rs index 74355badc..aef101972 100644 --- a/crates/oxc_cli/src/command/mod.rs +++ b/crates/oxc_cli/src/command/mod.rs @@ -7,7 +7,7 @@ use bpaf::Bpaf; pub use self::{ format::{format_command, FormatOptions}, ignore::IgnoreOptions, - lint::{lint_command, LintOptions}, + lint::{lint_command, LintOptions, OutputFormat, OutputOptions, WarningOptions}, }; use self::{format::format_options, lint::lint_options}; diff --git a/crates/oxc_cli/src/lint/mod.rs b/crates/oxc_cli/src/lint/mod.rs index 1209bc494..881995c84 100644 --- a/crates/oxc_cli/src/lint/mod.rs +++ b/crates/oxc_cli/src/lint/mod.rs @@ -6,7 +6,7 @@ use oxc_linter::{partial_loader::LINT_PARTIAL_LOADER_EXT, LintOptions, LintServi use oxc_span::VALID_EXTENSIONS; use crate::{ - command::LintOptions as CliLintOptions, + command::{LintOptions as CliLintOptions, OutputFormat, OutputOptions, WarningOptions}, walk::{Extensions, Walk}, CliRunResult, LintResult, Runner, }; @@ -37,6 +37,7 @@ impl Runner for LintRunner { fix_options, enable_plugins, config, + output_options, .. } = self.options; @@ -110,10 +111,7 @@ impl Runner for LintRunner { }; let lint_service = LintService::new(cwd, &paths, linter); - - let diagnostic_service = DiagnosticService::default() - .with_quiet(warning_options.quiet) - .with_max_warnings(warning_options.max_warnings); + let diagnostic_service = Self::get_diagnostic_service(&warning_options, &output_options); // Spawn linting in another thread so diagnostics can be printed immediately from diagnostic_service.run. rayon::spawn({ @@ -137,6 +135,24 @@ impl Runner for LintRunner { } } +impl LintRunner { + fn get_diagnostic_service( + warning_options: &WarningOptions, + output_options: &OutputOptions, + ) -> DiagnosticService { + let mut diagnostic_service = DiagnosticService::default() + .with_quiet(warning_options.quiet) + .with_max_warnings(warning_options.max_warnings); + + match output_options.format { + OutputFormat::Default => {} + OutputFormat::Json => diagnostic_service.set_json_reporter(), + } + + diagnostic_service + } +} + #[cfg(all(test, not(target_os = "windows")))] mod test { use super::LintRunner; diff --git a/crates/oxc_diagnostics/src/lib.rs b/crates/oxc_diagnostics/src/lib.rs index e67f4e7bd..bac04eb54 100644 --- a/crates/oxc_diagnostics/src/lib.rs +++ b/crates/oxc_diagnostics/src/lib.rs @@ -3,6 +3,7 @@ mod graphic_reporter; mod graphical_theme; +mod reporter; mod service; use std::path::PathBuf; diff --git a/crates/oxc_diagnostics/src/reporter.rs b/crates/oxc_diagnostics/src/reporter.rs new file mode 100644 index 000000000..804bd6597 --- /dev/null +++ b/crates/oxc_diagnostics/src/reporter.rs @@ -0,0 +1,26 @@ +use std::fmt; + +use crate::{miette::JSONReportHandler, Diagnostic, GraphicalReportHandler}; + +#[allow(clippy::large_enum_variant)] // Lerge size is fine because this is a singleton +#[derive(Debug)] +#[non_exhaustive] +pub enum DiagnosticReporter { + Graphical(GraphicalReportHandler), // 288 bytes + Json(JSONReportHandler), +} + +impl Default for DiagnosticReporter { + fn default() -> Self { + Self::Graphical(GraphicalReportHandler::new()) + } +} + +impl DiagnosticReporter { + pub fn render_report(&self, f: &mut T, diagnostic: &(dyn Diagnostic)) { + match self { + Self::Graphical(handler) => handler.render_report(f, diagnostic).unwrap(), + Self::Json(handler) => handler.render_report(f, diagnostic).unwrap(), + } + } +} diff --git a/crates/oxc_diagnostics/src/service.rs b/crates/oxc_diagnostics/src/service.rs index 0a9400819..0db87843a 100644 --- a/crates/oxc_diagnostics/src/service.rs +++ b/crates/oxc_diagnostics/src/service.rs @@ -2,17 +2,22 @@ use std::{ cell::Cell, io::{BufWriter, Write}, path::{Path, PathBuf}, - sync::mpsc, - sync::Arc, + sync::{mpsc, Arc}, }; -use crate::{miette::NamedSource, Error, GraphicalReportHandler, MinifiedFileError, Severity}; +use crate::{ + miette::{JSONReportHandler, NamedSource}, + reporter::DiagnosticReporter, + Error, MinifiedFileError, Severity, +}; pub type DiagnosticTuple = (PathBuf, Vec); pub type DiagnosticSender = mpsc::Sender>; pub type DiagnosticReceiver = mpsc::Receiver>; pub struct DiagnosticService { + reporter: DiagnosticReporter, + /// Disable reporting on warnings, only errors are reported quiet: bool, @@ -34,6 +39,7 @@ impl Default for DiagnosticService { fn default() -> Self { let (sender, receiver) = mpsc::channel(); Self { + reporter: DiagnosticReporter::default(), quiet: false, max_warnings: None, warnings_count: Cell::new(0), @@ -45,6 +51,10 @@ impl Default for DiagnosticService { } impl DiagnosticService { + pub fn set_json_reporter(&mut self) { + self.reporter = DiagnosticReporter::Json(JSONReportHandler::new()); + } + #[must_use] pub fn with_quiet(mut self, yes: bool) -> Self { self.quiet = yes; @@ -91,7 +101,6 @@ impl DiagnosticService { /// * When the writer fails to write pub fn run(&self) { let mut buf_writer = BufWriter::new(std::io::stdout()); - let handler = GraphicalReportHandler::new(); while let Ok(Some((path, diagnostics))) = self.receiver.recv() { let mut output = String::new(); @@ -116,7 +125,7 @@ impl DiagnosticService { } let mut err = String::new(); - handler.render_report(&mut err, diagnostic.as_ref()).unwrap(); + self.reporter.render_report(&mut err, diagnostic.as_ref()); // Skip large output and print only once if err.lines().any(|line| line.len() >= 400) { let minified_diagnostic = Error::new(MinifiedFileError(path.clone()));