UNPKG

4.76 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to enforce spacing around embedded expressions of template strings
3 * @author Toru Nagashima
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const astUtils = require("./utils/ast-utils");
13
14//------------------------------------------------------------------------------
15// Rule Definition
16//------------------------------------------------------------------------------
17
18module.exports = {
19 meta: {
20 type: "layout",
21
22 docs: {
23 description: "require or disallow spacing around embedded expressions of template strings",
24 category: "ECMAScript 6",
25 recommended: false,
26 url: "https://eslint.org/docs/rules/template-curly-spacing"
27 },
28
29 fixable: "whitespace",
30
31 schema: [
32 { enum: ["always", "never"] }
33 ],
34 messages: {
35 expectedBefore: "Expected space(s) before '}'.",
36 expectedAfter: "Expected space(s) after '${'.",
37 unexpectedBefore: "Unexpected space(s) before '}'.",
38 unexpectedAfter: "Unexpected space(s) after '${'."
39 }
40 },
41
42 create(context) {
43 const sourceCode = context.getSourceCode();
44 const always = context.options[0] === "always";
45
46 /**
47 * Checks spacing before `}` of a given token.
48 * @param {Token} token A token to check. This is a Template token.
49 * @returns {void}
50 */
51 function checkSpacingBefore(token) {
52 if (!token.value.startsWith("}")) {
53 return; // starts with a backtick, this is the first template element in the template literal
54 }
55
56 const prevToken = sourceCode.getTokenBefore(token, { includeComments: true }),
57 hasSpace = sourceCode.isSpaceBetween(prevToken, token);
58
59 if (!astUtils.isTokenOnSameLine(prevToken, token)) {
60 return;
61 }
62
63 if (always && !hasSpace) {
64 context.report({
65 loc: {
66 start: token.loc.start,
67 end: {
68 line: token.loc.start.line,
69 column: token.loc.start.column + 1
70 }
71 },
72 messageId: "expectedBefore",
73 fix: fixer => fixer.insertTextBefore(token, " ")
74 });
75 }
76
77 if (!always && hasSpace) {
78 context.report({
79 loc: {
80 start: prevToken.loc.end,
81 end: token.loc.start
82 },
83 messageId: "unexpectedBefore",
84 fix: fixer => fixer.removeRange([prevToken.range[1], token.range[0]])
85 });
86 }
87 }
88
89 /**
90 * Checks spacing after `${` of a given token.
91 * @param {Token} token A token to check. This is a Template token.
92 * @returns {void}
93 */
94 function checkSpacingAfter(token) {
95 if (!token.value.endsWith("${")) {
96 return; // ends with a backtick, this is the last template element in the template literal
97 }
98
99 const nextToken = sourceCode.getTokenAfter(token, { includeComments: true }),
100 hasSpace = sourceCode.isSpaceBetween(token, nextToken);
101
102 if (!astUtils.isTokenOnSameLine(token, nextToken)) {
103 return;
104 }
105
106 if (always && !hasSpace) {
107 context.report({
108 loc: {
109 start: {
110 line: token.loc.end.line,
111 column: token.loc.end.column - 2
112 },
113 end: token.loc.end
114 },
115 messageId: "expectedAfter",
116 fix: fixer => fixer.insertTextAfter(token, " ")
117 });
118 }
119
120 if (!always && hasSpace) {
121 context.report({
122 loc: {
123 start: token.loc.end,
124 end: nextToken.loc.start
125 },
126 messageId: "unexpectedAfter",
127 fix: fixer => fixer.removeRange([token.range[1], nextToken.range[0]])
128 });
129 }
130 }
131
132 return {
133 TemplateElement(node) {
134 const token = sourceCode.getFirstToken(node);
135
136 checkSpacingBefore(token);
137 checkSpacingAfter(token);
138 }
139 };
140 }
141};