2 * @fileoverview Rule to flag when IIFE is not wrapped in parens
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const astUtils = require("./utils/ast-utils");
14 //------------------------------------------------------------------------------
16 //------------------------------------------------------------------------------
23 description: "require parentheses around immediate `function` invocations",
24 category: "Best Practices",
26 url: "https://eslint.org/docs/rules/wrap-iife"
31 enum: ["outside", "inside", "any"]
36 functionPrototypeMethods: {
41 additionalProperties: false
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."
55 const style = context.options[0] || "outside";
56 const includeFunctionPrototypeMethods = context.options[1] && context.options[1].functionPrototypeMethods;
58 const sourceCode = context.getSourceCode();
61 * Check if the node is wrapped in ()
62 * @param {ASTNode} node node to evaluate
63 * @returns {boolean} True if it is wrapped
66 function wrapped(node) {
67 return astUtils.isParenthesised(sourceCode, node);
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
75 function getFunctionNodeFromIIFE(node) {
76 const callee = node.callee;
78 if (callee.type === "FunctionExpression") {
82 if (includeFunctionPrototypeMethods &&
83 callee.type === "MemberExpression" &&
84 callee.object.type === "FunctionExpression" &&
85 (astUtils.getStaticPropertyName(callee) === "call" || astUtils.getStaticPropertyName(callee) === "apply")
95 CallExpression(node) {
96 const innerNode = getFunctionNodeFromIIFE(node);
102 const callExpressionWrapped = wrapped(node),
103 functionExpressionWrapped = wrapped(innerNode);
105 if (!callExpressionWrapped && !functionExpressionWrapped) {
108 messageId: "wrapInvocation",
110 const nodeToSurround = style === "inside" ? innerNode : node;
112 return fixer.replaceText(nodeToSurround, `(${sourceCode.getText(nodeToSurround)})`);
115 } else if (style === "inside" && !functionExpressionWrapped) {
118 messageId: "wrapExpression",
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.
127 const parenAfter = sourceCode.getTokenAfter(node);
129 return fixer.replaceTextRange(
130 [innerNode.range[1], parenAfter.range[1]],
131 `)${sourceCode.getText().slice(innerNode.range[1], parenAfter.range[0])}`
135 } else if (style === "outside" && !callExpressionWrapped) {
138 messageId: "moveInvocation",
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))`.
147 const parenAfter = sourceCode.getTokenAfter(innerNode);
149 return fixer.replaceTextRange(
150 [parenAfter.range[0], node.range[1]],
151 `${sourceCode.getText().slice(parenAfter.range[1], node.range[1])})`