diff --git a/packages/eslint-plugin/src/rules/promise-function-async.ts b/packages/eslint-plugin/src/rules/promise-function-async.ts index ce624be643c1..68b1d42567c5 100644 --- a/packages/eslint-plugin/src/rules/promise-function-async.ts +++ b/packages/eslint-plugin/src/rules/promise-function-async.ts @@ -1,5 +1,6 @@ import { AST_NODE_TYPES, + AST_TOKEN_TYPES, TSESTree, } from '@typescript-eslint/experimental-utils'; import * as ts from 'typescript'; @@ -156,8 +157,40 @@ export default util.createRule({ (node.parent.type === AST_NODE_TYPES.Property && node.parent.method)) ) { - return fixer.insertTextBefore(node.parent.key, 'async '); + // this function is a class method or object function property shorthand + const method = node.parent; + + // the token to put `async` before + let keyToken = sourceCode.getFirstToken(method)!; + + // if there are decorators then skip past them + if ( + method.type === AST_NODE_TYPES.MethodDefinition && + method.decorators + ) { + const lastDecorator = + method.decorators[method.decorators.length - 1]; + keyToken = sourceCode.getTokenAfter(lastDecorator)!; + } + + // if current token is a keyword like `static` or `public` then skip it + while (keyToken.type === AST_TOKEN_TYPES.Keyword) { + keyToken = sourceCode.getTokenAfter(keyToken)!; + } + + // check if there is a space between key and previous token + const insertSpace = !sourceCode.isSpaceBetween!( + sourceCode.getTokenBefore(keyToken)!, + keyToken, + ); + + let code = 'async '; + if (insertSpace) { + code = ` ${code}`; + } + return fixer.insertTextBefore(keyToken, code); } + return fixer.insertTextBefore(node, 'async '); }, }); diff --git a/packages/eslint-plugin/tests/rules/promise-function-async.test.ts b/packages/eslint-plugin/tests/rules/promise-function-async.test.ts index 961047138c99..6a0451ed8580 100644 --- a/packages/eslint-plugin/tests/rules/promise-function-async.test.ts +++ b/packages/eslint-plugin/tests/rules/promise-function-async.test.ts @@ -1,3 +1,4 @@ +import { noFormat } from '@typescript-eslint/experimental-utils/src/eslint-utils'; import rule from '../../src/rules/promise-function-async'; import { getFixturesRootDir, RuleTester } from '../RuleTester'; @@ -630,6 +631,41 @@ class Test { public async test() { return Promise.resolve(123); } +} + `, + }, + { + code: noFormat` +class Test { + @decorator(async () => {}) + static protected[(1)]() { + return Promise.resolve(1); + } + public'bar'() { + return Promise.resolve(2); + } + private['baz']() { + return Promise.resolve(3); + } +} + `, + errors: [ + { line: 4, column: 3, messageId }, + { line: 7, column: 3, messageId }, + { line: 10, column: 3, messageId }, + ], + output: noFormat` +class Test { + @decorator(async () => {}) + static protected async [(1)]() { + return Promise.resolve(1); + } + public async 'bar'() { + return Promise.resolve(2); + } + private async ['baz']() { + return Promise.resolve(3); + } } `, },