mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(linter) eslint plugin unicorn: prefer date now (#1150)
This commit is contained in:
parent
f8be0b4cf2
commit
b8cc460c5c
3 changed files with 399 additions and 0 deletions
|
|
@ -148,6 +148,7 @@ mod unicorn {
|
|||
pub mod no_thenable;
|
||||
pub mod no_unnecessary_await;
|
||||
pub mod prefer_array_flat_map;
|
||||
pub mod prefer_date_now;
|
||||
pub mod prefer_logical_operator_over_ternary;
|
||||
pub mod prefer_query_selector;
|
||||
pub mod prefer_string_trim_start_end;
|
||||
|
|
@ -271,6 +272,7 @@ oxc_macros::declare_all_lint_rules! {
|
|||
unicorn::no_thenable,
|
||||
unicorn::no_unnecessary_await,
|
||||
unicorn::prefer_array_flat_map,
|
||||
unicorn::prefer_date_now,
|
||||
unicorn::prefer_logical_operator_over_ternary,
|
||||
unicorn::require_number_to_fixed_digits_argument,
|
||||
unicorn::switch_case_braces,
|
||||
|
|
|
|||
224
crates/oxc_linter/src/rules/unicorn/prefer_date_now.rs
Normal file
224
crates/oxc_linter/src/rules/unicorn/prefer_date_now.rs
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
use oxc_ast::{
|
||||
ast::{Argument, Expression},
|
||||
AstKind,
|
||||
};
|
||||
use oxc_diagnostics::{
|
||||
miette::{self, Diagnostic},
|
||||
thiserror::{self, Error},
|
||||
};
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::{Atom, GetSpan, Span};
|
||||
use oxc_syntax::operator::{AssignmentOperator, BinaryOperator, UnaryOperator};
|
||||
|
||||
use crate::{context::LintContext, rule::Rule, AstNode};
|
||||
|
||||
#[derive(Debug, Error, Diagnostic)]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
enum PreferDateNowDiagnostic {
|
||||
#[error("eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date()`")]
|
||||
#[diagnostic(severity(warning), help("Change to `Date.now()`."))]
|
||||
PreferDateNow(#[label] Span),
|
||||
|
||||
#[error("eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date().{1}()`")]
|
||||
#[diagnostic(severity(warning), help("Change to `Date.now()`."))]
|
||||
PreferDateNowOverMethods(#[label] Span, Atom),
|
||||
|
||||
#[error(
|
||||
"eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `Number(new Date())`"
|
||||
)]
|
||||
#[diagnostic(severity(warning), help("Change to `Date.now()`."))]
|
||||
PreferDateNowOverNumberDateObject(#[label] Span),
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct PreferDateNow;
|
||||
|
||||
declare_oxc_lint!(
|
||||
/// ### What it does
|
||||
///
|
||||
/// Prefers use of `Date.now()` over `new Date().getTime()` or `new Date().valueOf()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// Using `Date.now()` is shorter and nicer than `new Date().getTime()`, and avoids unnecessary instantiation of `Date` objects.
|
||||
///
|
||||
///
|
||||
/// ### Example
|
||||
/// ```javascript
|
||||
/// // bad
|
||||
/// const ts = new Date().getTime();
|
||||
/// const ts = new Date().valueOf();
|
||||
///
|
||||
/// // good
|
||||
/// const ts = Date.now();
|
||||
/// ```
|
||||
PreferDateNow,
|
||||
correctness
|
||||
);
|
||||
|
||||
impl Rule for PreferDateNow {
|
||||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
||||
match node.kind() {
|
||||
AstKind::CallExpression(call_expr) => {
|
||||
// `new Date().{getTime,valueOf}()`
|
||||
if let Expression::MemberExpression(member_expr) =
|
||||
&call_expr.callee.without_parenthesized()
|
||||
{
|
||||
if call_expr.arguments.is_empty()
|
||||
&& !member_expr.is_computed()
|
||||
&& matches!(member_expr.static_property_name(), Some("getTime" | "valueOf"))
|
||||
&& is_new_date(member_expr.object().without_parenthesized())
|
||||
{
|
||||
ctx.diagnostic(PreferDateNowDiagnostic::PreferDateNowOverMethods(
|
||||
call_expr.span,
|
||||
member_expr.static_property_name().unwrap().into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// `{Number,BigInt}(new Date())`
|
||||
if let Expression::Identifier(ident) = &call_expr.callee {
|
||||
if matches!(ident.name.as_str(), "Number" | "BigInt")
|
||||
&& call_expr.arguments.len() == 1
|
||||
{
|
||||
if let Some(Argument::Expression(expr)) = call_expr.arguments.get(0) {
|
||||
if is_new_date(expr.without_parenthesized()) {
|
||||
ctx.diagnostic(
|
||||
PreferDateNowDiagnostic::PreferDateNowOverNumberDateObject(
|
||||
call_expr.span,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
AstKind::UnaryExpression(unary_expr) => {
|
||||
if !matches!(
|
||||
unary_expr.operator,
|
||||
UnaryOperator::UnaryPlus | UnaryOperator::UnaryNegation,
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if is_new_date(&unary_expr.argument) {
|
||||
ctx.diagnostic(PreferDateNowDiagnostic::PreferDateNow(
|
||||
unary_expr.argument.span(),
|
||||
));
|
||||
}
|
||||
}
|
||||
AstKind::AssignmentExpression(assignment_expr) => {
|
||||
if !matches!(
|
||||
assignment_expr.operator,
|
||||
AssignmentOperator::Subtraction
|
||||
| AssignmentOperator::Multiplication
|
||||
| AssignmentOperator::Division
|
||||
| AssignmentOperator::Remainder
|
||||
| AssignmentOperator::Exponential
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if is_new_date(&assignment_expr.right) {
|
||||
ctx.diagnostic(PreferDateNowDiagnostic::PreferDateNow(
|
||||
assignment_expr.right.span(),
|
||||
));
|
||||
}
|
||||
}
|
||||
AstKind::BinaryExpression(bin_expr) => {
|
||||
if !matches!(
|
||||
bin_expr.operator,
|
||||
BinaryOperator::Subtraction
|
||||
| BinaryOperator::Multiplication
|
||||
| BinaryOperator::Division
|
||||
| BinaryOperator::Remainder
|
||||
| BinaryOperator::Exponential
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if is_new_date(&bin_expr.left) {
|
||||
ctx.diagnostic(PreferDateNowDiagnostic::PreferDateNow(bin_expr.left.span()));
|
||||
}
|
||||
if is_new_date(&bin_expr.right) {
|
||||
ctx.diagnostic(PreferDateNowDiagnostic::PreferDateNow(bin_expr.right.span()));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_new_date(expr: &Expression) -> bool {
|
||||
let Expression::NewExpression(new_expr) = expr else { return false };
|
||||
|
||||
if let Expression::Identifier(ident) = &new_expr.callee {
|
||||
return ident.name == "Date" && new_expr.arguments.is_empty();
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
use crate::tester::Tester;
|
||||
|
||||
let pass = vec![
|
||||
r#"const ts = Date.now()"#,
|
||||
r#"+Date()"#,
|
||||
r#"+ Date"#,
|
||||
r#"+ new window.Date()"#,
|
||||
r#"+ new Moments()"#,
|
||||
r#"+ new Date(0)"#,
|
||||
r#"+ new Date(...[])"#,
|
||||
r#"new Date.getTime()"#,
|
||||
r#"valueOf()"#,
|
||||
r#"new Date()[getTime]()"#,
|
||||
r#"new Date()["valueOf"]()"#,
|
||||
r#"new Date().notListed(0)"#,
|
||||
r#"new Date().getTime(0)"#,
|
||||
r#"new Date().valueOf(...[])"#,
|
||||
r#"new Number(new Date())"#,
|
||||
r#"window.BigInt(new Date())"#,
|
||||
r#"toNumber(new Date())"#,
|
||||
r#"BigInt()"#,
|
||||
r#"Number(new Date(), extraArgument)"#,
|
||||
r#"BigInt([...new Date()])"#,
|
||||
r#"throw new Date()"#,
|
||||
r#"typeof new Date()"#,
|
||||
r#"const foo = () => {return new Date()}"#,
|
||||
r#"foo += new Date()"#,
|
||||
r#"function * foo() {yield new Date()}"#,
|
||||
r#"new Date() + new Date()"#,
|
||||
r#"foo = new Date() | 0"#,
|
||||
r#"foo &= new Date()"#,
|
||||
r#"foo = new Date() >> 0"#,
|
||||
];
|
||||
|
||||
let fail = vec![
|
||||
r#"const ts = new Date().getTime();"#,
|
||||
r#"const ts = (new Date).getTime();"#,
|
||||
r#"const ts = (new Date()).getTime();"#,
|
||||
r#"const ts = new Date().valueOf();"#,
|
||||
r#"const ts = (new Date).valueOf();"#,
|
||||
r#"const ts = (new Date()).valueOf();"#,
|
||||
r#"const ts = /* 1 */ Number(/* 2 */ new /* 3 */ Date( /* 4 */ ) /* 5 */) /* 6 */"#,
|
||||
r#"const tsBigInt = /* 1 */ BigInt(/* 2 */ new /* 3 */ Date( /* 4 */ ) /* 5 */) /* 6 */"#,
|
||||
r#"const ts = + /* 1 */ new Date;"#,
|
||||
r#"const ts = - /* 1 */ new Date();"#,
|
||||
r#"const ts = new Date() - 0"#,
|
||||
r#"const foo = bar - new Date"#,
|
||||
r#"const foo = new Date() * bar"#,
|
||||
r#"const ts = new Date() / 1"#,
|
||||
r#"const ts = new Date() % Infinity"#,
|
||||
r#"const ts = new Date() ** 1"#,
|
||||
r#"const zero = (new Date(/* 1 */) /* 2 */) /* 3 */ - /* 4 */new Date"#,
|
||||
r#"foo -= new Date()"#,
|
||||
r#"foo *= new Date()"#,
|
||||
r#"foo /= new Date"#,
|
||||
r#"foo %= new Date()"#,
|
||||
r#"foo **= new Date()"#,
|
||||
r#"function foo(){return+new Date}"#,
|
||||
r#"function foo(){return-new Date}"#,
|
||||
];
|
||||
|
||||
Tester::new_without_config(PreferDateNow::NAME, pass, fail).test_and_snapshot();
|
||||
}
|
||||
173
crates/oxc_linter/src/snapshots/prefer_date_now.snap
Normal file
173
crates/oxc_linter/src/snapshots/prefer_date_now.snap
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
---
|
||||
source: crates/oxc_linter/src/tester.rs
|
||||
expression: prefer_date_now
|
||||
---
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date().getTime()`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ const ts = new Date().getTime();
|
||||
· ────────────────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date().getTime()`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ const ts = (new Date).getTime();
|
||||
· ────────────────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date().getTime()`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ const ts = (new Date()).getTime();
|
||||
· ──────────────────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date().valueOf()`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ const ts = new Date().valueOf();
|
||||
· ────────────────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date().valueOf()`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ const ts = (new Date).valueOf();
|
||||
· ────────────────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date().valueOf()`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ const ts = (new Date()).valueOf();
|
||||
· ──────────────────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `Number(new Date())`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ const ts = /* 1 */ Number(/* 2 */ new /* 3 */ Date( /* 4 */ ) /* 5 */) /* 6 */
|
||||
· ───────────────────────────────────────────────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `Number(new Date())`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ const tsBigInt = /* 1 */ BigInt(/* 2 */ new /* 3 */ Date( /* 4 */ ) /* 5 */) /* 6 */
|
||||
· ───────────────────────────────────────────────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date()`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ const ts = + /* 1 */ new Date;
|
||||
· ────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date()`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ const ts = - /* 1 */ new Date();
|
||||
· ──────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date()`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ const ts = new Date() - 0
|
||||
· ──────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date()`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ const foo = bar - new Date
|
||||
· ────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date()`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ const foo = new Date() * bar
|
||||
· ──────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date()`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ const ts = new Date() / 1
|
||||
· ──────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date()`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ const ts = new Date() % Infinity
|
||||
· ──────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date()`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ const ts = new Date() ** 1
|
||||
· ──────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date()`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ const zero = (new Date(/* 1 */) /* 2 */) /* 3 */ - /* 4 */new Date
|
||||
· ────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date()`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ foo -= new Date()
|
||||
· ──────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date()`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ foo *= new Date()
|
||||
· ──────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date()`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ foo /= new Date
|
||||
· ────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date()`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ foo %= new Date()
|
||||
· ──────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date()`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ foo **= new Date()
|
||||
· ──────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date()`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ function foo(){return+new Date}
|
||||
· ────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
⚠ eslint-plugin-unicorn(prefer-date-now): Prefer `Date.now()` over `new Date()`
|
||||
╭─[prefer_date_now.tsx:1:1]
|
||||
1 │ function foo(){return-new Date}
|
||||
· ────────
|
||||
╰────
|
||||
help: Change to `Date.now()`.
|
||||
|
||||
|
||||
Loading…
Reference in a new issue