From bc174281d21bea3e90f53daa804ea60c11fbeb9b Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 7 May 2024 20:04:16 +0800 Subject: [PATCH] Add `no-magic-array-flat-depth` rule (#2335) --- configs/recommended.js | 1 + docs/rules/no-magic-array-flat-depth.md | 40 +++++++++++ readme.md | 1 + rules/no-magic-array-flat-depth.js | 52 ++++++++++++++ test/no-magic-array-flat-depth.mjs | 28 ++++++++ .../no-magic-array-flat-depth.mjs.md | 65 ++++++++++++++++++ .../no-magic-array-flat-depth.mjs.snap | Bin 0 -> 336 bytes 7 files changed, 187 insertions(+) create mode 100644 docs/rules/no-magic-array-flat-depth.md create mode 100644 rules/no-magic-array-flat-depth.js create mode 100644 test/no-magic-array-flat-depth.mjs create mode 100644 test/snapshots/no-magic-array-flat-depth.mjs.md create mode 100644 test/snapshots/no-magic-array-flat-depth.mjs.snap diff --git a/configs/recommended.js b/configs/recommended.js index b6e543f10a..86fd567670 100644 --- a/configs/recommended.js +++ b/configs/recommended.js @@ -31,6 +31,7 @@ module.exports = { 'unicorn/no-invalid-remove-event-listener': 'error', 'unicorn/no-keyword-prefix': 'off', 'unicorn/no-lonely-if': 'error', + 'unicorn/no-magic-array-flat-depth': 'error', 'no-negated-condition': 'off', 'unicorn/no-negated-condition': 'error', 'no-nested-ternary': 'off', diff --git a/docs/rules/no-magic-array-flat-depth.md b/docs/rules/no-magic-array-flat-depth.md new file mode 100644 index 0000000000..c90ee32ee1 --- /dev/null +++ b/docs/rules/no-magic-array-flat-depth.md @@ -0,0 +1,40 @@ +# Disallow a magic number as the `depth` argument in `Array#flat(…).` + +💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs-eslintconfigjs). + + + + +When calling [`Array#flat(depth)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat), the depth argument should normally be `1` or `Infinity`, otherwise it should be a meaningful variable name or explained with a comment. + +## Fail + +```js +const foo = array.flat(2); +``` + +```js +const foo = array.flat(99); +``` + +## Pass + +```js +const foo = array.flat(); +``` + +```js +const foo = array.flat(Number.POSITIVE_INFINITY); +``` + +```js +const foo = array.flat(Infinity); +``` + +```js +const foo = array.flat(depth); +``` + +```js +const foo = array.flat(/* The depth is always 2 */ 2); +``` diff --git a/readme.md b/readme.md index e25135c52b..5dc4901f76 100644 --- a/readme.md +++ b/readme.md @@ -141,6 +141,7 @@ If you don't use the preset, ensure you use the same `env` and `parserOptions` c | [no-invalid-remove-event-listener](docs/rules/no-invalid-remove-event-listener.md) | Prevent calling `EventTarget#removeEventListener()` with the result of an expression. | ✅ | | | | [no-keyword-prefix](docs/rules/no-keyword-prefix.md) | Disallow identifiers starting with `new` or `class`. | | | | | [no-lonely-if](docs/rules/no-lonely-if.md) | Disallow `if` statements as the only statement in `if` blocks without `else`. | ✅ | 🔧 | | +| [no-magic-array-flat-depth](docs/rules/no-magic-array-flat-depth.md) | Disallow a magic number as the `depth` argument in `Array#flat(…).` | ✅ | | | | [no-negated-condition](docs/rules/no-negated-condition.md) | Disallow negated conditions. | ✅ | 🔧 | | | [no-nested-ternary](docs/rules/no-nested-ternary.md) | Disallow nested ternary expressions. | ✅ | 🔧 | | | [no-new-array](docs/rules/no-new-array.md) | Disallow `new Array()`. | ✅ | 🔧 | 💡 | diff --git a/rules/no-magic-array-flat-depth.js b/rules/no-magic-array-flat-depth.js new file mode 100644 index 0000000000..3decacae7a --- /dev/null +++ b/rules/no-magic-array-flat-depth.js @@ -0,0 +1,52 @@ +'use strict'; +const {isOpeningParenToken} = require('@eslint-community/eslint-utils'); +const {isMethodCall, isNumberLiteral} = require('./ast/index.js'); + +const MESSAGE_ID = 'no-magic-array-flat-depth'; +const messages = { + [MESSAGE_ID]: 'Magic number as depth is not allowed.', +}; + +/** @param {import('eslint').Rule.RuleContext} context */ +const create = context => ({ + CallExpression(callExpression) { + if (!isMethodCall(callExpression, { + method: 'flat', + argumentsLength: 1, + optionalCall: false, + })) { + return; + } + + const [depth] = callExpression.arguments; + + if (!isNumberLiteral(depth) || depth.value === 1) { + return; + } + + const {sourceCode} = context; + const openingParenthesisToken = sourceCode.getTokenAfter(callExpression.callee, isOpeningParenToken); + const closingParenthesisToken = sourceCode.getLastToken(callExpression); + if (sourceCode.commentsExistBetween(openingParenthesisToken, closingParenthesisToken)) { + return; + } + + return { + node: depth, + messageId: MESSAGE_ID, + }; + }, +}); + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + create, + meta: { + type: 'suggestion', + docs: { + description: 'Disallow a magic number as the `depth` argument in `Array#flat(…).`', + recommended: true, + }, + messages, + }, +}; diff --git a/test/no-magic-array-flat-depth.mjs b/test/no-magic-array-flat-depth.mjs new file mode 100644 index 0000000000..721204cb8a --- /dev/null +++ b/test/no-magic-array-flat-depth.mjs @@ -0,0 +1,28 @@ +import {getTester} from './utils/test.mjs'; + +const {test} = getTester(import.meta); + +test.snapshot({ + valid: [ + 'array.flat(1)', + 'array.flat(1.0)', + 'array.flat(0x01)', + 'array.flat(unknown)', + 'array.flat(Number.POSITIVE_INFINITY)', + 'array.flat(Infinity)', + 'array.flat(/* explanation */2)', + 'array.flat(2/* explanation */)', + 'array.flat()', + 'array.flat(2, extraArgument)', + 'new array.flat(2)', + 'array.flat?.(2)', + 'array.notFlat(2)', + 'flat(2)', + ], + invalid: [ + 'array.flat(2)', + 'array?.flat(2)', + 'array.flat(99,)', + 'array.flat(0b10,)', + ], +}); diff --git a/test/snapshots/no-magic-array-flat-depth.mjs.md b/test/snapshots/no-magic-array-flat-depth.mjs.md new file mode 100644 index 0000000000..570a6364bb --- /dev/null +++ b/test/snapshots/no-magic-array-flat-depth.mjs.md @@ -0,0 +1,65 @@ +# Snapshot report for `test/no-magic-array-flat-depth.mjs` + +The actual snapshot is saved in `no-magic-array-flat-depth.mjs.snap`. + +Generated by [AVA](https://avajs.dev). + +## invalid(1): array.flat(2) + +> Input + + `␊ + 1 | array.flat(2)␊ + ` + +> Error 1/1 + + `␊ + > 1 | array.flat(2)␊ + | ^ Magic number as depth is not allowed.␊ + ` + +## invalid(2): array?.flat(2) + +> Input + + `␊ + 1 | array?.flat(2)␊ + ` + +> Error 1/1 + + `␊ + > 1 | array?.flat(2)␊ + | ^ Magic number as depth is not allowed.␊ + ` + +## invalid(3): array.flat(99,) + +> Input + + `␊ + 1 | array.flat(99,)␊ + ` + +> Error 1/1 + + `␊ + > 1 | array.flat(99,)␊ + | ^^ Magic number as depth is not allowed.␊ + ` + +## invalid(4): array.flat(0b10,) + +> Input + + `␊ + 1 | array.flat(0b10,)␊ + ` + +> Error 1/1 + + `␊ + > 1 | array.flat(0b10,)␊ + | ^^^^ Magic number as depth is not allowed.␊ + ` diff --git a/test/snapshots/no-magic-array-flat-depth.mjs.snap b/test/snapshots/no-magic-array-flat-depth.mjs.snap new file mode 100644 index 0000000000000000000000000000000000000000..1b9af089940687e73a6d2181f562cb41eb59fed5 GIT binary patch literal 336 zcmV-W0k8f+RzV2##w@pjy*0Uqm&8EnP8W)9NQ{;@UrXlJdyzb zOriD$aD`9206BiinFcgKS!qYW4M=4Hg;1}IcB=pQh1N;~IVR4v?OZ+Q$`-@+y$d(% zu