From bd6140b2bc28a9abb7f5b7f584774e0bbdad1e71 Mon Sep 17 00:00:00 2001 From: auvred Date: Tue, 28 Nov 2023 17:51:28 +0000 Subject: [PATCH 1/2] feat(eslint-plugin): [require-await] allow yielding Promise in async generators --- .../eslint-plugin/src/rules/require-await.ts | 42 +++++++++++-------- .../tests/rules/require-await.test.ts | 24 +++++++++++ 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/packages/eslint-plugin/src/rules/require-await.ts b/packages/eslint-plugin/src/rules/require-await.ts index f834e17decec..95685f0dff55 100644 --- a/packages/eslint-plugin/src/rules/require-await.ts +++ b/packages/eslint-plugin/src/rules/require-await.ts @@ -112,32 +112,40 @@ export default createRule({ } /** - * mark `scopeInfo.isAsyncYield` to `true` if its a generator - * function and the delegate is `true` + * Mark `scopeInfo.isAsyncYield` to `true` if it + * 1) delegates async generator function + * or + * 2) yields thenable type */ - function markAsHasDelegateGen(node: TSESTree.YieldExpression): void { + function visitYieldExpression(node: TSESTree.YieldExpression): void { if (!scopeInfo?.isGen || !node.argument) { return; } if (node.argument.type === AST_NODE_TYPES.Literal) { - // making this `false` as for literals we don't need to check the definition + // ignoring this as for literals we don't need to check the definition // eg : async function* run() { yield* 1 } - scopeInfo.isAsyncYield ||= false; + return; } - const type = services.getTypeAtLocation(node.argument); - const typesToCheck = expandUnionOrIntersectionType(type); - for (const type of typesToCheck) { - const asyncIterator = tsutils.getWellKnownSymbolPropertyOfType( - type, - 'asyncIterator', - checker, - ); - if (asyncIterator !== undefined) { - scopeInfo.isAsyncYield = true; - break; + if (node.delegate) { + const type = services.getTypeAtLocation(node.argument); + const typesToCheck = expandUnionOrIntersectionType(type); + for (const type of typesToCheck) { + const asyncIterator = tsutils.getWellKnownSymbolPropertyOfType( + type, + 'asyncIterator', + checker, + ); + if (asyncIterator !== undefined) { + scopeInfo.isAsyncYield = true; + break; + } } + } else if ( + isThenableType(services.esTreeNodeToTSNodeMap.get(node.argument)) + ) { + scopeInfo.isAsyncYield = true; } } @@ -152,7 +160,7 @@ export default createRule({ AwaitExpression: markAsHasAwait, 'VariableDeclaration[kind = "await using"]': markAsHasAwait, 'ForOfStatement[await = true]': markAsHasAwait, - 'YieldExpression[delegate = true]': markAsHasDelegateGen, + YieldExpression: visitYieldExpression, // check body-less async arrow function. // ignore `async () => await foo` because it's obviously correct diff --git a/packages/eslint-plugin/tests/rules/require-await.test.ts b/packages/eslint-plugin/tests/rules/require-await.test.ts index 7bcb1d6a0b4f..f8f9624e26c8 100644 --- a/packages/eslint-plugin/tests/rules/require-await.test.ts +++ b/packages/eslint-plugin/tests/rules/require-await.test.ts @@ -237,6 +237,30 @@ async function* foo(): Promise { await using foo = new Bar(); }; `, + ` + async function* test1() { + yield Promise.resolve(1); + } + `, + ` + function asyncFunction() { + return Promise.resolve(1); + } + async function* test1() { + yield asyncFunction(); + } + `, + ` + declare const asyncFunction: () => Promise; + async function* test1() { + yield asyncFunction(); + } + `, + ` + async function* test1() { + yield new Promise(() => {}); + } + `, ], invalid: [ From 3a1c869de4ad0d69a56648e293bda9f72d683d50 Mon Sep 17 00:00:00 2001 From: auvred Date: Tue, 28 Nov 2023 17:57:59 +0000 Subject: [PATCH 2/2] chore: refactor `visitYieldExpression` a bit --- .../eslint-plugin/src/rules/require-await.ts | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/packages/eslint-plugin/src/rules/require-await.ts b/packages/eslint-plugin/src/rules/require-await.ts index 95685f0dff55..dd033c7cbe2d 100644 --- a/packages/eslint-plugin/src/rules/require-await.ts +++ b/packages/eslint-plugin/src/rules/require-await.ts @@ -128,24 +128,25 @@ export default createRule({ return; } - if (node.delegate) { - const type = services.getTypeAtLocation(node.argument); - const typesToCheck = expandUnionOrIntersectionType(type); - for (const type of typesToCheck) { - const asyncIterator = tsutils.getWellKnownSymbolPropertyOfType( - type, - 'asyncIterator', - checker, - ); - if (asyncIterator !== undefined) { - scopeInfo.isAsyncYield = true; - break; - } + if (!node.delegate) { + if (isThenableType(services.esTreeNodeToTSNodeMap.get(node.argument))) { + scopeInfo.isAsyncYield = true; + } + return; + } + + const type = services.getTypeAtLocation(node.argument); + const typesToCheck = expandUnionOrIntersectionType(type); + for (const type of typesToCheck) { + const asyncIterator = tsutils.getWellKnownSymbolPropertyOfType( + type, + 'asyncIterator', + checker, + ); + if (asyncIterator !== undefined) { + scopeInfo.isAsyncYield = true; + break; } - } else if ( - isThenableType(services.esTreeNodeToTSNodeMap.get(node.argument)) - ) { - scopeInfo.isAsyncYield = true; } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy