From 4132d08287213ded0e7fc67a49736fab545819b8 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Wed, 29 May 2024 23:49:23 +0100 Subject: [PATCH 1/6] fix: improve each block runtime heuristics be removing anchor text node --- .changeset/stale-nails-listen.md | 5 ++ .../src/internal/client/dom/blocks/each.js | 69 ++++++++++++------- .../client/dom/blocks/svelte-element.js | 17 ++--- .../svelte/src/internal/client/types.d.ts | 2 - 4 files changed, 58 insertions(+), 35 deletions(-) create mode 100644 .changeset/stale-nails-listen.md diff --git a/.changeset/stale-nails-listen.md b/.changeset/stale-nails-listen.md new file mode 100644 index 000000000000..269405302a97 --- /dev/null +++ b/.changeset/stale-nails-listen.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: improve each block runtime heuristics be removing anchor text node diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index 8c59e3aebab3..740fade795fc 100644 --- a/packages/svelte/src/internal/client/dom/blocks/each.js +++ b/packages/svelte/src/internal/client/dom/blocks/each.js @@ -174,11 +174,10 @@ export function each(anchor, flags, get_collection, get_key, render_fn, fallback break; } - var child_open = /** @type {Comment} */ (child_anchor); child_anchor = hydrate_anchor(child_anchor); var value = array[i]; var key = get_key(value, i); - item = create_item(child_open, child_anchor, prev, null, value, key, i, render_fn, flags); + item = create_item(child_anchor, prev, null, value, key, i, render_fn, flags); state.items.set(key, item); child_anchor = /** @type {Comment} */ (child_anchor.nextSibling); @@ -285,22 +284,9 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) { item = items.get(key); if (item === undefined) { - var child_open = empty(); - var child_anchor = current ? current.o : anchor; - - child_anchor.before(child_open); - - prev = create_item( - child_open, - child_anchor, - prev, - prev.next, - value, - key, - i, - render_fn, - flags - ); + var child_anchor = current ? get_first_node(current.e) : anchor; + + prev = create_item(child_anchor, prev, prev.next, value, key, i, render_fn, flags); items.set(key, prev); @@ -412,7 +398,6 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) { for (var i = 0; i < to_destroy.length; i += 1) { var item = to_destroy[i]; items.delete(item.k); - item.o.remove(); link(item.prev, item.next); } }); @@ -449,7 +434,6 @@ function update_item(item, value, index, type) { /** * @template V - * @param {Comment | Text} open * @param {Node} anchor * @param {import('#client').EachItem | import('#client').EachState} prev * @param {import('#client').EachItem | null} next @@ -460,7 +444,7 @@ function update_item(item, value, index, type) { * @param {number} flags * @returns {import('#client').EachItem} */ -function create_item(open, anchor, prev, next, value, key, index, render_fn, flags) { +function create_item(anchor, prev, next, value, key, index, render_fn, flags) { var previous_each_item = current_each_item; try { @@ -478,7 +462,6 @@ function create_item(open, anchor, prev, next, value, key, index, render_fn, fla a: null, // @ts-expect-error e: null, - o: open, prev, next }; @@ -495,16 +478,52 @@ function create_item(open, anchor, prev, next, value, key, index, render_fn, fla } } +/** + * @param {import('#client').TemplateNode} dom + * @param {import("#client").Effect} effect + * @returns {import('#client').TemplateNode} + */ +function get_adjusted_first_node(dom, effect) { + if (dom.nodeType === 3 && /** @type {Text} */ (dom).data === '') { + var adjusted = effect.first; + var next; + while (adjusted !== null) { + next = adjusted.first; + if (adjusted.dom !== null) { + break; + } else if (next === null) { + return /** @type {import('#client').TemplateNode} */ (dom.previousSibling); + } + adjusted = next; + } + return get_first_node(/** @type {import("#client").Effect} */ (adjusted)); + } + return dom; +} + +/** + * + * @param {import('#client').Effect} effect + * @returns {import('#client').TemplateNode} + */ +function get_first_node(effect) { + var dom = effect.dom; + if (is_array(dom)) { + return get_adjusted_first_node(dom[0], effect); + } + return get_adjusted_first_node(/** @type {import('#client').TemplateNode} **/ (dom), effect); +} + /** * @param {import('#client').EachItem} item * @param {import('#client').EachItem | null} next * @param {Text | Element | Comment} anchor */ function move(item, next, anchor) { - var end = item.next ? item.next.o : anchor; - var dest = next ? next.o : anchor; + var end = item.next ? get_first_node(item.next.e) : anchor; + var dest = next ? get_first_node(next.e) : anchor; - var node = /** @type {import('#client').TemplateNode} */ (item.o); + var node = get_first_node(item.e); while (node !== end) { var next_node = /** @type {import('#client').TemplateNode} */ (node.nextSibling); diff --git a/packages/svelte/src/internal/client/dom/blocks/svelte-element.js b/packages/svelte/src/internal/client/dom/blocks/svelte-element.js index df607fe54a58..e4e79b361b61 100644 --- a/packages/svelte/src/internal/client/dom/blocks/svelte-element.js +++ b/packages/svelte/src/internal/client/dom/blocks/svelte-element.js @@ -46,7 +46,6 @@ function swap_block_dom(effect, from, to) { * @returns {void} */ export function element(anchor, get_tag, is_svg, render_fn, get_namespace, location) { - const parent_effect = /** @type {import('#client').Effect} */ (current_effect); const filename = DEV && location && current_component_context?.function.filename; /** @type {string | null} */ @@ -69,6 +68,7 @@ export function element(anchor, get_tag, is_svg, render_fn, get_namespace, locat let each_item_block = current_each_item; block(() => { + const element_effect = /** @type {import('#client').Effect} */ (current_effect); const next_tag = get_tag() || null; const ns = get_namespace ? get_namespace() @@ -120,6 +120,14 @@ export function element(anchor, get_tag, is_svg, render_fn, get_namespace, locat }; } + if (prev_element) { + swap_block_dom(element_effect, prev_element, element); + prev_element.remove(); + } + if (!hydrating) { + push_template_node(element, element_effect); + } + if (render_fn) { // If hydrating, use the existing ssr comment as the anchor so that the // inner open and close methods can pick up the existing nodes correctly @@ -135,13 +143,6 @@ export function element(anchor, get_tag, is_svg, render_fn, get_namespace, locat } anchor.before(element); - - if (prev_element) { - swap_block_dom(parent_effect, prev_element, element); - prev_element.remove(); - } else if (!hydrating) { - push_template_node(element, parent_effect); - } }); } diff --git a/packages/svelte/src/internal/client/types.d.ts b/packages/svelte/src/internal/client/types.d.ts index 986fbfe7cf1e..292c233fa691 100644 --- a/packages/svelte/src/internal/client/types.d.ts +++ b/packages/svelte/src/internal/client/types.d.ts @@ -85,8 +85,6 @@ export type EachItem = { i: number | Source; /** key */ k: unknown; - /** anchor for items inserted before this */ - o: Comment | Text; prev: EachItem | EachState; next: EachItem | null; }; From f1f850dfd2506cfc73c8cd097d001bb0e30e2bbf Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 30 May 2024 12:19:27 +0100 Subject: [PATCH 2/6] change message --- .changeset/stale-nails-listen.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/stale-nails-listen.md b/.changeset/stale-nails-listen.md index 269405302a97..520fb4eeed38 100644 --- a/.changeset/stale-nails-listen.md +++ b/.changeset/stale-nails-listen.md @@ -2,4 +2,4 @@ "svelte": patch --- -fix: improve each block runtime heuristics be removing anchor text node +chore: improve each block runtime heuristics be removing anchor text node From da04d7b17f60fcb6b264d16ab794456869445a19 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Fri, 31 May 2024 21:39:13 +0100 Subject: [PATCH 3/6] conflict --- packages/svelte/src/internal/client/dom/blocks/each.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index 5a1e1d72eac9..9da3635dc938 100644 --- a/packages/svelte/src/internal/client/dom/blocks/each.js +++ b/packages/svelte/src/internal/client/dom/blocks/each.js @@ -57,8 +57,9 @@ export function index(_, i) { * subsequent destruction. Used in each blocks * @param {import('#client').EachItem[]} items * @param {null | Node} controlled_anchor + * @param {Map} items_map */ -function pause_effects(items, controlled_anchor) { +function pause_effects(items, controlled_anchor, items_map) { /** @type {import('#client').TransitionManager[]} */ var transitions = []; var length = items.length; @@ -81,6 +82,7 @@ function pause_effects(items, controlled_anchor) { run_out_transitions(transitions, () => { for (var i = 0; i < length; i++) { var item = items[i]; + items_map.delete(item.k); link(item.prev, item.next); destroy_effect(item.e, !is_controlled); } @@ -398,7 +400,7 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) { } } - pause_effects(to_destroy, controlled_anchor); + pause_effects(to_destroy, controlled_anchor, items); } if (is_animated) { From b42e5b385aac6251e508f73070fe9f51c08fc659 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Fri, 31 May 2024 21:58:32 +0100 Subject: [PATCH 4/6] merge --- packages/svelte/src/internal/client/dom/blocks/each.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index fc1251a7c2f1..cc725f18dc32 100644 --- a/packages/svelte/src/internal/client/dom/blocks/each.js +++ b/packages/svelte/src/internal/client/dom/blocks/each.js @@ -86,7 +86,6 @@ function pause_effects(items, controlled_anchor, items_map) { var item = items[i]; if (!is_controlled) { items_map.delete(item.k); - item.o.remove(); link(item.prev, item.next); } destroy_effect(item.e, !is_controlled); From 24828691f7a2114ef41aebe75152c7941509349d Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Sat, 8 Jun 2024 20:20:28 +0100 Subject: [PATCH 5/6] fix bug --- .../src/internal/client/dom/blocks/each.js | 2 +- .../client/dom/blocks/svelte-element.js | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index cc725f18dc32..adfa3d631d84 100644 --- a/packages/svelte/src/internal/client/dom/blocks/each.js +++ b/packages/svelte/src/internal/client/dom/blocks/each.js @@ -489,7 +489,7 @@ function create_item(anchor, prev, next, value, key, index, render_fn, flags) { * @returns {import('#client').TemplateNode} */ function get_adjusted_first_node(dom, effect) { - if (dom.nodeType === 3 && /** @type {Text} */ (dom).data === '') { + if ((dom.nodeType === 3 && /** @type {Text} */ (dom).data === '') || dom.nodeType === 8) { var adjusted = effect.first; var next; while (adjusted !== null) { diff --git a/packages/svelte/src/internal/client/dom/blocks/svelte-element.js b/packages/svelte/src/internal/client/dom/blocks/svelte-element.js index 71a33b35a59a..1b3daa5f04c2 100644 --- a/packages/svelte/src/internal/client/dom/blocks/svelte-element.js +++ b/packages/svelte/src/internal/client/dom/blocks/svelte-element.js @@ -62,6 +62,18 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio /** @type {import('#client').Effect | null} */ let effect; + const parent_effect = /** @type {import('#client').Effect} */ (current_effect); + + // Remove the the hydrated effect dom entry for our dynamic element + if (hydrating && is_array(parent_effect.dom)) { + var remove_index = parent_effect.dom.indexOf( + /** @type {import('#client').TemplateNode} */ (element) + ); + if (remove_index !== -1) { + parent_effect.dom.splice(remove_index, 1); + } + } + /** * The keyed `{#each ...}` item block, if any, that this element is inside. * We track this so we can set it when changing the element, allowing any @@ -123,11 +135,10 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio }; } - if (prev_element) { + if (prev_element && !hydrating) { swap_block_dom(element_effect, prev_element, element); prev_element.remove(); - } - if (!hydrating) { + } else { push_template_node(element, element_effect); } From 15af7b51899482cba6a6da843cff276c544819e1 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 20 Jun 2024 15:40:23 -0400 Subject: [PATCH 6/6] Update .changeset/stale-nails-listen.md --- .changeset/stale-nails-listen.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/stale-nails-listen.md b/.changeset/stale-nails-listen.md index 520fb4eeed38..9f9b473294f9 100644 --- a/.changeset/stale-nails-listen.md +++ b/.changeset/stale-nails-listen.md @@ -2,4 +2,4 @@ "svelte": patch --- -chore: improve each block runtime heuristics be removing anchor text node +chore: remove anchor node from each block items