1 | 'use strict';
|
2 | const getDocumentationUrl = require('./utils/get-documentation-url');
|
3 | const isShadowed = require('./utils/is-shadowed');
|
4 | const renameIdentifier = require('./utils/rename-identifier');
|
5 |
|
6 | const METHOD_ERROR_MESSAGE_ID = 'method-error';
|
7 | const METHOD_SUGGESTION_MESSAGE_ID = 'method-suggestion';
|
8 | const PROPERTY_ERROR_MESSAGE_ID = 'property-error';
|
9 |
|
10 | const methods = {
|
11 |
|
12 | parseInt: true,
|
13 | parseFloat: true,
|
14 |
|
15 | isNaN: false,
|
16 | isFinite: false
|
17 | };
|
18 |
|
19 | const methodsSelector = [
|
20 | 'CallExpression',
|
21 | '>',
|
22 | 'Identifier.callee',
|
23 | `:matches(${Object.keys(methods).map(name => `[name="${name}"]`).join(', ')})`
|
24 | ].join('');
|
25 |
|
26 | const propertiesSelector = [
|
27 | 'Identifier',
|
28 | '[name="NaN"]',
|
29 | `:not(${
|
30 | [
|
31 | 'MemberExpression[computed=false] > Identifier.property',
|
32 | 'FunctionDeclaration > Identifier.id',
|
33 | 'ClassDeclaration > Identifier.id',
|
34 | 'MethodDefinition > Identifier.key',
|
35 | 'VariableDeclarator > Identifier.id',
|
36 | 'Property[shorthand=false] > Identifier.key',
|
37 | 'TSDeclareFunction > Identifier.id',
|
38 | 'TSEnumMember > Identifier.id',
|
39 | 'TSPropertySignature > Identifier.key'
|
40 | ].join(', ')
|
41 | })`
|
42 | ].join('');
|
43 |
|
44 | const create = context => {
|
45 | const sourceCode = context.getSourceCode();
|
46 |
|
47 | return {
|
48 | [methodsSelector]: node => {
|
49 | if (isShadowed(context.getScope(), node)) {
|
50 | return;
|
51 | }
|
52 |
|
53 | const {name} = node;
|
54 | const isSafe = methods[name];
|
55 |
|
56 | const problem = {
|
57 | node,
|
58 | messageId: METHOD_ERROR_MESSAGE_ID,
|
59 | data: {
|
60 | name
|
61 | }
|
62 | };
|
63 |
|
64 | const fix = fixer => renameIdentifier(node, `Number.${name}`, fixer, sourceCode);
|
65 |
|
66 | if (isSafe) {
|
67 | problem.fix = fix;
|
68 | } else {
|
69 | problem.suggest = [
|
70 | {
|
71 | messageId: METHOD_SUGGESTION_MESSAGE_ID,
|
72 | data: {
|
73 | name
|
74 | },
|
75 | fix
|
76 | }
|
77 | ];
|
78 | }
|
79 |
|
80 | context.report(problem);
|
81 | },
|
82 | [propertiesSelector]: node => {
|
83 | if (isShadowed(context.getScope(), node)) {
|
84 | return;
|
85 | }
|
86 |
|
87 | const {name} = node;
|
88 | context.report({
|
89 | node,
|
90 | messageId: PROPERTY_ERROR_MESSAGE_ID,
|
91 | data: {
|
92 | name
|
93 | },
|
94 | fix: fixer => renameIdentifier(node, `Number.${name}`, fixer, sourceCode)
|
95 | });
|
96 | }
|
97 | };
|
98 | };
|
99 |
|
100 | module.exports = {
|
101 | create,
|
102 | meta: {
|
103 | type: 'suggestion',
|
104 | docs: {
|
105 | url: getDocumentationUrl(__filename)
|
106 | },
|
107 | fixable: 'code',
|
108 | messages: {
|
109 | [METHOD_ERROR_MESSAGE_ID]: 'Prefer `Number.{{name}}()` over `{{name}}()`.',
|
110 | [METHOD_SUGGESTION_MESSAGE_ID]: 'Replace `{{name}}()` with `Number.{{name}}()`.',
|
111 | [PROPERTY_ERROR_MESSAGE_ID]: 'Prefer `Number.{{name}}` over `{{name}}`.'
|
112 | }
|
113 | }
|
114 | };
|