Skip to content

Commit 1ed0701

Browse files
committed
Add no-await-in-promise-methods rule
1 parent eb5af8b commit 1ed0701

File tree

6 files changed

+198
-0
lines changed

6 files changed

+198
-0
lines changed

configs/recommended.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ module.exports = {
2020
'unicorn/no-array-push-push': 'error',
2121
'unicorn/no-array-reduce': 'error',
2222
'unicorn/no-await-expression-member': 'error',
23+
'unicorn/no-await-in-promise-methods': 'error',
2324
'unicorn/no-console-spaces': 'error',
2425
'unicorn/no-document-cookie': 'error',
2526
'unicorn/no-empty-file': 'error',
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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 automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
6+
7+
<!-- end auto-generated rule header -->
8+
<!-- Do not manually modify this header. Run: `npm run fix:eslint-docs` -->
9+
10+
Awaited parameters in a Promise.all(), Promise.allSettled(), Promise.any() or Promise.race() method is probably a mistake.
11+
12+
## Fail
13+
14+
```js
15+
const func = () => Promise.resolve('promise');
16+
const promise = func();
17+
18+
await Promise.all([func(), await func(), await promise, promise]);
19+
20+
await Promise.allSettled([func(), await func(), await promise, promise]);
21+
22+
await Promise.any([func(), await func(), await promise, promise]);
23+
24+
await Promise.race([func(), await func(), await promise, promise]);
25+
```
26+
27+
## Pass
28+
29+
```js
30+
const func = () => Promise.resolve('promise');
31+
const promise = func();
32+
33+
await Promise.all([func(), func(), promise, promise]);
34+
35+
await Promise.allSettled([func(), func(), promise, promise]);
36+
37+
await Promise.any([func(), func(), promise, promise]);
38+
39+
await Promise.race([func(), func(), promise, promise]);
40+
41+
await Promise.resolve([await func()]);
42+
```

readme.md

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

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

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
'use strict';
2+
const isPromiseMethodWithArray = require('./utils/is-promise-method-with-array.js');
3+
4+
const MESSAGE_ID = 'no-await-in-promise-methods';
5+
const messages = {
6+
[MESSAGE_ID]: 'Parameters in `Promise.{{method}}` should not be awaited.',
7+
};
8+
9+
const getArrayElements = node => node.arguments[0].elements;
10+
11+
const isAwait = element => element.type === 'AwaitExpression';
12+
13+
const getMethodName = node => node.callee.property.name;
14+
15+
const getFixer = ({sourceCode}, element) => fixer =>
16+
fixer.replaceText(element, sourceCode.getText(element.argument));
17+
18+
/** @param {import('eslint').Rule.RuleContext} context */
19+
const create = context => ({
20+
CallExpression(node) {
21+
if (isPromiseMethodWithArray(node)) {
22+
for (const element of getArrayElements(node)) {
23+
if (isAwait(element)) {
24+
context.report({
25+
node: element,
26+
messageId: MESSAGE_ID,
27+
data: {
28+
method: getMethodName(node),
29+
},
30+
fix: getFixer(context, element),
31+
});
32+
}
33+
}
34+
}
35+
},
36+
});
37+
38+
/** @type {import('eslint').Rule.RuleModule} */
39+
module.exports = {
40+
create,
41+
meta: {
42+
type: 'suggestion',
43+
docs: {
44+
description: 'Disallow using `await` in `Promise` method parameters.',
45+
},
46+
fixable: 'code',
47+
messages,
48+
},
49+
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
'use strict';
2+
const METHODS = new Set(['all', 'allSettled', 'any', 'race']);
3+
4+
const isPromiseMethodWithArray = node =>
5+
node.callee.type === 'MemberExpression'
6+
&& node.callee.object.type === 'Identifier'
7+
&& node.callee.object.name === 'Promise'
8+
&& node.callee.property.type === 'Identifier'
9+
&& METHODS.has(node.callee.property.name)
10+
&& node.arguments.length === 1
11+
&& node.arguments[0].type === 'ArrayExpression';
12+
13+
module.exports = isPromiseMethodWithArray;

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

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import outdent from 'outdent';
2+
import {getTester} from './utils/test.mjs';
3+
4+
const {test} = getTester(import.meta);
5+
6+
const error = {
7+
messageId: 'no-await-in-promise-methods',
8+
};
9+
10+
test({
11+
valid: [
12+
outdent`
13+
const func = () => Promise.resolve('promise');
14+
const promise = func();
15+
await Promise.all([func(), func(), promise, promise]);
16+
`,
17+
outdent`
18+
const func = () => Promise.resolve('promise');
19+
const promise = func();
20+
await Promise.allSettled([func(), func(), promise, promise]);
21+
`,
22+
outdent`
23+
const func = () => Promise.resolve('promise');
24+
const promise = func();
25+
await Promise.any([func(), func(), promise, promise]);
26+
`,
27+
outdent`
28+
const func = () => Promise.resolve('promise');
29+
const promise = func();
30+
await Promise.race([func(), func(), promise, promise]);
31+
`,
32+
outdent`
33+
const func = () => Promise.resolve('promise');
34+
await Promise.resolve([await func()]);
35+
`,
36+
],
37+
38+
invalid: [
39+
{
40+
code: outdent`
41+
const func = () => Promise.resolve('promise');
42+
const promise = func();
43+
await Promise.all([func(), await func(), await promise, promise]);
44+
`,
45+
errors: [error, error],
46+
output: outdent`
47+
const func = () => Promise.resolve('promise');
48+
const promise = func();
49+
await Promise.all([func(), func(), promise, promise]);
50+
`,
51+
},
52+
{
53+
code: outdent`
54+
const func = () => Promise.resolve('promise');
55+
const promise = func();
56+
await Promise.allSettled([func(), await func(), await promise, promise]);
57+
`,
58+
errors: [error, error],
59+
output: outdent`
60+
const func = () => Promise.resolve('promise');
61+
const promise = func();
62+
await Promise.allSettled([func(), func(), promise, promise]);
63+
`,
64+
},
65+
{
66+
code: outdent`
67+
const func = () => Promise.resolve('promise');
68+
const promise = func();
69+
await Promise.any([func(), await func(), await promise, promise]);
70+
`,
71+
errors: [error, error],
72+
output: outdent`
73+
const func = () => Promise.resolve('promise');
74+
const promise = func();
75+
await Promise.any([func(), func(), promise, promise]);
76+
`,
77+
},
78+
{
79+
code: outdent`
80+
const func = () => Promise.resolve('promise');
81+
const promise = func();
82+
await Promise.race([func(), await func(), await promise, promise]);
83+
`,
84+
errors: [error, error],
85+
output: outdent`
86+
const func = () => Promise.resolve('promise');
87+
const promise = func();
88+
await Promise.race([func(), func(), promise, promise]);
89+
`,
90+
},
91+
],
92+
});

0 commit comments

Comments
 (0)