diff --git a/Cargo.lock b/Cargo.lock index 598cbdf8af8fc..9a014ffd47938 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1746,7 +1746,6 @@ dependencies = [ name = "oxc_diagnostics" version = "0.62.0" dependencies = [ - "cow-utils", "oxc-miette", ] diff --git a/apps/oxlint/src/lint.rs b/apps/oxlint/src/lint.rs index a270a66d2ca59..5ca5af8227303 100644 --- a/apps/oxlint/src/lint.rs +++ b/apps/oxlint/src/lint.rs @@ -7,10 +7,10 @@ use std::{ use cow_utils::CowUtils; use ignore::{gitignore::Gitignore, overrides::OverrideBuilder}; -use oxc_diagnostics::{DiagnosticService, GraphicalReportHandler, OxcDiagnostic}; +use oxc_diagnostics::{GraphicalReportHandler, OxcDiagnostic}; use oxc_linter::{ - AllowWarnDeny, ConfigStore, ConfigStoreBuilder, InvalidFilterKind, LintFilter, LintOptions, - LintService, LintServiceOptions, Linter, Oxlintrc, + AllowWarnDeny, ConfigStore, ConfigStoreBuilder, DiagnosticService, InvalidFilterKind, + LintFilter, LintOptions, LintService, LintServiceOptions, Linter, Oxlintrc, }; use rustc_hash::{FxHashMap, FxHashSet}; use serde_json::Value; diff --git a/apps/oxlint/src/output_formatter/checkstyle.rs b/apps/oxlint/src/output_formatter/checkstyle.rs index e3d11b49991b6..d977372681d54 100644 --- a/apps/oxlint/src/output_formatter/checkstyle.rs +++ b/apps/oxlint/src/output_formatter/checkstyle.rs @@ -2,12 +2,9 @@ use std::borrow::Cow; use rustc_hash::FxHashMap; -use oxc_diagnostics::{ - Error, Severity, - reporter::{DiagnosticReporter, DiagnosticResult, Info}, -}; - use crate::output_formatter::{InternalFormatter, xml_utils::xml_escape}; +use oxc_diagnostics::{Error, Severity}; +use oxc_linter::{DiagnosticReporter, DiagnosticResult, Info}; #[derive(Debug, Default)] pub struct CheckStyleOutputFormatter; @@ -68,10 +65,8 @@ fn format_checkstyle(diagnostics: &[Error]) -> String { #[cfg(test)] mod test { - use oxc_diagnostics::{ - NamedSource, OxcDiagnostic, - reporter::{DiagnosticReporter, DiagnosticResult}, - }; + use oxc_diagnostics::{NamedSource, OxcDiagnostic}; + use oxc_linter::{DiagnosticReporter, DiagnosticResult}; use oxc_span::Span; use super::CheckstyleReporter; diff --git a/apps/oxlint/src/output_formatter/default.rs b/apps/oxlint/src/output_formatter/default.rs index 314599abf97ba..4fcf44b2b7860 100644 --- a/apps/oxlint/src/output_formatter/default.rs +++ b/apps/oxlint/src/output_formatter/default.rs @@ -1,11 +1,8 @@ use std::time::Duration; use crate::output_formatter::InternalFormatter; -use oxc_diagnostics::{ - Error, GraphicalReportHandler, - reporter::{DiagnosticReporter, DiagnosticResult}, -}; -use oxc_linter::table::RuleTable; +use oxc_diagnostics::{Error, GraphicalReportHandler}; +use oxc_linter::{DiagnosticReporter, DiagnosticResult, table::RuleTable}; #[derive(Debug)] pub struct DefaultOutputFormatter; @@ -115,10 +112,8 @@ fn get_diagnostic_result_output(result: &DiagnosticResult) -> String { #[cfg(test)] mod test_implementation { - use oxc_diagnostics::{ - Error, GraphicalReportHandler, GraphicalTheme, - reporter::{DiagnosticReporter, DiagnosticResult, Info}, - }; + use oxc_diagnostics::{Error, GraphicalReportHandler, GraphicalTheme}; + use oxc_linter::{DiagnosticReporter, DiagnosticResult, Info}; use crate::output_formatter::default::get_diagnostic_result_output; @@ -159,7 +154,7 @@ mod test { InternalFormatter, LintCommandInfo, default::{DefaultOutputFormatter, GraphicalReporter}, }; - use oxc_diagnostics::reporter::{DiagnosticReporter, DiagnosticResult}; + use oxc_linter::{DiagnosticReporter, DiagnosticResult}; #[test] fn all_rules() { diff --git a/apps/oxlint/src/output_formatter/github.rs b/apps/oxlint/src/output_formatter/github.rs index aa5438faaed4d..e304b6eb3fbeb 100644 --- a/apps/oxlint/src/output_formatter/github.rs +++ b/apps/oxlint/src/output_formatter/github.rs @@ -1,9 +1,7 @@ use std::borrow::Cow; -use oxc_diagnostics::{ - Error, Severity, - reporter::{DiagnosticReporter, DiagnosticResult, Info}, -}; +use oxc_diagnostics::{Error, Severity}; +use oxc_linter::{DiagnosticReporter, DiagnosticResult, Info}; use crate::output_formatter::InternalFormatter; @@ -81,10 +79,9 @@ fn escape_property(value: &str) -> String { #[cfg(test)] mod test { - use oxc_diagnostics::{ - NamedSource, OxcDiagnostic, - reporter::{DiagnosticReporter, DiagnosticResult}, - }; + use oxc_diagnostics::{NamedSource, OxcDiagnostic}; + use oxc_linter::{DiagnosticReporter, DiagnosticResult}; + use oxc_span::Span; use super::GithubReporter; diff --git a/apps/oxlint/src/output_formatter/json.rs b/apps/oxlint/src/output_formatter/json.rs index 5af7e039c940d..424ea6f03f427 100644 --- a/apps/oxlint/src/output_formatter/json.rs +++ b/apps/oxlint/src/output_formatter/json.rs @@ -1,8 +1,5 @@ -use oxc_diagnostics::{ - Error, - reporter::{DiagnosticReporter, DiagnosticResult}, -}; -use oxc_linter::{RuleCategory, rules::RULES}; +use oxc_diagnostics::Error; +use oxc_linter::{DiagnosticReporter, DiagnosticResult, RuleCategory, rules::RULES}; use miette::JSONReportHandler; @@ -76,10 +73,8 @@ fn format_json(diagnostics: &mut Vec) -> String { #[cfg(test)] mod test { - use oxc_diagnostics::{ - NamedSource, OxcDiagnostic, - reporter::{DiagnosticReporter, DiagnosticResult}, - }; + use oxc_diagnostics::{NamedSource, OxcDiagnostic}; + use oxc_linter::{DiagnosticReporter, DiagnosticResult}; use oxc_span::Span; use super::JsonReporter; diff --git a/apps/oxlint/src/output_formatter/junit.rs b/apps/oxlint/src/output_formatter/junit.rs index 89f800497a63a..cb978e3fecd78 100644 --- a/apps/oxlint/src/output_formatter/junit.rs +++ b/apps/oxlint/src/output_formatter/junit.rs @@ -1,7 +1,6 @@ -use oxc_diagnostics::{ - Error, Severity, - reporter::{DiagnosticReporter, DiagnosticResult, Info}, -}; +use oxc_diagnostics::{Error, Severity}; +use oxc_linter::{DiagnosticReporter, DiagnosticResult, Info}; + use rustc_hash::FxHashMap; use super::{InternalFormatter, xml_utils::xml_escape}; @@ -99,7 +98,8 @@ fn format_junit(diagnostics: &[Error]) -> String { #[cfg(test)] mod test { use super::*; - use oxc_diagnostics::{NamedSource, OxcDiagnostic, reporter::DiagnosticResult}; + use oxc_diagnostics::{NamedSource, OxcDiagnostic}; + use oxc_linter::DiagnosticResult; use oxc_span::Span; #[test] diff --git a/apps/oxlint/src/output_formatter/mod.rs b/apps/oxlint/src/output_formatter/mod.rs index a502f45efac6a..a04e1d74d725c 100644 --- a/apps/oxlint/src/output_formatter/mod.rs +++ b/apps/oxlint/src/output_formatter/mod.rs @@ -16,7 +16,7 @@ use junit::JUnitOutputFormatter; use stylish::StylishOutputFormatter; use unix::UnixOutputFormatter; -use oxc_diagnostics::reporter::DiagnosticReporter; +use oxc_linter::DiagnosticReporter; use crate::output_formatter::{default::DefaultOutputFormatter, json::JsonOutputFormatter}; diff --git a/apps/oxlint/src/output_formatter/stylish.rs b/apps/oxlint/src/output_formatter/stylish.rs index 8973dacb7c9a8..f22d0348d5a3b 100644 --- a/apps/oxlint/src/output_formatter/stylish.rs +++ b/apps/oxlint/src/output_formatter/stylish.rs @@ -1,9 +1,7 @@ use std::fmt::Write; -use oxc_diagnostics::{ - Error, Severity, - reporter::{DiagnosticReporter, DiagnosticResult, Info}, -}; +use oxc_diagnostics::{Error, Severity}; +use oxc_linter::{DiagnosticReporter, DiagnosticResult, Info}; use rustc_hash::FxHashMap; use crate::output_formatter::InternalFormatter; @@ -114,7 +112,8 @@ fn format_stylish(diagnostics: &[Error]) -> String { #[cfg(test)] mod test { use super::*; - use oxc_diagnostics::{NamedSource, OxcDiagnostic, reporter::DiagnosticResult}; + use oxc_diagnostics::{NamedSource, OxcDiagnostic}; + use oxc_linter::DiagnosticResult; use oxc_span::Span; #[test] diff --git a/apps/oxlint/src/output_formatter/unix.rs b/apps/oxlint/src/output_formatter/unix.rs index c083970287bd5..8b67256a1b583 100644 --- a/apps/oxlint/src/output_formatter/unix.rs +++ b/apps/oxlint/src/output_formatter/unix.rs @@ -1,11 +1,9 @@ use std::borrow::Cow; -use oxc_diagnostics::{ - Error, Severity, - reporter::{DiagnosticReporter, DiagnosticResult, Info}, -}; +use oxc_diagnostics::{Error, Severity}; use crate::output_formatter::InternalFormatter; +use oxc_linter::{DiagnosticReporter, DiagnosticResult, Info}; #[derive(Debug, Default)] pub struct UnixOutputFormatter; @@ -53,10 +51,8 @@ fn format_unix(diagnostic: &Error) -> String { #[cfg(test)] mod test { - use oxc_diagnostics::{ - NamedSource, OxcDiagnostic, - reporter::{DiagnosticReporter, DiagnosticResult}, - }; + use oxc_diagnostics::{NamedSource, OxcDiagnostic}; + use oxc_linter::{DiagnosticReporter, DiagnosticResult}; use oxc_span::Span; use super::UnixReporter; diff --git a/crates/oxc_diagnostics/Cargo.toml b/crates/oxc_diagnostics/Cargo.toml index bbccf8622c329..e091369efacad 100644 --- a/crates/oxc_diagnostics/Cargo.toml +++ b/crates/oxc_diagnostics/Cargo.toml @@ -19,5 +19,4 @@ workspace = true doctest = false [dependencies] -cow-utils = { workspace = true } miette = { workspace = true } diff --git a/crates/oxc_diagnostics/src/lib.rs b/crates/oxc_diagnostics/src/lib.rs index 4e208991563ed..d91c5d7d6e82f 100644 --- a/crates/oxc_diagnostics/src/lib.rs +++ b/crates/oxc_diagnostics/src/lib.rs @@ -14,11 +14,6 @@ //! //! See the [miette] documentation for more information on how to interact with diagnostics. //! -//! ## Reporting -//! If you are writing your own tools that may produce their own errors, you can use -//! [`DiagnosticService`] to format and render them to a string or a stream. It can receive -//! [`Error`]s over a multi-producer, single consumer -//! //! ``` //! use std::{sync::Arc, thread}; //! use oxc_diagnostics::{DiagnosticService, Error, OxcDiagnostic}; @@ -48,25 +43,19 @@ //! service.run(); //! ``` -mod service; - use std::{ borrow::Cow, fmt::{self, Display}, ops::{Deref, DerefMut}, }; -pub mod reporter; - -pub use crate::service::{DiagnosticSender, DiagnosticService, DiagnosticTuple}; - pub type Error = miette::Error; pub type Severity = miette::Severity; pub type Result = std::result::Result; use miette::{Diagnostic, SourceCode}; -pub use miette::{GraphicalReportHandler, GraphicalTheme, LabeledSpan, NamedSource}; +pub use miette::{GraphicalReportHandler, GraphicalTheme, LabeledSpan, NamedSource, SourceSpan}; /// Describes an error or warning that occurred. /// diff --git a/crates/oxc_linter/src/lib.rs b/crates/oxc_linter/src/lib.rs index 95b81935d252e..af1d935f5bda2 100644 --- a/crates/oxc_linter/src/lib.rs +++ b/crates/oxc_linter/src/lib.rs @@ -43,7 +43,10 @@ pub use crate::{ options::LintOptions, options::{AllowWarnDeny, InvalidFilterKind, LintFilter, LintFilterKind}, rule::{RuleCategory, RuleFixMeta, RuleMeta, RuleWithSeverity}, - service::{LintService, LintServiceOptions}, + service::{ + DiagnosticReporter, DiagnosticResult, DiagnosticService, Info, LintService, + LintServiceOptions, + }, }; use crate::{ config::{LintConfig, OxlintEnv, OxlintGlobals, OxlintSettings, ResolvedLinterState}, diff --git a/crates/oxc_diagnostics/src/service.rs b/crates/oxc_linter/src/service/diagnostic_service.rs similarity index 88% rename from crates/oxc_diagnostics/src/service.rs rename to crates/oxc_linter/src/service/diagnostic_service.rs index 7c90782e3f8db..6362f0eff86fe 100644 --- a/crates/oxc_diagnostics/src/service.rs +++ b/crates/oxc_linter/src/service/diagnostic_service.rs @@ -5,12 +5,10 @@ use std::{ }; use cow_utils::CowUtils; -use miette::LabeledSpan; -use crate::{ - Error, NamedSource, OxcDiagnostic, Severity, - reporter::{DiagnosticReporter, DiagnosticResult}, -}; +use oxc_diagnostics::{Error, LabeledSpan, NamedSource, OxcDiagnostic, Severity}; + +use super::{DiagnosticReporter, DiagnosticResult}; pub type DiagnosticTuple = (PathBuf, Vec); pub type DiagnosticSender = mpsc::Sender>; @@ -26,10 +24,34 @@ pub type DiagnosticReceiver = mpsc::Receiver>; /// # Example /// ```rust /// use std::thread; -/// use oxc_diagnostics::{Error, OxcDiagnostic, DiagnosticService}; +/// use oxc_diagnostics::{Error, OxcDiagnostic, GraphicalReportHandler}; +/// use oxc_linter::{DiagnosticService, DiagnosticResult, DiagnosticReporter}; +/// use std::path::PathBuf; +/// +/// struct GraphicalReporter { +/// handler: GraphicalReportHandler, +/// } +/// +/// impl Default for GraphicalReporter { +/// fn default() -> Self { +/// Self { handler: GraphicalReportHandler::new() } +/// } +/// } +/// +/// impl DiagnosticReporter for GraphicalReporter { +/// fn finish(&mut self, result: &DiagnosticResult) -> Option { +/// None +/// } +/// +/// fn render_error(&mut self, error: Error) -> Option { +/// let mut output = String::new(); +/// self.handler.render_report(&mut output, error.as_ref()).unwrap(); +/// Some(output) +/// } +/// } /// /// // By default, services will pretty-print diagnostics to the console -/// let mut service = DiagnosticService::default(); +/// let mut service = DiagnosticService::new(Box::new(GraphicalReporter::default())); /// // Get a clone of the sender to send diagnostics to the service /// let mut sender = service.sender().clone(); /// @@ -45,8 +67,9 @@ pub type DiagnosticReceiver = mpsc::Receiver>; /// sender.send(None); /// }); /// -/// // Listen for and process messages -/// service.run() +/// // Process messages and report a statistic about the number of errors/warnings. +/// let mut output = std::io::stdout(); +/// let result: DiagnosticResult = service.run(&mut output); /// ``` pub struct DiagnosticService { reporter: Box, diff --git a/crates/oxc_linter/src/service/lint_service.rs b/crates/oxc_linter/src/service/lint_service.rs new file mode 100644 index 0000000000000..cb836444638c9 --- /dev/null +++ b/crates/oxc_linter/src/service/lint_service.rs @@ -0,0 +1,98 @@ +use std::{ + ffi::OsStr, + path::{Path, PathBuf}, + sync::Arc, +}; + +use crate::Linter; + +use super::{DiagnosticSender, runtime::Runtime}; + +pub struct LintServiceOptions { + /// Current working directory + pub(crate) cwd: Box, + + /// All paths to lint + pub(crate) paths: Vec>, + + /// TypeScript `tsconfig.json` path for reading path alias and project references + pub(crate) tsconfig: Option, + + pub(crate) cross_module: bool, +} + +impl LintServiceOptions { + #[must_use] + pub fn new(cwd: T, paths: Vec>) -> Self + where + T: Into>, + { + Self { cwd: cwd.into(), paths, tsconfig: None, cross_module: false } + } + + #[inline] + #[must_use] + pub fn with_tsconfig(mut self, tsconfig: T) -> Self + where + T: Into, + { + let tsconfig = tsconfig.into(); + // Should this be canonicalized? + let tsconfig = if tsconfig.is_relative() { self.cwd.join(tsconfig) } else { tsconfig }; + debug_assert!(tsconfig.is_file()); + + self.tsconfig = Some(tsconfig); + self + } + + #[inline] + #[must_use] + pub fn with_cross_module(mut self, cross_module: bool) -> Self { + self.cross_module = cross_module; + self + } + + #[inline] + pub fn cwd(&self) -> &Path { + &self.cwd + } +} + +pub struct LintService { + runtime: Runtime, +} + +impl LintService { + pub fn new(linter: Linter, options: LintServiceOptions) -> Self { + let runtime = Runtime::new(linter, options); + Self { runtime } + } + + #[cfg(test)] + pub(crate) fn from_linter(linter: Linter, options: LintServiceOptions) -> Self { + let runtime = Runtime::new(linter, options); + Self { runtime } + } + + pub fn linter(&self) -> &Linter { + &self.runtime.linter + } + + /// # Panics + pub fn run(&mut self, tx_error: &DiagnosticSender) { + self.runtime.run(tx_error); + tx_error.send(None).unwrap(); + } + + /// For tests + #[cfg(test)] + pub(crate) fn run_source<'a>( + &mut self, + allocator: &'a oxc_allocator::Allocator, + source_text: &str, + check_syntax_errors: bool, + tx_error: &DiagnosticSender, + ) -> Vec> { + self.runtime.run_source(allocator, source_text, check_syntax_errors, tx_error) + } +} diff --git a/crates/oxc_linter/src/service/mod.rs b/crates/oxc_linter/src/service/mod.rs index c7ca1604321ca..ef448ed56380e 100644 --- a/crates/oxc_linter/src/service/mod.rs +++ b/crates/oxc_linter/src/service/mod.rs @@ -1,101 +1,8 @@ -use std::{ - ffi::OsStr, - path::{Path, PathBuf}, - sync::Arc, -}; - -use oxc_diagnostics::DiagnosticSender; -use runtime::Runtime; - -use crate::Linter; - +mod diagnostic_service; +mod lint_service; +mod reporter; mod runtime; -pub struct LintServiceOptions { - /// Current working directory - cwd: Box, - - /// All paths to lint - paths: Vec>, - - /// TypeScript `tsconfig.json` path for reading path alias and project references - tsconfig: Option, - - cross_module: bool, -} - -impl LintServiceOptions { - #[must_use] - pub fn new(cwd: T, paths: Vec>) -> Self - where - T: Into>, - { - Self { cwd: cwd.into(), paths, tsconfig: None, cross_module: false } - } - - #[inline] - #[must_use] - pub fn with_tsconfig(mut self, tsconfig: T) -> Self - where - T: Into, - { - let tsconfig = tsconfig.into(); - // Should this be canonicalized? - let tsconfig = if tsconfig.is_relative() { self.cwd.join(tsconfig) } else { tsconfig }; - debug_assert!(tsconfig.is_file()); - - self.tsconfig = Some(tsconfig); - self - } - - #[inline] - #[must_use] - pub fn with_cross_module(mut self, cross_module: bool) -> Self { - self.cross_module = cross_module; - self - } - - #[inline] - pub fn cwd(&self) -> &Path { - &self.cwd - } -} - -pub struct LintService { - runtime: Runtime, -} - -impl LintService { - pub fn new(linter: Linter, options: LintServiceOptions) -> Self { - let runtime = Runtime::new(linter, options); - Self { runtime } - } - - #[cfg(test)] - pub(crate) fn from_linter(linter: Linter, options: LintServiceOptions) -> Self { - let runtime = Runtime::new(linter, options); - Self { runtime } - } - - pub fn linter(&self) -> &Linter { - &self.runtime.linter - } - - /// # Panics - pub fn run(&mut self, tx_error: &DiagnosticSender) { - self.runtime.run(tx_error); - tx_error.send(None).unwrap(); - } - - /// For tests - #[cfg(test)] - pub(crate) fn run_source<'a>( - &mut self, - allocator: &'a oxc_allocator::Allocator, - source_text: &str, - check_syntax_errors: bool, - tx_error: &DiagnosticSender, - ) -> Vec> { - self.runtime.run_source(allocator, source_text, check_syntax_errors, tx_error) - } -} +pub use diagnostic_service::{DiagnosticSender, DiagnosticService}; +pub use lint_service::{LintService, LintServiceOptions}; +pub use reporter::{DiagnosticReporter, DiagnosticResult, Info}; diff --git a/crates/oxc_diagnostics/src/reporter.rs b/crates/oxc_linter/src/service/reporter.rs similarity index 95% rename from crates/oxc_diagnostics/src/reporter.rs rename to crates/oxc_linter/src/service/reporter.rs index efb764f6a6465..2a6b56adb3f1d 100644 --- a/crates/oxc_diagnostics/src/reporter.rs +++ b/crates/oxc_linter/src/service/reporter.rs @@ -1,8 +1,6 @@ //! [Reporters](DiagnosticReporter) for rendering and writing diagnostics. -use miette::SourceSpan; - -use crate::{Error, Severity}; +use oxc_diagnostics::{Error, Severity, SourceSpan}; /// Reporters are responsible for rendering diagnostics to some format and writing them to some /// form of output stream. @@ -12,7 +10,8 @@ use crate::{Error, Severity}; /// /// ## Example /// ``` -/// use oxc_diagnostics::{DiagnosticReporter, Error, Severity}; +/// use oxc_diagnostics::{Error, Severity}; +/// use oxc_linter::{DiagnosticReporter, DiagnosticResult}; /// /// #[derive(Default)] /// pub struct BufferedReporter; @@ -20,7 +19,7 @@ use crate::{Error, Severity}; /// impl DiagnosticReporter for BufferedReporter { /// // render the finished output, some reporters will store the errors in memory /// // to output all diagnostics at the end -/// fn finish(&mut self) -> Option { +/// fn finish(&mut self, result: &DiagnosticResult) -> Option { /// None /// } /// diff --git a/crates/oxc_linter/src/service/runtime.rs b/crates/oxc_linter/src/service/runtime.rs index aa961511a704a..28fdf0a567dd5 100644 --- a/crates/oxc_linter/src/service/runtime.rs +++ b/crates/oxc_linter/src/service/runtime.rs @@ -16,15 +16,15 @@ use self_cell::self_cell; use smallvec::SmallVec; use oxc_allocator::Allocator; -use oxc_diagnostics::{DiagnosticSender, DiagnosticService, Error, OxcDiagnostic}; +use oxc_diagnostics::{Error, OxcDiagnostic}; use oxc_parser::{ParseOptions, Parser}; use oxc_resolver::Resolver; use oxc_semantic::{Semantic, SemanticBuilder}; use oxc_span::{CompactStr, SourceType, VALID_EXTENSIONS}; -use super::LintServiceOptions; +use super::{DiagnosticSender, LintServiceOptions}; use crate::{ - Fixer, Linter, Message, + DiagnosticService, Fixer, Linter, Message, loader::{JavaScriptSource, LINT_PARTIAL_LOADER_EXTENSIONS, PartialLoader}, module_record::ModuleRecord, utils::read_to_string,