-
-
Notifications
You must be signed in to change notification settings - Fork 503
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(linter): Add eslint-plugin-promise rules: avoid-new, no-new-stat…
…ics, params-names (#4293) This introduces the `eslint-plugin-promise` plugin and implements three relatively simple rules. Split off from #4252
- Loading branch information
Showing
15 changed files
with
512 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
use oxc_ast::{ast::Expression, AstKind}; | ||
use oxc_diagnostics::OxcDiagnostic; | ||
use oxc_macros::declare_oxc_lint; | ||
use oxc_span::Span; | ||
|
||
use crate::{context::LintContext, rule::Rule, AstNode}; | ||
|
||
fn avoid_new_promise_diagnostic(span0: Span) -> OxcDiagnostic { | ||
OxcDiagnostic::warn("eslint-plugin-promise(avoid-new): Avoid creating new promises") | ||
.with_label(span0) | ||
} | ||
|
||
#[derive(Debug, Default, Clone)] | ||
pub struct AvoidNew; | ||
|
||
declare_oxc_lint!( | ||
/// ### What it does | ||
/// | ||
/// Disallow creating new promises outside of utility libs. | ||
/// | ||
/// ### Why is this bad? | ||
/// | ||
/// If you dislike the new promise style promises. | ||
/// | ||
/// ### Example | ||
/// ```javascript | ||
/// new Promise((resolve, reject) => { ... }); | ||
/// ``` | ||
AvoidNew, | ||
restriction, | ||
); | ||
|
||
impl Rule for AvoidNew { | ||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { | ||
let AstKind::NewExpression(expr) = node.kind() else { | ||
return; | ||
}; | ||
|
||
let Expression::Identifier(ident) = &expr.callee else { | ||
return; | ||
}; | ||
|
||
if ident.name == "Promise" && ctx.semantic().is_reference_to_global_variable(ident) { | ||
ctx.diagnostic(avoid_new_promise_diagnostic(expr.span)); | ||
} | ||
} | ||
} | ||
|
||
#[test] | ||
fn test() { | ||
use crate::tester::Tester; | ||
|
||
let pass = vec![ | ||
"Promise.resolve()", | ||
"Promise.reject()", | ||
"Promise.all()", | ||
"new Horse()", | ||
"new PromiseLikeThing()", | ||
"new Promise.resolve()", | ||
]; | ||
|
||
let fail = vec![ | ||
"var x = new Promise(function (x, y) {})", | ||
"new Promise()", | ||
"Thing(new Promise(() => {}))", | ||
]; | ||
|
||
Tester::new(AvoidNew::NAME, pass, fail).test_and_snapshot(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
use oxc_ast::{ast::Expression, AstKind}; | ||
use oxc_diagnostics::OxcDiagnostic; | ||
use oxc_macros::declare_oxc_lint; | ||
use oxc_span::Span; | ||
|
||
use crate::{context::LintContext, rule::Rule, AstNode}; | ||
|
||
fn static_promise_diagnostic(x0: &str, span0: Span) -> OxcDiagnostic { | ||
OxcDiagnostic::warn(format!( | ||
"eslint-plugin-promise(no-new-statics): Disallow calling `new` on a `Promise.{x0}`" | ||
)) | ||
.with_label(span0) | ||
} | ||
|
||
#[derive(Debug, Default, Clone)] | ||
pub struct NoNewStatics; | ||
|
||
declare_oxc_lint!( | ||
/// ### What it does | ||
/// | ||
/// Disallow calling new on a Promise static method. | ||
/// | ||
/// ### Why is this bad? | ||
/// | ||
/// Calling a Promise static method with new is invalid, resulting in a TypeError at runtime. | ||
/// | ||
/// ### Example | ||
/// ```javascript | ||
/// new Promise.resolve(value); | ||
/// ``` | ||
NoNewStatics, | ||
correctness | ||
); | ||
|
||
impl Rule for NoNewStatics { | ||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { | ||
let AstKind::NewExpression(new_expr) = node.kind() else { | ||
return; | ||
}; | ||
|
||
let Some(member_expr) = &new_expr.callee.get_member_expr() else { | ||
return; | ||
}; | ||
|
||
let Expression::Identifier(ident) = &member_expr.object() else { | ||
return; | ||
}; | ||
|
||
if ident.name != "Promise" || !ctx.semantic().is_reference_to_global_variable(ident) { | ||
return; | ||
} | ||
|
||
let Some(prop_name) = member_expr.static_property_name() else { | ||
return; | ||
}; | ||
|
||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise | ||
if matches!( | ||
prop_name, | ||
"resolve" | "reject" | "all" | "allSettled" | "race" | "any" | "withResolvers" | ||
) { | ||
ctx.diagnostic_with_fix( | ||
static_promise_diagnostic( | ||
prop_name, | ||
Span::new(new_expr.span.start, ident.span.start - 1), | ||
), | ||
|fixer| fixer.delete_range(Span::new(new_expr.span.start, ident.span.start)), | ||
); | ||
} | ||
} | ||
} | ||
|
||
#[test] | ||
fn test() { | ||
use crate::tester::Tester; | ||
|
||
let pass = vec![ | ||
"Promise.resolve()", | ||
"Promise.reject()", | ||
"Promise.all()", | ||
"Promise.race()", | ||
"new Promise(function (resolve, reject) {})", | ||
"new SomeClass()", | ||
"SomeClass.resolve()", | ||
"new SomeClass.resolve()", | ||
]; | ||
|
||
let fail = vec![ | ||
"new Promise.resolve()", | ||
"new Promise.reject()", | ||
"new Promise.all()", | ||
"new Promise.allSettled()", | ||
"new Promise.any()", | ||
"new Promise.race()", | ||
"function foo() { | ||
var a = getA() | ||
return new Promise.resolve(a) | ||
}", | ||
]; | ||
|
||
let fix = vec![ | ||
("new Promise.resolve()", "Promise.resolve()", None), | ||
("new Promise.reject()", "Promise.reject()", None), | ||
("new Promise.all()", "Promise.all()", None), | ||
("new Promise.allSettled()", "Promise.allSettled()", None), | ||
("new Promise.any()", "Promise.any()", None), | ||
("new Promise.race()", "Promise.race()", None), | ||
]; | ||
Tester::new(NoNewStatics::NAME, pass, fail).expect_fix(fix).test_and_snapshot(); | ||
} |
Oops, something went wrong.