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 pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy