diff --git a/crates/oxc_linter/src/rules/eslint/id_length.rs b/crates/oxc_linter/src/rules/eslint/id_length.rs index eaf3f490f9f41..87c9423430c4f 100644 --- a/crates/oxc_linter/src/rules/eslint/id_length.rs +++ b/crates/oxc_linter/src/rules/eslint/id_length.rs @@ -1,5 +1,11 @@ use std::ops::Deref; +use icu_segmenter::GraphemeClusterSegmenter; +use lazy_regex::Regex; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + use oxc_ast::AstKind; use oxc_ast::ast::{ BindingIdentifier, BindingPattern, BindingProperty, IdentifierName, PrivateIdentifier, @@ -7,13 +13,12 @@ use oxc_ast::ast::{ use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::{ContentEq, GetSpan, Span}; -use schemars::JsonSchema; -use crate::{AstNode, context::LintContext, rule::Rule}; -use icu_segmenter::GraphemeClusterSegmenter; -use lazy_regex::Regex; -use serde::Serialize; -use serde_json::Value; +use crate::{ + AstNode, + context::LintContext, + rule::{DefaultRuleConfig, Rule}, +}; fn id_length_is_too_short_diagnostic(span: Span, config_min: u64) -> OxcDiagnostic { OxcDiagnostic::warn(format!("Identifier name is too short (< {config_min}).")).with_label(span) @@ -26,7 +31,7 @@ fn id_length_is_too_long_diagnostic(span: Span, config_max: u64) -> OxcDiagnosti const DEFAULT_MAX_LENGTH: u64 = u64::MAX; const DEFAULT_MIN_LENGTH: u64 = 2; -#[derive(Debug, Default, Clone, PartialEq, JsonSchema, Serialize)] +#[derive(Debug, Default, Clone, PartialEq, JsonSchema, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] enum PropertyKind { #[default] @@ -34,13 +39,7 @@ enum PropertyKind { Never, } -impl PropertyKind { - pub fn from(raw: &str) -> Self { - if raw == "never" { PropertyKind::Never } else { PropertyKind::default() } - } -} - -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, Deserialize)] pub struct IdLength(Box); impl Deref for IdLength { @@ -51,11 +50,12 @@ impl Deref for IdLength { } } -#[derive(Debug, Clone, JsonSchema)] -#[serde(rename_all = "camelCase", default)] +#[derive(Debug, Clone, JsonSchema, Deserialize)] +#[serde(rename_all = "camelCase", default, deny_unknown_fields)] pub struct IdLengthConfig { /// An array of regex patterns for identifiers to exclude from the rule. /// For example, `["^x.*"]` would exclude all identifiers starting with "x". + #[serde(deserialize_with = "deserialize_exception_patterns")] exception_patterns: Vec, /// An array of identifier names that are excluded from the rule. /// For example, `["x", "y", "z"]` would allow single-letter identifiers "x", "y", and "z". @@ -86,6 +86,18 @@ impl Default for IdLengthConfig { } } +fn deserialize_exception_patterns<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + use serde::de::Error; + + Vec::::deserialize(deserializer)? + .into_iter() + .map(|pattern| Regex::new(&pattern).map_err(D::Error::custom)) + .collect() +} + declare_oxc_lint!( /// ### What it does /// @@ -171,42 +183,7 @@ declare_oxc_lint!( impl Rule for IdLength { fn from_configuration(value: Value) -> Result { - let object = value.get(0).and_then(Value::as_object); - - Ok(Self(Box::new(IdLengthConfig { - exception_patterns: object - .and_then(|map| map.get("exceptionPatterns")) - .and_then(Value::as_array) - .unwrap_or(&vec![]) - .iter() - .filter_map(|val| val.as_str().and_then(|val| Regex::new(val).ok())) - .collect(), - exceptions: object - .and_then(|map| map.get("exceptions")) - .and_then(Value::as_array) - .unwrap_or(&vec![]) - .iter() - .filter_map(|val| val.as_str()) - .map(ToString::to_string) - .collect(), - max: object - .and_then(|map| map.get("max")) - .and_then(Value::as_u64) - .unwrap_or(DEFAULT_MAX_LENGTH), - min: object - .and_then(|map| map.get("min")) - .and_then(Value::as_u64) - .unwrap_or(DEFAULT_MIN_LENGTH), - check_generic: object - .and_then(|map| map.get("checkGeneric")) - .and_then(Value::as_bool) - .unwrap_or(true), - properties: object - .and_then(|map| map.get("properties")) - .and_then(Value::as_str) - .map(PropertyKind::from) - .unwrap_or_default(), - }))) + serde_json::from_value::>(value).map(DefaultRuleConfig::into_inner) } fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {