1/** 2 * @fileoverview Enforce spacing between rest and spread operators and their expressions. 3 * @author Kai Cataldo 4 */ 5 6"use strict"; 7 8//------------------------------------------------------------------------------ 9// Rule Definition 10//------------------------------------------------------------------------------ 11 12module.exports = { 13 meta: { 14 type: "layout", 15 16 docs: { 17 description: "enforce spacing between rest and spread operators and their expressions", 18 category: "ECMAScript 6", 19 recommended: false, 20 url: "https://eslint.org/docs/rules/rest-spread-spacing" 21 }, 22 23 fixable: "whitespace", 24 25 schema: [ 26 { 27 enum: ["always", "never"] 28 } 29 ], 30 31 messages: { 32 unexpectedWhitespace: "Unexpected whitespace after {{type}} operator.", 33 expectedWhitespace: "Expected whitespace after {{type}} operator." 34 } 35 }, 36 37 create(context) { 38 const sourceCode = context.getSourceCode(), 39 alwaysSpace = context.options[0] === "always"; 40 41 //-------------------------------------------------------------------------- 42 // Helpers 43 //-------------------------------------------------------------------------- 44 45 /** 46 * Checks whitespace between rest/spread operators and their expressions 47 * @param {ASTNode} node The node to check 48 * @returns {void} 49 */ 50 function checkWhiteSpace(node) { 51 const operator = sourceCode.getFirstToken(node), 52 nextToken = sourceCode.getTokenAfter(operator), 53 hasWhitespace = sourceCode.isSpaceBetweenTokens(operator, nextToken); 54 let type; 55 56 switch (node.type) { 57 case "SpreadElement": 58 type = "spread"; 59 if (node.parent.type === "ObjectExpression") { 60 type += " property"; 61 } 62 break; 63 case "RestElement": 64 type = "rest"; 65 if (node.parent.type === "ObjectPattern") { 66 type += " property"; 67 } 68 break; 69 case "ExperimentalSpreadProperty": 70 type = "spread property"; 71 break; 72 case "ExperimentalRestProperty": 73 type = "rest property"; 74 break; 75 default: 76 return; 77 } 78 79 if (alwaysSpace && !hasWhitespace) { 80 context.report({ 81 node, 82 loc: operator.loc, 83 messageId: "expectedWhitespace", 84 data: { 85 type 86 }, 87 fix(fixer) { 88 return fixer.replaceTextRange([operator.range[1], nextToken.range[0]], " "); 89 } 90 }); 91 } else if (!alwaysSpace && hasWhitespace) { 92 context.report({ 93 node, 94 loc: { 95 start: operator.loc.end, 96 end: nextToken.loc.start 97 }, 98 messageId: "unexpectedWhitespace", 99 data: { 100 type 101 }, 102 fix(fixer) { 103 return fixer.removeRange([operator.range[1], nextToken.range[0]]); 104 } 105 }); 106 } 107 } 108 109 //-------------------------------------------------------------------------- 110 // Public 111 //-------------------------------------------------------------------------- 112 113 return { 114 SpreadElement: checkWhiteSpace, 115 RestElement: checkWhiteSpace, 116 ExperimentalSpreadProperty: checkWhiteSpace, 117 ExperimentalRestProperty: checkWhiteSpace 118 }; 119 } 120}; 121