Skip to content

Commit

Permalink
prefer-top-level-await: Ignore promises inside `Promise.{all,allSet…
Browse files Browse the repository at this point in the history
…tled,any,race}()` (#2139)
  • Loading branch information
fisker authored May 19, 2023
1 parent e303439 commit d3f6b60
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 6 deletions.
24 changes: 18 additions & 6 deletions rules/prefer-top-level-await.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';
const {findVariable, getFunctionHeadLocation} = require('@eslint-community/eslint-utils');
const {isFunction, isMemberExpression} = require('./ast/index.js');
const {isFunction, isMemberExpression, isMethodCall} = require('./ast/index.js');

const ERROR_PROMISE = 'promise';
const ERROR_IIFE = 'iife';
Expand All @@ -13,7 +13,7 @@ const messages = {
[SUGGESTION_ADD_AWAIT]: 'Insert `await`.',
};

const promiseMethods = ['then', 'catch', 'finally'];
const promisePrototypeMethods = ['then', 'catch', 'finally'];
const isTopLevelCallExpression = node => {
if (node.type !== 'CallExpression') {
return false;
Expand All @@ -37,17 +37,28 @@ const isPromiseMethodCalleeObject = node =>
&& node.parent.object === node
&& !node.parent.computed
&& node.parent.property.type === 'Identifier'
&& promiseMethods.includes(node.parent.property.name)
&& promisePrototypeMethods.includes(node.parent.property.name)
&& node.parent.parent.type === 'CallExpression'
&& node.parent.parent.callee === node.parent;
const isAwaitArgument = node => {
const isAwaitExpressionArgument = node => {
if (node.parent.type === 'ChainExpression') {
node = node.parent;
}

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

// `Promise.{all,allSettled,any,race}([foo()])`
const isInPromiseMethods = node =>
node.parent.type === 'ArrayExpression'
&& node.parent.elements.includes(node)
&& isMethodCall(node.parent.parent, {
object: 'Promise',
methods: ['all', 'allSettled', 'any', 'race'],
argumentsLength: 1,
})
&& node.parent.parent.arguments[0] === node.parent;

/** @param {import('eslint').Rule.RuleContext} context */
function create(context) {
if (context.getFilename().toLowerCase().endsWith('.cjs')) {
Expand All @@ -59,14 +70,15 @@ function create(context) {
if (
!isTopLevelCallExpression(node)
|| isPromiseMethodCalleeObject(node)
|| isAwaitArgument(node)
|| isAwaitExpressionArgument(node)
|| isInPromiseMethods(node)
) {
return;
}

// Promises
if (isMemberExpression(node.callee, {
properties: promiseMethods,
properties: promisePrototypeMethods,
computed: false,
})) {
return {
Expand Down
30 changes: 30 additions & 0 deletions test/prefer-top-level-await.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,36 @@ test.snapshot({
],
});

// In `Promise` methods
test.snapshot({
valid: [
outdent`
const foo = async () => {};
await Promise.all([
(async () => {})(),
/* hole */,
foo(),
foo.then(bar),
foo.catch(bar),
]);
await Promise.allSettled([foo()]);
await Promise?.any([foo()]);
await Promise.race?.([foo()]);
`,
outdent`
const foo = async () => {};
const promise = Promise.all([
(async () => {})(),
foo(),
foo.then(bar),
foo.catch(bar),
]);
await promise;
`,
],
invalid: [],
});

test.babel({
valid: [
'await foo',
Expand Down

0 comments on commit d3f6b60

Please sign in to comment.