massive update, probably broken
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / max-lines-per-function.js
1 /**
2  * @fileoverview A rule to set the maximum number of line of code in a function.
3  * @author Pete Ward <peteward44@gmail.com>
4  */
5 "use strict";
6
7 //------------------------------------------------------------------------------
8 // Requirements
9 //------------------------------------------------------------------------------
10
11 const astUtils = require("./utils/ast-utils");
12 const { upperCaseFirst } = require("../shared/string-utils");
13
14 //------------------------------------------------------------------------------
15 // Constants
16 //------------------------------------------------------------------------------
17
18 const OPTIONS_SCHEMA = {
19     type: "object",
20     properties: {
21         max: {
22             type: "integer",
23             minimum: 0
24         },
25         skipComments: {
26             type: "boolean"
27         },
28         skipBlankLines: {
29             type: "boolean"
30         },
31         IIFEs: {
32             type: "boolean"
33         }
34     },
35     additionalProperties: false
36 };
37
38 const OPTIONS_OR_INTEGER_SCHEMA = {
39     oneOf: [
40         OPTIONS_SCHEMA,
41         {
42             type: "integer",
43             minimum: 1
44         }
45     ]
46 };
47
48 /**
49  * Given a list of comment nodes, return a map with numeric keys (source code line numbers) and comment token values.
50  * @param {Array} comments An array of comment nodes.
51  * @returns {Map.<string,Node>} A map with numeric keys (source code line numbers) and comment token values.
52  */
53 function getCommentLineNumbers(comments) {
54     const map = new Map();
55
56     comments.forEach(comment => {
57         for (let i = comment.loc.start.line; i <= comment.loc.end.line; i++) {
58             map.set(i, comment);
59         }
60     });
61     return map;
62 }
63
64 //------------------------------------------------------------------------------
65 // Rule Definition
66 //------------------------------------------------------------------------------
67
68 module.exports = {
69     meta: {
70         type: "suggestion",
71
72         docs: {
73             description: "enforce a maximum number of lines of code in a function",
74             category: "Stylistic Issues",
75             recommended: false,
76             url: "https://eslint.org/docs/rules/max-lines-per-function"
77         },
78
79         schema: [
80             OPTIONS_OR_INTEGER_SCHEMA
81         ],
82         messages: {
83             exceed: "{{name}} has too many lines ({{lineCount}}). Maximum allowed is {{maxLines}}."
84         }
85     },
86
87     create(context) {
88         const sourceCode = context.getSourceCode();
89         const lines = sourceCode.lines;
90
91         const option = context.options[0];
92         let maxLines = 50;
93         let skipComments = false;
94         let skipBlankLines = false;
95         let IIFEs = false;
96
97         if (typeof option === "object") {
98             maxLines = typeof option.max === "number" ? option.max : 50;
99             skipComments = !!option.skipComments;
100             skipBlankLines = !!option.skipBlankLines;
101             IIFEs = !!option.IIFEs;
102         } else if (typeof option === "number") {
103             maxLines = option;
104         }
105
106         const commentLineNumbers = getCommentLineNumbers(sourceCode.getAllComments());
107
108         //--------------------------------------------------------------------------
109         // Helpers
110         //--------------------------------------------------------------------------
111
112         /**
113          * Tells if a comment encompasses the entire line.
114          * @param {string} line The source line with a trailing comment
115          * @param {number} lineNumber The one-indexed line number this is on
116          * @param {ASTNode} comment The comment to remove
117          * @returns {boolean} If the comment covers the entire line
118          */
119         function isFullLineComment(line, lineNumber, comment) {
120             const start = comment.loc.start,
121                 end = comment.loc.end,
122                 isFirstTokenOnLine = start.line === lineNumber && !line.slice(0, start.column).trim(),
123                 isLastTokenOnLine = end.line === lineNumber && !line.slice(end.column).trim();
124
125             return comment &&
126                 (start.line < lineNumber || isFirstTokenOnLine) &&
127                 (end.line > lineNumber || isLastTokenOnLine);
128         }
129
130         /**
131          * Identifies is a node is a FunctionExpression which is part of an IIFE
132          * @param {ASTNode} node Node to test
133          * @returns {boolean} True if it's an IIFE
134          */
135         function isIIFE(node) {
136             return (node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") && node.parent && node.parent.type === "CallExpression" && node.parent.callee === node;
137         }
138
139         /**
140          * Identifies is a node is a FunctionExpression which is embedded within a MethodDefinition or Property
141          * @param {ASTNode} node Node to test
142          * @returns {boolean} True if it's a FunctionExpression embedded within a MethodDefinition or Property
143          */
144         function isEmbedded(node) {
145             if (!node.parent) {
146                 return false;
147             }
148             if (node !== node.parent.value) {
149                 return false;
150             }
151             if (node.parent.type === "MethodDefinition") {
152                 return true;
153             }
154             if (node.parent.type === "Property") {
155                 return node.parent.method === true || node.parent.kind === "get" || node.parent.kind === "set";
156             }
157             return false;
158         }
159
160         /**
161          * Count the lines in the function
162          * @param {ASTNode} funcNode Function AST node
163          * @returns {void}
164          * @private
165          */
166         function processFunction(funcNode) {
167             const node = isEmbedded(funcNode) ? funcNode.parent : funcNode;
168
169             if (!IIFEs && isIIFE(node)) {
170                 return;
171             }
172             let lineCount = 0;
173
174             for (let i = node.loc.start.line - 1; i < node.loc.end.line; ++i) {
175                 const line = lines[i];
176
177                 if (skipComments) {
178                     if (commentLineNumbers.has(i + 1) && isFullLineComment(line, i + 1, commentLineNumbers.get(i + 1))) {
179                         continue;
180                     }
181                 }
182
183                 if (skipBlankLines) {
184                     if (line.match(/^\s*$/u)) {
185                         continue;
186                     }
187                 }
188
189                 lineCount++;
190             }
191
192             if (lineCount > maxLines) {
193                 const name = upperCaseFirst(astUtils.getFunctionNameWithKind(funcNode));
194
195                 context.report({
196                     node,
197                     messageId: "exceed",
198                     data: { name, lineCount, maxLines }
199                 });
200             }
201         }
202
203         //--------------------------------------------------------------------------
204         // Public API
205         //--------------------------------------------------------------------------
206
207         return {
208             FunctionDeclaration: processFunction,
209             FunctionExpression: processFunction,
210             ArrowFunctionExpression: processFunction
211         };
212     }
213 };