Skip to content

Commit b31946e

Browse files
authored
fix: Backtick now displays with templates (#9973)
* Add a test for backtick in template * Put sanitize_template_string and use it everywhere * Prettier * Add changeset
1 parent 5dffe71 commit b31946e

File tree

7 files changed

+32
-11
lines changed

7 files changed

+32
-11
lines changed

.changeset/long-buckets-lay.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
Fix interopability between backticks and templates

packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import {
3333
} from '../../../../../constants.js';
3434
import { regex_is_valid_identifier } from '../../../patterns.js';
3535
import { javascript_visitors_runes } from './javascript-runes.js';
36+
import { sanitize_template_string } from '../../../../utils/sanitize_template_string.js';
3637

3738
/**
3839
* @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} element
@@ -1637,7 +1638,7 @@ function serialize_template_literal(values, visit, state) {
16371638
const node = values[i];
16381639
if (node.type === 'Text') {
16391640
const last = /** @type {import('estree').TemplateElement} */ (quasis.at(-1));
1640-
last.value.raw += node.data;
1641+
last.value.raw += sanitize_template_string(node.data);
16411642
} else {
16421643
if (node.type === 'ExpressionTag' && node.metadata.contains_call_expression) {
16431644
contains_call_expression = true;

packages/svelte/src/compiler/phases/3-transform/server/transform-server.js

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { binding_properties } from '../../bindings.js';
2626
import { regex_starts_with_newline, regex_whitespaces_strict } from '../../patterns.js';
2727
import { remove_types } from '../typescript.js';
2828
import { DOMBooleanAttributes } from '../../../../constants.js';
29+
import { sanitize_template_string } from '../../../utils/sanitize_template_string.js';
2930

3031
/**
3132
* @param {string} value
@@ -117,14 +118,6 @@ function serialize_template(template, out = b.id('out')) {
117118
return statements;
118119
}
119120

120-
/**
121-
* @param {string} str
122-
* @returns {string}
123-
*/
124-
function sanitize_template_string(str) {
125-
return str.replace(/(`|\${|\\)/g, '\\$1');
126-
}
127-
128121
/**
129122
* Processes an array of template nodes, joining sibling text/expression nodes and
130123
* recursing into child nodes.
@@ -194,7 +187,10 @@ function process_children(nodes, parent, { visit, state }) {
194187
const node = sequence[i];
195188
if (node.type === 'Text' || node.type === 'Comment') {
196189
let last = /** @type {import('estree').TemplateElement} */ (quasis.at(-1));
197-
last.value.raw += node.type === 'Comment' ? `<!--${node.data}-->` : escape_html(node.data);
190+
last.value.raw +=
191+
node.type === 'Comment'
192+
? `<!--${node.data}-->`
193+
: sanitize_template_string(escape_html(node.data));
198194
} else if (node.type === 'Anchor') {
199195
expressions.push(node.id);
200196
quasis.push(b.quasi('', i + 1 === sequence.length));

packages/svelte/src/compiler/utils/builders.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { regex_is_valid_identifier } from '../phases/patterns.js';
2+
import { sanitize_template_string } from './sanitize_template_string.js';
23

34
/**
45
* @param {Array<import('estree').Expression | import('estree').SpreadElement | null>} elements
@@ -314,7 +315,7 @@ export function prop_def(key, value, computed = false, is_static = false) {
314315
* @returns {import('estree').TemplateElement}
315316
*/
316317
export function quasi(cooked, tail = false) {
317-
const raw = cooked.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$/g, '\\$');
318+
const raw = sanitize_template_string(cooked);
318319
return { type: 'TemplateElement', value: { raw, cooked }, tail };
319320
}
320321

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/**
2+
* @param {string} str
3+
* @returns {string}
4+
*/
5+
export function sanitize_template_string(str) {
6+
return str.replace(/(`|\${|\\)/g, '\\$1');
7+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
html: '<div>/ $clicks: 0 `tim$es` \\</div><div>$dollars `backticks` pyramid /\\</div>'
5+
});
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<div>
2+
/ $clicks: {0} `tim${"e"}s` \
3+
</div>
4+
<div>
5+
$dollars `backticks` pyramid /\
6+
</div>

0 commit comments

Comments
 (0)