| /** | |
| * @fileoverview Specify the maximum number of statements allowed per line. | |
| * @author Kenneth Williams | |
| */ | |
| "use strict"; | |
| //------------------------------------------------------------------------------ | |
| // Requirements | |
| //------------------------------------------------------------------------------ | |
| const astUtils = require("./utils/ast-utils"); | |
| //------------------------------------------------------------------------------ | |
| // Rule Definition | |
| //------------------------------------------------------------------------------ | |
| module.exports = { | |
| meta: { | |
| type: "layout", | |
| docs: { | |
| description: "enforce a maximum number of statements allowed per line", | |
| category: "Stylistic Issues", | |
| recommended: false, | |
| url: "https://eslint.org/docs/rules/max-statements-per-line" | |
| }, | |
| schema: [ | |
| { | |
| type: "object", | |
| properties: { | |
| max: { | |
| type: "integer", | |
| minimum: 1, | |
| default: 1 | |
| } | |
| }, | |
| additionalProperties: false | |
| } | |
| ], | |
| messages: { | |
| exceed: "This line has {{numberOfStatementsOnThisLine}} {{statements}}. Maximum allowed is {{maxStatementsPerLine}}." | |
| } | |
| }, | |
| create(context) { | |
| const sourceCode = context.getSourceCode(), | |
| options = context.options[0] || {}, | |
| maxStatementsPerLine = typeof options.max !== "undefined" ? options.max : 1; | |
| let lastStatementLine = 0, | |
| numberOfStatementsOnThisLine = 0, | |
| firstExtraStatement; | |
| //-------------------------------------------------------------------------- | |
| // Helpers | |
| //-------------------------------------------------------------------------- | |
| const SINGLE_CHILD_ALLOWED = /^(?:(?:DoWhile|For|ForIn|ForOf|If|Labeled|While)Statement|Export(?:Default|Named)Declaration)$/u; | |
| /** | |
| * Reports with the first extra statement, and clears it. | |
| * | |
| * @returns {void} | |
| */ | |
| function reportFirstExtraStatementAndClear() { | |
| if (firstExtraStatement) { | |
| context.report({ | |
| node: firstExtraStatement, | |
| messageId: "exceed", | |
| data: { | |
| numberOfStatementsOnThisLine, | |
| maxStatementsPerLine, | |
| statements: numberOfStatementsOnThisLine === 1 ? "statement" : "statements" | |
| } | |
| }); | |
| } | |
| firstExtraStatement = null; | |
| } | |
| /** | |
| * Gets the actual last token of a given node. | |
| * | |
| * @param {ASTNode} node - A node to get. This is a node except EmptyStatement. | |
| * @returns {Token} The actual last token. | |
| */ | |
| function getActualLastToken(node) { | |
| return sourceCode.getLastToken(node, astUtils.isNotSemicolonToken); | |
| } | |
| /** | |
| * Addresses a given node. | |
| * It updates the state of this rule, then reports the node if the node violated this rule. | |
| * | |
| * @param {ASTNode} node - A node to check. | |
| * @returns {void} | |
| */ | |
| function enterStatement(node) { | |
| const line = node.loc.start.line; | |
| /* | |
| * Skip to allow non-block statements if this is direct child of control statements. | |
| * `if (a) foo();` is counted as 1. | |
| * But `if (a) foo(); else foo();` should be counted as 2. | |
| */ | |
| if (SINGLE_CHILD_ALLOWED.test(node.parent.type) && | |
| node.parent.alternate !== node | |
| ) { | |
| return; | |
| } | |
| // Update state. | |
| if (line === lastStatementLine) { | |
| numberOfStatementsOnThisLine += 1; | |
| } else { | |
| reportFirstExtraStatementAndClear(); | |
| numberOfStatementsOnThisLine = 1; | |
| lastStatementLine = line; | |
| } | |
| // Reports if the node violated this rule. | |
| if (numberOfStatementsOnThisLine === maxStatementsPerLine + 1) { | |
| firstExtraStatement = firstExtraStatement || node; | |
| } | |
| } | |
| /** | |
| * Updates the state of this rule with the end line of leaving node to check with the next statement. | |
| * | |
| * @param {ASTNode} node - A node to check. | |
| * @returns {void} | |
| */ | |
| function leaveStatement(node) { | |
| const line = getActualLastToken(node).loc.end.line; | |
| // Update state. | |
| if (line !== lastStatementLine) { | |
| reportFirstExtraStatementAndClear(); | |
| numberOfStatementsOnThisLine = 1; | |
| lastStatementLine = line; | |
| } | |
| } | |
| //-------------------------------------------------------------------------- | |
| // Public API | |
| //-------------------------------------------------------------------------- | |
| return { | |
| BreakStatement: enterStatement, | |
| ClassDeclaration: enterStatement, | |
| ContinueStatement: enterStatement, | |
| DebuggerStatement: enterStatement, | |
| DoWhileStatement: enterStatement, | |
| ExpressionStatement: enterStatement, | |
| ForInStatement: enterStatement, | |
| ForOfStatement: enterStatement, | |
| ForStatement: enterStatement, | |
| FunctionDeclaration: enterStatement, | |
| IfStatement: enterStatement, | |
| ImportDeclaration: enterStatement, | |
| LabeledStatement: enterStatement, | |
| ReturnStatement: enterStatement, | |
| SwitchStatement: enterStatement, | |
| ThrowStatement: enterStatement, | |
| TryStatement: enterStatement, | |
| VariableDeclaration: enterStatement, | |
| WhileStatement: enterStatement, | |
| WithStatement: enterStatement, | |
| ExportNamedDeclaration: enterStatement, | |
| ExportDefaultDeclaration: enterStatement, | |
| ExportAllDeclaration: enterStatement, | |
| "BreakStatement:exit": leaveStatement, | |
| "ClassDeclaration:exit": leaveStatement, | |
| "ContinueStatement:exit": leaveStatement, | |
| "DebuggerStatement:exit": leaveStatement, | |
| "DoWhileStatement:exit": leaveStatement, | |
| "ExpressionStatement:exit": leaveStatement, | |
| "ForInStatement:exit": leaveStatement, | |
| "ForOfStatement:exit": leaveStatement, | |
| "ForStatement:exit": leaveStatement, | |
| "FunctionDeclaration:exit": leaveStatement, | |
| "IfStatement:exit": leaveStatement, | |
| "ImportDeclaration:exit": leaveStatement, | |
| "LabeledStatement:exit": leaveStatement, | |
| "ReturnStatement:exit": leaveStatement, | |
| "SwitchStatement:exit": leaveStatement, | |
| "ThrowStatement:exit": leaveStatement, | |
| "TryStatement:exit": leaveStatement, | |
| "VariableDeclaration:exit": leaveStatement, | |
| "WhileStatement:exit": leaveStatement, | |
| "WithStatement:exit": leaveStatement, | |
| "ExportNamedDeclaration:exit": leaveStatement, | |
| "ExportDefaultDeclaration:exit": leaveStatement, | |
| "ExportAllDeclaration:exit": leaveStatement, | |
| "Program:exit": reportFirstExtraStatementAndClear | |
| }; | |
| } | |
| }; |