From 8f0ee8908ec3feda681c015cda56f17871f13aa2 Mon Sep 17 00:00:00 2001 From: Clement398 <90264924+Clement398@users.noreply.github.com> Date: Fri, 23 Feb 2024 10:17:19 +0100 Subject: [PATCH] Add `no-single-promise-in-promise-methods` rule (#2258) Co-authored-by: fisker Co-authored-by: Sindre Sorhus --- configs/recommended.js | 1 + .../no-single-promise-in-promise-methods.md | 50 + readme.md | 1 + rules/no-single-promise-in-promise-methods.js | 167 +++ rules/utils/index.js | 1 + ...arentheses-to-await-expression-argument.js | 21 + test/no-single-promise-in-promise-methods.mjs | 104 ++ ...o-single-promise-in-promise-methods.mjs.md | 1128 +++++++++++++++++ ...single-promise-in-promise-methods.mjs.snap | Bin 0 -> 2274 bytes 9 files changed, 1473 insertions(+) create mode 100644 docs/rules/no-single-promise-in-promise-methods.md create mode 100644 rules/no-single-promise-in-promise-methods.js create mode 100644 rules/utils/should-add-parentheses-to-await-expression-argument.js create mode 100644 test/no-single-promise-in-promise-methods.mjs create mode 100644 test/snapshots/no-single-promise-in-promise-methods.mjs.md create mode 100644 test/snapshots/no-single-promise-in-promise-methods.mjs.snap diff --git a/configs/recommended.js b/configs/recommended.js index b4f4e8f9d6..2da3c5e960 100644 --- a/configs/recommended.js +++ b/configs/recommended.js @@ -40,6 +40,7 @@ module.exports = { 'unicorn/no-null': 'error', 'unicorn/no-object-as-default-parameter': 'error', 'unicorn/no-process-exit': 'error', + 'unicorn/no-single-promise-in-promise-methods': 'error', 'unicorn/no-static-only-class': 'error', 'unicorn/no-thenable': 'error', 'unicorn/no-this-assignment': 'error', diff --git a/docs/rules/no-single-promise-in-promise-methods.md b/docs/rules/no-single-promise-in-promise-methods.md new file mode 100644 index 0000000000..c7fe1754d0 --- /dev/null +++ b/docs/rules/no-single-promise-in-promise-methods.md @@ -0,0 +1,50 @@ +# Disallow passing single-element arrays to `Promise` methods + +πŸ’Ό This rule is enabled in the βœ… `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). + +πŸ”§πŸ’‘ This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix) and manually fixable by [editor suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions). + + + + +Passing a single-element array to `Promise.all()`, `Promise.any()`, or `Promise.race()` is likely a mistake. + +## Fail + +```js +const foo = await Promise.all([promise]); +``` + +```js +const foo = await Promise.any([promise]); +``` + +```js +const foo = await Promise.race([promise]); +``` + +```js +const promise = Promise.all([nonPromise]); +``` + +## Pass + +```js +const foo = await promise; +``` + +```js +const promise = Promise.resolve(nonPromise); +``` + +```js +const foo = await Promise.all(promises); +``` + +```js +const foo = await Promise.any([promise, anotherPromise]); +``` + +```js +const [{value: foo, reason: error}] = await Promise.allSettled([promise]); +``` diff --git a/readme.md b/readme.md index e8194a8097..fac04c3d34 100644 --- a/readme.md +++ b/readme.md @@ -148,6 +148,7 @@ If you don't use the preset, ensure you use the same `env` and `parserOptions` c | [no-null](docs/rules/no-null.md) | Disallow the use of the `null` literal. | βœ… | πŸ”§ | πŸ’‘ | | [no-object-as-default-parameter](docs/rules/no-object-as-default-parameter.md) | Disallow the use of objects as default parameters. | βœ… | | | | [no-process-exit](docs/rules/no-process-exit.md) | Disallow `process.exit()`. | βœ… | | | +| [no-single-promise-in-promise-methods](docs/rules/no-single-promise-in-promise-methods.md) | Disallow passing single-element arrays to `Promise` methods. | βœ… | πŸ”§ | πŸ’‘ | | [no-static-only-class](docs/rules/no-static-only-class.md) | Disallow classes that only have static members. | βœ… | πŸ”§ | | | [no-thenable](docs/rules/no-thenable.md) | Disallow `then` property. | βœ… | | | | [no-this-assignment](docs/rules/no-this-assignment.md) | Disallow assigning `this` to a variable. | βœ… | | | diff --git a/rules/no-single-promise-in-promise-methods.js b/rules/no-single-promise-in-promise-methods.js new file mode 100644 index 0000000000..b0090f1c2a --- /dev/null +++ b/rules/no-single-promise-in-promise-methods.js @@ -0,0 +1,167 @@ +'use strict'; +const { + isCommaToken, +} = require('@eslint-community/eslint-utils'); +const {isMethodCall} = require('./ast/index.js'); +const { + getParenthesizedText, + isParenthesized, + needsSemicolon, + shouldAddParenthesesToAwaitExpressionArgument, +} = require('./utils/index.js'); + +const MESSAGE_ID_ERROR = 'no-single-promise-in-promise-methods/error'; +const MESSAGE_ID_SUGGESTION_UNWRAP = 'no-single-promise-in-promise-methods/unwrap'; +const MESSAGE_ID_SUGGESTION_SWITCH_TO_PROMISE_RESOLVE = 'no-single-promise-in-promise-methods/use-promise-resolve'; +const messages = { + [MESSAGE_ID_ERROR]: 'Wrapping single-element array with `Promise.{{method}}()` is unnecessary.', + [MESSAGE_ID_SUGGESTION_UNWRAP]: 'Use the value directly.', + [MESSAGE_ID_SUGGESTION_SWITCH_TO_PROMISE_RESOLVE]: 'Switch to `Promise.resolve(…)`.', +}; +const METHODS = ['all', 'any', 'race']; + +const isPromiseMethodCallWithSingleElementArray = node => + isMethodCall(node, { + object: 'Promise', + methods: METHODS, + optionalMember: false, + optionalCall: false, + argumentsLength: 1, + }) + && node.arguments[0].type === 'ArrayExpression' + && node.arguments[0].elements.length === 1 + && node.arguments[0].elements[0] + && node.arguments[0].elements[0].type !== 'SpreadElement'; + +const unwrapAwaitedCallExpression = (callExpression, sourceCode) => fixer => { + const [promiseNode] = callExpression.arguments[0].elements; + let text = getParenthesizedText(promiseNode, sourceCode); + + if ( + !isParenthesized(promiseNode, sourceCode) + && shouldAddParenthesesToAwaitExpressionArgument(promiseNode) + ) { + text = `(${text})`; + } + + // The next node is already behind a `CallExpression`, there should be no ASI problem + + return fixer.replaceText(callExpression, text); +}; + +const unwrapNonAwaitedCallExpression = (callExpression, sourceCode) => fixer => { + const [promiseNode] = callExpression.arguments[0].elements; + let text = getParenthesizedText(promiseNode, sourceCode); + + if ( + !isParenthesized(promiseNode, sourceCode) + // Since the original call expression can be anywhere, it's hard to tell if the promise + // need to be parenthesized, but it's safe to add parentheses + && !( + // Known cases that not need parentheses + promiseNode.type === 'Identifier' + || promiseNode.type === 'MemberExpression' + ) + ) { + text = `(${text})`; + } + + const previousToken = sourceCode.getTokenBefore(callExpression); + if (needsSemicolon(previousToken, sourceCode, text)) { + text = `;${text}`; + } + + return fixer.replaceText(callExpression, text); +}; + +const switchToPromiseResolve = (callExpression, sourceCode) => function * (fixer) { + /* + ``` + Promise.all([promise,]) + // ^^^ methodNameNode + ``` + */ + const methodNameNode = callExpression.callee.property; + yield fixer.replaceText(methodNameNode, 'resolve'); + + const [arrayExpression] = callExpression.arguments; + /* + ``` + Promise.all([promise,]) + // ^ openingBracketToken + ``` + */ + const openingBracketToken = sourceCode.getFirstToken(arrayExpression); + /* + ``` + Promise.all([promise,]) + // ^ penultimateToken + // ^ closingBracketToken + ``` + */ + const [ + penultimateToken, + closingBracketToken, + ] = sourceCode.getLastTokens(arrayExpression, 2); + + yield fixer.remove(openingBracketToken); + yield fixer.remove(closingBracketToken); + + if (isCommaToken(penultimateToken)) { + yield fixer.remove(penultimateToken); + } +}; + +/** @param {import('eslint').Rule.RuleContext} context */ +const create = context => ({ + CallExpression(callExpression) { + if (!isPromiseMethodCallWithSingleElementArray(callExpression)) { + return; + } + + const problem = { + node: callExpression.arguments[0], + messageId: MESSAGE_ID_ERROR, + data: { + method: callExpression.callee.property.name, + }, + }; + + const {sourceCode} = context; + + if ( + callExpression.parent.type === 'AwaitExpression' + && callExpression.parent.argument === callExpression + ) { + problem.fix = unwrapAwaitedCallExpression(callExpression, sourceCode); + return problem; + } + + problem.suggest = [ + { + messageId: MESSAGE_ID_SUGGESTION_UNWRAP, + fix: unwrapNonAwaitedCallExpression(callExpression, sourceCode), + }, + { + messageId: MESSAGE_ID_SUGGESTION_SWITCH_TO_PROMISE_RESOLVE, + fix: switchToPromiseResolve(callExpression, sourceCode), + }, + ]; + + return problem; + }, +}); + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + create, + meta: { + type: 'suggestion', + docs: { + description: 'Disallow passing single-element arrays to `Promise` methods.', + }, + fixable: 'code', + hasSuggestions: true, + messages, + }, +}; diff --git a/rules/utils/index.js b/rules/utils/index.js index 6239c7e5d7..54c4c55f7b 100644 --- a/rules/utils/index.js +++ b/rules/utils/index.js @@ -45,6 +45,7 @@ module.exports = { isValueNotUsable: require('./is-value-not-usable.js'), needsSemicolon: require('./needs-semicolon.js'), shouldAddParenthesesToMemberExpressionObject: require('./should-add-parentheses-to-member-expression-object.js'), + shouldAddParenthesesToAwaitExpressionArgument: require('./should-add-parentheses-to-await-expression-argument.js'), singular: require('./singular.js'), toLocation: require('./to-location.js'), getAncestor: require('./get-ancestor.js'), diff --git a/rules/utils/should-add-parentheses-to-await-expression-argument.js b/rules/utils/should-add-parentheses-to-await-expression-argument.js new file mode 100644 index 0000000000..4f958f87a8 --- /dev/null +++ b/rules/utils/should-add-parentheses-to-await-expression-argument.js @@ -0,0 +1,21 @@ +'use strict'; + +/** +Check if parentheses should be added to a `node` when it's used as `argument` of `AwaitExpression`. + +@param {Node} node - The AST node to check. +@returns {boolean} +*/ +function shouldAddParenthesesToAwaitExpressionArgument(node) { + return ( + node.type === 'SequenceExpression' + || node.type === 'YieldExpression' + || node.type === 'ArrowFunctionExpression' + || node.type === 'ConditionalExpression' + || node.type === 'AssignmentExpression' + || node.type === 'LogicalExpression' + || node.type === 'BinaryExpression' + ); +} + +module.exports = shouldAddParenthesesToAwaitExpressionArgument; diff --git a/test/no-single-promise-in-promise-methods.mjs b/test/no-single-promise-in-promise-methods.mjs new file mode 100644 index 0000000000..4e73df0926 --- /dev/null +++ b/test/no-single-promise-in-promise-methods.mjs @@ -0,0 +1,104 @@ +import outdent from 'outdent'; +import {getTester} from './utils/test.mjs'; + +const {test} = getTester(import.meta); + +// `await`ed +test.snapshot({ + valid: [ + ], + invalid: [ + 'await Promise.all([(0, promise)])', + 'async function * foo() {await Promise.all([yield promise])}', + 'async function * foo() {await Promise.all([yield* promise])}', + 'await Promise.all([() => promise,],)', + 'await Promise.all([a ? b : c,],)', + 'await Promise.all([x ??= y,],)', + 'await Promise.all([x ||= y,],)', + 'await Promise.all([x &&= y,],)', + 'await Promise.all([x |= y,],)', + 'await Promise.all([x ^= y,],)', + 'await Promise.all([x ??= y,],)', + 'await Promise.all([x ||= y,],)', + 'await Promise.all([x &&= y,],)', + 'await Promise.all([x | y,],)', + 'await Promise.all([x ^ y,],)', + 'await Promise.all([x & y,],)', + 'await Promise.all([x !== y,],)', + 'await Promise.all([x == y,],)', + 'await Promise.all([x in y,],)', + 'await Promise.all([x >>> y,],)', + 'await Promise.all([x + y,],)', + 'await Promise.all([x / y,],)', + 'await Promise.all([x ** y,],)', + 'await Promise.all([promise,],)', + 'await Promise.all([getPromise(),],)', + 'await Promise.all([promises[0],],)', + 'await Promise.all([await promise])', + 'await Promise.any([promise])', + 'await Promise.race([promise])', + 'await Promise.all([new Promise(() => {})])', + '+await Promise.all([+1])', + + // ASI, `Promise.all()` is not really `await`ed + outdent` + await Promise.all([(x,y)]) + [0].toString() + `, + ], +}); + +// Not `await`ed +test.snapshot({ + valid: [ + 'Promise.all([promise, anotherPromise])', + 'Promise.all(notArrayLiteral)', + 'Promise.all([...promises])', + 'Promise.any([promise, anotherPromise])', + 'Promise.race([promise, anotherPromise])', + 'Promise.notListedMethod([promise])', + 'Promise[all]([promise])', + 'Promise.all([,])', + 'NotPromise.all([promise])', + 'Promise?.all([promise])', + 'Promise.all?.([promise])', + 'Promise.all(...[promise])', + 'Promise.all([promise], extraArguments)', + 'Promise.all()', + 'new Promise.all([promise])', + + // We are not checking these cases + 'globalThis.Promise.all([promise])', + 'Promise["all"]([promise])', + + // This can't be checked + 'Promise.allSettled([promise])', + ], + invalid: [ + 'Promise.all([promise,],)', + outdent` + foo + Promise.all([(0, promise),],) + `, + outdent` + foo + Promise.all([[array][0],],) + `, + 'Promise.all([promise]).then()', + 'Promise.all([1]).then()', + 'Promise.all([1.]).then()', + 'Promise.all([.1]).then()', + 'Promise.all([(0, promise)]).then()', + 'const _ = () => Promise.all([ a ?? b ,],)', + 'Promise.all([ {a} = 1 ,],)', + 'Promise.all([ function () {} ,],)', + 'Promise.all([ class {} ,],)', + 'Promise.all([ new Foo ,],).then()', + 'Promise.all([ new Foo ,],).toString', + 'foo(Promise.all([promise]))', + 'Promise.all([promise]).foo = 1', + 'Promise.all([promise])[0] ||= 1', + 'Promise.all([undefined]).then()', + 'Promise.all([null]).then()', + ], +}); diff --git a/test/snapshots/no-single-promise-in-promise-methods.mjs.md b/test/snapshots/no-single-promise-in-promise-methods.mjs.md new file mode 100644 index 0000000000..4433570d96 --- /dev/null +++ b/test/snapshots/no-single-promise-in-promise-methods.mjs.md @@ -0,0 +1,1128 @@ +# Snapshot report for `test/no-single-promise-in-promise-methods.mjs` + +The actual snapshot is saved in `no-single-promise-in-promise-methods.mjs.snap`. + +Generated by [AVA](https://avajs.dev). + +## invalid(1): await Promise.all([(0, promise)]) + +> Input + + `␊ + 1 | await Promise.all([(0, promise)])␊ + ` + +> Output + + `␊ + 1 | await (0, promise)␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([(0, promise)])␊ + | ^^^^^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(2): async function * foo() {await Promise.all([yield promise])} + +> Input + + `␊ + 1 | async function * foo() {await Promise.all([yield promise])}␊ + ` + +> Output + + `␊ + 1 | async function * foo() {await (yield promise)}␊ + ` + +> Error 1/1 + + `␊ + > 1 | async function * foo() {await Promise.all([yield promise])}␊ + | ^^^^^^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(3): async function * foo() {await Promise.all([yield* promise])} + +> Input + + `␊ + 1 | async function * foo() {await Promise.all([yield* promise])}␊ + ` + +> Output + + `␊ + 1 | async function * foo() {await (yield* promise)}␊ + ` + +> Error 1/1 + + `␊ + > 1 | async function * foo() {await Promise.all([yield* promise])}␊ + | ^^^^^^^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(4): await Promise.all([() => promise,],) + +> Input + + `␊ + 1 | await Promise.all([() => promise,],)␊ + ` + +> Output + + `␊ + 1 | await (() => promise)␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([() => promise,],)␊ + | ^^^^^^^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(5): await Promise.all([a ? b : c,],) + +> Input + + `␊ + 1 | await Promise.all([a ? b : c,],)␊ + ` + +> Output + + `␊ + 1 | await (a ? b : c)␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([a ? b : c,],)␊ + | ^^^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(6): await Promise.all([x ??= y,],) + +> Input + + `␊ + 1 | await Promise.all([x ??= y,],)␊ + ` + +> Output + + `␊ + 1 | await (x ??= y)␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([x ??= y,],)␊ + | ^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(7): await Promise.all([x ||= y,],) + +> Input + + `␊ + 1 | await Promise.all([x ||= y,],)␊ + ` + +> Output + + `␊ + 1 | await (x ||= y)␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([x ||= y,],)␊ + | ^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(8): await Promise.all([x &&= y,],) + +> Input + + `␊ + 1 | await Promise.all([x &&= y,],)␊ + ` + +> Output + + `␊ + 1 | await (x &&= y)␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([x &&= y,],)␊ + | ^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(9): await Promise.all([x |= y,],) + +> Input + + `␊ + 1 | await Promise.all([x |= y,],)␊ + ` + +> Output + + `␊ + 1 | await (x |= y)␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([x |= y,],)␊ + | ^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(10): await Promise.all([x ^= y,],) + +> Input + + `␊ + 1 | await Promise.all([x ^= y,],)␊ + ` + +> Output + + `␊ + 1 | await (x ^= y)␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([x ^= y,],)␊ + | ^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(11): await Promise.all([x ??= y,],) + +> Input + + `␊ + 1 | await Promise.all([x ??= y,],)␊ + ` + +> Output + + `␊ + 1 | await (x ??= y)␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([x ??= y,],)␊ + | ^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(12): await Promise.all([x ||= y,],) + +> Input + + `␊ + 1 | await Promise.all([x ||= y,],)␊ + ` + +> Output + + `␊ + 1 | await (x ||= y)␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([x ||= y,],)␊ + | ^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(13): await Promise.all([x &&= y,],) + +> Input + + `␊ + 1 | await Promise.all([x &&= y,],)␊ + ` + +> Output + + `␊ + 1 | await (x &&= y)␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([x &&= y,],)␊ + | ^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(14): await Promise.all([x | y,],) + +> Input + + `␊ + 1 | await Promise.all([x | y,],)␊ + ` + +> Output + + `␊ + 1 | await (x | y)␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([x | y,],)␊ + | ^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(15): await Promise.all([x ^ y,],) + +> Input + + `␊ + 1 | await Promise.all([x ^ y,],)␊ + ` + +> Output + + `␊ + 1 | await (x ^ y)␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([x ^ y,],)␊ + | ^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(16): await Promise.all([x & y,],) + +> Input + + `␊ + 1 | await Promise.all([x & y,],)␊ + ` + +> Output + + `␊ + 1 | await (x & y)␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([x & y,],)␊ + | ^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(17): await Promise.all([x !== y,],) + +> Input + + `␊ + 1 | await Promise.all([x !== y,],)␊ + ` + +> Output + + `␊ + 1 | await (x !== y)␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([x !== y,],)␊ + | ^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(18): await Promise.all([x == y,],) + +> Input + + `␊ + 1 | await Promise.all([x == y,],)␊ + ` + +> Output + + `␊ + 1 | await (x == y)␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([x == y,],)␊ + | ^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(19): await Promise.all([x in y,],) + +> Input + + `␊ + 1 | await Promise.all([x in y,],)␊ + ` + +> Output + + `␊ + 1 | await (x in y)␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([x in y,],)␊ + | ^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(20): await Promise.all([x >>> y,],) + +> Input + + `␊ + 1 | await Promise.all([x >>> y,],)␊ + ` + +> Output + + `␊ + 1 | await (x >>> y)␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([x >>> y,],)␊ + | ^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(21): await Promise.all([x + y,],) + +> Input + + `␊ + 1 | await Promise.all([x + y,],)␊ + ` + +> Output + + `␊ + 1 | await (x + y)␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([x + y,],)␊ + | ^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(22): await Promise.all([x / y,],) + +> Input + + `␊ + 1 | await Promise.all([x / y,],)␊ + ` + +> Output + + `␊ + 1 | await (x / y)␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([x / y,],)␊ + | ^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(23): await Promise.all([x ** y,],) + +> Input + + `␊ + 1 | await Promise.all([x ** y,],)␊ + ` + +> Output + + `␊ + 1 | await (x ** y)␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([x ** y,],)␊ + | ^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(24): await Promise.all([promise,],) + +> Input + + `␊ + 1 | await Promise.all([promise,],)␊ + ` + +> Output + + `␊ + 1 | await promise␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([promise,],)␊ + | ^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(25): await Promise.all([getPromise(),],) + +> Input + + `␊ + 1 | await Promise.all([getPromise(),],)␊ + ` + +> Output + + `␊ + 1 | await getPromise()␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([getPromise(),],)␊ + | ^^^^^^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(26): await Promise.all([promises[0],],) + +> Input + + `␊ + 1 | await Promise.all([promises[0],],)␊ + ` + +> Output + + `␊ + 1 | await promises[0]␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([promises[0],],)␊ + | ^^^^^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(27): await Promise.all([await promise]) + +> Input + + `␊ + 1 | await Promise.all([await promise])␊ + ` + +> Output + + `␊ + 1 | await await promise␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([await promise])␊ + | ^^^^^^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(28): await Promise.any([promise]) + +> Input + + `␊ + 1 | await Promise.any([promise])␊ + ` + +> Output + + `␊ + 1 | await promise␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.any([promise])␊ + | ^^^^^^^^^ Wrapping single-element array with \`Promise.any()\` is unnecessary.␊ + ` + +## invalid(29): await Promise.race([promise]) + +> Input + + `␊ + 1 | await Promise.race([promise])␊ + ` + +> Output + + `␊ + 1 | await promise␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.race([promise])␊ + | ^^^^^^^^^ Wrapping single-element array with \`Promise.race()\` is unnecessary.␊ + ` + +## invalid(30): await Promise.all([new Promise(() => {})]) + +> Input + + `␊ + 1 | await Promise.all([new Promise(() => {})])␊ + ` + +> Output + + `␊ + 1 | await new Promise(() => {})␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([new Promise(() => {})])␊ + | ^^^^^^^^^^^^^^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(31): +await Promise.all([+1]) + +> Input + + `␊ + 1 | +await Promise.all([+1])␊ + ` + +> Output + + `␊ + 1 | +await +1␊ + ` + +> Error 1/1 + + `␊ + > 1 | +await Promise.all([+1])␊ + | ^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ` + +## invalid(32): await Promise.all([(x,y)]) [0].toString() + +> Input + + `␊ + 1 | await Promise.all([(x,y)])␊ + 2 | [0].toString()␊ + ` + +> Error 1/1 + + `␊ + > 1 | await Promise.all([(x,y)])␊ + | ^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + 2 | [0].toString()␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Use the value directly.␊ + 1 | await (x,y)␊ + 2 | [0].toString()␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to \`Promise.resolve(…)\`.␊ + 1 | await Promise.resolve((x,y))␊ + 2 | [0].toString()␊ + ` + +## invalid(1): Promise.all([promise,],) + +> Input + + `␊ + 1 | Promise.all([promise,],)␊ + ` + +> Error 1/1 + + `␊ + > 1 | Promise.all([promise,],)␊ + | ^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Use the value directly.␊ + 1 | promise␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to \`Promise.resolve(…)\`.␊ + 1 | Promise.resolve(promise,)␊ + ` + +## invalid(2): foo Promise.all([(0, promise),],) + +> Input + + `␊ + 1 | foo␊ + 2 | Promise.all([(0, promise),],)␊ + ` + +> Error 1/1 + + `␊ + 1 | foo␊ + > 2 | Promise.all([(0, promise),],)␊ + | ^^^^^^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Use the value directly.␊ + 1 | foo␊ + 2 | ;(0, promise)␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to \`Promise.resolve(…)\`.␊ + 1 | foo␊ + 2 | Promise.resolve((0, promise),)␊ + ` + +## invalid(3): foo Promise.all([[array][0],],) + +> Input + + `␊ + 1 | foo␊ + 2 | Promise.all([[array][0],],)␊ + ` + +> Error 1/1 + + `␊ + 1 | foo␊ + > 2 | Promise.all([[array][0],],)␊ + | ^^^^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Use the value directly.␊ + 1 | foo␊ + 2 | ;[array][0]␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to \`Promise.resolve(…)\`.␊ + 1 | foo␊ + 2 | Promise.resolve([array][0],)␊ + ` + +## invalid(4): Promise.all([promise]).then() + +> Input + + `␊ + 1 | Promise.all([promise]).then()␊ + ` + +> Error 1/1 + + `␊ + > 1 | Promise.all([promise]).then()␊ + | ^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Use the value directly.␊ + 1 | promise.then()␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to \`Promise.resolve(…)\`.␊ + 1 | Promise.resolve(promise).then()␊ + ` + +## invalid(5): Promise.all([1]).then() + +> Input + + `␊ + 1 | Promise.all([1]).then()␊ + ` + +> Error 1/1 + + `␊ + > 1 | Promise.all([1]).then()␊ + | ^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Use the value directly.␊ + 1 | (1).then()␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to \`Promise.resolve(…)\`.␊ + 1 | Promise.resolve(1).then()␊ + ` + +## invalid(6): Promise.all([1.]).then() + +> Input + + `␊ + 1 | Promise.all([1.]).then()␊ + ` + +> Error 1/1 + + `␊ + > 1 | Promise.all([1.]).then()␊ + | ^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Use the value directly.␊ + 1 | (1.).then()␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to \`Promise.resolve(…)\`.␊ + 1 | Promise.resolve(1.).then()␊ + ` + +## invalid(7): Promise.all([.1]).then() + +> Input + + `␊ + 1 | Promise.all([.1]).then()␊ + ` + +> Error 1/1 + + `␊ + > 1 | Promise.all([.1]).then()␊ + | ^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Use the value directly.␊ + 1 | (.1).then()␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to \`Promise.resolve(…)\`.␊ + 1 | Promise.resolve(.1).then()␊ + ` + +## invalid(8): Promise.all([(0, promise)]).then() + +> Input + + `␊ + 1 | Promise.all([(0, promise)]).then()␊ + ` + +> Error 1/1 + + `␊ + > 1 | Promise.all([(0, promise)]).then()␊ + | ^^^^^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Use the value directly.␊ + 1 | (0, promise).then()␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to \`Promise.resolve(…)\`.␊ + 1 | Promise.resolve((0, promise)).then()␊ + ` + +## invalid(9): const _ = () => Promise.all([ a ?? b ,],) + +> Input + + `␊ + 1 | const _ = () => Promise.all([ a ?? b ,],)␊ + ` + +> Error 1/1 + + `␊ + > 1 | const _ = () => Promise.all([ a ?? b ,],)␊ + | ^^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Use the value directly.␊ + 1 | const _ = () => (a ?? b)␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to \`Promise.resolve(…)\`.␊ + 1 | const _ = () => Promise.resolve( a ?? b ,)␊ + ` + +## invalid(10): Promise.all([ {a} = 1 ,],) + +> Input + + `␊ + 1 | Promise.all([ {a} = 1 ,],)␊ + ` + +> Error 1/1 + + `␊ + > 1 | Promise.all([ {a} = 1 ,],)␊ + | ^^^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Use the value directly.␊ + 1 | ({a} = 1)␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to \`Promise.resolve(…)\`.␊ + 1 | Promise.resolve( {a} = 1 ,)␊ + ` + +## invalid(11): Promise.all([ function () {} ,],) + +> Input + + `␊ + 1 | Promise.all([ function () {} ,],)␊ + ` + +> Error 1/1 + + `␊ + > 1 | Promise.all([ function () {} ,],)␊ + | ^^^^^^^^^^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Use the value directly.␊ + 1 | (function () {})␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to \`Promise.resolve(…)\`.␊ + 1 | Promise.resolve( function () {} ,)␊ + ` + +## invalid(12): Promise.all([ class {} ,],) + +> Input + + `␊ + 1 | Promise.all([ class {} ,],)␊ + ` + +> Error 1/1 + + `␊ + > 1 | Promise.all([ class {} ,],)␊ + | ^^^^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Use the value directly.␊ + 1 | (class {})␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to \`Promise.resolve(…)\`.␊ + 1 | Promise.resolve( class {} ,)␊ + ` + +## invalid(13): Promise.all([ new Foo ,],).then() + +> Input + + `␊ + 1 | Promise.all([ new Foo ,],).then()␊ + ` + +> Error 1/1 + + `␊ + > 1 | Promise.all([ new Foo ,],).then()␊ + | ^^^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Use the value directly.␊ + 1 | (new Foo).then()␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to \`Promise.resolve(…)\`.␊ + 1 | Promise.resolve( new Foo ,).then()␊ + ` + +## invalid(14): Promise.all([ new Foo ,],).toString + +> Input + + `␊ + 1 | Promise.all([ new Foo ,],).toString␊ + ` + +> Error 1/1 + + `␊ + > 1 | Promise.all([ new Foo ,],).toString␊ + | ^^^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Use the value directly.␊ + 1 | (new Foo).toString␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to \`Promise.resolve(…)\`.␊ + 1 | Promise.resolve( new Foo ,).toString␊ + ` + +## invalid(15): foo(Promise.all([promise])) + +> Input + + `␊ + 1 | foo(Promise.all([promise]))␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo(Promise.all([promise]))␊ + | ^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Use the value directly.␊ + 1 | foo(promise)␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to \`Promise.resolve(…)\`.␊ + 1 | foo(Promise.resolve(promise))␊ + ` + +## invalid(16): Promise.all([promise]).foo = 1 + +> Input + + `␊ + 1 | Promise.all([promise]).foo = 1␊ + ` + +> Error 1/1 + + `␊ + > 1 | Promise.all([promise]).foo = 1␊ + | ^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Use the value directly.␊ + 1 | promise.foo = 1␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to \`Promise.resolve(…)\`.␊ + 1 | Promise.resolve(promise).foo = 1␊ + ` + +## invalid(17): Promise.all([promise])[0] ||= 1 + +> Input + + `␊ + 1 | Promise.all([promise])[0] ||= 1␊ + ` + +> Error 1/1 + + `␊ + > 1 | Promise.all([promise])[0] ||= 1␊ + | ^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Use the value directly.␊ + 1 | promise[0] ||= 1␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to \`Promise.resolve(…)\`.␊ + 1 | Promise.resolve(promise)[0] ||= 1␊ + ` + +## invalid(18): Promise.all([undefined]).then() + +> Input + + `␊ + 1 | Promise.all([undefined]).then()␊ + ` + +> Error 1/1 + + `␊ + > 1 | Promise.all([undefined]).then()␊ + | ^^^^^^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Use the value directly.␊ + 1 | undefined.then()␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to \`Promise.resolve(…)\`.␊ + 1 | Promise.resolve(undefined).then()␊ + ` + +## invalid(19): Promise.all([null]).then() + +> Input + + `␊ + 1 | Promise.all([null]).then()␊ + ` + +> Error 1/1 + + `␊ + > 1 | Promise.all([null]).then()␊ + | ^^^^^^ Wrapping single-element array with \`Promise.all()\` is unnecessary.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Use the value directly.␊ + 1 | (null).then()␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to \`Promise.resolve(…)\`.␊ + 1 | Promise.resolve(null).then()␊ + ` diff --git a/test/snapshots/no-single-promise-in-promise-methods.mjs.snap b/test/snapshots/no-single-promise-in-promise-methods.mjs.snap new file mode 100644 index 0000000000000000000000000000000000000000..6c58d9ef78bb5ac9b9a3d7df1ff568b36610cb4e GIT binary patch literal 2274 zcmV<82p#u9RzVR}DB0$Qm2S9*O2bw1KZsJ{UnxsvY1c{c%iARN? zigu~HaS{vHj@I5Lt0oeZ3m^`acolI#6iN?pqe6%Z2~qJBQUQU=i6Rah5jT#Y-T3v{ z-;8Iy&c@kvZ%yO>&iwzseB*D&v+pjP(9Pdr7W&T!+9Jy`he?b z^0&ZZI}F}rg6mBLAnfVFNPh*9e(got!SHEe(_WSCQSh)!!B_$ca!d@K00|vVh(Ruf zgCpQwCfnd37X!jI0K&IPLCB>@!lt57bWzw(smUUlo^dBLQXWCd@)QVTgi)5mT3EDs zpmqGz&KE&|=UBx#G9148yt#plxPgr0fhc5_oFG%Aiia@)>=Wo=039rdVW4y; z48pq_58)jU!dXTLa2PxRY^M1+*Zc|G{8chLJ53t6_4|QgLv-r{Dwy&6fX*Gzc;{N& z_P2rUuP|&64k6z3u2?=vvwUqW4a+^11eUk1?c8#I2pO^b0L}8Dp)@S_R1#R;y0&x6 z{UL0v<)Ul(K`P}`$H#zE`baJfjcyOM_8mE?9; zQn9;|3A^*2-IT`u#I5o(mobLBtpmJM; zahlt20=Hje6VB;%cbuklo~xvig4316PFK=!x{@iUscq&^3Qi9tc6ulcr-w4&v|q8Q z-Q~U0X;f@aB|*h*UE6uZ_J^>w72Ci0rnZ-K%iFm5W;q^n^G!Frottldzq?~MwYAiY z6zq0I64>3mv~#t#Lz>!t5BwW}j_wqy1-S#Ud-c38CA+s*9C#&U_$PA;mp^KU`KZ9fR?%F}HROSSvNPw;v) zfD0giHy8m#h7zB`rbkimqM-Df4mH+Xv5Y5i0GOAH{R427+TB+UT#n}O9?0Rf_&)@1 zAbc@&Ck09`3M{~$f8$Oeq=cm)0IQ$t;Gf8ab!b$T6r*7knjT8Un3D2iATGSJ)$G*KBemrEd*3yj)B8yaT~>P9Wv z=LObmZS%+^@-h~W1gs>Em9*=zF!lh2-B2Mfqt>4V)<5rBPkmeFU&ChFI^o$$>0^b} zk%m|n$y~84+2&EZrWuPuz~?jU2TiGh1B8^wT1XbL|JbfufS3RFGoDlQq?y>J;U_x! zfzgy-14rwNi>lRnl2$I1rpS|)O6(<-oKp05B^aW_;E1C~2wabX^ceu@LmVV2u0Zt$O44>aJ=UmQ9`^(_~{W0K`ueGTS_ejQ5$dfJWIF8n++{<$nOme_SZ}MX4XIW32W9 zs9|e_4L7nxD=j_um%RY#%npOIi!lDv$~u`QGKJD2p!7bhW&#Bi%PRoOOK7cxD(o8B z3*ZUPe6vJage)%;j#XKXY%2I)ZPNT!lXVJV_!lt63ZbA~2cZ3kgNBL4e)QWj*umLn zn!a#QbO;4-a23W~)fLO4q+}`v!pbDGnZ#*#RHz3hss0M>jW ztg6nmw?Wh4CluRq6v26AR?f~d08#$%$DTQ) zPy&Ak1YYGK(3RU!47!uvCKSTRSw5x!$FJiV#T_r|w>jlukX$muDd^4gP*`n~`ohz1GUl)V$HV$|jX7c9ApuqhMfV;v5&X?L5#QF2hK62rh zS>x}X{AJYyS!gSn^-%G@u2xvx;>5Gm#@TrW`P%{lvBgg4aKCSn+Trvp6G wP}g+^a2%fj0|k_|F9B;GqpU#{8fLvfn9!W+HfU18FvDd14+N574vk*`00)^;*Z=?k literal 0 HcmV?d00001