diff --git a/.changeset/flat-points-decide.md b/.changeset/flat-points-decide.md new file mode 100644 index 000000000000..1910819993f4 --- /dev/null +++ b/.changeset/flat-points-decide.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +feat: omit trailing `$.sibling` calls where possible diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js index 7f5d1aa19d15..9dd1a87b27da 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js @@ -147,7 +147,7 @@ export function Fragment(node, context) { // special case — we can use `$.text` instead of creating a unique template const id = b.id(context.state.scope.generate('text')); - process_children(trimmed, () => id, false, { + process_children(trimmed, () => id, false, false, { ...context, state }); @@ -157,12 +157,12 @@ export function Fragment(node, context) { } else { if (is_standalone) { // no need to create a template, we can just use the existing block's anchor - process_children(trimmed, () => b.id('$$anchor'), false, { ...context, state }); + process_children(trimmed, () => b.id('$$anchor'), false, false, { ...context, state }); } else { /** @type {(is_text: boolean) => Expression} */ const expression = (is_text) => b.call('$.first_child', id, is_text && b.true); - process_children(trimmed, expression, false, { ...context, state }); + process_children(trimmed, expression, false, false, { ...context, state }); let flags = TEMPLATE_FRAGMENT; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index 8c19bb4d6612..ca621c9d813a 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -340,7 +340,7 @@ export function RegularElement(node, context) { arg = b.member(arg, 'content'); } - process_children(trimmed, () => b.call('$.child', arg), true, { + process_children(trimmed, () => b.call('$.child', arg), true, needs_reset, { ...context, state: child_state }); 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 9251870c2784..dd19ad426a95 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 @@ -11,10 +11,11 @@ import { build_template_literal, build_update } from './utils.js'; * corresponding template node references these updates are applied to. * @param {SvelteNode[]} nodes * @param {(is_text: boolean) => Expression} initial - * @param {boolean} is_element + * @param {boolean} is_element TODO get rid of this — we should be able to determine controlled status during analysis + * @param {boolean} needs_reset * @param {ComponentContext} context */ -export function process_children(nodes, initial, is_element, { visit, state }) { +export function process_children(nodes, initial, is_element, needs_reset, { visit, state }) { const within_bound_contenteditable = state.metadata.bound_contenteditable; let prev = initial; let skipped = 0; @@ -116,7 +117,7 @@ export function process_children(nodes, initial, is_element, { visit, state }) { // if there are trailing static text nodes/elements, // traverse to the last (n - 1) one when hydrating - if (skipped > 1) { + if (skipped > 1 && !needs_reset) { skipped -= 1; state.init.push(b.stmt(b.call('$.next', skipped !== 1 && b.literal(skipped)))); }