mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(tasks): support init jest lint rule (#513)
This commit is contained in:
parent
e87445cc1a
commit
a0eba67ad9
3 changed files with 59 additions and 9 deletions
3
justfile
3
justfile
|
|
@ -68,6 +68,9 @@ benchmark:
|
||||||
new-rule name:
|
new-rule name:
|
||||||
cargo run -p rulegen {{name}}
|
cargo run -p rulegen {{name}}
|
||||||
|
|
||||||
|
new-jest-rule name:
|
||||||
|
cargo run -p rulegen {{name}} jest
|
||||||
|
|
||||||
# Sync all submodules with their own remote repos (this is for Boshen updating the submodules)
|
# Sync all submodules with their own remote repos (this is for Boshen updating the submodules)
|
||||||
sync:
|
sync:
|
||||||
git submodule update --init --remote
|
git submodule update --init --remote
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,8 @@
|
||||||
use std::borrow::Cow;
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
|
fmt,
|
||||||
|
fmt::{Display, Formatter},
|
||||||
|
};
|
||||||
|
|
||||||
use convert_case::{Case, Casing};
|
use convert_case::{Case, Casing};
|
||||||
use oxc_allocator::Allocator;
|
use oxc_allocator::Allocator;
|
||||||
|
|
@ -6,7 +10,7 @@ use oxc_ast::{
|
||||||
ast::{
|
ast::{
|
||||||
Argument, ArrayExpression, ArrayExpressionElement, CallExpression, Expression,
|
Argument, ArrayExpression, ArrayExpressionElement, CallExpression, Expression,
|
||||||
ExpressionStatement, ObjectExpression, ObjectProperty, ObjectPropertyKind, Program,
|
ExpressionStatement, ObjectExpression, ObjectProperty, ObjectPropertyKind, Program,
|
||||||
PropertyKey, Statement, StringLiteral, TemplateLiteral,
|
PropertyKey, Statement, StringLiteral, TaggedTemplateExpression, TemplateLiteral,
|
||||||
},
|
},
|
||||||
Visit,
|
Visit,
|
||||||
};
|
};
|
||||||
|
|
@ -21,6 +25,9 @@ mod template;
|
||||||
const ESLINT_TEST_PATH: &str =
|
const ESLINT_TEST_PATH: &str =
|
||||||
"https://raw.githubusercontent.com/eslint/eslint/main/tests/lib/rules";
|
"https://raw.githubusercontent.com/eslint/eslint/main/tests/lib/rules";
|
||||||
|
|
||||||
|
const JEST_TEST_PATH: &str =
|
||||||
|
"https://raw.githubusercontent.com/jest-community/eslint-plugin-jest/main/src/rules/__tests__";
|
||||||
|
|
||||||
struct TestCase<'a> {
|
struct TestCase<'a> {
|
||||||
source_text: &'a str,
|
source_text: &'a str,
|
||||||
code: Option<Cow<'a, str>>,
|
code: Option<Cow<'a, str>>,
|
||||||
|
|
@ -57,6 +64,9 @@ impl<'a> Visit<'a> for TestCase<'a> {
|
||||||
match expr {
|
match expr {
|
||||||
Expression::StringLiteral(lit) => self.visit_string_literal(lit),
|
Expression::StringLiteral(lit) => self.visit_string_literal(lit),
|
||||||
Expression::TemplateLiteral(lit) => self.visit_template_literal(lit),
|
Expression::TemplateLiteral(lit) => self.visit_template_literal(lit),
|
||||||
|
Expression::TaggedTemplateExpression(tag_expr) => {
|
||||||
|
self.visit_tagged_template_expression(tag_expr);
|
||||||
|
}
|
||||||
Expression::ObjectExpression(obj_expr) => self.visit_object_expression(obj_expr),
|
Expression::ObjectExpression(obj_expr) => self.visit_object_expression(obj_expr),
|
||||||
Expression::CallExpression(call_expr) => self.visit_call_expression(call_expr),
|
Expression::CallExpression(call_expr) => self.visit_call_expression(call_expr),
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
@ -107,6 +117,11 @@ impl<'a> Visit<'a> for TestCase<'a> {
|
||||||
self.test_code = None;
|
self.test_code = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn visit_tagged_template_expression(&mut self, tag_expr: &'a TaggedTemplateExpression<'a>) {
|
||||||
|
self.code = Some(Cow::Borrowed(tag_expr.quasi.quasi().unwrap().as_str()));
|
||||||
|
self.test_code = None;
|
||||||
|
}
|
||||||
|
|
||||||
fn visit_string_literal(&mut self, lit: &'a StringLiteral) {
|
fn visit_string_literal(&mut self, lit: &'a StringLiteral) {
|
||||||
self.code = Some(Cow::Borrowed(lit.value.as_str()));
|
self.code = Some(Cow::Borrowed(lit.value.as_str()));
|
||||||
self.test_code = None;
|
self.test_code = None;
|
||||||
|
|
@ -212,15 +227,43 @@ impl<'a> Visit<'a> for State<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum RuleKind {
|
||||||
|
ESLint,
|
||||||
|
Jest,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RuleKind {
|
||||||
|
fn from(kind: &str) -> Self {
|
||||||
|
match kind {
|
||||||
|
"jest" => Self::Jest,
|
||||||
|
_ => Self::ESLint,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for RuleKind {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::ESLint => write!(f, "eslint"),
|
||||||
|
Self::Jest => write!(f, "eslint-plugin-jest"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut args = std::env::args();
|
let mut args = std::env::args();
|
||||||
args.next();
|
args.next();
|
||||||
|
|
||||||
let rule_name = args.next().expect("expected rule name").to_case(Case::Snake);
|
let rule_name = args.next().expect("expected rule name").to_case(Case::Snake);
|
||||||
|
let rule_kind = args.next().map_or(RuleKind::ESLint, |kind| RuleKind::from(&kind));
|
||||||
let upper_rule_name = rule_name.to_case(Case::UpperCamel);
|
let upper_rule_name = rule_name.to_case(Case::UpperCamel);
|
||||||
let kebab_rule_name = rule_name.to_case(Case::Kebab);
|
let kebab_rule_name = rule_name.to_case(Case::Kebab);
|
||||||
|
|
||||||
let rule_test_path = format!("{ESLINT_TEST_PATH}/{kebab_rule_name}.js");
|
let rule_test_path = match rule_kind {
|
||||||
|
RuleKind::ESLint => format!("{ESLINT_TEST_PATH}/{kebab_rule_name}.js"),
|
||||||
|
RuleKind::Jest => format!("{JEST_TEST_PATH}/{kebab_rule_name}.test.ts"),
|
||||||
|
};
|
||||||
println!("Reading test file from {rule_test_path}");
|
println!("Reading test file from {rule_test_path}");
|
||||||
|
|
||||||
let body = ureq::get(&rule_test_path).call().map(Response::into_string);
|
let body = ureq::get(&rule_test_path).call().map(Response::into_string);
|
||||||
|
|
@ -245,7 +288,7 @@ fn main() {
|
||||||
Context::new(&upper_rule_name, &rule_name, &pass_cases, &fail_cases)
|
Context::new(&upper_rule_name, &rule_name, &pass_cases, &fail_cases)
|
||||||
}
|
}
|
||||||
Err(_err) => {
|
Err(_err) => {
|
||||||
println!("Rule {rule_name} cannot be found in eslint, use empty template.");
|
println!("Rule {rule_name} cannot be found in {rule_kind}, use empty template.");
|
||||||
Context::new(&upper_rule_name, &rule_name, "", "")
|
Context::new(&upper_rule_name, &rule_name, "", "")
|
||||||
}
|
}
|
||||||
Ok(Err(err)) => {
|
Ok(Err(err)) => {
|
||||||
|
|
@ -255,7 +298,7 @@ fn main() {
|
||||||
};
|
};
|
||||||
|
|
||||||
let template = template::Template::with_context(&context);
|
let template = template::Template::with_context(&context);
|
||||||
if let Err(err) = template.render() {
|
if let Err(err) = template.render(rule_kind) {
|
||||||
let rule_name = context.rule;
|
let rule_name = context.rule;
|
||||||
eprintln!("failed to render {rule_name} rule template: {err}");
|
eprintln!("failed to render {rule_name} rule template: {err}");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use std::{
|
||||||
|
|
||||||
use handlebars::Handlebars;
|
use handlebars::Handlebars;
|
||||||
|
|
||||||
use crate::Context;
|
use crate::{Context, RuleKind};
|
||||||
|
|
||||||
const RULE_TEMPLATE: &str = include_str!("../template.txt");
|
const RULE_TEMPLATE: &str = include_str!("../template.txt");
|
||||||
|
|
||||||
|
|
@ -23,14 +23,18 @@ impl<'a> Template<'a> {
|
||||||
Self { context, registry }
|
Self { context, registry }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&self) -> Result<(), Error> {
|
pub fn render(&self, rule_kind: RuleKind) -> Result<(), Error> {
|
||||||
let rendered = self
|
let rendered = self
|
||||||
.registry
|
.registry
|
||||||
.render_template(RULE_TEMPLATE, &handlebars::to_json(self.context))
|
.render_template(RULE_TEMPLATE, &handlebars::to_json(self.context))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let out_path = Path::new("crates/oxc_linter/src/rules/eslint")
|
let path = match rule_kind {
|
||||||
.join(format!("{}.rs", self.context.rule_name));
|
RuleKind::ESLint => Path::new("crates/oxc_linter/src/rules/eslint"),
|
||||||
|
RuleKind::Jest => Path::new("crates/oxc_linter/src/rules/jest"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let out_path = path.join(format!("{}.rs", self.context.rule_name));
|
||||||
|
|
||||||
File::create(out_path.clone())?.write_all(rendered.as_bytes())?;
|
File::create(out_path.clone())?.write_all(rendered.as_bytes())?;
|
||||||
format_rule_output(&out_path)?;
|
format_rule_output(&out_path)?;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue