1/** 2 * @fileoverview A rule to disallow or enforce spaces inside of single line blocks. 3 * @author Toru Nagashima 4 */ 5 6"use strict"; 7 8const util = require("./utils/ast-utils"); 9 10//------------------------------------------------------------------------------ 11// Rule Definition 12//------------------------------------------------------------------------------ 13 14module.exports = { 15 meta: { 16 type: "layout", 17 18 docs: { 19 description: "disallow or enforce spaces inside of blocks after opening block and before closing block", 20 category: "Stylistic Issues", 21 recommended: false, 22 url: "https://eslint.org/docs/rules/block-spacing" 23 }, 24 25 fixable: "whitespace", 26 27 schema: [ 28 { enum: ["always", "never"] } 29 ], 30 31 messages: { 32 missing: "Requires a space {{location}} '{{token}}'.", 33 extra: "Unexpected space(s) {{location}} '{{token}}'." 34 } 35 }, 36 37 create(context) { 38 const always = (context.options[0] !== "never"), 39 messageId = always ? "missing" : "extra", 40 sourceCode = context.getSourceCode(); 41 42 /** 43 * Gets the open brace token from a given node. 44 * @param {ASTNode} node A BlockStatement/SwitchStatement node to get. 45 * @returns {Token} The token of the open brace. 46 */ 47 function getOpenBrace(node) { 48 if (node.type === "SwitchStatement") { 49 if (node.cases.length > 0) { 50 return sourceCode.getTokenBefore(node.cases[0]); 51 } 52 return sourceCode.getLastToken(node, 1); 53 } 54 return sourceCode.getFirstToken(node); 55 } 56 57 /** 58 * Checks whether or not: 59 * - given tokens are on same line. 60 * - there is/isn't a space between given tokens. 61 * @param {Token} left A token to check. 62 * @param {Token} right The token which is next to `left`. 63 * @returns {boolean} 64 * When the option is `"always"`, `true` if there are one or more spaces between given tokens. 65 * When the option is `"never"`, `true` if there are not any spaces between given tokens. 66 * If given tokens are not on same line, it's always `true`. 67 */ 68 function isValid(left, right) { 69 return ( 70 !util.isTokenOnSameLine(left, right) || 71 sourceCode.isSpaceBetweenTokens(left, right) === always 72 ); 73 } 74 75 /** 76 * Reports invalid spacing style inside braces. 77 * @param {ASTNode} node A BlockStatement/SwitchStatement node to get. 78 * @returns {void} 79 */ 80 function checkSpacingInsideBraces(node) { 81 82 // Gets braces and the first/last token of content. 83 const openBrace = getOpenBrace(node); 84 const closeBrace = sourceCode.getLastToken(node); 85 const firstToken = sourceCode.getTokenAfter(openBrace, { includeComments: true }); 86 const lastToken = sourceCode.getTokenBefore(closeBrace, { includeComments: true }); 87 88 // Skip if the node is invalid or empty. 89 if (openBrace.type !== "Punctuator" || 90 openBrace.value !== "{" || 91 closeBrace.type !== "Punctuator" || 92 closeBrace.value !== "}" || 93 firstToken === closeBrace 94 ) { 95 return; 96 } 97 98 // Skip line comments for option never 99 if (!always && firstToken.type === "Line") { 100 return; 101 } 102 103 // Check. 104 if (!isValid(openBrace, firstToken)) { 105 let loc = openBrace.loc; 106 107 if (messageId === "extra") { 108 loc = { 109 start: openBrace.loc.end, 110 end: firstToken.loc.start 111 }; 112 } 113 114 context.report({ 115 node, 116 loc, 117 messageId, 118 data: { 119 location: "after", 120 token: openBrace.value 121 }, 122 fix(fixer) { 123 if (always) { 124 return fixer.insertTextBefore(firstToken, " "); 125 } 126 127 return fixer.removeRange([openBrace.range[1], firstToken.range[0]]); 128 } 129 }); 130 } 131 if (!isValid(lastToken, closeBrace)) { 132 let loc = closeBrace.loc; 133 134 if (messageId === "extra") { 135 loc = { 136 start: lastToken.loc.end, 137 end: closeBrace.loc.start 138 }; 139 } 140 context.report({ 141 node, 142 loc, 143 messageId, 144 data: { 145 location: "before", 146 token: closeBrace.value 147 }, 148 fix(fixer) { 149 if (always) { 150 return fixer.insertTextAfter(lastToken, " "); 151 } 152 153 return fixer.removeRange([lastToken.range[1], closeBrace.range[0]]); 154 } 155 }); 156 } 157 } 158 159 return { 160 BlockStatement: checkSpacingInsideBraces, 161 SwitchStatement: checkSpacingInsideBraces 162 }; 163 } 164}; 165