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/rules/array-element-newline"
25 fixable
: "whitespace",
32 enum: ["always", "never", "consistent"]
41 type
: ["integer", "null"],
45 additionalProperties
: false
54 $ref
: "#/definitions/basicConfig"
60 $ref
: "#/definitions/basicConfig"
63 $ref
: "#/definitions/basicConfig"
66 additionalProperties
: false,
75 unexpectedLineBreak
: "There should be no linebreak here.",
76 missingLineBreak
: "There should be a linebreak after this element."
81 const sourceCode
= context
.getSourceCode();
83 //----------------------------------------------------------------------
85 //----------------------------------------------------------------------
88 * Normalizes a given option value.
89 * @param {string|Object|undefined} providedOption An option value to parse.
90 * @returns {{multiline: boolean, minItems: number}} Normalized option object.
92 function normalizeOptionValue(providedOption
) {
93 let consistent
= false;
94 let multiline
= false;
97 const option
= providedOption
|| "always";
99 if (!option
|| option
=== "always" || option
.minItems
=== 0) {
101 } else if (option
=== "never") {
102 minItems
= Number
.POSITIVE_INFINITY
;
103 } else if (option
=== "consistent") {
105 minItems
= Number
.POSITIVE_INFINITY
;
107 multiline
= Boolean(option
.multiline
);
108 minItems
= option
.minItems
|| Number
.POSITIVE_INFINITY
;
111 return { consistent
, multiline
, minItems
};
115 * Normalizes a given option value.
116 * @param {string|Object|undefined} options An option value to parse.
117 * @returns {{ArrayExpression: {multiline: boolean, minItems: number}, ArrayPattern: {multiline: boolean, minItems: number}}} Normalized option object.
119 function normalizeOptions(options
) {
120 if (options
&& (options
.ArrayExpression
|| options
.ArrayPattern
)) {
121 let expressionOptions
, patternOptions
;
123 if (options
.ArrayExpression
) {
124 expressionOptions
= normalizeOptionValue(options
.ArrayExpression
);
127 if (options
.ArrayPattern
) {
128 patternOptions
= normalizeOptionValue(options
.ArrayPattern
);
131 return { ArrayExpression
: expressionOptions
, ArrayPattern
: patternOptions
};
134 const value
= normalizeOptionValue(options
);
136 return { ArrayExpression
: value
, ArrayPattern
: value
};
140 * Reports that there shouldn't be a line break after the first token
141 * @param {Token} token The token to use for the report.
144 function reportNoLineBreak(token
) {
145 const tokenBefore
= sourceCode
.getTokenBefore(token
, { includeComments
: true });
149 start
: tokenBefore
.loc
.end
,
152 messageId
: "unexpectedLineBreak",
154 if (astUtils
.isCommentToken(tokenBefore
)) {
158 if (!astUtils
.isTokenOnSameLine(tokenBefore
, token
)) {
159 return fixer
.replaceTextRange([tokenBefore
.range
[1], token
.range
[0]], " ");
163 * This will check if the comma is on the same line as the next element
176 const twoTokensBefore
= sourceCode
.getTokenBefore(tokenBefore
, { includeComments
: true });
178 if (astUtils
.isCommentToken(twoTokensBefore
)) {
182 return fixer
.replaceTextRange([twoTokensBefore
.range
[1], tokenBefore
.range
[0]], "");
189 * Reports that there should be a line break after the first token
190 * @param {Token} token The token to use for the report.
193 function reportRequiredLineBreak(token
) {
194 const tokenBefore
= sourceCode
.getTokenBefore(token
, { includeComments
: true });
198 start
: tokenBefore
.loc
.end
,
201 messageId
: "missingLineBreak",
203 return fixer
.replaceTextRange([tokenBefore
.range
[1], token
.range
[0]], "\n");
209 * Reports a given node if it violated this rule.
210 * @param {ASTNode} node A node to check. This is an ObjectExpression node or an ObjectPattern node.
213 function check(node
) {
214 const elements
= node
.elements
;
215 const normalizedOptions
= normalizeOptions(context
.options
[0]);
216 const options
= normalizedOptions
[node
.type
];
222 let elementBreak
= false;
226 * loop through every element and check
227 * if at least one element has linebreaks inside
228 * this ensures that following is not valid (due to elements are on the same line):
236 if (options
.multiline
) {
237 elementBreak
= elements
238 .filter(element
=> element
!== null)
239 .some(element
=> element
.loc
.start
.line
!== element
.loc
.end
.line
);
242 const linebreaksCount
= node
.elements
.map((element
, i
) => {
243 const previousElement
= elements
[i
- 1];
245 if (i
=== 0 || element
=== null || previousElement
=== null) {
249 const commaToken
= sourceCode
.getFirstTokenBetween(previousElement
, element
, astUtils
.isCommaToken
);
250 const lastTokenOfPreviousElement
= sourceCode
.getTokenBefore(commaToken
);
251 const firstTokenOfCurrentElement
= sourceCode
.getTokenAfter(commaToken
);
253 return !astUtils
.isTokenOnSameLine(lastTokenOfPreviousElement
, firstTokenOfCurrentElement
);
254 }).filter(isBreak
=> isBreak
=== true).length
;
256 const needsLinebreaks
= (
257 elements
.length
>= options
.minItems
||
263 options
.consistent
&&
264 linebreaksCount
> 0 &&
265 linebreaksCount
< node
.elements
.length
269 elements
.forEach((element
, i
) => {
270 const previousElement
= elements
[i
- 1];
272 if (i
=== 0 || element
=== null || previousElement
=== null) {
276 const commaToken
= sourceCode
.getFirstTokenBetween(previousElement
, element
, astUtils
.isCommaToken
);
277 const lastTokenOfPreviousElement
= sourceCode
.getTokenBefore(commaToken
);
278 const firstTokenOfCurrentElement
= sourceCode
.getTokenAfter(commaToken
);
280 if (needsLinebreaks
) {
281 if (astUtils
.isTokenOnSameLine(lastTokenOfPreviousElement
, firstTokenOfCurrentElement
)) {
282 reportRequiredLineBreak(firstTokenOfCurrentElement
);
285 if (!astUtils
.isTokenOnSameLine(lastTokenOfPreviousElement
, firstTokenOfCurrentElement
)) {
286 reportNoLineBreak(firstTokenOfCurrentElement
);
292 //----------------------------------------------------------------------
294 //----------------------------------------------------------------------
298 ArrayExpression
: check