minor adjustment to readme
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / prefer-promise-reject-errors.js
1 /**
2  * @fileoverview restrict values that can be used as Promise rejection reasons
3  * @author Teddy Katz
4  */
5 "use strict";
6
7 const astUtils = require("./utils/ast-utils");
8
9 //------------------------------------------------------------------------------
10 // Rule Definition
11 //------------------------------------------------------------------------------
12
13 module.exports = {
14     meta: {
15         type: "suggestion",
16
17         docs: {
18             description: "require using Error objects as Promise rejection reasons",
19             category: "Best Practices",
20             recommended: false,
21             url: "https://eslint.org/docs/rules/prefer-promise-reject-errors"
22         },
23
24         fixable: null,
25
26         schema: [
27             {
28                 type: "object",
29                 properties: {
30                     allowEmptyReject: { type: "boolean", default: false }
31                 },
32                 additionalProperties: false
33             }
34         ]
35     },
36
37     create(context) {
38
39         const ALLOW_EMPTY_REJECT = context.options.length && context.options[0].allowEmptyReject;
40
41         //----------------------------------------------------------------------
42         // Helpers
43         //----------------------------------------------------------------------
44
45         /**
46          * Checks the argument of a reject() or Promise.reject() CallExpression, and reports it if it can't be an Error
47          * @param {ASTNode} callExpression A CallExpression node which is used to reject a Promise
48          * @returns {void}
49          */
50         function checkRejectCall(callExpression) {
51             if (!callExpression.arguments.length && ALLOW_EMPTY_REJECT) {
52                 return;
53             }
54             if (
55                 !callExpression.arguments.length ||
56                 !astUtils.couldBeError(callExpression.arguments[0]) ||
57                 callExpression.arguments[0].type === "Identifier" && callExpression.arguments[0].name === "undefined"
58             ) {
59                 context.report({
60                     node: callExpression,
61                     message: "Expected the Promise rejection reason to be an Error."
62                 });
63             }
64         }
65
66         /**
67          * Determines whether a function call is a Promise.reject() call
68          * @param {ASTNode} node A CallExpression node
69          * @returns {boolean} `true` if the call is a Promise.reject() call
70          */
71         function isPromiseRejectCall(node) {
72             return node.callee.type === "MemberExpression" &&
73                 node.callee.object.type === "Identifier" && node.callee.object.name === "Promise" &&
74                 node.callee.property.type === "Identifier" && node.callee.property.name === "reject";
75         }
76
77         //----------------------------------------------------------------------
78         // Public
79         //----------------------------------------------------------------------
80
81         return {
82
83             // Check `Promise.reject(value)` calls.
84             CallExpression(node) {
85                 if (isPromiseRejectCall(node)) {
86                     checkRejectCall(node);
87                 }
88             },
89
90             /*
91              * Check for `new Promise((resolve, reject) => {})`, and check for reject() calls.
92              * This function is run on "NewExpression:exit" instead of "NewExpression" to ensure that
93              * the nodes in the expression already have the `parent` property.
94              */
95             "NewExpression:exit"(node) {
96                 if (
97                     node.callee.type === "Identifier" && node.callee.name === "Promise" &&
98                     node.arguments.length && astUtils.isFunction(node.arguments[0]) &&
99                     node.arguments[0].params.length > 1 && node.arguments[0].params[1].type === "Identifier"
100                 ) {
101                     context.getDeclaredVariables(node.arguments[0])
102
103                         /*
104                          * Find the first variable that matches the second parameter's name.
105                          * If the first parameter has the same name as the second parameter, then the variable will actually
106                          * be "declared" when the first parameter is evaluated, but then it will be immediately overwritten
107                          * by the second parameter. It's not possible for an expression with the variable to be evaluated before
108                          * the variable is overwritten, because functions with duplicate parameters cannot have destructuring or
109                          * default assignments in their parameter lists. Therefore, it's not necessary to explicitly account for
110                          * this case.
111                          */
112                         .find(variable => variable.name === node.arguments[0].params[1].name)
113
114                         // Get the references to that variable.
115                         .references
116
117                         // Only check the references that read the parameter's value.
118                         .filter(ref => ref.isRead())
119
120                         // Only check the references that are used as the callee in a function call, e.g. `reject(foo)`.
121                         .filter(ref => ref.identifier.parent.type === "CallExpression" && ref.identifier === ref.identifier.parent.callee)
122
123                         // Check the argument of the function call to determine whether it's an Error.
124                         .forEach(ref => checkRejectCall(ref.identifier.parent));
125                 }
126             }
127         };
128     }
129 };