1/** 2 * @fileoverview Disallows or enforces spaces inside computed properties. 3 * @author Jamund Ferguson 4 */ 5"use strict"; 6 7const astUtils = require("./utils/ast-utils"); 8 9//------------------------------------------------------------------------------ 10// Rule Definition 11//------------------------------------------------------------------------------ 12 13module.exports = { 14 meta: { 15 type: "layout", 16 17 docs: { 18 description: "enforce consistent spacing inside computed property brackets", 19 category: "Stylistic Issues", 20 recommended: false, 21 url: "https://eslint.org/docs/rules/computed-property-spacing" 22 }, 23 24 fixable: "whitespace", 25 26 schema: [ 27 { 28 enum: ["always", "never"] 29 }, 30 { 31 type: "object", 32 properties: { 33 enforceForClassMembers: { 34 type: "boolean", 35 default: true 36 } 37 }, 38 additionalProperties: false 39 } 40 ], 41 42 messages: { 43 unexpectedSpaceBefore: "There should be no space before '{{tokenValue}}'.", 44 unexpectedSpaceAfter: "There should be no space after '{{tokenValue}}'.", 45 46 missingSpaceBefore: "A space is required before '{{tokenValue}}'.", 47 missingSpaceAfter: "A space is required after '{{tokenValue}}'." 48 } 49 }, 50 51 create(context) { 52 const sourceCode = context.getSourceCode(); 53 const propertyNameMustBeSpaced = context.options[0] === "always"; // default is "never" 54 const enforceForClassMembers = !context.options[1] || context.options[1].enforceForClassMembers; 55 56 //-------------------------------------------------------------------------- 57 // Helpers 58 //-------------------------------------------------------------------------- 59 60 /** 61 * Reports that there shouldn't be a space after the first token 62 * @param {ASTNode} node The node to report in the event of an error. 63 * @param {Token} token The token to use for the report. 64 * @param {Token} tokenAfter The token after `token`. 65 * @returns {void} 66 */ 67 function reportNoBeginningSpace(node, token, tokenAfter) { 68 context.report({ 69 node, 70 loc: { start: token.loc.end, end: tokenAfter.loc.start }, 71 messageId: "unexpectedSpaceAfter", 72 data: { 73 tokenValue: token.value 74 }, 75 fix(fixer) { 76 return fixer.removeRange([token.range[1], tokenAfter.range[0]]); 77 } 78 }); 79 } 80 81 /** 82 * Reports that there shouldn't be a space before the last token 83 * @param {ASTNode} node The node to report in the event of an error. 84 * @param {Token} token The token to use for the report. 85 * @param {Token} tokenBefore The token before `token`. 86 * @returns {void} 87 */ 88 function reportNoEndingSpace(node, token, tokenBefore) { 89 context.report({ 90 node, 91 loc: { start: tokenBefore.loc.end, end: token.loc.start }, 92 messageId: "unexpectedSpaceBefore", 93 data: { 94 tokenValue: token.value 95 }, 96 fix(fixer) { 97 return fixer.removeRange([tokenBefore.range[1], token.range[0]]); 98 } 99 }); 100 } 101 102 /** 103 * Reports that there should be a space after the first token 104 * @param {ASTNode} node The node to report in the event of an error. 105 * @param {Token} token The token to use for the report. 106 * @returns {void} 107 */ 108 function reportRequiredBeginningSpace(node, token) { 109 context.report({ 110 node, 111 loc: token.loc, 112 messageId: "missingSpaceAfter", 113 data: { 114 tokenValue: token.value 115 }, 116 fix(fixer) { 117 return fixer.insertTextAfter(token, " "); 118 } 119 }); 120 } 121 122 /** 123 * Reports that there should be a space before the last token 124 * @param {ASTNode} node The node to report in the event of an error. 125 * @param {Token} token The token to use for the report. 126 * @returns {void} 127 */ 128 function reportRequiredEndingSpace(node, token) { 129 context.report({ 130 node, 131 loc: token.loc, 132 messageId: "missingSpaceBefore", 133 data: { 134 tokenValue: token.value 135 }, 136 fix(fixer) { 137 return fixer.insertTextBefore(token, " "); 138 } 139 }); 140 } 141 142 /** 143 * Returns a function that checks the spacing of a node on the property name 144 * that was passed in. 145 * @param {string} propertyName The property on the node to check for spacing 146 * @returns {Function} A function that will check spacing on a node 147 */ 148 function checkSpacing(propertyName) { 149 return function(node) { 150 if (!node.computed) { 151 return; 152 } 153 154 const property = node[propertyName]; 155 156 const before = sourceCode.getTokenBefore(property, astUtils.isOpeningBracketToken), 157 first = sourceCode.getTokenAfter(before, { includeComments: true }), 158 after = sourceCode.getTokenAfter(property, astUtils.isClosingBracketToken), 159 last = sourceCode.getTokenBefore(after, { includeComments: true }); 160 161 if (astUtils.isTokenOnSameLine(before, first)) { 162 if (propertyNameMustBeSpaced) { 163 if (!sourceCode.isSpaceBetweenTokens(before, first) && astUtils.isTokenOnSameLine(before, first)) { 164 reportRequiredBeginningSpace(node, before); 165 } 166 } else { 167 if (sourceCode.isSpaceBetweenTokens(before, first)) { 168 reportNoBeginningSpace(node, before, first); 169 } 170 } 171 } 172 173 if (astUtils.isTokenOnSameLine(last, after)) { 174 if (propertyNameMustBeSpaced) { 175 if (!sourceCode.isSpaceBetweenTokens(last, after) && astUtils.isTokenOnSameLine(last, after)) { 176 reportRequiredEndingSpace(node, after); 177 } 178 } else { 179 if (sourceCode.isSpaceBetweenTokens(last, after)) { 180 reportNoEndingSpace(node, after, last); 181 } 182 } 183 } 184 }; 185 } 186 187 188 //-------------------------------------------------------------------------- 189 // Public 190 //-------------------------------------------------------------------------- 191 192 const listeners = { 193 Property: checkSpacing("key"), 194 MemberExpression: checkSpacing("property") 195 }; 196 197 if (enforceForClassMembers) { 198 listeners.MethodDefinition = checkSpacing("key"); 199 } 200 201 return listeners; 202 203 } 204}; 205