max-lines.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. /**
  2. * @fileoverview enforce a maximum file length
  3. * @author Alberto Rodríguez
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Requirements
  8. //------------------------------------------------------------------------------
  9. const lodash = require("lodash");
  10. const astUtils = require("./utils/ast-utils");
  11. //------------------------------------------------------------------------------
  12. // Rule Definition
  13. //------------------------------------------------------------------------------
  14. module.exports = {
  15. meta: {
  16. type: "suggestion",
  17. docs: {
  18. description: "enforce a maximum number of lines per file",
  19. category: "Stylistic Issues",
  20. recommended: false,
  21. url: "https://eslint.org/docs/rules/max-lines"
  22. },
  23. schema: [
  24. {
  25. oneOf: [
  26. {
  27. type: "integer",
  28. minimum: 0
  29. },
  30. {
  31. type: "object",
  32. properties: {
  33. max: {
  34. type: "integer",
  35. minimum: 0
  36. },
  37. skipComments: {
  38. type: "boolean"
  39. },
  40. skipBlankLines: {
  41. type: "boolean"
  42. }
  43. },
  44. additionalProperties: false
  45. }
  46. ]
  47. }
  48. ],
  49. messages: {
  50. exceed:
  51. "File has too many lines ({{actual}}). Maximum allowed is {{max}}."
  52. }
  53. },
  54. create(context) {
  55. const option = context.options[0];
  56. let max = 300;
  57. if (
  58. typeof option === "object" &&
  59. Object.prototype.hasOwnProperty.call(option, "max")
  60. ) {
  61. max = option.max;
  62. } else if (typeof option === "number") {
  63. max = option;
  64. }
  65. const skipComments = option && option.skipComments;
  66. const skipBlankLines = option && option.skipBlankLines;
  67. const sourceCode = context.getSourceCode();
  68. /**
  69. * Returns whether or not a token is a comment node type
  70. * @param {Token} token The token to check
  71. * @returns {boolean} True if the token is a comment node
  72. */
  73. function isCommentNodeType(token) {
  74. return token && (token.type === "Block" || token.type === "Line");
  75. }
  76. /**
  77. * Returns the line numbers of a comment that don't have any code on the same line
  78. * @param {Node} comment The comment node to check
  79. * @returns {number[]} The line numbers
  80. */
  81. function getLinesWithoutCode(comment) {
  82. let start = comment.loc.start.line;
  83. let end = comment.loc.end.line;
  84. let token;
  85. token = comment;
  86. do {
  87. token = sourceCode.getTokenBefore(token, {
  88. includeComments: true
  89. });
  90. } while (isCommentNodeType(token));
  91. if (token && astUtils.isTokenOnSameLine(token, comment)) {
  92. start += 1;
  93. }
  94. token = comment;
  95. do {
  96. token = sourceCode.getTokenAfter(token, {
  97. includeComments: true
  98. });
  99. } while (isCommentNodeType(token));
  100. if (token && astUtils.isTokenOnSameLine(comment, token)) {
  101. end -= 1;
  102. }
  103. if (start <= end) {
  104. return lodash.range(start, end + 1);
  105. }
  106. return [];
  107. }
  108. return {
  109. "Program:exit"() {
  110. let lines = sourceCode.lines.map((text, i) => ({
  111. lineNumber: i + 1,
  112. text
  113. }));
  114. if (skipBlankLines) {
  115. lines = lines.filter(l => l.text.trim() !== "");
  116. }
  117. if (skipComments) {
  118. const comments = sourceCode.getAllComments();
  119. const commentLines = lodash.flatten(
  120. comments.map(comment => getLinesWithoutCode(comment))
  121. );
  122. lines = lines.filter(
  123. l => !lodash.includes(commentLines, l.lineNumber)
  124. );
  125. }
  126. if (lines.length > max) {
  127. const loc = {
  128. start: {
  129. line: lines[max].lineNumber,
  130. column: 0
  131. },
  132. end: {
  133. line: sourceCode.lines.length,
  134. column: lodash.last(sourceCode.lines).length
  135. }
  136. };
  137. context.report({
  138. loc,
  139. messageId: "exceed",
  140. data: {
  141. max,
  142. actual: lines.length
  143. }
  144. });
  145. }
  146. }
  147. };
  148. }
  149. };