From 008b0e650cacbdda63ea530a20b389969ea76633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=A6=E5=AE=87=E8=88=AA?= Date: Tue, 7 Nov 2023 20:28:39 +0800 Subject: [PATCH] feat(linter) eslint-plugin-import: no-amd (#1171) --- crates/oxc_linter/fixtures/import/no-amd.js | 0 crates/oxc_linter/src/rules.rs | 4 +- crates/oxc_linter/src/rules/import/no_amd.rs | 108 +++++++++++++++++++ crates/oxc_linter/src/snapshots/no_amd.snap | 48 +++++++++ 4 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 crates/oxc_linter/fixtures/import/no-amd.js create mode 100644 crates/oxc_linter/src/rules/import/no_amd.rs create mode 100644 crates/oxc_linter/src/snapshots/no_amd.snap diff --git a/crates/oxc_linter/fixtures/import/no-amd.js b/crates/oxc_linter/fixtures/import/no-amd.js new file mode 100644 index 000000000..e69de29bb diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index c41725d4c..52b9a3500 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -9,6 +9,7 @@ mod import { pub mod default; pub mod named; + pub mod no_amd; pub mod no_cycle; pub mod no_self_import; } @@ -318,5 +319,6 @@ oxc_macros::declare_all_lint_rules! { import::named, import::no_cycle, import::no_self_import, - jsx_a11y::alt_text + import::no_amd, + jsx_a11y::alt_text, } diff --git a/crates/oxc_linter/src/rules/import/no_amd.rs b/crates/oxc_linter/src/rules/import/no_amd.rs new file mode 100644 index 000000000..ba5c84a83 --- /dev/null +++ b/crates/oxc_linter/src/rules/import/no_amd.rs @@ -0,0 +1,108 @@ +use oxc_ast::ast::{Argument, Expression}; +use oxc_ast::AstKind; +use oxc_diagnostics::{ + miette::{self, Diagnostic}, + thiserror::Error, +}; +use oxc_macros::declare_oxc_lint; +use oxc_span::{Atom, Span}; + +use crate::{context::LintContext, rule::Rule, AstNode}; + +#[derive(Debug, Error, Diagnostic)] +#[error("eslint-plugin-import(no-amd): Do not use AMD `require` and `define` calls.")] +#[diagnostic(severity(warning), help("Expected imports instead of AMD {1}()"))] +struct NoAmdDiagnostic(#[label] pub Span, Atom); + +#[derive(Debug, Default, Clone)] +pub struct NoAmd; + +declare_oxc_lint!( + /// ### What it does + /// + /// Forbid AMD `require` and `define` calls. + /// + /// ### Example + /// + /// ```javascript + /// // fail + /// require([a, b], function() {} ); + /// // pass + /// require('../name'); + /// require(`../name`); + /// ``` + NoAmd, + nursery +); + +/// https://github.com/import-js/eslint-plugin-import/blob/main/src/rules/no-amd.js +impl Rule for NoAmd { + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + // not in top level + if node.scope_id() != ctx.scopes().root_scope_id() { + return; + } + if let AstKind::CallExpression(call_expr) = node.kind() { + if let Expression::Identifier(ref identifier) = &call_expr.callee { + if identifier.name != "define" && identifier.name != "require" { + return; + } + + if call_expr.arguments.len() != 2 { + return; + } + + if let Argument::Expression(Expression::ArrayExpression(_)) = call_expr.arguments[0] + { + ctx.diagnostic(NoAmdDiagnostic(identifier.span, identifier.name.clone())); + } + } + } + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + "var _ = require('lodash')", + "var find = require('lodash.find')", + "var foo = require('./foo')", + "var foo = require('../foo')", + "var foo = require('foo')", + "var foo = require('./')", + "var foo = require('@scope/foo')", + "var bar = require('./bar/index')", + r#"import "x";"#, + r#"import x from "x""#, + r#"var x = require("x")"#, + r#"require("x")"#, + // 2-args, not an array + r#"require("x", "y")"#, + // random other function + r#"setTimeout(foo, 100)"#, + // non-identifier callee + r#"(a || b)(1, 2, 3)"#, + // nested scope is fine + r#"function x() { define(["a"], function (a) {}) }"#, + r#"function x() { require(["a"], function (a) {}) }"#, + // unmatched arg types/number + r#"define(0, 1, 2)"#, + r#"define("a")"#, + ]; + + let fail = vec![ + "require([a, b], function() {})", + "define([a, b], function() {})", + r#"define([], function() {})"#, + r#"define(["a"], function(a) { console.log(a); })"#, + r#"require([], function() {})"#, + r#"require(["a"], function(a) { console.log(a); })"#, + ]; + + Tester::new_without_config(NoAmd::NAME, pass, fail) + .change_rule_path("no-amd.js") + .with_import_plugin(true) + .test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/no_amd.snap b/crates/oxc_linter/src/snapshots/no_amd.snap new file mode 100644 index 000000000..1ee6cc98d --- /dev/null +++ b/crates/oxc_linter/src/snapshots/no_amd.snap @@ -0,0 +1,48 @@ +--- +source: crates/oxc_linter/src/tester.rs +assertion_line: 119 +expression: no_amd +--- + ⚠ eslint-plugin-import(no-amd): Do not use AMD `require` and `define` calls. + ╭─[no-amd.js:1:1] + 1 │ require([a, b], function() {}) + · ─────── + ╰──── + help: Expected imports instead of AMD require() + + ⚠ eslint-plugin-import(no-amd): Do not use AMD `require` and `define` calls. + ╭─[no-amd.js:1:1] + 1 │ define([a, b], function() {}) + · ────── + ╰──── + help: Expected imports instead of AMD define() + + ⚠ eslint-plugin-import(no-amd): Do not use AMD `require` and `define` calls. + ╭─[no-amd.js:1:1] + 1 │ define([], function() {}) + · ────── + ╰──── + help: Expected imports instead of AMD define() + + ⚠ eslint-plugin-import(no-amd): Do not use AMD `require` and `define` calls. + ╭─[no-amd.js:1:1] + 1 │ define(["a"], function(a) { console.log(a); }) + · ────── + ╰──── + help: Expected imports instead of AMD define() + + ⚠ eslint-plugin-import(no-amd): Do not use AMD `require` and `define` calls. + ╭─[no-amd.js:1:1] + 1 │ require([], function() {}) + · ─────── + ╰──── + help: Expected imports instead of AMD require() + + ⚠ eslint-plugin-import(no-amd): Do not use AMD `require` and `define` calls. + ╭─[no-amd.js:1:1] + 1 │ require(["a"], function(a) { console.log(a); }) + · ─────── + ╰──── + help: Expected imports instead of AMD require() + +