Skip to content

Commit e36341f

Browse files
committed
feat: remove unnecessary String.raw
1 parent ad27922 commit e36341f

File tree

4 files changed

+256
-1
lines changed

4 files changed

+256
-1
lines changed

rules/prefer-string-raw.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import {isStringLiteral, isDirective} from './ast/index.js';
22
import {fixSpaceAroundKeyword} from './fix/index.js';
33

44
const MESSAGE_ID = 'prefer-string-raw';
5+
const MESSAGE_ID_UNNECESSARY_STRING_RAW = 'unnecessary-string-raw';
56
const messages = {
67
[MESSAGE_ID]: '`String.raw` should be used to avoid escaping `\\`.',
8+
[MESSAGE_ID_UNNECESSARY_STRING_RAW]: 'Using `String.raw` is unnecessary as the string does not contain any `\\`.',
79
};
810

911
const BACKSLASH = '\\';
@@ -64,6 +66,40 @@ const create = context => {
6466
},
6567
};
6668
});
69+
70+
context.on('TaggedTemplateExpression', node => {
71+
const {quasi, tag} = node;
72+
73+
if (tag.type !== 'MemberExpression'
74+
|| tag.object.type !== 'Identifier'
75+
|| tag.property.type !== 'Identifier'
76+
|| tag.object.name !== 'String'
77+
|| tag.property.name !== 'raw'
78+
) {
79+
return;
80+
}
81+
82+
const hasBackslash = quasi.quasis.some(
83+
quasi => quasi.value.raw.includes(BACKSLASH),
84+
);
85+
86+
if (hasBackslash) {
87+
return;
88+
}
89+
90+
const rawQuasi = context.sourceCode.getText(quasi);
91+
const suggestion = quasi.expressions.length > 0 || /\r?\n/.test(rawQuasi)
92+
? rawQuasi
93+
: `'${rawQuasi.slice(1, -1).replaceAll('\'', String.raw`\'`)}'`;
94+
95+
return {
96+
node,
97+
messageId: MESSAGE_ID_UNNECESSARY_STRING_RAW,
98+
* fix(fixer) {
99+
yield fixer.replaceText(node, suggestion);
100+
},
101+
};
102+
});
67103
};
68104

69105
/** @type {import('eslint').Rule.RuleModule} */

test/prefer-string-raw.js

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable no-template-curly-in-string */
12
import outdent from 'outdent';
23
import {getTester} from './utils/test.js';
34

@@ -18,7 +19,6 @@ test.snapshot({
1819
`,
1920
String.raw`a = 'a\\b\u{51}c'`,
2021
'a = "a\\\\b`"',
21-
// eslint-disable-next-line no-template-curly-in-string
2222
'a = "a\\\\b${foo}"',
2323
{
2424
code: String.raw`<Component attribute="a\\b" />`,
@@ -47,6 +47,37 @@ test.snapshot({
4747
],
4848
});
4949

50+
test.snapshot({
51+
valid: [
52+
'a = String.raw`a\\b`',
53+
'a = String.raw`a\\b${foo}cd`',
54+
'a = String.raw`ab${foo}c\\nd`',
55+
outdent`
56+
a = String.raw\`a
57+
b\\c
58+
de\`
59+
`,
60+
],
61+
invalid: [
62+
'a = String.raw`abc`',
63+
'a = String.raw`ab${foo}cd`',
64+
'a = String.raw`ab"c`',
65+
'a = String.raw`ab\'c`',
66+
'a = String.raw`ab\'"c`',
67+
'a = String.raw`ab\r\nc`',
68+
outdent`
69+
a = String.raw\`a
70+
bc
71+
de\`
72+
`,
73+
outdent`
74+
a = String.raw\`
75+
a\${foo}b
76+
\${bar}cd\`
77+
`,
78+
],
79+
});
80+
5081
test.typescript({
5182
valid: [
5283
outdent`

test/snapshots/prefer-string-raw.js.md

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,191 @@ Generated by [AVA](https://avajs.dev).
129129
> 1 | a = "a\\\\b\\""␊
130130
| ^^^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊
131131
`
132+
133+
## invalid(1): a = String.raw`abc`
134+
135+
> Input
136+
137+
`␊
138+
1 | a = String.raw\`abc\`␊
139+
`
140+
141+
> Output
142+
143+
`␊
144+
1 | a = 'abc'␊
145+
`
146+
147+
> Error 1/1
148+
149+
`␊
150+
> 1 | a = String.raw\`abc\`␊
151+
| ^^^^^^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊
152+
`
153+
154+
## invalid(2): a = String.raw`ab${foo}cd`
155+
156+
> Input
157+
158+
`␊
159+
1 | a = String.raw\`ab${foo}cd\`␊
160+
`
161+
162+
> Output
163+
164+
`␊
165+
1 | a = \`ab${foo}cd\`␊
166+
`
167+
168+
> Error 1/1
169+
170+
`␊
171+
> 1 | a = String.raw\`ab${foo}cd\`␊
172+
| ^^^^^^^^^^^^^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊
173+
`
174+
175+
## invalid(3): a = String.raw`ab"c`
176+
177+
> Input
178+
179+
`␊
180+
1 | a = String.raw\`ab"c\`␊
181+
`
182+
183+
> Output
184+
185+
`␊
186+
1 | a = 'ab"c'␊
187+
`
188+
189+
> Error 1/1
190+
191+
`␊
192+
> 1 | a = String.raw\`ab"c\`␊
193+
| ^^^^^^^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊
194+
`
195+
196+
## invalid(4): a = String.raw`ab'c`
197+
198+
> Input
199+
200+
`␊
201+
1 | a = String.raw\`ab'c\`␊
202+
`
203+
204+
> Output
205+
206+
`␊
207+
1 | a = 'ab\\'c'␊
208+
`
209+
210+
> Error 1/1
211+
212+
`␊
213+
> 1 | a = String.raw\`ab'c\`␊
214+
| ^^^^^^^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊
215+
`
216+
217+
## invalid(5): a = String.raw`ab'"c`
218+
219+
> Input
220+
221+
`␊
222+
1 | a = String.raw\`ab'"c\`␊
223+
`
224+
225+
> Output
226+
227+
`␊
228+
1 | a = 'ab\\'"c'␊
229+
`
230+
231+
> Error 1/1
232+
233+
`␊
234+
> 1 | a = String.raw\`ab'"c\`␊
235+
| ^^^^^^^^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊
236+
`
237+
238+
## invalid(6): a = String.raw`ab c`
239+
240+
> Input
241+
242+
`␊
243+
1 | a = String.raw\`ab␊
244+
2 | c\`␊
245+
`
246+
247+
> Output
248+
249+
`␊
250+
1 | a = \`ab␊
251+
2 | c\`␊
252+
`
253+
254+
> Error 1/1
255+
256+
`␊
257+
> 1 | a = String.raw\`ab␊
258+
| ^^^^^^^^^^^^^␊
259+
> 2 | c\`␊
260+
| ^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊
261+
`
262+
263+
## invalid(7): a = String.raw`a bc de`
264+
265+
> Input
266+
267+
`␊
268+
1 | a = String.raw\`a␊
269+
2 | bc␊
270+
3 | de\`␊
271+
`
272+
273+
> Output
274+
275+
`␊
276+
1 | a = \`a␊
277+
2 | bc␊
278+
3 | de\`␊
279+
`
280+
281+
> Error 1/1
282+
283+
`␊
284+
> 1 | a = String.raw\`a␊
285+
| ^^^^^^^^^^^^␊
286+
> 2 | bc␊
287+
| ^^^␊
288+
> 3 | de\`␊
289+
| ^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊
290+
`
291+
292+
## invalid(8): a = String.raw` a${foo}b ${bar}cd`
293+
294+
> Input
295+
296+
`␊
297+
1 | a = String.raw\`␊
298+
2 | a${foo}b␊
299+
3 | ${bar}cd\`␊
300+
`
301+
302+
> Output
303+
304+
`␊
305+
1 | a = \`␊
306+
2 | a${foo}b␊
307+
3 | ${bar}cd\`␊
308+
`
309+
310+
> Error 1/1
311+
312+
`␊
313+
> 1 | a = String.raw\`␊
314+
| ^^^^^^^^^^^␊
315+
> 2 | a${foo}b␊
316+
| ^^^^^^^^␊
317+
> 3 | ${bar}cd\`␊
318+
| ^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊
319+
`
404 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)