UNPKG

5.09 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to validate spacing before function paren.
3 * @author Mathias Schreck <https://github.com/lo1tuma>
4 */
5"use strict";
6
7//------------------------------------------------------------------------------
8// Requirements
9//------------------------------------------------------------------------------
10
11const astUtils = require("../ast-utils");
12
13//------------------------------------------------------------------------------
14// Rule Definition
15//------------------------------------------------------------------------------
16
17module.exports = {
18 meta: {
19 docs: {
20 description: "enforce consistent spacing before `function` definition opening parenthesis",
21 category: "Stylistic Issues",
22 recommended: false,
23 url: "https://eslint.org/docs/rules/space-before-function-paren"
24 },
25
26 fixable: "whitespace",
27
28 schema: [
29 {
30 oneOf: [
31 {
32 enum: ["always", "never"]
33 },
34 {
35 type: "object",
36 properties: {
37 anonymous: {
38 enum: ["always", "never", "ignore"]
39 },
40 named: {
41 enum: ["always", "never", "ignore"]
42 },
43 asyncArrow: {
44 enum: ["always", "never", "ignore"]
45 }
46 },
47 additionalProperties: false
48 }
49 ]
50 }
51 ]
52 },
53
54 create(context) {
55 const sourceCode = context.getSourceCode();
56 const baseConfig = typeof context.options[0] === "string" ? context.options[0] : "always";
57 const overrideConfig = typeof context.options[0] === "object" ? context.options[0] : {};
58
59 /**
60 * Determines whether a function has a name.
61 * @param {ASTNode} node The function node.
62 * @returns {boolean} Whether the function has a name.
63 */
64 function isNamedFunction(node) {
65 if (node.id) {
66 return true;
67 }
68
69 const parent = node.parent;
70
71 return parent.type === "MethodDefinition" ||
72 (parent.type === "Property" &&
73 (
74 parent.kind === "get" ||
75 parent.kind === "set" ||
76 parent.method
77 )
78 );
79 }
80
81 /**
82 * Gets the config for a given function
83 * @param {ASTNode} node The function node
84 * @returns {string} "always", "never", or "ignore"
85 */
86 function getConfigForFunction(node) {
87 if (node.type === "ArrowFunctionExpression") {
88
89 // Always ignore non-async functions and arrow functions without parens, e.g. async foo => bar
90 if (node.async && astUtils.isOpeningParenToken(sourceCode.getFirstToken(node, { skip: 1 }))) {
91 return overrideConfig.asyncArrow || baseConfig;
92 }
93 } else if (isNamedFunction(node)) {
94 return overrideConfig.named || baseConfig;
95
96 // `generator-star-spacing` should warn anonymous generators. E.g. `function* () {}`
97 } else if (!node.generator) {
98 return overrideConfig.anonymous || baseConfig;
99 }
100
101 return "ignore";
102 }
103
104 /**
105 * Checks the parens of a function node
106 * @param {ASTNode} node A function node
107 * @returns {void}
108 */
109 function checkFunction(node) {
110 const functionConfig = getConfigForFunction(node);
111
112 if (functionConfig === "ignore") {
113 return;
114 }
115
116 const rightToken = sourceCode.getFirstToken(node, astUtils.isOpeningParenToken);
117 const leftToken = sourceCode.getTokenBefore(rightToken);
118 const hasSpacing = sourceCode.isSpaceBetweenTokens(leftToken, rightToken);
119
120 if (hasSpacing && functionConfig === "never") {
121 context.report({
122 node,
123 loc: leftToken.loc.end,
124 message: "Unexpected space before function parentheses.",
125 fix: fixer => fixer.removeRange([leftToken.range[1], rightToken.range[0]])
126 });
127 } else if (!hasSpacing && functionConfig === "always") {
128 context.report({
129 node,
130 loc: leftToken.loc.end,
131 message: "Missing space before function parentheses.",
132 fix: fixer => fixer.insertTextAfter(leftToken, " ")
133 });
134 }
135 }
136
137 return {
138 ArrowFunctionExpression: checkFunction,
139 FunctionDeclaration: checkFunction,
140 FunctionExpression: checkFunction
141 };
142 }
143};