max-statements-per-line.js 7.3 KB

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