项目原始demo,不改动
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
Ce dépôt est archivé. Vous pouvez voir les fichiers et le cloner, mais vous ne pouvez pas pousser ni ouvrir de ticket/demande d'ajout.
 
 
 
 

399 lignes
16 KiB

  1. /**
  2. * @fileoverview Enforces empty lines around comments.
  3. * @author Jamund Ferguson
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Requirements
  8. //------------------------------------------------------------------------------
  9. const lodash = require("lodash"),
  10. astUtils = require("../ast-utils");
  11. //------------------------------------------------------------------------------
  12. // Helpers
  13. //------------------------------------------------------------------------------
  14. /**
  15. * Return an array with with any line numbers that are empty.
  16. * @param {Array} lines An array of each line of the file.
  17. * @returns {Array} An array of line numbers.
  18. */
  19. function getEmptyLineNums(lines) {
  20. const emptyLines = lines.map((line, i) => ({
  21. code: line.trim(),
  22. num: i + 1
  23. })).filter(line => !line.code).map(line => line.num);
  24. return emptyLines;
  25. }
  26. /**
  27. * Return an array with with any line numbers that contain comments.
  28. * @param {Array} comments An array of comment tokens.
  29. * @returns {Array} An array of line numbers.
  30. */
  31. function getCommentLineNums(comments) {
  32. const lines = [];
  33. comments.forEach(token => {
  34. const start = token.loc.start.line;
  35. const end = token.loc.end.line;
  36. lines.push(start, end);
  37. });
  38. return lines;
  39. }
  40. //------------------------------------------------------------------------------
  41. // Rule Definition
  42. //------------------------------------------------------------------------------
  43. module.exports = {
  44. meta: {
  45. docs: {
  46. description: "require empty lines around comments",
  47. category: "Stylistic Issues",
  48. recommended: false,
  49. url: "https://eslint.org/docs/rules/lines-around-comment"
  50. },
  51. fixable: "whitespace",
  52. schema: [
  53. {
  54. type: "object",
  55. properties: {
  56. beforeBlockComment: {
  57. type: "boolean"
  58. },
  59. afterBlockComment: {
  60. type: "boolean"
  61. },
  62. beforeLineComment: {
  63. type: "boolean"
  64. },
  65. afterLineComment: {
  66. type: "boolean"
  67. },
  68. allowBlockStart: {
  69. type: "boolean"
  70. },
  71. allowBlockEnd: {
  72. type: "boolean"
  73. },
  74. allowClassStart: {
  75. type: "boolean"
  76. },
  77. allowClassEnd: {
  78. type: "boolean"
  79. },
  80. allowObjectStart: {
  81. type: "boolean"
  82. },
  83. allowObjectEnd: {
  84. type: "boolean"
  85. },
  86. allowArrayStart: {
  87. type: "boolean"
  88. },
  89. allowArrayEnd: {
  90. type: "boolean"
  91. },
  92. ignorePattern: {
  93. type: "string"
  94. },
  95. applyDefaultIgnorePatterns: {
  96. type: "boolean"
  97. }
  98. },
  99. additionalProperties: false
  100. }
  101. ]
  102. },
  103. create(context) {
  104. const options = context.options[0] ? Object.assign({}, context.options[0]) : {};
  105. const ignorePattern = options.ignorePattern;
  106. const defaultIgnoreRegExp = astUtils.COMMENTS_IGNORE_PATTERN;
  107. const customIgnoreRegExp = new RegExp(ignorePattern);
  108. const applyDefaultIgnorePatterns = options.applyDefaultIgnorePatterns !== false;
  109. options.beforeLineComment = options.beforeLineComment || false;
  110. options.afterLineComment = options.afterLineComment || false;
  111. options.beforeBlockComment = typeof options.beforeBlockComment !== "undefined" ? options.beforeBlockComment : true;
  112. options.afterBlockComment = options.afterBlockComment || false;
  113. options.allowBlockStart = options.allowBlockStart || false;
  114. options.allowBlockEnd = options.allowBlockEnd || false;
  115. const sourceCode = context.getSourceCode();
  116. const lines = sourceCode.lines,
  117. numLines = lines.length + 1,
  118. comments = sourceCode.getAllComments(),
  119. commentLines = getCommentLineNums(comments),
  120. emptyLines = getEmptyLineNums(lines),
  121. commentAndEmptyLines = commentLines.concat(emptyLines);
  122. /**
  123. * Returns whether or not comments are on lines starting with or ending with code
  124. * @param {token} token The comment token to check.
  125. * @returns {boolean} True if the comment is not alone.
  126. */
  127. function codeAroundComment(token) {
  128. let currentToken = token;
  129. do {
  130. currentToken = sourceCode.getTokenBefore(currentToken, { includeComments: true });
  131. } while (currentToken && astUtils.isCommentToken(currentToken));
  132. if (currentToken && astUtils.isTokenOnSameLine(currentToken, token)) {
  133. return true;
  134. }
  135. currentToken = token;
  136. do {
  137. currentToken = sourceCode.getTokenAfter(currentToken, { includeComments: true });
  138. } while (currentToken && astUtils.isCommentToken(currentToken));
  139. if (currentToken && astUtils.isTokenOnSameLine(token, currentToken)) {
  140. return true;
  141. }
  142. return false;
  143. }
  144. /**
  145. * Returns whether or not comments are inside a node type or not.
  146. * @param {ASTNode} parent The Comment parent node.
  147. * @param {string} nodeType The parent type to check against.
  148. * @returns {boolean} True if the comment is inside nodeType.
  149. */
  150. function isParentNodeType(parent, nodeType) {
  151. return parent.type === nodeType ||
  152. (parent.body && parent.body.type === nodeType) ||
  153. (parent.consequent && parent.consequent.type === nodeType);
  154. }
  155. /**
  156. * Returns the parent node that contains the given token.
  157. * @param {token} token The token to check.
  158. * @returns {ASTNode} The parent node that contains the given token.
  159. */
  160. function getParentNodeOfToken(token) {
  161. return sourceCode.getNodeByRangeIndex(token.range[0]);
  162. }
  163. /**
  164. * Returns whether or not comments are at the parent start or not.
  165. * @param {token} token The Comment token.
  166. * @param {string} nodeType The parent type to check against.
  167. * @returns {boolean} True if the comment is at parent start.
  168. */
  169. function isCommentAtParentStart(token, nodeType) {
  170. const parent = getParentNodeOfToken(token);
  171. return parent && isParentNodeType(parent, nodeType) &&
  172. token.loc.start.line - parent.loc.start.line === 1;
  173. }
  174. /**
  175. * Returns whether or not comments are at the parent end or not.
  176. * @param {token} token The Comment token.
  177. * @param {string} nodeType The parent type to check against.
  178. * @returns {boolean} True if the comment is at parent end.
  179. */
  180. function isCommentAtParentEnd(token, nodeType) {
  181. const parent = getParentNodeOfToken(token);
  182. return parent && isParentNodeType(parent, nodeType) &&
  183. parent.loc.end.line - token.loc.end.line === 1;
  184. }
  185. /**
  186. * Returns whether or not comments are at the block start or not.
  187. * @param {token} token The Comment token.
  188. * @returns {boolean} True if the comment is at block start.
  189. */
  190. function isCommentAtBlockStart(token) {
  191. return isCommentAtParentStart(token, "ClassBody") || isCommentAtParentStart(token, "BlockStatement") || isCommentAtParentStart(token, "SwitchCase");
  192. }
  193. /**
  194. * Returns whether or not comments are at the block end or not.
  195. * @param {token} token The Comment token.
  196. * @returns {boolean} True if the comment is at block end.
  197. */
  198. function isCommentAtBlockEnd(token) {
  199. return isCommentAtParentEnd(token, "ClassBody") || isCommentAtParentEnd(token, "BlockStatement") || isCommentAtParentEnd(token, "SwitchCase") || isCommentAtParentEnd(token, "SwitchStatement");
  200. }
  201. /**
  202. * Returns whether or not comments are at the class start or not.
  203. * @param {token} token The Comment token.
  204. * @returns {boolean} True if the comment is at class start.
  205. */
  206. function isCommentAtClassStart(token) {
  207. return isCommentAtParentStart(token, "ClassBody");
  208. }
  209. /**
  210. * Returns whether or not comments are at the class end or not.
  211. * @param {token} token The Comment token.
  212. * @returns {boolean} True if the comment is at class end.
  213. */
  214. function isCommentAtClassEnd(token) {
  215. return isCommentAtParentEnd(token, "ClassBody");
  216. }
  217. /**
  218. * Returns whether or not comments are at the object start or not.
  219. * @param {token} token The Comment token.
  220. * @returns {boolean} True if the comment is at object start.
  221. */
  222. function isCommentAtObjectStart(token) {
  223. return isCommentAtParentStart(token, "ObjectExpression") || isCommentAtParentStart(token, "ObjectPattern");
  224. }
  225. /**
  226. * Returns whether or not comments are at the object end or not.
  227. * @param {token} token The Comment token.
  228. * @returns {boolean} True if the comment is at object end.
  229. */
  230. function isCommentAtObjectEnd(token) {
  231. return isCommentAtParentEnd(token, "ObjectExpression") || isCommentAtParentEnd(token, "ObjectPattern");
  232. }
  233. /**
  234. * Returns whether or not comments are at the array start or not.
  235. * @param {token} token The Comment token.
  236. * @returns {boolean} True if the comment is at array start.
  237. */
  238. function isCommentAtArrayStart(token) {
  239. return isCommentAtParentStart(token, "ArrayExpression") || isCommentAtParentStart(token, "ArrayPattern");
  240. }
  241. /**
  242. * Returns whether or not comments are at the array end or not.
  243. * @param {token} token The Comment token.
  244. * @returns {boolean} True if the comment is at array end.
  245. */
  246. function isCommentAtArrayEnd(token) {
  247. return isCommentAtParentEnd(token, "ArrayExpression") || isCommentAtParentEnd(token, "ArrayPattern");
  248. }
  249. /**
  250. * Checks if a comment token has lines around it (ignores inline comments)
  251. * @param {token} token The Comment token.
  252. * @param {Object} opts Options to determine the newline.
  253. * @param {boolean} opts.after Should have a newline after this line.
  254. * @param {boolean} opts.before Should have a newline before this line.
  255. * @returns {void}
  256. */
  257. function checkForEmptyLine(token, opts) {
  258. if (applyDefaultIgnorePatterns && defaultIgnoreRegExp.test(token.value)) {
  259. return;
  260. }
  261. if (ignorePattern && customIgnoreRegExp.test(token.value)) {
  262. return;
  263. }
  264. let after = opts.after,
  265. before = opts.before;
  266. const prevLineNum = token.loc.start.line - 1,
  267. nextLineNum = token.loc.end.line + 1,
  268. commentIsNotAlone = codeAroundComment(token);
  269. const blockStartAllowed = options.allowBlockStart &&
  270. isCommentAtBlockStart(token) &&
  271. !(options.allowClassStart === false &&
  272. isCommentAtClassStart(token)),
  273. blockEndAllowed = options.allowBlockEnd && isCommentAtBlockEnd(token) && !(options.allowClassEnd === false && isCommentAtClassEnd(token)),
  274. classStartAllowed = options.allowClassStart && isCommentAtClassStart(token),
  275. classEndAllowed = options.allowClassEnd && isCommentAtClassEnd(token),
  276. objectStartAllowed = options.allowObjectStart && isCommentAtObjectStart(token),
  277. objectEndAllowed = options.allowObjectEnd && isCommentAtObjectEnd(token),
  278. arrayStartAllowed = options.allowArrayStart && isCommentAtArrayStart(token),
  279. arrayEndAllowed = options.allowArrayEnd && isCommentAtArrayEnd(token);
  280. const exceptionStartAllowed = blockStartAllowed || classStartAllowed || objectStartAllowed || arrayStartAllowed;
  281. const exceptionEndAllowed = blockEndAllowed || classEndAllowed || objectEndAllowed || arrayEndAllowed;
  282. // ignore top of the file and bottom of the file
  283. if (prevLineNum < 1) {
  284. before = false;
  285. }
  286. if (nextLineNum >= numLines) {
  287. after = false;
  288. }
  289. // we ignore all inline comments
  290. if (commentIsNotAlone) {
  291. return;
  292. }
  293. const previousTokenOrComment = sourceCode.getTokenBefore(token, { includeComments: true });
  294. const nextTokenOrComment = sourceCode.getTokenAfter(token, { includeComments: true });
  295. // check for newline before
  296. if (!exceptionStartAllowed && before && !lodash.includes(commentAndEmptyLines, prevLineNum) &&
  297. !(astUtils.isCommentToken(previousTokenOrComment) && astUtils.isTokenOnSameLine(previousTokenOrComment, token))) {
  298. const lineStart = token.range[0] - token.loc.start.column;
  299. const range = [lineStart, lineStart];
  300. context.report({
  301. node: token,
  302. message: "Expected line before comment.",
  303. fix(fixer) {
  304. return fixer.insertTextBeforeRange(range, "\n");
  305. }
  306. });
  307. }
  308. // check for newline after
  309. if (!exceptionEndAllowed && after && !lodash.includes(commentAndEmptyLines, nextLineNum) &&
  310. !(astUtils.isCommentToken(nextTokenOrComment) && astUtils.isTokenOnSameLine(token, nextTokenOrComment))) {
  311. context.report({
  312. node: token,
  313. message: "Expected line after comment.",
  314. fix(fixer) {
  315. return fixer.insertTextAfter(token, "\n");
  316. }
  317. });
  318. }
  319. }
  320. //--------------------------------------------------------------------------
  321. // Public
  322. //--------------------------------------------------------------------------
  323. return {
  324. Program() {
  325. comments.forEach(token => {
  326. if (token.type === "Line") {
  327. if (options.beforeLineComment || options.afterLineComment) {
  328. checkForEmptyLine(token, {
  329. after: options.afterLineComment,
  330. before: options.beforeLineComment
  331. });
  332. }
  333. } else if (token.type === "Block") {
  334. if (options.beforeBlockComment || options.afterBlockComment) {
  335. checkForEmptyLine(token, {
  336. after: options.afterBlockComment,
  337. before: options.beforeBlockComment
  338. });
  339. }
  340. }
  341. });
  342. }
  343. };
  344. }
  345. };