UNPKG

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