From 63bce44e7b6326e1e94fc7f6283df8de7bbac273 Mon Sep 17 00:00:00 2001 From: Milos Djermanovic Date: Wed, 16 Nov 2022 03:21:10 +0100 Subject: [PATCH] feat: add `ignoreClassFieldInitialValues` option to no-magic-numbers (#16539) Fixes #16228 --- docs/src/rules/no-magic-numbers.md | 39 ++++++++++ lib/rules/no-magic-numbers.js | 19 ++++- tests/lib/rules/no-magic-numbers.js | 109 ++++++++++++++++++++++++++++ 3 files changed, 166 insertions(+), 1 deletion(-) diff --git a/docs/src/rules/no-magic-numbers.md b/docs/src/rules/no-magic-numbers.md index e09f9701104..638e0b54f4a 100644 --- a/docs/src/rules/no-magic-numbers.md +++ b/docs/src/rules/no-magic-numbers.md @@ -193,6 +193,45 @@ let head; ::: +### ignoreClassFieldInitialValues + +A boolean to specify if numbers used as initial values of class fields are considered okay. `false` by default. + +Examples of **correct** code for the `{ "ignoreClassFieldInitialValues": true }` option: + +::: correct + +```js +/*eslint no-magic-numbers: ["error", { "ignoreClassFieldInitialValues": true }]*/ + +class C { + foo = 2; + bar = -3; + #baz = 4; + static qux = 5; +} +``` + +::: + +Examples of **incorrect** code for the `{ "ignoreClassFieldInitialValues": true }` option: + +::: incorrect + +```js +/*eslint no-magic-numbers: ["error", { "ignoreClassFieldInitialValues": true }]*/ + +class C { + foo = 2 + 3; +} + +class D { + 2; +} +``` + +::: + ### 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 9b085881556..786e595220a 100644 --- a/lib/rules/no-magic-numbers.js +++ b/lib/rules/no-magic-numbers.js @@ -65,6 +65,10 @@ module.exports = { ignoreDefaultValues: { type: "boolean", default: false + }, + ignoreClassFieldInitialValues: { + type: "boolean", + default: false } }, additionalProperties: false @@ -82,7 +86,8 @@ module.exports = { enforceConst = !!config.enforceConst, ignore = new Set((config.ignore || []).map(normalizeIgnoreValue)), ignoreArrayIndexes = !!config.ignoreArrayIndexes, - ignoreDefaultValues = !!config.ignoreDefaultValues; + ignoreDefaultValues = !!config.ignoreDefaultValues, + ignoreClassFieldInitialValues = !!config.ignoreClassFieldInitialValues; const okTypes = detectObjects ? [] : ["ObjectExpression", "Property", "AssignmentExpression"]; @@ -106,6 +111,17 @@ module.exports = { return parent.type === "AssignmentPattern" && parent.right === fullNumberNode; } + /** + * Returns whether the number is the initial value of a class field. + * @param {ASTNode} fullNumberNode `Literal` or `UnaryExpression` full number node + * @returns {boolean} true if the number is the initial value of a class field. + */ + function isClassFieldInitialValue(fullNumberNode) { + const parent = fullNumberNode.parent; + + return parent.type === "PropertyDefinition" && parent.value === 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 @@ -194,6 +210,7 @@ module.exports = { if ( isIgnoredValue(value) || (ignoreDefaultValues && isDefaultValue(fullNumberNode)) || + (ignoreClassFieldInitialValues && isClassFieldInitialValue(fullNumberNode)) || isParseIntRadix(fullNumberNode) || isJSXNumber(fullNumberNode) || (ignoreArrayIndexes && isArrayIndex(fullNumberNode, value)) diff --git a/tests/lib/rules/no-magic-numbers.js b/tests/lib/rules/no-magic-numbers.js index 6afc328f040..e168249d117 100644 --- a/tests/lib/rules/no-magic-numbers.js +++ b/tests/lib/rules/no-magic-numbers.js @@ -257,6 +257,33 @@ ruleTester.run("no-magic-numbers", rule, { code: "foo?.[777]", options: [{ ignoreArrayIndexes: true }], parserOptions: { ecmaVersion: 2020 } + }, + + // ignoreClassFieldInitialValues + { + code: "class C { foo = 2; }", + options: [{ ignoreClassFieldInitialValues: true }], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { foo = -2; }", + options: [{ ignoreClassFieldInitialValues: true }], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { static foo = 2; }", + options: [{ ignoreClassFieldInitialValues: true }], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { #foo = 2; }", + options: [{ ignoreClassFieldInitialValues: true }], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { static #foo = 2; }", + options: [{ ignoreClassFieldInitialValues: true }], + parserOptions: { ecmaVersion: 2022 } } ], invalid: [ @@ -812,6 +839,88 @@ ruleTester.run("no-magic-numbers", rule, { { messageId: "noMagic", data: { raw: "1" }, line: 1 }, { messageId: "noMagic", data: { raw: "2" }, line: 1 } ] + }, + + // ignoreClassFieldInitialValues + { + code: "class C { foo = 2; }", + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: "noMagic", data: { raw: "2" }, line: 1, column: 17 } + ] + }, + { + code: "class C { foo = 2; }", + options: [{}], + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: "noMagic", data: { raw: "2" }, line: 1, column: 17 } + ] + }, + { + code: "class C { foo = 2; }", + options: [{ ignoreClassFieldInitialValues: false }], + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: "noMagic", data: { raw: "2" }, line: 1, column: 17 } + ] + }, + { + code: "class C { foo = -2; }", + options: [{ ignoreClassFieldInitialValues: false }], + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: "noMagic", data: { raw: "-2" }, line: 1, column: 17 } + ] + }, + { + code: "class C { static foo = 2; }", + options: [{ ignoreClassFieldInitialValues: false }], + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: "noMagic", data: { raw: "2" }, line: 1, column: 24 } + ] + }, + { + code: "class C { #foo = 2; }", + options: [{ ignoreClassFieldInitialValues: false }], + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: "noMagic", data: { raw: "2" }, line: 1, column: 18 } + ] + }, + { + code: "class C { static #foo = 2; }", + options: [{ ignoreClassFieldInitialValues: false }], + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: "noMagic", data: { raw: "2" }, line: 1, column: 25 } + ] + }, + { + code: "class C { foo = 2 + 3; }", + options: [{ ignoreClassFieldInitialValues: true }], + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: "noMagic", data: { raw: "2" }, line: 1, column: 17 }, + { messageId: "noMagic", data: { raw: "3" }, line: 1, column: 21 } + ] + }, + { + code: "class C { 2; }", + options: [{ ignoreClassFieldInitialValues: true }], + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: "noMagic", data: { raw: "2" }, line: 1, column: 11 } + ] + }, + { + code: "class C { [2]; }", + options: [{ ignoreClassFieldInitialValues: true }], + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: "noMagic", data: { raw: "2" }, line: 1, column: 12 } + ] } ] });