mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
refactor(linter): move DiagnosticsReporters to oxlint (#8454)
it was never the plan that oxc_diagnostics should be the
`std::io::stdout` writer:
8fc238ac34/crates/oxc_diagnostics/src/service.rs (L84-L85)
This PR does refactor all reporters to `oxlint` crate and make the
current implementation of `oxc_diagnostics` public for others to
consume.
I added some tests to reflect to expected output (and found some bugs^^)
For the future I think the BufWriter for `std::io::stdout` should come
from outside.
Or maybe `std::fmt::stdout`? I do not know^^"
I could not test 100% of the code, I hope I can fix this with the next
PR which will include a own Tester for oxlint (like `oxc_linter`).
Please be extra careful when reviewing it.
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
9ec4e24eb7
commit
b4c87e27a1
17 changed files with 486 additions and 328 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -1703,7 +1703,6 @@ name = "oxc_diagnostics"
|
|||
version = "0.46.0"
|
||||
dependencies = [
|
||||
"oxc-miette",
|
||||
"rustc-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2231,6 +2230,7 @@ dependencies = [
|
|||
"oxc_linter",
|
||||
"oxc_span",
|
||||
"rayon",
|
||||
"rustc-hash",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ bpaf = { workspace = true, features = ["autocomplete", "bright-color", "derive"]
|
|||
ignore = { workspace = true, features = ["simd-accel"] }
|
||||
miette = { workspace = true }
|
||||
rayon = { workspace = true }
|
||||
rustc-hash = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
use std::{
|
||||
env, fs,
|
||||
io::BufWriter,
|
||||
io::{BufWriter, Write},
|
||||
path::{Path, PathBuf},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use ignore::gitignore::Gitignore;
|
||||
|
||||
use oxc_diagnostics::{DiagnosticService, GraphicalReportHandler};
|
||||
use oxc_linter::{
|
||||
loader::LINT_PARTIAL_LOADER_EXT, AllowWarnDeny, ConfigStoreBuilder, InvalidFilterKind,
|
||||
|
|
@ -15,9 +14,7 @@ use oxc_linter::{
|
|||
use oxc_span::VALID_EXTENSIONS;
|
||||
|
||||
use crate::{
|
||||
cli::{
|
||||
CliRunResult, LintCommand, LintResult, MiscOptions, OutputOptions, Runner, WarningOptions,
|
||||
},
|
||||
cli::{CliRunResult, LintCommand, LintResult, MiscOptions, Runner, WarningOptions},
|
||||
output_formatter::{OutputFormat, OutputFormatter},
|
||||
walk::{Extensions, Walk},
|
||||
};
|
||||
|
|
@ -37,11 +34,15 @@ impl Runner for LintRunner {
|
|||
|
||||
fn run(self) -> CliRunResult {
|
||||
let format_str = self.options.output_options.format;
|
||||
let output_formatter = OutputFormatter::new(format_str);
|
||||
let mut output_formatter = OutputFormatter::new(format_str);
|
||||
|
||||
// stdio is blocked by LineWriter, use a BufWriter to reduce syscalls.
|
||||
// See `https://github.com/rust-lang/rust/issues/60673`.
|
||||
let mut stdout = BufWriter::new(std::io::stdout());
|
||||
|
||||
if self.options.list_rules {
|
||||
let mut stdout = BufWriter::new(std::io::stdout());
|
||||
output_formatter.all_rules(&mut stdout);
|
||||
stdout.flush().unwrap();
|
||||
return CliRunResult::None;
|
||||
}
|
||||
|
||||
|
|
@ -180,7 +181,7 @@ impl Runner for LintRunner {
|
|||
|
||||
let lint_service = LintService::new(linter, options);
|
||||
let mut diagnostic_service =
|
||||
Self::get_diagnostic_service(&warning_options, &output_options, &misc_options);
|
||||
Self::get_diagnostic_service(&output_formatter, &warning_options, &misc_options);
|
||||
|
||||
// Spawn linting in another thread so diagnostics can be printed immediately from diagnostic_service.run.
|
||||
rayon::spawn({
|
||||
|
|
@ -190,7 +191,7 @@ impl Runner for LintRunner {
|
|||
lint_service.run(&tx_error);
|
||||
}
|
||||
});
|
||||
diagnostic_service.run();
|
||||
diagnostic_service.run(&mut stdout);
|
||||
|
||||
CliRunResult::LintResult(LintResult {
|
||||
duration: now.elapsed(),
|
||||
|
|
@ -215,23 +216,14 @@ impl LintRunner {
|
|||
}
|
||||
|
||||
fn get_diagnostic_service(
|
||||
reporter: &OutputFormatter,
|
||||
warning_options: &WarningOptions,
|
||||
output_options: &OutputOptions,
|
||||
misc_options: &MiscOptions,
|
||||
) -> DiagnosticService {
|
||||
let mut diagnostic_service = DiagnosticService::default()
|
||||
DiagnosticService::new(reporter.get_diagnostic_reporter())
|
||||
.with_quiet(warning_options.quiet)
|
||||
.with_silent(misc_options.silent)
|
||||
.with_max_warnings(warning_options.max_warnings);
|
||||
|
||||
match output_options.format {
|
||||
OutputFormat::Default => {}
|
||||
OutputFormat::Json => diagnostic_service.set_json_reporter(),
|
||||
OutputFormat::Unix => diagnostic_service.set_unix_reporter(),
|
||||
OutputFormat::Checkstyle => diagnostic_service.set_checkstyle_reporter(),
|
||||
OutputFormat::Github => diagnostic_service.set_github_reporter(),
|
||||
}
|
||||
diagnostic_service
|
||||
.with_max_warnings(warning_options.max_warnings)
|
||||
}
|
||||
|
||||
// moved into a separate function for readability, but it's only ever used
|
||||
|
|
|
|||
|
|
@ -1,30 +1,47 @@
|
|||
use std::borrow::Cow;
|
||||
use std::{borrow::Cow, io::Write};
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use super::{DiagnosticReporter, Info};
|
||||
use crate::{Error, Severity};
|
||||
use oxc_diagnostics::{
|
||||
reporter::{DiagnosticReporter, Info},
|
||||
Error, Severity,
|
||||
};
|
||||
|
||||
use crate::output_formatter::InternalFormatter;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct CheckStyleOutputFormatter;
|
||||
|
||||
impl InternalFormatter for CheckStyleOutputFormatter {
|
||||
fn all_rules(&mut self, writer: &mut dyn Write) {
|
||||
writeln!(writer, "flag --rules with flag --format=checkstyle is not allowed").unwrap();
|
||||
}
|
||||
|
||||
fn get_diagnostic_reporter(&self) -> Box<dyn DiagnosticReporter> {
|
||||
Box::new(CheckstyleReporter::default())
|
||||
}
|
||||
}
|
||||
|
||||
/// Reporter to output diagnostics in checkstyle format
|
||||
///
|
||||
/// Checkstyle Format Documentation: <https://checkstyle.sourceforge.io/>
|
||||
#[derive(Default)]
|
||||
pub struct CheckstyleReporter {
|
||||
struct CheckstyleReporter {
|
||||
diagnostics: Vec<Error>,
|
||||
}
|
||||
|
||||
impl DiagnosticReporter for CheckstyleReporter {
|
||||
fn finish(&mut self) {
|
||||
format_checkstyle(&self.diagnostics);
|
||||
fn finish(&mut self) -> Option<String> {
|
||||
Some(format_checkstyle(&self.diagnostics))
|
||||
}
|
||||
|
||||
fn render_diagnostics(&mut self, _s: &[u8]) {}
|
||||
|
||||
fn render_error(&mut self, error: Error) -> Option<String> {
|
||||
self.diagnostics.push(error);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::print_stdout)]
|
||||
fn format_checkstyle(diagnostics: &[Error]) {
|
||||
fn format_checkstyle(diagnostics: &[Error]) -> String {
|
||||
let infos = diagnostics.iter().map(Info::new).collect::<Vec<_>>();
|
||||
let mut grouped: FxHashMap<String, Vec<Info>> = FxHashMap::default();
|
||||
for info in infos {
|
||||
|
|
@ -48,9 +65,9 @@ fn format_checkstyle(diagnostics: &[Error]) {
|
|||
let filename = &infos[0].filename;
|
||||
format!(r#"<file name="{filename}">{messages}</file>"#)
|
||||
}).collect::<Vec<_>>().join(" ");
|
||||
println!(
|
||||
format!(
|
||||
r#"<?xml version="1.0" encoding="utf-8"?><checkstyle version="4.3">{messages}</checkstyle>"#
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
/// <https://github.com/tafia/quick-xml/blob/6e34a730853fe295d68dc28460153f08a5a12955/src/escapei.rs#L84-L86>
|
||||
|
|
@ -103,3 +120,31 @@ fn xml_escape_impl<F: Fn(u8) -> bool>(raw: &str, escape_chars: F) -> Cow<str> {
|
|||
Cow::Borrowed(raw)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use oxc_diagnostics::{reporter::DiagnosticReporter, NamedSource, OxcDiagnostic};
|
||||
use oxc_span::Span;
|
||||
|
||||
use super::CheckstyleReporter;
|
||||
|
||||
#[test]
|
||||
fn reporter() {
|
||||
let mut reporter = CheckstyleReporter::default();
|
||||
|
||||
let error = OxcDiagnostic::warn("error message")
|
||||
.with_label(Span::new(0, 8))
|
||||
.with_source_code(NamedSource::new("file://test.ts", "debugger;"));
|
||||
|
||||
let first_result = reporter.render_error(error);
|
||||
|
||||
// reporter keeps it in memory
|
||||
assert!(first_result.is_none());
|
||||
|
||||
// report not gives us all diagnostics at ones
|
||||
let second_result = reporter.finish();
|
||||
|
||||
assert!(second_result.is_some());
|
||||
assert_eq!(second_result.unwrap(), "<?xml version=\"1.0\" encoding=\"utf-8\"?><checkstyle version=\"4.3\"><file name=\"file://test.ts\"><error line=\"1\" column=\"1\" severity=\"warning\" message=\"error message\" source=\"\" /></file></checkstyle>");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,15 @@
|
|||
use std::io::Write;
|
||||
|
||||
use oxc_diagnostics::{reporter::DiagnosticReporter, Error, GraphicalReportHandler};
|
||||
use oxc_linter::table::RuleTable;
|
||||
|
||||
use crate::output_formatter::InternalFormatter;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DefaultOutputFormatter;
|
||||
|
||||
impl DefaultOutputFormatter {
|
||||
pub fn all_rules<T: Write>(writer: &mut T) {
|
||||
impl InternalFormatter for DefaultOutputFormatter {
|
||||
fn all_rules(&mut self, writer: &mut dyn Write) {
|
||||
let table = RuleTable::new();
|
||||
for section in table.sections {
|
||||
writeln!(writer, "{}", section.render_markdown_table(None)).unwrap();
|
||||
|
|
@ -13,17 +17,88 @@ impl DefaultOutputFormatter {
|
|||
writeln!(writer, "Default: {}", table.turned_on_by_default_count).unwrap();
|
||||
writeln!(writer, "Total: {}", table.total).unwrap();
|
||||
}
|
||||
|
||||
fn get_diagnostic_reporter(&self) -> Box<dyn DiagnosticReporter> {
|
||||
Box::new(GraphicalReporter::default())
|
||||
}
|
||||
}
|
||||
|
||||
/// Pretty-prints diagnostics. Primarily meant for human-readable output in a terminal.
|
||||
///
|
||||
/// See [`GraphicalReportHandler`] for how to configure colors, context lines, etc.
|
||||
struct GraphicalReporter {
|
||||
handler: GraphicalReportHandler,
|
||||
}
|
||||
|
||||
impl Default for GraphicalReporter {
|
||||
fn default() -> Self {
|
||||
Self { handler: GraphicalReportHandler::new() }
|
||||
}
|
||||
}
|
||||
|
||||
impl DiagnosticReporter for GraphicalReporter {
|
||||
fn finish(&mut self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
fn render_error(&mut self, error: Error) -> Option<String> {
|
||||
let mut output = String::new();
|
||||
self.handler.render_report(&mut output, error.as_ref()).unwrap();
|
||||
Some(output)
|
||||
}
|
||||
}
|
||||
impl GraphicalReporter {
|
||||
#[cfg(test)]
|
||||
pub fn with_handler(mut self, handler: GraphicalReportHandler) -> Self {
|
||||
self.handler = handler;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::output_formatter::default::DefaultOutputFormatter;
|
||||
use crate::output_formatter::{
|
||||
default::{DefaultOutputFormatter, GraphicalReporter},
|
||||
InternalFormatter,
|
||||
};
|
||||
use miette::{GraphicalReportHandler, GraphicalTheme, NamedSource};
|
||||
use oxc_diagnostics::{reporter::DiagnosticReporter, OxcDiagnostic};
|
||||
use oxc_span::Span;
|
||||
|
||||
#[test]
|
||||
fn all_rules() {
|
||||
let mut writer = Vec::new();
|
||||
let mut formatter = DefaultOutputFormatter;
|
||||
|
||||
DefaultOutputFormatter::all_rules(&mut writer);
|
||||
formatter.all_rules(&mut writer);
|
||||
assert!(!writer.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reporter_finish() {
|
||||
let mut reporter = GraphicalReporter::default();
|
||||
|
||||
let result = reporter.finish();
|
||||
|
||||
assert!(result.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reporter_error() {
|
||||
let mut reporter = GraphicalReporter::default().with_handler(
|
||||
GraphicalReportHandler::new_themed(GraphicalTheme::none()).with_links(false),
|
||||
);
|
||||
|
||||
let error = OxcDiagnostic::warn("error message")
|
||||
.with_label(Span::new(0, 8))
|
||||
.with_source_code(NamedSource::new("file://test.ts", "debugger;"));
|
||||
|
||||
let result = reporter.render_error(error);
|
||||
|
||||
assert!(result.is_some());
|
||||
assert_eq!(
|
||||
result.unwrap(),
|
||||
"\n ! error message\n ,-[file://test.ts:1:1]\n 1 | debugger;\n : ^^^^^^^^\n `----\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,36 @@
|
|||
use std::{
|
||||
borrow::Cow,
|
||||
io::{BufWriter, Stdout, Write},
|
||||
use std::{borrow::Cow, io::Write};
|
||||
|
||||
use oxc_diagnostics::{
|
||||
reporter::{DiagnosticReporter, Info},
|
||||
Error, Severity,
|
||||
};
|
||||
|
||||
use super::{writer, DiagnosticReporter, Info};
|
||||
use crate::{Error, Severity};
|
||||
use crate::output_formatter::InternalFormatter;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GithubOutputFormatter;
|
||||
|
||||
impl InternalFormatter for GithubOutputFormatter {
|
||||
fn all_rules(&mut self, writer: &mut dyn Write) {
|
||||
writeln!(writer, "flag --rules with flag --format=github is not allowed").unwrap();
|
||||
}
|
||||
|
||||
fn get_diagnostic_reporter(&self) -> Box<dyn DiagnosticReporter> {
|
||||
Box::new(GithubReporter)
|
||||
}
|
||||
}
|
||||
|
||||
/// Formats reports using [GitHub Actions
|
||||
/// annotations](https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message). Useful for reporting in CI.
|
||||
pub struct GithubReporter {
|
||||
writer: BufWriter<Stdout>,
|
||||
}
|
||||
|
||||
impl Default for GithubReporter {
|
||||
fn default() -> Self {
|
||||
Self { writer: writer() }
|
||||
}
|
||||
}
|
||||
struct GithubReporter;
|
||||
|
||||
impl DiagnosticReporter for GithubReporter {
|
||||
fn finish(&mut self) {
|
||||
self.writer.flush().unwrap();
|
||||
fn finish(&mut self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
fn render_diagnostics(&mut self, _s: &[u8]) {}
|
||||
|
||||
fn render_error(&mut self, error: Error) -> Option<String> {
|
||||
let message = format_github(&error);
|
||||
self.writer.write_all(message.as_bytes()).unwrap();
|
||||
None
|
||||
Some(format_github(&error))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -79,3 +81,33 @@ fn escape_property(value: &str) -> String {
|
|||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use oxc_diagnostics::{reporter::DiagnosticReporter, NamedSource, OxcDiagnostic};
|
||||
use oxc_span::Span;
|
||||
|
||||
use super::GithubReporter;
|
||||
|
||||
#[test]
|
||||
fn reporter_finish() {
|
||||
let mut reporter = GithubReporter;
|
||||
|
||||
let result = reporter.finish();
|
||||
|
||||
assert!(result.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reporter_error() {
|
||||
let mut reporter = GithubReporter;
|
||||
let error = OxcDiagnostic::warn("error message")
|
||||
.with_label(Span::new(0, 8))
|
||||
.with_source_code(NamedSource::new("file://test.ts", "debugger;"));
|
||||
|
||||
let result = reporter.render_error(error);
|
||||
|
||||
assert!(result.is_some());
|
||||
assert_eq!(result.unwrap(), "::warning file=file%3A//test.ts,line=1,endLine=1,col=1,endColumn=1,title=oxlint::error message\n");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,18 @@
|
|||
use oxc_linter::rules::RULES;
|
||||
use oxc_linter::RuleCategory;
|
||||
use std::io::Write;
|
||||
|
||||
#[derive(Debug)]
|
||||
use oxc_diagnostics::{reporter::DiagnosticReporter, Error};
|
||||
use oxc_linter::rules::RULES;
|
||||
use oxc_linter::RuleCategory;
|
||||
|
||||
use miette::JSONReportHandler;
|
||||
|
||||
use crate::output_formatter::InternalFormatter;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct JsonOutputFormatter;
|
||||
|
||||
impl JsonOutputFormatter {
|
||||
pub fn all_rules<T: Write>(writer: &mut T) {
|
||||
impl InternalFormatter for JsonOutputFormatter {
|
||||
fn all_rules(&mut self, writer: &mut dyn Write) {
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
struct RuleInfoJson<'a> {
|
||||
scope: &'a str,
|
||||
|
|
@ -28,4 +34,77 @@ impl JsonOutputFormatter {
|
|||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn get_diagnostic_reporter(&self) -> Box<dyn DiagnosticReporter> {
|
||||
Box::new(JsonReporter::default())
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders reports as a JSON array of objects.
|
||||
///
|
||||
/// Note that, due to syntactic restrictions of JSON arrays, this reporter waits until all
|
||||
/// diagnostics have been reported before writing them to the output stream.
|
||||
#[derive(Default)]
|
||||
struct JsonReporter {
|
||||
diagnostics: Vec<Error>,
|
||||
}
|
||||
|
||||
impl DiagnosticReporter for JsonReporter {
|
||||
// NOTE: this output does not conform to eslint json format yet
|
||||
// https://eslint.org/docs/latest/use/formatters/#json
|
||||
fn finish(&mut self) -> Option<String> {
|
||||
Some(format_json(&mut self.diagnostics))
|
||||
}
|
||||
|
||||
fn render_error(&mut self, error: Error) -> Option<String> {
|
||||
self.diagnostics.push(error);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://github.com/fregante/eslint-formatters/tree/ae1fd9748596447d1fd09625c33d9e7ba9a3d06d/packages/eslint-formatter-json>
|
||||
#[allow(clippy::print_stdout)]
|
||||
fn format_json(diagnostics: &mut Vec<Error>) -> String {
|
||||
let handler = JSONReportHandler::new();
|
||||
let messages = diagnostics
|
||||
.drain(..)
|
||||
.map(|error| {
|
||||
let mut output = String::from("\t");
|
||||
handler.render_report(&mut output, error.as_ref()).unwrap();
|
||||
output
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(",\n");
|
||||
format!("[\n{messages}\n]")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use oxc_diagnostics::{reporter::DiagnosticReporter, NamedSource, OxcDiagnostic};
|
||||
use oxc_span::Span;
|
||||
|
||||
use super::JsonReporter;
|
||||
|
||||
#[test]
|
||||
fn reporter() {
|
||||
let mut reporter = JsonReporter::default();
|
||||
|
||||
let error = OxcDiagnostic::warn("error message")
|
||||
.with_label(Span::new(0, 8))
|
||||
.with_source_code(NamedSource::new("file://test.ts", "debugger;"));
|
||||
|
||||
let first_result = reporter.render_error(error);
|
||||
|
||||
// reporter keeps it in memory
|
||||
assert!(first_result.is_none());
|
||||
|
||||
// report not gives us all diagnostics at ones
|
||||
let second_result = reporter.finish();
|
||||
|
||||
assert!(second_result.is_some());
|
||||
assert_eq!(
|
||||
second_result.unwrap(),
|
||||
"[\n\t{\"message\": \"error message\",\"severity\": \"warning\",\"causes\": [],\"filename\": \"file://test.ts\",\"labels\": [{\"span\": {\"offset\": 0,\"length\": 8}}],\"related\": []}\n]"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,19 @@
|
|||
mod checkstyle;
|
||||
mod default;
|
||||
mod github;
|
||||
mod json;
|
||||
mod unix;
|
||||
|
||||
use std::io::Write;
|
||||
use std::io::{BufWriter, Stdout, Write};
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::output_formatter::{default::DefaultOutputFormatter, json::JsonOutputFormatter};
|
||||
use checkstyle::CheckStyleOutputFormatter;
|
||||
use github::GithubOutputFormatter;
|
||||
use unix::UnixOutputFormatter;
|
||||
|
||||
pub struct OutputFormatter {
|
||||
format: OutputFormat,
|
||||
}
|
||||
use oxc_diagnostics::reporter::DiagnosticReporter;
|
||||
|
||||
use crate::output_formatter::{default::DefaultOutputFormatter, json::JsonOutputFormatter};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum OutputFormat {
|
||||
|
|
@ -36,15 +41,38 @@ impl FromStr for OutputFormat {
|
|||
}
|
||||
}
|
||||
|
||||
trait InternalFormatter {
|
||||
// print all rules which are currently supported by oxlint
|
||||
fn all_rules(&mut self, writer: &mut dyn Write);
|
||||
|
||||
fn get_diagnostic_reporter(&self) -> Box<dyn DiagnosticReporter>;
|
||||
}
|
||||
|
||||
pub struct OutputFormatter {
|
||||
internal_formatter: Box<dyn InternalFormatter>,
|
||||
}
|
||||
|
||||
impl OutputFormatter {
|
||||
pub fn new(format: OutputFormat) -> Self {
|
||||
Self { format }
|
||||
Self { internal_formatter: Self::get_internal_formatter(format) }
|
||||
}
|
||||
// print all rules which are currently supported by oxlint
|
||||
pub fn all_rules<T: Write>(&self, writer: &mut T) {
|
||||
match self.format {
|
||||
OutputFormat::Json => JsonOutputFormatter::all_rules(writer),
|
||||
_ => DefaultOutputFormatter::all_rules(writer),
|
||||
|
||||
fn get_internal_formatter(format: OutputFormat) -> Box<dyn InternalFormatter> {
|
||||
match format {
|
||||
OutputFormat::Json => Box::<JsonOutputFormatter>::default(),
|
||||
OutputFormat::Checkstyle => Box::<CheckStyleOutputFormatter>::default(),
|
||||
OutputFormat::Github => Box::new(GithubOutputFormatter),
|
||||
OutputFormat::Unix => Box::<UnixOutputFormatter>::default(),
|
||||
OutputFormat::Default => Box::new(DefaultOutputFormatter),
|
||||
}
|
||||
}
|
||||
|
||||
// print all rules which are currently supported by oxlint
|
||||
pub fn all_rules(&mut self, writer: &mut BufWriter<Stdout>) {
|
||||
self.internal_formatter.all_rules(writer);
|
||||
}
|
||||
|
||||
pub fn get_diagnostic_reporter(&self) -> Box<dyn DiagnosticReporter> {
|
||||
self.internal_formatter.get_diagnostic_reporter()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
101
apps/oxlint/src/output_formatter/unix.rs
Normal file
101
apps/oxlint/src/output_formatter/unix.rs
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
use std::{borrow::Cow, io::Write};
|
||||
|
||||
use oxc_diagnostics::{
|
||||
reporter::{DiagnosticReporter, Info},
|
||||
Error, Severity,
|
||||
};
|
||||
|
||||
use crate::output_formatter::InternalFormatter;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct UnixOutputFormatter;
|
||||
|
||||
impl InternalFormatter for UnixOutputFormatter {
|
||||
fn all_rules(&mut self, writer: &mut dyn Write) {
|
||||
writeln!(writer, "flag --rules with flag --format=unix is not allowed").unwrap();
|
||||
}
|
||||
|
||||
fn get_diagnostic_reporter(&self) -> Box<dyn DiagnosticReporter> {
|
||||
Box::new(UnixReporter::default())
|
||||
}
|
||||
}
|
||||
|
||||
/// Reporter to output diagnostics in a simple one line output.
|
||||
/// At the end it reports the total numbers of diagnostics.
|
||||
#[derive(Default)]
|
||||
struct UnixReporter {
|
||||
total: usize,
|
||||
}
|
||||
|
||||
impl DiagnosticReporter for UnixReporter {
|
||||
fn finish(&mut self) -> Option<String> {
|
||||
let total = self.total;
|
||||
if total > 0 {
|
||||
return Some(format!("\n{total} problem{}\n", if total > 1 { "s" } else { "" }));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn render_error(&mut self, error: Error) -> Option<String> {
|
||||
self.total += 1;
|
||||
Some(format_unix(&error))
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://github.com/fregante/eslint-formatters/tree/ae1fd9748596447d1fd09625c33d9e7ba9a3d06d/packages/eslint-formatter-unix>
|
||||
fn format_unix(diagnostic: &Error) -> String {
|
||||
let Info { line, column, filename, message, severity, rule_id } = Info::new(diagnostic);
|
||||
let severity = match severity {
|
||||
Severity::Error => "Error",
|
||||
_ => "Warning",
|
||||
};
|
||||
let rule_id =
|
||||
rule_id.map_or_else(|| Cow::Borrowed(""), |rule_id| Cow::Owned(format!("/{rule_id}")));
|
||||
format!("{filename}:{line}:{column}: {message} [{severity}{rule_id}]\n")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use oxc_diagnostics::{reporter::DiagnosticReporter, NamedSource, OxcDiagnostic};
|
||||
use oxc_span::Span;
|
||||
|
||||
use super::UnixReporter;
|
||||
|
||||
#[test]
|
||||
fn reporter_finish_empty() {
|
||||
let mut reporter = UnixReporter::default();
|
||||
|
||||
let result = reporter.finish();
|
||||
|
||||
assert!(result.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reporter_finish_one_entry() {
|
||||
let mut reporter = UnixReporter::default();
|
||||
|
||||
let error = OxcDiagnostic::warn("error message")
|
||||
.with_label(Span::new(0, 8))
|
||||
.with_source_code(NamedSource::new("file://test.ts", "debugger;"));
|
||||
|
||||
let _ = reporter.render_error(error);
|
||||
let result = reporter.finish();
|
||||
|
||||
assert!(result.is_some());
|
||||
assert_eq!(result.unwrap(), "\n1 problem\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reporter_error() {
|
||||
let mut reporter = UnixReporter::default();
|
||||
let error = OxcDiagnostic::warn("error message")
|
||||
.with_label(Span::new(0, 8))
|
||||
.with_source_code(NamedSource::new("file://test.ts", "debugger;"));
|
||||
|
||||
let result = reporter.render_error(error);
|
||||
|
||||
assert!(result.is_some());
|
||||
assert_eq!(result.unwrap(), "file://test.ts:1:1: error message [Warning]\n");
|
||||
}
|
||||
}
|
||||
|
|
@ -20,4 +20,3 @@ doctest = false
|
|||
|
||||
[dependencies]
|
||||
miette = { workspace = true }
|
||||
rustc-hash = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@
|
|||
//! service.run();
|
||||
//! ```
|
||||
|
||||
mod reporter;
|
||||
mod service;
|
||||
|
||||
use std::{
|
||||
|
|
@ -57,6 +56,8 @@ use std::{
|
|||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
pub mod reporter;
|
||||
|
||||
pub use crate::service::{DiagnosticSender, DiagnosticService, DiagnosticTuple};
|
||||
|
||||
pub type Error = miette::Error;
|
||||
|
|
|
|||
|
|
@ -1,25 +1,7 @@
|
|||
//! [Reporters](DiagnosticReporter) for rendering and writing diagnostics.
|
||||
|
||||
mod checkstyle;
|
||||
mod github;
|
||||
mod graphical;
|
||||
mod json;
|
||||
mod unix;
|
||||
|
||||
use std::io::{BufWriter, Stdout};
|
||||
|
||||
pub use self::{
|
||||
checkstyle::CheckstyleReporter, github::GithubReporter, graphical::GraphicalReporter,
|
||||
json::JsonReporter, unix::UnixReporter,
|
||||
};
|
||||
use crate::{Error, Severity};
|
||||
|
||||
/// stdio is blocked by LineWriter, use a BufWriter to reduce syscalls.
|
||||
/// See `https://github.com/rust-lang/rust/issues/60673`.
|
||||
fn writer() -> BufWriter<Stdout> {
|
||||
BufWriter::new(std::io::stdout())
|
||||
}
|
||||
|
||||
/// Reporters are responsible for rendering diagnostics to some format and writing them to some
|
||||
/// form of output stream.
|
||||
///
|
||||
|
|
@ -28,28 +10,16 @@ fn writer() -> BufWriter<Stdout> {
|
|||
///
|
||||
/// ## Example
|
||||
/// ```
|
||||
/// use std::io::{self, Write, BufWriter, Stderr};
|
||||
/// use oxc_diagnostics::{DiagnosticReporter, Error, Severity};
|
||||
///
|
||||
/// pub struct BufReporter {
|
||||
/// writer: BufWriter<Stderr>,
|
||||
/// }
|
||||
///
|
||||
/// impl Default for BufReporter {
|
||||
/// fn default() -> Self {
|
||||
/// Self { writer: BufWriter::new(io::stderr()) }
|
||||
/// }
|
||||
/// }
|
||||
/// #[derive(Default)]
|
||||
/// pub struct BufferedReporter;
|
||||
///
|
||||
/// impl DiagnosticReporter for BufferedReporter {
|
||||
/// // flush all remaining bytes when no more diagnostics will be reported
|
||||
/// fn finish(&mut self) {
|
||||
/// self.writer.flush().unwrap();
|
||||
/// }
|
||||
///
|
||||
/// // write rendered reports to stderr
|
||||
/// fn render_diagnostics(&mut self, s: &[u8]) {
|
||||
/// self.writer.write_all(s).unwrap();
|
||||
/// // render the finished output, some reporters will store the errors in memory
|
||||
/// // to output all diagnostics at the end
|
||||
/// fn finish(&mut self) -> Option<String> {
|
||||
/// None
|
||||
/// }
|
||||
///
|
||||
/// // render diagnostics to a simple Apache-like log format
|
||||
|
|
@ -68,39 +38,32 @@ fn writer() -> BufWriter<Stdout> {
|
|||
pub trait DiagnosticReporter {
|
||||
/// Lifecycle hook that gets called when no more diagnostics will be reported.
|
||||
///
|
||||
/// Used primarily for flushing output stream buffers, but you don't just have to use it for
|
||||
/// that. Some reporters (e.g. [`JSONReporter`]) store all diagnostics in memory, then write them
|
||||
/// Some reporters (e.g. `JSONReporter`) store all diagnostics in memory, then write them
|
||||
/// all at once.
|
||||
///
|
||||
/// While this method _should_ only ever be called a single time, this is not a guarantee
|
||||
/// upheld in Oxc's API. Do not rely on this behavior.
|
||||
///
|
||||
/// [`JSONReporter`]: crate::reporter::JsonReporter
|
||||
fn finish(&mut self);
|
||||
|
||||
/// Write a rendered collection of diagnostics to this reporter's output stream.
|
||||
fn render_diagnostics(&mut self, s: &[u8]);
|
||||
fn finish(&mut self) -> Option<String>;
|
||||
|
||||
/// Render a diagnostic into this reporter's desired format. For example, a JSONLinesReporter
|
||||
/// might return a stringified JSON object on a single line. Returns [`None`] to skip reporting
|
||||
/// of this diagnostic.
|
||||
///
|
||||
/// Reporters should not use this method to write diagnostics to their output stream. That
|
||||
/// should be done in [`render_diagnostics`](DiagnosticReporter::render_diagnostics).
|
||||
/// Reporters should use this method to write diagnostics to their output stream.
|
||||
fn render_error(&mut self, error: Error) -> Option<String>;
|
||||
}
|
||||
|
||||
struct Info {
|
||||
line: usize,
|
||||
column: usize,
|
||||
filename: String,
|
||||
message: String,
|
||||
severity: Severity,
|
||||
rule_id: Option<String>,
|
||||
pub struct Info {
|
||||
pub line: usize,
|
||||
pub column: usize,
|
||||
pub filename: String,
|
||||
pub message: String,
|
||||
pub severity: Severity,
|
||||
pub rule_id: Option<String>,
|
||||
}
|
||||
|
||||
impl Info {
|
||||
fn new(diagnostic: &Error) -> Self {
|
||||
pub fn new(diagnostic: &Error) -> Self {
|
||||
let mut line = 0;
|
||||
let mut column = 0;
|
||||
let mut filename = String::new();
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
use std::io::{BufWriter, ErrorKind, Stdout, Write};
|
||||
|
||||
use super::{writer, DiagnosticReporter};
|
||||
use crate::{Error, GraphicalReportHandler};
|
||||
|
||||
/// Pretty-prints diagnostics. Primarily meant for human-readable output in a terminal.
|
||||
///
|
||||
/// See [`GraphicalReportHandler`] for how to configure colors, context lines, etc.
|
||||
pub struct GraphicalReporter {
|
||||
handler: GraphicalReportHandler,
|
||||
writer: BufWriter<Stdout>,
|
||||
}
|
||||
|
||||
impl Default for GraphicalReporter {
|
||||
fn default() -> Self {
|
||||
Self { handler: GraphicalReportHandler::new(), writer: writer() }
|
||||
}
|
||||
}
|
||||
|
||||
impl DiagnosticReporter for GraphicalReporter {
|
||||
fn finish(&mut self) {
|
||||
self.writer
|
||||
.flush()
|
||||
.or_else(|e| {
|
||||
// Do not panic when the process is skill (e.g. piping into `less`).
|
||||
if matches!(e.kind(), ErrorKind::Interrupted | ErrorKind::BrokenPipe) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn render_diagnostics(&mut self, s: &[u8]) {
|
||||
self.writer
|
||||
.write_all(s)
|
||||
.or_else(|e| {
|
||||
// Do not panic when the process is skill (e.g. piping into `less`).
|
||||
if matches!(e.kind(), ErrorKind::Interrupted | ErrorKind::BrokenPipe) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn render_error(&mut self, error: Error) -> Option<String> {
|
||||
let mut output = String::new();
|
||||
self.handler.render_report(&mut output, error.as_ref()).unwrap();
|
||||
Some(output)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
use miette::JSONReportHandler;
|
||||
|
||||
use super::DiagnosticReporter;
|
||||
use crate::Error;
|
||||
|
||||
/// Renders reports as a JSON array of objects.
|
||||
///
|
||||
/// Note that, due to syntactic restrictions of JSON arrays, this reporter waits until all
|
||||
/// diagnostics have been reported before writing them to the output stream.
|
||||
#[derive(Default)]
|
||||
pub struct JsonReporter {
|
||||
diagnostics: Vec<Error>,
|
||||
}
|
||||
|
||||
impl DiagnosticReporter for JsonReporter {
|
||||
// NOTE: this output does not conform to eslint json format yet
|
||||
// https://eslint.org/docs/latest/use/formatters/#json
|
||||
fn finish(&mut self) {
|
||||
format_json(&mut self.diagnostics);
|
||||
}
|
||||
|
||||
fn render_diagnostics(&mut self, _s: &[u8]) {}
|
||||
|
||||
fn render_error(&mut self, error: Error) -> Option<String> {
|
||||
self.diagnostics.push(error);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://github.com/fregante/eslint-formatters/tree/ae1fd9748596447d1fd09625c33d9e7ba9a3d06d/packages/eslint-formatter-json>
|
||||
#[allow(clippy::print_stdout)]
|
||||
fn format_json(diagnostics: &mut Vec<Error>) {
|
||||
let handler = JSONReportHandler::new();
|
||||
let messages = diagnostics
|
||||
.drain(..)
|
||||
.map(|error| {
|
||||
let mut output = String::from("\t");
|
||||
handler.render_report(&mut output, error.as_ref()).unwrap();
|
||||
output
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(",\n");
|
||||
println!("[\n{messages}\n]");
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
use std::{
|
||||
borrow::Cow,
|
||||
io::{BufWriter, Stdout, Write},
|
||||
};
|
||||
|
||||
use super::{writer, DiagnosticReporter, Info};
|
||||
use crate::{Error, Severity};
|
||||
|
||||
pub struct UnixReporter {
|
||||
total: usize,
|
||||
writer: BufWriter<Stdout>,
|
||||
}
|
||||
|
||||
impl Default for UnixReporter {
|
||||
fn default() -> Self {
|
||||
Self { total: 0, writer: writer() }
|
||||
}
|
||||
}
|
||||
|
||||
impl DiagnosticReporter for UnixReporter {
|
||||
fn finish(&mut self) {
|
||||
let total = self.total;
|
||||
if total > 0 {
|
||||
let line = format!("\n{total} problem{}\n", if total > 1 { "s" } else { "" });
|
||||
self.writer.write_all(line.as_bytes()).unwrap();
|
||||
}
|
||||
self.writer.flush().unwrap();
|
||||
}
|
||||
|
||||
fn render_diagnostics(&mut self, s: &[u8]) {
|
||||
self.writer.write_all(s).unwrap();
|
||||
}
|
||||
|
||||
fn render_error(&mut self, error: Error) -> Option<String> {
|
||||
self.total += 1;
|
||||
Some(format_unix(&error))
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://github.com/fregante/eslint-formatters/tree/ae1fd9748596447d1fd09625c33d9e7ba9a3d06d/packages/eslint-formatter-unix>
|
||||
fn format_unix(diagnostic: &Error) -> String {
|
||||
let Info { line, column, filename, message, severity, rule_id } = Info::new(diagnostic);
|
||||
let severity = match severity {
|
||||
Severity::Error => "Error",
|
||||
_ => "Warning",
|
||||
};
|
||||
let rule_id =
|
||||
rule_id.map_or_else(|| Cow::Borrowed(""), |rule_id| Cow::Owned(format!("/{rule_id}")));
|
||||
format!("{filename}:{line}:{column}: {message} [{severity}{rule_id}]\n")
|
||||
}
|
||||
|
|
@ -1,16 +1,11 @@
|
|||
use std::{
|
||||
cell::Cell,
|
||||
io::{ErrorKind, Write},
|
||||
path::{Path, PathBuf},
|
||||
sync::{mpsc, Arc},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
reporter::{
|
||||
CheckstyleReporter, DiagnosticReporter, GithubReporter, GraphicalReporter, JsonReporter,
|
||||
UnixReporter,
|
||||
},
|
||||
Error, NamedSource, OxcDiagnostic, Severity,
|
||||
};
|
||||
use crate::{reporter::DiagnosticReporter, Error, NamedSource, OxcDiagnostic, Severity};
|
||||
|
||||
pub type DiagnosticTuple = (PathBuf, Vec<Error>);
|
||||
pub type DiagnosticSender = mpsc::Sender<Option<DiagnosticTuple>>;
|
||||
|
|
@ -71,22 +66,13 @@ pub struct DiagnosticService {
|
|||
receiver: DiagnosticReceiver,
|
||||
}
|
||||
|
||||
impl Default for DiagnosticService {
|
||||
fn default() -> Self {
|
||||
Self::new(GraphicalReporter::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl DiagnosticService {
|
||||
/// Create a new [`DiagnosticService`] that will render and report diagnostics using the
|
||||
/// provided [`DiagnosticReporter`].
|
||||
///
|
||||
/// TODO(@DonIsaac): make `DiagnosticReporter` public so oxc consumers can create their own
|
||||
/// implementations.
|
||||
pub(crate) fn new<R: DiagnosticReporter + 'static>(reporter: R) -> Self {
|
||||
pub fn new(reporter: Box<dyn DiagnosticReporter>) -> Self {
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
Self {
|
||||
reporter: Box::new(reporter) as Box<dyn DiagnosticReporter>,
|
||||
reporter,
|
||||
quiet: false,
|
||||
silent: false,
|
||||
max_warnings: None,
|
||||
|
|
@ -97,25 +83,6 @@ impl DiagnosticService {
|
|||
}
|
||||
}
|
||||
|
||||
/// Configure this service to format reports as a JSON array of objects.
|
||||
pub fn set_json_reporter(&mut self) {
|
||||
self.reporter = Box::<JsonReporter>::default();
|
||||
}
|
||||
|
||||
pub fn set_unix_reporter(&mut self) {
|
||||
self.reporter = Box::<UnixReporter>::default();
|
||||
}
|
||||
|
||||
pub fn set_checkstyle_reporter(&mut self) {
|
||||
self.reporter = Box::<CheckstyleReporter>::default();
|
||||
}
|
||||
|
||||
/// Configure this service to formats reports using [GitHub Actions
|
||||
/// annotations](https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message).
|
||||
pub fn set_github_reporter(&mut self) {
|
||||
self.reporter = Box::<GithubReporter>::default();
|
||||
}
|
||||
|
||||
/// Set to `true` to only report errors and ignore warnings.
|
||||
///
|
||||
/// Use [`with_silent`](DiagnosticService::with_silent) to disable reporting entirely.
|
||||
|
|
@ -198,9 +165,8 @@ impl DiagnosticService {
|
|||
/// # Panics
|
||||
///
|
||||
/// * When the writer fails to write
|
||||
pub fn run(&mut self) {
|
||||
pub fn run(&mut self, writer: &mut dyn Write) {
|
||||
while let Ok(Some((path, diagnostics))) = self.receiver.recv() {
|
||||
let mut output = String::new();
|
||||
for diagnostic in diagnostics {
|
||||
let severity = diagnostic.severity();
|
||||
let is_warning = severity == Some(Severity::Warning);
|
||||
|
|
@ -225,7 +191,7 @@ impl DiagnosticService {
|
|||
continue;
|
||||
}
|
||||
|
||||
if let Some(mut err_str) = self.reporter.render_error(diagnostic) {
|
||||
if let Some(err_str) = self.reporter.render_error(diagnostic) {
|
||||
// Skip large output and print only once.
|
||||
// Setting to 1200 because graphical output may contain ansi escape codes and other decorations.
|
||||
if err_str.lines().any(|line| line.len() >= 1200) {
|
||||
|
|
@ -233,16 +199,40 @@ impl DiagnosticService {
|
|||
OxcDiagnostic::warn("File is too long to fit on the screen")
|
||||
.with_help(format!("{path:?} seems like a minified file")),
|
||||
);
|
||||
err_str = format!("{minified_diagnostic:?}");
|
||||
output = err_str;
|
||||
|
||||
if let Some(err_str) = self.reporter.render_error(minified_diagnostic) {
|
||||
writer
|
||||
.write_all(err_str.as_bytes())
|
||||
.or_else(Self::check_for_writer_error)
|
||||
.unwrap();
|
||||
}
|
||||
break;
|
||||
}
|
||||
output.push_str(&err_str);
|
||||
|
||||
writer
|
||||
.write_all(err_str.as_bytes())
|
||||
.or_else(Self::check_for_writer_error)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
self.reporter.render_diagnostics(output.as_bytes());
|
||||
}
|
||||
|
||||
self.reporter.finish();
|
||||
if let Some(finish_output) = self.reporter.finish() {
|
||||
writer
|
||||
.write_all(finish_output.as_bytes())
|
||||
.or_else(Self::check_for_writer_error)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
writer.flush().or_else(Self::check_for_writer_error).unwrap();
|
||||
}
|
||||
|
||||
fn check_for_writer_error(error: std::io::Error) -> Result<(), std::io::Error> {
|
||||
// Do not panic when the process is skill (e.g. piping into `less`).
|
||||
if matches!(error.kind(), ErrorKind::Interrupted | ErrorKind::BrokenPipe) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
use std::{
|
||||
env,
|
||||
path::{Path, PathBuf},
|
||||
sync::mpsc,
|
||||
};
|
||||
|
||||
use cow_utils::CowUtils;
|
||||
use oxc_allocator::Allocator;
|
||||
use oxc_diagnostics::{DiagnosticService, GraphicalReportHandler, GraphicalTheme, NamedSource};
|
||||
use oxc_diagnostics::{GraphicalReportHandler, GraphicalTheme, NamedSource};
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
|
||||
|
|
@ -471,9 +472,8 @@ impl Tester {
|
|||
let options =
|
||||
LintServiceOptions::new(cwd, paths).with_cross_module(self.plugins.has_import());
|
||||
let lint_service = LintService::from_linter(linter, options);
|
||||
let diagnostic_service = DiagnosticService::default();
|
||||
let tx_error = diagnostic_service.sender();
|
||||
let result = lint_service.run_source(&allocator, source_text, false, tx_error);
|
||||
let (sender, _receiver) = mpsc::channel();
|
||||
let result = lint_service.run_source(&allocator, source_text, false, &sender);
|
||||
|
||||
if result.is_empty() {
|
||||
return TestResult::Passed;
|
||||
|
|
|
|||
Loading…
Reference in a new issue