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