123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
-
- "use strict";
-
-
-
-
-
- const LETTER_PATTERN = require("../util/patterns/letters");
- const astUtils = require("../util/ast-utils");
-
-
-
-
-
- const DEFAULT_IGNORE_PATTERN = astUtils.COMMENTS_IGNORE_PATTERN,
- WHITESPACE = /\s/g,
- MAYBE_URL = /^\s*[^:/?#\s]+:\/\/[^?#]/,
- DEFAULTS = {
- ignorePattern: null,
- ignoreInlineComments: false,
- ignoreConsecutiveComments: false
- };
-
-
- const SCHEMA_BODY = {
- type: "object",
- properties: {
- ignorePattern: {
- type: "string"
- },
- ignoreInlineComments: {
- type: "boolean"
- },
- ignoreConsecutiveComments: {
- type: "boolean"
- }
- },
- additionalProperties: false
- };
-
-
- function getNormalizedOptions(rawOptions, which) {
- if (!rawOptions) {
- return Object.assign({}, DEFAULTS);
- }
-
- return Object.assign({}, DEFAULTS, rawOptions[which] || rawOptions);
- }
-
-
- function getAllNormalizedOptions(rawOptions) {
- return {
- Line: getNormalizedOptions(rawOptions, "line"),
- Block: getNormalizedOptions(rawOptions, "block")
- };
- }
-
-
- function createRegExpForIgnorePatterns(normalizedOptions) {
- Object.keys(normalizedOptions).forEach(key => {
- const ignorePatternStr = normalizedOptions[key].ignorePattern;
-
- if (ignorePatternStr) {
- const regExp = RegExp(`^\\s*(?:${ignorePatternStr})`);
-
- normalizedOptions[key].ignorePatternRegExp = regExp;
- }
- });
- }
-
-
-
-
-
- module.exports = {
- meta: {
- type: "suggestion",
-
- docs: {
- description: "enforce or disallow capitalization of the first letter of a comment",
- category: "Stylistic Issues",
- recommended: false,
- url: "https://eslint.org/docs/rules/capitalized-comments"
- },
-
- fixable: "code",
-
- schema: [
- { enum: ["always", "never"] },
- {
- oneOf: [
- SCHEMA_BODY,
- {
- type: "object",
- properties: {
- line: SCHEMA_BODY,
- block: SCHEMA_BODY
- },
- additionalProperties: false
- }
- ]
- }
- ],
-
- messages: {
- unexpectedLowercaseComment: "Comments should not begin with a lowercase character",
- unexpectedUppercaseComment: "Comments should not begin with an uppercase character"
- }
- },
-
- create(context) {
-
- const capitalize = context.options[0] || "always",
- normalizedOptions = getAllNormalizedOptions(context.options[1]),
- sourceCode = context.getSourceCode();
-
- createRegExpForIgnorePatterns(normalizedOptions);
-
-
-
-
-
-
-
- function isInlineComment(comment) {
- const previousToken = sourceCode.getTokenBefore(comment, { includeComments: true }),
- nextToken = sourceCode.getTokenAfter(comment, { includeComments: true });
-
- return Boolean(
- previousToken &&
- nextToken &&
- comment.loc.start.line === previousToken.loc.end.line &&
- comment.loc.end.line === nextToken.loc.start.line
- );
- }
-
-
-
- function isConsecutiveComment(comment) {
- const previousTokenOrComment = sourceCode.getTokenBefore(comment, { includeComments: true });
-
- return Boolean(
- previousTokenOrComment &&
- ["Block", "Line"].indexOf(previousTokenOrComment.type) !== -1
- );
- }
-
-
-
- function isCommentValid(comment, options) {
-
-
- if (DEFAULT_IGNORE_PATTERN.test(comment.value)) {
- return true;
- }
-
-
- const commentWithoutAsterisks = comment.value
- .replace(/\*/g, "");
-
- if (options.ignorePatternRegExp && options.ignorePatternRegExp.test(commentWithoutAsterisks)) {
- return true;
- }
-
-
- if (options.ignoreInlineComments && isInlineComment(comment)) {
- return true;
- }
-
-
- if (options.ignoreConsecutiveComments && isConsecutiveComment(comment)) {
- return true;
- }
-
-
- if (MAYBE_URL.test(commentWithoutAsterisks)) {
- return true;
- }
-
-
- const commentWordCharsOnly = commentWithoutAsterisks
- .replace(WHITESPACE, "");
-
- if (commentWordCharsOnly.length === 0) {
- return true;
- }
-
- const firstWordChar = commentWordCharsOnly[0];
-
- if (!LETTER_PATTERN.test(firstWordChar)) {
- return true;
- }
-
-
- const isUppercase = firstWordChar !== firstWordChar.toLocaleLowerCase(),
- isLowercase = firstWordChar !== firstWordChar.toLocaleUpperCase();
-
- if (capitalize === "always" && isLowercase) {
- return false;
- }
- if (capitalize === "never" && isUppercase) {
- return false;
- }
-
- return true;
- }
-
-
-
- function processComment(comment) {
- const options = normalizedOptions[comment.type],
- commentValid = isCommentValid(comment, options);
-
- if (!commentValid) {
- const messageId = capitalize === "always"
- ? "unexpectedLowercaseComment"
- : "unexpectedUppercaseComment";
-
- context.report({
- node: null,
- loc: comment.loc,
- messageId,
- fix(fixer) {
- const match = comment.value.match(LETTER_PATTERN);
-
- return fixer.replaceTextRange(
-
-
- [comment.range[0] + match.index + 2, comment.range[0] + match.index + 3],
- capitalize === "always" ? match[0].toLocaleUpperCase() : match[0].toLocaleLowerCase()
- );
- }
- });
- }
- }
-
-
-
-
-
- return {
- Program() {
- const comments = sourceCode.getAllComments();
-
- comments.filter(token => token.type !== "Shebang").forEach(processComment);
- }
- };
- }
- };
|