2 * @fileoverview Disallows multiple blank lines.
3 * implementation adapted from the no-trailing-spaces rule.
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 /** @type {import('../shared/types').Rule} */
18 description
: "Disallow multiple empty lines",
20 url
: "https://eslint.org/docs/rules/no-multiple-empty-lines"
23 fixable
: "whitespace",
43 additionalProperties
: false
48 blankBeginningOfFile
: "Too many blank lines at the beginning of file. Max of {{max}} allowed.",
49 blankEndOfFile
: "Too many blank lines at the end of file. Max of {{max}} allowed.",
50 consecutiveBlank
: "More than {{max}} blank {{pluralizedLines}} not allowed."
56 // Use options.max or 2 as default
61 if (context
.options
.length
) {
62 max
= context
.options
[0].max
;
63 maxEOF
= typeof context
.options
[0].maxEOF
!== "undefined" ? context
.options
[0].maxEOF
: max
;
64 maxBOF
= typeof context
.options
[0].maxBOF
!== "undefined" ? context
.options
[0].maxBOF
: max
;
67 const sourceCode
= context
.getSourceCode();
69 // Swallow the final newline, as some editors add it automatically and we don't want it to cause an issue
70 const allLines
= sourceCode
.lines
[sourceCode
.lines
.length
- 1] === "" ? sourceCode
.lines
.slice(0, -1) : sourceCode
.lines
;
71 const templateLiteralLines
= new Set();
73 //--------------------------------------------------------------------------
75 //--------------------------------------------------------------------------
78 TemplateLiteral(node
) {
79 node
.quasis
.forEach(literalPart
=> {
81 // Empty lines have a semantic meaning if they're inside template literals. Don't count these as empty lines.
82 for (let ignoredLine
= literalPart
.loc
.start
.line
; ignoredLine
< literalPart
.loc
.end
.line
; ignoredLine
++) {
83 templateLiteralLines
.add(ignoredLine
);
87 "Program:exit"(node
) {
90 // Given a list of lines, first get a list of line numbers that are non-empty.
91 .reduce((nonEmptyLineNumbers
, line
, index
) => {
92 if (line
.trim() || templateLiteralLines
.has(index
+ 1)) {
93 nonEmptyLineNumbers
.push(index
+ 1);
95 return nonEmptyLineNumbers
;
98 // Add a value at the end to allow trailing empty lines to be checked.
99 .concat(allLines
.length
+ 1)
101 // Given two line numbers of non-empty lines, report the lines between if the difference is too large.
102 .reduce((lastLineNumber
, lineNumber
) => {
103 let messageId
, maxAllowed
;
105 if (lastLineNumber
=== 0) {
106 messageId
= "blankBeginningOfFile";
108 } else if (lineNumber
=== allLines
.length
+ 1) {
109 messageId
= "blankEndOfFile";
112 messageId
= "consecutiveBlank";
116 if (lineNumber
- lastLineNumber
- 1 > maxAllowed
) {
120 start
: { line
: lastLineNumber
+ maxAllowed
+ 1, column
: 0 },
121 end
: { line
: lineNumber
, column
: 0 }
126 pluralizedLines
: maxAllowed
=== 1 ? "line" : "lines"
129 const rangeStart
= sourceCode
.getIndexFromLoc({ line
: lastLineNumber
+ 1, column
: 0 });
132 * The end of the removal range is usually the start index of the next line.
133 * However, at the end of the file there is no next line, so the end of the
134 * range is just the length of the text.
136 const lineNumberAfterRemovedLines
= lineNumber
- maxAllowed
;
137 const rangeEnd
= lineNumberAfterRemovedLines
<= allLines
.length
138 ? sourceCode
.getIndexFromLoc({ line
: lineNumberAfterRemovedLines
, column
: 0 })
139 : sourceCode
.text
.length
;
141 return fixer
.removeRange([rangeStart
, rangeEnd
]);