From e36341f00612dff72c4fabf97186a8010b27199b Mon Sep 17 00:00:00 2001 From: Som Shekhar Mukherjee Date: Wed, 25 Jun 2025 12:44:33 +0530 Subject: [PATCH 1/9] feat: remove unnecessary `String.raw` --- rules/prefer-string-raw.js | 36 +++++ test/prefer-string-raw.js | 33 +++- test/snapshots/prefer-string-raw.js.md | 188 +++++++++++++++++++++++ test/snapshots/prefer-string-raw.js.snap | Bin 514 -> 918 bytes 4 files changed, 256 insertions(+), 1 deletion(-) diff --git a/rules/prefer-string-raw.js b/rules/prefer-string-raw.js index 32ea732218..32e98a8370 100644 --- a/rules/prefer-string-raw.js +++ b/rules/prefer-string-raw.js @@ -2,8 +2,10 @@ import {isStringLiteral, isDirective} from './ast/index.js'; import {fixSpaceAroundKeyword} from './fix/index.js'; const MESSAGE_ID = 'prefer-string-raw'; +const MESSAGE_ID_UNNECESSARY_STRING_RAW = 'unnecessary-string-raw'; const messages = { [MESSAGE_ID]: '`String.raw` should be used to avoid escaping `\\`.', + [MESSAGE_ID_UNNECESSARY_STRING_RAW]: 'Using `String.raw` is unnecessary as the string does not contain any `\\`.', }; const BACKSLASH = '\\'; @@ -64,6 +66,40 @@ const create = context => { }, }; }); + + context.on('TaggedTemplateExpression', node => { + const {quasi, tag} = node; + + if (tag.type !== 'MemberExpression' + || tag.object.type !== 'Identifier' + || tag.property.type !== 'Identifier' + || tag.object.name !== 'String' + || tag.property.name !== 'raw' + ) { + return; + } + + const hasBackslash = quasi.quasis.some( + quasi => quasi.value.raw.includes(BACKSLASH), + ); + + if (hasBackslash) { + return; + } + + const rawQuasi = context.sourceCode.getText(quasi); + const suggestion = quasi.expressions.length > 0 || /\r?\n/.test(rawQuasi) + ? rawQuasi + : `'${rawQuasi.slice(1, -1).replaceAll('\'', String.raw`\'`)}'`; + + return { + node, + messageId: MESSAGE_ID_UNNECESSARY_STRING_RAW, + * fix(fixer) { + yield fixer.replaceText(node, suggestion); + }, + }; + }); }; /** @type {import('eslint').Rule.RuleModule} */ diff --git a/test/prefer-string-raw.js b/test/prefer-string-raw.js index b0c463b0f9..f1edb8f9b7 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``, @@ -47,6 +47,37 @@ test.snapshot({ ], }); +test.snapshot({ + valid: [ + 'a = String.raw`a\\b`', + 'a = String.raw`a\\b${foo}cd`', + 'a = String.raw`ab${foo}c\\nd`', + outdent` + a = String.raw\`a + b\\c + de\` + `, + ], + invalid: [ + 'a = String.raw`abc`', + 'a = String.raw`ab${foo}cd`', + 'a = String.raw`ab"c`', + 'a = String.raw`ab\'c`', + 'a = String.raw`ab\'"c`', + 'a = String.raw`ab\r\nc`', + outdent` + a = String.raw\`a + bc + de\` + `, + outdent` + a = String.raw\` + a\${foo}b + \${bar}cd\` + `, + ], +}); + test.typescript({ valid: [ outdent` diff --git a/test/snapshots/prefer-string-raw.js.md b/test/snapshots/prefer-string-raw.js.md index cd8cc4004b..50cabdabf6 100644 --- a/test/snapshots/prefer-string-raw.js.md +++ b/test/snapshots/prefer-string-raw.js.md @@ -129,3 +129,191 @@ Generated by [AVA](https://avajs.dev). > 1 | a = "a\\\\b\\""␊ | ^^^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ ` + +## invalid(1): a = String.raw`abc` + +> Input + + `␊ + 1 | a = String.raw\`abc\`␊ + ` + +> Output + + `␊ + 1 | a = 'abc'␊ + ` + +> Error 1/1 + + `␊ + > 1 | a = String.raw\`abc\`␊ + | ^^^^^^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + ` + +## invalid(2): a = String.raw`ab${foo}cd` + +> Input + + `␊ + 1 | a = String.raw\`ab${foo}cd\`␊ + ` + +> Output + + `␊ + 1 | a = \`ab${foo}cd\`␊ + ` + +> Error 1/1 + + `␊ + > 1 | a = String.raw\`ab${foo}cd\`␊ + | ^^^^^^^^^^^^^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + ` + +## invalid(3): a = String.raw`ab"c` + +> Input + + `␊ + 1 | a = String.raw\`ab"c\`␊ + ` + +> Output + + `␊ + 1 | a = 'ab"c'␊ + ` + +> Error 1/1 + + `␊ + > 1 | a = String.raw\`ab"c\`␊ + | ^^^^^^^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + ` + +## invalid(4): a = String.raw`ab'c` + +> Input + + `␊ + 1 | a = String.raw\`ab'c\`␊ + ` + +> Output + + `␊ + 1 | a = 'ab\\'c'␊ + ` + +> Error 1/1 + + `␊ + > 1 | a = String.raw\`ab'c\`␊ + | ^^^^^^^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + ` + +## invalid(5): a = String.raw`ab'"c` + +> Input + + `␊ + 1 | a = String.raw\`ab'"c\`␊ + ` + +> Output + + `␊ + 1 | a = 'ab\\'"c'␊ + ` + +> Error 1/1 + + `␊ + > 1 | a = String.raw\`ab'"c\`␊ + | ^^^^^^^^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + ` + +## invalid(6): a = String.raw`ab c` + +> Input + + `␊ + 1 | a = String.raw\`ab␊ + 2 | c\`␊ + ` + +> Output + + `␊ + 1 | a = \`ab␊ + 2 | c\`␊ + ` + +> Error 1/1 + + `␊ + > 1 | a = String.raw\`ab␊ + | ^^^^^^^^^^^^^␊ + > 2 | c\`␊ + | ^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + ` + +## invalid(7): a = String.raw`a bc de` + +> Input + + `␊ + 1 | a = String.raw\`a␊ + 2 | bc␊ + 3 | de\`␊ + ` + +> Output + + `␊ + 1 | a = \`a␊ + 2 | bc␊ + 3 | de\`␊ + ` + +> Error 1/1 + + `␊ + > 1 | a = String.raw\`a␊ + | ^^^^^^^^^^^^␊ + > 2 | bc␊ + | ^^^␊ + > 3 | de\`␊ + | ^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + ` + +## invalid(8): a = String.raw` a${foo}b ${bar}cd` + +> Input + + `␊ + 1 | a = String.raw\`␊ + 2 | a${foo}b␊ + 3 | ${bar}cd\`␊ + ` + +> Output + + `␊ + 1 | a = \`␊ + 2 | a${foo}b␊ + 3 | ${bar}cd\`␊ + ` + +> Error 1/1 + + `␊ + > 1 | a = String.raw\`␊ + | ^^^^^^^^^^^␊ + > 2 | a${foo}b␊ + | ^^^^^^^^␊ + > 3 | ${bar}cd\`␊ + | ^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + ` diff --git a/test/snapshots/prefer-string-raw.js.snap b/test/snapshots/prefer-string-raw.js.snap index 2afb8c0aa6f50423092454417f4e3b8adef456d4..f8e6fa9710ece4e36874f655a3919065cd77a275 100644 GIT binary patch literal 918 zcmV;H18Mw0RzVeJHIeRB>#DIKzR8e}Ke4;C~?}>tx57wNjM?AKIwR%&foJ%sM|ByyaW( z!;@1da-){B?Rwj+<=TstQXLqqfzAwr>u%_=t+44w;nAsWvxvQdi-pUD3(`M|b)*9IrF*p};Uf@zp#QP_PF1bzUO`xU{mJDs{L z377VpZq>JS;0J>|I$G=v=~hu7rSt%WYA7`0Bd=Ti zD{_4g*kTFVffL1n*If$XS3&Ea69_peD9Kfn;|M!Kcjp*0yS?@GB3XVJ@$Nx zT`kqY^1U#EhVR1~Xvr_b*sa#I)j{bpx+xGys(6Zd1!X7^8Kj%+$g z1XL41TBok2odqb3IS-I_R-j(d(E`n6PgZ%^+%gB9hC42~Wl~%-D0mX>LT>4#(J_iS zl#H?k2dPrA7hy+2c5_3BJmR_Qh9vXAfMm^OVy}tJKP5~ ziKT$^o-4Vg*tlO1?I$IzmbZ1s%2}m2^>c9QH0LaezJ@ss{hXfq|Mr~C9?N)n*a zNT!vbiVd1~ymLcWZPBlY^hiybzemTqNoy1;P^LqU)*`@C@;G<)(VC6KxZe@(i4s=@ zTU<}Gci%7`=eg8*g>VLLf$b2~^p~1!xbV8dts=m3^2&Bdb^41|S3NHYZon_pfS;-c zDEtL~w~p{HuJpM61hg4(y-gr}I0l-?LL4q+z+d zoWzqt8Xt=Y00000000BE)Im$bFcb&yiHNY9;z3d2QH5H;X?0H6(3=NO9t07s+H}oa zuqLxj=Rn1S==ac1Dx0ax>#~99a@bD)|4V+9_t6Lr{cj zKG?+b0-GhAw34Qf$?Inr5=qWrvM^ofo{6c#%*1RFz=bC*QKS(GXhe^AE0rqapvH?7_{0l*6>c1-vY!r7`m}hEp9lc()ct|0-T_~$g`_FcD1?AQn$QrW0MZsL z1e(-Ii}4%qJ-?&|v%@$RF}RzqV(BbrF5AlG@#1^xWfg+wK^_}jc0qe83YwI*P}D-v zoRi*xvF=dGwT6Y~_szzkW@laAt!4nA$Az&Y@?w{v%$`z|NC|yF20);dz zZ(`m<4UVBfdt|SxQ?B?mBk>FBx>v+QZ!_q7)j$7U+_%4V%$IPngSZM$b0x z#&wih8oC>8^KxvW_<4Rgc8BKIK=Kufz From 494361d835860049991a20ca8497890381797417 Mon Sep 17 00:00:00 2001 From: Som Shekhar Mukherjee Date: Wed, 25 Jun 2025 13:56:43 +0530 Subject: [PATCH 2/9] test: add cases covering the early exit check --- test/prefer-string-raw.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/prefer-string-raw.js b/test/prefer-string-raw.js index f1edb8f9b7..0246957b47 100644 --- a/test/prefer-string-raw.js +++ b/test/prefer-string-raw.js @@ -49,6 +49,13 @@ test.snapshot({ test.snapshot({ valid: [ + 'a = foo`ab`', + 'a = foo().bar`ab`', + 'a = foo.bar()`ab`', + 'a = String["raw"]`ab`', + 'a = foo.raw`ab`', + 'a = String.foo`ab`', + 'a = String.raw`a\\b`', 'a = String.raw`a\\b`', 'a = String.raw`a\\b${foo}cd`', 'a = String.raw`ab${foo}c\\nd`', From 8b2bc0bb1b9f57cfc39954071d85878f3fed9916 Mon Sep 17 00:00:00 2001 From: Som Shekhar Mukherjee Date: Wed, 25 Jun 2025 19:04:14 +0530 Subject: [PATCH 3/9] doc: add example --- docs/rules/prefer-string-raw.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/rules/prefer-string-raw.md b/docs/rules/prefer-string-raw.md index 2f8b608bb1..678caabb54 100644 --- a/docs/rules/prefer-string-raw.md +++ b/docs/rules/prefer-string-raw.md @@ -28,3 +28,17 @@ const file = String.raw`C:\windows\style\path\to\file.js`; ```js const regexp = new RegExp(String.raw`foo\.bar`); ``` + +[`String.raw`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/raw) should not be used if the string does not contain any `\`. + +## Fail + +```js +const noBackslash = String.raw`foobar` +``` + +## Pass + +```js +const noBackslash = 'foobar' +``` From ae37793dd7d5f274ba9c12f17a67f4b272c6d285 Mon Sep 17 00:00:00 2001 From: Som Shekhar Mukherjee Date: Thu, 26 Jun 2025 19:47:42 +0530 Subject: [PATCH 4/9] refactor: use `isMemberExpression` --- rules/prefer-string-raw.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/rules/prefer-string-raw.js b/rules/prefer-string-raw.js index 32e98a8370..b6365e61f5 100644 --- a/rules/prefer-string-raw.js +++ b/rules/prefer-string-raw.js @@ -1,4 +1,4 @@ -import {isStringLiteral, isDirective} from './ast/index.js'; +import {isStringLiteral, isDirective, isMemberExpression} from './ast/index.js'; import {fixSpaceAroundKeyword} from './fix/index.js'; const MESSAGE_ID = 'prefer-string-raw'; @@ -70,12 +70,7 @@ const create = context => { context.on('TaggedTemplateExpression', node => { const {quasi, tag} = node; - if (tag.type !== 'MemberExpression' - || tag.object.type !== 'Identifier' - || tag.property.type !== 'Identifier' - || tag.object.name !== 'String' - || tag.property.name !== 'raw' - ) { + if (!isMemberExpression(tag, {object: 'String', property: 'raw'})) { return; } From be1ece719cee06d8d7e32502dfbe5d9653f591ea Mon Sep 17 00:00:00 2001 From: Som Shekhar Mukherjee Date: Thu, 26 Jun 2025 20:40:16 +0530 Subject: [PATCH 5/9] fix: ASI issue --- rules/prefer-string-raw.js | 9 ++++- test/prefer-string-raw.js | 9 +++++ test/snapshots/prefer-string-raw.js.md | 48 +++++++++++++++++++++++ test/snapshots/prefer-string-raw.js.snap | Bin 918 -> 994 bytes 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/rules/prefer-string-raw.js b/rules/prefer-string-raw.js index b6365e61f5..5edf52a9d3 100644 --- a/rules/prefer-string-raw.js +++ b/rules/prefer-string-raw.js @@ -1,5 +1,6 @@ import {isStringLiteral, isDirective, isMemberExpression} from './ast/index.js'; import {fixSpaceAroundKeyword} from './fix/index.js'; +import needsSemicolon from './utils/needs-semicolon.js'; const MESSAGE_ID = 'prefer-string-raw'; const MESSAGE_ID_UNNECESSARY_STRING_RAW = 'unnecessary-string-raw'; @@ -69,6 +70,7 @@ const create = context => { context.on('TaggedTemplateExpression', node => { const {quasi, tag} = node; + const {sourceCode} = context; if (!isMemberExpression(tag, {object: 'String', property: 'raw'})) { return; @@ -82,7 +84,7 @@ const create = context => { return; } - const rawQuasi = context.sourceCode.getText(quasi); + const rawQuasi = sourceCode.getText(quasi); const suggestion = quasi.expressions.length > 0 || /\r?\n/.test(rawQuasi) ? rawQuasi : `'${rawQuasi.slice(1, -1).replaceAll('\'', String.raw`\'`)}'`; @@ -91,6 +93,11 @@ const create = context => { node, messageId: MESSAGE_ID_UNNECESSARY_STRING_RAW, * fix(fixer) { + const tokenBefore = sourceCode.getTokenBefore(node); + if (needsSemicolon(tokenBefore, sourceCode, suggestion)) { + yield fixer.insertTextAfter(tokenBefore, ';'); + } + yield fixer.replaceText(node, suggestion); }, }; diff --git a/test/prefer-string-raw.js b/test/prefer-string-raw.js index 0246957b47..ce7623b41f 100644 --- a/test/prefer-string-raw.js +++ b/test/prefer-string-raw.js @@ -82,6 +82,15 @@ test.snapshot({ a\${foo}b \${bar}cd\` `, + outdent` + a + String.raw\`abc\` + `, + // ASI + outdent` + a + String.raw\`a\${b}\` + `, ], }); diff --git a/test/snapshots/prefer-string-raw.js.md b/test/snapshots/prefer-string-raw.js.md index 50cabdabf6..6f50ca43f4 100644 --- a/test/snapshots/prefer-string-raw.js.md +++ b/test/snapshots/prefer-string-raw.js.md @@ -317,3 +317,51 @@ Generated by [AVA](https://avajs.dev). > 3 | ${bar}cd\`␊ | ^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ ` + +## invalid(9): a String.raw`abc` + +> Input + + `␊ + 1 | a␊ + 2 | String.raw\`abc\`␊ + ` + +> Output + + `␊ + 1 | a␊ + 2 | 'abc'␊ + ` + +> Error 1/1 + + `␊ + 1 | a␊ + > 2 | String.raw\`abc\`␊ + | ^^^^^^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + ` + +## invalid(10): a String.raw`a${b}` + +> Input + + `␊ + 1 | a␊ + 2 | String.raw\`a${b}\`␊ + ` + +> Output + + `␊ + 1 | a;␊ + 2 | \`a${b}\`␊ + ` + +> Error 1/1 + + `␊ + 1 | a␊ + > 2 | String.raw\`a${b}\`␊ + | ^^^^^^^^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + ` diff --git a/test/snapshots/prefer-string-raw.js.snap b/test/snapshots/prefer-string-raw.js.snap index f8e6fa9710ece4e36874f655a3919065cd77a275..770bcecc5ac70ec1415ec6e1b18931ed3cfad872 100644 GIT binary patch literal 994 zcmV<810DQ9RzV8kBJY|F2^fq!&rnk- zrvdV?FI1$h2GgyE^~~972D~PVYVHU%F9OeYHgg`^sWMHk^5WX_X3(oPQT3~IB|ahm zz<&4zHrf|dg;?E%-9^{wuoMm@9 za#`Unt+$;~-<3c==;u+9=~Tc3N zaboFg`h7Qg*G1w^MYs>eeNj`1ol!xza5KI$YAU%m-a}P)rF)}FZ;o|T{wBUThJ(hp z#}NS)IgrvGS5bxmifYaSqzntx9dxunGx3vEoVIRM`<(_mE_I_yam}FONwf-eLnV!l zQO%)X6d$mkDiV9?w?K3a z$B$w(xlF6(x(9|yc*+k}I)P%HM;TK>22qK1zl)`pj#Dz;UW!zCeApnK;C)k7sAW6Y z8#Sz@fRmmpbw#ppPY~^ADXo&UbjQkBpg7fYaH=%tEULbNIW_g1p6dVhoQ)sLczWde zypKqxrJ#}pnsmJDnkrkO-w^4MoHTijj&+e1DO8|L2i#iI04w3+-0)j#yb|O7K)7F} zxH8yuB}(tSU_8!qk@Fhi47vfPMNrcpYU1VG>zZz)0anAGOp8>fKWKH-lR|IL$Fbg}yno5$@gp82r7h;U~jTn;eJHIeRB>#DIKzR8e}Ke4;C~?}>tx57wNjM?AKIwR%&foJ%sM|ByyaW( z!;@1da-){B?Rwj+<=TstQXLqqfzAwr>u%_=t+44w;nAsWvxvQdi-pUD3(`M|b)*9IrF*p};Uf@zp#QP_PF1bzUO`xU{mJDs{L z377VpZq>JS;0J>|I$G=v=~hu7rSt%WYA7`0Bd=Ti zD{_4g*kTFVffL1n*If$XS3&Ea69_peD9Kfn;|M!Kcjp*0yS?@GB3XVJ@$Nx zT`kqY^1U#EhVR1~Xvr_b*sa#I)j{bpx+xGys(6Zd1!X7^8Kj%+$g z1XL41TBok2odqb3IS-I_R-j(d(E`n6PgZ%^+%gB9hC42~Wl~%-D0mX>LT>4#(J_iS zl#H?k2dPrA7hy+2c5_3BJmR_Qh9vXAfMm^OVy}tJKP5~ ziKT$^o-4Vg*tlO1?I$IzmbZ1s%2}m2^>c9QH0LaezJ@ss{hXfq|Mr~C9?N)n*a zNT!vbiVd1~ymLcWZPBlY^hiybzemTqNoy1;P^LqU)*`@C@;G<)(VC6KxZe@(i4s=@ zTU<}Gci%7`=eg8*g>VLLf$b2~^p~1!xbV8dts=m3^2&Bdb^41|S3NHYZon_pfS;-c zDEtL~w~p{HuJ Date: Fri, 27 Jun 2025 15:27:21 +0530 Subject: [PATCH 6/9] chore: add `optional: false` --- 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 5edf52a9d3..39f0cbcb0b 100644 --- a/rules/prefer-string-raw.js +++ b/rules/prefer-string-raw.js @@ -72,7 +72,7 @@ const create = context => { const {quasi, tag} = node; const {sourceCode} = context; - if (!isMemberExpression(tag, {object: 'String', property: 'raw'})) { + if (!isMemberExpression(tag, {object: 'String', property: 'raw', optional: false})) { return; } From ee17e724108ae44aa36f4a76a03d188350aacd14 Mon Sep 17 00:00:00 2001 From: Som Shekhar Mukherjee Date: Fri, 27 Jun 2025 15:47:21 +0530 Subject: [PATCH 7/9] fix: semicolon position --- rules/prefer-string-raw.js | 2 +- test/snapshots/prefer-string-raw.js.md | 4 ++-- test/snapshots/prefer-string-raw.js.snap | Bin 994 -> 994 bytes 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rules/prefer-string-raw.js b/rules/prefer-string-raw.js index 39f0cbcb0b..88281a55f7 100644 --- a/rules/prefer-string-raw.js +++ b/rules/prefer-string-raw.js @@ -95,7 +95,7 @@ const create = context => { * fix(fixer) { const tokenBefore = sourceCode.getTokenBefore(node); if (needsSemicolon(tokenBefore, sourceCode, suggestion)) { - yield fixer.insertTextAfter(tokenBefore, ';'); + yield fixer.insertTextBefore(node, ';'); } yield fixer.replaceText(node, suggestion); diff --git a/test/snapshots/prefer-string-raw.js.md b/test/snapshots/prefer-string-raw.js.md index 6f50ca43f4..fc84c220f3 100644 --- a/test/snapshots/prefer-string-raw.js.md +++ b/test/snapshots/prefer-string-raw.js.md @@ -354,8 +354,8 @@ Generated by [AVA](https://avajs.dev). > Output `␊ - 1 | a;␊ - 2 | \`a${b}\`␊ + 1 | a␊ + 2 | ;\`a${b}\`␊ ` > Error 1/1 diff --git a/test/snapshots/prefer-string-raw.js.snap b/test/snapshots/prefer-string-raw.js.snap index 770bcecc5ac70ec1415ec6e1b18931ed3cfad872..a4ab5acd03c15c86d01880a1bcb9e507b7da9014 100644 GIT binary patch delta 85 zcmV-b0IL7u2jT}ZK~_N^Q*L2!b7*gLAa*kf0{}dv<|g0xD(*+9e#3=KMov rY`{;+=i4~Y7$C7SmjfB#5oKI?#(67u?SzoBBenep6M8wFd=&ryYuqMs delta 85 zcmV-b0IL7u2jT}ZK~_N^Q*L2!b7*gLAa*kf0{|=mkOFxN%kg#hXI!wMBz|gw*DD$d rF#E;#qcwWzG>fq^mjf9n^9)>h#(67u?SzoBBenepURRZkd=&rykTfEp From cc0a128ecce34fbd9dda948d4dd172f2926747fc Mon Sep 17 00:00:00 2001 From: Som Shekhar Mukherjee Date: Fri, 27 Jun 2025 16:12:25 +0530 Subject: [PATCH 8/9] fix: preserve comments after `String.raw` --- rules/prefer-string-raw.js | 3 +- test/prefer-string-raw.js | 15 ++++++ test/snapshots/prefer-string-raw.js.md | 58 +++++++++++++++++++++++ test/snapshots/prefer-string-raw.js.snap | Bin 994 -> 1120 bytes 4 files changed, 75 insertions(+), 1 deletion(-) diff --git a/rules/prefer-string-raw.js b/rules/prefer-string-raw.js index 88281a55f7..ccb6995a04 100644 --- a/rules/prefer-string-raw.js +++ b/rules/prefer-string-raw.js @@ -98,7 +98,8 @@ const create = context => { yield fixer.insertTextBefore(node, ';'); } - yield fixer.replaceText(node, suggestion); + yield fixer.replaceText(node.quasi, suggestion); + yield fixer.remove(node.tag); }, }; }); diff --git a/test/prefer-string-raw.js b/test/prefer-string-raw.js index ce7623b41f..83450ad838 100644 --- a/test/prefer-string-raw.js +++ b/test/prefer-string-raw.js @@ -64,6 +64,11 @@ test.snapshot({ b\\c de\` `, + outdent` + a = String.raw + // Comment + \`ab\\c\` + `, ], invalid: [ 'a = String.raw`abc`', @@ -91,6 +96,16 @@ test.snapshot({ a String.raw\`a\${b}\` `, + outdent` + a = String.raw + // Comment + \`ab + c\` + `, + outdent` + a = String.raw /* comment */ \`ab + c\` + `, ], }); diff --git a/test/snapshots/prefer-string-raw.js.md b/test/snapshots/prefer-string-raw.js.md index fc84c220f3..a8af4b4299 100644 --- a/test/snapshots/prefer-string-raw.js.md +++ b/test/snapshots/prefer-string-raw.js.md @@ -365,3 +365,61 @@ Generated by [AVA](https://avajs.dev). > 2 | String.raw\`a${b}\`␊ | ^^^^^^^^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ ` + +## invalid(11): a = String.raw // Comment `ab c` + +> Input + + `␊ + 1 | a = String.raw␊ + 2 | // Comment␊ + 3 | \`ab␊ + 4 | c\`␊ + ` + +> Output + + `␊ + 1 | a = ␊ + 2 | // Comment␊ + 3 | \`ab␊ + 4 | c\`␊ + ` + +> Error 1/1 + + `␊ + > 1 | a = String.raw␊ + | ^^^^^^^^^^␊ + > 2 | // Comment␊ + | ^^^^^^^^^^␊ + > 3 | \`ab␊ + | ^^^^^^^^^^␊ + > 4 | c\`␊ + | ^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + ` + +## invalid(12): a = String.raw /* comment */ `ab c` + +> Input + + `␊ + 1 | a = String.raw /* comment */ \`ab␊ + 2 | c\`␊ + ` + +> Output + + `␊ + 1 | a = /* comment */ \`ab␊ + 2 | c\`␊ + ` + +> Error 1/1 + + `␊ + > 1 | a = String.raw /* comment */ \`ab␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^␊ + > 2 | c\`␊ + | ^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + ` diff --git a/test/snapshots/prefer-string-raw.js.snap b/test/snapshots/prefer-string-raw.js.snap index a4ab5acd03c15c86d01880a1bcb9e507b7da9014..3eaa222a0c4f0b2a55e2ad9c465551706adc8071 100644 GIT binary patch literal 1120 zcmV-m1fTmsRzV00000000BUm|suRP#DHhV~o4QKM6(&FNIi!!kEmdBP=m$qE}uRjnRrb z{WEDCEw&wECLzXni7{O3g~ktHV&bh=d;>p_>2|uFw|!69g4A9BVSAqU^!J>-@9Dla z^_FYA^mo5mfgQB04cpmZE!&(a7i+*^8C0fex@!9lTlZJpz<>YEG+DqN!?BSQBjI)A z_{ivyQw4xBycHA$tHpGyW!-bu+X2@Uk>p{6x z=2$kzBQ94sm(J5()n_%(x6*i2wAgdPts)^sX#q(UBu#JFj$hdmv^t`uuvxQ-t{({*7Pl<=3PZC0wvQkwKUx+&n0WO@qoHRMnv9E6+fdTmy! zjU^KCHu8GK5ldH-=DQMK{}S$41ox`g7d4gWj0(DiGs(`Vsbp`QMXC$Z-l)>eaTUp* zB%5PcG}#^x2~d>-Qo3~&Wf(w_<{*HSVFC4v9xkAXts9Lrr@_XhZZs%d6G%LY zcA;*lB%>Xq*%lZj3#L&;#2)%x5+XO-hqmKbhUNRrdjrgeVATS@`wlQ&%Lm5|!q0fX zYzLV0CL83%oe>${*hNY?KiD8n z@YGTjYTFL>K@GJO;H-0{u1GfSCxrH$6s?lAb#uj8p>V1Pz^T$WCz1Lz#;K_XlgJz9)T~lRSbQd9gFGrfaM|0hzRSGIlOb2YO1%NBzac=n5 znp9%AUl81nQn)g(g?i-PdqX~)ry|bR2+p7zU|Ixf`k^K%=e$npMgd?k{L8dR>hyzF zk9w8~4&WyW;JYjU34e~iSBLX2)Z>c%f94X)d=$&|bS%Jn(^-eX1^qv(UI}QgnIpi;_Tw`_~l$u?0;c%qnqNshwV88 zcOrtz!47cXo$GEMet7 z^H$EaXF`(Ah3#CQZTis=N+r1EuB};40J7nG7g4H_{e~lp-FS=W4aJIdDA$qqg5W)b zBTnWIN@kDlJ7nA8*`bm+G~RjQQIB4#_XE)tZ<;eQJ_EQKqa=>54iv5eD)W-UQt;T82|u}u_gBa literal 994 zcmV<810DQ9RzV8kBJY|F2^fq!&rnk- zrvdV?FI1$h2GgyE^~~972D~PVYVHU%F9OeYHgg`^sWMHk^5WX_X3(oPQT3~IB|ahm zz<&4zHrf|dg;?E%-9^{wuoMm@9 za#`Unt+$;~-<3c==;u+9=~Tc3N zaboFg`h7Qg*G1w^MYs>eeNj`1ol!xza5KI$YAU%m-a}P)rF)}FZ;o|T{wBUThJ(hp z#}NS)IgrvGS5bxmifYaSqzntx9dxunGx3vEoVIRM`<(_mE_I_yam}FONwf-eLnV!l zQO%)X6d$mkDiV9?w?K3a z$B$w(xlF6(x(9|yc*+k}I)P%HM;TK>22qK1zl)`pj#Dz;UW!zCeApnK;C)k7sAW6Y z8#Sz@fRmmpbw#ppPY~^ADXo&UbjQkBpg7fYaH=%tEULbNIW_g1p6dVhoQ)sLczWde zypKqxrJ#}pnsmJDnkrkO-w^4MoHTijj&+e1DO8|L2i#iI04w3+-0)j#yb|O7K)7F} zxH8yuB}(tSU_8!qk@Fhi47vfPMNrcpYU1VG>zZz)0anAGOp8>fKWKH-lR|IL$Fbg}yno5$@gp82r7h;U~jTn; Date: Fri, 27 Jun 2025 16:18:33 +0530 Subject: [PATCH 9/9] refactor: update error range --- rules/prefer-string-raw.js | 2 +- test/snapshots/prefer-string-raw.js.md | 51 ++++++++++------------- test/snapshots/prefer-string-raw.js.snap | Bin 1120 -> 1067 bytes 3 files changed, 22 insertions(+), 31 deletions(-) diff --git a/rules/prefer-string-raw.js b/rules/prefer-string-raw.js index ccb6995a04..e70c128765 100644 --- a/rules/prefer-string-raw.js +++ b/rules/prefer-string-raw.js @@ -90,7 +90,7 @@ const create = context => { : `'${rawQuasi.slice(1, -1).replaceAll('\'', String.raw`\'`)}'`; return { - node, + node: tag, messageId: MESSAGE_ID_UNNECESSARY_STRING_RAW, * fix(fixer) { const tokenBefore = sourceCode.getTokenBefore(node); diff --git a/test/snapshots/prefer-string-raw.js.md b/test/snapshots/prefer-string-raw.js.md index a8af4b4299..003390a67c 100644 --- a/test/snapshots/prefer-string-raw.js.md +++ b/test/snapshots/prefer-string-raw.js.md @@ -148,7 +148,7 @@ Generated by [AVA](https://avajs.dev). `␊ > 1 | a = String.raw\`abc\`␊ - | ^^^^^^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + | ^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ ` ## invalid(2): a = String.raw`ab${foo}cd` @@ -169,7 +169,7 @@ Generated by [AVA](https://avajs.dev). `␊ > 1 | a = String.raw\`ab${foo}cd\`␊ - | ^^^^^^^^^^^^^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + | ^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ ` ## invalid(3): a = String.raw`ab"c` @@ -190,7 +190,7 @@ Generated by [AVA](https://avajs.dev). `␊ > 1 | a = String.raw\`ab"c\`␊ - | ^^^^^^^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + | ^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ ` ## invalid(4): a = String.raw`ab'c` @@ -211,7 +211,7 @@ Generated by [AVA](https://avajs.dev). `␊ > 1 | a = String.raw\`ab'c\`␊ - | ^^^^^^^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + | ^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ ` ## invalid(5): a = String.raw`ab'"c` @@ -232,7 +232,7 @@ Generated by [AVA](https://avajs.dev). `␊ > 1 | a = String.raw\`ab'"c\`␊ - | ^^^^^^^^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + | ^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ ` ## invalid(6): a = String.raw`ab c` @@ -255,9 +255,8 @@ Generated by [AVA](https://avajs.dev). `␊ > 1 | a = String.raw\`ab␊ - | ^^^^^^^^^^^^^␊ - > 2 | c\`␊ - | ^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + | ^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + 2 | c\`␊ ` ## invalid(7): a = String.raw`a bc de` @@ -282,11 +281,9 @@ Generated by [AVA](https://avajs.dev). `␊ > 1 | a = String.raw\`a␊ - | ^^^^^^^^^^^^␊ - > 2 | bc␊ - | ^^^␊ - > 3 | de\`␊ - | ^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + | ^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + 2 | bc␊ + 3 | de\`␊ ` ## invalid(8): a = String.raw` a${foo}b ${bar}cd` @@ -311,11 +308,9 @@ Generated by [AVA](https://avajs.dev). `␊ > 1 | a = String.raw\`␊ - | ^^^^^^^^^^^␊ - > 2 | a${foo}b␊ - | ^^^^^^^^␊ - > 3 | ${bar}cd\`␊ - | ^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + | ^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + 2 | a${foo}b␊ + 3 | ${bar}cd\`␊ ` ## invalid(9): a String.raw`abc` @@ -339,7 +334,7 @@ Generated by [AVA](https://avajs.dev). `␊ 1 | a␊ > 2 | String.raw\`abc\`␊ - | ^^^^^^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + | ^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ ` ## invalid(10): a String.raw`a${b}` @@ -363,7 +358,7 @@ Generated by [AVA](https://avajs.dev). `␊ 1 | a␊ > 2 | String.raw\`a${b}\`␊ - | ^^^^^^^^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + | ^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ ` ## invalid(11): a = String.raw // Comment `ab c` @@ -390,13 +385,10 @@ Generated by [AVA](https://avajs.dev). `␊ > 1 | a = String.raw␊ - | ^^^^^^^^^^␊ - > 2 | // Comment␊ - | ^^^^^^^^^^␊ - > 3 | \`ab␊ - | ^^^^^^^^^^␊ - > 4 | c\`␊ - | ^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + | ^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + 2 | // Comment␊ + 3 | \`ab␊ + 4 | c\`␊ ` ## invalid(12): a = String.raw /* comment */ `ab c` @@ -419,7 +411,6 @@ Generated by [AVA](https://avajs.dev). `␊ > 1 | a = String.raw /* comment */ \`ab␊ - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^␊ - > 2 | c\`␊ - | ^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + | ^^^^^^^^^^ Using \`String.raw\` is unnecessary as the string does not contain any \`\\\`.␊ + 2 | c\`␊ ` diff --git a/test/snapshots/prefer-string-raw.js.snap b/test/snapshots/prefer-string-raw.js.snap index 3eaa222a0c4f0b2a55e2ad9c465551706adc8071..0efee9208961aaa0d4628cbbf6783d80f3ed9904 100644 GIT binary patch literal 1067 zcmV+`1l0RMRzVL^vMUKF)c9aXd@w#ieDKdS-EP@E?YX6sAF~hJ9`^m7b3f*?#g zm;)%nwxGyaHKtoN>yfio54a|eG}i>plfbi`})@vr{7Sw&qgun&1?%F0;zQNXPXBjG5Ws(QBA9$YYL9tNeST+YD zF4LS#<7F%At1{?2Njxe__NuT{B%~-!A*q6-F;1cfhP8@JUJI~r{5G?bpr%IDX7XjI z)*U0TT?g1$9vYq%)IFz_3c;_4@*n09QY6y`bPkS833MK--QXG@iN67hTF z^_C-+W|QQ*)V_`rZhr*#me?0HmFSELTEgXcXVg@(H;yCKb!l%@>E^hC>3fCYK_n}>=8!E}@ zGt#^h7{vo7Q7tXOQ@;s8L}vR?cO1*Ge4lw+z>l95wZ5Al2@H9E#6foEW(y zZ=h8TNJ&bzWZL}JHC47LzarVTl;!`hD@znipjc*XoH>B$@SN6pmwrHr%X#z*x{(8z34fUu37URrYJpBO!OVXl=HFz@PX{@fZxa7x zg#UIDKd{zV)nTNeGmkzX+(+n2bR~FBksnX&z?CXV8TsB6OOnkE*XB2>%@0X!PNy`7 zB%bmP^yE~1%IF&#-($IGZm<6<^ zp%j=+gAV#Qx0uJu^x-Hc+VbcE(}@G7$pHNtWc3^_n`USMp&H3gck#dW{#PCA*8T&X6U8 lm=|?s$?!HTG|gg#i?oM|v`ZIBV%web&|hny1v*d|005ZL`c41< literal 1120 zcmV-m1fTmsRzV00000000BUm|suRP#DHhV~o4QKM6(&FNIi!!kEmdBP=m$qE}uRjnRrb z{WEDCEw&wECLzXni7{O3g~ktHV&bh=d;>p_>2|uFw|!69g4A9BVSAqU^!J>-@9Dla z^_FYA^mo5mfgQB04cpmZE!&(a7i+*^8C0fex@!9lTlZJpz<>YEG+DqN!?BSQBjI)A z_{ivyQw4xBycHA$tHpGyW!-bu+X2@Uk>p{6x z=2$kzBQ94sm(J5()n_%(x6*i2wAgdPts)^sX#q(UBu#JFj$hdmv^t`uuvxQ-t{({*7Pl<=3PZC0wvQkwKUx+&n0WO@qoHRMnv9E6+fdTmy! zjU^KCHu8GK5ldH-=DQMK{}S$41ox`g7d4gWj0(DiGs(`Vsbp`QMXC$Z-l)>eaTUp* zB%5PcG}#^x2~d>-Qo3~&Wf(w_<{*HSVFC4v9xkAXts9Lrr@_XhZZs%d6G%LY zcA;*lB%>Xq*%lZj3#L&;#2)%x5+XO-hqmKbhUNRrdjrgeVATS@`wlQ&%Lm5|!q0fX zYzLV0CL83%oe>${*hNY?KiD8n z@YGTjYTFL>K@GJO;H-0{u1GfSCxrH$6s?lAb#uj8p>V1Pz^T$WCz1Lz#;K_XlgJz9)T~lRSbQd9gFGrfaM|0hzRSGIlOb2YO1%NBzac=n5 znp9%AUl81nQn)g(g?i-PdqX~)ry|bR2+p7zU|Ixf`k^K%=e$npMgd?k{L8dR>hyzF zk9w8~4&WyW;JYjU34e~iSBLX2)Z>c%f94X)d=$&|bS%Jn(^-eX1^qv(UI}QgnIpi;_Tw`_~l$u?0;c%qnqNshwV88 zcOrtz!47cXo$GEMet7 z^H$EaXF`(Ah3#CQZTis=N+r1EuB};40J7nG7g4H_{e~lp-FS=W4aJIdDA$qqg5W)b zBTnWIN@kDlJ7nA8*`bm+G~RjQQIB4#_XE)tZ<;eQJ_EQKqa=>54iv5eD)W-UQt;T82|u}u_gBa