diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index ffe855e5f884f..b1fbdff96088a 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -492,6 +492,7 @@ mod vitest { mod node { pub mod no_exports_assign; + pub mod no_new_require; } oxc_macros::declare_all_lint_rules! { @@ -738,6 +739,7 @@ oxc_macros::declare_all_lint_rules! { nextjs::no_typos, nextjs::no_unwanted_polyfillio, node::no_exports_assign, + node::no_new_require, oxc::approx_constant, oxc::bad_array_method_on_arguments, oxc::bad_bitwise_operator, diff --git a/crates/oxc_linter/src/rules/node/no_new_require.rs b/crates/oxc_linter/src/rules/node/no_new_require.rs new file mode 100644 index 0000000000000..8fb2ee4badcc4 --- /dev/null +++ b/crates/oxc_linter/src/rules/node/no_new_require.rs @@ -0,0 +1,73 @@ +use oxc_ast::AstKind; +use oxc_diagnostics::OxcDiagnostic; +use oxc_macros::declare_oxc_lint; +use oxc_span::Span; + +use crate::{context::LintContext, rule::Rule, AstNode}; + +fn no_new_require(span: Span) -> OxcDiagnostic { + OxcDiagnostic::warn("Unexpected use of new with require") + .with_label(span) + .with_help("Initialise the constructor separate from the import statement") +} + +#[derive(Debug, Default, Clone)] +pub struct NoNewRequire; + +declare_oxc_lint!( + /// ### What it does + /// + /// Warn about calling `new` on `require`. + /// + /// ### Why is this bad? + /// + /// The `require` function is used to include modules and might return a constructor. As this + /// is not always the case this can be confusing. + /// + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: + /// ```js + /// var appHeader = new require('app-header'); + /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```js + /// var AppHeader = require('app-header'); + /// var appHeader = new AppHeader(); + /// ``` + NoNewRequire, + restriction); + +impl Rule for NoNewRequire { + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + let AstKind::NewExpression(new_expression) = node.kind() else { + return; + }; + + if !new_expression.callee.is_specific_id("require") { + return; + }; + + ctx.diagnostic(no_new_require(new_expression.span)); + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + "var appHeader = require('app-header')", + "var AppHeader = new (require('app-header'))", + "var AppHeader = new (require('headers').appHeader)", + "var AppHeader = require('app-header'); var appHeader = new AppHeader();", + ]; + + let fail = vec![ + "var appHeader = new require('app-header')", + "var appHeader = new require('headers').appHeader", + ]; + + Tester::new(NoNewRequire::NAME, pass, fail).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/no_new_require.snap b/crates/oxc_linter/src/snapshots/no_new_require.snap new file mode 100644 index 0000000000000..43ea431319597 --- /dev/null +++ b/crates/oxc_linter/src/snapshots/no_new_require.snap @@ -0,0 +1,16 @@ +--- +source: crates/oxc_linter/src/tester.rs +--- + ⚠ eslint-plugin-node(no-new-require): Unexpected use of new with require + ╭─[no_new_require.tsx:1:17] + 1 │ var appHeader = new require('app-header') + · ───────────────────────── + ╰──── + help: Initialise the constructor separate from the import statement + + ⚠ eslint-plugin-node(no-new-require): Unexpected use of new with require + ╭─[no_new_require.tsx:1:17] + 1 │ var appHeader = new require('headers').appHeader + · ────────────────────── + ╰──── + help: Initialise the constructor separate from the import statement