]> git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/max-lines.js
import 8.4.0 source
[pve-eslint.git] / eslint / lib / rules / max-lines.js
1 /**
2 * @fileoverview enforce a maximum file length
3 * @author Alberto Rodríguez
4 */
5 "use strict";
6
7 //------------------------------------------------------------------------------
8 // Requirements
9 //------------------------------------------------------------------------------
10
11 const astUtils = require("./utils/ast-utils");
12
13 //------------------------------------------------------------------------------
14 // Helpers
15 //------------------------------------------------------------------------------
16
17 /**
18 * Creates an array of numbers from `start` up to, but not including, `end`
19 * @param {number} start The start of the range
20 * @param {number} end The end of the range
21 * @returns {number[]} The range of numbers
22 */
23 function range(start, end) {
24 return [...Array(end - start).keys()].map(x => x + start);
25 }
26
27 //------------------------------------------------------------------------------
28 // Rule Definition
29 //------------------------------------------------------------------------------
30
31 /** @type {import('../shared/types').Rule} */
32 module.exports = {
33 meta: {
34 type: "suggestion",
35
36 docs: {
37 description: "enforce a maximum number of lines per file",
38 recommended: false,
39 url: "https://eslint.org/docs/rules/max-lines"
40 },
41
42 schema: [
43 {
44 oneOf: [
45 {
46 type: "integer",
47 minimum: 0
48 },
49 {
50 type: "object",
51 properties: {
52 max: {
53 type: "integer",
54 minimum: 0
55 },
56 skipComments: {
57 type: "boolean"
58 },
59 skipBlankLines: {
60 type: "boolean"
61 }
62 },
63 additionalProperties: false
64 }
65 ]
66 }
67 ],
68 messages: {
69 exceed:
70 "File has too many lines ({{actual}}). Maximum allowed is {{max}}."
71 }
72 },
73
74 create(context) {
75 const option = context.options[0];
76 let max = 300;
77
78 if (
79 typeof option === "object" &&
80 Object.prototype.hasOwnProperty.call(option, "max")
81 ) {
82 max = option.max;
83 } else if (typeof option === "number") {
84 max = option;
85 }
86
87 const skipComments = option && option.skipComments;
88 const skipBlankLines = option && option.skipBlankLines;
89
90 const sourceCode = context.getSourceCode();
91
92 /**
93 * Returns whether or not a token is a comment node type
94 * @param {Token} token The token to check
95 * @returns {boolean} True if the token is a comment node
96 */
97 function isCommentNodeType(token) {
98 return token && (token.type === "Block" || token.type === "Line");
99 }
100
101 /**
102 * Returns the line numbers of a comment that don't have any code on the same line
103 * @param {Node} comment The comment node to check
104 * @returns {number[]} The line numbers
105 */
106 function getLinesWithoutCode(comment) {
107 let start = comment.loc.start.line;
108 let end = comment.loc.end.line;
109
110 let token;
111
112 token = comment;
113 do {
114 token = sourceCode.getTokenBefore(token, {
115 includeComments: true
116 });
117 } while (isCommentNodeType(token));
118
119 if (token && astUtils.isTokenOnSameLine(token, comment)) {
120 start += 1;
121 }
122
123 token = comment;
124 do {
125 token = sourceCode.getTokenAfter(token, {
126 includeComments: true
127 });
128 } while (isCommentNodeType(token));
129
130 if (token && astUtils.isTokenOnSameLine(comment, token)) {
131 end -= 1;
132 }
133
134 if (start <= end) {
135 return range(start, end + 1);
136 }
137 return [];
138 }
139
140 return {
141 "Program:exit"() {
142 let lines = sourceCode.lines.map((text, i) => ({
143 lineNumber: i + 1,
144 text
145 }));
146
147 /*
148 * If file ends with a linebreak, `sourceCode.lines` will have one extra empty line at the end.
149 * That isn't a real line, so we shouldn't count it.
150 */
151 if (lines.length > 1 && lines[lines.length - 1].text === "") {
152 lines.pop();
153 }
154
155 if (skipBlankLines) {
156 lines = lines.filter(l => l.text.trim() !== "");
157 }
158
159 if (skipComments) {
160 const comments = sourceCode.getAllComments();
161
162 const commentLines = comments.flatMap(getLinesWithoutCode);
163
164 lines = lines.filter(
165 l => !commentLines.includes(l.lineNumber)
166 );
167 }
168
169 if (lines.length > max) {
170 const loc = {
171 start: {
172 line: lines[max].lineNumber,
173 column: 0
174 },
175 end: {
176 line: sourceCode.lines.length,
177 column: sourceCode.lines[sourceCode.lines.length - 1].length
178 }
179 };
180
181 context.report({
182 loc,
183 messageId: "exceed",
184 data: {
185 max,
186 actual: lines.length
187 }
188 });
189 }
190 }
191 };
192 }
193 };