diff --git a/.changeset/little-needles-itch.md b/.changeset/little-needles-itch.md new file mode 100644 index 000000000000..1f83e77e8763 --- /dev/null +++ b/.changeset/little-needles-itch.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +breaking: adjust template string concat strategy diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js index 4008dbdfa3cb..a63fbc9b7185 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js @@ -74,8 +74,23 @@ export function process_children(nodes, initial, is_element, { visit, state }) { // no text node was created because the expression was empty during SSR const is_text = sequence.length === 1; const id = flush_node(is_text, 'text'); + const parts = []; + + if (state.analysis.runes && value.type === 'TemplateLiteral') { + for (let i = 0; i < value.quasis.length; i++) { + const quasi = value.quasis[i]; + parts.push(b.literal(/** @type {string} */ (quasi.value.cooked))); + if (i !== value.quasis.length - 1) { + const expression = value.expressions[i]; + parts.push(expression); + } + } + } - const update = b.stmt(b.call('$.set_text', id, value)); + const update = + parts.length > 0 + ? b.stmt(b.call('$.set_text_parts', id, ...parts)) + : b.stmt(b.call('$.set_text', id, value)); if (has_call && !within_bound_contenteditable) { state.init.push(build_update(update)); diff --git a/packages/svelte/src/internal/client/dom/operations.js b/packages/svelte/src/internal/client/dom/operations.js index 76e5ca3cb2a4..a403e67f3e3a 100644 --- a/packages/svelte/src/internal/client/dom/operations.js +++ b/packages/svelte/src/internal/client/dom/operations.js @@ -48,6 +48,8 @@ export function init_operations() { // @ts-expect-error Text.prototype.__t = undefined; + // @ts-expect-error + Text.prototype.__p = undefined; if (DEV) { // @ts-expect-error diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 306fc69ca745..aef348568de6 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -123,7 +123,7 @@ export { update_pre_store, update_store } from './reactivity/store.js'; -export { set_text } from './render.js'; +export { set_text, set_text_parts } from './render.js'; export { get, safe_get, diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index de986ae04e08..187a648c9aa6 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -9,7 +9,7 @@ import { init_operations } from './dom/operations.js'; import { HYDRATION_END, HYDRATION_ERROR, HYDRATION_START } from '../../constants.js'; -import { push, pop, component_context, active_effect } from './runtime.js'; +import { push, pop, component_context, active_effect, untrack } from './runtime.js'; import { effect_root, branch } from './reactivity/effects.js'; import { hydrate_next, @@ -56,6 +56,31 @@ export function set_text(text, value) { } } +/** + * @param {Element} text + * @param {any[]} parts + * @returns {void} + */ +export function set_text_parts(text, ...parts) { + var value = ''; + // @ts-expect-error + var prev_parts = text.__p || []; + var changed = false; + for (var i = 0; i < parts.length; i++) { + var prev = prev_parts[i]; + var next = parts[i]; + if (prev !== next) { + changed = true; + } + value += next; + } + // @ts-expect-error + text.__p = parts; + if (changed) { + set_text(text, value); + } +} + /** * Mounts a component to the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component. * Transitions will play during the initial render unless the `intro` option is set to `false`. diff --git a/packages/svelte/tests/migrate/samples/self-closing-elements/output.svelte b/packages/svelte/tests/migrate/samples/self-closing-elements/output.svelte index 6aee688bb804..6dc21840d1bb 100644 --- a/packages/svelte/tests/migrate/samples/self-closing-elements/output.svelte +++ b/packages/svelte/tests/migrate/samples/self-closing-elements/output.svelte @@ -2,4 +2,4 @@
props: {p0} {p1} {p2} {p3} {p4} {p5} {p6} {p7}
-log: {log}
+log: {log.toString()}
diff --git a/packages/svelte/tests/runtime-runes/samples/props-default-value-lazy/sub.svelte b/packages/svelte/tests/runtime-runes/samples/props-default-value-lazy/sub.svelte index fe2ac37bd3f6..ce2a5e5e2dbf 100644 --- a/packages/svelte/tests/runtime-runes/samples/props-default-value-lazy/sub.svelte +++ b/packages/svelte/tests/runtime-runes/samples/props-default-value-lazy/sub.svelte @@ -26,4 +26,4 @@props: {p0} {p1} {p2} {p3} {p4} {p5} {p6} {p7}
-log: {log}
+log: {log.toString()}
diff --git a/packages/svelte/tests/runtime-runes/samples/proxy-to-primitive/_config.js b/packages/svelte/tests/runtime-runes/samples/proxy-to-primitive/_config.js new file mode 100644 index 000000000000..684e5870c19d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/proxy-to-primitive/_config.js @@ -0,0 +1,30 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: ` +