mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 20:32:10 +00:00
feat(linter): add jsx-a11y settings (#1668)
When we developed linter for #1141 , we needed to configure some settings for `jsx-a11y`, which was not supported before, but I am trying to support it now. like this: ``` fn config() -> serde_json::Value { serde_json::json!([2,{ "ignoreNonDOM": true }]) } fn settings() -> serde_json::Value { serde_json::json!({ "jsx-a11y": { "components": { "Button": "button", } } }) } let pass = vec![ ("<Button />", Some(config()), Some(settings())), ]; ```
This commit is contained in:
parent
cf91379d1b
commit
6a90cd4af4
13 changed files with 255 additions and 77 deletions
|
|
@ -1,4 +1,4 @@
|
|||
use std::path::PathBuf;
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
|
||||
pub mod errors;
|
||||
use oxc_diagnostics::{Error, FailedToOpenFileError, Report};
|
||||
|
|
@ -7,7 +7,7 @@ use serde_json::Value;
|
|||
|
||||
use crate::{
|
||||
rules::{RuleEnum, RULES},
|
||||
AllowWarnDeny,
|
||||
AllowWarnDeny, JsxA11y, LintSettings,
|
||||
};
|
||||
|
||||
use self::errors::{
|
||||
|
|
@ -17,6 +17,7 @@ use self::errors::{
|
|||
|
||||
pub struct ESLintConfig {
|
||||
rules: std::vec::Vec<RuleEnum>,
|
||||
settings: LintSettings,
|
||||
}
|
||||
|
||||
impl ESLintConfig {
|
||||
|
|
@ -66,6 +67,8 @@ impl ESLintConfig {
|
|||
}
|
||||
};
|
||||
|
||||
let settings = parse_settings_from_root(&file);
|
||||
|
||||
// `extends` provides the defaults
|
||||
// `rules` provides the overrides
|
||||
let rules = RULES.clone().into_iter().filter_map(|rule| {
|
||||
|
|
@ -91,12 +94,16 @@ impl ESLintConfig {
|
|||
}
|
||||
});
|
||||
|
||||
Ok(Self { rules: rules.collect::<Vec<_>>() })
|
||||
Ok(Self { rules: rules.collect::<Vec<_>>(), settings })
|
||||
}
|
||||
|
||||
pub fn into_rules(mut self) -> Vec<RuleEnum> {
|
||||
pub fn into_rules(mut self) -> Self {
|
||||
self.rules.sort_unstable_by_key(RuleEnum::name);
|
||||
self.rules
|
||||
self
|
||||
}
|
||||
|
||||
pub fn get_config(self) -> (std::vec::Vec<RuleEnum>, LintSettings) {
|
||||
(self.rules, self.settings)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -151,6 +158,42 @@ fn parse_rules(
|
|||
.collect::<Result<Vec<_>, Error>>()
|
||||
}
|
||||
|
||||
fn parse_settings_from_root(root_json: &Value) -> LintSettings {
|
||||
let Value::Object(root_object) = root_json else { return LintSettings::default() };
|
||||
|
||||
let Some(settings_value) = root_object.get("settings") else { return LintSettings::default() };
|
||||
|
||||
parse_settings(settings_value)
|
||||
}
|
||||
|
||||
pub fn parse_settings(setting_value: &Value) -> LintSettings {
|
||||
if let Value::Object(settings_object) = setting_value {
|
||||
if let Some(Value::Object(jsx_a11y)) = settings_object.get("jsx-a11y") {
|
||||
let mut jsx_a11y_setting =
|
||||
JsxA11y { polymorphic_prop_name: None, components: HashMap::new() };
|
||||
|
||||
if let Some(Value::Object(components)) = jsx_a11y.get("components") {
|
||||
let components_map: HashMap<String, String> = components
|
||||
.iter()
|
||||
.map(|(key, value)| (String::from(key), String::from(value.as_str().unwrap())))
|
||||
.collect();
|
||||
|
||||
jsx_a11y_setting.set_components(components_map);
|
||||
}
|
||||
|
||||
if let Some(Value::String(polymorphic_prop_name)) = jsx_a11y.get("polymorphicPropName")
|
||||
{
|
||||
jsx_a11y_setting
|
||||
.set_polymorphic_prop_name(Some(String::from(polymorphic_prop_name)));
|
||||
}
|
||||
|
||||
return LintSettings { jsx_a11y: jsx_a11y_setting };
|
||||
}
|
||||
}
|
||||
|
||||
LintSettings::default()
|
||||
}
|
||||
|
||||
pub const EXTENDS_MAP: Map<&'static str, &'static str> = phf_map! {
|
||||
"eslint:recommended" => "eslint",
|
||||
"plugin:react/recommended" => "react",
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use oxc_span::SourceType;
|
|||
use crate::{
|
||||
disable_directives::{DisableDirectives, DisableDirectivesBuilder},
|
||||
fixer::{Fix, Message},
|
||||
AstNode,
|
||||
AstNode, LintSettings,
|
||||
};
|
||||
|
||||
pub struct LintContext<'a> {
|
||||
|
|
@ -24,10 +24,12 @@ pub struct LintContext<'a> {
|
|||
current_rule_name: &'static str,
|
||||
|
||||
file_path: Box<Path>,
|
||||
|
||||
settings: LintSettings,
|
||||
}
|
||||
|
||||
impl<'a> LintContext<'a> {
|
||||
pub fn new(file_path: Box<Path>, semantic: &Rc<Semantic<'a>>) -> Self {
|
||||
pub fn new(file_path: Box<Path>, semantic: &Rc<Semantic<'a>>, settings: LintSettings) -> Self {
|
||||
let disable_directives =
|
||||
DisableDirectivesBuilder::new(semantic.source_text(), semantic.trivias()).build();
|
||||
Self {
|
||||
|
|
@ -37,6 +39,7 @@ impl<'a> LintContext<'a> {
|
|||
fix: false,
|
||||
current_rule_name: "",
|
||||
file_path,
|
||||
settings,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -54,6 +57,10 @@ impl<'a> LintContext<'a> {
|
|||
&self.disable_directives
|
||||
}
|
||||
|
||||
pub fn settings(&self) -> LintSettings {
|
||||
self.settings.clone()
|
||||
}
|
||||
|
||||
pub fn source_text(&self) -> &'a str {
|
||||
self.semantic().source_text()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ mod rules;
|
|||
mod service;
|
||||
mod utils;
|
||||
|
||||
use std::{self, fs, io::Write, rc::Rc, time::Duration};
|
||||
use std::{self, collections::HashMap, fs, io::Write, rc::Rc, time::Duration};
|
||||
|
||||
use oxc_diagnostics::Report;
|
||||
pub(crate) use oxc_semantic::AstNode;
|
||||
|
|
@ -33,10 +33,38 @@ pub use crate::{
|
|||
};
|
||||
pub(crate) use rules::{RuleEnum, RULES};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LintSettings {
|
||||
jsx_a11y: JsxA11y,
|
||||
}
|
||||
|
||||
impl Default for LintSettings {
|
||||
fn default() -> Self {
|
||||
Self { jsx_a11y: JsxA11y { polymorphic_prop_name: None, components: HashMap::new() } }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JsxA11y {
|
||||
polymorphic_prop_name: Option<String>,
|
||||
components: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl JsxA11y {
|
||||
pub fn set_components(&mut self, components: HashMap<String, String>) {
|
||||
self.components = components;
|
||||
}
|
||||
|
||||
pub fn set_polymorphic_prop_name(&mut self, name: Option<String>) {
|
||||
self.polymorphic_prop_name = name;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Linter {
|
||||
rules: Vec<RuleEnum>,
|
||||
options: LintOptions,
|
||||
settings: LintSettings,
|
||||
}
|
||||
|
||||
impl Default for Linter {
|
||||
|
|
@ -52,15 +80,21 @@ impl Linter {
|
|||
.filter(|&rule| rule.category() == RuleCategory::Correctness)
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
Self { rules, options: LintOptions::default() }
|
||||
Self {
|
||||
rules,
|
||||
options: LintOptions::default(),
|
||||
settings: LintSettings {
|
||||
jsx_a11y: JsxA11y { polymorphic_prop_name: None, components: HashMap::new() },
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if there are any errors parsing the configuration file.
|
||||
pub fn from_options(options: LintOptions) -> Result<Self, Report> {
|
||||
let rules = options.derive_rules()?;
|
||||
Ok(Self { rules, options })
|
||||
let (rules, settings) = options.derive_rules_and_settings()?;
|
||||
Ok(Self { rules, options, settings })
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
|
@ -69,6 +103,12 @@ impl Linter {
|
|||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_settings(mut self, settings: LintSettings) -> Self {
|
||||
self.settings = settings;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn rules(&self) -> &Vec<RuleEnum> {
|
||||
&self.rules
|
||||
}
|
||||
|
|
@ -120,6 +160,9 @@ impl Linter {
|
|||
ctx.into_message()
|
||||
}
|
||||
|
||||
pub fn get_settings(&self) -> LintSettings {
|
||||
self.settings.clone()
|
||||
}
|
||||
#[allow(unused)]
|
||||
fn read_rules_configuration() -> Option<serde_json::Map<String, serde_json::Value>> {
|
||||
fs::read_to_string(".eslintrc.json")
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
ESLintConfig,
|
||||
},
|
||||
rules::RULES,
|
||||
RuleCategory, RuleEnum,
|
||||
LintSettings, RuleCategory, RuleEnum,
|
||||
};
|
||||
use oxc_diagnostics::{Error, Report};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
|
@ -145,12 +145,12 @@ const JSX_A11Y_PLUGIN_NAME: &str = "jsx_a11y";
|
|||
impl LintOptions {
|
||||
/// # Errors
|
||||
/// Returns `Err` if there are any errors parsing the configuration file.
|
||||
pub fn derive_rules(&self) -> Result<Vec<RuleEnum>, Report> {
|
||||
pub fn derive_rules_and_settings(&self) -> Result<(Vec<RuleEnum>, LintSettings), Report> {
|
||||
let mut rules: FxHashSet<RuleEnum> = FxHashSet::default();
|
||||
|
||||
if let Some(path) = &self.config_path {
|
||||
let rules = ESLintConfig::new(path)?.into_rules();
|
||||
return Ok(rules);
|
||||
let (rules, settings) = ESLintConfig::new(path)?.into_rules().get_config();
|
||||
return Ok((rules, settings));
|
||||
}
|
||||
|
||||
let all_rules = self.get_filtered_rules();
|
||||
|
|
@ -195,7 +195,7 @@ impl LintOptions {
|
|||
let mut rules = rules.into_iter().collect::<Vec<_>>();
|
||||
// for stable diagnostics output ordering
|
||||
rules.sort_unstable_by_key(RuleEnum::name);
|
||||
Ok(rules)
|
||||
Ok((rules, LintSettings::default()))
|
||||
}
|
||||
|
||||
// get final filtered rules by reading `self.jest_plugin` and `self.jsx_a11y_plugin`
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use phf::phf_set;
|
||||
|
||||
use oxc_ast::{ast::JSXElementName, AstKind};
|
||||
use oxc_ast::AstKind;
|
||||
use oxc_diagnostics::{
|
||||
miette::{self, Diagnostic},
|
||||
thiserror::Error,
|
||||
|
|
@ -8,7 +8,12 @@ use oxc_diagnostics::{
|
|||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::Span;
|
||||
|
||||
use crate::{context::LintContext, rule::Rule, utils::has_jsx_prop, AstNode};
|
||||
use crate::{
|
||||
context::LintContext,
|
||||
rule::Rule,
|
||||
utils::{get_element_type, has_jsx_prop},
|
||||
AstNode,
|
||||
};
|
||||
|
||||
#[derive(Debug, Error, Diagnostic)]
|
||||
#[error("eslint-plugin-jsx-a11y(no-autofocus): The `autofocus` attribute is found here, which can cause usability issues for sighted and non-sighted users")]
|
||||
|
|
@ -86,12 +91,11 @@ impl Rule for NoAutofocus {
|
|||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
||||
if let AstKind::JSXElement(jsx_el) = node.kind() {
|
||||
if let Option::Some(autofocus) = has_jsx_prop(&jsx_el.opening_element, "autoFocus") {
|
||||
let Some(element_type) = get_element_type(ctx, &jsx_el.opening_element) else {
|
||||
return;
|
||||
};
|
||||
if self.ignore_non_dom {
|
||||
let JSXElementName::Identifier(ident) = &jsx_el.opening_element.name else {
|
||||
return;
|
||||
};
|
||||
let name = ident.name.as_str();
|
||||
if HTML_TAG.contains(name) {
|
||||
if HTML_TAG.contains(&element_type) {
|
||||
if let oxc_ast::ast::JSXAttributeItem::Attribute(attr) = autofocus {
|
||||
ctx.diagnostic(NoAutofocusDiagnostic(attr.span));
|
||||
}
|
||||
|
|
@ -262,38 +266,46 @@ const HTML_TAG: phf::Set<&'static str> = phf_set! {
|
|||
#[test]
|
||||
fn test() {
|
||||
use crate::tester::Tester;
|
||||
fn array() -> serde_json::Value {
|
||||
fn config() -> serde_json::Value {
|
||||
serde_json::json!([2,{
|
||||
"ignoreNonDOM": true
|
||||
}])
|
||||
}
|
||||
|
||||
fn settings() -> serde_json::Value {
|
||||
serde_json::json!({
|
||||
"jsx-a11y": {
|
||||
"components": {
|
||||
"Button": "button",
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let pass = vec![
|
||||
("<div />;", None),
|
||||
("<div autofocus />;", None),
|
||||
("<input autofocus='true' />;", None),
|
||||
("<Foo bar />", None),
|
||||
("<Button />", None),
|
||||
("<Foo autoFocus />", Some(array())),
|
||||
("<div><div autofocus /></div>", Some(array())),
|
||||
// TODO we need components_settings to test this
|
||||
// ("<Button />", Some(serde_json::json!(ignoreNonDOMSchema))),
|
||||
// ("<Button />", Some(serde_json::json!(ignoreNonDOMSchema)), setting),
|
||||
("<div />;", None, None),
|
||||
("<div autofocus />;", None, None),
|
||||
("<input autofocus='true' />;", None, None),
|
||||
("<Foo bar />", None, None),
|
||||
("<Button />", None, None),
|
||||
("<Foo autoFocus />", Some(config()), None),
|
||||
("<div><div autofocus /></div>", Some(config()), None),
|
||||
("<Button />", None, Some(settings())),
|
||||
("<Button />", Some(config()), Some(settings())),
|
||||
];
|
||||
|
||||
let fail = vec![
|
||||
("<div autoFocus />", None),
|
||||
("<div autoFocus={true} />", None),
|
||||
("<div autoFocus={false} />", None),
|
||||
("<div autoFocus={undefined} />", None),
|
||||
("<div autoFocus='true' />", None),
|
||||
("<div autoFocus='false' />", None),
|
||||
("<input autoFocus />", None),
|
||||
("<Foo autoFocus />", None),
|
||||
("<Button autoFocus />", None),
|
||||
// TODO we need components_settings to test this
|
||||
// ("<Button autoFocus />", Some(array())),
|
||||
("<div autoFocus />", None, None),
|
||||
("<div autoFocus={true} />", None, None),
|
||||
("<div autoFocus={false} />", None, None),
|
||||
("<div autoFocus={undefined} />", None, None),
|
||||
("<div autoFocus='true' />", None, None),
|
||||
("<div autoFocus='false' />", None, None),
|
||||
("<input autoFocus />", None, None),
|
||||
("<Foo autoFocus />", None, None),
|
||||
("<Button autoFocus />", None, None),
|
||||
("<Button autoFocus />", Some(config()), Some(settings())),
|
||||
];
|
||||
|
||||
Tester::new(NoAutofocus::NAME, pass, fail).test_and_snapshot();
|
||||
Tester::new_with_settings(NoAutofocus::NAME, pass, fail).test_and_snapshot();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,6 +169,7 @@ impl Runtime {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn process_source<'a>(
|
||||
&self,
|
||||
path: &Path,
|
||||
|
|
@ -233,8 +234,11 @@ impl Runtime {
|
|||
return semantic_ret.errors.into_iter().map(|err| Message::new(err, None)).collect();
|
||||
};
|
||||
|
||||
let lint_ctx =
|
||||
LintContext::new(path.to_path_buf().into_boxed_path(), &Rc::new(semantic_ret.semantic));
|
||||
let lint_ctx = LintContext::new(
|
||||
path.to_path_buf().into_boxed_path(),
|
||||
&Rc::new(semantic_ret.semantic),
|
||||
self.linter.get_settings(),
|
||||
);
|
||||
self.linter.run(lint_ctx)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
source: crates/oxc_linter/src/tester.rs
|
||||
assertion_line: 119
|
||||
assertion_line: 130
|
||||
expression: no_autofocus
|
||||
---
|
||||
⚠ eslint-plugin-jsx-a11y(no-autofocus): The `autofocus` attribute is found here, which can cause usability issues for sighted and non-sighted users
|
||||
|
|
@ -66,4 +66,11 @@ expression: no_autofocus
|
|||
╰────
|
||||
help: Remove `autofocus` attribute
|
||||
|
||||
⚠ eslint-plugin-jsx-a11y(no-autofocus): The `autofocus` attribute is found here, which can cause usability issues for sighted and non-sighted users
|
||||
╭─[no_autofocus.tsx:1:1]
|
||||
1 │ <Button autoFocus />
|
||||
· ─────────
|
||||
╰────
|
||||
help: Remove `autofocus` attribute
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ use oxc_diagnostics::miette::{GraphicalReportHandler, GraphicalTheme, NamedSourc
|
|||
use oxc_diagnostics::DiagnosticService;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{rules::RULES, Fixer, LintOptions, LintService, Linter, RuleEnum};
|
||||
use crate::{
|
||||
config::parse_settings, rules::RULES, Fixer, LintOptions, LintService, LintSettings, Linter,
|
||||
RuleEnum,
|
||||
};
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
enum TestResult {
|
||||
|
|
@ -20,8 +23,8 @@ enum TestResult {
|
|||
pub struct Tester {
|
||||
rule_name: &'static str,
|
||||
rule_path: PathBuf,
|
||||
expect_pass: Vec<(String, Option<Value>)>,
|
||||
expect_fail: Vec<(String, Option<Value>)>,
|
||||
expect_pass: Vec<(String, Option<Value>, Option<Value>)>,
|
||||
expect_fail: Vec<(String, Option<Value>, Option<Value>)>,
|
||||
expect_fix: Vec<(String, String, Option<Value>)>,
|
||||
snapshot: String,
|
||||
current_working_directory: Box<Path>,
|
||||
|
|
@ -35,10 +38,28 @@ impl Tester {
|
|||
rule_name: &'static str,
|
||||
expect_pass: Vec<(S, Option<Value>)>,
|
||||
expect_fail: Vec<(S, Option<Value>)>,
|
||||
) -> Self {
|
||||
let expect_pass =
|
||||
expect_pass.into_iter().map(|(s, r)| (s.into(), r, None)).collect::<Vec<_>>();
|
||||
let expect_fail =
|
||||
expect_fail.into_iter().map(|(s, r)| (s.into(), r, None)).collect::<Vec<_>>();
|
||||
Self::new_with_settings(rule_name, expect_pass, expect_fail)
|
||||
}
|
||||
|
||||
pub fn new_with_settings<S: Into<String>>(
|
||||
rule_name: &'static str,
|
||||
expect_pass: Vec<(S, Option<Value>, Option<Value>)>,
|
||||
expect_fail: Vec<(S, Option<Value>, Option<Value>)>,
|
||||
) -> Self {
|
||||
let rule_path = PathBuf::from(rule_name.replace('-', "_")).with_extension("tsx");
|
||||
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<_>>();
|
||||
let expect_pass = expect_pass
|
||||
.into_iter()
|
||||
.map(|(s, r, settings)| (s.into(), r, settings))
|
||||
.collect::<Vec<_>>();
|
||||
let expect_fail = expect_fail
|
||||
.into_iter()
|
||||
.map(|(s, r, settings)| (s.into(), r, settings))
|
||||
.collect::<Vec<_>>();
|
||||
let current_working_directory =
|
||||
env::current_dir().unwrap().join("fixtures/import").into_boxed_path();
|
||||
Self {
|
||||
|
|
@ -60,9 +81,11 @@ impl Tester {
|
|||
expect_pass: Vec<S>,
|
||||
expect_fail: Vec<S>,
|
||||
) -> Self {
|
||||
let expect_pass = expect_pass.into_iter().map(|s| (s.into(), None)).collect::<Vec<_>>();
|
||||
let expect_fail = expect_fail.into_iter().map(|s| (s.into(), None)).collect::<Vec<_>>();
|
||||
Self::new(rule_name, expect_pass, expect_fail)
|
||||
let expect_pass =
|
||||
expect_pass.into_iter().map(|s| (s.into(), None, None)).collect::<Vec<_>>();
|
||||
let expect_fail =
|
||||
expect_fail.into_iter().map(|s| (s.into(), None, None)).collect::<Vec<_>>();
|
||||
Self::new_with_settings(rule_name, expect_pass, expect_fail)
|
||||
}
|
||||
|
||||
pub fn update_expect_pass_fail<S: Into<String>>(
|
||||
|
|
@ -70,8 +93,10 @@ impl Tester {
|
|||
expect_pass: Vec<S>,
|
||||
expect_fail: Vec<S>,
|
||||
) -> Self {
|
||||
self.expect_pass = expect_pass.into_iter().map(|s| (s.into(), None)).collect::<Vec<_>>();
|
||||
self.expect_fail = expect_fail.into_iter().map(|s| (s.into(), None)).collect::<Vec<_>>();
|
||||
self.expect_pass =
|
||||
expect_pass.into_iter().map(|s| (s.into(), None, None)).collect::<Vec<_>>();
|
||||
self.expect_fail =
|
||||
expect_fail.into_iter().map(|s| (s.into(), None, None)).collect::<Vec<_>>();
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -121,16 +146,16 @@ impl Tester {
|
|||
}
|
||||
|
||||
fn test_pass(&mut self) {
|
||||
for (test, config) in self.expect_pass.clone() {
|
||||
let result = self.run(&test, config, false);
|
||||
for (test, config, settings) in self.expect_pass.clone() {
|
||||
let result = self.run(&test, config, false, &settings);
|
||||
let passed = result == TestResult::Passed;
|
||||
assert!(passed, "expect test to pass: {test} {}", self.snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
fn test_fail(&mut self) {
|
||||
for (test, config) in self.expect_fail.clone() {
|
||||
let result = self.run(&test, config, false);
|
||||
for (test, config, settings) in self.expect_fail.clone() {
|
||||
let result = self.run(&test, config, false, &settings);
|
||||
let failed = result == TestResult::Failed;
|
||||
assert!(failed, "expect test to fail: {test}");
|
||||
}
|
||||
|
|
@ -138,7 +163,7 @@ impl Tester {
|
|||
|
||||
fn test_fix(&mut self) {
|
||||
for (test, expected, config) in self.expect_fix.clone() {
|
||||
let result = self.run(&test, config, true);
|
||||
let result = self.run(&test, config, true, &None);
|
||||
if let TestResult::Fixed(fixed_str) = result {
|
||||
assert_eq!(expected, fixed_str);
|
||||
} else {
|
||||
|
|
@ -147,15 +172,26 @@ impl Tester {
|
|||
}
|
||||
}
|
||||
|
||||
fn run(&mut self, source_text: &str, config: Option<Value>, is_fix: bool) -> TestResult {
|
||||
fn run(
|
||||
&mut self,
|
||||
source_text: &str,
|
||||
config: Option<Value>,
|
||||
is_fix: bool,
|
||||
settings: &Option<Value>,
|
||||
) -> TestResult {
|
||||
let allocator = Allocator::default();
|
||||
let rule = self.find_rule().read_json(config);
|
||||
let lint_settings: LintSettings =
|
||||
settings.as_ref().map_or_else(LintSettings::default, parse_settings);
|
||||
let options = LintOptions::default()
|
||||
.with_fix(is_fix)
|
||||
.with_import_plugin(self.import_plugin)
|
||||
.with_jest_plugin(self.jest_plugin)
|
||||
.with_jsx_a11y_plugin(self.jsx_a11y_plugin);
|
||||
let linter = Linter::from_options(options).unwrap().with_rules(vec![rule]);
|
||||
let linter = Linter::from_options(options)
|
||||
.unwrap()
|
||||
.with_rules(vec![rule])
|
||||
.with_settings(lint_settings);
|
||||
let path_to_lint = if self.import_plugin {
|
||||
self.current_working_directory.join(&self.rule_path)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use oxc_ast::{
|
|||
};
|
||||
use oxc_semantic::{AstNode, SymbolFlags};
|
||||
|
||||
use crate::LintContext;
|
||||
use crate::{JsxA11y, LintContext, LintSettings};
|
||||
|
||||
pub fn is_create_element_call(call_expr: &CallExpression) -> bool {
|
||||
if let Some(member_expr) = call_expr.callee.get_member_expr() {
|
||||
|
|
@ -163,3 +163,18 @@ pub fn get_parent_es6_component<'a, 'b>(ctx: &'b LintContext<'a>) -> Option<&'b
|
|||
None
|
||||
})
|
||||
}
|
||||
pub fn get_element_type(context: &LintContext, element: &JSXOpeningElement) -> Option<String> {
|
||||
let JSXElementName::Identifier(ident) = &element.name else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let mut element_type = String::from(ident.name.as_str());
|
||||
|
||||
let LintSettings { jsx_a11y } = context.settings();
|
||||
let JsxA11y { polymorphic_prop_name: _, components } = jsx_a11y;
|
||||
if let Some(val) = components.get(&element_type) {
|
||||
element_type = String::from(val);
|
||||
}
|
||||
|
||||
Some(element_type)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ fn run_individual_test(
|
|||
) -> std::result::Result<Vec<Report>, Vec<Report>> {
|
||||
use std::rc::Rc;
|
||||
|
||||
use oxc_linter::LintContext;
|
||||
use oxc_linter::{LintContext, LintSettings};
|
||||
|
||||
let file_path = &test.relative_path.last().expect("there to be atleast 1 path part");
|
||||
let source_text = &test.code;
|
||||
|
|
@ -48,8 +48,11 @@ fn run_individual_test(
|
|||
|
||||
let semantic = Rc::new(semantic);
|
||||
|
||||
let mut lint_ctx =
|
||||
LintContext::new(PathBuf::from(file_path).into_boxed_path(), &Rc::clone(&semantic));
|
||||
let mut lint_ctx = LintContext::new(
|
||||
PathBuf::from(file_path).into_boxed_path(),
|
||||
&Rc::clone(&semantic),
|
||||
LintSettings::default(),
|
||||
);
|
||||
|
||||
let result = plugin.lint_file_with_rule(
|
||||
&mut lint_ctx,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use oxc::{
|
|||
span::SourceType,
|
||||
transformer::{TransformOptions, TransformTarget, Transformer},
|
||||
};
|
||||
use oxc_linter::{LintContext, Linter};
|
||||
use oxc_linter::{LintContext, LintSettings, Linter};
|
||||
use oxc_prettier::{Prettier, PrettierOptions};
|
||||
use oxc_query::{schema, Adapter, SCHEMA_TEXT};
|
||||
use oxc_type_synthesis::{synthesize_program, Diagnostic as TypeCheckDiagnostic};
|
||||
|
|
@ -205,7 +205,8 @@ impl Oxc {
|
|||
self.save_diagnostics(semantic_ret.errors);
|
||||
|
||||
let semantic = Rc::new(semantic_ret.semantic);
|
||||
let lint_ctx = LintContext::new(path.into_boxed_path(), &semantic);
|
||||
let lint_ctx =
|
||||
LintContext::new(path.into_boxed_path(), &semantic, LintSettings::default());
|
||||
let linter_ret = Linter::new().run(lint_ctx);
|
||||
let diagnostics = linter_ret.into_iter().map(|e| e.error).collect();
|
||||
self.save_diagnostics(diagnostics);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use crate::walk::Walk;
|
|||
use miette::NamedSource;
|
||||
use oxc_allocator::Allocator;
|
||||
use oxc_diagnostics::{miette, Error, Severity};
|
||||
use oxc_linter::{LintContext, Linter};
|
||||
use oxc_linter::{LintContext, LintSettings, Linter};
|
||||
use oxc_linter_plugin::{make_relative_path_parts, LinterPlugin};
|
||||
use oxc_parser::Parser;
|
||||
use oxc_semantic::SemanticBuilder;
|
||||
|
|
@ -270,8 +270,11 @@ impl IsolatedLintHandler {
|
|||
return Some(Self::wrap_diagnostics(path, &source_text, reports));
|
||||
};
|
||||
|
||||
let mut lint_ctx =
|
||||
LintContext::new(path.to_path_buf().into_boxed_path(), &Rc::new(semantic_ret.semantic));
|
||||
let mut lint_ctx = LintContext::new(
|
||||
path.to_path_buf().into_boxed_path(),
|
||||
&Rc::new(semantic_ret.semantic),
|
||||
LintSettings::default(),
|
||||
);
|
||||
{
|
||||
if let Ok(guard) = plugin.read() {
|
||||
if let Some(plugin) = &*guard {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use std::{path::PathBuf, rc::Rc};
|
|||
|
||||
use oxc_allocator::Allocator;
|
||||
use oxc_benchmark::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||
use oxc_linter::{AllowWarnDeny, LintContext, LintOptions, Linter};
|
||||
use oxc_linter::{AllowWarnDeny, LintContext, LintOptions, LintSettings, Linter};
|
||||
use oxc_parser::Parser;
|
||||
use oxc_semantic::SemanticBuilder;
|
||||
use oxc_span::SourceType;
|
||||
|
|
@ -38,7 +38,11 @@ fn bench_linter(criterion: &mut Criterion) {
|
|||
let linter = Linter::from_options(lint_options).unwrap();
|
||||
let semantic = Rc::new(semantic_ret.semantic);
|
||||
b.iter(|| {
|
||||
linter.run(LintContext::new(PathBuf::from("").into_boxed_path(), &semantic))
|
||||
linter.run(LintContext::new(
|
||||
PathBuf::from("").into_boxed_path(),
|
||||
&semantic,
|
||||
LintSettings::default(),
|
||||
))
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue