| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 | /** * @fileoverview A rule to ensure whitespace before blocks. * @author Mathias Schreck <https://github.com/lo1tuma> */"use strict";//------------------------------------------------------------------------------// Requirements//------------------------------------------------------------------------------const astUtils = require("./utils/ast-utils");//------------------------------------------------------------------------------// Helpers//------------------------------------------------------------------------------/** * Checks whether the given node represents the body of a function. * @param {ASTNode} node the node to check. * @returns {boolean} `true` if the node is function body. */function isFunctionBody(node) {    const parent = node.parent;    return (        node.type === "BlockStatement" &&        astUtils.isFunction(parent) &&        parent.body === node    );}//------------------------------------------------------------------------------// Rule Definition//------------------------------------------------------------------------------/** @type {import('../shared/types').Rule} */module.exports = {    meta: {        type: "layout",        docs: {            description: "Enforce consistent spacing before blocks",            recommended: false,            url: "https://eslint.org/docs/rules/space-before-blocks"        },        fixable: "whitespace",        schema: [            {                oneOf: [                    {                        enum: ["always", "never"]                    },                    {                        type: "object",                        properties: {                            keywords: {                                enum: ["always", "never", "off"]                            },                            functions: {                                enum: ["always", "never", "off"]                            },                            classes: {                                enum: ["always", "never", "off"]                            }                        },                        additionalProperties: false                    }                ]            }        ],        messages: {            unexpectedSpace: "Unexpected space before opening brace.",            missingSpace: "Missing space before opening brace."        }    },    create(context) {        const config = context.options[0],            sourceCode = context.getSourceCode();        let alwaysFunctions = true,            alwaysKeywords = true,            alwaysClasses = true,            neverFunctions = false,            neverKeywords = false,            neverClasses = false;        if (typeof config === "object") {            alwaysFunctions = config.functions === "always";            alwaysKeywords = config.keywords === "always";            alwaysClasses = config.classes === "always";            neverFunctions = config.functions === "never";            neverKeywords = config.keywords === "never";            neverClasses = config.classes === "never";        } else if (config === "never") {            alwaysFunctions = false;            alwaysKeywords = false;            alwaysClasses = false;            neverFunctions = true;            neverKeywords = true;            neverClasses = true;        }        /**         * Checks whether the spacing before the given block is already controlled by another rule:         * - `arrow-spacing` checks spaces after `=>`.         * - `keyword-spacing` checks spaces after keywords in certain contexts.         * - `switch-colon-spacing` checks spaces after `:` of switch cases.         * @param {Token} precedingToken first token before the block.         * @param {ASTNode|Token} node `BlockStatement` node or `{` token of a `SwitchStatement` node.         * @returns {boolean} `true` if requiring or disallowing spaces before the given block could produce conflicts with other rules.         */        function isConflicted(precedingToken, node) {            return (                astUtils.isArrowToken(precedingToken) ||                (                    astUtils.isKeywordToken(precedingToken) &&                    !isFunctionBody(node)                ) ||                (                    astUtils.isColonToken(precedingToken) &&                    node.parent &&                    node.parent.type === "SwitchCase" &&                    precedingToken === astUtils.getSwitchCaseColonToken(node.parent, sourceCode)                )            );        }        /**         * Checks the given BlockStatement node has a preceding space if it doesn’t start on a new line.         * @param {ASTNode|Token} node The AST node of a BlockStatement.         * @returns {void} undefined.         */        function checkPrecedingSpace(node) {            const precedingToken = sourceCode.getTokenBefore(node);            if (precedingToken && !isConflicted(precedingToken, node) && astUtils.isTokenOnSameLine(precedingToken, node)) {                const hasSpace = sourceCode.isSpaceBetweenTokens(precedingToken, node);                let requireSpace;                let requireNoSpace;                if (isFunctionBody(node)) {                    requireSpace = alwaysFunctions;                    requireNoSpace = neverFunctions;                } else if (node.type === "ClassBody") {                    requireSpace = alwaysClasses;                    requireNoSpace = neverClasses;                } else {                    requireSpace = alwaysKeywords;                    requireNoSpace = neverKeywords;                }                if (requireSpace && !hasSpace) {                    context.report({                        node,                        messageId: "missingSpace",                        fix(fixer) {                            return fixer.insertTextBefore(node, " ");                        }                    });                } else if (requireNoSpace && hasSpace) {                    context.report({                        node,                        messageId: "unexpectedSpace",                        fix(fixer) {                            return fixer.removeRange([precedingToken.range[1], node.range[0]]);                        }                    });                }            }        }        /**         * Checks if the CaseBlock of an given SwitchStatement node has a preceding space.         * @param {ASTNode} node The node of a SwitchStatement.         * @returns {void} undefined.         */        function checkSpaceBeforeCaseBlock(node) {            const cases = node.cases;            let openingBrace;            if (cases.length > 0) {                openingBrace = sourceCode.getTokenBefore(cases[0]);            } else {                openingBrace = sourceCode.getLastToken(node, 1);            }            checkPrecedingSpace(openingBrace);        }        return {            BlockStatement: checkPrecedingSpace,            ClassBody: checkPrecedingSpace,            SwitchStatement: checkSpaceBeforeCaseBlock        };    }};
 |