deno_lint 0.84.1

lint for deno
Documentation
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

use super::{Context, LintRule};
use crate::Program;
use deno_ast::SourceRange;

#[derive(Debug)]
pub struct PreferAscii;

const CODE: &str = "prefer-ascii";
const MESSAGE: &str = "Non-ASCII characters are not allowed";

fn hint(c: char) -> String {
  format!(
    "`{}` is \\u{{{:04x}}} and this is not an ASCII. Consider replacing it with an ASCII character",
    c, c as u32
  )
}

impl LintRule for PreferAscii {
  fn code(&self) -> &'static str {
    CODE
  }

  fn lint_program_with_ast_view(
    &self,
    context: &mut Context,
    _program: Program<'_>,
  ) {
    let mut not_asciis = Vec::new();

    let text_info = context.text_info();
    let mut src_chars = text_info.text_str().char_indices().peekable();
    let start_pos = text_info.range().start;
    while let Some((i, c)) = src_chars.next() {
      if let Some(&(pi, _)) = src_chars.peek() {
        if (pi > i + 1) || !c.is_ascii() {
          let range = SourceRange::new(start_pos + i, start_pos + pi);
          not_asciis.push((c, range));
        }
      }
    }

    for (c, range) in not_asciis {
      context.add_diagnostic_with_hint(range, CODE, MESSAGE, hint(c));
    }
  }
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn prefer_ascii_valid() {
    assert_lint_ok! {
      PreferAscii,
      r#"const pi = Math.PI;"#,
      r#"const ninja = "ninja";"#,
      r#"
function hello(name: string) {
  console.log(`Hello, ${name}`);
}
      "#,
      r#"// "comments" are also checked"#,
      r#"/* "comments" are also checked */"#,
    };
  }

  #[test]
  fn prefer_ascii_invalid() {
    assert_lint_err! {
      PreferAscii,
      r#"const π = Math.PI;"#: [
        {
          line: 1,
          col: 6,
          message: MESSAGE,
          hint: hint('π'),
        },
      ],
      r#"const ninja = "🥷";"#: [
        {
          line: 1,
          col: 15,
          message: MESSAGE,
          hint: hint('🥷'),
        },
      ],
      r#"function こんにちは(名前: string) {}"#: [
        {
          line: 1,
          col: 9,
          message: MESSAGE,
          hint: hint('こ'),
        },
        {
          line: 1,
          col: 10,
          message: MESSAGE,
          hint: hint('ん'),
        },
        {
          line: 1,
          col: 11,
          message: MESSAGE,
          hint: hint('に'),
        },
        {
          line: 1,
          col: 12,
          message: MESSAGE,
          hint: hint('ち'),
        },
        {
          line: 1,
          col: 13,
          message: MESSAGE,
          hint: hint('は'),
        },
        {
          line: 1,
          col: 15,
          message: MESSAGE,
          hint: hint('名'),
        },
        {
          line: 1,
          col: 16,
          message: MESSAGE,
          hint: hint('前'),
        },
      ],
      r#"// “comments” are also checked"#: [
        {
          line: 1,
          col: 3,
          message: MESSAGE,
          hint: hint('“'),
        },
        {
          line: 1,
          col: 12,
          message: MESSAGE,
          hint: hint('”'),
        },
      ],
      r#"/* “comments” are also checked */"#: [
        {
          line: 1,
          col: 3,
          message: MESSAGE,
          hint: hint('“'),
        },
        {
          line: 1,
          col: 12,
          message: MESSAGE,
          hint: hint('”'),
        },
      ],
    };
  }
}