Skip to content

Commit 8a34447

Browse files
committed
refactor(linter): improve unicorn/text-encoding-identifier-case (#11386)
- Improve the documentation - Better code style
1 parent 24aba18 commit 8a34447

File tree

1 file changed

+31
-63
lines changed

1 file changed

+31
-63
lines changed

crates/oxc_linter/src/rules/unicorn/text_encoding_identifier_case.rs

Lines changed: 31 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use cow_utils::CowUtils;
21
use oxc_ast::{
32
AstKind,
43
ast::{JSXAttributeItem, JSXAttributeName},
@@ -24,38 +23,36 @@ pub struct TextEncodingIdentifierCase;
2423
declare_oxc_lint!(
2524
/// ### What it does
2625
///
27-
/// This rule aims to enforce consistent case for text encoding identifiers.
28-
///
29-
/// Enforces `'utf8'` for UTF-8 encoding
30-
/// Enforces `'ascii'` for ASCII encoding.
26+
/// This rule enforces consistent casing for text encoding identifiers, specifically:
27+
/// - `'utf8'` instead of `'UTF-8'` or `'utf-8'`
28+
/// - `'ascii'` instead of `'ASCII'`
3129
///
3230
/// ### Why is this bad?
3331
///
34-
/// - Inconsistency in text encoding identifiers can make the code harder to read and understand.
35-
/// - The ECMAScript specification does not define the case sensitivity of text encoding identifiers, but it is common practice to use lowercase.
32+
/// Inconsistent casing of encoding identifiers reduces code readability and
33+
/// can lead to subtle confusion across a codebase. Although casing is not
34+
/// strictly enforced by ECMAScript or Node.js, using lowercase is the
35+
/// conventional and widely recognized style.
3636
///
3737
/// ### Examples
3838
///
3939
/// Examples of **incorrect** code for this rule:
4040
/// ```javascript
4141
/// import fs from 'node:fs/promises';
4242
/// async function bad() {
43-
/// await fs.readFile(file, 'UTF-8');
44-
///
45-
/// await fs.readFile(file, 'ASCII');
46-
///
47-
/// const string = buffer.toString('utf-8');
43+
/// await fs.readFile(file, 'UTF-8');
44+
/// await fs.readFile(file, 'ASCII');
45+
/// const string = buffer.toString('utf-8');
4846
/// }
4947
/// ```
5048
///
5149
/// Examples of **correct** code for this rule:
5250
/// ```javascript
51+
/// import fs from 'node:fs/promises';
5352
/// async function good() {
54-
/// await fs.readFile(file, 'utf8');
55-
///
56-
/// await fs.readFile(file, 'ascii');
57-
///
58-
/// const string = buffer.toString('utf8');
53+
/// await fs.readFile(file, 'utf8');
54+
/// await fs.readFile(file, 'ascii');
55+
/// const string = buffer.toString('utf8');
5956
/// }
6057
/// ```
6158
TextEncodingIdentifierCase,
@@ -67,79 +64,50 @@ declare_oxc_lint!(
6764
impl Rule for TextEncodingIdentifierCase {
6865
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
6966
let (s, span) = match node.kind() {
70-
AstKind::StringLiteral(string_lit) => (&string_lit.value, string_lit.span),
71-
AstKind::JSXText(jsx_text) => (&jsx_text.value, jsx_text.span),
72-
_ => {
73-
return;
74-
}
67+
AstKind::StringLiteral(string_lit) => (string_lit.value.as_str(), string_lit.span),
68+
AstKind::JSXText(jsx_text) => (jsx_text.value.as_str(), jsx_text.span),
69+
_ => return,
7570
};
76-
let s = s.as_str();
77-
7871
if s == "utf-8" && is_jsx_meta_elem_with_charset_attr(node.id(), ctx) {
7972
return;
8073
}
81-
82-
let Some(replacement) = get_replacement(s) else {
74+
let replacement = if s.eq_ignore_ascii_case("utf8") || s.eq_ignore_ascii_case("utf-8") {
75+
"utf8"
76+
} else if s.eq_ignore_ascii_case("ascii") {
77+
"ascii"
78+
} else {
8379
return;
8480
};
85-
86-
if replacement == s {
87-
return;
81+
if replacement != s {
82+
ctx.diagnostic_with_fix(
83+
text_encoding_identifier_case_diagnostic(span, replacement, s),
84+
|fixer| fixer.replace(Span::new(span.start + 1, span.end - 1), replacement),
85+
);
8886
}
89-
90-
ctx.diagnostic_with_fix(
91-
text_encoding_identifier_case_diagnostic(span, replacement, s),
92-
|fixer| fixer.replace(Span::new(span.start + 1, span.end - 1), replacement),
93-
);
94-
}
95-
}
96-
97-
fn get_replacement(node: &str) -> Option<&'static str> {
98-
if !matches!(node.len(), 4 | 5) {
99-
return None;
10087
}
101-
102-
let node_lower = node.cow_to_ascii_lowercase();
103-
104-
if node_lower == "utf-8" || node_lower == "utf8" {
105-
return Some("utf8");
106-
}
107-
108-
if node_lower == "ascii" {
109-
return Some("ascii");
110-
}
111-
112-
None
11388
}
11489

11590
fn is_jsx_meta_elem_with_charset_attr(id: NodeId, ctx: &LintContext) -> bool {
11691
let Some(parent) = ctx.nodes().parent_node(id) else {
11792
return false;
11893
};
119-
12094
let AstKind::JSXAttributeItem(JSXAttributeItem::Attribute(jsx_attr)) = parent.kind() else {
12195
return false;
12296
};
123-
12497
let JSXAttributeName::Identifier(ident) = &jsx_attr.name else {
12598
return false;
12699
};
127100
if !ident.name.eq_ignore_ascii_case("charset") {
128101
return false;
129102
}
130-
131103
let Some(AstKind::JSXOpeningElement(opening_elem)) = ctx.nodes().parent_kind(parent.id())
132104
else {
133105
return false;
134106
};
135-
136-
let Some(tag_name) = opening_elem.name.get_identifier_name() else { return false };
137-
138-
if !tag_name.eq_ignore_ascii_case("meta") {
139-
return false;
140-
}
141-
142-
true
107+
opening_elem
108+
.name
109+
.get_identifier_name()
110+
.is_some_and(|tag_name| tag_name.eq_ignore_ascii_case("meta"))
143111
}
144112

145113
#[test]

0 commit comments

Comments
 (0)