.
This commit is contained in:
		
							
								
								
									
										300
									
								
								qwen/nodejs/node_modules/eslint/lib/rules/capitalized-comments.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										300
									
								
								qwen/nodejs/node_modules/eslint/lib/rules/capitalized-comments.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,300 @@
 | 
			
		||||
/**
 | 
			
		||||
 * @fileoverview enforce or disallow capitalization of the first letter of a comment
 | 
			
		||||
 * @author Kevin Partington
 | 
			
		||||
 */
 | 
			
		||||
"use strict";
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// Requirements
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
const LETTER_PATTERN = require("./utils/patterns/letters");
 | 
			
		||||
const astUtils = require("./utils/ast-utils");
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// Helpers
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
const DEFAULT_IGNORE_PATTERN = astUtils.COMMENTS_IGNORE_PATTERN,
 | 
			
		||||
    WHITESPACE = /\s/gu,
 | 
			
		||||
    MAYBE_URL = /^\s*[^:/?#\s]+:\/\/[^?#]/u; // TODO: Combine w/ max-len pattern?
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Base schema body for defining the basic capitalization rule, ignorePattern,
 | 
			
		||||
 * and ignoreInlineComments values.
 | 
			
		||||
 * This can be used in a few different ways in the actual schema.
 | 
			
		||||
 */
 | 
			
		||||
const SCHEMA_BODY = {
 | 
			
		||||
    type: "object",
 | 
			
		||||
    properties: {
 | 
			
		||||
        ignorePattern: {
 | 
			
		||||
            type: "string"
 | 
			
		||||
        },
 | 
			
		||||
        ignoreInlineComments: {
 | 
			
		||||
            type: "boolean"
 | 
			
		||||
        },
 | 
			
		||||
        ignoreConsecutiveComments: {
 | 
			
		||||
            type: "boolean"
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    additionalProperties: false
 | 
			
		||||
};
 | 
			
		||||
const DEFAULTS = {
 | 
			
		||||
    ignorePattern: "",
 | 
			
		||||
    ignoreInlineComments: false,
 | 
			
		||||
    ignoreConsecutiveComments: false
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get normalized options for either block or line comments from the given
 | 
			
		||||
 * user-provided options.
 | 
			
		||||
 * - If the user-provided options is just a string, returns a normalized
 | 
			
		||||
 *   set of options using default values for all other options.
 | 
			
		||||
 * - If the user-provided options is an object, then a normalized option
 | 
			
		||||
 *   set is returned. Options specified in overrides will take priority
 | 
			
		||||
 *   over options specified in the main options object, which will in
 | 
			
		||||
 *   turn take priority over the rule's defaults.
 | 
			
		||||
 * @param {Object|string} rawOptions The user-provided options.
 | 
			
		||||
 * @param {string} which Either "line" or "block".
 | 
			
		||||
 * @returns {Object} The normalized options.
 | 
			
		||||
 */
 | 
			
		||||
function getNormalizedOptions(rawOptions, which) {
 | 
			
		||||
    return Object.assign({}, DEFAULTS, rawOptions[which] || rawOptions);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get normalized options for block and line comments.
 | 
			
		||||
 * @param {Object|string} rawOptions The user-provided options.
 | 
			
		||||
 * @returns {Object} An object with "Line" and "Block" keys and corresponding
 | 
			
		||||
 * normalized options objects.
 | 
			
		||||
 */
 | 
			
		||||
function getAllNormalizedOptions(rawOptions = {}) {
 | 
			
		||||
    return {
 | 
			
		||||
        Line: getNormalizedOptions(rawOptions, "line"),
 | 
			
		||||
        Block: getNormalizedOptions(rawOptions, "block")
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Creates a regular expression for each ignorePattern defined in the rule
 | 
			
		||||
 * options.
 | 
			
		||||
 *
 | 
			
		||||
 * This is done in order to avoid invoking the RegExp constructor repeatedly.
 | 
			
		||||
 * @param {Object} normalizedOptions The normalized rule options.
 | 
			
		||||
 * @returns {void}
 | 
			
		||||
 */
 | 
			
		||||
function createRegExpForIgnorePatterns(normalizedOptions) {
 | 
			
		||||
    Object.keys(normalizedOptions).forEach(key => {
 | 
			
		||||
        const ignorePatternStr = normalizedOptions[key].ignorePattern;
 | 
			
		||||
 | 
			
		||||
        if (ignorePatternStr) {
 | 
			
		||||
            const regExp = RegExp(`^\\s*(?:${ignorePatternStr})`, "u");
 | 
			
		||||
 | 
			
		||||
            normalizedOptions[key].ignorePatternRegExp = regExp;
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// Rule Definition
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
/** @type {import('../shared/types').Rule} */
 | 
			
		||||
module.exports = {
 | 
			
		||||
    meta: {
 | 
			
		||||
        type: "suggestion",
 | 
			
		||||
 | 
			
		||||
        docs: {
 | 
			
		||||
            description: "Enforce or disallow capitalization of the first letter of a comment",
 | 
			
		||||
            recommended: false,
 | 
			
		||||
            url: "https://eslint.org/docs/latest/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.sourceCode;
 | 
			
		||||
 | 
			
		||||
        createRegExpForIgnorePatterns(normalizedOptions);
 | 
			
		||||
 | 
			
		||||
        //----------------------------------------------------------------------
 | 
			
		||||
        // Helpers
 | 
			
		||||
        //----------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Checks whether a comment is an inline comment.
 | 
			
		||||
         *
 | 
			
		||||
         * For the purpose of this rule, a comment is inline if:
 | 
			
		||||
         * 1. The comment is preceded by a token on the same line; and
 | 
			
		||||
         * 2. The command is followed by a token on the same line.
 | 
			
		||||
         *
 | 
			
		||||
         * Note that the comment itself need not be single-line!
 | 
			
		||||
         *
 | 
			
		||||
         * Also, it follows from this definition that only block comments can
 | 
			
		||||
         * be considered as possibly inline. This is because line comments
 | 
			
		||||
         * would consume any following tokens on the same line as the comment.
 | 
			
		||||
         * @param {ASTNode} comment The comment node to check.
 | 
			
		||||
         * @returns {boolean} True if the comment is an inline comment, false
 | 
			
		||||
         * otherwise.
 | 
			
		||||
         */
 | 
			
		||||
        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
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Determine if a comment follows another comment.
 | 
			
		||||
         * @param {ASTNode} comment The comment to check.
 | 
			
		||||
         * @returns {boolean} True if the comment follows a valid comment.
 | 
			
		||||
         */
 | 
			
		||||
        function isConsecutiveComment(comment) {
 | 
			
		||||
            const previousTokenOrComment = sourceCode.getTokenBefore(comment, { includeComments: true });
 | 
			
		||||
 | 
			
		||||
            return Boolean(
 | 
			
		||||
                previousTokenOrComment &&
 | 
			
		||||
                ["Block", "Line"].includes(previousTokenOrComment.type)
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Check a comment to determine if it is valid for this rule.
 | 
			
		||||
         * @param {ASTNode} comment The comment node to process.
 | 
			
		||||
         * @param {Object} options The options for checking this comment.
 | 
			
		||||
         * @returns {boolean} True if the comment is valid, false otherwise.
 | 
			
		||||
         */
 | 
			
		||||
        function isCommentValid(comment, options) {
 | 
			
		||||
 | 
			
		||||
            // 1. Check for default ignore pattern.
 | 
			
		||||
            if (DEFAULT_IGNORE_PATTERN.test(comment.value)) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 2. Check for custom ignore pattern.
 | 
			
		||||
            const commentWithoutAsterisks = comment.value
 | 
			
		||||
                .replace(/\*/gu, "");
 | 
			
		||||
 | 
			
		||||
            if (options.ignorePatternRegExp && options.ignorePatternRegExp.test(commentWithoutAsterisks)) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 3. Check for inline comments.
 | 
			
		||||
            if (options.ignoreInlineComments && isInlineComment(comment)) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 4. Is this a consecutive comment (and are we tolerating those)?
 | 
			
		||||
            if (options.ignoreConsecutiveComments && isConsecutiveComment(comment)) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 5. Does the comment start with a possible URL?
 | 
			
		||||
            if (MAYBE_URL.test(commentWithoutAsterisks)) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 6. Is the initial word character a letter?
 | 
			
		||||
            const commentWordCharsOnly = commentWithoutAsterisks
 | 
			
		||||
                .replace(WHITESPACE, "");
 | 
			
		||||
 | 
			
		||||
            if (commentWordCharsOnly.length === 0) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const firstWordChar = commentWordCharsOnly[0];
 | 
			
		||||
 | 
			
		||||
            if (!LETTER_PATTERN.test(firstWordChar)) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 7. Check the case of the initial word character.
 | 
			
		||||
            const isUppercase = firstWordChar !== firstWordChar.toLocaleLowerCase(),
 | 
			
		||||
                isLowercase = firstWordChar !== firstWordChar.toLocaleUpperCase();
 | 
			
		||||
 | 
			
		||||
            if (capitalize === "always" && isLowercase) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            if (capitalize === "never" && isUppercase) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Process a comment to determine if it needs to be reported.
 | 
			
		||||
         * @param {ASTNode} comment The comment node to process.
 | 
			
		||||
         * @returns {void}
 | 
			
		||||
         */
 | 
			
		||||
        function processComment(comment) {
 | 
			
		||||
            const options = normalizedOptions[comment.type],
 | 
			
		||||
                commentValid = isCommentValid(comment, options);
 | 
			
		||||
 | 
			
		||||
            if (!commentValid) {
 | 
			
		||||
                const messageId = capitalize === "always"
 | 
			
		||||
                    ? "unexpectedLowercaseComment"
 | 
			
		||||
                    : "unexpectedUppercaseComment";
 | 
			
		||||
 | 
			
		||||
                context.report({
 | 
			
		||||
                    node: null, // Intentionally using loc instead
 | 
			
		||||
                    loc: comment.loc,
 | 
			
		||||
                    messageId,
 | 
			
		||||
                    fix(fixer) {
 | 
			
		||||
                        const match = comment.value.match(LETTER_PATTERN);
 | 
			
		||||
 | 
			
		||||
                        return fixer.replaceTextRange(
 | 
			
		||||
 | 
			
		||||
                            // Offset match.index by 2 to account for the first 2 characters that start the comment (// or /*)
 | 
			
		||||
                            [comment.range[0] + match.index + 2, comment.range[0] + match.index + 3],
 | 
			
		||||
                            capitalize === "always" ? match[0].toLocaleUpperCase() : match[0].toLocaleLowerCase()
 | 
			
		||||
                        );
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //----------------------------------------------------------------------
 | 
			
		||||
        // Public
 | 
			
		||||
        //----------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            Program() {
 | 
			
		||||
                const comments = sourceCode.getAllComments();
 | 
			
		||||
 | 
			
		||||
                comments.filter(token => token.type !== "Shebang").forEach(processComment);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
		Reference in New Issue
	
	Block a user