Skip to content

Commit

Permalink
Add no-magic-array-flat-depth rule (#2335)
Browse files Browse the repository at this point in the history
  • Loading branch information
fisker committed May 7, 2024
1 parent 204d31c commit bc17428
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 0 deletions.
1 change: 1 addition & 0 deletions configs/recommended.js
Expand Up @@ -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',
Expand Down
40 changes: 40 additions & 0 deletions 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).

<!-- end auto-generated rule header -->
<!-- Do not manually modify this header. Run: `npm run fix:eslint-docs` -->

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);
```
1 change: 1 addition & 0 deletions readme.md
Expand Up @@ -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()`. || 🔧 | 💡 |
Expand Down
52 changes: 52 additions & 0 deletions 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,
},
};
28 changes: 28 additions & 0 deletions 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,)',
],
});
65 changes: 65 additions & 0 deletions 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.␊
`
Binary file added test/snapshots/no-magic-array-flat-depth.mjs.snap
Binary file not shown.

0 comments on commit bc17428

Please sign in to comment.