feat(linter) eslint-plugin-unicorn: prefer code point (#1167)

This commit is contained in:
Cameron 2023-11-06 12:26:56 +00:00 committed by GitHub
parent ba603cebb9
commit 5cf8543be1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 135 additions and 0 deletions

View file

@ -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,

View 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#""🦄".codePointAt(0)"#,
r#"foo.charCodeAt"#,
r#"new foo.charCodeAt"#,
r#"charCodeAt(0)"#,
r#"foo.charCodeAt?.(0)"#,
r#"foo?.charCodeAt(0)"#,
r#"foo[charCodeAt](0)"#,
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();
}

View 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`