From e132cac2e4decb760e07d773b309ac097f3e723a Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 2 Feb 2019 23:56:34 -0500 Subject: [PATCH 1/7] feat: (eslint-plugin) added new rule await-promise Adds the equivalent of TSLint's `promise-function-async` rule. --- packages/eslint-plugin/README.md | 1 + packages/eslint-plugin/ROADMAP.md | 26 +- .../docs/rules/promise-function-async.md | 105 +++++++ .../lib/rules/promise-function-async.js | 127 ++++++++ packages/eslint-plugin/lib/util.js | 34 +++ .../tests/lib/rules/promise-function-async.js | 275 ++++++++++++++++++ 6 files changed, 555 insertions(+), 13 deletions(-) create mode 100644 packages/eslint-plugin/docs/rules/promise-function-async.md create mode 100644 packages/eslint-plugin/lib/rules/promise-function-async.js create mode 100644 packages/eslint-plugin/tests/lib/rules/promise-function-async.js diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 1ec6df216048..ca5e51a1321b 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -128,6 +128,7 @@ Install [`eslint-config-prettier`](https://github.com/prettier/eslint-config-pre | [`@typescript-eslint/no-var-requires`](./docs/rules/no-var-requires.md) | Disallows the use of require statements except in import statements (`no-var-requires` from TSLint) | :heavy_check_mark: | | | [`@typescript-eslint/prefer-interface`](./docs/rules/prefer-interface.md) | Prefer an interface declaration over a type literal (type T = { ... }) (`interface-over-type-literal` from TSLint) | :heavy_check_mark: | :wrench: | | [`@typescript-eslint/prefer-namespace-keyword`](./docs/rules/prefer-namespace-keyword.md) | Require the use of the `namespace` keyword instead of the `module` keyword to declare custom TypeScript modules. (`no-internal-module` from TSLint) | :heavy_check_mark: | :wrench: | +| [`@typescript-eslint/promise-function-async`](./docs/rules/promise-function-async.md) | Requires any function or method that returns a Promise to be marked async. (`promise-function-async` from TSLint) | :heavy_check_mark: | | | [`@typescript-eslint/restrict-plus-operands`](./docs/rules/restrict-plus-operands.md) | When adding two variables, operands must both be of type number or of type string. (`restrict-plus-operands` from TSLint) | | | | [`@typescript-eslint/type-annotation-spacing`](./docs/rules/type-annotation-spacing.md) | Require consistent spacing around type annotations (`typedef-whitespace` from TSLint) | :heavy_check_mark: | :wrench: | diff --git a/packages/eslint-plugin/ROADMAP.md b/packages/eslint-plugin/ROADMAP.md index 378a3140ffb8..d8fe951a8a70 100644 --- a/packages/eslint-plugin/ROADMAP.md +++ b/packages/eslint-plugin/ROADMAP.md @@ -1,9 +1,9 @@ # Roadmap -✅ (28) = done -🌟 (79) = in ESLint core -🔌 (33) = in another plugin -🌓 (16) = implementations differ or ESLint version is missing functionality +✅ (28) = done +🌟 (79) = in ESLint core +🔌 (33) = in another plugin +🌓 (16) = implementations differ or ESLint version is missing functionality 🛑 (70) = unimplemented ## TSLint rules @@ -30,7 +30,7 @@ | [`no-var-requires`] | ✅ | [`@typescript-eslint/no-var-requires`] | | [`only-arrow-functions`] | 🔌 | [`prefer-arrow/prefer-arrow-functions`] | | [`prefer-for-of`] | 🛑 | N/A | -| [`promise-function-async`] | 🛑 | N/A ([relevant plugin][plugin:promise]) | +| [`promise-function-async`] | ✅ | [`@typescript-eslint/promise-function-async`] | | [`typedef`] | 🛑 | N/A | | [`typedef-whitespace`] | ✅ | [`@typescript-eslint/type-annotation-spacing`] | | [`unified-signatures`] | 🛑 | N/A | @@ -96,7 +96,7 @@ | [`use-default-type-parameter`] | 🛑 | N/A | | [`use-isnan`] | 🌟 | [`use-isnan`][use-isnan] | -[1] The ESLint rule also supports silencing with an extra set of parens (`if ((foo = bar)) {}`) +[1] The ESLint rule also supports silencing with an extra set of parens (`if ((foo = bar)) {}`) [2] Missing private class member support. [`@typescript-eslint/no-unused-vars`] adds support for some TS-specific features. ### Maintainability @@ -120,7 +120,7 @@ | [`prefer-readonly`] | 🛑 | N/A | | [`trailing-comma`] | 🌓 | [`comma-dangle`][comma-dangle] or [Prettier] | -[1] Only warns when importing deprecated symbols +[1] Only warns when importing deprecated symbols [2] Missing support for blank-line-delimited sections ### Style @@ -179,7 +179,7 @@ | [`variable-name`] | 🌟 | [2] | | [`whitespace`] | 🔌 | Use [Prettier] | -[1] Recommended config: `["error", { blankLine: "always", prev: "*", next: "return" }]` +[1] Recommended config: `["error", { blankLine: "always", prev: "*", next: "return" }]` [2] [`camelcase`][camelcase], [`no-underscore-dangle`][no-underscore-dangle], [`id-blacklist`][id-blacklist], and/or [`id-match`][id-match] ## tslint-microsoft-contrib rules @@ -245,10 +245,10 @@ Relevant plugins: [`chai-expect-keywords`](https://github.com/gavinaiken/eslint- | `use-named-parameter` | 🛑 | N/A | | `use-simple-attributes` | 🛑 | N/A | -[1] Enforces blank lines both at the beginning and end of a block -[2] Recommended config: `["error", "ForInStatement"]` -[3] Recommended config: `["error", "declaration", { "allowArrowFunctions": true }]` -[4] Recommended config: `["error", { "terms": ["BUG", "HACK", "FIXME", "LATER", "LATER2", "TODO"], "location": "anywhere" }]` +[1] Enforces blank lines both at the beginning and end of a block +[2] Recommended config: `["error", "ForInStatement"]` +[3] Recommended config: `["error", "declaration", { "allowArrowFunctions": true }]` +[4] Recommended config: `["error", { "terms": ["BUG", "HACK", "FIXME", "LATER", "LATER2", "TODO"], "location": "anywhere" }]` [5] Does not check class fields. [insecure-random]: https://github.com/desktop/desktop/blob/master/eslint-rules/insecure-random.js @@ -310,7 +310,7 @@ Relevant plugins: [`chai-expect-keywords`](https://github.com/gavinaiken/eslint- | `react-a11y-titles` | 🛑 | N/A | | `react-anchor-blank-noopener` | 🛑 | N/A | -[1] TSLint rule is more strict +[1] TSLint rule is more strict [2] ESLint rule only reports for click handlers [prettier]: https://prettier.io diff --git a/packages/eslint-plugin/docs/rules/promise-function-async.md b/packages/eslint-plugin/docs/rules/promise-function-async.md new file mode 100644 index 000000000000..056aabe670f6 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/promise-function-async.md @@ -0,0 +1,105 @@ +# Functions that return promises must be async (promise-function-async) + +Requires any function or method that returns a Promise to be marked async. +Ensures that each function is only capable of: + +- returning a rejected promise, or +- throwing an Error object. + +In contrast, non-`async` `Promise`-returning functions are technically capable of either. +Code that handles the results of those functions will often need to handle both cases, which can get complex. +This rule's practice removes a requirement for creating code to handle both cases. + +## Rule Details + +Examples of **incorrect** code for this rule + +```ts +const arrowFunctionReturnsPromise = () => Promise.resolve('value'); + +function functionDeturnsPromise() { + return Math.random() > 0.5 ? Promise.resolve('value') : false; +} + +const someObject = { + functionExpressionReturnsPromise() { + if (Math.random() > 0.5) { + return Promise.resolve('value'); + } + + return undefined; + } +}; + +class ContainsMethod { + methodDeclarationReturnsPromise(condition: boolean, value: string) { + if (condition) { + return undefined; + } + + return Promise.resolve(value); + } +} +``` + +Examples of **correct** code for this rule + +```ts +const arrowFunctionReturnsPromise = async () => 'value'; + +async function functionDeturnsPromise() { + return Math.random() > 0.5 ? 'value' : false; +} + +const someObject = { + functionExpressionReturnsPromise: async function() { + if (Math.random() > 0.5) { + return 'value'; + } + + return undefined; + } +}; + +class ContainsMethod { + async methodDeclarationReturnsPromise(condition: boolean, value: string) { + if (condition) { + return undefined; + } + + return value; + } +} +``` + +## Options + +Options may be provided as an object with: + +- `allowedPromiseNames` to indicate any extra names of classes or interfaces to be considered Promises when returned. + +In addition, each of the following properties may be provided, and default to `true`: + +- `checkArrowFunctions` +- `checkFunctionDeclarations` +- `checkFunctionExpressions` +- `checkMethodDeclarations` + +```json +{ + "@typescript-eslint/promise-function-async": [ + "error", + { + "allowedPromiseNames": ["Thenable"], + "checkArrowFunctions": true, + "checkFunctionDeclarations": true, + "checkFunctionExpressions": true, + "checkMethodDeclarations": true + } + ] +} +``` + +## Related To + +- TSLint: [promise-function-async](https://palantir.github.io/tslint/rules/promise-function-async) diff --git a/packages/eslint-plugin/lib/rules/promise-function-async.js b/packages/eslint-plugin/lib/rules/promise-function-async.js new file mode 100644 index 000000000000..3ef54e0e248f --- /dev/null +++ b/packages/eslint-plugin/lib/rules/promise-function-async.js @@ -0,0 +1,127 @@ +/** + * @fileoverview Requires any function or method that returns a Promise to be marked async + * @author Josh Goldberg + */ +'use strict'; + +const util = require('../util'); + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +const defaultOptions = [ + { + allowedPromiseNames: [], + checkArrowFunctions: true, + checkFunctionDeclarations: true, + checkFunctionExpressions: true, + checkMethodDeclarations: true + } +]; + +/** + * @type {import("eslint").Rule.RuleModule} + */ +module.exports = { + meta: { + type: 'suggestion', + docs: { + description: + 'Requires any function or method that returns a Promise to be marked async.', + extraDescription: [util.tslintRule('promise-function-async')], + category: 'TypeScript', + url: util.metaDocsUrl('promise-function-async'), + recommended: 'error' + }, + fixable: null, + messages: { + missingAsync: 'Functions that return promises must be async.' + }, + schema: [ + { + type: 'object', + properties: { + allowedPromiseNames: { + type: 'array', + items: { + type: 'string' + } + }, + checkArrowFunctions: { + type: 'boolean' + }, + checkFunctionDeclarations: { + type: 'boolean' + }, + checkFunctionExpressions: { + type: 'boolean' + }, + checkMethodDeclarations: { + type: 'boolean' + } + }, + additionalProperties: false + } + ] + }, + + create(context) { + const { + allowedPromiseNames, + checkArrowFunctions, + checkFunctionDeclarations, + checkFunctionExpressions, + checkMethodDeclarations + } = util.applyDefault(defaultOptions, context.options)[0]; + + const allAllowedPromiseNames = new Set(['Promise', ...allowedPromiseNames]); + const parserServices = util.getParserServices(context); + const checker = parserServices.program.getTypeChecker(); + + /** + * @param {import("estree").Function} node + */ + function validateNode(node) { + const originalNode = parserServices.esTreeNodeToTSNodeMap.get(node); + const [callSignature] = checker + .getTypeAtLocation(originalNode) + .getCallSignatures(); + const returnType = checker.getReturnTypeOfSignature(callSignature); + + if (!util.containsTypeByName(returnType, allAllowedPromiseNames)) { + return; + } + + context.report({ + messageId: 'missingAsync', + node + }); + } + + return { + ArrowFunctionExpression(node) { + if (checkArrowFunctions && !node.async) { + validateNode(node); + } + }, + FunctionDeclaration(node) { + if (checkFunctionDeclarations && !node.async) { + validateNode(node); + } + }, + FunctionExpression(node) { + if ( + typeof node.parent !== 'undefined' && + node.parent.kind === 'method' + ) { + if (checkMethodDeclarations && !node.async) { + validateNode(node.parent); + } + } else if (checkFunctionExpressions && !node.async) { + validateNode(node); + } + } + }; + } +}; diff --git a/packages/eslint-plugin/lib/util.js b/packages/eslint-plugin/lib/util.js index dac41fe21bb7..4f283c205882 100644 --- a/packages/eslint-plugin/lib/util.js +++ b/packages/eslint-plugin/lib/util.js @@ -1,5 +1,8 @@ 'use strict'; +const tsutils = require('tsutils'); +const ts = require('typescript'); + const version = require('../package.json').version; exports.tslintRule = name => `\`${name}\` from TSLint`; @@ -127,3 +130,34 @@ exports.getParserServices = context => { } return context.parserServices; }; + +/** + * @param {string} type Type to check the name of. + * @param {Set} allowedNames Symbol names being checked for. + */ +exports.containsTypeByName = (type, allowedNames) => { + if (tsutils.isTypeFlagSet(type, ts.TypeFlags.Any | ts.TypeFlags.Unknown)) { + return true; + } + + if (tsutils.isTypeReference(type)) { + type = type.target; + } + + if ( + typeof type.symbol !== 'undefined' && + allowedNames.has(type.symbol.name) + ) { + return true; + } + + if (tsutils.isUnionOrIntersectionType(type)) { + return type.types.some(t => containsType(t, allowedNames)); + } + + const bases = type.getBaseTypes(); + return ( + typeof bases !== 'undefined' && + bases.some(t => containsType(t, allowedNames)) + ); +}; diff --git a/packages/eslint-plugin/tests/lib/rules/promise-function-async.js b/packages/eslint-plugin/tests/lib/rules/promise-function-async.js new file mode 100644 index 000000000000..dddb26046435 --- /dev/null +++ b/packages/eslint-plugin/tests/lib/rules/promise-function-async.js @@ -0,0 +1,275 @@ +/** + * @fileoverview Requires any function or method that returns a Promise to be marked async + * @author Josh Goldberg + */ +'use strict'; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require('../../../lib/rules/promise-function-async'), + RuleTester = require('eslint').RuleTester, + path = require('path'); + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const rootDir = path.join(process.cwd(), 'tests/fixtures/'); +const parserOptions = { + ecmaVersion: 2018, + tsconfigRootDir: rootDir, + project: './tsconfig.json' +}; + +const messageId = 'missingAsync'; + +const ruleTester = new RuleTester({ + parserOptions, + parser: '@typescript-eslint/parser' +}); + +ruleTester.run('promise-function-async', rule, { + valid: [ + ` +const nonAsyncNonPromiseArrowFunction = (n: number) => n; + +function nonAsyncNonPromiseFunctionDeclaration(n: number) { return n; } + +const asyncPromiseFunctionExpressionA = async function(p: Promise) { return p; }; +const asyncPromiseFunctionExpressionB = async function() { return new Promise(); }; + +class Test { + public nonAsyncNonPromiseArrowFunction = (n: number) => n; + + public nonAsyncNonPromiseMethod() { + return 0; + } + + public async asyncPromiseMethodA(p: Promise) { + return p; + } + + public async asyncPromiseMethodB() { + return new Promise(); + } +} +` + ], + invalid: [ + { + code: ` +const nonAsyncPromiseFunctionExpressionA = function(p: Promise) { return p; }; + +const nonAsyncPromiseFunctionExpressionB = function() { return new Promise(); }; + +function nonAsyncPromiseFunctionDeclarationA(p: Promise) { return p; } + +function nonAsyncPromiseFunctionDeclarationB() { return new Promise(); } + +const nonAsyncPromiseArrowFunctionA = (p: Promise) => p; + +const nonAsyncPromiseArrowFunctionB = () => new Promise(); + +class Test { + public nonAsyncPromiseMethodA(p: Promise) { + return p; + } + + public nonAsyncPromiseMethodB() { + return new Promise(); + } +} +`, + errors: [ + { + line: 2, + messageId + }, + { + line: 4, + messageId + }, + { + line: 6, + messageId + }, + { + line: 8, + messageId + }, + { + line: 10, + messageId + }, + { + line: 12, + messageId + }, + { + line: 15, + messageId + }, + { + line: 19, + messageId + } + ] + }, + { + code: ` +const nonAsyncPromiseFunctionExpression = function(p: Promise) { return p; }; + +function nonAsyncPromiseFunctionDeclaration(p: Promise) { return p; } + +const nonAsyncPromiseArrowFunction = (p: Promise) => p; + +class Test { + public nonAsyncPromiseMethod(p: Promise) { + return p; + } +} +`, + options: [ + { + checkArrowFunctions: false + } + ], + errors: [ + { + line: 2, + messageId + }, + { + line: 4, + messageId + }, + { + line: 9, + messageId + } + ] + }, + { + code: ` +const nonAsyncPromiseFunctionExpression = function(p: Promise) { return p; }; + +function nonAsyncPromiseFunctionDeclaration(p: Promise) { return p; } + +const nonAsyncPromiseArrowFunction = (p: Promise) => p; + +class Test { + public nonAsyncPromiseMethod(p: Promise) { + return p; + } +} +`, + options: [ + { + checkFunctionDeclarations: false + } + ], + errors: [ + { + line: 2, + messageId + }, + { + line: 6, + messageId + }, + { + line: 9, + messageId + } + ] + }, + { + code: ` +const nonAsyncPromiseFunctionExpression = function(p: Promise) { return p; }; + +function nonAsyncPromiseFunctionDeclaration(p: Promise) { return p; } + +const nonAsyncPromiseArrowFunction = (p: Promise) => p; + +class Test { + public nonAsyncPromiseMethod(p: Promise) { + return p; + } +} +`, + options: [ + { + checkFunctionExpressions: false + } + ], + errors: [ + { + line: 4, + messageId + }, + { + line: 6, + messageId + }, + { + line: 9, + messageId + } + ] + }, + { + code: ` +const nonAsyncPromiseFunctionExpression = function(p: Promise) { return p; }; + +function nonAsyncPromiseFunctionDeclaration(p: Promise) { return p; } + +const nonAsyncPromiseArrowFunction = (p: Promise) => p; + +class Test { + public nonAsyncPromiseMethod(p: Promise) { + return p; + } +} +`, + options: [ + { + checkMethodDeclarations: false + } + ], + errors: [ + { + line: 2, + messageId + }, + { + line: 4, + messageId + }, + { + line: 6, + messageId + } + ] + }, + { + code: ` +class PromiseType { } + +const returnAllowedType = () => new PromiseType(); +`, + options: [ + { + allowedPromiseNames: ['PromiseType'] + } + ], + errors: [ + { + line: 4, + messageId + } + ] + } + ] +}); From 336d231696bc9b121e67f0e5ebe2fe15d693947f Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sun, 3 Feb 2019 09:42:57 -0500 Subject: [PATCH 2/7] Merge branch master; moved util to its own file --- .../lib/rules/promise-function-async.js | 3 +- packages/eslint-plugin/lib/util.js | 34 ------------------ packages/eslint-plugin/lib/utils/types.js | 36 +++++++++++++++++++ 3 files changed, 38 insertions(+), 35 deletions(-) create mode 100644 packages/eslint-plugin/lib/utils/types.js diff --git a/packages/eslint-plugin/lib/rules/promise-function-async.js b/packages/eslint-plugin/lib/rules/promise-function-async.js index 3ef54e0e248f..f0223acabb9f 100644 --- a/packages/eslint-plugin/lib/rules/promise-function-async.js +++ b/packages/eslint-plugin/lib/rules/promise-function-async.js @@ -5,6 +5,7 @@ 'use strict'; const util = require('../util'); +const types = require('../utils/types'); //------------------------------------------------------------------------------ // Rule Definition @@ -89,7 +90,7 @@ module.exports = { .getCallSignatures(); const returnType = checker.getReturnTypeOfSignature(callSignature); - if (!util.containsTypeByName(returnType, allAllowedPromiseNames)) { + if (!types.containsTypeByName(returnType, allAllowedPromiseNames)) { return; } diff --git a/packages/eslint-plugin/lib/util.js b/packages/eslint-plugin/lib/util.js index 4f283c205882..dac41fe21bb7 100644 --- a/packages/eslint-plugin/lib/util.js +++ b/packages/eslint-plugin/lib/util.js @@ -1,8 +1,5 @@ 'use strict'; -const tsutils = require('tsutils'); -const ts = require('typescript'); - const version = require('../package.json').version; exports.tslintRule = name => `\`${name}\` from TSLint`; @@ -130,34 +127,3 @@ exports.getParserServices = context => { } return context.parserServices; }; - -/** - * @param {string} type Type to check the name of. - * @param {Set} allowedNames Symbol names being checked for. - */ -exports.containsTypeByName = (type, allowedNames) => { - if (tsutils.isTypeFlagSet(type, ts.TypeFlags.Any | ts.TypeFlags.Unknown)) { - return true; - } - - if (tsutils.isTypeReference(type)) { - type = type.target; - } - - if ( - typeof type.symbol !== 'undefined' && - allowedNames.has(type.symbol.name) - ) { - return true; - } - - if (tsutils.isUnionOrIntersectionType(type)) { - return type.types.some(t => containsType(t, allowedNames)); - } - - const bases = type.getBaseTypes(); - return ( - typeof bases !== 'undefined' && - bases.some(t => containsType(t, allowedNames)) - ); -}; diff --git a/packages/eslint-plugin/lib/utils/types.js b/packages/eslint-plugin/lib/utils/types.js new file mode 100644 index 000000000000..246e71cb5f56 --- /dev/null +++ b/packages/eslint-plugin/lib/utils/types.js @@ -0,0 +1,36 @@ +'use strict'; + +const tsutils = require('tsutils'); +const ts = require('typescript'); + +/** + * @param {string} type Type being checked by name. + * @param {Set} allowedNames Symbol names checking on the type. + * @returns {boolean} Whether the type is, extends, or contains any of the allowed names. + */ +exports.containsTypeByName = (type, allowedNames) => { + if (tsutils.isTypeFlagSet(type, ts.TypeFlags.Any | ts.TypeFlags.Unknown)) { + return true; + } + + if (tsutils.isTypeReference(type)) { + type = type.target; + } + + if ( + typeof type.symbol !== 'undefined' && + allowedNames.has(type.symbol.name) + ) { + return true; + } + + if (tsutils.isUnionOrIntersectionType(type)) { + return type.types.some(t => containsTypeByName(t, allowedNames)); + } + + const bases = type.getBaseTypes(); + return ( + typeof bases !== 'undefined' && + bases.some(t => containsTypeByName(t, allowedNames)) + ); +}; From eed13104ea28ab5a2508fb08bfa936d4754a7250 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sun, 3 Feb 2019 09:56:25 -0500 Subject: [PATCH 3/7] Corrected containsTypeByName to be able to reference itself --- packages/eslint-plugin/lib/utils/types.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin/lib/utils/types.js b/packages/eslint-plugin/lib/utils/types.js index 246e71cb5f56..adfb402b72ae 100644 --- a/packages/eslint-plugin/lib/utils/types.js +++ b/packages/eslint-plugin/lib/utils/types.js @@ -8,7 +8,7 @@ const ts = require('typescript'); * @param {Set} allowedNames Symbol names checking on the type. * @returns {boolean} Whether the type is, extends, or contains any of the allowed names. */ -exports.containsTypeByName = (type, allowedNames) => { +function containsTypeByName(type, allowedNames) { if (tsutils.isTypeFlagSet(type, ts.TypeFlags.Any | ts.TypeFlags.Unknown)) { return true; } @@ -33,4 +33,6 @@ exports.containsTypeByName = (type, allowedNames) => { typeof bases !== 'undefined' && bases.some(t => containsTypeByName(t, allowedNames)) ); -}; +} + +exports.containsTypeByName = containsTypeByName; From 0d852133bfe4391b43c10b3bc7760713be1df333 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sun, 3 Feb 2019 13:50:46 -0500 Subject: [PATCH 4/7] Fixed more merge conflicts... --- packages/eslint-plugin/ROADMAP.md | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/packages/eslint-plugin/ROADMAP.md b/packages/eslint-plugin/ROADMAP.md index 97fef7a8a53a..2617becdf768 100644 --- a/packages/eslint-plugin/ROADMAP.md +++ b/packages/eslint-plugin/ROADMAP.md @@ -245,19 +245,11 @@ Relevant plugins: [`chai-expect-keywords`](https://github.com/gavinaiken/eslint- | `use-named-parameter` | 🛑 | N/A | | `use-simple-attributes` | 🛑 | N/A | -<<<<<<< HEAD -[1] Enforces blank lines both at the beginning and end of a block -[2] Recommended config: `["error", "ForInStatement"]` -[3] Recommended config: `["error", "declaration", { "allowArrowFunctions": true }]` -[4] Recommended config: `["error", { "terms": ["BUG", "HACK", "FIXME", "LATER", "LATER2", "TODO"], "location": "anywhere" }]` -======= [1] Enforces blank lines both at the beginning and end of a block
[2] Recommended config: `["error", "ForInStatement"]`
[3] Recommended config: `["error", "declaration", { "allowArrowFunctions": true }]`
[4] Recommended config: `["error", { "terms": ["BUG", "HACK", "FIXME", "LATER", "LATER2", "TODO"], "location": "anywhere" }]`
- -> > > > > > > master -> > > > > > > [5] Does not check class fields. +[5] Does not check class fields. [insecure-random]: https://github.com/desktop/desktop/blob/master/eslint-rules/insecure-random.js @@ -318,13 +310,8 @@ Relevant plugins: [`chai-expect-keywords`](https://github.com/gavinaiken/eslint- | `react-a11y-titles` | 🛑 | N/A | | `react-anchor-blank-noopener` | 🛑 | N/A | -<<<<<<< HEAD -[1] TSLint rule is more strict -======= [1] TSLint rule is more strict
- -> > > > > > > master -> > > > > > > [2] ESLint rule only reports for click handlers +[2] ESLint rule only reports for click handlers [prettier]: https://prettier.io From c45499ee4bbc6bed3ec1e375416c4702dc7298a9 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Mon, 4 Feb 2019 21:16:40 -0500 Subject: [PATCH 5/7] Update packages/eslint-plugin/lib/rules/promise-function-async.js Co-Authored-By: JoshuaKGoldberg --- packages/eslint-plugin/lib/rules/promise-function-async.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/lib/rules/promise-function-async.js b/packages/eslint-plugin/lib/rules/promise-function-async.js index f0223acabb9f..7670000a5576 100644 --- a/packages/eslint-plugin/lib/rules/promise-function-async.js +++ b/packages/eslint-plugin/lib/rules/promise-function-async.js @@ -113,7 +113,7 @@ module.exports = { }, FunctionExpression(node) { if ( - typeof node.parent !== 'undefined' && + !!node.parent && node.parent.kind === 'method' ) { if (checkMethodDeclarations && !node.async) { From 1a4d17df82969835a46a0967e3b77efda329fb79 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Mon, 4 Feb 2019 21:28:49 -0500 Subject: [PATCH 6/7] Fixed formatting post-suggestion --- packages/eslint-plugin/lib/rules/promise-function-async.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/eslint-plugin/lib/rules/promise-function-async.js b/packages/eslint-plugin/lib/rules/promise-function-async.js index 7670000a5576..febbbc2a012b 100644 --- a/packages/eslint-plugin/lib/rules/promise-function-async.js +++ b/packages/eslint-plugin/lib/rules/promise-function-async.js @@ -112,10 +112,7 @@ module.exports = { } }, FunctionExpression(node) { - if ( - !!node.parent && - node.parent.kind === 'method' - ) { + if (!!node.parent && node.parent.kind === 'method') { if (checkMethodDeclarations && !node.async) { validateNode(node.parent); } From a1f4554f23521cdfaf866838d07e8e03df24b0ca Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Mon, 4 Feb 2019 21:29:48 -0500 Subject: [PATCH 7/7] Simplified explanations --- .../docs/rules/promise-function-async.md | 40 ------------------- 1 file changed, 40 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/promise-function-async.md b/packages/eslint-plugin/docs/rules/promise-function-async.md index 056aabe670f6..c64c25c11b5a 100644 --- a/packages/eslint-plugin/docs/rules/promise-function-async.md +++ b/packages/eslint-plugin/docs/rules/promise-function-async.md @@ -20,26 +20,6 @@ const arrowFunctionReturnsPromise = () => Promise.resolve('value'); function functionDeturnsPromise() { return Math.random() > 0.5 ? Promise.resolve('value') : false; } - -const someObject = { - functionExpressionReturnsPromise() { - if (Math.random() > 0.5) { - return Promise.resolve('value'); - } - - return undefined; - } -}; - -class ContainsMethod { - methodDeclarationReturnsPromise(condition: boolean, value: string) { - if (condition) { - return undefined; - } - - return Promise.resolve(value); - } -} ``` Examples of **correct** code for this rule @@ -50,26 +30,6 @@ const arrowFunctionReturnsPromise = async () => 'value'; async function functionDeturnsPromise() { return Math.random() > 0.5 ? 'value' : false; } - -const someObject = { - functionExpressionReturnsPromise: async function() { - if (Math.random() > 0.5) { - return 'value'; - } - - return undefined; - } -}; - -class ContainsMethod { - async methodDeclarationReturnsPromise(condition: boolean, value: string) { - if (condition) { - return undefined; - } - - return value; - } -} ``` ## Options