feat(linter): implement no-var-requires (#575)

This commit is contained in:
Makoto Tateno 2023-07-21 11:58:25 +09:00 committed by GitHub
parent 836d430a10
commit 52c3c37b2b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 201 additions and 0 deletions

View file

@ -64,6 +64,7 @@ oxc_macros::declare_all_lint_rules! {
typescript::no_unnecessary_type_constraint,
typescript::no_misused_new,
typescript::no_this_alias,
typescript::no_var_requires,
jest::no_disabled_tests,
jest::no_test_prefixes,
}

View file

@ -0,0 +1,113 @@
use oxc_ast::AstKind;
use oxc_diagnostics::{
miette::{self, Diagnostic},
thiserror::Error,
};
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};
#[derive(Debug, Error, Diagnostic)]
#[error(
"typescript-eslint(no-var-requires): Use ES6 style imports or import foo = require(\"foo\") imports."
)]
#[diagnostic(severity(error))]
struct NoVarRequiresDiagnostic(#[label] pub Span);
#[derive(Debug, Default, Clone)]
pub struct NoVarRequires;
declare_oxc_lint!(
/// ### What it does
///
/// Disallow `require` statements except in import statements
///
/// ### Why is this bad?
///
/// In other words, the use of forms such as var foo = require("foo") are banned. Instead use ES6 style imports or import foo = require("foo") imports.
///
/// ```typescript
/// var foo = require('foo');
/// const foo = require('foo');
/// let foo = require('foo');
/// ```
NoVarRequires,
correctness
);
impl Rule for NoVarRequires {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
if !ctx.source_type().is_typescript() {
return;
}
if let AstKind::CallExpression(expr) = node.kind() && expr.is_require_call() {
if ctx.scopes().get_bindings(node.scope_id()).contains_key("require") {
return;
}
if let Some(parent_node) = ctx.nodes().parent_node(node.id()) {
if let AstKind::Argument(_) = parent_node.kind() {
if let Some(parent_node) = ctx.nodes().parent_node(parent_node.id()) {
if is_target_node(&parent_node.kind()) {
ctx.diagnostic(NoVarRequiresDiagnostic(expr.span));
}
}
}
if is_target_node(&parent_node.kind()) {
ctx.diagnostic(NoVarRequiresDiagnostic(expr.span));
}
}
}
}
}
fn is_target_node(node_kind: &AstKind<'_>) -> bool {
matches!(node_kind, AstKind::CallExpression(_)
| AstKind::MemberExpression(_)
| AstKind::NewExpression(_)
| AstKind::TSAsExpression(_)
| AstKind::TSTypeAssertion(_)
| AstKind::VariableDeclarator(_))
}
#[test]
fn test() {
use crate::tester::Tester;
let pass = vec![
"import foo = require('foo');",
"require('foo');",
"require?.('foo');",
r#"
import { createRequire } from 'module';
const require = createRequire('foo');
const json = require('./some.json');
"#,
];
let fail = vec![
"var foo = require('foo');",
"const foo = require('foo');",
"let foo = require('foo');",
"let foo = trick(require('foo'));",
"var foo = require?.('foo');",
"const foo = require?.('foo');",
"let foo = require?.('foo');",
"let foo = trick(require?.('foo'));",
"let foo = trick?.(require('foo'));",
"const foo = require('./foo.json') as Foo;",
// Because of TypeScript disallows angle bracket type assertions in .tsx files, comment out this below case all tests parsing as tsx.
// "const foo = <Foo>require('./foo.json');",
"const foo: Foo = require('./foo.json').default;",
r#"
const configValidator = new Validator(require('./a.json'));
configValidator.addSchema(require('./a.json'));
"#
];
Tester::new_without_config(NoVarRequires::NAME, pass, fail).test_and_snapshot();
}

View file

@ -0,0 +1,87 @@
---
source: crates/oxc_linter/src/tester.rs
expression: no_var_requires
---
× typescript-eslint(no-var-requires): Use ES6 style imports or import foo = require("foo") imports.
╭─[no_var_requires.tsx:1:1]
1 │ var foo = require('foo');
· ──────────────
╰────
× typescript-eslint(no-var-requires): Use ES6 style imports or import foo = require("foo") imports.
╭─[no_var_requires.tsx:1:1]
1 │ const foo = require('foo');
· ──────────────
╰────
× typescript-eslint(no-var-requires): Use ES6 style imports or import foo = require("foo") imports.
╭─[no_var_requires.tsx:1:1]
1 │ let foo = require('foo');
· ──────────────
╰────
× typescript-eslint(no-var-requires): Use ES6 style imports or import foo = require("foo") imports.
╭─[no_var_requires.tsx:1:1]
1 │ let foo = trick(require('foo'));
· ──────────────
╰────
× typescript-eslint(no-var-requires): Use ES6 style imports or import foo = require("foo") imports.
╭─[no_var_requires.tsx:1:1]
1 │ var foo = require?.('foo');
· ────────────────
╰────
× typescript-eslint(no-var-requires): Use ES6 style imports or import foo = require("foo") imports.
╭─[no_var_requires.tsx:1:1]
1 │ const foo = require?.('foo');
· ────────────────
╰────
× typescript-eslint(no-var-requires): Use ES6 style imports or import foo = require("foo") imports.
╭─[no_var_requires.tsx:1:1]
1 │ let foo = require?.('foo');
· ────────────────
╰────
× typescript-eslint(no-var-requires): Use ES6 style imports or import foo = require("foo") imports.
╭─[no_var_requires.tsx:1:1]
1 │ let foo = trick(require?.('foo'));
· ────────────────
╰────
× typescript-eslint(no-var-requires): Use ES6 style imports or import foo = require("foo") imports.
╭─[no_var_requires.tsx:1:1]
1 │ let foo = trick?.(require('foo'));
· ──────────────
╰────
× typescript-eslint(no-var-requires): Use ES6 style imports or import foo = require("foo") imports.
╭─[no_var_requires.tsx:1:1]
1 │ const foo = require('./foo.json') as Foo;
· ─────────────────────
╰────
× typescript-eslint(no-var-requires): Use ES6 style imports or import foo = require("foo") imports.
╭─[no_var_requires.tsx:1:1]
1 │ const foo: Foo = require('./foo.json').default;
· ─────────────────────
╰────
× typescript-eslint(no-var-requires): Use ES6 style imports or import foo = require("foo") imports.
╭─[no_var_requires.tsx:1:1]
1 │
2 │ const configValidator = new Validator(require('./a.json'));
· ───────────────────
3 │ configValidator.addSchema(require('./a.json'));
╰────
× typescript-eslint(no-var-requires): Use ES6 style imports or import foo = require("foo") imports.
╭─[no_var_requires.tsx:2:1]
2 │ const configValidator = new Validator(require('./a.json'));
3 │ configValidator.addSchema(require('./a.json'));
· ───────────────────
4 │
╰────