项目原始demo,不改动
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
Este repositorio está archivado. Puede ver los archivos y clonarlo, pero no puede subir cambios o reportar incidencias ni pedir Pull Requests.
 
 
 
 

258 líneas
9.0 KiB

  1. /**
  2. * @fileoverview A rule to ensure blank lines within blocks.
  3. * @author Mathias Schreck <https://github.com/lo1tuma>
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Rule Definition
  8. //------------------------------------------------------------------------------
  9. module.exports = {
  10. meta: {
  11. docs: {
  12. description: "require or disallow padding within blocks",
  13. category: "Stylistic Issues",
  14. recommended: false,
  15. url: "https://eslint.org/docs/rules/padded-blocks"
  16. },
  17. fixable: "whitespace",
  18. schema: [
  19. {
  20. oneOf: [
  21. {
  22. enum: ["always", "never"]
  23. },
  24. {
  25. type: "object",
  26. properties: {
  27. blocks: {
  28. enum: ["always", "never"]
  29. },
  30. switches: {
  31. enum: ["always", "never"]
  32. },
  33. classes: {
  34. enum: ["always", "never"]
  35. }
  36. },
  37. additionalProperties: false,
  38. minProperties: 1
  39. }
  40. ]
  41. }
  42. ]
  43. },
  44. create(context) {
  45. const options = {};
  46. const config = context.options[0] || "always";
  47. if (typeof config === "string") {
  48. const shouldHavePadding = config === "always";
  49. options.blocks = shouldHavePadding;
  50. options.switches = shouldHavePadding;
  51. options.classes = shouldHavePadding;
  52. } else {
  53. if (config.hasOwnProperty("blocks")) {
  54. options.blocks = config.blocks === "always";
  55. }
  56. if (config.hasOwnProperty("switches")) {
  57. options.switches = config.switches === "always";
  58. }
  59. if (config.hasOwnProperty("classes")) {
  60. options.classes = config.classes === "always";
  61. }
  62. }
  63. const ALWAYS_MESSAGE = "Block must be padded by blank lines.",
  64. NEVER_MESSAGE = "Block must not be padded by blank lines.";
  65. const sourceCode = context.getSourceCode();
  66. /**
  67. * Gets the open brace token from a given node.
  68. * @param {ASTNode} node - A BlockStatement or SwitchStatement node from which to get the open brace.
  69. * @returns {Token} The token of the open brace.
  70. */
  71. function getOpenBrace(node) {
  72. if (node.type === "SwitchStatement") {
  73. return sourceCode.getTokenBefore(node.cases[0]);
  74. }
  75. return sourceCode.getFirstToken(node);
  76. }
  77. /**
  78. * Checks if the given parameter is a comment node
  79. * @param {ASTNode|Token} node An AST node or token
  80. * @returns {boolean} True if node is a comment
  81. */
  82. function isComment(node) {
  83. return node.type === "Line" || node.type === "Block";
  84. }
  85. /**
  86. * Checks if there is padding between two tokens
  87. * @param {Token} first The first token
  88. * @param {Token} second The second token
  89. * @returns {boolean} True if there is at least a line between the tokens
  90. */
  91. function isPaddingBetweenTokens(first, second) {
  92. return second.loc.start.line - first.loc.end.line >= 2;
  93. }
  94. /**
  95. * Checks if the given token has a blank line after it.
  96. * @param {Token} token The token to check.
  97. * @returns {boolean} Whether or not the token is followed by a blank line.
  98. */
  99. function getFirstBlockToken(token) {
  100. let prev,
  101. first = token;
  102. do {
  103. prev = first;
  104. first = sourceCode.getTokenAfter(first, { includeComments: true });
  105. } while (isComment(first) && first.loc.start.line === prev.loc.end.line);
  106. return first;
  107. }
  108. /**
  109. * Checks if the given token is preceeded by a blank line.
  110. * @param {Token} token The token to check
  111. * @returns {boolean} Whether or not the token is preceeded by a blank line
  112. */
  113. function getLastBlockToken(token) {
  114. let last = token,
  115. next;
  116. do {
  117. next = last;
  118. last = sourceCode.getTokenBefore(last, { includeComments: true });
  119. } while (isComment(last) && last.loc.end.line === next.loc.start.line);
  120. return last;
  121. }
  122. /**
  123. * Checks if a node should be padded, according to the rule config.
  124. * @param {ASTNode} node The AST node to check.
  125. * @returns {boolean} True if the node should be padded, false otherwise.
  126. */
  127. function requirePaddingFor(node) {
  128. switch (node.type) {
  129. case "BlockStatement":
  130. return options.blocks;
  131. case "SwitchStatement":
  132. return options.switches;
  133. case "ClassBody":
  134. return options.classes;
  135. /* istanbul ignore next */
  136. default:
  137. throw new Error("unreachable");
  138. }
  139. }
  140. /**
  141. * Checks the given BlockStatement node to be padded if the block is not empty.
  142. * @param {ASTNode} node The AST node of a BlockStatement.
  143. * @returns {void} undefined.
  144. */
  145. function checkPadding(node) {
  146. const openBrace = getOpenBrace(node),
  147. firstBlockToken = getFirstBlockToken(openBrace),
  148. tokenBeforeFirst = sourceCode.getTokenBefore(firstBlockToken, { includeComments: true }),
  149. closeBrace = sourceCode.getLastToken(node),
  150. lastBlockToken = getLastBlockToken(closeBrace),
  151. tokenAfterLast = sourceCode.getTokenAfter(lastBlockToken, { includeComments: true }),
  152. blockHasTopPadding = isPaddingBetweenTokens(tokenBeforeFirst, firstBlockToken),
  153. blockHasBottomPadding = isPaddingBetweenTokens(lastBlockToken, tokenAfterLast);
  154. if (requirePaddingFor(node)) {
  155. if (!blockHasTopPadding) {
  156. context.report({
  157. node,
  158. loc: { line: tokenBeforeFirst.loc.start.line, column: tokenBeforeFirst.loc.start.column },
  159. fix(fixer) {
  160. return fixer.insertTextAfter(tokenBeforeFirst, "\n");
  161. },
  162. message: ALWAYS_MESSAGE
  163. });
  164. }
  165. if (!blockHasBottomPadding) {
  166. context.report({
  167. node,
  168. loc: { line: tokenAfterLast.loc.end.line, column: tokenAfterLast.loc.end.column - 1 },
  169. fix(fixer) {
  170. return fixer.insertTextBefore(tokenAfterLast, "\n");
  171. },
  172. message: ALWAYS_MESSAGE
  173. });
  174. }
  175. } else {
  176. if (blockHasTopPadding) {
  177. context.report({
  178. node,
  179. loc: { line: tokenBeforeFirst.loc.start.line, column: tokenBeforeFirst.loc.start.column },
  180. fix(fixer) {
  181. return fixer.replaceTextRange([tokenBeforeFirst.range[1], firstBlockToken.range[0] - firstBlockToken.loc.start.column], "\n");
  182. },
  183. message: NEVER_MESSAGE
  184. });
  185. }
  186. if (blockHasBottomPadding) {
  187. context.report({
  188. node,
  189. loc: { line: tokenAfterLast.loc.end.line, column: tokenAfterLast.loc.end.column - 1 },
  190. message: NEVER_MESSAGE,
  191. fix(fixer) {
  192. return fixer.replaceTextRange([lastBlockToken.range[1], tokenAfterLast.range[0] - tokenAfterLast.loc.start.column], "\n");
  193. }
  194. });
  195. }
  196. }
  197. }
  198. const rule = {};
  199. if (options.hasOwnProperty("switches")) {
  200. rule.SwitchStatement = function(node) {
  201. if (node.cases.length === 0) {
  202. return;
  203. }
  204. checkPadding(node);
  205. };
  206. }
  207. if (options.hasOwnProperty("blocks")) {
  208. rule.BlockStatement = function(node) {
  209. if (node.body.length === 0) {
  210. return;
  211. }
  212. checkPadding(node);
  213. };
  214. }
  215. if (options.hasOwnProperty("classes")) {
  216. rule.ClassBody = function(node) {
  217. if (node.body.length === 0) {
  218. return;
  219. }
  220. checkPadding(node);
  221. };
  222. }
  223. return rule;
  224. }
  225. };