2 * @fileoverview Disallows multiple blank lines.
3 * implementation adapted from the no-trailing-spaces rule.
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
17 description
: "disallow multiple empty lines",
19 url
: "https://eslint.org/docs/rules/no-multiple-empty-lines"
22 fixable
: "whitespace",
42 additionalProperties
: false
47 blankBeginningOfFile
: "Too many blank lines at the beginning of file. Max of {{max}} allowed.",
48 blankEndOfFile
: "Too many blank lines at the end of file. Max of {{max}} allowed.",
49 consecutiveBlank
: "More than {{max}} blank {{pluralizedLines}} not allowed."
55 // Use options.max or 2 as default
60 if (context
.options
.length
) {
61 max
= context
.options
[0].max
;
62 maxEOF
= typeof context
.options
[0].maxEOF
!== "undefined" ? context
.options
[0].maxEOF
: max
;
63 maxBOF
= typeof context
.options
[0].maxBOF
!== "undefined" ? context
.options
[0].maxBOF
: max
;
66 const sourceCode
= context
.getSourceCode();
68 // Swallow the final newline, as some editors add it automatically and we don't want it to cause an issue
69 const allLines
= sourceCode
.lines
[sourceCode
.lines
.length
- 1] === "" ? sourceCode
.lines
.slice(0, -1) : sourceCode
.lines
;
70 const templateLiteralLines
= new Set();
72 //--------------------------------------------------------------------------
74 //--------------------------------------------------------------------------
77 TemplateLiteral(node
) {
78 node
.quasis
.forEach(literalPart
=> {
80 // Empty lines have a semantic meaning if they're inside template literals. Don't count these as empty lines.
81 for (let ignoredLine
= literalPart
.loc
.start
.line
; ignoredLine
< literalPart
.loc
.end
.line
; ignoredLine
++) {
82 templateLiteralLines
.add(ignoredLine
);
86 "Program:exit"(node
) {
89 // Given a list of lines, first get a list of line numbers that are non-empty.
90 .reduce((nonEmptyLineNumbers
, line
, index
) => {
91 if (line
.trim() || templateLiteralLines
.has(index
+ 1)) {
92 nonEmptyLineNumbers
.push(index
+ 1);
94 return nonEmptyLineNumbers
;
97 // Add a value at the end to allow trailing empty lines to be checked.
98 .concat(allLines
.length
+ 1)
100 // Given two line numbers of non-empty lines, report the lines between if the difference is too large.
101 .reduce((lastLineNumber
, lineNumber
) => {
102 let messageId
, maxAllowed
;
104 if (lastLineNumber
=== 0) {
105 messageId
= "blankBeginningOfFile";
107 } else if (lineNumber
=== allLines
.length
+ 1) {
108 messageId
= "blankEndOfFile";
111 messageId
= "consecutiveBlank";
115 if (lineNumber
- lastLineNumber
- 1 > maxAllowed
) {
119 start
: { line
: lastLineNumber
+ maxAllowed
+ 1, column
: 0 },
120 end
: { line
: lineNumber
, column
: 0 }
125 pluralizedLines
: maxAllowed
=== 1 ? "line" : "lines"
128 const rangeStart
= sourceCode
.getIndexFromLoc({ line
: lastLineNumber
+ 1, column
: 0 });
131 * The end of the removal range is usually the start index of the next line.
132 * However, at the end of the file there is no next line, so the end of the
133 * range is just the length of the text.
135 const lineNumberAfterRemovedLines
= lineNumber
- maxAllowed
;
136 const rangeEnd
= lineNumberAfterRemovedLines
<= allLines
.length
137 ? sourceCode
.getIndexFromLoc({ line
: lineNumberAfterRemovedLines
, column
: 0 })
138 : sourceCode
.text
.length
;
140 return fixer
.removeRange([rangeStart
, rangeEnd
]);