max-statements-per-line.js 6.6 KB

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