Skip to content

Commit edb98fd

Browse files
committed
breaking: adjust template string concat strategy
1 parent 2b0741f commit edb98fd

File tree

15 files changed

+109
-11
lines changed

15 files changed

+109
-11
lines changed

.changeset/little-needles-itch.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+
breaking: adjust template string concat strategy

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,23 @@ export function process_children(nodes, initial, is_element, { visit, state }) {
7474
// no text node was created because the expression was empty during SSR
7575
const is_text = sequence.length === 1;
7676
const id = flush_node(is_text, 'text');
77+
const parts = [];
78+
79+
if (state.analysis.runes && value.type === 'TemplateLiteral') {
80+
for (let i = 0; i < value.quasis.length; i++) {
81+
const quasi = value.quasis[i];
82+
parts.push(b.literal(/** @type {string} */ (quasi.value.cooked)));
83+
if (i !== value.quasis.length - 1) {
84+
const expression = value.expressions[i];
85+
parts.push(expression);
86+
}
87+
}
88+
}
7789

78-
const update = b.stmt(b.call('$.set_text', id, value));
90+
const update =
91+
parts.length > 0
92+
? b.stmt(b.call('$.set_text_parts', id, ...parts))
93+
: b.stmt(b.call('$.set_text', id, value));
7994

8095
if (has_call && !within_bound_contenteditable) {
8196
state.init.push(build_update(update));

packages/svelte/src/internal/client/dom/operations.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ export function init_operations() {
4848

4949
// @ts-expect-error
5050
Text.prototype.__t = undefined;
51+
// @ts-expect-error
52+
Text.prototype.__p = undefined;
5153

5254
if (DEV) {
5355
// @ts-expect-error

packages/svelte/src/internal/client/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ export {
123123
update_pre_store,
124124
update_store
125125
} from './reactivity/store.js';
126-
export { set_text } from './render.js';
126+
export { set_text, set_text_parts } from './render.js';
127127
export {
128128
get,
129129
safe_get,

packages/svelte/src/internal/client/render.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
init_operations
1010
} from './dom/operations.js';
1111
import { HYDRATION_END, HYDRATION_ERROR, HYDRATION_START } from '../../constants.js';
12-
import { push, pop, component_context, active_effect } from './runtime.js';
12+
import { push, pop, component_context, active_effect, untrack } from './runtime.js';
1313
import { effect_root, branch } from './reactivity/effects.js';
1414
import {
1515
hydrate_next,
@@ -56,6 +56,31 @@ export function set_text(text, value) {
5656
}
5757
}
5858

59+
/**
60+
* @param {Element} text
61+
* @param {any[]} parts
62+
* @returns {void}
63+
*/
64+
export function set_text_parts(text, ...parts) {
65+
var value = '';
66+
// @ts-expect-error
67+
var prev_parts = text.__p || [];
68+
var changed = false;
69+
for (var i = 0; i < parts.length; i++) {
70+
var prev = prev_parts[i];
71+
var next = parts[i];
72+
if (prev !== next) {
73+
changed = true;
74+
}
75+
value += next;
76+
}
77+
// @ts-expect-error
78+
text.__p = parts;
79+
if (changed) {
80+
set_text(text, value);
81+
}
82+
}
83+
5984
/**
6085
* Mounts a component to the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component.
6186
* Transitions will play during the initial render unless the `intro` option is set to `false`.

packages/svelte/tests/migrate/samples/self-closing-elements/output.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
<div title="preserve"></div>
33
<input type="text" />
44
<hr />
5-
<f:table></f:table>
5+
<f:table></f:table>

packages/svelte/tests/runtime-runes/samples/inspect-derived-2/main.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@
1818
</script>
1919

2020
<button onclick={() => (state.data.list.push(1))}>update</button>
21-
{state.data.list}
21+
{state.data.list.toString()}

packages/svelte/tests/runtime-runes/samples/props-default-value-lazy-accessors/main.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,4 @@
2626
</script>
2727

2828
<p>props: {p0} {p1} {p2} {p3} {p4} {p5} {p6} {p7}</p>
29-
<p>log: {log}</p>
29+
<p>log: {log.toString()}</p>

packages/svelte/tests/runtime-runes/samples/props-default-value-lazy/sub.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,4 @@
2626
</script>
2727

2828
<p>props: {p0} {p1} {p2} {p3} {p4} {p5} {p6} {p7}</p>
29-
<p>log: {log}</p>
29+
<p>log: {log.toString()}</p>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
html: `
6+
<button>add</button><ul><li>1,2,3</li><li>1,2,3</li><li>text
7+
1,2,3</li><li>1,2,3</li><li>1,2,3</li><li title="1,2,3"></li><li title="1,2,3"></li><li><input readonly="" type="text"></li><li><input readonly="" type="text"></li></ul>
8+
`,
9+
10+
ssrHtml: `
11+
<button>add</button><ul><li>1,2,3</li><li>1,2,3</li><li>text
12+
1,2,3</li><li>1,2,3</li><li>1,2,3</li><li title="1,2,3"></li><li title="1,2,3"></li><li><input readonly="" type="text" value="1,2,3"></li><li><input readonly="" type="text" value="1,2,3"></li></ul>
13+
`,
14+
15+
test({ assert, target }) {
16+
const [btn1] = target.querySelectorAll('button');
17+
18+
flushSync(() => {
19+
btn1?.click();
20+
});
21+
22+
assert.htmlEqual(
23+
target.innerHTML,
24+
`
25+
<button>add</button><ul><li>1,2,3</li><li>1,2,3,4</li><li>text
26+
1,2,3</li><li>1,2,3</li><li>1,2,3,4</li><li title="1,2,3"></li><li title="1,2,3,4"></li><li><input readonly="" type="text"></li><li><input readonly="" type="text"></li></ul>
27+
`
28+
);
29+
}
30+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script>
2+
let array = $state([1,2,3]);
3+
4+
function addToArray() {
5+
array.push(array.length+1);
6+
}
7+
</script>
8+
9+
<button onclick={addToArray}>add</button>
10+
11+
<ul>
12+
<li>{@html array}</li>
13+
<li>{@html array?.toString()}</li>
14+
<li>text {array}</li>
15+
<li>{array}</li>
16+
<li>{array?.toString()}</li>
17+
<li title={array}></li>
18+
<li title={array?.toString()}></li>
19+
<li><input type="text" value={array} readonly/> </li>
20+
<li><input type="text" value={array?.toString()} readonly/> </li>
21+
</ul>

packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,6 @@ export default function Bind_component_snippet($$anchor) {
2929

3030
var text_1 = $.sibling(node);
3131

32-
$.template_effect(() => $.set_text(text_1, ` value: ${$.get(value) ?? ""}`));
32+
$.template_effect(() => $.set_text_parts(text_1, " value: ", $.get(value) ?? "", ""));
3333
$.append($$anchor, fragment);
3434
}

packages/svelte/tests/snapshot/samples/each-string-template/_expected/client/index.svelte.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ export default function Each_string_template($$anchor) {
1515
});
1616

1717
$.append($$anchor, fragment);
18-
}
18+
}

packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/client/index.svelte.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export default function Function_prop_no_getter($$anchor) {
1919

2020
var text = $.text();
2121

22-
$.template_effect(() => $.set_text(text, `clicks: ${$.get(count) ?? ""}`));
22+
$.template_effect(() => $.set_text_parts(text, "clicks: ", $.get(count) ?? "", ""));
2323
$.append($$anchor, text);
2424
},
2525
$$slots: { default: true }

packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/client/index.svelte.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export default function Text_nodes_deriveds($$anchor) {
2020
const stringified_text_1 = $.derived(() => text2() ?? "");
2121
var text = $.child(p);
2222

23-
$.template_effect(() => $.set_text(text, `${$.get(stringified_text)}${$.get(stringified_text_1)}`));
23+
$.template_effect(() => $.set_text_parts(text, "", $.get(stringified_text), "", $.get(stringified_text_1), ""));
2424
$.reset(p);
2525
$.append($$anchor, p);
2626
}

0 commit comments

Comments
 (0)