Skip to content

Commit

Permalink
Add no-await-in-promise-methods rule
Browse files Browse the repository at this point in the history
  • Loading branch information
Clement398 committed Jan 14, 2024
1 parent eb5af8b commit b384e43
Show file tree
Hide file tree
Showing 5 changed files with 192 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 @@ -20,6 +20,7 @@ module.exports = {
'unicorn/no-array-push-push': 'error',
'unicorn/no-array-reduce': 'error',
'unicorn/no-await-expression-member': 'error',
'unicorn/no-await-in-promise-methods': 'off',
'unicorn/no-console-spaces': 'error',
'unicorn/no-document-cookie': 'error',
'unicorn/no-empty-file': 'error',
Expand Down
42 changes: 42 additions & 0 deletions docs/rules/no-await-in-promise-methods.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Disallow Promise methods containing an await

🚫 This rule is _disabled_ in the ✅ `recommended` [config](https:/sindresorhus/eslint-plugin-unicorn#preset-configs).

🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).

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

It's useless to use an await in a Promise all, allSettled, any, or race method and this reduces the code readability.

## Fail

```js
const fn = () => Promise.resolve('fn');
const pr = fn();

await Promise.all([fn(), await fn(), await pr, pr]);

await Promise.allSettled([fn(), await fn(), await pr, pr]);

await Promise.any([fn(), await fn(), await pr, pr]);

await Promise.race([fn(), await fn(), await pr, pr]);
```

## Pass

```js
const fn = () => Promise.resolve('fn');
const pr = fn();

await Promise.all([fn(), fn(), pr, pr]);

await Promise.allSettled([fn(), fn(), pr, pr]);

await Promise.any([fn(), fn(), pr, pr]);

await Promise.race([fn(), fn(), pr, pr]);

await Promise.resolve([await fn()]);
```
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ If you don't use the preset, ensure you use the same `env` and `parserOptions` c
| [no-array-push-push](docs/rules/no-array-push-push.md) | Enforce combining multiple `Array#push()` into one call. || 🔧 | 💡 |
| [no-array-reduce](docs/rules/no-array-reduce.md) | Disallow `Array#reduce()` and `Array#reduceRight()`. || | |
| [no-await-expression-member](docs/rules/no-await-expression-member.md) | Disallow member access from await expression. || 🔧 | |
| [no-await-in-promise-methods](docs/rules/no-await-in-promise-methods.md) | Disallow Promise methods containing an await. | | 🔧 | |
| [no-console-spaces](docs/rules/no-console-spaces.md) | Do not use leading/trailing space between `console.log` parameters. || 🔧 | |
| [no-document-cookie](docs/rules/no-document-cookie.md) | Do not use `document.cookie` directly. || | |
| [no-empty-file](docs/rules/no-empty-file.md) | Disallow empty files. || | |
Expand Down
56 changes: 56 additions & 0 deletions rules/no-await-in-promise-methods.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
'use strict';
const MESSAGE_ID = 'no-await-in-promise-methods';
const messages = {
[MESSAGE_ID]: 'Promise.{{ method }} contains an await.',
};

const isPromiseMethodWithArray = node =>
node.callee.type === 'MemberExpression'
&& node.callee.object.type === 'Identifier'
&& node.callee.object.name === 'Promise'
&& node.callee.property.type === 'Identifier'
&& ['all', 'allSettled', 'any', 'race'].includes(node.callee.property.name)
&& node.arguments.length === 1
&& node.arguments[0].type === 'ArrayExpression';

const getArrayElements = node => node.arguments[0].elements;

const isAwait = element => element.type === 'AwaitExpression';

const getMethodName = node => node.callee.property.name;

const getFixer = ({sourceCode}, element) => fixer =>
fixer.replaceText(element, sourceCode.getText(element.argument));

/** @param {import('eslint').Rule.RuleContext} context */
const create = context => ({
CallExpression(node) {
if (isPromiseMethodWithArray(node)) {
for (const element of getArrayElements(node)) {
if (isAwait(element)) {
context.report({
node: element,
messageId: MESSAGE_ID,
data: {
method: getMethodName(node),
},
fix: getFixer(context, element),
});
}
}
}
},
});

/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
create,
meta: {
type: 'suggestion',
docs: {
description: 'Disallow Promise methods containing an await.',
},
fixable: 'code',
messages,
},
};
92 changes: 92 additions & 0 deletions test/no-await-in-promise-methods.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import outdent from 'outdent';
import {getTester} from './utils/test.mjs';

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

const error = {
messageId: 'no-await-in-promise-methods',
};

test({
valid: [
outdent`
const fn = () => Promise.resolve('fn');
const pr = fn();
await Promise.all([fn(), fn(), pr, pr]);
`,
outdent`
const fn = () => Promise.resolve('fn');
const pr = fn();
await Promise.allSettled([fn(), fn(), pr, pr]);
`,
outdent`
const fn = () => Promise.resolve('fn');
const pr = fn();
await Promise.any([fn(), fn(), pr, pr]);
`,
outdent`
const fn = () => Promise.resolve('fn');
const pr = fn();
await Promise.race([fn(), fn(), pr, pr]);
`,
outdent`
const fn = () => Promise.resolve('fn');
await Promise.resolve([await fn()]);
`,
],

invalid: [
{
code: outdent`
const fn = () => Promise.resolve('fn');
const pr = fn();
await Promise.all([fn(), await fn(), await pr, pr]);
`,
errors: [error, error],
output: outdent`
const fn = () => Promise.resolve('fn');
const pr = fn();
await Promise.all([fn(), fn(), pr, pr]);
`,
},
{
code: outdent`
const fn = () => Promise.resolve('fn');
const pr = fn();
await Promise.allSettled([fn(), await fn(), await pr, pr]);
`,
errors: [error, error],
output: outdent`
const fn = () => Promise.resolve('fn');
const pr = fn();
await Promise.allSettled([fn(), fn(), pr, pr]);
`,
},
{
code: outdent`
const fn = () => Promise.resolve('fn');
const pr = fn();
await Promise.any([fn(), await fn(), await pr, pr]);
`,
errors: [error, error],
output: outdent`
const fn = () => Promise.resolve('fn');
const pr = fn();
await Promise.any([fn(), fn(), pr, pr]);
`,
},
{
code: outdent`
const fn = () => Promise.resolve('fn');
const pr = fn();
await Promise.race([fn(), await fn(), await pr, pr]);
`,
errors: [error, error],
output: outdent`
const fn = () => Promise.resolve('fn');
const pr = fn();
await Promise.race([fn(), fn(), pr, pr]);
`,
},
],
});

0 comments on commit b384e43

Please sign in to comment.