feat(tasks): support init jest lint rule (#513)

This commit is contained in:
Wenzhe Wang 2023-07-04 14:53:29 +08:00 committed by GitHub
parent e87445cc1a
commit a0eba67ad9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 59 additions and 9 deletions

View file

@ -68,6 +68,9 @@ benchmark:
new-rule 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:
git submodule update --init --remote

View file

@ -1,4 +1,8 @@
use std::borrow::Cow;
use std::{
borrow::Cow,
fmt,
fmt::{Display, Formatter},
};
use convert_case::{Case, Casing};
use oxc_allocator::Allocator;
@ -6,7 +10,7 @@ use oxc_ast::{
ast::{
Argument, ArrayExpression, ArrayExpressionElement, CallExpression, Expression,
ExpressionStatement, ObjectExpression, ObjectProperty, ObjectPropertyKind, Program,
PropertyKey, Statement, StringLiteral, TemplateLiteral,
PropertyKey, Statement, StringLiteral, TaggedTemplateExpression, TemplateLiteral,
},
Visit,
};
@ -21,6 +25,9 @@ mod template;
const ESLINT_TEST_PATH: &str =
"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> {
source_text: &'a str,
code: Option<Cow<'a, str>>,
@ -57,6 +64,9 @@ impl<'a> Visit<'a> for TestCase<'a> {
match expr {
Expression::StringLiteral(lit) => self.visit_string_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::CallExpression(call_expr) => self.visit_call_expression(call_expr),
_ => {}
@ -107,6 +117,11 @@ impl<'a> Visit<'a> for TestCase<'a> {
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) {
self.code = Some(Cow::Borrowed(lit.value.as_str()));
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() {
let mut args = std::env::args();
args.next();
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 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}");
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)
}
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, "", "")
}
Ok(Err(err)) => {
@ -255,7 +298,7 @@ fn main() {
};
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;
eprintln!("failed to render {rule_name} rule template: {err}");
}

View file

@ -7,7 +7,7 @@ use std::{
use handlebars::Handlebars;
use crate::Context;
use crate::{Context, RuleKind};
const RULE_TEMPLATE: &str = include_str!("../template.txt");
@ -23,14 +23,18 @@ impl<'a> Template<'a> {
Self { context, registry }
}
pub fn render(&self) -> Result<(), Error> {
pub fn render(&self, rule_kind: RuleKind) -> Result<(), Error> {
let rendered = self
.registry
.render_template(RULE_TEMPLATE, &handlebars::to_json(self.context))
.unwrap();
let out_path = Path::new("crates/oxc_linter/src/rules/eslint")
.join(format!("{}.rs", self.context.rule_name));
let path = match rule_kind {
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())?;
format_rule_output(&out_path)?;