From 459ee7274e6c42cc85bd1fa37da675fbe3f3a57f Mon Sep 17 00:00:00 2001 From: Blair Zajac Date: Mon, 2 Apr 2018 23:09:49 -0700 Subject: [PATCH] promise-function-async: add options to specify function types to check. This is useful if you only want your function and method declarations to warn, or in other words, one doesn't want each arrow function that is passed to Promise#then() or Promise#catch() to be async. --- src/rules/promiseFunctionAsyncRule.ts | 86 +++++++++++++++++++++------ 1 file changed, 69 insertions(+), 17 deletions(-) diff --git a/src/rules/promiseFunctionAsyncRule.ts b/src/rules/promiseFunctionAsyncRule.ts index 582ea323a41..ab39ba00c74 100644 --- a/src/rules/promiseFunctionAsyncRule.ts +++ b/src/rules/promiseFunctionAsyncRule.ts @@ -19,6 +19,32 @@ import { hasModifier } from "tsutils"; import * as ts from "typescript"; import * as Lint from "../index"; +const OPTION_FUNCTION_DECLARATION = "check-function-declaration"; +const OPTION_FUNCTION_EXPRESSION = "check-function-expression"; +const OPTION_ARROW_FUNCTION = "check-arrow-function"; +const OPTION_METHOD_DECLARATION = "check-method-declaration"; + +const KIND_FOR_OPTION: { [arg: string]: number } = { + [OPTION_FUNCTION_DECLARATION]: ts.SyntaxKind.FunctionDeclaration, + [OPTION_FUNCTION_EXPRESSION]: ts.SyntaxKind.FunctionExpression, + [OPTION_ARROW_FUNCTION]: ts.SyntaxKind.ArrowFunction, + [OPTION_METHOD_DECLARATION]: ts.SyntaxKind.MethodDeclaration, +}; + +type EnabledSyntaxKinds = ReadonlySet; + +function parseOptions(ruleArguments: string[]): EnabledSyntaxKinds { + if (ruleArguments.length === 0) { + ruleArguments = Object.keys(KIND_FOR_OPTION); + } + + const enabledKinds = new Set(); + for (const arg of ruleArguments) { + enabledKinds.add(KIND_FOR_OPTION[arg]); + } + return enabledKinds; +} + export class Rule extends Lint.Rules.TypedRule { /* tslint:disable:object-literal-sort-keys */ public static metadata: Lint.IRuleMetadata = { @@ -29,10 +55,32 @@ export class Rule extends Lint.Rules.TypedRule { throwing an Error object. In contrast, non-\`async\` \`Promise\`-returning functions are technically capable of either. This practice removes a requirement for consuming code to handle both cases. + + If no optional arguments are provided then all function types are checked, + otherwise the specific function types are checked: + + * \`"${OPTION_FUNCTION_DECLARATION}"\` check function declarations. + * \`"${OPTION_FUNCTION_EXPRESSION}"\` check function expressions. + * \`"${OPTION_ARROW_FUNCTION}"\` check arrow functions. + * \`"${OPTION_METHOD_DECLARATION}"\` check method declarations. `, optionsDescription: "Not configurable.", - options: null, - optionExamples: [true], + options: { + type: "array", + items: { + type: "string", + enum: [ + OPTION_FUNCTION_DECLARATION, + OPTION_FUNCTION_EXPRESSION, + OPTION_ARROW_FUNCTION, + OPTION_METHOD_DECLARATION, + ], + }, + minLength: 0, + maxLength: 4, + }, + optionExamples: [true, + [true, OPTION_FUNCTION_DECLARATION, OPTION_METHOD_DECLARATION]], type: "typescript", typescriptOnly: false, requiresTypeInfo: true, @@ -42,24 +90,28 @@ export class Rule extends Lint.Rules.TypedRule { public static FAILURE_STRING = "functions that return promises must be async"; public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] { - return this.applyWithFunction(sourceFile, walk, undefined, program.getTypeChecker()); + return this.applyWithFunction(sourceFile, walk, parseOptions(this.ruleArguments), program.getTypeChecker()); } } -function walk(ctx: Lint.WalkContext, tc: ts.TypeChecker) { - return ts.forEachChild(ctx.sourceFile, function cb(node): void { - switch (node.kind) { - case ts.SyntaxKind.MethodDeclaration: - case ts.SyntaxKind.FunctionDeclaration: - if ((node as ts.FunctionLikeDeclaration).body === undefined) { - break; - } - // falls through - case ts.SyntaxKind.FunctionExpression: - case ts.SyntaxKind.ArrowFunction: - if (!hasModifier(node.modifiers, ts.SyntaxKind.AsyncKeyword) && returnsPromise(node as ts.FunctionLikeDeclaration, tc)) { - ctx.addFailure(node.getStart(ctx.sourceFile), (node as ts.FunctionLikeDeclaration).body!.pos, Rule.FAILURE_STRING); - } +function walk(ctx: Lint.WalkContext, tc: ts.TypeChecker) { + const { sourceFile, options } = ctx; + return ts.forEachChild(sourceFile, function cb(node): void { + if (options.has(node.kind)) { + switch (node.kind) { + case ts.SyntaxKind.MethodDeclaration: + case ts.SyntaxKind.FunctionDeclaration: + if ((node as ts.FunctionLikeDeclaration).body === undefined) { + break; + } + // falls through + case ts.SyntaxKind.FunctionExpression: + case ts.SyntaxKind.ArrowFunction: + if (!hasModifier(node.modifiers, ts.SyntaxKind.AsyncKeyword) + && returnsPromise(node as ts.FunctionLikeDeclaration, tc)) { + ctx.addFailure(node.getStart(sourceFile), (node as ts.FunctionLikeDeclaration).body!.pos, Rule.FAILURE_STRING); + } + } } return ts.forEachChild(node, cb); });