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:
msdlisper 2023-12-16 13:45:14 +08:00 committed by GitHub
parent cf91379d1b
commit 6a90cd4af4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 255 additions and 77 deletions

View file

@ -1,4 +1,4 @@
use std::path::PathBuf; use std::{collections::HashMap, path::PathBuf};
pub mod errors; pub mod errors;
use oxc_diagnostics::{Error, FailedToOpenFileError, Report}; use oxc_diagnostics::{Error, FailedToOpenFileError, Report};
@ -7,7 +7,7 @@ use serde_json::Value;
use crate::{ use crate::{
rules::{RuleEnum, RULES}, rules::{RuleEnum, RULES},
AllowWarnDeny, AllowWarnDeny, JsxA11y, LintSettings,
}; };
use self::errors::{ use self::errors::{
@ -17,6 +17,7 @@ use self::errors::{
pub struct ESLintConfig { pub struct ESLintConfig {
rules: std::vec::Vec<RuleEnum>, rules: std::vec::Vec<RuleEnum>,
settings: LintSettings,
} }
impl ESLintConfig { impl ESLintConfig {
@ -66,6 +67,8 @@ impl ESLintConfig {
} }
}; };
let settings = parse_settings_from_root(&file);
// `extends` provides the defaults // `extends` provides the defaults
// `rules` provides the overrides // `rules` provides the overrides
let rules = RULES.clone().into_iter().filter_map(|rule| { 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.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>>() .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! { pub const EXTENDS_MAP: Map<&'static str, &'static str> = phf_map! {
"eslint:recommended" => "eslint", "eslint:recommended" => "eslint",
"plugin:react/recommended" => "react", "plugin:react/recommended" => "react",

View file

@ -8,7 +8,7 @@ use oxc_span::SourceType;
use crate::{ use crate::{
disable_directives::{DisableDirectives, DisableDirectivesBuilder}, disable_directives::{DisableDirectives, DisableDirectivesBuilder},
fixer::{Fix, Message}, fixer::{Fix, Message},
AstNode, AstNode, LintSettings,
}; };
pub struct LintContext<'a> { pub struct LintContext<'a> {
@ -24,10 +24,12 @@ pub struct LintContext<'a> {
current_rule_name: &'static str, current_rule_name: &'static str,
file_path: Box<Path>, file_path: Box<Path>,
settings: LintSettings,
} }
impl<'a> LintContext<'a> { 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 = let disable_directives =
DisableDirectivesBuilder::new(semantic.source_text(), semantic.trivias()).build(); DisableDirectivesBuilder::new(semantic.source_text(), semantic.trivias()).build();
Self { Self {
@ -37,6 +39,7 @@ impl<'a> LintContext<'a> {
fix: false, fix: false,
current_rule_name: "", current_rule_name: "",
file_path, file_path,
settings,
} }
} }
@ -54,6 +57,10 @@ impl<'a> LintContext<'a> {
&self.disable_directives &self.disable_directives
} }
pub fn settings(&self) -> LintSettings {
self.settings.clone()
}
pub fn source_text(&self) -> &'a str { pub fn source_text(&self) -> &'a str {
self.semantic().source_text() self.semantic().source_text()
} }

View file

@ -17,7 +17,7 @@ mod rules;
mod service; mod service;
mod utils; 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; use oxc_diagnostics::Report;
pub(crate) use oxc_semantic::AstNode; pub(crate) use oxc_semantic::AstNode;
@ -33,10 +33,38 @@ pub use crate::{
}; };
pub(crate) use rules::{RuleEnum, RULES}; 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)] #[derive(Debug)]
pub struct Linter { pub struct Linter {
rules: Vec<RuleEnum>, rules: Vec<RuleEnum>,
options: LintOptions, options: LintOptions,
settings: LintSettings,
} }
impl Default for Linter { impl Default for Linter {
@ -52,15 +80,21 @@ impl Linter {
.filter(|&rule| rule.category() == RuleCategory::Correctness) .filter(|&rule| rule.category() == RuleCategory::Correctness)
.cloned() .cloned()
.collect::<Vec<_>>(); .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 /// # Errors
/// ///
/// Returns `Err` if there are any errors parsing the configuration file. /// Returns `Err` if there are any errors parsing the configuration file.
pub fn from_options(options: LintOptions) -> Result<Self, Report> { pub fn from_options(options: LintOptions) -> Result<Self, Report> {
let rules = options.derive_rules()?; let (rules, settings) = options.derive_rules_and_settings()?;
Ok(Self { rules, options }) Ok(Self { rules, options, settings })
} }
#[must_use] #[must_use]
@ -69,6 +103,12 @@ impl Linter {
self self
} }
#[must_use]
pub fn with_settings(mut self, settings: LintSettings) -> Self {
self.settings = settings;
self
}
pub fn rules(&self) -> &Vec<RuleEnum> { pub fn rules(&self) -> &Vec<RuleEnum> {
&self.rules &self.rules
} }
@ -120,6 +160,9 @@ impl Linter {
ctx.into_message() ctx.into_message()
} }
pub fn get_settings(&self) -> LintSettings {
self.settings.clone()
}
#[allow(unused)] #[allow(unused)]
fn read_rules_configuration() -> Option<serde_json::Map<String, serde_json::Value>> { fn read_rules_configuration() -> Option<serde_json::Map<String, serde_json::Value>> {
fs::read_to_string(".eslintrc.json") fs::read_to_string(".eslintrc.json")

View file

@ -9,7 +9,7 @@ use crate::{
ESLintConfig, ESLintConfig,
}, },
rules::RULES, rules::RULES,
RuleCategory, RuleEnum, LintSettings, RuleCategory, RuleEnum,
}; };
use oxc_diagnostics::{Error, Report}; use oxc_diagnostics::{Error, Report};
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
@ -145,12 +145,12 @@ const JSX_A11Y_PLUGIN_NAME: &str = "jsx_a11y";
impl LintOptions { impl LintOptions {
/// # Errors /// # Errors
/// Returns `Err` if there are any errors parsing the configuration file. /// 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(); let mut rules: FxHashSet<RuleEnum> = FxHashSet::default();
if let Some(path) = &self.config_path { if let Some(path) = &self.config_path {
let rules = ESLintConfig::new(path)?.into_rules(); let (rules, settings) = ESLintConfig::new(path)?.into_rules().get_config();
return Ok(rules); return Ok((rules, settings));
} }
let all_rules = self.get_filtered_rules(); let all_rules = self.get_filtered_rules();
@ -195,7 +195,7 @@ impl LintOptions {
let mut rules = rules.into_iter().collect::<Vec<_>>(); let mut rules = rules.into_iter().collect::<Vec<_>>();
// for stable diagnostics output ordering // for stable diagnostics output ordering
rules.sort_unstable_by_key(RuleEnum::name); 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` // get final filtered rules by reading `self.jest_plugin` and `self.jsx_a11y_plugin`

View file

@ -1,6 +1,6 @@
use phf::phf_set; use phf::phf_set;
use oxc_ast::{ast::JSXElementName, AstKind}; use oxc_ast::AstKind;
use oxc_diagnostics::{ use oxc_diagnostics::{
miette::{self, Diagnostic}, miette::{self, Diagnostic},
thiserror::Error, thiserror::Error,
@ -8,7 +8,12 @@ use oxc_diagnostics::{
use oxc_macros::declare_oxc_lint; use oxc_macros::declare_oxc_lint;
use oxc_span::Span; 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)] #[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")] #[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>) { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
if let AstKind::JSXElement(jsx_el) = node.kind() { if let AstKind::JSXElement(jsx_el) = node.kind() {
if let Option::Some(autofocus) = has_jsx_prop(&jsx_el.opening_element, "autoFocus") { if let Option::Some(autofocus) = has_jsx_prop(&jsx_el.opening_element, "autoFocus") {
if self.ignore_non_dom { let Some(element_type) = get_element_type(ctx, &jsx_el.opening_element) else {
let JSXElementName::Identifier(ident) = &jsx_el.opening_element.name else {
return; return;
}; };
let name = ident.name.as_str(); if self.ignore_non_dom {
if HTML_TAG.contains(name) { if HTML_TAG.contains(&element_type) {
if let oxc_ast::ast::JSXAttributeItem::Attribute(attr) = autofocus { if let oxc_ast::ast::JSXAttributeItem::Attribute(attr) = autofocus {
ctx.diagnostic(NoAutofocusDiagnostic(attr.span)); ctx.diagnostic(NoAutofocusDiagnostic(attr.span));
} }
@ -262,38 +266,46 @@ const HTML_TAG: phf::Set<&'static str> = phf_set! {
#[test] #[test]
fn test() { fn test() {
use crate::tester::Tester; use crate::tester::Tester;
fn array() -> serde_json::Value { fn config() -> serde_json::Value {
serde_json::json!([2,{ serde_json::json!([2,{
"ignoreNonDOM": true "ignoreNonDOM": true
}]) }])
} }
fn settings() -> serde_json::Value {
serde_json::json!({
"jsx-a11y": {
"components": {
"Button": "button",
}
}
})
}
let pass = vec![ let pass = vec![
("<div />;", None), ("<div />;", None, None),
("<div autofocus />;", None), ("<div autofocus />;", None, None),
("<input autofocus='true' />;", None), ("<input autofocus='true' />;", None, None),
("<Foo bar />", None), ("<Foo bar />", None, None),
("<Button />", None), ("<Button />", None, None),
("<Foo autoFocus />", Some(array())), ("<Foo autoFocus />", Some(config()), None),
("<div><div autofocus /></div>", Some(array())), ("<div><div autofocus /></div>", Some(config()), None),
// TODO we need components_settings to test this ("<Button />", None, Some(settings())),
// ("<Button />", Some(serde_json::json!(ignoreNonDOMSchema))), ("<Button />", Some(config()), Some(settings())),
// ("<Button />", Some(serde_json::json!(ignoreNonDOMSchema)), setting),
]; ];
let fail = vec![ let fail = vec![
("<div autoFocus />", None), ("<div autoFocus />", None, None),
("<div autoFocus={true} />", None), ("<div autoFocus={true} />", None, None),
("<div autoFocus={false} />", None), ("<div autoFocus={false} />", None, None),
("<div autoFocus={undefined} />", None), ("<div autoFocus={undefined} />", None, None),
("<div autoFocus='true' />", None), ("<div autoFocus='true' />", None, None),
("<div autoFocus='false' />", None), ("<div autoFocus='false' />", None, None),
("<input autoFocus />", None), ("<input autoFocus />", None, None),
("<Foo autoFocus />", None), ("<Foo autoFocus />", None, None),
("<Button autoFocus />", None), ("<Button autoFocus />", None, None),
// TODO we need components_settings to test this ("<Button autoFocus />", Some(config()), Some(settings())),
// ("<Button autoFocus />", Some(array())),
]; ];
Tester::new(NoAutofocus::NAME, pass, fail).test_and_snapshot(); Tester::new_with_settings(NoAutofocus::NAME, pass, fail).test_and_snapshot();
} }

View file

@ -169,6 +169,7 @@ impl Runtime {
} }
} }
#[allow(clippy::too_many_arguments)]
fn process_source<'a>( fn process_source<'a>(
&self, &self,
path: &Path, path: &Path,
@ -233,8 +234,11 @@ impl Runtime {
return semantic_ret.errors.into_iter().map(|err| Message::new(err, None)).collect(); return semantic_ret.errors.into_iter().map(|err| Message::new(err, None)).collect();
}; };
let lint_ctx = let lint_ctx = LintContext::new(
LintContext::new(path.to_path_buf().into_boxed_path(), &Rc::new(semantic_ret.semantic)); path.to_path_buf().into_boxed_path(),
&Rc::new(semantic_ret.semantic),
self.linter.get_settings(),
);
self.linter.run(lint_ctx) self.linter.run(lint_ctx)
} }

View file

@ -1,6 +1,6 @@
--- ---
source: crates/oxc_linter/src/tester.rs source: crates/oxc_linter/src/tester.rs
assertion_line: 119 assertion_line: 130
expression: no_autofocus 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 ⚠ 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 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

View file

@ -8,7 +8,10 @@ use oxc_diagnostics::miette::{GraphicalReportHandler, GraphicalTheme, NamedSourc
use oxc_diagnostics::DiagnosticService; use oxc_diagnostics::DiagnosticService;
use serde_json::Value; 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)] #[derive(Eq, PartialEq)]
enum TestResult { enum TestResult {
@ -20,8 +23,8 @@ enum TestResult {
pub struct Tester { pub struct Tester {
rule_name: &'static str, rule_name: &'static str,
rule_path: PathBuf, rule_path: PathBuf,
expect_pass: Vec<(String, Option<Value>)>, expect_pass: Vec<(String, Option<Value>, Option<Value>)>,
expect_fail: Vec<(String, Option<Value>)>, expect_fail: Vec<(String, Option<Value>, Option<Value>)>,
expect_fix: Vec<(String, String, Option<Value>)>, expect_fix: Vec<(String, String, Option<Value>)>,
snapshot: String, snapshot: String,
current_working_directory: Box<Path>, current_working_directory: Box<Path>,
@ -35,10 +38,28 @@ impl Tester {
rule_name: &'static str, rule_name: &'static str,
expect_pass: Vec<(S, Option<Value>)>, expect_pass: Vec<(S, Option<Value>)>,
expect_fail: 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 { ) -> Self {
let rule_path = PathBuf::from(rule_name.replace('-', "_")).with_extension("tsx"); 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_pass = expect_pass
let expect_fail = expect_fail.into_iter().map(|(s, r)| (s.into(), r)).collect::<Vec<_>>(); .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 = let current_working_directory =
env::current_dir().unwrap().join("fixtures/import").into_boxed_path(); env::current_dir().unwrap().join("fixtures/import").into_boxed_path();
Self { Self {
@ -60,9 +81,11 @@ impl Tester {
expect_pass: Vec<S>, expect_pass: Vec<S>,
expect_fail: Vec<S>, expect_fail: Vec<S>,
) -> Self { ) -> Self {
let expect_pass = expect_pass.into_iter().map(|s| (s.into(), None)).collect::<Vec<_>>(); let expect_pass =
let expect_fail = expect_fail.into_iter().map(|s| (s.into(), None)).collect::<Vec<_>>(); expect_pass.into_iter().map(|s| (s.into(), None, None)).collect::<Vec<_>>();
Self::new(rule_name, expect_pass, expect_fail) 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>>( pub fn update_expect_pass_fail<S: Into<String>>(
@ -70,8 +93,10 @@ impl Tester {
expect_pass: Vec<S>, expect_pass: Vec<S>,
expect_fail: Vec<S>, expect_fail: Vec<S>,
) -> Self { ) -> Self {
self.expect_pass = expect_pass.into_iter().map(|s| (s.into(), None)).collect::<Vec<_>>(); self.expect_pass =
self.expect_fail = expect_fail.into_iter().map(|s| (s.into(), None)).collect::<Vec<_>>(); 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 self
} }
@ -121,16 +146,16 @@ impl Tester {
} }
fn test_pass(&mut self) { fn test_pass(&mut self) {
for (test, config) in self.expect_pass.clone() { for (test, config, settings) in self.expect_pass.clone() {
let result = self.run(&test, config, false); let result = self.run(&test, config, false, &settings);
let passed = result == TestResult::Passed; let passed = result == TestResult::Passed;
assert!(passed, "expect test to pass: {test} {}", self.snapshot); assert!(passed, "expect test to pass: {test} {}", self.snapshot);
} }
} }
fn test_fail(&mut self) { fn test_fail(&mut self) {
for (test, config) in self.expect_fail.clone() { for (test, config, settings) in self.expect_fail.clone() {
let result = self.run(&test, config, false); let result = self.run(&test, config, false, &settings);
let failed = result == TestResult::Failed; let failed = result == TestResult::Failed;
assert!(failed, "expect test to fail: {test}"); assert!(failed, "expect test to fail: {test}");
} }
@ -138,7 +163,7 @@ impl Tester {
fn test_fix(&mut self) { fn test_fix(&mut self) {
for (test, expected, config) in self.expect_fix.clone() { 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 { if let TestResult::Fixed(fixed_str) = result {
assert_eq!(expected, fixed_str); assert_eq!(expected, fixed_str);
} else { } 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 allocator = Allocator::default();
let rule = self.find_rule().read_json(config); 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() let options = LintOptions::default()
.with_fix(is_fix) .with_fix(is_fix)
.with_import_plugin(self.import_plugin) .with_import_plugin(self.import_plugin)
.with_jest_plugin(self.jest_plugin) .with_jest_plugin(self.jest_plugin)
.with_jsx_a11y_plugin(self.jsx_a11y_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 { let path_to_lint = if self.import_plugin {
self.current_working_directory.join(&self.rule_path) self.current_working_directory.join(&self.rule_path)
} else { } else {

View file

@ -8,7 +8,7 @@ use oxc_ast::{
}; };
use oxc_semantic::{AstNode, SymbolFlags}; use oxc_semantic::{AstNode, SymbolFlags};
use crate::LintContext; use crate::{JsxA11y, LintContext, LintSettings};
pub fn is_create_element_call(call_expr: &CallExpression) -> bool { pub fn is_create_element_call(call_expr: &CallExpression) -> bool {
if let Some(member_expr) = call_expr.callee.get_member_expr() { 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 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)
}

View file

@ -23,7 +23,7 @@ fn run_individual_test(
) -> std::result::Result<Vec<Report>, Vec<Report>> { ) -> std::result::Result<Vec<Report>, Vec<Report>> {
use std::rc::Rc; 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 file_path = &test.relative_path.last().expect("there to be atleast 1 path part");
let source_text = &test.code; let source_text = &test.code;
@ -48,8 +48,11 @@ fn run_individual_test(
let semantic = Rc::new(semantic); let semantic = Rc::new(semantic);
let mut lint_ctx = let mut lint_ctx = LintContext::new(
LintContext::new(PathBuf::from(file_path).into_boxed_path(), &Rc::clone(&semantic)); PathBuf::from(file_path).into_boxed_path(),
&Rc::clone(&semantic),
LintSettings::default(),
);
let result = plugin.lint_file_with_rule( let result = plugin.lint_file_with_rule(
&mut lint_ctx, &mut lint_ctx,

View file

@ -13,7 +13,7 @@ use oxc::{
span::SourceType, span::SourceType,
transformer::{TransformOptions, TransformTarget, Transformer}, transformer::{TransformOptions, TransformTarget, Transformer},
}; };
use oxc_linter::{LintContext, Linter}; use oxc_linter::{LintContext, LintSettings, Linter};
use oxc_prettier::{Prettier, PrettierOptions}; use oxc_prettier::{Prettier, PrettierOptions};
use oxc_query::{schema, Adapter, SCHEMA_TEXT}; use oxc_query::{schema, Adapter, SCHEMA_TEXT};
use oxc_type_synthesis::{synthesize_program, Diagnostic as TypeCheckDiagnostic}; use oxc_type_synthesis::{synthesize_program, Diagnostic as TypeCheckDiagnostic};
@ -205,7 +205,8 @@ impl Oxc {
self.save_diagnostics(semantic_ret.errors); self.save_diagnostics(semantic_ret.errors);
let semantic = Rc::new(semantic_ret.semantic); 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 linter_ret = Linter::new().run(lint_ctx);
let diagnostics = linter_ret.into_iter().map(|e| e.error).collect(); let diagnostics = linter_ret.into_iter().map(|e| e.error).collect();
self.save_diagnostics(diagnostics); self.save_diagnostics(diagnostics);

View file

@ -13,7 +13,7 @@ use crate::walk::Walk;
use miette::NamedSource; use miette::NamedSource;
use oxc_allocator::Allocator; use oxc_allocator::Allocator;
use oxc_diagnostics::{miette, Error, Severity}; 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_linter_plugin::{make_relative_path_parts, LinterPlugin};
use oxc_parser::Parser; use oxc_parser::Parser;
use oxc_semantic::SemanticBuilder; use oxc_semantic::SemanticBuilder;
@ -270,8 +270,11 @@ impl IsolatedLintHandler {
return Some(Self::wrap_diagnostics(path, &source_text, reports)); return Some(Self::wrap_diagnostics(path, &source_text, reports));
}; };
let mut lint_ctx = let mut lint_ctx = LintContext::new(
LintContext::new(path.to_path_buf().into_boxed_path(), &Rc::new(semantic_ret.semantic)); path.to_path_buf().into_boxed_path(),
&Rc::new(semantic_ret.semantic),
LintSettings::default(),
);
{ {
if let Ok(guard) = plugin.read() { if let Ok(guard) = plugin.read() {
if let Some(plugin) = &*guard { if let Some(plugin) = &*guard {

View file

@ -10,7 +10,7 @@ use std::{path::PathBuf, rc::Rc};
use oxc_allocator::Allocator; use oxc_allocator::Allocator;
use oxc_benchmark::{criterion_group, criterion_main, BenchmarkId, Criterion}; 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_parser::Parser;
use oxc_semantic::SemanticBuilder; use oxc_semantic::SemanticBuilder;
use oxc_span::SourceType; use oxc_span::SourceType;
@ -38,7 +38,11 @@ fn bench_linter(criterion: &mut Criterion) {
let linter = Linter::from_options(lint_options).unwrap(); let linter = Linter::from_options(lint_options).unwrap();
let semantic = Rc::new(semantic_ret.semantic); let semantic = Rc::new(semantic_ret.semantic);
b.iter(|| { b.iter(|| {
linter.run(LintContext::new(PathBuf::from("").into_boxed_path(), &semantic)) linter.run(LintContext::new(
PathBuf::from("").into_boxed_path(),
&semantic,
LintSettings::default(),
))
}); });
}, },
); );