2 * @fileoverview Rule to enforce line breaks after each array element
3 * @author Jan Peer Stöcklmair <https://github.com/JPeer264>
8 const astUtils
= require("./utils/ast-utils");
10 //------------------------------------------------------------------------------
12 //------------------------------------------------------------------------------
14 /** @type {import('../shared/types').Rule} */
20 description
: "Enforce line breaks after each array element",
22 url
: "https://eslint.org/docs/latest/rules/array-element-newline"
25 fixable
: "whitespace",
32 enum: ["always", "never", "consistent"]
41 type
: ["integer", "null"],
45 additionalProperties
: false
55 $ref
: "#/definitions/basicConfig"
61 $ref
: "#/definitions/basicConfig"
64 $ref
: "#/definitions/basicConfig"
67 additionalProperties
: false,
76 unexpectedLineBreak
: "There should be no linebreak here.",
77 missingLineBreak
: "There should be a linebreak after this element."
82 const sourceCode
= context
.sourceCode
;
84 //----------------------------------------------------------------------
86 //----------------------------------------------------------------------
89 * Normalizes a given option value.
90 * @param {string|Object|undefined} providedOption An option value to parse.
91 * @returns {{multiline: boolean, minItems: number}} Normalized option object.
93 function normalizeOptionValue(providedOption
) {
94 let consistent
= false;
95 let multiline
= false;
98 const option
= providedOption
|| "always";
100 if (!option
|| option
=== "always" || option
.minItems
=== 0) {
102 } else if (option
=== "never") {
103 minItems
= Number
.POSITIVE_INFINITY
;
104 } else if (option
=== "consistent") {
106 minItems
= Number
.POSITIVE_INFINITY
;
108 multiline
= Boolean(option
.multiline
);
109 minItems
= option
.minItems
|| Number
.POSITIVE_INFINITY
;
112 return { consistent
, multiline
, minItems
};
116 * Normalizes a given option value.
117 * @param {string|Object|undefined} options An option value to parse.
118 * @returns {{ArrayExpression: {multiline: boolean, minItems: number}, ArrayPattern: {multiline: boolean, minItems: number}}} Normalized option object.
120 function normalizeOptions(options
) {
121 if (options
&& (options
.ArrayExpression
|| options
.ArrayPattern
)) {
122 let expressionOptions
, patternOptions
;
124 if (options
.ArrayExpression
) {
125 expressionOptions
= normalizeOptionValue(options
.ArrayExpression
);
128 if (options
.ArrayPattern
) {
129 patternOptions
= normalizeOptionValue(options
.ArrayPattern
);
132 return { ArrayExpression
: expressionOptions
, ArrayPattern
: patternOptions
};
135 const value
= normalizeOptionValue(options
);
137 return { ArrayExpression
: value
, ArrayPattern
: value
};
141 * Reports that there shouldn't be a line break after the first token
142 * @param {Token} token The token to use for the report.
145 function reportNoLineBreak(token
) {
146 const tokenBefore
= sourceCode
.getTokenBefore(token
, { includeComments
: true });
150 start
: tokenBefore
.loc
.end
,
153 messageId
: "unexpectedLineBreak",
155 if (astUtils
.isCommentToken(tokenBefore
)) {
159 if (!astUtils
.isTokenOnSameLine(tokenBefore
, token
)) {
160 return fixer
.replaceTextRange([tokenBefore
.range
[1], token
.range
[0]], " ");
164 * This will check if the comma is on the same line as the next element
177 const twoTokensBefore
= sourceCode
.getTokenBefore(tokenBefore
, { includeComments
: true });
179 if (astUtils
.isCommentToken(twoTokensBefore
)) {
183 return fixer
.replaceTextRange([twoTokensBefore
.range
[1], tokenBefore
.range
[0]], "");
190 * Reports that there should be a line break after the first token
191 * @param {Token} token The token to use for the report.
194 function reportRequiredLineBreak(token
) {
195 const tokenBefore
= sourceCode
.getTokenBefore(token
, { includeComments
: true });
199 start
: tokenBefore
.loc
.end
,
202 messageId
: "missingLineBreak",
204 return fixer
.replaceTextRange([tokenBefore
.range
[1], token
.range
[0]], "\n");
210 * Reports a given node if it violated this rule.
211 * @param {ASTNode} node A node to check. This is an ObjectExpression node or an ObjectPattern node.
214 function check(node
) {
215 const elements
= node
.elements
;
216 const normalizedOptions
= normalizeOptions(context
.options
[0]);
217 const options
= normalizedOptions
[node
.type
];
223 let elementBreak
= false;
227 * loop through every element and check
228 * if at least one element has linebreaks inside
229 * this ensures that following is not valid (due to elements are on the same line):
237 if (options
.multiline
) {
238 elementBreak
= elements
239 .filter(element
=> element
!== null)
240 .some(element
=> element
.loc
.start
.line
!== element
.loc
.end
.line
);
243 const linebreaksCount
= node
.elements
.map((element
, i
) => {
244 const previousElement
= elements
[i
- 1];
246 if (i
=== 0 || element
=== null || previousElement
=== null) {
250 const commaToken
= sourceCode
.getFirstTokenBetween(previousElement
, element
, astUtils
.isCommaToken
);
251 const lastTokenOfPreviousElement
= sourceCode
.getTokenBefore(commaToken
);
252 const firstTokenOfCurrentElement
= sourceCode
.getTokenAfter(commaToken
);
254 return !astUtils
.isTokenOnSameLine(lastTokenOfPreviousElement
, firstTokenOfCurrentElement
);
255 }).filter(isBreak
=> isBreak
=== true).length
;
257 const needsLinebreaks
= (
258 elements
.length
>= options
.minItems
||
264 options
.consistent
&&
265 linebreaksCount
> 0 &&
266 linebreaksCount
< node
.elements
.length
270 elements
.forEach((element
, i
) => {
271 const previousElement
= elements
[i
- 1];
273 if (i
=== 0 || element
=== null || previousElement
=== null) {
277 const commaToken
= sourceCode
.getFirstTokenBetween(previousElement
, element
, astUtils
.isCommaToken
);
278 const lastTokenOfPreviousElement
= sourceCode
.getTokenBefore(commaToken
);
279 const firstTokenOfCurrentElement
= sourceCode
.getTokenAfter(commaToken
);
281 if (needsLinebreaks
) {
282 if (astUtils
.isTokenOnSameLine(lastTokenOfPreviousElement
, firstTokenOfCurrentElement
)) {
283 reportRequiredLineBreak(firstTokenOfCurrentElement
);
286 if (!astUtils
.isTokenOnSameLine(lastTokenOfPreviousElement
, firstTokenOfCurrentElement
)) {
287 reportNoLineBreak(firstTokenOfCurrentElement
);
293 //----------------------------------------------------------------------
295 //----------------------------------------------------------------------
299 ArrayExpression
: check