Skip to content
Permalink
9bfb9ba527
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
196 lines (174 sloc) 7.27 KB
/**
* @fileoverview Specify the maximum number of statements allowed per line.
* @author Kenneth Williams
*/
"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {
meta: {
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
};
}
};