123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- /**
- * @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"
- },
- enforceConst: {
- type: "boolean"
- },
- ignore: {
- type: "array",
- items: {
- type: "number"
- },
- uniqueItems: true
- },
- ignoreArrayIndexes: {
- type: "boolean"
- }
- },
- 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
- }
- });
- }
- }
- };
- }
- };
|