From 88f08f410760f58fdc2de58ecd9dab9610821642 Mon Sep 17 00:00:00 2001 From: Alexander T Date: Mon, 20 Jul 2020 08:23:37 +0300 Subject: [PATCH] feat(eslint-plugin): [no-empty-function] add `decoratedFunctions` option (#2295) --- .../docs/rules/no-empty-function.md | 2 +- .../src/rules/no-empty-function.ts | 35 ++++++++++++- .../tests/rules/no-empty-function.test.ts | 50 +++++++++++++++++++ packages/types/src/ts-estree.ts | 1 + 4 files changed, 86 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/no-empty-function.md b/packages/eslint-plugin/docs/rules/no-empty-function.md index df6a3f82583..43dcf5375b0 100644 --- a/packages/eslint-plugin/docs/rules/no-empty-function.md +++ b/packages/eslint-plugin/docs/rules/no-empty-function.md @@ -5,7 +5,7 @@ This rule extends the base [`eslint/no-empty-function`](https://eslint.org/docs/rules/no-empty-function) rule. It adds support for handling TypeScript specific code that would otherwise trigger the rule. -One example of valid TypeScript specific code that would otherwise trigger the `no-empty-function` rule is the use of [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) in constructor functions: +One example of valid TypeScript specific code that would otherwise trigger the `no-empty-function` rule is the use of [parameter properties](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) in constructor functions. ## How to use diff --git a/packages/eslint-plugin/src/rules/no-empty-function.ts b/packages/eslint-plugin/src/rules/no-empty-function.ts index 6dc610941e6..7f5a03a8c39 100644 --- a/packages/eslint-plugin/src/rules/no-empty-function.ts +++ b/packages/eslint-plugin/src/rules/no-empty-function.ts @@ -29,6 +29,7 @@ const schema = util.deepMerge( 'protected-constructors', 'asyncFunctions', 'asyncMethods', + 'decoratedFunctions', ], }, }, @@ -61,6 +62,7 @@ export default util.createRule({ 'protected-constructors', ); const isAllowedPrivateConstructors = allow.includes('private-constructors'); + const isAllowedDecoratedFunctions = allow.includes('decoratedFunctions'); /** * Check if the method body is empty @@ -117,15 +119,46 @@ export default util.createRule({ return false; } + /** + * @param node the node to be validated + * @returns true if a function has decorators + * @private + */ + function isAllowedEmptyDecoratedFunctions( + node: TSESTree.FunctionExpression | TSESTree.FunctionDeclaration, + ): boolean { + if (isAllowedDecoratedFunctions && isBodyEmpty(node)) { + const decorators = + node.type === AST_NODE_TYPES.FunctionDeclaration + ? node.decorators + : node.parent?.type === AST_NODE_TYPES.MethodDefinition + ? node.parent.decorators + : undefined; + return !!decorators && !!decorators.length; + } + + return false; + } + return { ...rules, FunctionExpression(node): void { - if (isAllowedEmptyConstructor(node)) { + if ( + isAllowedEmptyConstructor(node) || + isAllowedEmptyDecoratedFunctions(node) + ) { return; } rules.FunctionExpression(node); }, + FunctionDeclaration(node): void { + if (isAllowedEmptyDecoratedFunctions(node)) { + return; + } + + rules.FunctionDeclaration(node); + }, }; }, }); diff --git a/packages/eslint-plugin/tests/rules/no-empty-function.test.ts b/packages/eslint-plugin/tests/rules/no-empty-function.test.ts index 9ec4d3c3097..258ef3e0ff5 100644 --- a/packages/eslint-plugin/tests/rules/no-empty-function.test.ts +++ b/packages/eslint-plugin/tests/rules/no-empty-function.test.ts @@ -63,6 +63,22 @@ function foo() { } `, }, + { + code: ` +@decorator() +function foo() {} + `, + options: [{ allow: ['decoratedFunctions'] }], + }, + { + code: ` +class Foo { + @decorator() + foo() {} +} + `, + options: [{ allow: ['decoratedFunctions'] }], + }, ], invalid: [ @@ -149,5 +165,39 @@ function foo() {} }, ], }, + { + code: ` +@decorator() +function foo() {} + `, + errors: [ + { + messageId: 'unexpected', + data: { + name: "function 'foo'", + }, + line: 3, + column: 16, + }, + ], + }, + { + code: ` +class Foo { + @decorator() + foo() {} +} + `, + errors: [ + { + messageId: 'unexpected', + data: { + name: "method 'foo'", + }, + line: 4, + column: 9, + }, + ], + }, ], }); diff --git a/packages/types/src/ts-estree.ts b/packages/types/src/ts-estree.ts index b72a1f58b09..527e67623f1 100644 --- a/packages/types/src/ts-estree.ts +++ b/packages/types/src/ts-estree.ts @@ -946,6 +946,7 @@ export interface ForStatement extends BaseNode { export interface FunctionDeclaration extends FunctionDeclarationBase { type: AST_NODE_TYPES.FunctionDeclaration; body: BlockStatement; + decorators?: Decorator[]; } export interface FunctionExpression extends FunctionDeclarationBase {