2 * @fileoverview Rule to flag statements that use magic numbers (adapted from https://github.com/danielstjules/buddy.js)
3 * @author Vincent Lemeunier
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
17 description: "disallow magic numbers",
18 category: "Best Practices",
20 url: "https://eslint.org/docs/rules/no-magic-numbers"
46 additionalProperties: false
50 useConst: "Number constants declarations must use 'const'.",
51 noMagic: "No magic number: {{raw}}."
56 const config = context.options[0] || {},
57 detectObjects = !!config.detectObjects,
58 enforceConst = !!config.enforceConst,
59 ignore = config.ignore || [],
60 ignoreArrayIndexes = !!config.ignoreArrayIndexes;
63 * Returns whether the node is number literal
64 * @param {Node} node the node literal being evaluated
65 * @returns {boolean} true if the node is a number literal
67 function isNumber(node) {
68 return typeof node.value === "number";
72 * Returns whether the number should be ignored
73 * @param {number} num the number
74 * @returns {boolean} true if the number should be ignored
76 function shouldIgnoreNumber(num) {
77 return ignore.indexOf(num) !== -1;
81 * Returns whether the number should be ignored when used as a radix within parseInt() or Number.parseInt()
82 * @param {ASTNode} parent the non-"UnaryExpression" parent
83 * @param {ASTNode} node the node literal being evaluated
84 * @returns {boolean} true if the number should be ignored
86 function shouldIgnoreParseInt(parent, node) {
87 return parent.type === "CallExpression" && node === parent.arguments[1] &&
88 (parent.callee.name === "parseInt" ||
89 parent.callee.type === "MemberExpression" &&
90 parent.callee.object.name === "Number" &&
91 parent.callee.property.name === "parseInt");
95 * Returns whether the number should be ignored when used to define a JSX prop
96 * @param {ASTNode} parent the non-"UnaryExpression" parent
97 * @returns {boolean} true if the number should be ignored
99 function shouldIgnoreJSXNumbers(parent) {
100 return parent.type.indexOf("JSX") === 0;
104 * Returns whether the number should be ignored when used as an array index with enabled 'ignoreArrayIndexes' option.
105 * @param {ASTNode} parent the non-"UnaryExpression" parent.
106 * @returns {boolean} true if the number should be ignored
108 function shouldIgnoreArrayIndexes(parent) {
109 return parent.type === "MemberExpression" && ignoreArrayIndexes;
114 const okTypes = detectObjects ? [] : ["ObjectExpression", "Property", "AssignmentExpression"];
116 if (!isNumber(node)) {
125 // For negative magic numbers: update the value and parent node
126 if (node.parent.type === "UnaryExpression" && node.parent.operator === "-") {
127 fullNumberNode = node.parent;
128 parent = fullNumberNode.parent;
130 raw = `-${node.raw}`;
132 fullNumberNode = node;
133 parent = node.parent;
138 if (shouldIgnoreNumber(value) ||
139 shouldIgnoreParseInt(parent, fullNumberNode) ||
140 shouldIgnoreArrayIndexes(parent) ||
141 shouldIgnoreJSXNumbers(parent)) {
145 if (parent.type === "VariableDeclarator") {
146 if (enforceConst && parent.parent.kind !== "const") {
148 node: fullNumberNode,
149 messageId: "useConst"
153 okTypes.indexOf(parent.type) === -1 ||
154 (parent.type === "AssignmentExpression" && parent.left.type === "Identifier")
157 node: fullNumberNode,
158 messageId: "noMagic",