Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ module.exports = [
| :------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------ | :-- |
| [await-async-events](docs/rules/await-async-events.md) | Enforce promises from async event methods are handled | ![badge-angular][] ![badge-dom][] ![badge-marko][] ![badge-react][] ![badge-svelte][] ![badge-vue][] | | 🔧 |
| [await-async-queries](docs/rules/await-async-queries.md) | Enforce promises from async queries to be handled | ![badge-angular][] ![badge-dom][] ![badge-marko][] ![badge-react][] ![badge-svelte][] ![badge-vue][] | | 🔧 |
| [await-async-utils](docs/rules/await-async-utils.md) | Enforce promises from async utils to be awaited properly | ![badge-angular][] ![badge-dom][] ![badge-marko][] ![badge-react][] ![badge-svelte][] ![badge-vue][] | | |
| [await-async-utils](docs/rules/await-async-utils.md) | Enforce promises from async utils to be awaited properly | ![badge-angular][] ![badge-dom][] ![badge-marko][] ![badge-react][] ![badge-svelte][] ![badge-vue][] | | 🔧 |
| [consistent-data-testid](docs/rules/consistent-data-testid.md) | Ensures consistent usage of `data-testid` | | | |
| [no-await-sync-events](docs/rules/no-await-sync-events.md) | Disallow unnecessary `await` for sync events | ![badge-angular][] ![badge-dom][] ![badge-react][] | | |
| [no-await-sync-queries](docs/rules/no-await-sync-queries.md) | Disallow unnecessary `await` for sync queries | ![badge-angular][] ![badge-dom][] ![badge-marko][] ![badge-react][] ![badge-svelte][] ![badge-vue][] | | 🔧 |
Expand Down
2 changes: 2 additions & 0 deletions docs/rules/await-async-utils.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

💼 This rule is enabled in the following configs: `angular`, `dom`, `marko`, `react`, `svelte`, `vue`.

🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).

<!-- end auto-generated rule header -->

Ensure that promises returned by async utils are handled properly.
Expand Down
52 changes: 51 additions & 1 deletion lib/rules/await-async-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@ import { ASTUtils } from '@typescript-eslint/utils';
import { createTestingLibraryRule } from '../create-testing-library-rule';
import {
findClosestCallExpressionNode,
findClosestFunctionExpressionNode,
getDeepestIdentifierNode,
getFunctionName,
getInnermostReturningFunction,
getReferenceNode,
getVariableReferences,
isCallExpression,
isObjectPattern,
isPromiseHandled,
isProperty,
} from '../node-utils';

import type { TSESTree } from '@typescript-eslint/utils';
import type { TSESLint, TSESTree } from '@typescript-eslint/utils';

export const RULE_NAME = 'await-async-utils';
export type MessageIds = 'asyncUtilWrapper' | 'awaitAsyncUtil';
Expand All @@ -40,6 +42,7 @@ export default createTestingLibraryRule<Options, MessageIds>({
'Promise returned from {{ name }} wrapper over async util must be handled',
},
schema: [],
fixable: 'code',
},
defaultOptions: [],

Expand Down Expand Up @@ -90,6 +93,33 @@ export default createTestingLibraryRule<Options, MessageIds>({
}
}
}
function wrapWithFunctionExpressionFix(
fixer: TSESLint.RuleFixer,
ruleFix: TSESLint.RuleFix,
functionExpression:
| TSESTree.ArrowFunctionExpression
| TSESTree.FunctionDeclaration
| TSESTree.FunctionExpression
| null
) {
if (functionExpression && !functionExpression.async) {
/**
* Mutate the actual node so if other nodes exist in this
* function expression body they don't also try to fix it.
*/
functionExpression.async = true;

return [ruleFix, fixer.insertTextBefore(functionExpression, 'async ')];
}
return ruleFix;
}

function insertAwaitBeforeNode(
fixer: TSESLint.RuleFixer,
node: TSESTree.Node
) {
return fixer.insertTextBefore(node, 'await ');
}

/*
Either we report a direct usage of an async util or a usage of a wrapper
Expand Down Expand Up @@ -155,6 +185,7 @@ export default createTestingLibraryRule<Options, MessageIds>({
context,
closestCallExpression.parent
);
const functionExpression = findClosestFunctionExpressionNode(node);

if (references.length === 0) {
if (!isPromiseHandled(callExpressionIdentifier)) {
Expand All @@ -164,6 +195,17 @@ export default createTestingLibraryRule<Options, MessageIds>({
data: {
name: callExpressionIdentifier.name,
},
fix: (fixer) => {
const referenceNode = getReferenceNode(
callExpressionIdentifier
);
const awaitFix = insertAwaitBeforeNode(fixer, referenceNode);
return wrapWithFunctionExpressionFix(
fixer,
awaitFix,
functionExpression
);
},
});
}
} else {
Expand All @@ -176,6 +218,14 @@ export default createTestingLibraryRule<Options, MessageIds>({
data: {
name: callExpressionIdentifier.name,
},
fix: (fixer) => {
const awaitFix = insertAwaitBeforeNode(fixer, referenceNode);
return wrapWithFunctionExpressionFix(
fixer,
awaitFix,
functionExpression
);
},
});
return;
}
Expand Down
Loading