项目原始demo,不改动
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
Tento repozitář je archivovaný. Můžete prohlížet soubory, klonovat, ale nemůžete nahrávat a vytvářet nové úkoly a požadavky na natažení.

capitalized-comments.js 11 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /**
  2. * @fileoverview enforce or disallow capitalization of the first letter of a comment
  3. * @author Kevin Partington
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Requirements
  8. //------------------------------------------------------------------------------
  9. const LETTER_PATTERN = require("../util/patterns/letters");
  10. const astUtils = require("../ast-utils");
  11. //------------------------------------------------------------------------------
  12. // Helpers
  13. //------------------------------------------------------------------------------
  14. const DEFAULT_IGNORE_PATTERN = astUtils.COMMENTS_IGNORE_PATTERN,
  15. WHITESPACE = /\s/g,
  16. MAYBE_URL = /^\s*[^:/?#\s]+:\/\/[^?#]/, // TODO: Combine w/ max-len pattern?
  17. DEFAULTS = {
  18. ignorePattern: null,
  19. ignoreInlineComments: false,
  20. ignoreConsecutiveComments: false
  21. };
  22. /*
  23. * Base schema body for defining the basic capitalization rule, ignorePattern,
  24. * and ignoreInlineComments values.
  25. * This can be used in a few different ways in the actual schema.
  26. */
  27. const SCHEMA_BODY = {
  28. type: "object",
  29. properties: {
  30. ignorePattern: {
  31. type: "string"
  32. },
  33. ignoreInlineComments: {
  34. type: "boolean"
  35. },
  36. ignoreConsecutiveComments: {
  37. type: "boolean"
  38. }
  39. },
  40. additionalProperties: false
  41. };
  42. /**
  43. * Get normalized options for either block or line comments from the given
  44. * user-provided options.
  45. * - If the user-provided options is just a string, returns a normalized
  46. * set of options using default values for all other options.
  47. * - If the user-provided options is an object, then a normalized option
  48. * set is returned. Options specified in overrides will take priority
  49. * over options specified in the main options object, which will in
  50. * turn take priority over the rule's defaults.
  51. *
  52. * @param {Object|string} rawOptions The user-provided options.
  53. * @param {string} which Either "line" or "block".
  54. * @returns {Object} The normalized options.
  55. */
  56. function getNormalizedOptions(rawOptions, which) {
  57. if (!rawOptions) {
  58. return Object.assign({}, DEFAULTS);
  59. }
  60. return Object.assign({}, DEFAULTS, rawOptions[which] || rawOptions);
  61. }
  62. /**
  63. * Get normalized options for block and line comments.
  64. *
  65. * @param {Object|string} rawOptions The user-provided options.
  66. * @returns {Object} An object with "Line" and "Block" keys and corresponding
  67. * normalized options objects.
  68. */
  69. function getAllNormalizedOptions(rawOptions) {
  70. return {
  71. Line: getNormalizedOptions(rawOptions, "line"),
  72. Block: getNormalizedOptions(rawOptions, "block")
  73. };
  74. }
  75. /**
  76. * Creates a regular expression for each ignorePattern defined in the rule
  77. * options.
  78. *
  79. * This is done in order to avoid invoking the RegExp constructor repeatedly.
  80. *
  81. * @param {Object} normalizedOptions The normalized rule options.
  82. * @returns {void}
  83. */
  84. function createRegExpForIgnorePatterns(normalizedOptions) {
  85. Object.keys(normalizedOptions).forEach(key => {
  86. const ignorePatternStr = normalizedOptions[key].ignorePattern;
  87. if (ignorePatternStr) {
  88. const regExp = RegExp(`^\\s*(?:${ignorePatternStr})`);
  89. normalizedOptions[key].ignorePatternRegExp = regExp;
  90. }
  91. });
  92. }
  93. //------------------------------------------------------------------------------
  94. // Rule Definition
  95. //------------------------------------------------------------------------------
  96. module.exports = {
  97. meta: {
  98. docs: {
  99. description: "enforce or disallow capitalization of the first letter of a comment",
  100. category: "Stylistic Issues",
  101. recommended: false,
  102. url: "https://eslint.org/docs/rules/capitalized-comments"
  103. },
  104. fixable: "code",
  105. schema: [
  106. { enum: ["always", "never"] },
  107. {
  108. oneOf: [
  109. SCHEMA_BODY,
  110. {
  111. type: "object",
  112. properties: {
  113. line: SCHEMA_BODY,
  114. block: SCHEMA_BODY
  115. },
  116. additionalProperties: false
  117. }
  118. ]
  119. }
  120. ],
  121. messages: {
  122. unexpectedLowercaseComment: "Comments should not begin with a lowercase character",
  123. unexpectedUppercaseComment: "Comments should not begin with an uppercase character"
  124. }
  125. },
  126. create(context) {
  127. const capitalize = context.options[0] || "always",
  128. normalizedOptions = getAllNormalizedOptions(context.options[1]),
  129. sourceCode = context.getSourceCode();
  130. createRegExpForIgnorePatterns(normalizedOptions);
  131. //----------------------------------------------------------------------
  132. // Helpers
  133. //----------------------------------------------------------------------
  134. /**
  135. * Checks whether a comment is an inline comment.
  136. *
  137. * For the purpose of this rule, a comment is inline if:
  138. * 1. The comment is preceded by a token on the same line; and
  139. * 2. The command is followed by a token on the same line.
  140. *
  141. * Note that the comment itself need not be single-line!
  142. *
  143. * Also, it follows from this definition that only block comments can
  144. * be considered as possibly inline. This is because line comments
  145. * would consume any following tokens on the same line as the comment.
  146. *
  147. * @param {ASTNode} comment The comment node to check.
  148. * @returns {boolean} True if the comment is an inline comment, false
  149. * otherwise.
  150. */
  151. function isInlineComment(comment) {
  152. const previousToken = sourceCode.getTokenBefore(comment, { includeComments: true }),
  153. nextToken = sourceCode.getTokenAfter(comment, { includeComments: true });
  154. return Boolean(
  155. previousToken &&
  156. nextToken &&
  157. comment.loc.start.line === previousToken.loc.end.line &&
  158. comment.loc.end.line === nextToken.loc.start.line
  159. );
  160. }
  161. /**
  162. * Determine if a comment follows another comment.
  163. *
  164. * @param {ASTNode} comment The comment to check.
  165. * @returns {boolean} True if the comment follows a valid comment.
  166. */
  167. function isConsecutiveComment(comment) {
  168. const previousTokenOrComment = sourceCode.getTokenBefore(comment, { includeComments: true });
  169. return Boolean(
  170. previousTokenOrComment &&
  171. ["Block", "Line"].indexOf(previousTokenOrComment.type) !== -1
  172. );
  173. }
  174. /**
  175. * Check a comment to determine if it is valid for this rule.
  176. *
  177. * @param {ASTNode} comment The comment node to process.
  178. * @param {Object} options The options for checking this comment.
  179. * @returns {boolean} True if the comment is valid, false otherwise.
  180. */
  181. function isCommentValid(comment, options) {
  182. // 1. Check for default ignore pattern.
  183. if (DEFAULT_IGNORE_PATTERN.test(comment.value)) {
  184. return true;
  185. }
  186. // 2. Check for custom ignore pattern.
  187. const commentWithoutAsterisks = comment.value
  188. .replace(/\*/g, "");
  189. if (options.ignorePatternRegExp && options.ignorePatternRegExp.test(commentWithoutAsterisks)) {
  190. return true;
  191. }
  192. // 3. Check for inline comments.
  193. if (options.ignoreInlineComments && isInlineComment(comment)) {
  194. return true;
  195. }
  196. // 4. Is this a consecutive comment (and are we tolerating those)?
  197. if (options.ignoreConsecutiveComments && isConsecutiveComment(comment)) {
  198. return true;
  199. }
  200. // 5. Does the comment start with a possible URL?
  201. if (MAYBE_URL.test(commentWithoutAsterisks)) {
  202. return true;
  203. }
  204. // 6. Is the initial word character a letter?
  205. const commentWordCharsOnly = commentWithoutAsterisks
  206. .replace(WHITESPACE, "");
  207. if (commentWordCharsOnly.length === 0) {
  208. return true;
  209. }
  210. const firstWordChar = commentWordCharsOnly[0];
  211. if (!LETTER_PATTERN.test(firstWordChar)) {
  212. return true;
  213. }
  214. // 7. Check the case of the initial word character.
  215. const isUppercase = firstWordChar !== firstWordChar.toLocaleLowerCase(),
  216. isLowercase = firstWordChar !== firstWordChar.toLocaleUpperCase();
  217. if (capitalize === "always" && isLowercase) {
  218. return false;
  219. }
  220. if (capitalize === "never" && isUppercase) {
  221. return false;
  222. }
  223. return true;
  224. }
  225. /**
  226. * Process a comment to determine if it needs to be reported.
  227. *
  228. * @param {ASTNode} comment The comment node to process.
  229. * @returns {void}
  230. */
  231. function processComment(comment) {
  232. const options = normalizedOptions[comment.type],
  233. commentValid = isCommentValid(comment, options);
  234. if (!commentValid) {
  235. const messageId = capitalize === "always"
  236. ? "unexpectedLowercaseComment"
  237. : "unexpectedUppercaseComment";
  238. context.report({
  239. node: null, // Intentionally using loc instead
  240. loc: comment.loc,
  241. messageId,
  242. fix(fixer) {
  243. const match = comment.value.match(LETTER_PATTERN);
  244. return fixer.replaceTextRange(
  245. // Offset match.index by 2 to account for the first 2 characters that start the comment (// or /*)
  246. [comment.range[0] + match.index + 2, comment.range[0] + match.index + 3],
  247. capitalize === "always" ? match[0].toLocaleUpperCase() : match[0].toLocaleLowerCase()
  248. );
  249. }
  250. });
  251. }
  252. }
  253. //----------------------------------------------------------------------
  254. // Public
  255. //----------------------------------------------------------------------
  256. return {
  257. Program() {
  258. const comments = sourceCode.getAllComments();
  259. comments.filter(token => token.type !== "Shebang").forEach(processComment);
  260. }
  261. };
  262. }
  263. };