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