From d900274e5cfd42057c274a75e7569142a8290a6c Mon Sep 17 00:00:00 2001 From: Som Shekhar Mukherjee Date: Mon, 23 Jun 2025 20:53:42 +0530 Subject: [PATCH 01/17] feat: add support for template literals --- rules/prefer-string-raw.js | 72 ++++++++++----- test/prefer-string-raw.js | 27 +++++- test/snapshots/prefer-string-raw.js.md | 112 +++++++++++++++++++++++ test/snapshots/prefer-string-raw.js.snap | Bin 446 -> 688 bytes 4 files changed, 188 insertions(+), 23 deletions(-) diff --git a/rules/prefer-string-raw.js b/rules/prefer-string-raw.js index 00dd81c97f..3d79146f68 100644 --- a/rules/prefer-string-raw.js +++ b/rules/prefer-string-raw.js @@ -8,27 +8,9 @@ const messages = { const BACKSLASH = '\\'; -function unescapeBackslash(raw) { - const quote = raw.charAt(0); - - raw = raw.slice(1, -1); - - let result = ''; - for (let position = 0; position < raw.length; position++) { - const character = raw[position]; - if (character === BACKSLASH) { - const nextCharacter = raw[position + 1]; - if (nextCharacter === BACKSLASH || nextCharacter === quote) { - result += nextCharacter; - position++; - continue; - } - } - - result += character; - } - - return result; +function unescapeBackslash(value, quote = '') { + return value + .replaceAll(new RegExp(String.raw`\\([\\${quote}])`, 'g'), '$1'); } /** @param {import('eslint').Rule.RuleContext} context */ @@ -65,7 +47,7 @@ const create = context => { return; } - const unescaped = unescapeBackslash(raw); + const unescaped = unescapeBackslash(raw.slice(1, -1), raw.charAt(0)); if (unescaped !== node.value) { return; } @@ -79,6 +61,52 @@ const create = context => { }, }; }); + + context.on('TemplateLiteral', node => { + if (node.parent.type === 'TaggedTemplateExpression') { + return; + } + + let suggestedValue = ''; + let hasBackslash = false; + + for (let index = 0; index < node.quasis.length; index++) { + const quasi = node.quasis[index]; + const {raw, cooked} = quasi.value; + + if (cooked.at(-1) === BACKSLASH) { + return; + } + + const unescaped = unescapeBackslash(raw); + if (unescaped !== cooked) { + return; + } + + if (cooked.includes(BACKSLASH)) { + hasBackslash = true; + } + + if (index > 0) { + suggestedValue += '${' + context.sourceCode.getText(node.expressions[index - 1]) + '}'; + } + + suggestedValue += unescaped; + } + + if (!hasBackslash) { + return; + } + + return { + node, + messageId: MESSAGE_ID, + * fix(fixer) { + yield fixer.replaceText(node, `String.raw\`${suggestedValue}\``); + yield * fixSpaceAroundKeyword(fixer, node, context.sourceCode); + }, + }; + }); }; /** @type {import('eslint').Rule.RuleModule} */ diff --git a/test/prefer-string-raw.js b/test/prefer-string-raw.js index 5d3acf25c4..03cce1b2ef 100644 --- a/test/prefer-string-raw.js +++ b/test/prefer-string-raw.js @@ -1,3 +1,4 @@ +/* eslint-disable no-template-curly-in-string */ import outdent from 'outdent'; import {getTester} from './utils/test.js'; @@ -18,7 +19,6 @@ test.snapshot({ `, String.raw`a = 'a\\b\u{51}c'`, 'a = "a\\\\b`"', - // eslint-disable-next-line no-template-curly-in-string 'a = "a\\\\b${foo}"', { code: String.raw``, @@ -43,6 +43,31 @@ test.snapshot({ ], }); +test.snapshot({ + valid: [ + 'a = `a`', + 'a = `${foo}`', + 'a = `a${100}b`', + 'a = `a\\t${foo.bar}b\\\\c`', + 'a = `${foo}\\\\a${bar}\\``', + 'a = `a\\${`', + 'a = `${a}\\\'${b}\\\\`', + 'a = `\\"a\\\\b`', + 'a = `\\\\a${foo}b\\\\${foo}`', + outdent` + a = \`\\\\a \\ + b\` + `, + ], + invalid: [ + 'a = `a\\\\b`', + 'a = `a\\\\b${foo}cd`', + 'a = `a\\\\b${foo}cd${foo.bar}e\\\\f`', + 'a = `a${foo}${foo.bar}b\\\\c`', + 'a = `a\\\\b${"c\\\\d"}e`', + ], +}); + test.typescript({ valid: [ outdent` diff --git a/test/snapshots/prefer-string-raw.js.md b/test/snapshots/prefer-string-raw.js.md index 44a6d762d6..4f587b9403 100644 --- a/test/snapshots/prefer-string-raw.js.md +++ b/test/snapshots/prefer-string-raw.js.md @@ -87,3 +87,115 @@ Generated by [AVA](https://avajs.dev). > 1 | const foo = "foo \\\\x46";␊ | ^^^^^^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ ` + +## invalid(1): a = `a\\b` + +> Input + + `␊ + 1 | a = \`a\\\\b\`␊ + ` + +> Output + + `␊ + 1 | a = String.raw\`a\\b\`␊ + ` + +> Error 1/1 + + `␊ + > 1 | a = \`a\\\\b\`␊ + | ^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ + ` + +## invalid(2): a = `a\\b${foo}cd` + +> Input + + `␊ + 1 | a = \`a\\\\b${foo}cd\`␊ + ` + +> Output + + `␊ + 1 | a = String.raw\`a\\b${foo}cd\`␊ + ` + +> Error 1/1 + + `␊ + > 1 | a = \`a\\\\b${foo}cd\`␊ + | ^^^^^^^^^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ + ` + +## invalid(3): a = `a\\b${foo}cd${foo.bar}e\\f` + +> Input + + `␊ + 1 | a = \`a\\\\b${foo}cd${foo.bar}e\\\\f\`␊ + ` + +> Output + + `␊ + 1 | a = String.raw\`a\\b${foo}cd${foo.bar}e\\f\`␊ + ` + +> Error 1/1 + + `␊ + > 1 | a = \`a\\\\b${foo}cd${foo.bar}e\\\\f\`␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ + ` + +## invalid(4): a = `a${foo}${foo.bar}b\\c` + +> Input + + `␊ + 1 | a = \`a${foo}${foo.bar}b\\\\c\`␊ + ` + +> Output + + `␊ + 1 | a = String.raw\`a${foo}${foo.bar}b\\c\`␊ + ` + +> Error 1/1 + + `␊ + > 1 | a = \`a${foo}${foo.bar}b\\\\c\`␊ + | ^^^^^^^^^^^^^^^^^^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ + ` + +## invalid(5): a = `a\\b${"c\\d"}e` + +> Input + + `␊ + 1 | a = \`a\\\\b${"c\\\\d"}e\`␊ + ` + +> Output + + `␊ + 1 | a = String.raw\`a\\b${String.raw\`c\\d\`}e\`␊ + ` + +> Error 1/2 + + `␊ + > 1 | a = \`a\\\\b${"c\\\\d"}e\`␊ + | ^^^^^^^^^^^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ + ` + +> Error 2/2 + + `␊ + > 1 | a = \`a\\\\b${"c\\\\d"}e\`␊ + | ^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ + ` diff --git a/test/snapshots/prefer-string-raw.js.snap b/test/snapshots/prefer-string-raw.js.snap index 93f30083b58f405c3c7578fa09f89c6f3bfb4523..3679132113ef333deb79b018b521a6fc97e54b92 100644 GIT binary patch literal 688 zcmV;h0#E%xRzV!%s#%B2cNR;D zw6bP@K_80<00000000BMmQ8QdKoEvg5JJigZhUIwR&tDr+k_CLr0tCZCk{vmiKTdL zuTz8T4fZ;*i6pA^^bo>;cE~1CC+~=?Tvw zA6y1m%6t$U=$MB-u+*cYFdl+K99tF+pC<`VpuN|YSn>x;E*;5b@^(@6T_4y#EU2z zer=S0TS8c2p(8m!CXZK`lm}1`^*i!AlTcTMQt{%me%L!p*j=j+9*rOA{L5@=}lP%o`Ink63{+au((ebN_+ta)e!<=}O}3gbhpBPw~hb z9wELlWeVkQvZF_+qx;n{ztrmR9w4>7VziZSkN8gEhXLov@I!U3i#YXU*MPJm^JFN` W@bB`h_cx@o*ZBiKSXv-T3;+ON{!hdJ literal 446 zcmV;v0YUyjRzV?Tb`3eKscS^9@Fmvikhc$#i~b|Ls!=CRPR`^HmIFi+_WMaw9<;AD1S ztT$BhVqoF@bF=?Yvu0`+whM8_L&XFKQn6u>QkA8AdP Date: Mon, 23 Jun 2025 21:58:52 +0530 Subject: [PATCH 02/17] fix: replace template literal with `String.raw` --- rules/prefer-string-replace-all.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rules/prefer-string-replace-all.js b/rules/prefer-string-replace-all.js index 226a879141..cfae07956f 100644 --- a/rules/prefer-string-replace-all.js +++ b/rules/prefer-string-replace-all.js @@ -57,11 +57,11 @@ function getPatternReplacement(node) { return String.raw`\t`; } - return `\\u{${codePoint.toString(16)}}`; + return String.raw`\u{${codePoint.toString(16)}}`; } if (kind === 'octal') { - return `\\u{${codePoint.toString(16)}}`; + return String.raw`\u{${codePoint.toString(16)}}`; } let character = raw; From 3ed5c79e89d0a4ae03e44ba4957dfc47146a9fab Mon Sep 17 00:00:00 2001 From: Som Shekhar Mukherjee Date: Tue, 24 Jun 2025 12:41:05 +0530 Subject: [PATCH 03/17] chore: add comments to biffurcate test cases --- test/prefer-string-raw.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/test/prefer-string-raw.js b/test/prefer-string-raw.js index 03cce1b2ef..1933334f1d 100644 --- a/test/prefer-string-raw.js +++ b/test/prefer-string-raw.js @@ -45,15 +45,22 @@ test.snapshot({ test.snapshot({ valid: [ + // No backslash 'a = `a`', 'a = `${foo}`', 'a = `a${100}b`', - 'a = `a\\t${foo.bar}b\\\\c`', - 'a = `${foo}\\\\a${bar}\\``', - 'a = `a\\${`', - 'a = `${a}\\\'${b}\\\\`', - 'a = `\\"a\\\\b`', + + // Escaped characters other than backslash + 'a = `a\\t${foo.bar}b\\\\c`', // \t + 'a = `${foo}\\\\a${bar}\\``', // \` + 'a = `a\\${`', // \$ + 'a = `${a}\\\'${b}\\\\`', // \' + 'a = `\\"a\\\\b`', // \" + + // Ending with backslash 'a = `\\\\a${foo}b\\\\${foo}`', + + // Multiline outdent` a = \`\\\\a \\ b\` From ce72f0d847651e5a05cb39b35fe1f47058e2b417 Mon Sep 17 00:00:00 2001 From: Som Shekhar Mukherjee Date: Tue, 24 Jun 2025 12:44:58 +0530 Subject: [PATCH 04/17] test: add case for tagged template expression --- test/prefer-string-raw.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/prefer-string-raw.js b/test/prefer-string-raw.js index 1933334f1d..5f73bf5def 100644 --- a/test/prefer-string-raw.js +++ b/test/prefer-string-raw.js @@ -60,6 +60,9 @@ test.snapshot({ // Ending with backslash 'a = `\\\\a${foo}b\\\\${foo}`', + // Tagged template expression + 'a = String.raw`a\\\\b`', + // Multiline outdent` a = \`\\\\a \\ From a72253a11174493457f10af9407c23abe90b27aa Mon Sep 17 00:00:00 2001 From: Som Shekhar Mukherjee Date: Tue, 24 Jun 2025 12:52:06 +0530 Subject: [PATCH 05/17] refactor: add variation in test cases --- test/prefer-string-raw.js | 4 ++-- test/snapshots/prefer-string-raw.js.md | 20 ++++++++++---------- test/snapshots/prefer-string-raw.js.snap | Bin 688 -> 714 bytes 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/test/prefer-string-raw.js b/test/prefer-string-raw.js index 5f73bf5def..e5ed421d12 100644 --- a/test/prefer-string-raw.js +++ b/test/prefer-string-raw.js @@ -71,8 +71,8 @@ test.snapshot({ ], invalid: [ 'a = `a\\\\b`', - 'a = `a\\\\b${foo}cd`', - 'a = `a\\\\b${foo}cd${foo.bar}e\\\\f`', + 'function a() {return `a\\\\b${foo}cd`}', + 'a = {[`a\\\\b${foo}cd${foo.bar}e\\\\f`]: b}', 'a = `a${foo}${foo.bar}b\\\\c`', 'a = `a\\\\b${"c\\\\d"}e`', ], diff --git a/test/snapshots/prefer-string-raw.js.md b/test/snapshots/prefer-string-raw.js.md index 4f587b9403..a2d6ca6a15 100644 --- a/test/snapshots/prefer-string-raw.js.md +++ b/test/snapshots/prefer-string-raw.js.md @@ -109,46 +109,46 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ ` -## invalid(2): a = `a\\b${foo}cd` +## invalid(2): function a() {return `a\\b${foo}cd`} > Input `␊ - 1 | a = \`a\\\\b${foo}cd\`␊ + 1 | function a() {return \`a\\\\b${foo}cd\`}␊ ` > Output `␊ - 1 | a = String.raw\`a\\b${foo}cd\`␊ + 1 | function a() {return String.raw\`a\\b${foo}cd\`}␊ ` > Error 1/1 `␊ - > 1 | a = \`a\\\\b${foo}cd\`␊ - | ^^^^^^^^^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ + > 1 | function a() {return \`a\\\\b${foo}cd\`}␊ + | ^^^^^^^^^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ ` -## invalid(3): a = `a\\b${foo}cd${foo.bar}e\\f` +## invalid(3): a = {[`a\\b${foo}cd${foo.bar}e\\f`]: b} > Input `␊ - 1 | a = \`a\\\\b${foo}cd${foo.bar}e\\\\f\`␊ + 1 | a = {[\`a\\\\b${foo}cd${foo.bar}e\\\\f\`]: b}␊ ` > Output `␊ - 1 | a = String.raw\`a\\b${foo}cd${foo.bar}e\\f\`␊ + 1 | a = {[String.raw\`a\\b${foo}cd${foo.bar}e\\f\`]: b}␊ ` > Error 1/1 `␊ - > 1 | a = \`a\\\\b${foo}cd${foo.bar}e\\\\f\`␊ - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ + > 1 | a = {[\`a\\\\b${foo}cd${foo.bar}e\\\\f\`]: b}␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ ` ## invalid(4): a = `a${foo}${foo.bar}b\\c` diff --git a/test/snapshots/prefer-string-raw.js.snap b/test/snapshots/prefer-string-raw.js.snap index 3679132113ef333deb79b018b521a6fc97e54b92..1184640457864165f1c5cba21480a2ef006899b7 100644 GIT binary patch literal 714 zcmV;*0yX_XRzV1Ouk%t9+T`f+^pQL%+}Ve%AKvdRe&~}Yl
    vcheMih52OG#5`hOiaoI4420#gIs*Qu=I2JLqTWy79{BX&oqqt1oEOvcsgEP(J zX`rz8w4)-SiP8>|){r#AN%z38J|dHM8Z5m3irFTpHI~$NEgSqaa%CVQVA~c9WP$%hh1O7?pijuB1$CqLO>sC zvkyYX-ThiGPr8W?1qxD;-h}x8IamePx# literal 688 zcmV;h0#E%xRzV!%s#%B2cNR;D zw6bP@K_80<00000000BMmQ8QdKoEvg5JJigZhUIwR&tDr+k_CLr0tCZCk{vmiKTdL zuTz8T4fZ;*i6pA^^bo>;cE~1CC+~=?Tvw zA6y1m%6t$U=$MB-u+*cYFdl+K99tF+pC<`VpuN|YSn>x;E*;5b@^(@6T_4y#EU2z zer=S0TS8c2p(8m!CXZK`lm}1`^*i!AlTcTMQt{%me%L!p*j=j+9*rOA{L5@=}lP%o`Ink63{+au((ebN_+ta)e!<=}O}3gbhpBPw~hb z9wELlWeVkQvZF_+qx;n{ztrmR9w4>7VziZSkN8gEhXLov@I!U3i#YXU*MPJm^JFN` W@bB`h_cx@o*ZBiKSXv-T3;+ON{!hdJ From 8b4352dc33e2030a5289ab7056e97e97b8274344 Mon Sep 17 00:00:00 2001 From: Som Shekhar Mukherjee Date: Tue, 24 Jun 2025 14:28:30 +0530 Subject: [PATCH 06/17] doc: add example --- docs/rules/prefer-string-raw.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/rules/prefer-string-raw.md b/docs/rules/prefer-string-raw.md index 2f8b608bb1..bc2d229258 100644 --- a/docs/rules/prefer-string-raw.md +++ b/docs/rules/prefer-string-raw.md @@ -19,6 +19,10 @@ const file = "C:\\windows\\style\\path\\to\\file.js"; const regexp = new RegExp('foo\\.bar'); ``` +```js +const file = `C:\\windows\\temp\\myapp-${process.pid}.log`; +``` + ## Pass ```js @@ -28,3 +32,7 @@ const file = String.raw`C:\windows\style\path\to\file.js`; ```js const regexp = new RegExp(String.raw`foo\.bar`); ``` + +```js +const file = String.raw`C:\windows\temp\myapp-${process.pid}.log`; +``` From 3de20320b5200e097580337bb208d5c49f3c9467 Mon Sep 17 00:00:00 2001 From: Som Shekhar Mukherjee Date: Tue, 24 Jun 2025 14:31:41 +0530 Subject: [PATCH 07/17] fix: merge changes --- rules/prefer-string-raw.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/prefer-string-raw.js b/rules/prefer-string-raw.js index 3d79146f68..718ddee8f5 100644 --- a/rules/prefer-string-raw.js +++ b/rules/prefer-string-raw.js @@ -10,7 +10,7 @@ const BACKSLASH = '\\'; function unescapeBackslash(value, quote = '') { return value - .replaceAll(new RegExp(String.raw`\\([\\${quote}])`, 'g'), '$1'); + .replaceAll(new RegExp(String.raw`\\(?[\\${quote}])`, 'g'), '$'); } /** @param {import('eslint').Rule.RuleContext} context */ From ee6c6f458f571ac260d49dd1937202c2915055dd Mon Sep 17 00:00:00 2001 From: Som Shekhar Mukherjee Date: Tue, 24 Jun 2025 15:12:13 +0530 Subject: [PATCH 08/17] test: fix case to cover `fixSpaceAroundKeyword` --- test/prefer-string-raw.js | 2 +- test/snapshots/prefer-string-raw.js.md | 8 ++++---- test/snapshots/prefer-string-raw.js.snap | Bin 773 -> 772 bytes 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/prefer-string-raw.js b/test/prefer-string-raw.js index de1ba93c1d..39eef577d7 100644 --- a/test/prefer-string-raw.js +++ b/test/prefer-string-raw.js @@ -75,7 +75,7 @@ test.snapshot({ ], invalid: [ 'a = `a\\\\b`', - 'function a() {return `a\\\\b${foo}cd`}', + 'function a() {return`a\\\\b${foo}cd`}', 'a = {[`a\\\\b${foo}cd${foo.bar}e\\\\f`]: b}', 'a = `a${foo}${foo.bar}b\\\\c`', 'a = `a\\\\b${"c\\\\d"}e`', diff --git a/test/snapshots/prefer-string-raw.js.md b/test/snapshots/prefer-string-raw.js.md index 4b1013c261..021dcc5ef9 100644 --- a/test/snapshots/prefer-string-raw.js.md +++ b/test/snapshots/prefer-string-raw.js.md @@ -151,12 +151,12 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ ` -## invalid(2): function a() {return `a\\b${foo}cd`} +## invalid(2): function a() {return`a\\b${foo}cd`} > Input `␊ - 1 | function a() {return \`a\\\\b${foo}cd\`}␊ + 1 | function a() {return\`a\\\\b${foo}cd\`}␊ ` > Output @@ -168,8 +168,8 @@ Generated by [AVA](https://avajs.dev). > Error 1/1 `␊ - > 1 | function a() {return \`a\\\\b${foo}cd\`}␊ - | ^^^^^^^^^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ + > 1 | function a() {return\`a\\\\b${foo}cd\`}␊ + | ^^^^^^^^^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ ` ## invalid(3): a = {[`a\\b${foo}cd${foo.bar}e\\f`]: b} diff --git a/test/snapshots/prefer-string-raw.js.snap b/test/snapshots/prefer-string-raw.js.snap index 3309dfc9a30374697e87b8ca8c28307b352a2145..c8f4f10011eac8941a35bc32618b07e3ca8251f4 100644 GIT binary patch literal 772 zcmV+f1N;0zRzV z(CfEl)gOxp00000000BMmdkGvQ543h#+b0vxX`G{$&_R|K0qj?DTo`>g)0{(#^|-q zofk|U=i)pl2_-IkExLE%&i z9=iUJdakuo*V;g#3o0eVP~Fg{gRsw{@awr{(TKi*tEKCu>Djtgy0LY$3{Zztp`vVi z)UZAKg+GWRUZbI!yF$&YC~*B=HJ~RtC5Fz6otJU6thP|~>+-W?M*x7+=?5Im4(Ng` zgnbr!78o|fp>09LfDV~!fgPH3;QBq#iC*Q6-NPVY0n}@Cj%9Rz!KJ~ujNi?>zHfsu zN#jwG**n5jQK5{|6I808(gY__1H<}+L_P?xaQ`K2lQ!DdOL}D-Q1Y)_`oyzN-ceCN?3Cp0NaV z5zHFeWLC<_p3Wf>+|OB!WXev?zVsVCpR*ELm{xmDuTk<-$a2-ZNR+w9r9*=dQxY-q zn2EBNRe%PwBWW5H=*|KYu3_lMqXp*aGEmD+gAxR)+T} zs;*{6dSxv9Hi@M#^)TQO5;{lFxStsL>0QpxW|!wit??I<(B(=14*&oe CAafT0 literal 773 zcmV+g1N!_yRzVnV-Q%1K! zri?*_y&sDR00000000BMmQ9b+P!xt4jWJ=TaiLL@lSt3r@bvhq}~OD#d*(rpO*KWKaSxs^J92+Zbxq9 z*<;rqQ_r<_n_3qr^g*SB7^)lkbQBI*6n;IoEE>_*aJ6#1GCw=lDmQj+RsouDDpXW$ zj~cdTKlev*#A`HEb62Q&83nFCs0Z{!r^L{CvGXF1R@D}&epP*%?g#*II{$#<7 zLO5ixXMtfu9NHE{4Ct7-7TBRlN3K5ro#=Jm*gXmY7C^Jn$@%( zvn(DJnY|-i6&1=T?V(Zym8Lj}8W`3mB=TN>h5N6Vl|fBiQrp+M;KaTexy%Q;tHCs| zqd4#vRwVqI$o~Tlp+G`LvWG+-7l@=^KsnU!NbiJ0EixtI#b^7lYfIQ|tqUge!w4M4 zpbyIYi;zjXqx7<*>*!LzA?4M}FdrfXO`;%NGSQl}J9YEJKMaBOdth{5HgLO|FtkP&sOw^_hV>8pzC9|T7{68v+ z`IMH79x!)xJoTg~ydcgAh{^ zGx7ziKtlE`M?quamc@X_TauXCpa!=XFE)h1QcL4rQC?+A6GP4EB-_{PTbgc~U z6I5N#jrH18_-ztPU+H1M6D0T{o%Euee6m|W+Twm<KigfPTaDIV54r2f1P=fJ D**A1) From defb3165d64c7e6815470285eca7dd0fafbdc699 Mon Sep 17 00:00:00 2001 From: Som Shekhar Mukherjee Date: Tue, 24 Jun 2025 15:16:32 +0530 Subject: [PATCH 09/17] style: improve variable names --- rules/prefer-string-raw.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rules/prefer-string-raw.js b/rules/prefer-string-raw.js index 718ddee8f5..69bf5ee0d6 100644 --- a/rules/prefer-string-raw.js +++ b/rules/prefer-string-raw.js @@ -67,7 +67,7 @@ const create = context => { return; } - let suggestedValue = ''; + let unescaped = ''; let hasBackslash = false; for (let index = 0; index < node.quasis.length; index++) { @@ -78,8 +78,8 @@ const create = context => { return; } - const unescaped = unescapeBackslash(raw); - if (unescaped !== cooked) { + const unescapedQuasi = unescapeBackslash(raw); + if (unescapedQuasi !== cooked) { return; } @@ -88,10 +88,10 @@ const create = context => { } if (index > 0) { - suggestedValue += '${' + context.sourceCode.getText(node.expressions[index - 1]) + '}'; + unescaped += '${' + context.sourceCode.getText(node.expressions[index - 1]) + '}'; } - suggestedValue += unescaped; + unescaped += unescapedQuasi; } if (!hasBackslash) { @@ -102,7 +102,7 @@ const create = context => { node, messageId: MESSAGE_ID, * fix(fixer) { - yield fixer.replaceText(node, `String.raw\`${suggestedValue}\``); + yield fixer.replaceText(node, `String.raw\`${unescaped}\``); yield * fixSpaceAroundKeyword(fixer, node, context.sourceCode); }, }; From 4bf7953066f5467b43c1754774e3ed7b4a70bbb6 Mon Sep 17 00:00:00 2001 From: Som Shekhar Mukherjee Date: Tue, 24 Jun 2025 21:37:29 +0530 Subject: [PATCH 10/17] test: add case for multiline template literals --- test/prefer-string-raw.js | 9 ++++ test/snapshots/prefer-string-raw.js.md | 54 +++++++++++++++++++++++ test/snapshots/prefer-string-raw.js.snap | Bin 772 -> 906 bytes 3 files changed, 63 insertions(+) diff --git a/test/prefer-string-raw.js b/test/prefer-string-raw.js index 39eef577d7..d40514bbeb 100644 --- a/test/prefer-string-raw.js +++ b/test/prefer-string-raw.js @@ -79,6 +79,15 @@ test.snapshot({ 'a = {[`a\\\\b${foo}cd${foo.bar}e\\\\f`]: b}', 'a = `a${foo}${foo.bar}b\\\\c`', 'a = `a\\\\b${"c\\\\d"}e`', + outdent` + a = \`\\\\a + b\` + `, + outdent` + a = \`\\\\a\${foo} + b\${bar}c + d\\\\\\\\e\` + `, ], }); diff --git a/test/snapshots/prefer-string-raw.js.md b/test/snapshots/prefer-string-raw.js.md index 021dcc5ef9..59428a9232 100644 --- a/test/snapshots/prefer-string-raw.js.md +++ b/test/snapshots/prefer-string-raw.js.md @@ -241,3 +241,57 @@ Generated by [AVA](https://avajs.dev). > 1 | a = \`a\\\\b${"c\\\\d"}e\`␊ | ^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ ` + +## invalid(6): a = `\\a b` + +> Input + + `␊ + 1 | a = \`\\\\a␊ + 2 | b\`␊ + ` + +> Output + + `␊ + 1 | a = String.raw\`\\a␊ + 2 | b\`␊ + ` + +> Error 1/1 + + `␊ + > 1 | a = \`\\\\a␊ + | ^^^^␊ + > 2 | b\`␊ + | ^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ + ` + +## invalid(7): a = `\\a${foo} b${bar}c d\\\\e` + +> Input + + `␊ + 1 | a = \`\\\\a${foo}␊ + 2 | b${bar}c␊ + 3 | d\\\\\\\\e\`␊ + ` + +> Output + + `␊ + 1 | a = String.raw\`\\a${foo}␊ + 2 | b${bar}c␊ + 3 | d\\\\e\`␊ + ` + +> Error 1/1 + + `␊ + > 1 | a = \`\\\\a${foo}␊ + | ^^^^^^^^^^␊ + > 2 | b${bar}c␊ + | ^^^^^^^^␊ + > 3 | d\\\\\\\\e\`␊ + | ^^^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ + ` diff --git a/test/snapshots/prefer-string-raw.js.snap b/test/snapshots/prefer-string-raw.js.snap index c8f4f10011eac8941a35bc32618b07e3ca8251f4..1a2e4442b429e0a9f87f6401d063d34c9344e903 100644 GIT binary patch literal 906 zcmV;519kjCRzVOG*a1u7*(SDnD6!(iVFgOA6iNkjSq&Q~s0;zw9U=sWIS!>30YCA!AM_R}bwt4*YSUfmWW0su_n7ucOG z&^TKN_PuCef^I<+SSExX&_mBL!3qpIblg4Ah}K|%ogLrzd}uXW49nD zU-v+tX7Q*f*=yWZkx)kI9+E0ZDsbXGFsygT(qN-N3Ib%o(puf z4rAX6Bi~I@k??Dw{BJP`1r{=rePnXCz(o85%AtNnen$*yQBpi!yp|8Umcs7Vdti8O z5Q6P_umwu|M#!kUq4cw)8)#F&Am!<0m^YC_UE;uPa^^MaZfJce;t$B{Jwu$EO_uNO zd@U31)fDbJUl)lgsf-HR!WFSH5>;9oZzI)>+}fzhi{n0$zZ8pO98@fiB>~!CfRtHW zML7$gRP!%@l(Pal30f+kOk>sJu-aJ~Yohp{jg>_#v#~b$#wshNvm9z;+w-P|sYzPJ zeU$!ML6sM*WF#LUv-Q)rjEpU2{Db#B_q9^cJH#v(^^h`dbBAx-$<%<-n!Pu4>Q5~~ zY%OO9+Cng!Xp&iICp(=TWVl%{8p)jPf_do|I-hS9G%zjBH9JPhS1H3)_oSjEInFcG z2{9xkMm}5>Sjf(zhfMC|)SRrHAumE-klhFsA$d;Oa$O>95c@y-PbdZJ z=|v-if~-B~KiI{RDnJ`1An(%o4|d!>vL5&t17u?H*pm4C(N(uotK|D7Uy8Mq+9pRW zxvZeq#_?4(z$AnaOG5G?+DS(snb@Q(m{}E|1C!KJ#`rNZ?d36E;GT1Z{-7rDvm(Gh gG0ond^dRPu*m0!4^4r2cF5|%d0tgHX$>tFN0OA(46951J literal 772 zcmV+f1N;0zRzV z(CfEl)gOxp00000000BMmdkGvQ543h#+b0vxX`G{$&_R|K0qj?DTo`>g)0{(#^|-q zofk|U=i)pl2_-IkExLE%&i z9=iUJdakuo*V;g#3o0eVP~Fg{gRsw{@awr{(TKi*tEKCu>Djtgy0LY$3{Zztp`vVi z)UZAKg+GWRUZbI!yF$&YC~*B=HJ~RtC5Fz6otJU6thP|~>+-W?M*x7+=?5Im4(Ng` zgnbr!78o|fp>09LfDV~!fgPH3;QBq#iC*Q6-NPVY0n}@Cj%9Rz!KJ~ujNi?>zHfsu zN#jwG**n5jQK5{|6I808(gY__1H<}+L_P?xaQ`K2lQ!DdOL}D-Q1Y)_`oyzN-ceCN?3Cp0NaV z5zHFeWLC<_p3Wf>+|OB!WXev?zVsVCpR*ELm{xmDuTk<-$a2-ZNR+w9r9*=dQxY-q zn2EBNRe%PwBWW5H=*|KYu3_lMqXp*aGEmD+gAxR)+T} zs;*{6dSxv9Hi@M#^)TQO5;{lFxStsL>0QpxW|!wit??I<(B(=14*&oe CAafT0 From 9c4d6c7145c4de6c14fd95a87925241062e71588 Mon Sep 17 00:00:00 2001 From: Som Shekhar Mukherjee Date: Tue, 24 Jun 2025 21:37:59 +0530 Subject: [PATCH 11/17] style: update comment text --- test/prefer-string-raw.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/prefer-string-raw.js b/test/prefer-string-raw.js index d40514bbeb..b492f239d2 100644 --- a/test/prefer-string-raw.js +++ b/test/prefer-string-raw.js @@ -67,7 +67,7 @@ test.snapshot({ // Tagged template expression 'a = String.raw`a\\\\b`', - // Multiline + // Slash before newline (spread into multiple lines) outdent` a = \`\\\\a \\ b\` From 30ed7093dc62c010dca1dc7284f047c20db3a38b Mon Sep 17 00:00:00 2001 From: Som Shekhar Mukherjee Date: Thu, 26 Jun 2025 18:48:53 +0530 Subject: [PATCH 12/17] fix: preserve comments and whitespaces inside expressions --- rules/prefer-string-raw.js | 15 ++---- test/prefer-string-raw.js | 3 ++ test/snapshots/prefer-string-raw.js.md | 63 +++++++++++++++++++++++ test/snapshots/prefer-string-raw.js.snap | Bin 906 -> 1042 bytes 4 files changed, 70 insertions(+), 11 deletions(-) diff --git a/rules/prefer-string-raw.js b/rules/prefer-string-raw.js index 69bf5ee0d6..b4c0514ee6 100644 --- a/rules/prefer-string-raw.js +++ b/rules/prefer-string-raw.js @@ -1,5 +1,5 @@ import {isStringLiteral, isDirective} from './ast/index.js'; -import {fixSpaceAroundKeyword} from './fix/index.js'; +import {fixSpaceAroundKeyword, replaceTemplateElement} from './fix/index.js'; const MESSAGE_ID = 'prefer-string-raw'; const messages = { @@ -67,11 +67,9 @@ const create = context => { return; } - let unescaped = ''; let hasBackslash = false; - for (let index = 0; index < node.quasis.length; index++) { - const quasi = node.quasis[index]; + for (const quasi of node.quasis) { const {raw, cooked} = quasi.value; if (cooked.at(-1) === BACKSLASH) { @@ -86,12 +84,6 @@ const create = context => { if (cooked.includes(BACKSLASH)) { hasBackslash = true; } - - if (index > 0) { - unescaped += '${' + context.sourceCode.getText(node.expressions[index - 1]) + '}'; - } - - unescaped += unescapedQuasi; } if (!hasBackslash) { @@ -102,8 +94,9 @@ const create = context => { node, messageId: MESSAGE_ID, * fix(fixer) { - yield fixer.replaceText(node, `String.raw\`${unescaped}\``); yield * fixSpaceAroundKeyword(fixer, node, context.sourceCode); + yield * node.quasis.map(quasi => replaceTemplateElement(fixer, quasi, quasi.value.cooked)); + yield fixer.insertTextBefore(node, 'String.raw'); }, }; }); diff --git a/test/prefer-string-raw.js b/test/prefer-string-raw.js index b492f239d2..6119ec257e 100644 --- a/test/prefer-string-raw.js +++ b/test/prefer-string-raw.js @@ -88,6 +88,9 @@ test.snapshot({ b\${bar}c d\\\\\\\\e\` `, + 'a = `a\\\\b${ foo /* bar */}c\\\\d`', + 'a = `a\\\\b${ foo + bar }`', + 'a = `${ foo .bar }a\\\\b`', ], }); diff --git a/test/snapshots/prefer-string-raw.js.md b/test/snapshots/prefer-string-raw.js.md index 59428a9232..a77e25b02c 100644 --- a/test/snapshots/prefer-string-raw.js.md +++ b/test/snapshots/prefer-string-raw.js.md @@ -295,3 +295,66 @@ Generated by [AVA](https://avajs.dev). > 3 | d\\\\\\\\e\`␊ | ^^^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ ` + +## invalid(8): a = `a\\b${ foo /* bar */}c\\d` + +> Input + + `␊ + 1 | a = \`a\\\\b${ foo /* bar */}c\\\\d\`␊ + ` + +> Output + + `␊ + 1 | a = String.raw\`a\\b${ foo /* bar */}c\\d\`␊ + ` + +> Error 1/1 + + `␊ + > 1 | a = \`a\\\\b${ foo /* bar */}c\\\\d\`␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ + ` + +## invalid(9): a = `a\\b${ foo + bar }` + +> Input + + `␊ + 1 | a = \`a\\\\b${ foo + bar }\`␊ + ` + +> Output + + `␊ + 1 | a = String.raw\`a\\b${ foo + bar }\`␊ + ` + +> Error 1/1 + + `␊ + > 1 | a = \`a\\\\b${ foo + bar }\`␊ + | ^^^^^^^^^^^^^^^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ + ` + +## invalid(10): a = `${ foo .bar }a\\b` + +> Input + + `␊ + 1 | a = \`${ foo .bar }a\\\\b\`␊ + ` + +> Output + + `␊ + 1 | a = String.raw\`${ foo .bar }a\\b\`␊ + ` + +> Error 1/1 + + `␊ + > 1 | a = \`${ foo .bar }a\\\\b\`␊ + | ^^^^^^^^^^^^^^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ + ` diff --git a/test/snapshots/prefer-string-raw.js.snap b/test/snapshots/prefer-string-raw.js.snap index 1a2e4442b429e0a9f87f6401d063d34c9344e903..f2a57deab9854a3f9076bf51d1582bbc2880c3b1 100644 GIT binary patch literal 1042 zcmV+t1nv7lRzV6 z)2_zdy1QuuX|&+4g9kkcdb210Oi6Y&nK%1p(_+>=BqZT|=gsG3zVFTNmf7~L*WveL zCvu~^p=3L>z%*7%= z4Gx8fqSH1^r|mrS_Bs)dDWjOHLd@eRaJ`*MU>xWMGj$%^den)=(Kd>HQrwhR1OPZ3 zyukMG0bP)VaM$m&Z7?0^gpLi755~Ul+TetivFCa_pfkO~7k0OTzz?8St#K^f>j{@S z=hAx~ulk}1WQc^`=_pS)$HWHI9(qVAb5g^b=IXE~{dj#(Y<;YU0e6w$E7|46bkt<0fV9r@>7aaePL|Jfhv#~= z&g*+1PcJ%S22kx;eX$#a6angR2y~ZLU+jc^q&09ELnPve*y8oY(iJxosrdUTABu&9 z+NwY;KCEEQ_6A4Q0-G_$9D?LMw3CKJGL}hLaIqpl0}c~Oitz&^+Eg)~psqMVe^8V7 zS&-n5MAN4yDa3dsUXJ8faa;JuTncsvyV&q*RhWZDH7-O<;G0xS%Z>l5VZJGv_Va!CrqK0;c7 zg7ruu8*(mmYqt};JekTc7sZWh5$)%ONH-l$!CgTDb2MB@N@4Hskkp>YmdGU&?KH^# M1*X#W4<8l)0NDKamH+?% literal 906 zcmV;519kjCRzVOG*a1u7*(SDnD6!(iVFgOA6iNkjSq&Q~s0;zw9U=sWIS!>30YCA!AM_R}bwt4*YSUfmWW0su_n7ucOG z&^TKN_PuCef^I<+SSExX&_mBL!3qpIblg4Ah}K|%ogLrzd}uXW49nD zU-v+tX7Q*f*=yWZkx)kI9+E0ZDsbXGFsygT(qN-N3Ib%o(puf z4rAX6Bi~I@k??Dw{BJP`1r{=rePnXCz(o85%AtNnen$*yQBpi!yp|8Umcs7Vdti8O z5Q6P_umwu|M#!kUq4cw)8)#F&Am!<0m^YC_UE;uPa^^MaZfJce;t$B{Jwu$EO_uNO zd@U31)fDbJUl)lgsf-HR!WFSH5>;9oZzI)>+}fzhi{n0$zZ8pO98@fiB>~!CfRtHW zML7$gRP!%@l(Pal30f+kOk>sJu-aJ~Yohp{jg>_#v#~b$#wshNvm9z;+w-P|sYzPJ zeU$!ML6sM*WF#LUv-Q)rjEpU2{Db#B_q9^cJH#v(^^h`dbBAx-$<%<-n!Pu4>Q5~~ zY%OO9+Cng!Xp&iICp(=TWVl%{8p)jPf_do|I-hS9G%zjBH9JPhS1H3)_oSjEInFcG z2{9xkMm}5>Sjf(zhfMC|)SRrHAumE-klhFsA$d;Oa$O>95c@y-PbdZJ z=|v-if~-B~KiI{RDnJ`1An(%o4|d!>vL5&t17u?H*pm4C(N(uotK|D7Uy8Mq+9pRW zxvZeq#_?4(z$AnaOG5G?+DS(snb@Q(m{}E|1C!KJ#`rNZ?d36E;GT1Z{-7rDvm(Gh gG0ond^dRPu*m0!4^4r2cF5|%d0tgHX$>tFN0OA(46951J From 629191f3f0a93d7d1d25ec361aa04be119ee582d Mon Sep 17 00:00:00 2001 From: Som Shekhar Mukherjee Date: Fri, 27 Jun 2025 11:44:46 +0530 Subject: [PATCH 13/17] test: add case for `\{` --- test/prefer-string-raw.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/prefer-string-raw.js b/test/prefer-string-raw.js index 6119ec257e..79b912c4a1 100644 --- a/test/prefer-string-raw.js +++ b/test/prefer-string-raw.js @@ -58,6 +58,7 @@ test.snapshot({ 'a = `a\\t${foo.bar}b\\\\c`', // \t 'a = `${foo}\\\\a${bar}\\``', // \` 'a = `a\\${`', // \$ + 'a = `a$\\{`', // \{ 'a = `${a}\\\'${b}\\\\`', // \' 'a = `\\"a\\\\b`', // \" From 6d81306540c492e7e6194b0e70ffd2182f742e3b Mon Sep 17 00:00:00 2001 From: Som Shekhar Mukherjee Date: Fri, 27 Jun 2025 12:51:52 +0530 Subject: [PATCH 14/17] fix: replace only necessary quasis --- rules/prefer-string-raw.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rules/prefer-string-raw.js b/rules/prefer-string-raw.js index b4c0514ee6..ef3e939d5d 100644 --- a/rules/prefer-string-raw.js +++ b/rules/prefer-string-raw.js @@ -95,8 +95,13 @@ const create = context => { messageId: MESSAGE_ID, * fix(fixer) { yield * fixSpaceAroundKeyword(fixer, node, context.sourceCode); - yield * node.quasis.map(quasi => replaceTemplateElement(fixer, quasi, quasi.value.cooked)); yield fixer.insertTextBefore(node, 'String.raw'); + for (const quasis of node.quasis) { + const {cooked} = quasis.value; + if (cooked.includes(BACKSLASH)) { + yield replaceTemplateElement(fixer, quasis, cooked); + } + } }, }; }); From 5ef2291bc66da80285541728f10e74e4295b615c Mon Sep 17 00:00:00 2001 From: fisker Date: Sat, 28 Jun 2025 00:15:40 +0800 Subject: [PATCH 15/17] Simplify logic --- rules/prefer-string-raw.js | 43 ++++++++++++-------------------------- 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/rules/prefer-string-raw.js b/rules/prefer-string-raw.js index ef3e939d5d..6d7f98059d 100644 --- a/rules/prefer-string-raw.js +++ b/rules/prefer-string-raw.js @@ -8,9 +8,8 @@ const messages = { const BACKSLASH = '\\'; -function unescapeBackslash(value, quote = '') { - return value - .replaceAll(new RegExp(String.raw`\\(?[\\${quote}])`, 'g'), '$'); +function unescapeBackslash(text, quote = '') { + return text.replaceAll(new RegExp(String.raw`\\(?[\\${quote}])`, 'g'), '$'); } /** @param {import('eslint').Rule.RuleContext} context */ @@ -63,30 +62,11 @@ const create = context => { }); context.on('TemplateLiteral', node => { - if (node.parent.type === 'TaggedTemplateExpression') { - return; - } - - let hasBackslash = false; - - for (const quasi of node.quasis) { - const {raw, cooked} = quasi.value; - - if (cooked.at(-1) === BACKSLASH) { - return; - } - - const unescapedQuasi = unescapeBackslash(raw); - if (unescapedQuasi !== cooked) { - return; - } - - if (cooked.includes(BACKSLASH)) { - hasBackslash = true; - } - } - - if (!hasBackslash) { + if ( + (node.parent.type === 'TaggedTemplateExpression' && node.parent.quasi === node) + || node.quasis.every(({value: {cooked, raw}}) => cooked === raw) + || node.quasis.some(({value: {cooked, raw}}) => cooked.at(-1) === BACKSLASH || unescapeBackslash(raw) !== cooked) + ) { return; } @@ -96,11 +76,14 @@ const create = context => { * fix(fixer) { yield * fixSpaceAroundKeyword(fixer, node, context.sourceCode); yield fixer.insertTextBefore(node, 'String.raw'); + for (const quasis of node.quasis) { - const {cooked} = quasis.value; - if (cooked.includes(BACKSLASH)) { - yield replaceTemplateElement(fixer, quasis, cooked); + const {cooked, raw} = quasis.value; + if (cooked === raw) { + continue; } + + yield replaceTemplateElement(fixer, quasis, cooked); } }, }; From 0318edf39636f86ca6cf329136440183260cd256 Mon Sep 17 00:00:00 2001 From: fisker Date: Sat, 28 Jun 2025 00:19:18 +0800 Subject: [PATCH 16/17] Move `fixSpaceAroundKeyword` to upper in `Literal` fix too --- rules/prefer-string-raw.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/prefer-string-raw.js b/rules/prefer-string-raw.js index 6d7f98059d..2dcd4e5456 100644 --- a/rules/prefer-string-raw.js +++ b/rules/prefer-string-raw.js @@ -55,8 +55,8 @@ const create = context => { node, messageId: MESSAGE_ID, * fix(fixer) { - yield fixer.replaceText(node, `String.raw\`${unescaped}\``); yield * fixSpaceAroundKeyword(fixer, node, sourceCode); + yield fixer.replaceText(node, `String.raw\`${unescaped}\``); }, }; }); From a8e612b84e6696f0e7ea70936983da049228c275 Mon Sep 17 00:00:00 2001 From: fisker Date: Sat, 28 Jun 2025 00:21:54 +0800 Subject: [PATCH 17/17] Add comment --- test/prefer-string-raw.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/prefer-string-raw.js b/test/prefer-string-raw.js index 79b912c4a1..064b8215b2 100644 --- a/test/prefer-string-raw.js +++ b/test/prefer-string-raw.js @@ -4,6 +4,7 @@ import {getTester} from './utils/test.js'; const {test} = getTester(import.meta); +// String literal to `String.raw` test.snapshot({ valid: [ String.raw`a = '\''`, @@ -47,6 +48,7 @@ test.snapshot({ ], }); +// `TemplateLiteral` to `String.raw` test.snapshot({ valid: [ // No backslash