]>
Commit | Line | Data |
---|---|---|
eb39fafa DC |
1 | /** |
2 | * @fileoverview disallow unnecessary concatenation of template strings | |
3 | * @author Henry Zhu | |
4 | */ | |
5 | "use strict"; | |
6 | ||
7 | //------------------------------------------------------------------------------ | |
8 | // Requirements | |
9 | //------------------------------------------------------------------------------ | |
10 | ||
11 | const 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 | */ | |
22 | function 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 | */ | |
31 | function 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 | */ | |
40 | function 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 | */ | |
54 | function 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 | ||
34eeec05 | 67 | /** @type {import('../shared/types').Rule} */ |
eb39fafa DC |
68 | module.exports = { |
69 | meta: { | |
70 | type: "suggestion", | |
71 | ||
72 | docs: { | |
73 | description: "disallow unnecessary concatenation of literals or template literals", | |
eb39fafa DC |
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, | |
56c4a2cb | 108 | loc: operatorToken.loc, |
eb39fafa DC |
109 | messageId: "unexpectedConcat" |
110 | }); | |
111 | } | |
112 | } | |
113 | }; | |
114 | } | |
115 | }; |