@@ -285,6 +285,16 @@ function restoreUnresolvedSentinels(value: string, items: SentinelOccurrence[]):
285285 return value . replace ( / \$ [ 0 - 9 A - Z a - z ] * \$ / g, ( sentinel ) => rawBySentinel . get ( sentinel ) ?? sentinel )
286286}
287287
288+ const UNSAFE_JAVASCRIPT_SOURCE_CHARACTERS = / [ < > \u2028 \u2029 ] / g
289+
290+ /** Serializes a string literal without embedding HTML terminators or JavaScript line separators. */
291+ function serializeJavaScriptStringLiteral ( value : string ) : string {
292+ return JSON . stringify ( value ) . replace (
293+ UNSAFE_JAVASCRIPT_SOURCE_CHARACTERS ,
294+ ( character ) => `\\u${ character . charCodeAt ( 0 ) . toString ( 16 ) . padStart ( 4 , '0' ) } `
295+ )
296+ }
297+
288298function buildStringExpression (
289299 value : string ,
290300 items : SentinelOccurrence [ ] ,
@@ -306,13 +316,13 @@ function buildStringExpression(
306316 const resolved = resolve ( item . occurrence )
307317 if ( ! resolved ) continue
308318 matched = true
309- parts . push ( JSON . stringify ( restore ( value . slice ( cursor , match . index ) ) ) )
319+ parts . push ( serializeJavaScriptStringLiteral ( restore ( value . slice ( cursor , match . index ) ) ) )
310320 parts . push ( resolved . bindingName )
311321 cursor = match . index + match [ 0 ] . length
312322 consumed . add ( item . occurrence )
313323 }
314324 if ( ! matched ) return undefined
315- parts . push ( JSON . stringify ( restore ( value . slice ( cursor ) ) ) )
325+ parts . push ( serializeJavaScriptStringLiteral ( restore ( value . slice ( cursor ) ) ) )
316326 return `(${ parts . join ( ' + ' ) } )`
317327}
318328
@@ -593,7 +603,7 @@ export async function compileJavaScriptPlaceholders(
593603 }
594604 const segmentExpression = ( value : string , items : SentinelOccurrence [ ] ) : string =>
595605 buildStringExpression ( value , items , context . resolve , consumed ) ??
596- JSON . stringify ( restoreUnresolvedSentinels ( value , items ) )
606+ serializeJavaScriptStringLiteral ( restoreUnresolvedSentinels ( value , items ) )
597607 const cookedSegments = tokens . map ( ( token , index ) =>
598608 hasInvalidTemplateEscape ( token )
599609 ? 'undefined'
@@ -684,7 +694,7 @@ export async function compileJavaScriptPlaceholders(
684694 edits . push ( {
685695 start : node . getStart ( sourceFile ) ,
686696 end : node . getEnd ( ) ,
687- text : `new ${ intrinsics . name } .RegExp(${ expression } , ${ JSON . stringify ( parsed . flags ) } )` ,
697+ text : `new ${ intrinsics . name } .RegExp(${ expression } , ${ serializeJavaScriptStringLiteral ( parsed . flags ) } )` ,
688698 } )
689699 return
690700 }
0 commit comments