]>
git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/function-paren-newline.js
894c8e331a716815c2e5d460205074cdc4c5658e
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",
23 category
: "Stylistic Issues",
25 url
: "https://eslint.org/docs/rules/function-paren-newline"
28 fixable
: "whitespace",
34 enum: ["always", "never", "consistent", "multiline", "multiline-arguments"]
44 additionalProperties
: false
51 expectedBefore
: "Expected newline before ')'.",
52 expectedAfter
: "Expected newline after '('.",
53 expectedBetween
: "Expected newline between arguments/params.",
54 unexpectedBefore
: "Unexpected newline before ')'.",
55 unexpectedAfter
: "Unexpected newline after '('."
60 const sourceCode
= context
.getSourceCode();
61 const rawOption
= context
.options
[0] || "multiline";
62 const multilineOption
= rawOption
=== "multiline";
63 const multilineArgumentsOption
= rawOption
=== "multiline-arguments";
64 const consistentOption
= rawOption
=== "consistent";
67 if (typeof rawOption
=== "object") {
68 minItems
= rawOption
.minItems
;
69 } else if (rawOption
=== "always") {
71 } else if (rawOption
=== "never") {
77 //----------------------------------------------------------------------
79 //----------------------------------------------------------------------
82 * Determines whether there should be newlines inside function parens
83 * @param {ASTNode[]} elements The arguments or parameters in the list
84 * @param {boolean} hasLeftNewline `true` if the left paren has a newline in the current code.
85 * @returns {boolean} `true` if there should be newlines inside the function parens
87 function shouldHaveNewlines(elements
, hasLeftNewline
) {
88 if (multilineArgumentsOption
&& elements
.length
=== 1) {
89 return hasLeftNewline
;
91 if (multilineOption
|| multilineArgumentsOption
) {
92 return elements
.some((element
, index
) => index
!== elements
.length
- 1 && element
.loc
.end
.line
!== elements
[index
+ 1].loc
.start
.line
);
94 if (consistentOption
) {
95 return hasLeftNewline
;
97 return elements
.length
>= minItems
;
102 * @param {Object} parens An object with keys `leftParen` for the left paren token, and `rightParen` for the right paren token
103 * @param {ASTNode[]} elements The arguments or parameters in the list
106 function validateParens(parens
, elements
) {
107 const leftParen
= parens
.leftParen
;
108 const rightParen
= parens
.rightParen
;
109 const tokenAfterLeftParen
= sourceCode
.getTokenAfter(leftParen
);
110 const tokenBeforeRightParen
= sourceCode
.getTokenBefore(rightParen
);
111 const hasLeftNewline
= !astUtils
.isTokenOnSameLine(leftParen
, tokenAfterLeftParen
);
112 const hasRightNewline
= !astUtils
.isTokenOnSameLine(tokenBeforeRightParen
, rightParen
);
113 const needsNewlines
= shouldHaveNewlines(elements
, hasLeftNewline
);
115 if (hasLeftNewline
&& !needsNewlines
) {
118 messageId
: "unexpectedAfter",
120 return sourceCode
.getText().slice(leftParen
.range
[1], tokenAfterLeftParen
.range
[0]).trim()
122 // If there is a comment between the ( and the first element, don't do a fix.
124 : fixer
.removeRange([leftParen
.range
[1], tokenAfterLeftParen
.range
[0]]);
127 } else if (!hasLeftNewline
&& needsNewlines
) {
130 messageId
: "expectedAfter",
131 fix
: fixer
=> fixer
.insertTextAfter(leftParen
, "\n")
135 if (hasRightNewline
&& !needsNewlines
) {
138 messageId
: "unexpectedBefore",
140 return sourceCode
.getText().slice(tokenBeforeRightParen
.range
[1], rightParen
.range
[0]).trim()
142 // If there is a comment between the last element and the ), don't do a fix.
144 : fixer
.removeRange([tokenBeforeRightParen
.range
[1], rightParen
.range
[0]]);
147 } else if (!hasRightNewline
&& needsNewlines
) {
150 messageId
: "expectedBefore",
151 fix
: fixer
=> fixer
.insertTextBefore(rightParen
, "\n")
157 * Validates a list of arguments or parameters
158 * @param {Object} parens An object with keys `leftParen` for the left paren token, and `rightParen` for the right paren token
159 * @param {ASTNode[]} elements The arguments or parameters in the list
162 function validateArguments(parens
, elements
) {
163 const leftParen
= parens
.leftParen
;
164 const tokenAfterLeftParen
= sourceCode
.getTokenAfter(leftParen
);
165 const hasLeftNewline
= !astUtils
.isTokenOnSameLine(leftParen
, tokenAfterLeftParen
);
166 const needsNewlines
= shouldHaveNewlines(elements
, hasLeftNewline
);
168 for (let i
= 0; i
<= elements
.length
- 2; i
++) {
169 const currentElement
= elements
[i
];
170 const nextElement
= elements
[i
+ 1];
171 const hasNewLine
= currentElement
.loc
.end
.line
!== nextElement
.loc
.start
.line
;
173 if (!hasNewLine
&& needsNewlines
) {
175 node
: currentElement
,
176 messageId
: "expectedBetween",
177 fix
: fixer
=> fixer
.insertTextBefore(nextElement
, "\n")
184 * Gets the left paren and right paren tokens of a node.
185 * @param {ASTNode} node The node with parens
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
);
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
);