• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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: "File has too many lines ({{actual}}). Maximum allowed is {{max}}."
57        }
58    },
59
60    create(context) {
61        const option = context.options[0];
62        let max = 300;
63
64        if (typeof option === "object" && Object.prototype.hasOwnProperty.call(option, "max")) {
65            max = option.max;
66        } else if (typeof option === "number") {
67            max = option;
68        }
69
70        const skipComments = option && option.skipComments;
71        const skipBlankLines = option && option.skipBlankLines;
72
73        const sourceCode = context.getSourceCode();
74
75        /**
76         * Returns whether or not a token is a comment node type
77         * @param {Token} token The token to check
78         * @returns {boolean} True if the token is a comment node
79         */
80        function isCommentNodeType(token) {
81            return token && (token.type === "Block" || token.type === "Line");
82        }
83
84        /**
85         * Returns the line numbers of a comment that don't have any code on the same line
86         * @param {Node} comment The comment node to check
87         * @returns {number[]} The line numbers
88         */
89        function getLinesWithoutCode(comment) {
90            let start = comment.loc.start.line;
91            let end = comment.loc.end.line;
92
93            let token;
94
95            token = comment;
96            do {
97                token = sourceCode.getTokenBefore(token, { includeComments: true });
98            } while (isCommentNodeType(token));
99
100            if (token && astUtils.isTokenOnSameLine(token, comment)) {
101                start += 1;
102            }
103
104            token = comment;
105            do {
106                token = sourceCode.getTokenAfter(token, { includeComments: true });
107            } while (isCommentNodeType(token));
108
109            if (token && astUtils.isTokenOnSameLine(comment, token)) {
110                end -= 1;
111            }
112
113            if (start <= end) {
114                return lodash.range(start, end + 1);
115            }
116            return [];
117        }
118
119        return {
120            "Program:exit"() {
121                let lines = sourceCode.lines.map((text, i) => ({ lineNumber: i + 1, text }));
122
123                if (skipBlankLines) {
124                    lines = lines.filter(l => l.text.trim() !== "");
125                }
126
127                if (skipComments) {
128                    const comments = sourceCode.getAllComments();
129
130                    const commentLines = lodash.flatten(comments.map(comment => getLinesWithoutCode(comment)));
131
132                    lines = lines.filter(l => !lodash.includes(commentLines, l.lineNumber));
133                }
134
135                if (lines.length > max) {
136                    context.report({
137                        loc: { line: 1, column: 0 },
138                        messageId: "exceed",
139                        data: {
140                            max,
141                            actual: lines.length
142                        }
143                    });
144                }
145            }
146        };
147    }
148};
149