1/** 2 * @fileoverview disallow unnecessary concatenation of template strings 3 * @author Henry Zhu 4 */ 5"use strict"; 6 7//------------------------------------------------------------------------------ 8// Requirements 9//------------------------------------------------------------------------------ 10 11const astUtils = require("./utils/ast-utils"); 12 13//------------------------------------------------------------------------------ 14// Helpers 15//------------------------------------------------------------------------------ 16 17/** 18 * Checks whether or not a given node is a concatenation. 19 * @param {ASTNode} node A node to check. 20 * @returns {boolean} `true` if the node is a concatenation. 21 */ 22function isConcatenation(node) { 23 return node.type === "BinaryExpression" && node.operator === "+"; 24} 25 26/** 27 * Checks if the given token is a `+` token or not. 28 * @param {Token} token The token to check. 29 * @returns {boolean} `true` if the token is a `+` token. 30 */ 31function isConcatOperatorToken(token) { 32 return token.value === "+" && token.type === "Punctuator"; 33} 34 35/** 36 * Get's the right most node on the left side of a BinaryExpression with + operator. 37 * @param {ASTNode} node A BinaryExpression node to check. 38 * @returns {ASTNode} node 39 */ 40function getLeft(node) { 41 let left = node.left; 42 43 while (isConcatenation(left)) { 44 left = left.right; 45 } 46 return left; 47} 48 49/** 50 * Get's the left most node on the right side of a BinaryExpression with + operator. 51 * @param {ASTNode} node A BinaryExpression node to check. 52 * @returns {ASTNode} node 53 */ 54function getRight(node) { 55 let right = node.right; 56 57 while (isConcatenation(right)) { 58 right = right.left; 59 } 60 return right; 61} 62 63//------------------------------------------------------------------------------ 64// Rule Definition 65//------------------------------------------------------------------------------ 66 67module.exports = { 68 meta: { 69 type: "suggestion", 70 71 docs: { 72 description: "disallow unnecessary concatenation of literals or template literals", 73 category: "Best Practices", 74 recommended: false, 75 url: "https://eslint.org/docs/rules/no-useless-concat" 76 }, 77 78 schema: [], 79 80 messages: { 81 unexpectedConcat: "Unexpected string concatenation of literals." 82 } 83 }, 84 85 create(context) { 86 const sourceCode = context.getSourceCode(); 87 88 return { 89 BinaryExpression(node) { 90 91 // check if not concatenation 92 if (node.operator !== "+") { 93 return; 94 } 95 96 // account for the `foo + "a" + "b"` case 97 const left = getLeft(node); 98 const right = getRight(node); 99 100 if (astUtils.isStringLiteral(left) && 101 astUtils.isStringLiteral(right) && 102 astUtils.isTokenOnSameLine(left, right) 103 ) { 104 const operatorToken = sourceCode.getFirstTokenBetween(left, right, isConcatOperatorToken); 105 106 context.report({ 107 node, 108 loc: operatorToken.loc, 109 messageId: "unexpectedConcat" 110 }); 111 } 112 } 113 }; 114 } 115}; 116