123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- 'use strict';
- const isShadowed = require('./utils/is-shadowed.js');
- const {
- referenceIdentifierSelector,
- callExpressionSelector,
- } = require('./selectors/index.js');
- const {replaceReferenceIdentifier} = require('./fix/index.js');
- const {fixSpaceAroundKeyword} = require('./fix/index.js');
- const METHOD_ERROR_MESSAGE_ID = 'method-error';
- const METHOD_SUGGESTION_MESSAGE_ID = 'method-suggestion';
- const PROPERTY_ERROR_MESSAGE_ID = 'property-error';
- const messages = {
- [METHOD_ERROR_MESSAGE_ID]: 'Prefer `Number.{{name}}()` over `{{name}}()`.',
- [METHOD_SUGGESTION_MESSAGE_ID]: 'Replace `{{name}}()` with `Number.{{name}}()`.',
- [PROPERTY_ERROR_MESSAGE_ID]: 'Prefer `Number.{{property}}` over `{{identifier}}`.',
- };
- const methods = {
- // Safe
- parseInt: true,
- parseFloat: true,
- // Unsafe
- isNaN: false,
- isFinite: false,
- };
- const methodsSelector = [
- callExpressionSelector(Object.keys(methods)),
- ' > ',
- '.callee',
- ].join('');
- const propertiesSelector = referenceIdentifierSelector(['NaN', 'Infinity']);
- const isNegative = node => {
- const {parent} = node;
- return parent && parent.type === 'UnaryExpression' && parent.operator === '-' && parent.argument === node;
- };
- /** @param {import('eslint').Rule.RuleContext} context */
- const create = context => {
- const sourceCode = context.getSourceCode();
- const options = {
- checkInfinity: true,
- ...context.options[0],
- };
- // Cache `NaN` and `Infinity` in `foo = {NaN, Infinity}`
- const reported = new WeakSet();
- return {
- [methodsSelector]: node => {
- if (isShadowed(context.getScope(), node)) {
- return;
- }
- const {name} = node;
- const isSafe = methods[name];
- const problem = {
- node,
- messageId: METHOD_ERROR_MESSAGE_ID,
- data: {
- name,
- },
- };
- const fix = fixer => replaceReferenceIdentifier(node, `Number.${name}`, fixer, sourceCode);
- if (isSafe) {
- problem.fix = fix;
- } else {
- problem.suggest = [
- {
- messageId: METHOD_SUGGESTION_MESSAGE_ID,
- data: {
- name,
- },
- fix,
- },
- ];
- }
- return problem;
- },
- [propertiesSelector]: node => {
- if (reported.has(node) || isShadowed(context.getScope(), node)) {
- return;
- }
- const {name, parent} = node;
- if (name === 'Infinity' && !options.checkInfinity) {
- return;
- }
- let property = name;
- if (name === 'Infinity') {
- property = isNegative(node) ? 'NEGATIVE_INFINITY' : 'POSITIVE_INFINITY';
- }
- const problem = {
- node,
- messageId: PROPERTY_ERROR_MESSAGE_ID,
- data: {
- identifier: name,
- property,
- },
- };
- if (property === 'NEGATIVE_INFINITY') {
- problem.node = parent;
- problem.data.identifier = '-Infinity';
- problem.fix = function * (fixer) {
- yield fixer.replaceText(parent, 'Number.NEGATIVE_INFINITY');
- yield * fixSpaceAroundKeyword(fixer, parent, sourceCode);
- };
- } else {
- problem.fix = fixer => replaceReferenceIdentifier(node, `Number.${property}`, fixer, sourceCode);
- }
- reported.add(node);
- return problem;
- },
- };
- };
- const schema = [
- {
- type: 'object',
- additionalProperties: false,
- properties: {
- checkInfinity: {
- type: 'boolean',
- default: true,
- },
- },
- },
- ];
- /** @type {import('eslint').Rule.RuleModule} */
- module.exports = {
- create,
- meta: {
- type: 'suggestion',
- docs: {
- description: 'Prefer `Number` static properties over global ones.',
- },
- fixable: 'code',
- hasSuggestions: true,
- schema,
- messages,
- },
- };
|