UNPKG

6.78 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to control spacing within function calls
3 * @author Matt DuVall <http://www.mattduvall.com>
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const astUtils = require("../ast-utils");
13
14//------------------------------------------------------------------------------
15// Rule Definition
16//------------------------------------------------------------------------------
17
18module.exports = {
19 meta: {
20 docs: {
21 description: "require or disallow spacing between function identifiers and their invocations",
22 category: "Stylistic Issues",
23 recommended: false,
24 url: "https://eslint.org/docs/rules/func-call-spacing"
25 },
26
27 fixable: "whitespace",
28 schema: {
29 anyOf: [
30 {
31 type: "array",
32 items: [
33 {
34 enum: ["never"]
35 }
36 ],
37 minItems: 0,
38 maxItems: 1
39 },
40 {
41 type: "array",
42 items: [
43 {
44 enum: ["always"]
45 },
46 {
47 type: "object",
48 properties: {
49 allowNewlines: {
50 type: "boolean"
51 }
52 },
53 additionalProperties: false
54 }
55 ],
56 minItems: 0,
57 maxItems: 2
58 }
59 ]
60 }
61 },
62
63 create(context) {
64
65 const never = context.options[0] !== "always";
66 const allowNewlines = !never && context.options[1] && context.options[1].allowNewlines;
67 const sourceCode = context.getSourceCode();
68 const text = sourceCode.getText();
69
70 /**
71 * Check if open space is present in a function name
72 * @param {ASTNode} node node to evaluate
73 * @returns {void}
74 * @private
75 */
76 function checkSpacing(node) {
77 const lastToken = sourceCode.getLastToken(node);
78 const lastCalleeToken = sourceCode.getLastToken(node.callee);
79 const parenToken = sourceCode.getFirstTokenBetween(lastCalleeToken, lastToken, astUtils.isOpeningParenToken);
80 const prevToken = parenToken && sourceCode.getTokenBefore(parenToken);
81
82 // Parens in NewExpression are optional
83 if (!(parenToken && parenToken.range[1] < node.range[1])) {
84 return;
85 }
86
87 const textBetweenTokens = text.slice(prevToken.range[1], parenToken.range[0]).replace(/\/\*.*?\*\//g, "");
88 const hasWhitespace = /\s/.test(textBetweenTokens);
89 const hasNewline = hasWhitespace && astUtils.LINEBREAK_MATCHER.test(textBetweenTokens);
90
91 /*
92 * never allowNewlines hasWhitespace hasNewline message
93 * F F F F Missing space between function name and paren.
94 * F F F T (Invalid `!hasWhitespace && hasNewline`)
95 * F F T T Unexpected newline between function name and paren.
96 * F F T F (OK)
97 * F T T F (OK)
98 * F T T T (OK)
99 * F T F T (Invalid `!hasWhitespace && hasNewline`)
100 * F T F F Missing space between function name and paren.
101 * T T F F (Invalid `never && allowNewlines`)
102 * T T F T (Invalid `!hasWhitespace && hasNewline`)
103 * T T T T (Invalid `never && allowNewlines`)
104 * T T T F (Invalid `never && allowNewlines`)
105 * T F T F Unexpected space between function name and paren.
106 * T F T T Unexpected space between function name and paren.
107 * T F F T (Invalid `!hasWhitespace && hasNewline`)
108 * T F F F (OK)
109 *
110 * T T Unexpected space between function name and paren.
111 * F F Missing space between function name and paren.
112 * F F T Unexpected newline between function name and paren.
113 */
114
115 if (never && hasWhitespace) {
116 context.report({
117 node,
118 loc: lastCalleeToken.loc.start,
119 message: "Unexpected space between function name and paren.",
120 fix(fixer) {
121
122 /*
123 * Only autofix if there is no newline
124 * https://github.com/eslint/eslint/issues/7787
125 */
126 if (!hasNewline) {
127 return fixer.removeRange([prevToken.range[1], parenToken.range[0]]);
128 }
129
130 return null;
131 }
132 });
133 } else if (!never && !hasWhitespace) {
134 context.report({
135 node,
136 loc: lastCalleeToken.loc.start,
137 message: "Missing space between function name and paren.",
138 fix(fixer) {
139 return fixer.insertTextBefore(parenToken, " ");
140 }
141 });
142 } else if (!never && !allowNewlines && hasNewline) {
143 context.report({
144 node,
145 loc: lastCalleeToken.loc.start,
146 message: "Unexpected newline between function name and paren.",
147 fix(fixer) {
148 return fixer.replaceTextRange([prevToken.range[1], parenToken.range[0]], " ");
149 }
150 });
151 }
152 }
153
154 return {
155 CallExpression: checkSpacing,
156 NewExpression: checkSpacing
157 };
158
159 }
160};