'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, }, };