Skip to content

Commit a3be554

Browse files
Clement398fiskersindresorhus
authored
Add no-await-in-promise-methods rule (#2259)
Co-authored-by: fisker <[email protected]> Co-authored-by: Sindre Sorhus <[email protected]>
1 parent 4594020 commit a3be554

7 files changed

+390
-0
lines changed

configs/recommended.js

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ module.exports = {
2121
'unicorn/no-array-push-push': 'error',
2222
'unicorn/no-array-reduce': 'error',
2323
'unicorn/no-await-expression-member': 'error',
24+
'unicorn/no-await-in-promise-methods': 'error',
2425
'unicorn/no-console-spaces': 'error',
2526
'unicorn/no-document-cookie': 'error',
2627
'unicorn/no-empty-file': 'error',
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Disallow using `await` in `Promise` method parameters
2+
3+
💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs).
4+
5+
💡 This rule is manually fixable by [editor suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
6+
7+
<!-- end auto-generated rule header -->
8+
<!-- Do not manually modify this header. Run: `npm run fix:eslint-docs` -->
9+
10+
Using `await` on promises passed as arguments to `Promise.all()`, `Promise.allSettled()`, `Promise.any()`, or `Promise.race()` is likely a mistake.
11+
12+
## Fail
13+
14+
```js
15+
Promise.all([await promise, anotherPromise]);
16+
17+
Promise.allSettled([await promise, anotherPromise]);
18+
19+
Promise.any([await promise, anotherPromise]);
20+
21+
Promise.race([await promise, anotherPromise]);
22+
```
23+
24+
## Pass
25+
26+
```js
27+
Promise.all([promise, anotherPromise]);
28+
29+
Promise.allSettled([promise, anotherPromise]);
30+
31+
Promise.any([promise, anotherPromise]);
32+
33+
Promise.race([promise, anotherPromise]);
34+
```

readme.md

+1
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ If you don't use the preset, ensure you use the same `env` and `parserOptions` c
131131
| [no-array-push-push](docs/rules/no-array-push-push.md) | Enforce combining multiple `Array#push()` into one call. || 🔧 | 💡 |
132132
| [no-array-reduce](docs/rules/no-array-reduce.md) | Disallow `Array#reduce()` and `Array#reduceRight()`. || | |
133133
| [no-await-expression-member](docs/rules/no-await-expression-member.md) | Disallow member access from await expression. || 🔧 | |
134+
| [no-await-in-promise-methods](docs/rules/no-await-in-promise-methods.md) | Disallow using `await` in `Promise` method parameters. || | 💡 |
134135
| [no-console-spaces](docs/rules/no-console-spaces.md) | Do not use leading/trailing space between `console.log` parameters. || 🔧 | |
135136
| [no-document-cookie](docs/rules/no-document-cookie.md) | Do not use `document.cookie` directly. || | |
136137
| [no-empty-file](docs/rules/no-empty-file.md) | Disallow empty files. || | |

rules/no-await-in-promise-methods.js

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
'use strict';
2+
const {isMethodCall} = require('./ast/index.js');
3+
const {removeSpacesAfter} = require('./fix/index.js');
4+
5+
const MESSAGE_ID_ERROR = 'no-await-in-promise-methods/error';
6+
const MESSAGE_ID_SUGGESTION = 'no-await-in-promise-methods/suggestion';
7+
const messages = {
8+
[MESSAGE_ID_ERROR]: 'Promise in `Promise.{{method}}()` should not be awaited.',
9+
[MESSAGE_ID_SUGGESTION]: 'Remove `await`.',
10+
};
11+
const METHODS = ['all', 'allSettled', 'any', 'race'];
12+
13+
const isPromiseMethodCallWithArrayExpression = node =>
14+
isMethodCall(node, {
15+
object: 'Promise',
16+
methods: METHODS,
17+
optionalMember: false,
18+
optionalCall: false,
19+
argumentsLength: 1,
20+
})
21+
&& node.arguments[0].type === 'ArrayExpression';
22+
23+
/** @param {import('eslint').Rule.RuleContext} context */
24+
const create = context => ({
25+
* CallExpression(callExpression) {
26+
if (!isPromiseMethodCallWithArrayExpression(callExpression)) {
27+
return;
28+
}
29+
30+
for (const element of callExpression.arguments[0].elements) {
31+
if (element?.type !== 'AwaitExpression') {
32+
continue;
33+
}
34+
35+
yield {
36+
node: element,
37+
messageId: MESSAGE_ID_ERROR,
38+
data: {
39+
method: callExpression.callee.property.name,
40+
},
41+
suggest: [
42+
{
43+
messageId: MESSAGE_ID_SUGGESTION,
44+
* fix(fixer) {
45+
const {sourceCode} = context;
46+
const awaitToken = context.sourceCode.getFirstToken(element);
47+
yield fixer.remove(awaitToken);
48+
yield removeSpacesAfter(awaitToken, sourceCode, fixer);
49+
},
50+
},
51+
],
52+
};
53+
}
54+
},
55+
});
56+
57+
/** @type {import('eslint').Rule.RuleModule} */
58+
module.exports = {
59+
create,
60+
meta: {
61+
type: 'suggestion',
62+
docs: {
63+
description: 'Disallow using `await` in `Promise` method parameters.',
64+
},
65+
hasSuggestions: true,
66+
messages,
67+
},
68+
};

test/no-await-in-promise-methods.mjs

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import {getTester} from './utils/test.mjs';
2+
3+
const {test} = getTester(import.meta);
4+
5+
test.snapshot({
6+
valid: [
7+
'Promise.all([promise1, promise2, promise3, promise4])',
8+
'Promise.allSettled([promise1, promise2, promise3, promise4])',
9+
'Promise.any([promise1, promise2, promise3, promise4])',
10+
'Promise.race([promise1, promise2, promise3, promise4])',
11+
'Promise.all(...[await promise])',
12+
'Promise.all([await promise], extraArguments)',
13+
'Promise.all()',
14+
'Promise.all(notArrayExpression)',
15+
'Promise.all([,])',
16+
'Promise[all]([await promise])',
17+
'Promise.all?.([await promise])',
18+
'Promise?.all([await promise])',
19+
'Promise.notListedMethod([await promise])',
20+
'NotPromise.all([await promise])',
21+
'Promise.all([(await promise, 0)])',
22+
'new Promise.all([await promise])',
23+
24+
// We are not checking these cases
25+
'globalThis.Promise.all([await promise])',
26+
'Promise["all"]([await promise])',
27+
],
28+
invalid: [
29+
'Promise.all([await promise])',
30+
'Promise.allSettled([await promise])',
31+
'Promise.any([await promise])',
32+
'Promise.race([await promise])',
33+
'Promise.all([, await promise])',
34+
'Promise.all([await promise,])',
35+
'Promise.all([await promise],)',
36+
'Promise.all([await (0, promise)],)',
37+
'Promise.all([await (( promise ))])',
38+
'Promise.all([await await promise])',
39+
'Promise.all([...foo, await promise1, await promise2])',
40+
'Promise.all([await /* comment*/ promise])',
41+
],
42+
});

0 commit comments

Comments
 (0)