mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 20:32:10 +00:00
feat(linter) eslint-plugin-unicorn: prefer code point (#1167)
This commit is contained in:
parent
ba603cebb9
commit
5cf8543be1
3 changed files with 135 additions and 0 deletions
|
|
@ -153,6 +153,7 @@ mod unicorn {
|
|||
pub mod no_thenable;
|
||||
pub mod no_unnecessary_await;
|
||||
pub mod prefer_array_flat_map;
|
||||
pub mod prefer_code_point;
|
||||
pub mod prefer_date_now;
|
||||
pub mod prefer_logical_operator_over_ternary;
|
||||
pub mod prefer_query_selector;
|
||||
|
|
@ -283,6 +284,7 @@ oxc_macros::declare_all_lint_rules! {
|
|||
unicorn::no_thenable,
|
||||
unicorn::no_unnecessary_await,
|
||||
unicorn::prefer_array_flat_map,
|
||||
unicorn::prefer_code_point,
|
||||
unicorn::prefer_date_now,
|
||||
unicorn::prefer_logical_operator_over_ternary,
|
||||
unicorn::prefer_query_selector,
|
||||
|
|
|
|||
100
crates/oxc_linter/src/rules/unicorn/prefer_code_point.rs
Normal file
100
crates/oxc_linter/src/rules/unicorn/prefer_code_point.rs
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
use oxc_ast::{ast::Expression, AstKind};
|
||||
use oxc_diagnostics::{
|
||||
miette::{self, Diagnostic},
|
||||
thiserror::{self, Error},
|
||||
};
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::Span;
|
||||
|
||||
use crate::{context::LintContext, rule::Rule, AstNode};
|
||||
|
||||
#[derive(Debug, Error, Diagnostic)]
|
||||
#[error("eslint-plugin-unicorn(prefer-code-point): Prefer `{1}` over `{2}`")]
|
||||
#[diagnostic(severity(warning), help("Unicode is better supported in `{1}` than `{2}`"))]
|
||||
struct PreferCodePointDiagnostic(#[label] pub Span, pub &'static str, pub &'static str);
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct PreferCodePoint;
|
||||
|
||||
declare_oxc_lint!(
|
||||
/// ### What it does
|
||||
///
|
||||
/// Prefers usage of `String.prototype.codePointAt` over `String.prototype.charCodeAt`.
|
||||
/// Prefers usage of `String.fromCodePoint` over `String.fromCharCode`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// Unicode is better supported in [`String#codePointAt()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt) and [`String.fromCodePoint()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint).
|
||||
///
|
||||
/// [Difference between `String.fromCodePoint()` and `String.fromCharCode()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint#compared_to_fromcharcode)
|
||||
///
|
||||
/// ### Example
|
||||
/// ```javascript
|
||||
/// // bad
|
||||
/// '🦄'.charCodeAt(0);
|
||||
/// String.fromCharCode(0x1f984);
|
||||
///
|
||||
/// // good
|
||||
/// '🦄'.codePointAt(0);
|
||||
/// String.fromCodePoint(0x1f984);
|
||||
/// ```
|
||||
PreferCodePoint,
|
||||
pedantic
|
||||
);
|
||||
|
||||
impl Rule for PreferCodePoint {
|
||||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
||||
let AstKind::CallExpression(call_expr) = node.kind() else { return };
|
||||
|
||||
let Expression::MemberExpression(memb_expr) = &call_expr.callee else { return };
|
||||
|
||||
if memb_expr.is_computed() || memb_expr.optional() || call_expr.optional {
|
||||
return;
|
||||
}
|
||||
|
||||
let (current, replacement, span) = match memb_expr.static_property_info() {
|
||||
Some((span, "charCodeAt")) => ("charCodeAt", "codePointAt", span),
|
||||
Some((span, "fromCharCode")) => ("fromCharCode", "fromCodePoint", span),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
ctx.diagnostic(PreferCodePointDiagnostic(span, replacement, current));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
use crate::tester::Tester;
|
||||
|
||||
let pass = vec"#,
|
||||
r#"foo["charCodeAt"](0)"#,
|
||||
r#"foo.notCharCodeAt(0)"#,
|
||||
r#"String.fromCodePoint(0x1f984)"#,
|
||||
r#"String.fromCodePoint"#,
|
||||
r#"new String.fromCodePoint"#,
|
||||
r#"fromCodePoint(foo)"#,
|
||||
r#"String.fromCodePoint?.(foo)"#,
|
||||
r#"String?.fromCodePoint(foo)"#,
|
||||
r#"window.String.fromCodePoint(foo)"#,
|
||||
r#"String[fromCodePoint](foo)"#,
|
||||
r#"String["fromCodePoint"](foo)"#,
|
||||
r#"String.notFromCodePoint(foo)"#,
|
||||
r#"NotString.fromCodePoint(foo)"#,
|
||||
];
|
||||
|
||||
let fail = vec![
|
||||
r#"string.charCodeAt(index)"#,
|
||||
r#"(( (( string )).charCodeAt( ((index)), )))"#,
|
||||
r#"String.fromCharCode( code )"#,
|
||||
r#"(( (( String )).fromCharCode( ((code)), ) ))"#,
|
||||
];
|
||||
|
||||
Tester::new_without_config(PreferCodePoint::NAME, pass, fail).test_and_snapshot();
|
||||
}
|
||||
33
crates/oxc_linter/src/snapshots/prefer_code_point.snap
Normal file
33
crates/oxc_linter/src/snapshots/prefer_code_point.snap
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
source: crates/oxc_linter/src/tester.rs
|
||||
expression: prefer_code_point
|
||||
---
|
||||
⚠ eslint-plugin-unicorn(prefer-code-point): Prefer `codePointAt` over `charCodeAt`
|
||||
╭─[prefer_code_point.tsx:1:1]
|
||||
1 │ string.charCodeAt(index)
|
||||
· ──────────
|
||||
╰────
|
||||
help: Unicode is better supported in `codePointAt` than `charCodeAt`
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-code-point): Prefer `codePointAt` over `charCodeAt`
|
||||
╭─[prefer_code_point.tsx:1:1]
|
||||
1 │ (( (( string )).charCodeAt( ((index)), )))
|
||||
· ──────────
|
||||
╰────
|
||||
help: Unicode is better supported in `codePointAt` than `charCodeAt`
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-code-point): Prefer `fromCodePoint` over `fromCharCode`
|
||||
╭─[prefer_code_point.tsx:1:1]
|
||||
1 │ String.fromCharCode( code )
|
||||
· ────────────
|
||||
╰────
|
||||
help: Unicode is better supported in `fromCodePoint` than `fromCharCode`
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-code-point): Prefer `fromCodePoint` over `fromCharCode`
|
||||
╭─[prefer_code_point.tsx:1:1]
|
||||
1 │ (( (( String )).fromCharCode( ((code)), ) ))
|
||||
· ────────────
|
||||
╰────
|
||||
help: Unicode is better supported in `fromCodePoint` than `fromCharCode`
|
||||
|
||||
|
||||
Loading…
Reference in a new issue