mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(linter): configure rules from json
This commit is contained in:
parent
a342a01c4a
commit
5c64b4874c
7 changed files with 66 additions and 48 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -884,6 +884,7 @@ dependencies = [
|
|||
"oxc_diagnostics",
|
||||
"oxc_parser",
|
||||
"oxc_semantic",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ oxc_diagnostics = { path = "../oxc_diagnostics" }
|
|||
oxc_semantic = { path = "../oxc_semantic" }
|
||||
|
||||
lazy_static = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
[dev_dependencies]
|
||||
oxc_allocator = { path = "../oxc_allocator" }
|
||||
|
|
|
|||
|
|
@ -5,5 +5,9 @@ use crate::{context::LintContext, AstNode};
|
|||
pub trait Rule: Sized + Default + Debug {
|
||||
const NAME: &'static str;
|
||||
|
||||
fn from_json(_value: serde_json::Value) -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn run<'a>(&self, node: &AstNode<'a>, _ctx: &LintContext<'a>);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,17 @@ impl RuleEnum {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_json(&self, maybe_value: Option<serde_json::Value>) -> Self {
|
||||
match self {
|
||||
Self::NoDebugger(_) => {
|
||||
RuleEnum::NoDebugger(maybe_value.map(NoDebugger::from_json).unwrap_or_default())
|
||||
}
|
||||
Self::NoEmpty(_) => {
|
||||
RuleEnum::NoEmpty(maybe_value.map(NoEmpty::from_json).unwrap_or_default())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
||||
match self {
|
||||
Self::NoDebugger(rule) => rule.run(node, ctx),
|
||||
|
|
|
|||
|
|
@ -30,9 +30,9 @@ impl Rule for NoDebugger {
|
|||
fn test() {
|
||||
use crate::tester::Tester;
|
||||
|
||||
let pass = vec!["var test = { debugger: 1 }; test.debugger;"];
|
||||
let pass = vec![("var test = { debugger: 1 }; test.debugger;", None)];
|
||||
|
||||
let fail = vec!["if (foo) debugger"];
|
||||
let fail = vec![("if (foo) debugger", None)];
|
||||
|
||||
Tester::new(RULE_NAME, pass, fail).test_and_snapshot();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,43 +48,43 @@ fn test() {
|
|||
use crate::tester::Tester;
|
||||
|
||||
let pass = vec![
|
||||
"if (foo) { bar() }",
|
||||
"while (foo) { bar() }",
|
||||
"for (;foo;) { bar() }",
|
||||
"try { foo() } catch (ex) { foo() }",
|
||||
"switch(foo) {case 'foo': break;}",
|
||||
"(function() { }())",
|
||||
"var foo = () => {};",
|
||||
"function foo() { }",
|
||||
"if (foo) {/* empty */}",
|
||||
"while (foo) {/* empty */}",
|
||||
"for (;foo;) {/* empty */}",
|
||||
"try { foo() } catch (ex) {/* empty */}",
|
||||
"try { foo() } catch (ex) {// empty\n}",
|
||||
"try { foo() } finally {// empty\n}",
|
||||
"try { foo() } finally {// test\n}",
|
||||
"try { foo() } finally {\n \n // hi i am off no use\n}",
|
||||
"try { foo() } catch (ex) {/* test111 */}",
|
||||
"if (foo) { bar() } else { // nothing in me \n}",
|
||||
"if (foo) { bar() } else { /**/ \n}",
|
||||
"if (foo) { bar() } else { // \n}",
|
||||
"try { foo(); } catch (ex) {}",
|
||||
"try { foo(); } catch (ex) {} finally { bar(); }",
|
||||
("if (foo) { bar() }", None),
|
||||
("while (foo) { bar() }", None),
|
||||
("for (;foo;) { bar() }", None),
|
||||
("try { foo() } catch (ex) { foo() }", None),
|
||||
("switch(foo) {case 'foo': break;}", None),
|
||||
("(function() { }())", None),
|
||||
("var foo = () => {};", None),
|
||||
("function foo() { }", None),
|
||||
("if (foo) {/* empty */}", None),
|
||||
("while (foo) {/* empty */}", None),
|
||||
("for (;foo;) {/* empty */}", None),
|
||||
("try { foo() } catch (ex) {/* empty */}", None),
|
||||
("try { foo() } catch (ex) {// empty\n}", None),
|
||||
("try { foo() } finally {// empty\n}", None),
|
||||
("try { foo() } finally {// test\n}", None),
|
||||
("try { foo() } finally {\n \n // hi i am off no use\n}", None),
|
||||
("try { foo() } catch (ex) {/* test111 */}", None),
|
||||
("if (foo) { bar() } else { // nothing in me \n}", None),
|
||||
("if (foo) { bar() } else { /**/ \n}", None),
|
||||
("if (foo) { bar() } else { // \n}", None),
|
||||
("try { foo(); } catch (ex) {}", None),
|
||||
("try { foo(); } catch (ex) {} finally { bar(); }", None),
|
||||
];
|
||||
|
||||
let fail = vec![
|
||||
"try {} catch (ex) {throw ex}",
|
||||
("try {} catch (ex) {throw ex}", None),
|
||||
("try { foo() } catch (ex) {throw ex} finally {}", None),
|
||||
// "try { foo() } catch (ex) {}", // TODO: options
|
||||
"try { foo() } catch (ex) {throw ex} finally {}",
|
||||
"if (foo) {}",
|
||||
"while (foo) {}",
|
||||
"for (;foo;) {}",
|
||||
"switch(foo) {}",
|
||||
"switch (foo) { /* empty */ }",
|
||||
"try {} catch (ex) {}",
|
||||
"try { foo(); } catch (ex) {} finally {}",
|
||||
"try {} catch (ex) {} finally {}",
|
||||
"try { foo(); } catch (ex) {} finally {}",
|
||||
("if (foo) {}", None),
|
||||
("while (foo) {}", None),
|
||||
("for (;foo;) {}", None),
|
||||
("switch(foo) {}", None),
|
||||
("switch (foo) { /* empty */ }", None),
|
||||
("try {} catch (ex) {}", None),
|
||||
("try { foo(); } catch (ex) {} finally {}", None),
|
||||
("try {} catch (ex) {} finally {}", None),
|
||||
("try { foo(); } catch (ex) {} finally {}", None),
|
||||
];
|
||||
|
||||
Tester::new(RULE_NAME, pass, fail).test_and_snapshot();
|
||||
|
|
|
|||
|
|
@ -5,25 +5,25 @@ use oxc_ast::SourceType;
|
|||
use oxc_diagnostics::miette::{GraphicalReportHandler, GraphicalTheme, NamedSource};
|
||||
use oxc_parser::Parser;
|
||||
use oxc_semantic::SemanticBuilder;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{rules::RULES, Linter};
|
||||
|
||||
pub struct Tester {
|
||||
rule_name: &'static str,
|
||||
expect_pass: Vec<String>,
|
||||
expect_fail: Vec<String>,
|
||||
expect_pass: Vec<(String, Option<Value>)>,
|
||||
expect_fail: Vec<(String, Option<Value>)>,
|
||||
snapshot: String,
|
||||
}
|
||||
|
||||
impl Tester {
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub fn new<S: Into<String>>(
|
||||
rule_name: &'static str,
|
||||
expect_pass: Vec<S>,
|
||||
expect_fail: Vec<S>,
|
||||
expect_pass: Vec<(S, Option<Value>)>,
|
||||
expect_fail: Vec<(S, Option<Value>)>,
|
||||
) -> Self {
|
||||
let expect_pass = expect_pass.into_iter().map(Into::into).collect::<Vec<_>>();
|
||||
let expect_fail = expect_fail.into_iter().map(Into::into).collect::<Vec<_>>();
|
||||
let expect_pass = expect_pass.into_iter().map(|(s, r)| (s.into(), r)).collect::<Vec<_>>();
|
||||
let expect_fail = expect_fail.into_iter().map(|(s, r)| (s.into(), r)).collect::<Vec<_>>();
|
||||
Self { rule_name, expect_pass, expect_fail, snapshot: String::new() }
|
||||
}
|
||||
|
||||
|
|
@ -34,15 +34,15 @@ impl Tester {
|
|||
}
|
||||
|
||||
fn test_pass(&mut self) {
|
||||
for test in self.expect_pass.clone() {
|
||||
let passed = self.run(&test);
|
||||
for (test, config) in self.expect_pass.clone() {
|
||||
let passed = self.run(&test, config);
|
||||
assert!(passed, "expect test to pass: {test} {}", self.snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
fn test_fail(&mut self) {
|
||||
for test in self.expect_fail.clone() {
|
||||
let passed = self.run(&test);
|
||||
for (test, config) in self.expect_fail.clone() {
|
||||
let passed = self.run(&test, config);
|
||||
assert!(!passed, "expect test to fail: {test}");
|
||||
}
|
||||
}
|
||||
|
|
@ -54,7 +54,7 @@ impl Tester {
|
|||
});
|
||||
}
|
||||
|
||||
fn run(&mut self, source_text: &str) -> bool {
|
||||
fn run(&mut self, source_text: &str, config: Option<Value>) -> bool {
|
||||
let name = self.rule_name.replace('-', "_");
|
||||
let allocator = Allocator::default();
|
||||
let path = PathBuf::from(name).with_extension("tsx");
|
||||
|
|
@ -65,7 +65,8 @@ impl Tester {
|
|||
let semantic = SemanticBuilder::new().build(program, ret.trivias);
|
||||
let semantic = std::rc::Rc::new(semantic);
|
||||
let rule = RULES.iter().find(|rule| rule.name() == self.rule_name).unwrap();
|
||||
let diagnostics = Linter::from_rules(vec![rule.clone()]).run(&semantic);
|
||||
let rule = rule.from_json(config);
|
||||
let diagnostics = Linter::from_rules(vec![rule]).run(&semantic);
|
||||
if diagnostics.is_empty() {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue