max-statements-per-line.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  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("../util/ast-utils");
  10. //------------------------------------------------------------------------------
  11. // Rule Definition
  12. //------------------------------------------------------------------------------
  13. module.exports = {
  14. meta: {
  15. type: "layout",
  16. docs: {
  17. description: "enforce a maximum number of statements allowed per line",
  18. category: "Stylistic Issues",
  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. *
  53. * @returns {void}
  54. */
  55. function reportFirstExtraStatementAndClear() {
  56. if (firstExtraStatement) {
  57. context.report({
  58. node: firstExtraStatement,
  59. messageId: "exceed",
  60. data: {
  61. numberOfStatementsOnThisLine,
  62. maxStatementsPerLine,
  63. statements: numberOfStatementsOnThisLine === 1 ? "statement" : "statements"
  64. }
  65. });
  66. }
  67. firstExtraStatement = null;
  68. }
  69. /**
  70. * Gets the actual last token of a given node.
  71. *
  72. * @param {ASTNode} node - A node to get. This is a node except EmptyStatement.
  73. * @returns {Token} The actual last token.
  74. */
  75. function getActualLastToken(node) {
  76. return sourceCode.getLastToken(node, astUtils.isNotSemicolonToken);
  77. }
  78. /**
  79. * Addresses a given node.
  80. * It updates the state of this rule, then reports the node if the node violated this rule.
  81. *
  82. * @param {ASTNode} node - A node to check.
  83. * @returns {void}
  84. */
  85. function enterStatement(node) {
  86. const line = node.loc.start.line;
  87. /*
  88. * Skip to allow non-block statements if this is direct child of control statements.
  89. * `if (a) foo();` is counted as 1.
  90. * But `if (a) foo(); else foo();` should be counted as 2.
  91. */
  92. if (SINGLE_CHILD_ALLOWED.test(node.parent.type) &&
  93. node.parent.alternate !== node
  94. ) {
  95. return;
  96. }
  97. // Update state.
  98. if (line === lastStatementLine) {
  99. numberOfStatementsOnThisLine += 1;
  100. } else {
  101. reportFirstExtraStatementAndClear();
  102. numberOfStatementsOnThisLine = 1;
  103. lastStatementLine = line;
  104. }
  105. // Reports if the node violated this rule.
  106. if (numberOfStatementsOnThisLine === maxStatementsPerLine + 1) {
  107. firstExtraStatement = firstExtraStatement || node;
  108. }
  109. }
  110. /**
  111. * Updates the state of this rule with the end line of leaving node to check with the next statement.
  112. *
  113. * @param {ASTNode} node - A node to check.
  114. * @returns {void}
  115. */
  116. function leaveStatement(node) {
  117. const line = getActualLastToken(node).loc.end.line;
  118. // Update state.
  119. if (line !== lastStatementLine) {
  120. reportFirstExtraStatementAndClear();
  121. numberOfStatementsOnThisLine = 1;
  122. lastStatementLine = line;
  123. }
  124. }
  125. //--------------------------------------------------------------------------
  126. // Public API
  127. //--------------------------------------------------------------------------
  128. return {
  129. BreakStatement: enterStatement,
  130. ClassDeclaration: enterStatement,
  131. ContinueStatement: enterStatement,
  132. DebuggerStatement: enterStatement,
  133. DoWhileStatement: enterStatement,
  134. ExpressionStatement: enterStatement,
  135. ForInStatement: enterStatement,
  136. ForOfStatement: enterStatement,
  137. ForStatement: enterStatement,
  138. FunctionDeclaration: enterStatement,
  139. IfStatement: enterStatement,
  140. ImportDeclaration: enterStatement,
  141. LabeledStatement: enterStatement,
  142. ReturnStatement: enterStatement,
  143. SwitchStatement: enterStatement,
  144. ThrowStatement: enterStatement,
  145. TryStatement: enterStatement,
  146. VariableDeclaration: enterStatement,
  147. WhileStatement: enterStatement,
  148. WithStatement: enterStatement,
  149. ExportNamedDeclaration: enterStatement,
  150. ExportDefaultDeclaration: enterStatement,
  151. ExportAllDeclaration: enterStatement,
  152. "BreakStatement:exit": leaveStatement,
  153. "ClassDeclaration:exit": leaveStatement,
  154. "ContinueStatement:exit": leaveStatement,
  155. "DebuggerStatement:exit": leaveStatement,
  156. "DoWhileStatement:exit": leaveStatement,
  157. "ExpressionStatement:exit": leaveStatement,
  158. "ForInStatement:exit": leaveStatement,
  159. "ForOfStatement:exit": leaveStatement,
  160. "ForStatement:exit": leaveStatement,
  161. "FunctionDeclaration:exit": leaveStatement,
  162. "IfStatement:exit": leaveStatement,
  163. "ImportDeclaration:exit": leaveStatement,
  164. "LabeledStatement:exit": leaveStatement,
  165. "ReturnStatement:exit": leaveStatement,
  166. "SwitchStatement:exit": leaveStatement,
  167. "ThrowStatement:exit": leaveStatement,
  168. "TryStatement:exit": leaveStatement,
  169. "VariableDeclaration:exit": leaveStatement,
  170. "WhileStatement:exit": leaveStatement,
  171. "WithStatement:exit": leaveStatement,
  172. "ExportNamedDeclaration:exit": leaveStatement,
  173. "ExportDefaultDeclaration:exit": leaveStatement,
  174. "ExportAllDeclaration:exit": leaveStatement,
  175. "Program:exit": reportFirstExtraStatementAndClear
  176. };
  177. }
  178. };