Skip to content

Commit

Permalink
Add no-unreadable-iife rule (#1765)
Browse files Browse the repository at this point in the history
  • Loading branch information
fisker committed Mar 31, 2022
1 parent ea6f300 commit ce8a4b7
Show file tree
Hide file tree
Showing 7 changed files with 273 additions and 0 deletions.
1 change: 1 addition & 0 deletions configs/recommended.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ module.exports = {
'unicorn/no-thenable': 'error',
'unicorn/no-this-assignment': 'error',
'unicorn/no-unreadable-array-destructuring': 'error',
'unicorn/no-unreadable-iife': 'error',
'unicorn/no-unsafe-regex': 'off',
'unicorn/no-unused-properties': 'off',
'unicorn/no-useless-fallback-in-spread': 'error',
Expand Down
36 changes: 36 additions & 0 deletions docs/rules/no-unreadable-iife.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Disallow unreadable IIFEs

<!-- Do not manually modify RULE_NOTICE part. Run: `npm run generate-rule-notices` -->
<!-- RULE_NOTICE -->
*This rule is part of the [recommended](https://github.com/sindresorhus/eslint-plugin-unicorn#recommended-config) config.*
<!-- /RULE_NOTICE -->

[IIFE](https://en.wikipedia.org/wiki/Immediately_invoked_function_expression) with parenthesized arrow function body is considered unreadable.

## Fail

```js
const foo = (bar => (bar ? bar.baz : baz))(getBar());
```

```js
const foo = ((bar, baz) => ({bar, baz}))(bar, baz);
```

## Pass

```js
const bar = getBar();
const foo = bar ? bar.baz : baz;
```

```js
const getBaz = bar => (bar ? bar.baz : baz);
const foo = getBaz(getBar());
```

```js
const foo = (bar => {
return bar ? bar.baz : baz;
})(getBar());
```
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ Each rule has emojis denoting:
| [no-thenable](docs/rules/no-thenable.md) | Disallow `then` property. || | |
| [no-this-assignment](docs/rules/no-this-assignment.md) | Disallow assigning `this` to a variable. || | |
| [no-unreadable-array-destructuring](docs/rules/no-unreadable-array-destructuring.md) | Disallow unreadable array destructuring. || 🔧 | |
| [no-unreadable-iife](docs/rules/no-unreadable-iife.md) | Disallow unreadable IIFEs. || | |
| [no-unsafe-regex](docs/rules/no-unsafe-regex.md) | Disallow unsafe regular expressions. | | | |
| [no-unused-properties](docs/rules/no-unused-properties.md) | Disallow unused object properties. | | | |
| [no-useless-fallback-in-spread](docs/rules/no-useless-fallback-in-spread.md) | Disallow useless fallback when spreading in object literals. || 🔧 | |
Expand Down
48 changes: 48 additions & 0 deletions rules/no-unreadable-iife.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use strict';
const {
isParenthesized,
getParenthesizedRange,
} = require('./utils/parentheses.js');
const toLocation = require('./utils/to-location.js');

const MESSAGE_ID_ERROR = 'no-unreadable-iife';
const messages = {
[MESSAGE_ID_ERROR]: 'IIFE with parenthesized arrow function body is considered unreadable.',
};

const selector = [
'CallExpression',
' > ',
'ArrowFunctionExpression.callee',
' > ',
':not(BlockStatement).body',
].join('');

/** @param {import('eslint').Rule.RuleContext} context */
const create = context => ({
[selector](node) {
const sourceCode = context.getSourceCode();
if (!isParenthesized(node, sourceCode)) {
return;
}

return {
node,
loc: toLocation(getParenthesizedRange(node, sourceCode), sourceCode),
messageId: MESSAGE_ID_ERROR,
};
},
});

/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
create,
meta: {
type: 'suggestion',
docs: {
description: 'Disallow unreadable IIFEs.',
},
hasSuggestions: false,
messages,
},
};
52 changes: 52 additions & 0 deletions test/no-unreadable-iife.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import outdent from 'outdent';
import {getTester} from './utils/test.mjs';

const {test} = getTester(import.meta);

test.snapshot({
valid: [
'const foo = (bar => bar)();',
outdent`
const foo = (() => {
return a ? b : c
})();
`,
],
invalid: [
'const foo = (() => (a ? b : c))();',
outdent`
const foo = (() => (
a ? b : c
))();
`,
outdent`
const foo = (
() => (
a ? b : c
)
)();
`,
outdent`
const foo = (() => (
a, b
))();
`,
outdent`
const foo = (() => ({
a: b,
}))();
`,
'const foo = (bar => (bar))();',
outdent`
(async () => ({
bar,
}))();
`,
outdent`
const foo = (async (bar) => ({
bar: await baz(),
}))();
`,
'(async () => (( {bar} )))();',
],
});
135 changes: 135 additions & 0 deletions test/snapshots/no-unreadable-iife.mjs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Snapshot report for `test/no-unreadable-iife.mjs`

The actual snapshot is saved in `no-unreadable-iife.mjs.snap`.

Generated by [AVA](https://avajs.dev).

## Invalid #1
1 | const foo = (() => (a ? b : c))();

> Error 1/1
`␊
> 1 | const foo = (() => (a ? b : c))();␊
| ^^^^^^^^^^^ IIFE with parenthesized arrow function body is considered unreadable.␊
`

## Invalid #2
1 | const foo = (() => (
2 | a ? b : c
3 | ))();

> Error 1/1
`␊
> 1 | const foo = (() => (␊
| ^␊
> 2 | a ? b : c␊
| ^^^^^^^^^^␊
> 3 | ))();␊
| ^^ IIFE with parenthesized arrow function body is considered unreadable.␊
`

## Invalid #3
1 | const foo = (
2 | () => (
3 | a ? b : c
4 | )
5 | )();

> Error 1/1
`␊
1 | const foo = (␊
> 2 | () => (␊
| ^␊
> 3 | a ? b : c␊
| ^^^^^^^^^^^␊
> 4 | )␊
| ^^^ IIFE with parenthesized arrow function body is considered unreadable.␊
5 | )();␊
`

## Invalid #4
1 | const foo = (() => (
2 | a, b
3 | ))();

> Error 1/1
`␊
> 1 | const foo = (() => (␊
| ^␊
> 2 | a, b␊
| ^^^^^␊
> 3 | ))();␊
| ^^ IIFE with parenthesized arrow function body is considered unreadable.␊
`

## Invalid #5
1 | const foo = (() => ({
2 | a: b,
3 | }))();

> Error 1/1
`␊
> 1 | const foo = (() => ({␊
| ^^␊
> 2 | a: b,␊
| ^^^^^^␊
> 3 | }))();␊
| ^^^ IIFE with parenthesized arrow function body is considered unreadable.␊
`

## Invalid #6
1 | const foo = (bar => (bar))();

> Error 1/1
`␊
> 1 | const foo = (bar => (bar))();␊
| ^^^^^ IIFE with parenthesized arrow function body is considered unreadable.␊
`

## Invalid #7
1 | (async () => ({
2 | bar,
3 | }))();

> Error 1/1
`␊
> 1 | (async () => ({␊
| ^^␊
> 2 | bar,␊
| ^^^^^␊
> 3 | }))();␊
| ^^^ IIFE with parenthesized arrow function body is considered unreadable.␊
`

## Invalid #8
1 | const foo = (async (bar) => ({
2 | bar: await baz(),
3 | }))();

> Error 1/1
`␊
> 1 | const foo = (async (bar) => ({␊
| ^^␊
> 2 | bar: await baz(),␊
| ^^^^^^^^^^^^^^^^^^␊
> 3 | }))();␊
| ^^^ IIFE with parenthesized arrow function body is considered unreadable.␊
`

## Invalid #9
1 | (async () => (( {bar} )))();

> Error 1/1
`␊
> 1 | (async () => (( {bar} )))();␊
| ^^^^^^^^^^^ IIFE with parenthesized arrow function body is considered unreadable.␊
`
Binary file added test/snapshots/no-unreadable-iife.mjs.snap
Binary file not shown.

0 comments on commit ce8a4b7

Please sign in to comment.