feat(tasks/website): code generate the linter rules

This commit is contained in:
Boshen 2024-05-23 15:09:33 +08:00
parent ead637bf50
commit 3671b5c4d0
No known key found for this signature in database
GPG key ID: 9C7A8C8AB22BEBD1
5 changed files with 156 additions and 57 deletions

View file

@ -12,15 +12,15 @@ mod fixer;
mod globals;
mod javascript_globals;
mod options;
pub mod partial_loader;
pub mod rule;
mod rule;
mod rules;
mod service;
mod utils;
use std::{io::Write, rc::Rc, sync::Arc};
pub mod partial_loader;
pub mod table;
use rustc_hash::{FxHashMap, FxHashSet};
use std::{io::Write, rc::Rc, sync::Arc};
use oxc_diagnostics::Error;
use oxc_semantic::AstNode;
@ -29,15 +29,15 @@ pub use crate::{
config::ESLintConfig,
context::LintContext,
options::{AllowWarnDeny, LintOptions},
rule::RuleWithSeverity,
rule::{RuleCategory, RuleMeta, RuleWithSeverity},
service::{LintService, LintServiceOptions},
};
use crate::{
config::{ESLintEnv, ESLintGlobals, ESLintSettings},
fixer::Fix,
fixer::{Fixer, Message},
rule::RuleCategory,
rules::{RuleEnum, RULES},
rules::RuleEnum,
table::RuleTable,
};
#[cfg(target_pointer_width = "64")]
@ -132,51 +132,12 @@ impl Linter {
/// # Panics
pub fn print_rules<W: Write>(writer: &mut W) {
let default_rules = Linter::default()
.rules
.into_iter()
.map(|rule| rule.name())
.collect::<FxHashSet<&str>>();
let rules_by_category = RULES.iter().fold(
FxHashMap::default(),
|mut map: FxHashMap<RuleCategory, Vec<&RuleEnum>>, rule| {
map.entry(rule.category()).or_default().push(rule);
map
},
);
let mut default_count = 0;
for (category, rules) in rules_by_category {
writeln!(writer, "## {} ({}):", category, rules.len()).unwrap();
let rule_width = rules.iter().map(|r| r.name().len()).max().unwrap();
let plugin_width = rules.iter().map(|r| r.plugin_name().len()).max().unwrap();
let x = "";
writeln!(
writer,
"| {:<rule_width$} | {:<plugin_width$} | Default |",
"Rule name", "Source"
)
.unwrap();
writeln!(writer, "| {x:-<rule_width$} | {x:-<plugin_width$} | {x:-<7} |").unwrap();
for rule in rules {
let rule_name = rule.name();
let plugin_name = rule.plugin_name();
let (default, default_width) = if default_rules.contains(rule_name) {
default_count += 1;
("", 6)
} else {
("", 7)
};
writeln!(writer, "| {rule_name:<rule_width$} | {plugin_name:<plugin_width$} | {default:<default_width$} |").unwrap();
}
writeln!(writer).unwrap();
let table = RuleTable::new();
for section in table.sections {
writeln!(writer, "{}", section.render_markdown_table()).unwrap();
}
writeln!(writer, "Default: {default_count}").unwrap();
writeln!(writer, "Total: {}", RULES.len()).unwrap();
writeln!(writer, "Default: {}", table.turned_on_by_default_count).unwrap();
writeln!(writer, "Total: {}", table.total).unwrap();
}
}

View file

@ -122,7 +122,7 @@ impl RuleWithSeverity {
#[cfg(test)]
mod test {
use crate::RULES;
use crate::rules::RULES;
#[test]
fn ensure_documentation() {

View file

@ -0,0 +1,112 @@
use std::{collections::HashMap, fmt::Write};
use rustc_hash::FxHashSet;
use crate::{rules::RULES, Linter};
pub struct RuleTable {
pub sections: Vec<RuleTableSection>,
pub total: usize,
pub turned_on_by_default_count: usize,
}
pub struct RuleTableSection {
pub rows: Vec<RuleTableRow>,
pub category: String,
pub rule_column_width: usize,
pub plugin_column_width: usize,
}
pub struct RuleTableRow {
pub name: &'static str,
pub plugin: String,
pub category: String,
pub documentation: Option<&'static str>,
pub turned_on_by_default: bool,
}
impl Default for RuleTable {
fn default() -> Self {
Self::new()
}
}
impl RuleTable {
pub fn new() -> Self {
let default_rules = Linter::default()
.rules
.into_iter()
.map(|rule| rule.name())
.collect::<FxHashSet<&str>>();
let mut rows = RULES
.iter()
.map(|rule| {
let name = rule.name();
RuleTableRow {
name,
documentation: rule.documentation(),
plugin: rule.plugin_name().to_string(),
category: rule.category().to_string(),
turned_on_by_default: default_rules.contains(name),
}
})
.collect::<Vec<_>>();
let total = rows.len();
rows.sort_by_key(|row| (row.plugin.clone(), row.name));
let mut rows_by_category = rows.into_iter().fold(
HashMap::default(),
|mut map: HashMap<String, Vec<RuleTableRow>>, row| {
map.entry(row.category.clone()).or_default().push(row);
map
},
);
let sections =
["Correctness", "Perf", "Restriction", "Suspicious", "Pedantic", "Style", "Nursery"]
.into_iter()
.filter_map(|category| {
let rows = rows_by_category.remove(category)?;
let rule_column_width = rows.iter().map(|r| r.name.len()).max()?;
let plugin_column_width = rows.iter().map(|r| r.plugin.len()).max()?;
Some(RuleTableSection {
rows,
category: category.to_string(),
rule_column_width,
plugin_column_width,
})
})
.collect::<Vec<_>>();
RuleTable { total, sections, turned_on_by_default_count: default_rules.len() }
}
}
impl RuleTableSection {
pub fn render_markdown_table(&self) -> String {
let mut s = String::new();
let category = &self.category;
let rows = &self.rows;
let rule_width = self.rule_column_width;
let plugin_width = self.plugin_column_width;
writeln!(s, "## {} ({}):", category, rows.len()).unwrap();
let x = "";
writeln!(s, "| {:<rule_width$} | {:<plugin_width$} | Default |", "Rule name", "Source")
.unwrap();
writeln!(s, "| {x:-<rule_width$} | {x:-<plugin_width$} | {x:-<7} |").unwrap();
for row in rows {
let rule_name = row.name;
let plugin_name = &row.plugin;
let (default, default_width) =
if row.turned_on_by_default { ("", 6) } else { ("", 7) };
writeln!(s, "| {rule_name:<rule_width$} | {plugin_name:<plugin_width$} | {default:<default_width$} |").unwrap();
}
s
}
}

View file

@ -1,4 +1,4 @@
use oxc_linter::rule::{RuleCategory, RuleMeta};
use oxc_linter::{RuleCategory, RuleMeta};
use oxc_macros::declare_oxc_lint_test;
struct TestRule;

View file

@ -1,7 +1,33 @@
use oxc_linter::table::RuleTable;
// `cargo run -p website linter-rules > /path/to/oxc/oxc-project.github.io/src/docs/guide/usage/linter/generated-rules.md`
// <https://oxc-project.github.io/docs/guide/usage/linter/rules.html>
pub fn generate_rules() {
use oxc_linter::Linter;
let mut v = vec![];
Linter::print_rules(&mut v);
println!("{}", String::from_utf8(v).unwrap());
let table = RuleTable::new();
let total = table.total;
let turned_on_by_default_count = table.turned_on_by_default_count;
let body = table
.sections
.into_iter()
.map(|section| section.render_markdown_table())
.collect::<Vec<_>>()
.join("\n");
println!("
# Rules
The progress of all rule implementations is tracked [here](https://github.com/oxc-project/oxc/issues/481).
- Total number of rules: {total}
- Rules turned on by default: {turned_on_by_default_count}
<!-- textlint-disable terminology -->
{body}
<!-- textlint-enable -->
");
}