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
- };
- }
- };
|