mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(tasks/website): code generate the linter rules
This commit is contained in:
parent
ead637bf50
commit
3671b5c4d0
5 changed files with 156 additions and 57 deletions
|
|
@ -12,15 +12,15 @@ mod fixer;
|
||||||
mod globals;
|
mod globals;
|
||||||
mod javascript_globals;
|
mod javascript_globals;
|
||||||
mod options;
|
mod options;
|
||||||
pub mod partial_loader;
|
mod rule;
|
||||||
pub mod rule;
|
|
||||||
mod rules;
|
mod rules;
|
||||||
mod service;
|
mod service;
|
||||||
mod utils;
|
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_diagnostics::Error;
|
||||||
use oxc_semantic::AstNode;
|
use oxc_semantic::AstNode;
|
||||||
|
|
@ -29,15 +29,15 @@ pub use crate::{
|
||||||
config::ESLintConfig,
|
config::ESLintConfig,
|
||||||
context::LintContext,
|
context::LintContext,
|
||||||
options::{AllowWarnDeny, LintOptions},
|
options::{AllowWarnDeny, LintOptions},
|
||||||
rule::RuleWithSeverity,
|
rule::{RuleCategory, RuleMeta, RuleWithSeverity},
|
||||||
service::{LintService, LintServiceOptions},
|
service::{LintService, LintServiceOptions},
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{ESLintEnv, ESLintGlobals, ESLintSettings},
|
config::{ESLintEnv, ESLintGlobals, ESLintSettings},
|
||||||
fixer::Fix,
|
fixer::Fix,
|
||||||
fixer::{Fixer, Message},
|
fixer::{Fixer, Message},
|
||||||
rule::RuleCategory,
|
rules::RuleEnum,
|
||||||
rules::{RuleEnum, RULES},
|
table::RuleTable,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
|
@ -132,51 +132,12 @@ impl Linter {
|
||||||
|
|
||||||
/// # Panics
|
/// # Panics
|
||||||
pub fn print_rules<W: Write>(writer: &mut W) {
|
pub fn print_rules<W: Write>(writer: &mut W) {
|
||||||
let default_rules = Linter::default()
|
let table = RuleTable::new();
|
||||||
.rules
|
for section in table.sections {
|
||||||
.into_iter()
|
writeln!(writer, "{}", section.render_markdown_table()).unwrap();
|
||||||
.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();
|
|
||||||
}
|
}
|
||||||
writeln!(writer, "Default: {default_count}").unwrap();
|
writeln!(writer, "Default: {}", table.turned_on_by_default_count).unwrap();
|
||||||
writeln!(writer, "Total: {}", RULES.len()).unwrap();
|
writeln!(writer, "Total: {}", table.total).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ impl RuleWithSeverity {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::RULES;
|
use crate::rules::RULES;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ensure_documentation() {
|
fn ensure_documentation() {
|
||||||
|
|
|
||||||
112
crates/oxc_linter/src/table.rs
Normal file
112
crates/oxc_linter/src/table.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use oxc_linter::rule::{RuleCategory, RuleMeta};
|
use oxc_linter::{RuleCategory, RuleMeta};
|
||||||
use oxc_macros::declare_oxc_lint_test;
|
use oxc_macros::declare_oxc_lint_test;
|
||||||
|
|
||||||
struct TestRule;
|
struct TestRule;
|
||||||
|
|
|
||||||
|
|
@ -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>
|
// <https://oxc-project.github.io/docs/guide/usage/linter/rules.html>
|
||||||
pub fn generate_rules() {
|
pub fn generate_rules() {
|
||||||
use oxc_linter::Linter;
|
let table = RuleTable::new();
|
||||||
let mut v = vec![];
|
|
||||||
Linter::print_rules(&mut v);
|
let total = table.total;
|
||||||
println!("{}", String::from_utf8(v).unwrap());
|
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 -->
|
||||||
|
|
||||||
|
");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue