massive update, probably broken
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / 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 module.exports = {
32     meta: {
33         type: "suggestion",
34
35         docs: {
36             description: "enforce a maximum number of lines per file",
37             category: "Stylistic Issues",
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         /**
141          * Returns a new array formed by applying a given callback function to each element of the array, and then flattening the result by one level.
142          * TODO(stephenwade): Replace this with array.flatMap when we drop support for Node v10
143          * @param {any[]} array The array to process
144          * @param {Function} fn The function to use
145          * @returns {any[]} The result array
146          */
147         function flatMap(array, fn) {
148             const mapped = array.map(fn);
149             const flattened = [].concat(...mapped);
150
151             return flattened;
152         }
153
154         return {
155             "Program:exit"() {
156                 let lines = sourceCode.lines.map((text, i) => ({
157                     lineNumber: i + 1,
158                     text
159                 }));
160
161                 /*
162                  * If file ends with a linebreak, `sourceCode.lines` will have one extra empty line at the end.
163                  * That isn't a real line, so we shouldn't count it.
164                  */
165                 if (lines.length > 1 && lines[lines.length - 1].text === "") {
166                     lines.pop();
167                 }
168
169                 if (skipBlankLines) {
170                     lines = lines.filter(l => l.text.trim() !== "");
171                 }
172
173                 if (skipComments) {
174                     const comments = sourceCode.getAllComments();
175
176                     const commentLines = flatMap(comments, comment => getLinesWithoutCode(comment));
177
178                     lines = lines.filter(
179                         l => !commentLines.includes(l.lineNumber)
180                     );
181                 }
182
183                 if (lines.length > max) {
184                     const loc = {
185                         start: {
186                             line: lines[max].lineNumber,
187                             column: 0
188                         },
189                         end: {
190                             line: sourceCode.lines.length,
191                             column: sourceCode.lines[sourceCode.lines.length - 1].length
192                         }
193                     };
194
195                     context.report({
196                         loc,
197                         messageId: "exceed",
198                         data: {
199                             max,
200                             actual: lines.length
201                         }
202                     });
203                 }
204             }
205         };
206     }
207 };