Permalink
Cannot retrieve contributors at this time
| /** | |
| * @fileoverview Rule to flag when IIFE is not wrapped in parens | |
| * @author Ilya Volodin | |
| */ | |
| "use strict"; | |
| //------------------------------------------------------------------------------ | |
| // Requirements | |
| //------------------------------------------------------------------------------ | |
| const astUtils = require("./utils/ast-utils"); | |
| //------------------------------------------------------------------------------ | |
| // Rule Definition | |
| //------------------------------------------------------------------------------ | |
| module.exports = { | |
| meta: { | |
| type: "layout", | |
| docs: { | |
| description: "require parentheses around immediate `function` invocations", | |
| category: "Best Practices", | |
| recommended: false, | |
| url: "https://eslint.org/docs/rules/wrap-iife" | |
| }, | |
| schema: [ | |
| { | |
| enum: ["outside", "inside", "any"] | |
| }, | |
| { | |
| type: "object", | |
| properties: { | |
| functionPrototypeMethods: { | |
| type: "boolean", | |
| default: false | |
| } | |
| }, | |
| additionalProperties: false | |
| } | |
| ], | |
| fixable: "code", | |
| messages: { | |
| wrapInvocation: "Wrap an immediate function invocation in parentheses.", | |
| wrapExpression: "Wrap only the function expression in parens.", | |
| moveInvocation: "Move the invocation into the parens that contain the function." | |
| } | |
| }, | |
| create(context) { | |
| const style = context.options[0] || "outside"; | |
| const includeFunctionPrototypeMethods = context.options[1] && context.options[1].functionPrototypeMethods; | |
| const sourceCode = context.getSourceCode(); | |
| /** | |
| * Check if the node is wrapped in () | |
| * @param {ASTNode} node node to evaluate | |
| * @returns {boolean} True if it is wrapped | |
| * @private | |
| */ | |
| function wrapped(node) { | |
| return astUtils.isParenthesised(sourceCode, node); | |
| } | |
| /** | |
| * Get the function node from an IIFE | |
| * @param {ASTNode} node node to evaluate | |
| * @returns {ASTNode} node that is the function expression of the given IIFE, or null if none exist | |
| */ | |
| function getFunctionNodeFromIIFE(node) { | |
| const callee = node.callee; | |
| if (callee.type === "FunctionExpression") { | |
| return callee; | |
| } | |
| if (includeFunctionPrototypeMethods && | |
| callee.type === "MemberExpression" && | |
| callee.object.type === "FunctionExpression" && | |
| (astUtils.getStaticPropertyName(callee) === "call" || astUtils.getStaticPropertyName(callee) === "apply") | |
| ) { | |
| return callee.object; | |
| } | |
| return null; | |
| } | |
| return { | |
| CallExpression(node) { | |
| const innerNode = getFunctionNodeFromIIFE(node); | |
| if (!innerNode) { | |
| return; | |
| } | |
| const callExpressionWrapped = wrapped(node), | |
| functionExpressionWrapped = wrapped(innerNode); | |
| if (!callExpressionWrapped && !functionExpressionWrapped) { | |
| context.report({ | |
| node, | |
| messageId: "wrapInvocation", | |
| fix(fixer) { | |
| const nodeToSurround = style === "inside" ? innerNode : node; | |
| return fixer.replaceText(nodeToSurround, `(${sourceCode.getText(nodeToSurround)})`); | |
| } | |
| }); | |
| } else if (style === "inside" && !functionExpressionWrapped) { | |
| context.report({ | |
| node, | |
| messageId: "wrapExpression", | |
| fix(fixer) { | |
| /* | |
| * The outer call expression will always be wrapped at this point. | |
| * Replace the range between the end of the function expression and the end of the call expression. | |
| * for example, in `(function(foo) {}(bar))`, the range `(bar))` should get replaced with `)(bar)`. | |
| * Replace the parens from the outer expression, and parenthesize the function expression. | |
| */ | |
| const parenAfter = sourceCode.getTokenAfter(node); | |
| return fixer.replaceTextRange( | |
| [innerNode.range[1], parenAfter.range[1]], | |
| `)${sourceCode.getText().slice(innerNode.range[1], parenAfter.range[0])}` | |
| ); | |
| } | |
| }); | |
| } else if (style === "outside" && !callExpressionWrapped) { | |
| context.report({ | |
| node, | |
| messageId: "moveInvocation", | |
| fix(fixer) { | |
| /* | |
| * The inner function expression will always be wrapped at this point. | |
| * It's only necessary to replace the range between the end of the function expression | |
| * and the call expression. For example, in `(function(foo) {})(bar)`, the range `)(bar)` | |
| * should get replaced with `(bar))`. | |
| */ | |
| const parenAfter = sourceCode.getTokenAfter(innerNode); | |
| return fixer.replaceTextRange( | |
| [parenAfter.range[0], node.range[1]], | |
| `${sourceCode.getText().slice(parenAfter.range[1], node.range[1])})` | |
| ); | |
| } | |
| }); | |
| } | |
| } | |
| }; | |
| } | |
| }; |