UNPKG

7.27 kBJavaScriptView Raw
1/**
2 * @fileoverview Specify the maximum number of statements allowed per line.
3 * @author Kenneth Williams
4 */
5"use strict";
6
7//------------------------------------------------------------------------------
8// Requirements
9//------------------------------------------------------------------------------
10
11const astUtils = require("../ast-utils");
12
13//------------------------------------------------------------------------------
14// Rule Definition
15//------------------------------------------------------------------------------
16
17module.exports = {
18 meta: {
19 docs: {
20 description: "enforce a maximum number of statements allowed per line",
21 category: "Stylistic Issues",
22 recommended: false
23 },
24
25 schema: [
26 {
27 type: "object",
28 properties: {
29 max: {
30 type: "integer",
31 minimum: 1
32 }
33 },
34 additionalProperties: false
35 }
36 ]
37 },
38
39 create(context) {
40
41 const sourceCode = context.getSourceCode(),
42 options = context.options[0] || {},
43 maxStatementsPerLine = typeof options.max !== "undefined" ? options.max : 1,
44 message = "This line has {{numberOfStatementsOnThisLine}} {{statements}}. Maximum allowed is {{maxStatementsPerLine}}.";
45
46 let lastStatementLine = 0,
47 numberOfStatementsOnThisLine = 0,
48 firstExtraStatement;
49
50 //--------------------------------------------------------------------------
51 // Helpers
52 //--------------------------------------------------------------------------
53
54 const SINGLE_CHILD_ALLOWED = /^(?:(?:DoWhile|For|ForIn|ForOf|If|Labeled|While)Statement|Export(?:Default|Named)Declaration)$/;
55
56 /**
57 * Reports with the first extra statement, and clears it.
58 *
59 * @returns {void}
60 */
61 function reportFirstExtraStatementAndClear() {
62 if (firstExtraStatement) {
63 context.report({
64 node: firstExtraStatement,
65 message,
66 data: {
67 numberOfStatementsOnThisLine,
68 maxStatementsPerLine,
69 statements: numberOfStatementsOnThisLine === 1 ? "statement" : "statements"
70 }
71 });
72 }
73 firstExtraStatement = null;
74 }
75
76 /**
77 * Gets the actual last token of a given node.
78 *
79 * @param {ASTNode} node - A node to get. This is a node except EmptyStatement.
80 * @returns {Token} The actual last token.
81 */
82 function getActualLastToken(node) {
83 return sourceCode.getLastToken(node, astUtils.isNotSemicolonToken);
84 }
85
86 /**
87 * Addresses a given node.
88 * It updates the state of this rule, then reports the node if the node violated this rule.
89 *
90 * @param {ASTNode} node - A node to check.
91 * @returns {void}
92 */
93 function enterStatement(node) {
94 const line = node.loc.start.line;
95
96 // Skip to allow non-block statements if this is direct child of control statements.
97 // `if (a) foo();` is counted as 1.
98 // But `if (a) foo(); else foo();` should be counted as 2.
99 if (SINGLE_CHILD_ALLOWED.test(node.parent.type) &&
100 node.parent.alternate !== node
101 ) {
102 return;
103 }
104
105 // Update state.
106 if (line === lastStatementLine) {
107 numberOfStatementsOnThisLine += 1;
108 } else {
109 reportFirstExtraStatementAndClear();
110 numberOfStatementsOnThisLine = 1;
111 lastStatementLine = line;
112 }
113
114 // Reports if the node violated this rule.
115 if (numberOfStatementsOnThisLine === maxStatementsPerLine + 1) {
116 firstExtraStatement = firstExtraStatement || node;
117 }
118 }
119
120 /**
121 * Updates the state of this rule with the end line of leaving node to check with the next statement.
122 *
123 * @param {ASTNode} node - A node to check.
124 * @returns {void}
125 */
126 function leaveStatement(node) {
127 const line = getActualLastToken(node).loc.end.line;
128
129 // Update state.
130 if (line !== lastStatementLine) {
131 reportFirstExtraStatementAndClear();
132 numberOfStatementsOnThisLine = 1;
133 lastStatementLine = line;
134 }
135 }
136
137 //--------------------------------------------------------------------------
138 // Public API
139 //--------------------------------------------------------------------------
140
141 return {
142 BreakStatement: enterStatement,
143 ClassDeclaration: enterStatement,
144 ContinueStatement: enterStatement,
145 DebuggerStatement: enterStatement,
146 DoWhileStatement: enterStatement,
147 ExpressionStatement: enterStatement,
148 ForInStatement: enterStatement,
149 ForOfStatement: enterStatement,
150 ForStatement: enterStatement,
151 FunctionDeclaration: enterStatement,
152 IfStatement: enterStatement,
153 ImportDeclaration: enterStatement,
154 LabeledStatement: enterStatement,
155 ReturnStatement: enterStatement,
156 SwitchStatement: enterStatement,
157 ThrowStatement: enterStatement,
158 TryStatement: enterStatement,
159 VariableDeclaration: enterStatement,
160 WhileStatement: enterStatement,
161 WithStatement: enterStatement,
162 ExportNamedDeclaration: enterStatement,
163 ExportDefaultDeclaration: enterStatement,
164 ExportAllDeclaration: enterStatement,
165
166 "BreakStatement:exit": leaveStatement,
167 "ClassDeclaration:exit": leaveStatement,
168 "ContinueStatement:exit": leaveStatement,
169 "DebuggerStatement:exit": leaveStatement,
170 "DoWhileStatement:exit": leaveStatement,
171 "ExpressionStatement:exit": leaveStatement,
172 "ForInStatement:exit": leaveStatement,
173 "ForOfStatement:exit": leaveStatement,
174 "ForStatement:exit": leaveStatement,
175 "FunctionDeclaration:exit": leaveStatement,
176 "IfStatement:exit": leaveStatement,
177 "ImportDeclaration:exit": leaveStatement,
178 "LabeledStatement:exit": leaveStatement,
179 "ReturnStatement:exit": leaveStatement,
180 "SwitchStatement:exit": leaveStatement,
181 "ThrowStatement:exit": leaveStatement,
182 "TryStatement:exit": leaveStatement,
183 "VariableDeclaration:exit": leaveStatement,
184 "WhileStatement:exit": leaveStatement,
185 "WithStatement:exit": leaveStatement,
186 "ExportNamedDeclaration:exit": leaveStatement,
187 "ExportDefaultDeclaration:exit": leaveStatement,
188 "ExportAllDeclaration:exit": leaveStatement,
189 "Program:exit": reportFirstExtraStatementAndClear
190 };
191 }
192};