diff --git a/apps/oxlint/src/lint.rs b/apps/oxlint/src/lint.rs index 4c36af07d6929..aa4c0e2941abb 100644 --- a/apps/oxlint/src/lint.rs +++ b/apps/oxlint/src/lint.rs @@ -17,7 +17,6 @@ use oxc_diagnostics::{DiagnosticSender, DiagnosticService, GraphicalReportHandle use oxc_linter::{ AllowWarnDeny, Config, ConfigStore, ConfigStoreBuilder, ExternalLinter, ExternalPluginStore, InvalidFilterKind, LintFilter, LintOptions, LintRunner, LintServiceOptions, Linter, Oxlintrc, - table::RuleTable, }; use crate::{ @@ -304,25 +303,20 @@ impl CliRunner { // If the user requested `--rules`, print a CLI-specific table that // includes an "Enabled?" column based on the resolved configuration. if self.options.list_rules { - // Preserve previous behavior of `--rules` output when `-f` is set - if self.options.output_options.format == OutputFormat::Default { + // Put together the enabled hashset if the format is default or json, otherwise None + let is_format_with_enabled = matches!( + self.options.output_options.format, + OutputFormat::Default | OutputFormat::Json + ); + + let enabled: Option> = if is_format_with_enabled { // Build the set of enabled builtin rule names from the resolved config. - let enabled: FxHashSet<&str> = - config_store.rules().iter().map(|(rule, _)| rule.name()).collect(); - - let table = RuleTable::default(); - for section in &table.sections { - let md = section.render_markdown_table_cli(None, &enabled); - print_and_flush_stdout(stdout, &md); - print_and_flush_stdout(stdout, "\n"); - } + Some(config_store.rules().iter().map(|(rule, _)| rule.name()).collect()) + } else { + None + }; - print_and_flush_stdout( - stdout, - format!("Default: {}\n", table.turned_on_by_default_count).as_str(), - ); - print_and_flush_stdout(stdout, format!("Total: {}\n", table.total).as_str()); - } else if let Some(output) = output_formatter.all_rules() { + if let Some(output) = output_formatter.all_rules(enabled.as_ref()) { print_and_flush_stdout(stdout, &output); } @@ -1310,6 +1304,7 @@ mod test { assert!(rule_obj.contains_key("scope"), "Rule should contain 'scope' field"); assert!(rule_obj.contains_key("value"), "Rule should contain 'value' field"); assert!(rule_obj.contains_key("category"), "Rule should contain 'category' field"); + assert!(rule_obj.contains_key("enabled"), "Rule should contain 'enabled' field"); } } diff --git a/apps/oxlint/src/output_formatter/default.rs b/apps/oxlint/src/output_formatter/default.rs index 236bb96f017c9..938be973c3abc 100644 --- a/apps/oxlint/src/output_formatter/default.rs +++ b/apps/oxlint/src/output_formatter/default.rs @@ -6,19 +6,23 @@ use oxc_diagnostics::{ reporter::{DiagnosticReporter, DiagnosticResult}, }; use oxc_linter::table::RuleTable; +use rustc_hash::FxHashSet; #[derive(Debug)] pub struct DefaultOutputFormatter; impl InternalFormatter for DefaultOutputFormatter { - fn all_rules(&self) -> Option { + fn all_rules(&self, enabled: Option<&FxHashSet<&str>>) -> Option { let mut output = String::new(); let table = RuleTable::default(); for section in table.sections { - output.push_str(section.render_markdown_table(None).as_str()); + output.push_str(section.render_markdown_table(None, enabled).as_str()); output.push('\n'); } output.push_str(format!("Default: {}\n", table.turned_on_by_default_count).as_str()); + if let Some(enabled) = enabled { + output.push_str(format!("Enabled: {}\n", enabled.len()).as_str()); + } output.push_str(format!("Total: {}\n", table.total).as_str()); Some(output) } @@ -163,13 +167,26 @@ mod test { default::{DefaultOutputFormatter, GraphicalReporter}, }; use oxc_diagnostics::reporter::{DiagnosticReporter, DiagnosticResult}; + use rustc_hash::FxHashSet; #[test] fn all_rules() { let formatter = DefaultOutputFormatter; - let result = formatter.all_rules(); + let result = formatter.all_rules(None); + + assert!(result.is_some()); + } + + #[test] + fn all_rules_with_enabled() { + let formatter = DefaultOutputFormatter; + // Pass in one enabled rule to make sure it renders fine: + let mut enabled = FxHashSet::default(); + enabled.insert("no-unused-vars"); + let result = formatter.all_rules(Some(&enabled)); assert!(result.is_some()); + assert!(result.unwrap().contains("Enabled: 1\n")); } #[test] diff --git a/apps/oxlint/src/output_formatter/json.rs b/apps/oxlint/src/output_formatter/json.rs index 095902056cbac..0996e63de0b07 100644 --- a/apps/oxlint/src/output_formatter/json.rs +++ b/apps/oxlint/src/output_formatter/json.rs @@ -1,6 +1,7 @@ use std::{cell::RefCell, rc::Rc}; use miette::JSONReportHandler; +use rustc_hash::FxHashSet; use serde::Serialize; use oxc_diagnostics::{ @@ -17,18 +18,20 @@ pub struct JsonOutputFormatter { } impl InternalFormatter for JsonOutputFormatter { - fn all_rules(&self) -> Option { + fn all_rules(&self, enabled: Option<&FxHashSet<&str>>) -> Option { #[derive(Debug, Serialize)] struct RuleInfoJson<'a> { scope: &'a str, value: &'a str, category: RuleCategory, + enabled: bool, } let rules_info = RULES.iter().map(|rule| RuleInfoJson { scope: rule.plugin_name(), value: rule.name(), category: rule.category(), + enabled: enabled.is_some_and(|enabled_set| enabled_set.contains(rule.name())), }); Some( diff --git a/apps/oxlint/src/output_formatter/mod.rs b/apps/oxlint/src/output_formatter/mod.rs index 39a922fae62ea..830da899aaa29 100644 --- a/apps/oxlint/src/output_formatter/mod.rs +++ b/apps/oxlint/src/output_formatter/mod.rs @@ -15,6 +15,7 @@ use checkstyle::CheckStyleOutputFormatter; use github::GithubOutputFormatter; use gitlab::GitlabOutputFormatter; use junit::JUnitOutputFormatter; +use rustc_hash::FxHashSet; use stylish::StylishOutputFormatter; use unix::UnixOutputFormatter; @@ -72,7 +73,7 @@ pub struct LintCommandInfo { /// The Formatter is then managed by [`OutputFormatter`]. trait InternalFormatter { /// Print all available rules by oxlint - fn all_rules(&self) -> Option { + fn all_rules(&self, _enabled: Option<&FxHashSet<&str>>) -> Option { None } @@ -110,8 +111,8 @@ impl OutputFormatter { /// Print all available rules by oxlint /// See [`InternalFormatter::all_rules`] for more details. - pub fn all_rules(&self) -> Option { - self.internal.all_rules() + pub fn all_rules(&self, enabled: Option<&FxHashSet<&str>>) -> Option { + self.internal.all_rules(enabled) } /// At the end of the Lint command we may output extra information. diff --git a/crates/oxc_linter/src/config/config_store.rs b/crates/oxc_linter/src/config/config_store.rs index 0b94ad4944221..a6a89d54c35fa 100644 --- a/crates/oxc_linter/src/config/config_store.rs +++ b/crates/oxc_linter/src/config/config_store.rs @@ -302,6 +302,7 @@ impl ConfigStore { Some(count) } + // TODO: fix this to return rules based on whether type_aware is enabled pub fn rules(&self) -> &Arc<[(RuleEnum, AllowWarnDeny)]> { &self.base.base.rules } diff --git a/crates/oxc_linter/src/table.rs b/crates/oxc_linter/src/table.rs index d79258f4f88fb..cbecc7a4270b1 100644 --- a/crates/oxc_linter/src/table.rs +++ b/crates/oxc_linter/src/table.rs @@ -225,16 +225,15 @@ impl RuleTableSection { /// /// Provide [`Some`] prefix to render the rule name as a link. Provide /// [`None`] to just display the rule name as text. - pub fn render_markdown_table(&self, link_prefix: Option<&str>) -> String { - self.render_markdown_table_inner(link_prefix, None) - } - - pub fn render_markdown_table_cli( + /// + /// Provide [`Some`] set of enabled rule names to include an "Enabled?" column. + /// Provide [`None`] to omit the column. + pub fn render_markdown_table( &self, link_prefix: Option<&str>, - enabled: &FxHashSet<&str>, + enabled: Option<&FxHashSet<&str>>, ) -> String { - self.render_markdown_table_inner(link_prefix, Some(enabled)) + self.render_markdown_table_inner(link_prefix, enabled) } } @@ -256,7 +255,7 @@ mod test { fn test_table_no_links() { let options = Options::gfm(); for section in &table().sections { - let rendered_table = section.render_markdown_table(None); + let rendered_table = section.render_markdown_table(None, None); assert!(!rendered_table.is_empty()); assert_eq!(rendered_table.split('\n').count(), 5 + section.rows.len()); @@ -274,7 +273,7 @@ mod test { let options = Options::gfm(); for section in &table().sections { - let rendered_table = section.render_markdown_table(Some(PREFIX)); + let rendered_table = section.render_markdown_table(Some(PREFIX), None); assert!(!rendered_table.is_empty()); assert_eq!(rendered_table.split('\n').count(), 5 + section.rows.len()); @@ -296,7 +295,7 @@ mod test { enabled.insert(first.name); } - let rendered_table = section.render_markdown_table_cli(Some(PREFIX), &enabled); + let rendered_table = section.render_markdown_table(Some(PREFIX), Some(&enabled)); assert!(!rendered_table.is_empty()); // same number of lines as other renderer (header + desc + separator + rows + trailing newline) assert_eq!(rendered_table.split('\n').count(), 5 + section.rows.len()); diff --git a/tasks/website/src/linter/rules/table.rs b/tasks/website/src/linter/rules/table.rs index ba5cec35ce423..f7c675d73f735 100644 --- a/tasks/website/src/linter/rules/table.rs +++ b/tasks/website/src/linter/rules/table.rs @@ -14,7 +14,7 @@ pub fn render_rules_table(table: &RuleTable, docs_prefix: &str) -> String { let body = table .sections .iter() - .map(|s| s.render_markdown_table(Some(docs_prefix))) + .map(|s| s.render_markdown_table(Some(docs_prefix), None)) .collect::>() .join("\n");