| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 | /** * @fileoverview Rule to flag block statements that do not use the one true brace style * @author Ian Christian Myers */"use strict";const astUtils = require("./utils/ast-utils");//------------------------------------------------------------------------------// Rule Definition//------------------------------------------------------------------------------module.exports = {    meta: {        type: "layout",        docs: {            description: "enforce consistent brace style for blocks",            category: "Stylistic Issues",            recommended: false,            url: "https://eslint.org/docs/rules/brace-style"        },        schema: [            {                enum: ["1tbs", "stroustrup", "allman"]            },            {                type: "object",                properties: {                    allowSingleLine: {                        type: "boolean",                        default: false                    }                },                additionalProperties: false            }        ],        fixable: "whitespace",        messages: {            nextLineOpen: "Opening curly brace does not appear on the same line as controlling statement.",            sameLineOpen: "Opening curly brace appears on the same line as controlling statement.",            blockSameLine: "Statement inside of curly braces should be on next line.",            nextLineClose: "Closing curly brace does not appear on the same line as the subsequent block.",            singleLineClose: "Closing curly brace should be on the same line as opening curly brace or on the line after the previous block.",            sameLineClose: "Closing curly brace appears on the same line as the subsequent block."        }    },    create(context) {        const style = context.options[0] || "1tbs",            params = context.options[1] || {},            sourceCode = context.getSourceCode();        //--------------------------------------------------------------------------        // Helpers        //--------------------------------------------------------------------------        /**         * Fixes a place where a newline unexpectedly appears         * @param {Token} firstToken The token before the unexpected newline         * @param {Token} secondToken The token after the unexpected newline         * @returns {Function} A fixer function to remove the newlines between the tokens         */        function removeNewlineBetween(firstToken, secondToken) {            const textRange = [firstToken.range[1], secondToken.range[0]];            const textBetween = sourceCode.text.slice(textRange[0], textRange[1]);            // Don't do a fix if there is a comment between the tokens            if (textBetween.trim()) {                return null;            }            return fixer => fixer.replaceTextRange(textRange, " ");        }        /**         * Validates a pair of curly brackets based on the user's config         * @param {Token} openingCurly The opening curly bracket         * @param {Token} closingCurly The closing curly bracket         * @returns {void}         */        function validateCurlyPair(openingCurly, closingCurly) {            const tokenBeforeOpeningCurly = sourceCode.getTokenBefore(openingCurly);            const tokenAfterOpeningCurly = sourceCode.getTokenAfter(openingCurly);            const tokenBeforeClosingCurly = sourceCode.getTokenBefore(closingCurly);            const singleLineException = params.allowSingleLine && astUtils.isTokenOnSameLine(openingCurly, closingCurly);            if (style !== "allman" && !astUtils.isTokenOnSameLine(tokenBeforeOpeningCurly, openingCurly)) {                context.report({                    node: openingCurly,                    messageId: "nextLineOpen",                    fix: removeNewlineBetween(tokenBeforeOpeningCurly, openingCurly)                });            }            if (style === "allman" && astUtils.isTokenOnSameLine(tokenBeforeOpeningCurly, openingCurly) && !singleLineException) {                context.report({                    node: openingCurly,                    messageId: "sameLineOpen",                    fix: fixer => fixer.insertTextBefore(openingCurly, "\n")                });            }            if (astUtils.isTokenOnSameLine(openingCurly, tokenAfterOpeningCurly) && tokenAfterOpeningCurly !== closingCurly && !singleLineException) {                context.report({                    node: openingCurly,                    messageId: "blockSameLine",                    fix: fixer => fixer.insertTextAfter(openingCurly, "\n")                });            }            if (tokenBeforeClosingCurly !== openingCurly && !singleLineException && astUtils.isTokenOnSameLine(tokenBeforeClosingCurly, closingCurly)) {                context.report({                    node: closingCurly,                    messageId: "singleLineClose",                    fix: fixer => fixer.insertTextBefore(closingCurly, "\n")                });            }        }        /**         * Validates the location of a token that appears before a keyword (e.g. a newline before `else`)         * @param {Token} curlyToken The closing curly token. This is assumed to precede a keyword token (such as `else` or `finally`).         * @returns {void}         */        function validateCurlyBeforeKeyword(curlyToken) {            const keywordToken = sourceCode.getTokenAfter(curlyToken);            if (style === "1tbs" && !astUtils.isTokenOnSameLine(curlyToken, keywordToken)) {                context.report({                    node: curlyToken,                    messageId: "nextLineClose",                    fix: removeNewlineBetween(curlyToken, keywordToken)                });            }            if (style !== "1tbs" && astUtils.isTokenOnSameLine(curlyToken, keywordToken)) {                context.report({                    node: curlyToken,                    messageId: "sameLineClose",                    fix: fixer => fixer.insertTextAfter(curlyToken, "\n")                });            }        }        //--------------------------------------------------------------------------        // Public API        //--------------------------------------------------------------------------        return {            BlockStatement(node) {                if (!astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type)) {                    validateCurlyPair(sourceCode.getFirstToken(node), sourceCode.getLastToken(node));                }            },            ClassBody(node) {                validateCurlyPair(sourceCode.getFirstToken(node), sourceCode.getLastToken(node));            },            SwitchStatement(node) {                const closingCurly = sourceCode.getLastToken(node);                const openingCurly = sourceCode.getTokenBefore(node.cases.length ? node.cases[0] : closingCurly);                validateCurlyPair(openingCurly, closingCurly);            },            IfStatement(node) {                if (node.consequent.type === "BlockStatement" && node.alternate) {                    // Handle the keyword after the `if` block (before `else`)                    validateCurlyBeforeKeyword(sourceCode.getLastToken(node.consequent));                }            },            TryStatement(node) {                // Handle the keyword after the `try` block (before `catch` or `finally`)                validateCurlyBeforeKeyword(sourceCode.getLastToken(node.block));                if (node.handler && node.finalizer) {                    // Handle the keyword after the `catch` block (before `finally`)                    validateCurlyBeforeKeyword(sourceCode.getLastToken(node.handler.body));                }            }        };    }};
 |