UNPKG

4.93 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// Rule Definition
9//------------------------------------------------------------------------------
10
11module.exports = {
12 meta: {
13 docs: {
14 description: "enforce consistent spacing before `function` definition opening parenthesis",
15 category: "Stylistic Issues",
16 recommended: false
17 },
18
19 fixable: "whitespace",
20
21 schema: [
22 {
23 oneOf: [
24 {
25 enum: ["always", "never"]
26 },
27 {
28 type: "object",
29 properties: {
30 anonymous: {
31 enum: ["always", "never", "ignore"]
32 },
33 named: {
34 enum: ["always", "never", "ignore"]
35 }
36 },
37 additionalProperties: false
38 }
39 ]
40 }
41 ]
42 },
43
44 create(context) {
45
46 const configuration = context.options[0],
47 sourceCode = context.getSourceCode();
48 let requireAnonymousFunctionSpacing = true,
49 forbidAnonymousFunctionSpacing = false,
50 requireNamedFunctionSpacing = true,
51 forbidNamedFunctionSpacing = false;
52
53 if (typeof configuration === "object") {
54 requireAnonymousFunctionSpacing = (
55 !configuration.anonymous || configuration.anonymous === "always");
56 forbidAnonymousFunctionSpacing = configuration.anonymous === "never";
57 requireNamedFunctionSpacing = (
58 !configuration.named || configuration.named === "always");
59 forbidNamedFunctionSpacing = configuration.named === "never";
60 } else if (configuration === "never") {
61 requireAnonymousFunctionSpacing = false;
62 forbidAnonymousFunctionSpacing = true;
63 requireNamedFunctionSpacing = false;
64 forbidNamedFunctionSpacing = true;
65 }
66
67 /**
68 * Determines whether a function has a name.
69 * @param {ASTNode} node The function node.
70 * @returns {boolean} Whether the function has a name.
71 */
72 function isNamedFunction(node) {
73 if (node.id) {
74 return true;
75 }
76
77 const parent = node.parent;
78
79 return parent.type === "MethodDefinition" ||
80 (parent.type === "Property" &&
81 (
82 parent.kind === "get" ||
83 parent.kind === "set" ||
84 parent.method
85 )
86 );
87 }
88
89 /**
90 * Validates the spacing before function parentheses.
91 * @param {ASTNode} node The node to be validated.
92 * @returns {void}
93 */
94 function validateSpacingBeforeParentheses(node) {
95 const isNamed = isNamedFunction(node);
96 let rightToken;
97
98 if (node.generator && !isNamed) {
99 return;
100 }
101
102 rightToken = sourceCode.getFirstToken(node);
103 while (rightToken.value !== "(") {
104 rightToken = sourceCode.getTokenAfter(rightToken);
105 }
106 const leftToken = sourceCode.getTokenBefore(rightToken);
107 const location = leftToken.loc.end;
108
109 if (sourceCode.isSpaceBetweenTokens(leftToken, rightToken)) {
110 if ((isNamed && forbidNamedFunctionSpacing) || (!isNamed && forbidAnonymousFunctionSpacing)) {
111 context.report({
112 node,
113 loc: location,
114 message: "Unexpected space before function parentheses.",
115 fix(fixer) {
116 return fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
117 }
118 });
119 }
120 } else {
121 if ((isNamed && requireNamedFunctionSpacing) || (!isNamed && requireAnonymousFunctionSpacing)) {
122 context.report({
123 node,
124 loc: location,
125 message: "Missing space before function parentheses.",
126 fix(fixer) {
127 return fixer.insertTextAfter(leftToken, " ");
128 }
129 });
130 }
131 }
132 }
133
134 return {
135 FunctionDeclaration: validateSpacingBeforeParentheses,
136 FunctionExpression: validateSpacingBeforeParentheses
137 };
138 }
139};