-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
1 parent
ca01e00
commit f091d95
Showing
9 changed files
with
377 additions
and
40 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,79 @@ | ||
# require using Error objects as Promise rejection reasons (prefer-promise-reject-errors) | ||
|
||
It is considered good practice to only instances of the built-in `Error` object for user-defined errors in Promises. `Error` objects automatically store a stack trace, which can be used to debug an error by determining where it came from. If a Promise is rejected with a non-`Error` value, it can be difficult to determine where the rejection occurred. | ||
|
||
|
||
## Rule Details | ||
|
||
This rule aims to ensure that Promises are only rejected with `Error` objects. | ||
|
||
## Options | ||
|
||
This rule takes one optional object argument: | ||
|
||
* `allowEmptyReject: true` (`false` by default) allows calls to `Promise.reject()` with no arguments. | ||
|
||
Examples of **incorrect** code for this rule: | ||
|
||
```js | ||
/*eslint prefer-promise-reject-errors: "error"*/ | ||
|
||
Promise.reject("something bad happened"); | ||
|
||
Promise.reject(5); | ||
|
||
Promise.reject(); | ||
|
||
new Promise(function(resolve, reject) { | ||
reject("something bad happened"); | ||
}); | ||
|
||
new Promise(function(resolve, reject) { | ||
reject(); | ||
}); | ||
|
||
``` | ||
|
||
Examples of **correct** code for this rule: | ||
|
||
```js | ||
/*eslint prefer-promise-reject-errors: "error"*/ | ||
|
||
Promise.reject(new Error("something bad happened")); | ||
|
||
Promise.reject(new TypeError("something bad happened")); | ||
|
||
new Promise(function(resolve, reject) { | ||
reject(new Error("something bad happened")); | ||
}); | ||
|
||
var foo = getUnknownValue(); | ||
Promise.reject(foo); | ||
``` | ||
|
||
Examples of **correct** code for this rule with the `allowEmptyReject: true` option: | ||
|
||
```js | ||
/*eslint prefer-promise-reject-errors: ["error", {"allowEmptyReject": true}]*/ | ||
|
||
Promise.reject(); | ||
|
||
new Promise(function(resolve, reject) { | ||
reject(); | ||
}); | ||
``` | ||
|
||
## Known Limitations | ||
|
||
Due to the limits of static analysis, this rule cannot guarantee that you will only reject Promises with `Error` objects. While the rule will report cases where it can guarantee that the rejection reason is clearly not an `Error`, it will not report cases where there is uncertainty about whether a given reason is an `Error`. For more information on this caveat, see the [similar limitations](http://eslint.org/docs/rules/no-throw-literal#known-limitations) in the `no-throw-literal` rule. | ||
|
||
To avoid conflicts between rules, this rule does not report non-error values used in `throw` statements in async functions, even though these lead to Promise rejections. To lint for these cases, use the [`no-throw-literal`](http://eslint.org/docs/rules/no-throw-literal) rule. | ||
|
||
## When Not To Use It | ||
|
||
If you're using custom non-error values as Promise rejection reasons, you can turn off this rule. | ||
|
||
## Further Reading | ||
|
||
* [`no-throw-literal`](http://eslint.org/docs/rules/no-throw-literal) | ||
* [Warning: a promise was rejected with a non-error](http://bluebirdjs.com/docs/warning-explanations.html#warning-a-promise-was-rejected-with-a-non-error) |
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
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,124 @@ | ||
/** | ||
* @fileoverview restrict values that can be used as Promise rejection reasons | ||
* @author Teddy Katz | ||
*/ | ||
"use strict"; | ||
|
||
const astUtils = require("../ast-utils"); | ||
|
||
//------------------------------------------------------------------------------ | ||
// Rule Definition | ||
//------------------------------------------------------------------------------ | ||
|
||
module.exports = { | ||
meta: { | ||
docs: { | ||
description: "require using Error objects as Promise rejection reasons", | ||
category: "Best Practices", | ||
recommended: false | ||
}, | ||
fixable: null, | ||
schema: [ | ||
{ | ||
type: "object", | ||
properties: { | ||
allowEmptyReject: { type: "boolean" } | ||
}, | ||
additionalProperties: false | ||
} | ||
] | ||
}, | ||
|
||
create(context) { | ||
|
||
const ALLOW_EMPTY_REJECT = context.options.length && context.options[0].allowEmptyReject; | ||
|
||
//---------------------------------------------------------------------- | ||
// Helpers | ||
//---------------------------------------------------------------------- | ||
|
||
/** | ||
* Checks the argument of a reject() or Promise.reject() CallExpression, and reports it if it can't be an Error | ||
* @param {ASTNode} callExpression A CallExpression node which is used to reject a Promise | ||
* @returns {void} | ||
*/ | ||
function checkRejectCall(callExpression) { | ||
if (!callExpression.arguments.length && ALLOW_EMPTY_REJECT) { | ||
return; | ||
} | ||
if ( | ||
!callExpression.arguments.length || | ||
!astUtils.couldBeError(callExpression.arguments[0]) || | ||
callExpression.arguments[0].type === "Identifier" && callExpression.arguments[0].name === "undefined" | ||
) { | ||
context.report({ | ||
node: callExpression, | ||
message: "Expected the Promise rejection reason to be an Error." | ||
}); | ||
} | ||
} | ||
|
||
/** | ||
* Determines whether a function call is a Promise.reject() call | ||
* @param {ASTNode} node A CallExpression node | ||
* @returns {boolean} `true` if the call is a Promise.reject() call | ||
*/ | ||
function isPromiseRejectCall(node) { | ||
return node.callee.type === "MemberExpression" && | ||
node.callee.object.type === "Identifier" && node.callee.object.name === "Promise" && | ||
node.callee.property.type === "Identifier" && node.callee.property.name === "reject"; | ||
} | ||
|
||
//---------------------------------------------------------------------- | ||
// Public | ||
//---------------------------------------------------------------------- | ||
|
||
return { | ||
|
||
// Check `Promise.reject(value)` calls. | ||
CallExpression(node) { | ||
if (isPromiseRejectCall(node)) { | ||
checkRejectCall(node); | ||
} | ||
}, | ||
|
||
/* | ||
* Check for `new Promise((resolve, reject) => {})`, and check for reject() calls. | ||
* This function is run on "NewExpression:exit" instead of "NewExpression" to ensure that | ||
* the nodes in the expression already have the `parent` property. | ||
*/ | ||
"NewExpression:exit"(node) { | ||
if ( | ||
node.callee.type === "Identifier" && node.callee.name === "Promise" && | ||
node.arguments.length && astUtils.isFunction(node.arguments[0]) && | ||
node.arguments[0].params.length > 1 && node.arguments[0].params[1].type === "Identifier" | ||
) { | ||
context.getDeclaredVariables(node.arguments[0]) | ||
|
||
/* | ||
* Find the first variable that matches the second parameter's name. | ||
* If the first parameter has the same name as the second parameter, then the variable will actually | ||
* be "declared" when the first parameter is evaluated, but then it will be immediately overwritten | ||
* by the second parameter. It's not possible for an expression with the variable to be evaluated before | ||
* the variable is overwritten, because functions with duplicate parameters cannot have destructuring or | ||
* default assignments in their parameter lists. Therefore, it's not necessary to explicitly account for | ||
* this case. | ||
*/ | ||
.find(variable => variable.name === node.arguments[0].params[1].name) | ||
|
||
// Get the references to that variable. | ||
.references | ||
|
||
// Only check the references that read the parameter's value. | ||
.filter(ref => ref.isRead()) | ||
|
||
// Only check the references that are used as the callee in a function call, e.g. `reject(foo)`. | ||
.filter(ref => ref.identifier.parent.type === "CallExpression" && ref.identifier === ref.identifier.parent.callee) | ||
|
||
// Check the argument of the function call to determine whether it's an Error. | ||
.forEach(ref => checkRejectCall(ref.identifier.parent)); | ||
} | ||
} | ||
}; | ||
} | ||
}; |
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
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
Oops, something went wrong.