| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 | /** * @fileoverview A rule to ensure blank lines within blocks. * @author Mathias Schreck <https://github.com/lo1tuma> */"use strict";//------------------------------------------------------------------------------// Rule Definition//------------------------------------------------------------------------------module.exports = {    meta: {        docs: {            description: "require or disallow padding within blocks",            category: "Stylistic Issues",            recommended: false,            url: "https://eslint.org/docs/rules/padded-blocks"        },        fixable: "whitespace",        schema: [            {                oneOf: [                    {                        enum: ["always", "never"]                    },                    {                        type: "object",                        properties: {                            blocks: {                                enum: ["always", "never"]                            },                            switches: {                                enum: ["always", "never"]                            },                            classes: {                                enum: ["always", "never"]                            }                        },                        additionalProperties: false,                        minProperties: 1                    }                ]            }        ]    },    create(context) {        const options = {};        const config = context.options[0] || "always";        if (typeof config === "string") {            const shouldHavePadding = config === "always";            options.blocks = shouldHavePadding;            options.switches = shouldHavePadding;            options.classes = shouldHavePadding;        } else {            if (config.hasOwnProperty("blocks")) {                options.blocks = config.blocks === "always";            }            if (config.hasOwnProperty("switches")) {                options.switches = config.switches === "always";            }            if (config.hasOwnProperty("classes")) {                options.classes = config.classes === "always";            }        }        const ALWAYS_MESSAGE = "Block must be padded by blank lines.",            NEVER_MESSAGE = "Block must not be padded by blank lines.";        const sourceCode = context.getSourceCode();        /**         * Gets the open brace token from a given node.         * @param {ASTNode} node - A BlockStatement or SwitchStatement node from which to get the open brace.         * @returns {Token} The token of the open brace.         */        function getOpenBrace(node) {            if (node.type === "SwitchStatement") {                return sourceCode.getTokenBefore(node.cases[0]);            }            return sourceCode.getFirstToken(node);        }        /**         * Checks if the given parameter is a comment node         * @param {ASTNode|Token} node An AST node or token         * @returns {boolean} True if node is a comment         */        function isComment(node) {            return node.type === "Line" || node.type === "Block";        }        /**         * Checks if there is padding between two tokens         * @param {Token} first The first token         * @param {Token} second The second token         * @returns {boolean} True if there is at least a line between the tokens         */        function isPaddingBetweenTokens(first, second) {            return second.loc.start.line - first.loc.end.line >= 2;        }        /**         * Checks if the given token has a blank line after it.         * @param {Token} token The token to check.         * @returns {boolean} Whether or not the token is followed by a blank line.         */        function getFirstBlockToken(token) {            let prev,                first = token;            do {                prev = first;                first = sourceCode.getTokenAfter(first, { includeComments: true });            } while (isComment(first) && first.loc.start.line === prev.loc.end.line);            return first;        }        /**         * Checks if the given token is preceeded by a blank line.         * @param {Token} token The token to check         * @returns {boolean} Whether or not the token is preceeded by a blank line         */        function getLastBlockToken(token) {            let last = token,                next;            do {                next = last;                last = sourceCode.getTokenBefore(last, { includeComments: true });            } while (isComment(last) && last.loc.end.line === next.loc.start.line);            return last;        }        /**         * Checks if a node should be padded, according to the rule config.         * @param {ASTNode} node The AST node to check.         * @returns {boolean} True if the node should be padded, false otherwise.         */        function requirePaddingFor(node) {            switch (node.type) {                case "BlockStatement":                    return options.blocks;                case "SwitchStatement":                    return options.switches;                case "ClassBody":                    return options.classes;                /* istanbul ignore next */                default:                    throw new Error("unreachable");            }        }        /**         * Checks the given BlockStatement node to be padded if the block is not empty.         * @param {ASTNode} node The AST node of a BlockStatement.         * @returns {void} undefined.         */        function checkPadding(node) {            const openBrace = getOpenBrace(node),                firstBlockToken = getFirstBlockToken(openBrace),                tokenBeforeFirst = sourceCode.getTokenBefore(firstBlockToken, { includeComments: true }),                closeBrace = sourceCode.getLastToken(node),                lastBlockToken = getLastBlockToken(closeBrace),                tokenAfterLast = sourceCode.getTokenAfter(lastBlockToken, { includeComments: true }),                blockHasTopPadding = isPaddingBetweenTokens(tokenBeforeFirst, firstBlockToken),                blockHasBottomPadding = isPaddingBetweenTokens(lastBlockToken, tokenAfterLast);            if (requirePaddingFor(node)) {                if (!blockHasTopPadding) {                    context.report({                        node,                        loc: { line: tokenBeforeFirst.loc.start.line, column: tokenBeforeFirst.loc.start.column },                        fix(fixer) {                            return fixer.insertTextAfter(tokenBeforeFirst, "\n");                        },                        message: ALWAYS_MESSAGE                    });                }                if (!blockHasBottomPadding) {                    context.report({                        node,                        loc: { line: tokenAfterLast.loc.end.line, column: tokenAfterLast.loc.end.column - 1 },                        fix(fixer) {                            return fixer.insertTextBefore(tokenAfterLast, "\n");                        },                        message: ALWAYS_MESSAGE                    });                }            } else {                if (blockHasTopPadding) {                    context.report({                        node,                        loc: { line: tokenBeforeFirst.loc.start.line, column: tokenBeforeFirst.loc.start.column },                        fix(fixer) {                            return fixer.replaceTextRange([tokenBeforeFirst.range[1], firstBlockToken.range[0] - firstBlockToken.loc.start.column], "\n");                        },                        message: NEVER_MESSAGE                    });                }                if (blockHasBottomPadding) {                    context.report({                        node,                        loc: { line: tokenAfterLast.loc.end.line, column: tokenAfterLast.loc.end.column - 1 },                        message: NEVER_MESSAGE,                        fix(fixer) {                            return fixer.replaceTextRange([lastBlockToken.range[1], tokenAfterLast.range[0] - tokenAfterLast.loc.start.column], "\n");                        }                    });                }            }        }        const rule = {};        if (options.hasOwnProperty("switches")) {            rule.SwitchStatement = function(node) {                if (node.cases.length === 0) {                    return;                }                checkPadding(node);            };        }        if (options.hasOwnProperty("blocks")) {            rule.BlockStatement = function(node) {                if (node.body.length === 0) {                    return;                }                checkPadding(node);            };        }        if (options.hasOwnProperty("classes")) {            rule.ClassBody = function(node) {                if (node.body.length === 0) {                    return;                }                checkPadding(node);            };        }        return rule;    }};
 |