|                                                                                                                                                                                                    |  | /** * @fileoverview Rule to flag fall-through cases in switch statements. * @author Matt DuVall <http://mattduvall.com/>
 */"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const { directivesPattern } = require("../shared/directives");
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
const DEFAULT_FALLTHROUGH_COMMENT = /falls?\s?through/iu;
/** * Checks all segments in a set and returns true if any are reachable. * @param {Set<CodePathSegment>} segments The segments to check. * @returns {boolean} True if any segment is reachable; false otherwise. */function isAnySegmentReachable(segments) {
    for (const segment of segments) {        if (segment.reachable) {            return true;        }    }
    return false;}
/** * Checks whether or not a given comment string is really a fallthrough comment and not an ESLint directive. * @param {string} comment The comment string to check. * @param {RegExp} fallthroughCommentPattern The regular expression used for checking for fallthrough comments. * @returns {boolean} `true` if the comment string is truly a fallthrough comment. */function isFallThroughComment(comment, fallthroughCommentPattern) {    return fallthroughCommentPattern.test(comment) && !directivesPattern.test(comment.trim());}
/** * Checks whether or not a given case has a fallthrough comment. * @param {ASTNode} caseWhichFallsThrough SwitchCase node which falls through. * @param {ASTNode} subsequentCase The case after caseWhichFallsThrough. * @param {RuleContext} context A rule context which stores comments. * @param {RegExp} fallthroughCommentPattern A pattern to match comment to. * @returns {boolean} `true` if the case has a valid fallthrough comment. */function hasFallthroughComment(caseWhichFallsThrough, subsequentCase, context, fallthroughCommentPattern) {    const sourceCode = context.sourceCode;
    if (caseWhichFallsThrough.consequent.length === 1 && caseWhichFallsThrough.consequent[0].type === "BlockStatement") {        const trailingCloseBrace = sourceCode.getLastToken(caseWhichFallsThrough.consequent[0]);        const commentInBlock = sourceCode.getCommentsBefore(trailingCloseBrace).pop();
        if (commentInBlock && isFallThroughComment(commentInBlock.value, fallthroughCommentPattern)) {            return true;        }    }
    const comment = sourceCode.getCommentsBefore(subsequentCase).pop();
    return Boolean(comment && isFallThroughComment(comment.value, fallthroughCommentPattern));}
/** * Checks whether a node and a token are separated by blank lines * @param {ASTNode} node The node to check * @param {Token} token The token to compare against * @returns {boolean} `true` if there are blank lines between node and token */function hasBlankLinesBetween(node, token) {    return token.loc.start.line > node.loc.end.line + 1;}
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */module.exports = {    meta: {        type: "problem",
        docs: {            description: "Disallow fallthrough of `case` statements",            recommended: true,            url: "https://eslint.org/docs/latest/rules/no-fallthrough"        },
        schema: [            {                type: "object",                properties: {                    commentPattern: {                        type: "string",                        default: ""                    },                    allowEmptyCase: {                        type: "boolean",                        default: false                    }                },                additionalProperties: false            }        ],        messages: {            case: "Expected a 'break' statement before 'case'.",            default: "Expected a 'break' statement before 'default'."        }    },
    create(context) {        const options = context.options[0] || {};        const codePathSegments = [];        let currentCodePathSegments = new Set();        const sourceCode = context.sourceCode;        const allowEmptyCase = options.allowEmptyCase || false;
        /*         * We need to use leading comments of the next SwitchCase node because         * trailing comments is wrong if semicolons are omitted.         */        let fallthroughCase = null;        let fallthroughCommentPattern = null;
        if (options.commentPattern) {            fallthroughCommentPattern = new RegExp(options.commentPattern, "u");        } else {            fallthroughCommentPattern = DEFAULT_FALLTHROUGH_COMMENT;        }        return {
            onCodePathStart() {                codePathSegments.push(currentCodePathSegments);                currentCodePathSegments = new Set();            },
            onCodePathEnd() {                currentCodePathSegments = codePathSegments.pop();            },
            onUnreachableCodePathSegmentStart(segment) {                currentCodePathSegments.add(segment);            },
            onUnreachableCodePathSegmentEnd(segment) {                currentCodePathSegments.delete(segment);            },
            onCodePathSegmentStart(segment) {                currentCodePathSegments.add(segment);            },
            onCodePathSegmentEnd(segment) {                currentCodePathSegments.delete(segment);            },
            SwitchCase(node) {
                /*                 * Checks whether or not there is a fallthrough comment.                 * And reports the previous fallthrough node if that does not exist.                 */
                if (fallthroughCase && (!hasFallthroughComment(fallthroughCase, node, context, fallthroughCommentPattern))) {                    context.report({                        messageId: node.test ? "case" : "default",                        node                    });                }                fallthroughCase = null;            },
            "SwitchCase:exit"(node) {                const nextToken = sourceCode.getTokenAfter(node);
                /*                 * `reachable` meant fall through because statements preceded by                 * `break`, `return`, or `throw` are unreachable.                 * And allows empty cases and the last case.                 */                if (isAnySegmentReachable(currentCodePathSegments) &&                    (node.consequent.length > 0 || (!allowEmptyCase && hasBlankLinesBetween(node, nextToken))) &&                    node.parent.cases[node.parent.cases.length - 1] !== node) {                    fallthroughCase = node;                }            }        };    }};
 |