UNPKG

5.43 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to flag statements that use magic numbers (adapted from https://github.com/danielstjules/buddy.js)
3 * @author Vincent Lemeunier
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Rule Definition
10//------------------------------------------------------------------------------
11
12module.exports = {
13 meta: {
14 docs: {
15 description: "disallow magic numbers",
16 category: "Best Practices",
17 recommended: false,
18 url: "https://eslint.org/docs/rules/no-magic-numbers"
19 },
20
21 schema: [{
22 type: "object",
23 properties: {
24 detectObjects: {
25 type: "boolean"
26 },
27 enforceConst: {
28 type: "boolean"
29 },
30 ignore: {
31 type: "array",
32 items: {
33 type: "number"
34 },
35 uniqueItems: true
36 },
37 ignoreArrayIndexes: {
38 type: "boolean"
39 }
40 },
41 additionalProperties: false
42 }]
43 },
44
45 create(context) {
46 const config = context.options[0] || {},
47 detectObjects = !!config.detectObjects,
48 enforceConst = !!config.enforceConst,
49 ignore = config.ignore || [],
50 ignoreArrayIndexes = !!config.ignoreArrayIndexes;
51
52 /**
53 * Returns whether the node is number literal
54 * @param {Node} node - the node literal being evaluated
55 * @returns {boolean} true if the node is a number literal
56 */
57 function isNumber(node) {
58 return typeof node.value === "number";
59 }
60
61 /**
62 * Returns whether the number should be ignored
63 * @param {number} num - the number
64 * @returns {boolean} true if the number should be ignored
65 */
66 function shouldIgnoreNumber(num) {
67 return ignore.indexOf(num) !== -1;
68 }
69
70 /**
71 * Returns whether the number should be ignored when used as a radix within parseInt() or Number.parseInt()
72 * @param {ASTNode} parent - the non-"UnaryExpression" parent
73 * @param {ASTNode} node - the node literal being evaluated
74 * @returns {boolean} true if the number should be ignored
75 */
76 function shouldIgnoreParseInt(parent, node) {
77 return parent.type === "CallExpression" && node === parent.arguments[1] &&
78 (parent.callee.name === "parseInt" ||
79 parent.callee.type === "MemberExpression" &&
80 parent.callee.object.name === "Number" &&
81 parent.callee.property.name === "parseInt");
82 }
83
84 /**
85 * Returns whether the number should be ignored when used to define a JSX prop
86 * @param {ASTNode} parent - the non-"UnaryExpression" parent
87 * @returns {boolean} true if the number should be ignored
88 */
89 function shouldIgnoreJSXNumbers(parent) {
90 return parent.type.indexOf("JSX") === 0;
91 }
92
93 /**
94 * Returns whether the number should be ignored when used as an array index with enabled 'ignoreArrayIndexes' option.
95 * @param {ASTNode} parent - the non-"UnaryExpression" parent.
96 * @returns {boolean} true if the number should be ignored
97 */
98 function shouldIgnoreArrayIndexes(parent) {
99 return parent.type === "MemberExpression" && ignoreArrayIndexes;
100 }
101
102 return {
103 Literal(node) {
104 let parent = node.parent,
105 value = node.value,
106 raw = node.raw;
107 const okTypes = detectObjects ? [] : ["ObjectExpression", "Property", "AssignmentExpression"];
108
109 if (!isNumber(node)) {
110 return;
111 }
112
113 // For negative magic numbers: update the value and parent node
114 if (parent.type === "UnaryExpression" && parent.operator === "-") {
115 node = parent;
116 parent = node.parent;
117 value = -value;
118 raw = `-${raw}`;
119 }
120
121 if (shouldIgnoreNumber(value) ||
122 shouldIgnoreParseInt(parent, node) ||
123 shouldIgnoreArrayIndexes(parent) ||
124 shouldIgnoreJSXNumbers(parent)) {
125 return;
126 }
127
128 if (parent.type === "VariableDeclarator") {
129 if (enforceConst && parent.parent.kind !== "const") {
130 context.report({
131 node,
132 message: "Number constants declarations must use 'const'."
133 });
134 }
135 } else if (
136 okTypes.indexOf(parent.type) === -1 ||
137 (parent.type === "AssignmentExpression" && parent.left.type === "Identifier")
138 ) {
139 context.report({
140 node,
141 message: "No magic number: {{raw}}.",
142 data: {
143 raw
144 }
145 });
146 }
147 }
148 };
149 }
150};