Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 29 additions & 52 deletions crates/oxc_linter/src/rules/eslint/id_length.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
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,
};
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)
Expand All @@ -26,21 +31,15 @@ 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]
Always,
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<IdLengthConfig>);

impl Deref for IdLength {
Expand All @@ -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<Regex>,
/// 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".
Expand Down Expand Up @@ -86,6 +86,18 @@ impl Default for IdLengthConfig {
}
}

fn deserialize_exception_patterns<'de, D>(deserializer: D) -> Result<Vec<Regex>, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::Error;

Vec::<String>::deserialize(deserializer)?
.into_iter()
.map(|pattern| Regex::new(&pattern).map_err(D::Error::custom))
.collect()
}

declare_oxc_lint!(
/// ### What it does
///
Expand Down Expand Up @@ -171,42 +183,7 @@ declare_oxc_lint!(

impl Rule for IdLength {
fn from_configuration(value: Value) -> Result<Self, serde_json::error::Error> {
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::<DefaultRuleConfig<Self>>(value).map(DefaultRuleConfig::into_inner)
}

fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
Expand Down
Loading