Permalink
Cannot retrieve contributors at this time
| /** | |
| * @fileoverview Rule to flag statements that use magic numbers (adapted from https://github.com/danielstjules/buddy.js) | |
| * @author Vincent Lemeunier | |
| */ | |
| "use strict"; | |
| //------------------------------------------------------------------------------ | |
| // Rule Definition | |
| //------------------------------------------------------------------------------ | |
| module.exports = { | |
| meta: { | |
| type: "suggestion", | |
| docs: { | |
| description: "disallow magic numbers", | |
| category: "Best Practices", | |
| recommended: false, | |
| url: "https://eslint.org/docs/rules/no-magic-numbers" | |
| }, | |
| schema: [{ | |
| type: "object", | |
| properties: { | |
| detectObjects: { | |
| type: "boolean", | |
| default: false | |
| }, | |
| enforceConst: { | |
| type: "boolean", | |
| default: false | |
| }, | |
| ignore: { | |
| type: "array", | |
| items: { | |
| type: "number" | |
| }, | |
| uniqueItems: true | |
| }, | |
| ignoreArrayIndexes: { | |
| type: "boolean", | |
| default: false | |
| } | |
| }, | |
| additionalProperties: false | |
| }], | |
| messages: { | |
| useConst: "Number constants declarations must use 'const'.", | |
| noMagic: "No magic number: {{raw}}." | |
| } | |
| }, | |
| create(context) { | |
| const config = context.options[0] || {}, | |
| detectObjects = !!config.detectObjects, | |
| enforceConst = !!config.enforceConst, | |
| ignore = config.ignore || [], | |
| ignoreArrayIndexes = !!config.ignoreArrayIndexes; | |
| /** | |
| * Returns whether the node is number literal | |
| * @param {Node} node the node literal being evaluated | |
| * @returns {boolean} true if the node is a number literal | |
| */ | |
| function isNumber(node) { | |
| return typeof node.value === "number"; | |
| } | |
| /** | |
| * Returns whether the number should be ignored | |
| * @param {number} num the number | |
| * @returns {boolean} true if the number should be ignored | |
| */ | |
| function shouldIgnoreNumber(num) { | |
| return ignore.indexOf(num) !== -1; | |
| } | |
| /** | |
| * Returns whether the number should be ignored when used as a radix within parseInt() or Number.parseInt() | |
| * @param {ASTNode} parent the non-"UnaryExpression" parent | |
| * @param {ASTNode} node the node literal being evaluated | |
| * @returns {boolean} true if the number should be ignored | |
| */ | |
| function shouldIgnoreParseInt(parent, node) { | |
| return parent.type === "CallExpression" && node === parent.arguments[1] && | |
| (parent.callee.name === "parseInt" || | |
| parent.callee.type === "MemberExpression" && | |
| parent.callee.object.name === "Number" && | |
| parent.callee.property.name === "parseInt"); | |
| } | |
| /** | |
| * Returns whether the number should be ignored when used to define a JSX prop | |
| * @param {ASTNode} parent the non-"UnaryExpression" parent | |
| * @returns {boolean} true if the number should be ignored | |
| */ | |
| function shouldIgnoreJSXNumbers(parent) { | |
| return parent.type.indexOf("JSX") === 0; | |
| } | |
| /** | |
| * Returns whether the number should be ignored when used as an array index with enabled 'ignoreArrayIndexes' option. | |
| * @param {ASTNode} parent the non-"UnaryExpression" parent. | |
| * @returns {boolean} true if the number should be ignored | |
| */ | |
| function shouldIgnoreArrayIndexes(parent) { | |
| return parent.type === "MemberExpression" && ignoreArrayIndexes; | |
| } | |
| return { | |
| Literal(node) { | |
| const okTypes = detectObjects ? [] : ["ObjectExpression", "Property", "AssignmentExpression"]; | |
| if (!isNumber(node)) { | |
| return; | |
| } | |
| let fullNumberNode; | |
| let parent; | |
| let value; | |
| let raw; | |
| // For negative magic numbers: update the value and parent node | |
| if (node.parent.type === "UnaryExpression" && node.parent.operator === "-") { | |
| fullNumberNode = node.parent; | |
| parent = fullNumberNode.parent; | |
| value = -node.value; | |
| raw = `-${node.raw}`; | |
| } else { | |
| fullNumberNode = node; | |
| parent = node.parent; | |
| value = node.value; | |
| raw = node.raw; | |
| } | |
| if (shouldIgnoreNumber(value) || | |
| shouldIgnoreParseInt(parent, fullNumberNode) || | |
| shouldIgnoreArrayIndexes(parent) || | |
| shouldIgnoreJSXNumbers(parent)) { | |
| return; | |
| } | |
| if (parent.type === "VariableDeclarator") { | |
| if (enforceConst && parent.parent.kind !== "const") { | |
| context.report({ | |
| node: fullNumberNode, | |
| messageId: "useConst" | |
| }); | |
| } | |
| } else if ( | |
| okTypes.indexOf(parent.type) === -1 || | |
| (parent.type === "AssignmentExpression" && parent.left.type === "Identifier") | |
| ) { | |
| context.report({ | |
| node: fullNumberNode, | |
| messageId: "noMagic", | |
| data: { | |
| raw | |
| } | |
| }); | |
| } | |
| } | |
| }; | |
| } | |
| }; |