mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +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:
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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}");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
|
|
|||
Loading…
Reference in a new issue