/** * @fileoverview Specify the maximum number of statements allowed per line. * @author Kenneth Williams * @deprecated in ESLint v8.53.0 */ 'use strict'; //------------------------------------------------------------------------------ // Requirements //------------------------------------------------------------------------------ const astUtils = require('./utils/ast-utils'); //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ /** @type {import('../types').Rule.RuleModule} */ module.exports = { meta: { deprecated: { message: 'Formatting rules are being moved out of ESLint core.', url: 'https://eslint.org/blog/2023/10/deprecating-formatting-rules/', deprecatedSince: '8.53.0', availableUntil: '10.0.0', replacedBy: [ { message: 'ESLint Stylistic now maintains deprecated stylistic core rules.', url: 'https://eslint.style/guide/migration', plugin: { name: '@stylistic/eslint-plugin-js', url: 'https://eslint.style/packages/js', }, rule: { name: 'max-statements-per-line', url: 'https://eslint.style/rules/js/max-statements-per-line', }, }, ], }, type: 'layout', docs: { description: 'Enforce a maximum number of statements allowed per line', recommended: false, url: 'https://eslint.org/docs/latest/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.sourceCode, 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, }; }, };