minor adjustment to readme
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / wrap-iife.js
1 /**
2  * @fileoverview Rule to flag when IIFE is not wrapped in parens
3  * @author Ilya Volodin
4  */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Requirements
10 //------------------------------------------------------------------------------
11
12 const astUtils = require("./utils/ast-utils");
13
14 //------------------------------------------------------------------------------
15 // Rule Definition
16 //------------------------------------------------------------------------------
17
18 module.exports = {
19     meta: {
20         type: "layout",
21
22         docs: {
23             description: "require parentheses around immediate `function` invocations",
24             category: "Best Practices",
25             recommended: false,
26             url: "https://eslint.org/docs/rules/wrap-iife"
27         },
28
29         schema: [
30             {
31                 enum: ["outside", "inside", "any"]
32             },
33             {
34                 type: "object",
35                 properties: {
36                     functionPrototypeMethods: {
37                         type: "boolean",
38                         default: false
39                     }
40                 },
41                 additionalProperties: false
42             }
43         ],
44
45         fixable: "code",
46         messages: {
47             wrapInvocation: "Wrap an immediate function invocation in parentheses.",
48             wrapExpression: "Wrap only the function expression in parens.",
49             moveInvocation: "Move the invocation into the parens that contain the function."
50         }
51     },
52
53     create(context) {
54
55         const style = context.options[0] || "outside";
56         const includeFunctionPrototypeMethods = context.options[1] && context.options[1].functionPrototypeMethods;
57
58         const sourceCode = context.getSourceCode();
59
60         /**
61          * Check if the node is wrapped in ()
62          * @param {ASTNode} node node to evaluate
63          * @returns {boolean} True if it is wrapped
64          * @private
65          */
66         function wrapped(node) {
67             return astUtils.isParenthesised(sourceCode, node);
68         }
69
70         /**
71          * Get the function node from an IIFE
72          * @param {ASTNode} node node to evaluate
73          * @returns {ASTNode} node that is the function expression of the given IIFE, or null if none exist
74          */
75         function getFunctionNodeFromIIFE(node) {
76             const callee = node.callee;
77
78             if (callee.type === "FunctionExpression") {
79                 return callee;
80             }
81
82             if (includeFunctionPrototypeMethods &&
83                 callee.type === "MemberExpression" &&
84                 callee.object.type === "FunctionExpression" &&
85                 (astUtils.getStaticPropertyName(callee) === "call" || astUtils.getStaticPropertyName(callee) === "apply")
86             ) {
87                 return callee.object;
88             }
89
90             return null;
91         }
92
93
94         return {
95             CallExpression(node) {
96                 const innerNode = getFunctionNodeFromIIFE(node);
97
98                 if (!innerNode) {
99                     return;
100                 }
101
102                 const callExpressionWrapped = wrapped(node),
103                     functionExpressionWrapped = wrapped(innerNode);
104
105                 if (!callExpressionWrapped && !functionExpressionWrapped) {
106                     context.report({
107                         node,
108                         messageId: "wrapInvocation",
109                         fix(fixer) {
110                             const nodeToSurround = style === "inside" ? innerNode : node;
111
112                             return fixer.replaceText(nodeToSurround, `(${sourceCode.getText(nodeToSurround)})`);
113                         }
114                     });
115                 } else if (style === "inside" && !functionExpressionWrapped) {
116                     context.report({
117                         node,
118                         messageId: "wrapExpression",
119                         fix(fixer) {
120
121                             /*
122                              * The outer call expression will always be wrapped at this point.
123                              * Replace the range between the end of the function expression and the end of the call expression.
124                              * for example, in `(function(foo) {}(bar))`, the range `(bar))` should get replaced with `)(bar)`.
125                              * Replace the parens from the outer expression, and parenthesize the function expression.
126                              */
127                             const parenAfter = sourceCode.getTokenAfter(node);
128
129                             return fixer.replaceTextRange(
130                                 [innerNode.range[1], parenAfter.range[1]],
131                                 `)${sourceCode.getText().slice(innerNode.range[1], parenAfter.range[0])}`
132                             );
133                         }
134                     });
135                 } else if (style === "outside" && !callExpressionWrapped) {
136                     context.report({
137                         node,
138                         messageId: "moveInvocation",
139                         fix(fixer) {
140
141                             /*
142                              * The inner function expression will always be wrapped at this point.
143                              * It's only necessary to replace the range between the end of the function expression
144                              * and the call expression. For example, in `(function(foo) {})(bar)`, the range `)(bar)`
145                              * should get replaced with `(bar))`.
146                              */
147                             const parenAfter = sourceCode.getTokenAfter(innerNode);
148
149                             return fixer.replaceTextRange(
150                                 [parenAfter.range[0], node.range[1]],
151                                 `${sourceCode.getText().slice(parenAfter.range[1], node.range[1])})`
152                             );
153                         }
154                     });
155                 }
156             }
157         };
158
159     }
160 };