-
-
Notifications
You must be signed in to change notification settings - Fork 367
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add
no-single-promise-in-promise-methods
rule
- Loading branch information
1 parent
702d51b
commit 80e3343
Showing
6 changed files
with
216 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# Disallow using `Promise` method with a single element array as parameter | ||
|
||
💼 This rule is enabled in the ✅ `recommended` [config](https:/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). | ||
|
||
<!-- end auto-generated rule header --> | ||
<!-- Do not manually modify this header. Run: `npm run fix:eslint-docs` --> | ||
|
||
Single element array parameter in a Promise.all(), Promise.any() or Promise.race() method is probably a mistake. | ||
|
||
## Fail | ||
|
||
```js | ||
Promise.all([promise]) | ||
|
||
Promise.any([promise]) | ||
|
||
Promise.race([promise]) | ||
``` | ||
|
||
## Pass | ||
|
||
```js | ||
Promise.all([promise, anotherPromise]) | ||
Promise.all(notArrayLiteral) | ||
Promise.all([...promises]) | ||
|
||
Promise.any([promise, anotherPromise]) | ||
|
||
Promise.race([promise, anotherPromise]) | ||
|
||
Promise.allSettled([promise]) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
'use strict'; | ||
const isPromiseMethodWithArray = require('./utils/is-promise-method-with-array.js'); | ||
|
||
const MESSAGE_ID_ERROR = 'no-single-promise-in-promise-methods/error'; | ||
const MESSAGE_ID_SUGGESTION_1 = 'no-single-promise-in-promise-methods/suggestion-1'; | ||
const MESSAGE_ID_SUGGESTION_2 = 'no-single-promise-in-promise-methods/suggestion-2'; | ||
const messages = { | ||
[MESSAGE_ID_ERROR]: 'Parameter in `Promise.{{method}}` should not be a single element array.', | ||
[MESSAGE_ID_SUGGESTION_1]: 'Use the value directly.', | ||
[MESSAGE_ID_SUGGESTION_2]: 'Wrap the value in a `Promise.resolve`.', | ||
}; | ||
const METHODS = ['all', 'any', 'race']; | ||
|
||
const isPromiseMethodWithSinglePromise = (node, methods) => { | ||
const types = new Set(['CallExpression', 'Identifier', 'MemberExpression']); | ||
|
||
if (!isPromiseMethodWithArray(node, methods) || node.arguments[0].elements.length !== 1) { | ||
return false; | ||
} | ||
|
||
const [element] = node.arguments[0].elements; | ||
|
||
return types.has(element.type) | ||
|| (element.type === 'AwaitExpression' && types.has(element.argument.type)); | ||
}; | ||
|
||
const getMethodName = node => node.callee.property.name; | ||
|
||
const getAutoFixer = ({sourceCode}, node) => fixer => { | ||
const [element] = node.arguments[0].elements; | ||
const elementWithoutAwait = element.type === 'AwaitExpression' ? element.argument : element; | ||
|
||
return fixer.replaceText(node, sourceCode.getText(elementWithoutAwait)); | ||
}; | ||
|
||
const getSuggestion1Fixer = ({sourceCode}, node) => fixer => | ||
fixer.replaceText(node, sourceCode.getText(node.arguments[0].elements[0])); | ||
|
||
const getSuggestion2Fixer = ({sourceCode}, node) => fixer => { | ||
const text = sourceCode.getText(node.arguments[0].elements[0]); | ||
|
||
return fixer.replaceText(node, `Promise.resolve(${text})`); | ||
}; | ||
|
||
/** @param {import('eslint').Rule.RuleContext} context */ | ||
const create = context => ({ | ||
CallExpression(node) { | ||
if (!isPromiseMethodWithSinglePromise(node, METHODS)) { | ||
return; | ||
} | ||
|
||
const descriptor = { | ||
node, | ||
messageId: MESSAGE_ID_ERROR, | ||
data: { | ||
method: getMethodName(node), | ||
}, | ||
}; | ||
|
||
if (node.parent.type === 'AwaitExpression') { | ||
context.report({ | ||
...descriptor, | ||
fix: getAutoFixer(context, node), | ||
}); | ||
} else { | ||
context.report({ | ||
...descriptor, | ||
suggest: [ | ||
{ | ||
messageId: MESSAGE_ID_SUGGESTION_1, | ||
fix: getSuggestion1Fixer(context, node), | ||
}, | ||
{ | ||
messageId: MESSAGE_ID_SUGGESTION_2, | ||
fix: getSuggestion2Fixer(context, node), | ||
}, | ||
], | ||
}); | ||
} | ||
}, | ||
}); | ||
|
||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
create, | ||
meta: { | ||
type: 'suggestion', | ||
docs: { | ||
description: 'Disallow using `Promise` method with a single element array as parameter.', | ||
}, | ||
fixable: 'code', | ||
hasSuggestions: true, | ||
messages, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
'use strict'; | ||
const {isMethodCall} = require('../ast/index.js'); | ||
|
||
const isPromiseMethodWithArray = (node, methods) => | ||
node.callee.type === 'MemberExpression' | ||
&& node.callee.object.type === 'Identifier' | ||
&& node.callee.object.name === 'Promise' | ||
&& isMethodCall(node, methods) | ||
&& node.arguments.length === 1 | ||
&& node.arguments[0].type === 'ArrayExpression' | ||
&& node.arguments[0].elements.some(element => element !== null); | ||
|
||
module.exports = isPromiseMethodWithArray; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import {getTester} from './utils/test.mjs'; | ||
|
||
const {test} = getTester(import.meta); | ||
|
||
const error = { | ||
messageId: 'no-single-promise-in-promise-methods/error', | ||
}; | ||
|
||
test({ | ||
valid: [ | ||
'Promise.all([promise, anotherPromise])', | ||
'Promise.all(notArrayLiteral)', | ||
'Promise.all([...promises])', | ||
'Promise.all([await -1])', | ||
'Promise.any([promise, anotherPromise])', | ||
'Promise.race([promise, anotherPromise])', | ||
'Promise.allSettled([promise])', | ||
'Promise[all]([promise])', | ||
'Promise.all([,])', | ||
], | ||
|
||
invalid: [ | ||
{ | ||
code: 'await Promise.all([promise])', | ||
errors: [error], | ||
output: 'await promise', | ||
}, | ||
{ | ||
code: 'await Promise.all([func()])', | ||
errors: [error], | ||
output: 'await func()', | ||
}, | ||
{ | ||
code: 'await Promise.all([promises[0]])', | ||
errors: [error], | ||
output: 'await promises[0]', | ||
}, | ||
{ | ||
code: 'await Promise.all([await promise])', | ||
errors: [error], | ||
output: 'await promise', | ||
}, | ||
{ | ||
code: 'await Promise.any([promise])', | ||
errors: [error], | ||
output: 'await promise', | ||
}, | ||
{ | ||
code: 'await Promise.race([promise])', | ||
errors: [error], | ||
output: 'await promise', | ||
}, | ||
{ | ||
code: 'Promise.all([somethingMaybeNotPromise])', | ||
errors: [ | ||
{ | ||
...error, | ||
suggestions: [ | ||
{ | ||
messageId: 'no-single-promise-in-promise-methods/suggestion-1', | ||
output: 'somethingMaybeNotPromise', | ||
}, | ||
{ | ||
messageId: 'no-single-promise-in-promise-methods/suggestion-2', | ||
output: 'Promise.resolve(somethingMaybeNotPromise)', | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
], | ||
}); |