diff --git a/docs/rules/no-magic-numbers.md b/docs/rules/no-magic-numbers.md index ee6d3de7ee7..dfe21d83c1b 100644 --- a/docs/rules/no-magic-numbers.md +++ b/docs/rules/no-magic-numbers.md @@ -128,6 +128,27 @@ a = data[4294967295]; // above the max array index a = data[1e500]; // same as data["Infinity"] ``` +### ignoreDefaultValues + +A boolean to specify if numbers used in default value assignments are considered okay. `false` by default. + +Examples of **correct** code for the `{ "ignoreDefaultValues": true }` option: + +```js +/*eslint no-magic-numbers: ["error", { "ignoreDefaultValues": true }]*/ + +const { tax = 0.25 } = accountancy; + +function mapParallel(concurrency = 3) { /***/ } +``` + +```js +/*eslint no-magic-numbers: ["error", { "ignoreDefaultValues": true }]*/ + +let head; +[head = 100] = [] +``` + ### enforceConst A boolean to specify if we should check for the const keyword in variable declaration of numbers. `false` by default. diff --git a/lib/rules/no-magic-numbers.js b/lib/rules/no-magic-numbers.js index 6f6a156eb75..510b3f9b261 100644 --- a/lib/rules/no-magic-numbers.js +++ b/lib/rules/no-magic-numbers.js @@ -61,6 +61,10 @@ module.exports = { ignoreArrayIndexes: { type: "boolean", default: false + }, + ignoreDefaultValues: { + type: "boolean", + default: false } }, additionalProperties: false @@ -77,7 +81,8 @@ module.exports = { detectObjects = !!config.detectObjects, enforceConst = !!config.enforceConst, ignore = (config.ignore || []).map(normalizeIgnoreValue), - ignoreArrayIndexes = !!config.ignoreArrayIndexes; + ignoreArrayIndexes = !!config.ignoreArrayIndexes, + ignoreDefaultValues = !!config.ignoreDefaultValues; const okTypes = detectObjects ? [] : ["ObjectExpression", "Property", "AssignmentExpression"]; @@ -90,6 +95,17 @@ module.exports = { return ignore.indexOf(value) !== -1; } + /** + * Returns whether the number is a default value assignment. + * @param {ASTNode} fullNumberNode `Literal` or `UnaryExpression` full number node + * @returns {boolean} true if the number is a default value + */ + function isDefaultValue(fullNumberNode) { + const parent = fullNumberNode.parent; + + return parent.type === "AssignmentPattern" && parent.right === fullNumberNode; + } + /** * Returns whether the given node is used as a radix within parseInt() or Number.parseInt() * @param {ASTNode} fullNumberNode `Literal` or `UnaryExpression` full number node @@ -172,9 +188,12 @@ module.exports = { raw = node.raw; } + const parent = fullNumberNode.parent; + // Always allow radix arguments and JSX props if ( isIgnoredValue(value) || + (ignoreDefaultValues && isDefaultValue(fullNumberNode)) || isParseIntRadix(fullNumberNode) || isJSXNumber(fullNumberNode) || (ignoreArrayIndexes && isArrayIndex(fullNumberNode, value)) @@ -182,8 +201,6 @@ module.exports = { return; } - const parent = fullNumberNode.parent; - if (parent.type === "VariableDeclarator") { if (enforceConst && parent.parent.kind !== "const") { context.report({ diff --git a/tests/lib/rules/no-magic-numbers.js b/tests/lib/rules/no-magic-numbers.js index a47b2a10555..bbdf8ca984f 100644 --- a/tests/lib/rules/no-magic-numbers.js +++ b/tests/lib/rules/no-magic-numbers.js @@ -214,6 +214,31 @@ ruleTester.run("no-magic-numbers", rule, { options: [{ ignore: ["-100n"] }], parserOptions: { ecmaVersion: 2020 } }, + { + code: "const { param = 123 } = sourceObject;", + options: [{ ignoreDefaultValues: true }], + env: { es6: true } + }, + { + code: "const func = (param = 123) => {}", + options: [{ ignoreDefaultValues: true }], + env: { es6: true } + }, + { + code: "const func = ({ param = 123 }) => {}", + options: [{ ignoreDefaultValues: true }], + env: { es6: true } + }, + { + code: "const [one = 1, two = 2] = []", + options: [{ ignoreDefaultValues: true }], + env: { es6: true } + }, + { + code: "var one, two; [one = 1, two = 2] = []", + options: [{ ignoreDefaultValues: true }], + env: { es6: true } + }, // Optional chaining { @@ -738,6 +763,55 @@ ruleTester.run("no-magic-numbers", rule, { errors: [ { messageId: "noMagic", data: { raw: "100" }, line: 1 } ] + }, + { + code: "const func = (param = 123) => {}", + options: [{ ignoreDefaultValues: false }], + env: { es6: true }, + errors: [ + { messageId: "noMagic", data: { raw: "123" }, line: 1 } + ] + }, + { + code: "const { param = 123 } = sourceObject;", + options: [{}], + env: { es6: true }, + errors: [ + { messageId: "noMagic", data: { raw: "123" }, line: 1 } + ] + }, + { + code: "const { param = 123 } = sourceObject;", + env: { es6: true }, + errors: [ + { messageId: "noMagic", data: { raw: "123" }, line: 1 } + ] + }, + { + code: "const { param = 123 } = sourceObject;", + options: [{ ignoreDefaultValues: false }], + env: { es6: true }, + errors: [ + { messageId: "noMagic", data: { raw: "123" }, line: 1 } + ] + }, + { + code: "const [one = 1, two = 2] = []", + options: [{ ignoreDefaultValues: false }], + env: { es6: true }, + errors: [ + { messageId: "noMagic", data: { raw: "1" }, line: 1 }, + { messageId: "noMagic", data: { raw: "2" }, line: 1 } + ] + }, + { + code: "var one, two; [one = 1, two = 2] = []", + options: [{ ignoreDefaultValues: false }], + env: { es6: true }, + errors: [ + { messageId: "noMagic", data: { raw: "1" }, line: 1 }, + { messageId: "noMagic", data: { raw: "2" }, line: 1 } + ] } ] });