]>
git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/function-paren-newline.js
18435b7897823c59c6ba2ae50a330e9ffab0a9ad
2 * @fileoverview enforce consistent line breaks inside function parentheses
7 //------------------------------------------------------------------------------
9 //------------------------------------------------------------------------------
11 const astUtils
= require("./utils/ast-utils");
13 //------------------------------------------------------------------------------
15 //------------------------------------------------------------------------------
22 description
: "enforce consistent line breaks inside function parentheses",
24 url
: "https://eslint.org/docs/rules/function-paren-newline"
27 fixable
: "whitespace",
33 enum: ["always", "never", "consistent", "multiline", "multiline-arguments"]
43 additionalProperties
: false
50 expectedBefore
: "Expected newline before ')'.",
51 expectedAfter
: "Expected newline after '('.",
52 expectedBetween
: "Expected newline between arguments/params.",
53 unexpectedBefore
: "Unexpected newline before ')'.",
54 unexpectedAfter
: "Unexpected newline after '('."
59 const sourceCode
= context
.getSourceCode();
60 const rawOption
= context
.options
[0] || "multiline";
61 const multilineOption
= rawOption
=== "multiline";
62 const multilineArgumentsOption
= rawOption
=== "multiline-arguments";
63 const consistentOption
= rawOption
=== "consistent";
66 if (typeof rawOption
=== "object") {
67 minItems
= rawOption
.minItems
;
68 } else if (rawOption
=== "always") {
70 } else if (rawOption
=== "never") {
76 //----------------------------------------------------------------------
78 //----------------------------------------------------------------------
81 * Determines whether there should be newlines inside function parens
82 * @param {ASTNode[]} elements The arguments or parameters in the list
83 * @param {boolean} hasLeftNewline `true` if the left paren has a newline in the current code.
84 * @returns {boolean} `true` if there should be newlines inside the function parens
86 function shouldHaveNewlines(elements
, hasLeftNewline
) {
87 if (multilineArgumentsOption
&& elements
.length
=== 1) {
88 return hasLeftNewline
;
90 if (multilineOption
|| multilineArgumentsOption
) {
91 return elements
.some((element
, index
) => index
!== elements
.length
- 1 && element
.loc
.end
.line
!== elements
[index
+ 1].loc
.start
.line
);
93 if (consistentOption
) {
94 return hasLeftNewline
;
96 return elements
.length
>= minItems
;
101 * @param {Object} parens An object with keys `leftParen` for the left paren token, and `rightParen` for the right paren token
102 * @param {ASTNode[]} elements The arguments or parameters in the list
105 function validateParens(parens
, elements
) {
106 const leftParen
= parens
.leftParen
;
107 const rightParen
= parens
.rightParen
;
108 const tokenAfterLeftParen
= sourceCode
.getTokenAfter(leftParen
);
109 const tokenBeforeRightParen
= sourceCode
.getTokenBefore(rightParen
);
110 const hasLeftNewline
= !astUtils
.isTokenOnSameLine(leftParen
, tokenAfterLeftParen
);
111 const hasRightNewline
= !astUtils
.isTokenOnSameLine(tokenBeforeRightParen
, rightParen
);
112 const needsNewlines
= shouldHaveNewlines(elements
, hasLeftNewline
);
114 if (hasLeftNewline
&& !needsNewlines
) {
117 messageId
: "unexpectedAfter",
119 return sourceCode
.getText().slice(leftParen
.range
[1], tokenAfterLeftParen
.range
[0]).trim()
121 // If there is a comment between the ( and the first element, don't do a fix.
123 : fixer
.removeRange([leftParen
.range
[1], tokenAfterLeftParen
.range
[0]]);
126 } else if (!hasLeftNewline
&& needsNewlines
) {
129 messageId
: "expectedAfter",
130 fix
: fixer
=> fixer
.insertTextAfter(leftParen
, "\n")
134 if (hasRightNewline
&& !needsNewlines
) {
137 messageId
: "unexpectedBefore",
139 return sourceCode
.getText().slice(tokenBeforeRightParen
.range
[1], rightParen
.range
[0]).trim()
141 // If there is a comment between the last element and the ), don't do a fix.
143 : fixer
.removeRange([tokenBeforeRightParen
.range
[1], rightParen
.range
[0]]);
146 } else if (!hasRightNewline
&& needsNewlines
) {
149 messageId
: "expectedBefore",
150 fix
: fixer
=> fixer
.insertTextBefore(rightParen
, "\n")
156 * Validates a list of arguments or parameters
157 * @param {Object} parens An object with keys `leftParen` for the left paren token, and `rightParen` for the right paren token
158 * @param {ASTNode[]} elements The arguments or parameters in the list
161 function validateArguments(parens
, elements
) {
162 const leftParen
= parens
.leftParen
;
163 const tokenAfterLeftParen
= sourceCode
.getTokenAfter(leftParen
);
164 const hasLeftNewline
= !astUtils
.isTokenOnSameLine(leftParen
, tokenAfterLeftParen
);
165 const needsNewlines
= shouldHaveNewlines(elements
, hasLeftNewline
);
167 for (let i
= 0; i
<= elements
.length
- 2; i
++) {
168 const currentElement
= elements
[i
];
169 const nextElement
= elements
[i
+ 1];
170 const hasNewLine
= currentElement
.loc
.end
.line
!== nextElement
.loc
.start
.line
;
172 if (!hasNewLine
&& needsNewlines
) {
174 node
: currentElement
,
175 messageId
: "expectedBetween",
176 fix
: fixer
=> fixer
.insertTextBefore(nextElement
, "\n")
183 * Gets the left paren and right paren tokens of a node.
184 * @param {ASTNode} node The node with parens
185 * @throws {TypeError} Unexecpted node type.
186 * @returns {Object} An object with keys `leftParen` for the left paren token, and `rightParen` for the right paren token.
187 * Can also return `null` if an expression has no parens (e.g. a NewExpression with no arguments, or an ArrowFunctionExpression
188 * with a single parameter)
190 function getParenTokens(node
) {
192 case "NewExpression":
193 if (!node
.arguments
.length
&& !(
194 astUtils
.isOpeningParenToken(sourceCode
.getLastToken(node
, { skip
: 1 })) &&
195 astUtils
.isClosingParenToken(sourceCode
.getLastToken(node
))
198 // If the NewExpression does not have parens (e.g. `new Foo`), return null.
204 case "CallExpression":
206 leftParen
: sourceCode
.getTokenAfter(node
.callee
, astUtils
.isOpeningParenToken
),
207 rightParen
: sourceCode
.getLastToken(node
)
210 case "FunctionDeclaration":
211 case "FunctionExpression": {
212 const leftParen
= sourceCode
.getFirstToken(node
, astUtils
.isOpeningParenToken
);
213 const rightParen
= node
.params
.length
214 ? sourceCode
.getTokenAfter(node
.params
[node
.params
.length
- 1], astUtils
.isClosingParenToken
)
215 : sourceCode
.getTokenAfter(leftParen
);
217 return { leftParen
, rightParen
};
220 case "ArrowFunctionExpression": {
221 const firstToken
= sourceCode
.getFirstToken(node
, { skip
: (node
.async
? 1 : 0) });
223 if (!astUtils
.isOpeningParenToken(firstToken
)) {
225 // If the ArrowFunctionExpression has a single param without parens, return null.
230 leftParen
: firstToken
,
231 rightParen
: sourceCode
.getTokenBefore(node
.body
, astUtils
.isClosingParenToken
)
235 case "ImportExpression": {
236 const leftParen
= sourceCode
.getFirstToken(node
, 1);
237 const rightParen
= sourceCode
.getLastToken(node
);
239 return { leftParen
, rightParen
};
243 throw new TypeError(`unexpected node with type ${node.type}`);
247 //----------------------------------------------------------------------
249 //----------------------------------------------------------------------
253 "ArrowFunctionExpression",
255 "FunctionDeclaration",
256 "FunctionExpression",
260 const parens
= getParenTokens(node
);
263 if (node
.type
=== "ImportExpression") {
264 params
= [node
.source
];
265 } else if (astUtils
.isFunction(node
)) {
266 params
= node
.params
;
268 params
= node
.arguments
;
272 validateParens(parens
, params
);
274 if (multilineArgumentsOption
) {
275 validateArguments(parens
, params
);