116 lines
3.0 KiB
JavaScript
116 lines
3.0 KiB
JavaScript
|
'use strict';
|
||
|
const {
|
||
|
matches,
|
||
|
methodCallSelector,
|
||
|
newExpressionSelector,
|
||
|
callExpressionSelector,
|
||
|
} = require('./selectors/index.js');
|
||
|
const {fixSpaceAroundKeyword} = require('./fix/index.js');
|
||
|
|
||
|
const MESSAGE_ID_DEFAULT = 'prefer-date';
|
||
|
const MESSAGE_ID_METHOD = 'prefer-date-now-over-methods';
|
||
|
const MESSAGE_ID_NUMBER = 'prefer-date-now-over-number-data-object';
|
||
|
const messages = {
|
||
|
[MESSAGE_ID_DEFAULT]: 'Prefer `Date.now()` over `new Date()`.',
|
||
|
[MESSAGE_ID_METHOD]: 'Prefer `Date.now()` over `Date#{{method}}()`.',
|
||
|
[MESSAGE_ID_NUMBER]: 'Prefer `Date.now()` over `Number(new Date())`.',
|
||
|
};
|
||
|
|
||
|
const createNewDateSelector = path => newExpressionSelector({path, name: 'Date', argumentsLength: 0});
|
||
|
const operatorsSelector = (...operators) => matches(operators.map(operator => `[operator="${operator}"]`));
|
||
|
// `new Date()`
|
||
|
const newDateSelector = createNewDateSelector();
|
||
|
// `new Date().{getTime,valueOf}()`
|
||
|
const methodsSelector = [
|
||
|
methodCallSelector({
|
||
|
methods: ['getTime', 'valueOf'],
|
||
|
argumentsLength: 0,
|
||
|
}),
|
||
|
createNewDateSelector('callee.object'),
|
||
|
].join('');
|
||
|
// `{Number,BigInt}(new Date())`
|
||
|
const builtinObjectSelector = [
|
||
|
callExpressionSelector({names: ['Number', 'BigInt'], argumentsLength: 1}),
|
||
|
createNewDateSelector('arguments.0'),
|
||
|
].join('');
|
||
|
// https://github.com/estree/estree/blob/master/es5.md#unaryoperator
|
||
|
const unaryExpressionsSelector = [
|
||
|
'UnaryExpression',
|
||
|
operatorsSelector('+', '-'),
|
||
|
createNewDateSelector('argument'),
|
||
|
].join('');
|
||
|
const assignmentExpressionSelector = [
|
||
|
'AssignmentExpression',
|
||
|
operatorsSelector('-=', '*=', '/=', '%=', '**='),
|
||
|
'>',
|
||
|
`${newDateSelector}.right`,
|
||
|
].join('');
|
||
|
const binaryExpressionSelector = [
|
||
|
'BinaryExpression',
|
||
|
operatorsSelector('-', '*', '/', '%', '**'),
|
||
|
// Both `left` and `right` properties
|
||
|
'>',
|
||
|
newDateSelector,
|
||
|
].join('');
|
||
|
|
||
|
const getProblem = (node, problem, sourceCode) => ({
|
||
|
node,
|
||
|
messageId: MESSAGE_ID_DEFAULT,
|
||
|
* fix(fixer) {
|
||
|
yield fixer.replaceText(node, 'Date.now()');
|
||
|
|
||
|
if (node.type === 'UnaryExpression') {
|
||
|
yield * fixSpaceAroundKeyword(fixer, node, sourceCode);
|
||
|
}
|
||
|
},
|
||
|
...problem,
|
||
|
});
|
||
|
|
||
|
/** @param {import('eslint').Rule.RuleContext} context */
|
||
|
const create = context => ({
|
||
|
[methodsSelector](node) {
|
||
|
const method = node.callee.property;
|
||
|
return getProblem(node, {
|
||
|
node: method,
|
||
|
messageId: MESSAGE_ID_METHOD,
|
||
|
data: {method: method.name},
|
||
|
});
|
||
|
},
|
||
|
[builtinObjectSelector](node) {
|
||
|
const {name} = node.callee;
|
||
|
if (name === 'Number') {
|
||
|
return getProblem(node, {
|
||
|
messageId: MESSAGE_ID_NUMBER,
|
||
|
});
|
||
|
}
|
||
|
|
||
|
return getProblem(node.arguments[0]);
|
||
|
},
|
||
|
[unaryExpressionsSelector](node) {
|
||
|
return getProblem(
|
||
|
node.operator === '-' ? node.argument : node,
|
||
|
{},
|
||
|
context.getSourceCode(),
|
||
|
);
|
||
|
},
|
||
|
[assignmentExpressionSelector](node) {
|
||
|
return getProblem(node);
|
||
|
},
|
||
|
[binaryExpressionSelector](node) {
|
||
|
return getProblem(node);
|
||
|
},
|
||
|
});
|
||
|
|
||
|
/** @type {import('eslint').Rule.RuleModule} */
|
||
|
module.exports = {
|
||
|
create,
|
||
|
meta: {
|
||
|
type: 'suggestion',
|
||
|
docs: {
|
||
|
description: 'Prefer `Date.now()` to get the number of milliseconds since the Unix Epoch.',
|
||
|
},
|
||
|
fixable: 'code',
|
||
|
messages,
|
||
|
},
|
||
|
};
|