Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(linter): add output formatter #8436

Merged
merged 3 commits into from
Jan 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/oxlint/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ bpaf = { workspace = true, features = ["autocomplete", "bright-color", "derive"]
ignore = { workspace = true, features = ["simd-accel"] }
miette = { workspace = true }
rayon = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
tempfile = { workspace = true }
tracing-subscriber = { workspace = true, features = [] } # Omit the `regex` feature

Expand Down
30 changes: 3 additions & 27 deletions apps/oxlint/src/command/lint.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use std::{path::PathBuf, str::FromStr};
use std::path::PathBuf;

use bpaf::Bpaf;
use oxc_linter::{AllowWarnDeny, FixKind, LintPlugins};

use crate::output_formatter::OutputFormat;

use super::{
ignore::{ignore_options, IgnoreOptions},
misc_options, validate_paths, MiscOptions, PATHS_ERROR_MESSAGE, VERSION,
Expand Down Expand Up @@ -184,32 +186,6 @@ pub struct OutputOptions {
pub format: OutputFormat,
}

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum OutputFormat {
Default,
/// GitHub Check Annotation
/// <https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-a-notice-message>
Github,
Json,
Unix,
Checkstyle,
}

impl FromStr for OutputFormat {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"json" => Ok(Self::Json),
"default" => Ok(Self::Default),
"unix" => Ok(Self::Unix),
"checkstyle" => Ok(Self::Checkstyle),
"github" => Ok(Self::Github),
_ => Err(format!("'{s}' is not a known format")),
}
}
}

/// Enable Plugins
#[allow(clippy::struct_field_names)]
#[derive(Debug, Default, Clone, Bpaf)]
Expand Down
2 changes: 1 addition & 1 deletion apps/oxlint/src/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use bpaf::Bpaf;

pub use self::{
ignore::IgnoreOptions,
lint::{lint_command, LintCommand, OutputFormat, OutputOptions, WarningOptions},
lint::{lint_command, LintCommand, OutputOptions, WarningOptions},
};

const VERSION: &str = match option_env!("OXC_VERSION") {
Expand Down
1 change: 1 addition & 0 deletions apps/oxlint/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod command;
mod lint;
mod output_formatter;
mod result;
mod runner;
mod walk;
Expand Down
13 changes: 6 additions & 7 deletions apps/oxlint/src/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ use oxc_span::VALID_EXTENSIONS;

use crate::{
cli::{
CliRunResult, LintCommand, LintResult, MiscOptions, OutputFormat, OutputOptions, Runner,
WarningOptions,
CliRunResult, LintCommand, LintResult, MiscOptions, OutputOptions, Runner, WarningOptions,
},
output_formatter::{OutputFormat, OutputFormatter},
walk::{Extensions, Walk},
};

Expand All @@ -36,13 +36,12 @@ impl Runner for LintRunner {
}

fn run(self) -> CliRunResult {
let format_str = self.options.output_options.format;
let output_formatter = OutputFormatter::new(format_str);

if self.options.list_rules {
let mut stdout = BufWriter::new(std::io::stdout());
if self.options.output_options.format == OutputFormat::Json {
Linter::print_rules_json(&mut stdout);
} else {
Linter::print_rules(&mut stdout);
}
output_formatter.all_rules(&mut stdout);
return CliRunResult::None;
}

Expand Down
29 changes: 29 additions & 0 deletions apps/oxlint/src/output_formatter/default.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use std::io::Write;

use oxc_linter::table::RuleTable;

pub struct DefaultOutputFormatter;

impl DefaultOutputFormatter {
pub fn all_rules<T: Write>(writer: &mut T) {
let table = RuleTable::new();
for section in table.sections {
writeln!(writer, "{}", section.render_markdown_table(None)).unwrap();
}
writeln!(writer, "Default: {}", table.turned_on_by_default_count).unwrap();
writeln!(writer, "Total: {}", table.total).unwrap();
}
}

#[cfg(test)]
mod test {
use crate::output_formatter::default::DefaultOutputFormatter;

#[test]
fn all_rules() {
let mut writer = Vec::new();

DefaultOutputFormatter::all_rules(&mut writer);
assert!(!writer.is_empty());
}
}
31 changes: 31 additions & 0 deletions apps/oxlint/src/output_formatter/json.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use oxc_linter::rules::RULES;
use oxc_linter::RuleCategory;
use std::io::Write;

#[derive(Debug)]
pub struct JsonOutputFormatter;

impl JsonOutputFormatter {
pub fn all_rules<T: Write>(writer: &mut T) {
#[derive(Debug, serde::Serialize)]
struct RuleInfoJson<'a> {
scope: &'a str,
value: &'a str,
category: RuleCategory,
}

let rules_info = RULES.iter().map(|rule| RuleInfoJson {
scope: rule.plugin_name(),
value: rule.name(),
category: rule.category(),
});

writer
.write_all(
serde_json::to_string_pretty(&rules_info.collect::<Vec<_>>())
.expect("Failed to serialize")
.as_bytes(),
)
.unwrap();
}
}
50 changes: 50 additions & 0 deletions apps/oxlint/src/output_formatter/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
mod default;
mod json;

use std::io::Write;
use std::str::FromStr;

use crate::output_formatter::{default::DefaultOutputFormatter, json::JsonOutputFormatter};

pub struct OutputFormatter {
format: OutputFormat,
}

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum OutputFormat {
Default,
/// GitHub Check Annotation
/// <https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-a-notice-message>
Github,
Json,
Unix,
Checkstyle,
}

impl FromStr for OutputFormat {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"json" => Ok(Self::Json),
"default" => Ok(Self::Default),
"unix" => Ok(Self::Unix),
"checkstyle" => Ok(Self::Checkstyle),
"github" => Ok(Self::Github),
_ => Err(format!("'{s}' is not a known format")),
}
}
}

impl OutputFormatter {
pub fn new(format: OutputFormat) -> Self {
Self { 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),
}
}
}
49 changes: 3 additions & 46 deletions crates/oxc_linter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,16 @@ mod module_graph_visitor;
mod module_record;
mod options;
mod rule;
mod rules;
mod service;
mod utils;

pub mod loader;
pub mod rules;
pub mod table;

use std::{io::Write, path::Path, rc::Rc, sync::Arc};
use std::{path::Path, rc::Rc, sync::Arc};

use oxc_semantic::{AstNode, Semantic};
use rules::RULES;

pub use crate::{
config::{
Expand All @@ -45,7 +44,6 @@ use crate::{
context::ContextHost,
fixer::{Fixer, Message},
rules::RuleEnum,
table::RuleTable,
utils::iter_possible_jest_call_node,
};

Expand Down Expand Up @@ -183,52 +181,11 @@ impl Linter {

ctx_host.take_diagnostics()
}

/// # Panics
pub fn print_rules<W: Write>(writer: &mut W) {
let table = RuleTable::new();
for section in table.sections {
writeln!(writer, "{}", section.render_markdown_table(None)).unwrap();
}
writeln!(writer, "Default: {}", table.turned_on_by_default_count).unwrap();
writeln!(writer, "Total: {}", table.total).unwrap();
}

/// # Panics
pub fn print_rules_json<W: Write>(writer: &mut W) {
#[derive(Debug, serde::Serialize)]
struct RuleInfoJson<'a> {
scope: &'a str,
value: &'a str,
category: RuleCategory,
}

let rules_info = RULES.iter().map(|rule| RuleInfoJson {
scope: rule.plugin_name(),
value: rule.name(),
category: rule.category(),
});

writer
.write_all(
serde_json::to_string_pretty(&rules_info.collect::<Vec<_>>())
.expect("Failed to serialize")
.as_bytes(),
)
.unwrap();
}
}

#[cfg(test)]
mod test {
use super::{Linter, Oxlintrc};

#[test]
fn print_rules() {
let mut writer = Vec::new();
Linter::print_rules(&mut writer);
assert!(!writer.is_empty());
}
use super::Oxlintrc;

#[test]
fn test_schema_json() {
Expand Down
Loading