Skip to content

Commit d3f6b60

Browse files
authored
prefer-top-level-await: Ignore promises inside Promise.{all,allSettled,any,race}() (#2139)
1 parent e303439 commit d3f6b60

File tree

2 files changed

+48
-6
lines changed

2 files changed

+48
-6
lines changed

rules/prefer-top-level-await.js

+18-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22
const {findVariable, getFunctionHeadLocation} = require('@eslint-community/eslint-utils');
3-
const {isFunction, isMemberExpression} = require('./ast/index.js');
3+
const {isFunction, isMemberExpression, isMethodCall} = require('./ast/index.js');
44

55
const ERROR_PROMISE = 'promise';
66
const ERROR_IIFE = 'iife';
@@ -13,7 +13,7 @@ const messages = {
1313
[SUGGESTION_ADD_AWAIT]: 'Insert `await`.',
1414
};
1515

16-
const promiseMethods = ['then', 'catch', 'finally'];
16+
const promisePrototypeMethods = ['then', 'catch', 'finally'];
1717
const isTopLevelCallExpression = node => {
1818
if (node.type !== 'CallExpression') {
1919
return false;
@@ -37,17 +37,28 @@ const isPromiseMethodCalleeObject = node =>
3737
&& node.parent.object === node
3838
&& !node.parent.computed
3939
&& node.parent.property.type === 'Identifier'
40-
&& promiseMethods.includes(node.parent.property.name)
40+
&& promisePrototypeMethods.includes(node.parent.property.name)
4141
&& node.parent.parent.type === 'CallExpression'
4242
&& node.parent.parent.callee === node.parent;
43-
const isAwaitArgument = node => {
43+
const isAwaitExpressionArgument = node => {
4444
if (node.parent.type === 'ChainExpression') {
4545
node = node.parent;
4646
}
4747

4848
return node.parent.type === 'AwaitExpression' && node.parent.argument === node;
4949
};
5050

51+
// `Promise.{all,allSettled,any,race}([foo()])`
52+
const isInPromiseMethods = node =>
53+
node.parent.type === 'ArrayExpression'
54+
&& node.parent.elements.includes(node)
55+
&& isMethodCall(node.parent.parent, {
56+
object: 'Promise',
57+
methods: ['all', 'allSettled', 'any', 'race'],
58+
argumentsLength: 1,
59+
})
60+
&& node.parent.parent.arguments[0] === node.parent;
61+
5162
/** @param {import('eslint').Rule.RuleContext} context */
5263
function create(context) {
5364
if (context.getFilename().toLowerCase().endsWith('.cjs')) {
@@ -59,14 +70,15 @@ function create(context) {
5970
if (
6071
!isTopLevelCallExpression(node)
6172
|| isPromiseMethodCalleeObject(node)
62-
|| isAwaitArgument(node)
73+
|| isAwaitExpressionArgument(node)
74+
|| isInPromiseMethods(node)
6375
) {
6476
return;
6577
}
6678

6779
// Promises
6880
if (isMemberExpression(node.callee, {
69-
properties: promiseMethods,
81+
properties: promisePrototypeMethods,
7082
computed: false,
7183
})) {
7284
return {

test/prefer-top-level-await.mjs

+30
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,36 @@ test.snapshot({
200200
],
201201
});
202202

203+
// In `Promise` methods
204+
test.snapshot({
205+
valid: [
206+
outdent`
207+
const foo = async () => {};
208+
await Promise.all([
209+
(async () => {})(),
210+
/* hole */,
211+
foo(),
212+
foo.then(bar),
213+
foo.catch(bar),
214+
]);
215+
await Promise.allSettled([foo()]);
216+
await Promise?.any([foo()]);
217+
await Promise.race?.([foo()]);
218+
`,
219+
outdent`
220+
const foo = async () => {};
221+
const promise = Promise.all([
222+
(async () => {})(),
223+
foo(),
224+
foo.then(bar),
225+
foo.catch(bar),
226+
]);
227+
await promise;
228+
`,
229+
],
230+
invalid: [],
231+
});
232+
203233
test.babel({
204234
valid: [
205235
'await foo',

0 commit comments

Comments
 (0)