deno_lint 0.2.1

lint for deno
Documentation
use super::{Context, LintRule};
use regex::{Matches, Regex};
use std::sync::Arc;
use swc_common::{hygiene::SyntaxContext, BytePos, Span};
use swc_ecmascript::ast::Module;
use swc_ecmascript::ast::Str;
use swc_ecmascript::visit::Node;
use swc_ecmascript::visit::Visit;

pub struct NoIrregularWhitespace;

lazy_static! {
  static ref IRREGULAR_WHITESPACE: Regex = Regex::new(r"[\f\v\u0085\ufeff\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u202f\u205f\u3000]+").unwrap();
  static ref IRREGULAR_LINE_TERMINATORS: Regex = Regex::new(r"[\u2028\u2029]").unwrap();
}

fn test_for_whitespace(value: &str) -> Option<Vec<Matches>> {
  let mut matches_vector: Vec<Matches> = vec![];
  if IRREGULAR_WHITESPACE.is_match(value) {
    let matches = IRREGULAR_WHITESPACE.find_iter(value);
    matches_vector.push(matches);
  }
  if IRREGULAR_LINE_TERMINATORS.is_match(value) {
    let matches = IRREGULAR_LINE_TERMINATORS.find_iter(value);
    matches_vector.push(matches);
  }
  if !matches_vector.is_empty() {
    Some(matches_vector)
  } else {
    None
  }
}

impl LintRule for NoIrregularWhitespace {
  fn new() -> Box<Self> {
    Box::new(NoIrregularWhitespace)
  }

  fn code(&self) -> &'static str {
    "no-irregular-whitespace"
  }

  fn lint_module(&self, context: Arc<Context>, module: &Module) {
    let mut visitor = NoIrregularWhitespaceVisitor::default();
    visitor.visit_module(module, module);

    let excluded_ranges = visitor.ranges.iter();

    let file_and_lines = context.source_map.span_to_lines(module.span).unwrap();
    let file = file_and_lines.file;

    for line_index in 0..file.count_lines() {
      let line = file.get_line(line_index).unwrap();
      let (byte_pos, _hi) = file.line_bounds(line_index);

      if let Some(whitespace_results) = test_for_whitespace(&line) {
        for whitespace_matches in whitespace_results.into_iter() {
          for whitespace_match in whitespace_matches {
            let range = whitespace_match.range();
            let span = Span::new(
              byte_pos + BytePos(range.start as u32),
              byte_pos + BytePos(range.end as u32),
              SyntaxContext::empty(),
            );
            let is_excluded =
              excluded_ranges.clone().any(|range| range.contains(span));
            if !is_excluded {
              context.add_diagnostic(
                span,
                "no-irregular-whitespace",
                "Irregular whitespace not allowed.",
              );
            }
          }
        }
      }
    }
  }
}

struct NoIrregularWhitespaceVisitor {
  ranges: Vec<Span>,
}

impl NoIrregularWhitespaceVisitor {
  fn default() -> Self {
    Self { ranges: vec![] }
  }
}

impl Visit for NoIrregularWhitespaceVisitor {
  fn visit_str(&mut self, string_literal: &Str, _parent: &dyn Node) {
    self.ranges.push(string_literal.span);
  }
}

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

  #[test]
  fn no_irregular_whitespace_valid() {
    assert_lint_ok::<NoIrregularWhitespace>("'\\u{000B}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\u{000C}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\u{0085}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\u{00A0}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\u{180E}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\u{feff}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\u{2000}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\u{2001}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\u{2002}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\u{2003}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\u{2004}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\u{2005}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\u{2006}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\u{2007}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\u{2008}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\u{2009}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\u{200A}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\u{200B}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\u{2028}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\u{2029}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\u{202F}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\u{205f}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\u{3000}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\u{000B}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\u{000C}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\u{0085}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\u{00A0}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\u{180E}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\u{feff}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\u{2000}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\u{2001}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\u{2002}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\u{2003}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\u{2004}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\u{2005}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\u{2006}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\u{2007}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\u{2008}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\u{2009}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\u{200A}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\u{200B}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\\u{2028}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\\\u{2029}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\u{202F}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\u{205f}';");
    assert_lint_ok::<NoIrregularWhitespace>("'\u{3000}';");
  }

  #[test]
  fn no_irregular_whitespace_invalid() {
    assert_lint_err::<NoIrregularWhitespace>("var any \u{000B} = 'thing';", 8);
    assert_lint_err::<NoIrregularWhitespace>("var any \u{000C} = 'thing';", 8);
    assert_lint_err::<NoIrregularWhitespace>("var any \u{00A0} = 'thing';", 8);
    assert_lint_err::<NoIrregularWhitespace>("var any \u{feff} = 'thing';", 8);
    assert_lint_err::<NoIrregularWhitespace>("var any \u{2000} = 'thing';", 8);
    assert_lint_err::<NoIrregularWhitespace>("var any \u{2001} = 'thing';", 8);
    assert_lint_err::<NoIrregularWhitespace>("var any \u{2002} = 'thing';", 8);
    assert_lint_err::<NoIrregularWhitespace>("var any \u{2003} = 'thing';", 8);
    assert_lint_err::<NoIrregularWhitespace>("var any \u{2004} = 'thing';", 8);
    assert_lint_err::<NoIrregularWhitespace>("var any \u{2005} = 'thing';", 8);
    assert_lint_err::<NoIrregularWhitespace>("var any \u{2006} = 'thing';", 8);
    assert_lint_err::<NoIrregularWhitespace>("var any \u{2007} = 'thing';", 8);
    assert_lint_err::<NoIrregularWhitespace>("var any \u{2008} = 'thing';", 8);
    assert_lint_err::<NoIrregularWhitespace>("var any \u{2009} = 'thing';", 8);
    assert_lint_err::<NoIrregularWhitespace>("var any \u{200A} = 'thing';", 8);
    assert_lint_err::<NoIrregularWhitespace>("var any \u{2028} = 'thing';", 8);
    assert_lint_err::<NoIrregularWhitespace>("var any \u{2029} = 'thing';", 8);
    assert_lint_err::<NoIrregularWhitespace>("var any \u{202F} = 'thing';", 8);
    assert_lint_err::<NoIrregularWhitespace>("var any \u{205f} = 'thing';", 8);
    assert_lint_err::<NoIrregularWhitespace>("var any \u{3000} = 'thing';", 8);
    assert_lint_err_on_line_n::<NoIrregularWhitespace>(
      "var a = 'b',\u{2028}c = 'd',\ne = 'f'\u{2028}",
      vec![(1, 12), (2, 7)],
    );
    assert_lint_err_on_line_n::<NoIrregularWhitespace>(
      "var any \u{3000} = 'thing', other \u{3000} = 'thing';\nvar third \u{3000} = 'thing';",
      vec![(1, 8), (1, 27), (2, 10)],
    );
  }
}