diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index c1e0dc82f398..663cee91dbd6 100644 --- a/packages/svelte/src/internal/client/dom/blocks/each.js +++ b/packages/svelte/src/internal/client/dom/blocks/each.js @@ -25,6 +25,7 @@ import { import { source, mutable_source, set } from '../../reactivity/sources.js'; import { is_array, is_frozen } from '../../utils.js'; import { INERT, STATE_SYMBOL } from '../../constants.js'; +import { push_template_node } from '../template.js'; /** * The row of a keyed each block that is currently updating. We track this @@ -168,10 +169,11 @@ 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_anchor, prev, null, value, key, i, render_fn, flags); + item = create_item(child_open, child_anchor, prev, null, value, key, i, render_fn, flags); state.items.set(key, item); child_anchor = /** @type {Comment} */ (child_anchor.nextSibling); @@ -278,8 +280,14 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) { item = items.get(key); if (item === undefined) { + var child_open = /** @type {Text} */ (push_template_node(empty())); + var child_anchor = current ? current.o : anchor; + + child_anchor.before(child_open); + prev = create_item( - current ? get_first_child(current) : anchor, + child_open, + child_anchor, prev, prev.next, value, @@ -312,7 +320,7 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) { if (matched.length < stashed.length) { // more efficient to move later items to the front var start = stashed[0]; - var local_anchor = get_first_child(start); + var local_anchor = start.o; var j; prev = start.prev; @@ -341,7 +349,7 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) { } else { // more efficient to move earlier items to the back seen.delete(item); - move(item, current ? get_first_child(current) : anchor); + move(item, current ? current.o : anchor); link(item.prev, item.next); link(item, prev.next); @@ -402,20 +410,6 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) { } } -/** - * @param {import('#client').EachItem} item - * @returns {Text | Element | Comment} - */ -function get_first_child(item) { - var current = item.e.dom; - - if (is_array(current)) { - return /** @type {Text | Element | Comment} */ (current[0]); - } - - return /** @type {Text | Element | Comment} */ (current); -} - /** * @param {import('#client').EachItem} item * @param {any} value @@ -437,6 +431,7 @@ 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 @@ -447,7 +442,7 @@ function update_item(item, value, index, type) { * @param {number} flags * @returns {import('#client').EachItem} */ -function create_item(anchor, prev, next, value, key, index, render_fn, flags) { +function create_item(open, anchor, prev, next, value, key, index, render_fn, flags) { var previous_each_item = current_each_item; try { @@ -465,6 +460,7 @@ function create_item(anchor, prev, next, value, key, index, render_fn, flags) { a: null, // @ts-expect-error e: null, + o: open, prev, next }; @@ -486,6 +482,8 @@ function create_item(anchor, prev, next, value, key, index, render_fn, flags) { * @param {Text | Element | Comment} anchor */ function move(item, anchor) { + anchor.before(item.o); + var dom = item.e.dom; if (dom !== null) { diff --git a/packages/svelte/src/internal/client/dom/blocks/html.js b/packages/svelte/src/internal/client/dom/blocks/html.js index 40583e09f242..ea8e011443dd 100644 --- a/packages/svelte/src/internal/client/dom/blocks/html.js +++ b/packages/svelte/src/internal/client/dom/blocks/html.js @@ -79,7 +79,7 @@ function html_to_dom(target, effect, value, svg) { var child = /** @type {Text | Element | Comment} */ (node.firstChild); target.before(child); if (effect !== null) { - push_template_node(effect, child); + push_template_node(child, effect); } return child; } @@ -95,7 +95,7 @@ function html_to_dom(target, effect, value, svg) { } if (effect !== null) { - push_template_node(effect, nodes); + push_template_node(nodes, effect); } return nodes; 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 c7daff227ba8..ba447b2c1fb3 100644 --- a/packages/svelte/src/internal/client/dom/blocks/svelte-element.js +++ b/packages/svelte/src/internal/client/dom/blocks/svelte-element.js @@ -133,7 +133,7 @@ export function element(anchor, get_tag, is_svg, render_fn) { swap_block_dom(parent_effect, prev_element, element); prev_element.remove(); } else if (!hydrating) { - push_template_node(parent_effect, element); + push_template_node(element, parent_effect); } }); } diff --git a/packages/svelte/src/internal/client/dom/template.js b/packages/svelte/src/internal/client/dom/template.js index 5baeb2f313de..4d37c610eb72 100644 --- a/packages/svelte/src/internal/client/dom/template.js +++ b/packages/svelte/src/internal/client/dom/template.js @@ -7,10 +7,13 @@ import { effect } from '../reactivity/effects.js'; import { is_array } from '../utils.js'; /** - * @param {import("#client").Effect} effect * @param {import("#client").TemplateNode | import("#client").TemplateNode[]} dom + * @param {import("#client").Effect} effect */ -export function push_template_node(effect, dom) { +export function push_template_node( + dom, + effect = /** @type {import('#client').Effect} */ (current_effect) +) { var current_dom = effect.dom; if (current_dom === null) { effect.dom = dom; @@ -18,19 +21,12 @@ export function push_template_node(effect, dom) { if (!is_array(current_dom)) { current_dom = effect.dom = [current_dom]; } - var anchor; - // If we're working with an anchor, then remove it and put it at the end. - if (current_dom[0].nodeType === 8) { - anchor = current_dom.pop(); - } + if (is_array(dom)) { current_dom.push(...dom); } else { current_dom.push(dom); } - if (anchor !== undefined) { - current_dom.push(anchor); - } } return dom; } @@ -49,12 +45,8 @@ export function template(content, flags) { var node; return () => { - var effect = /** @type {import('#client').Effect} */ (current_effect); if (hydrating) { - var hydration_content = push_template_node( - effect, - is_fragment ? hydrate_nodes : hydrate_nodes[0] - ); + var hydration_content = push_template_node(is_fragment ? hydrate_nodes : hydrate_nodes[0]); return /** @type {Node} */ (hydration_content); } @@ -64,14 +56,11 @@ export function template(content, flags) { } var clone = use_import_node ? document.importNode(node, true) : clone_node(node, true); - if (is_fragment) { - push_template_node( - effect, - /** @type {import('#client').TemplateNode[]} */ ([...clone.childNodes]) - ); - } else { - push_template_node(effect, /** @type {import('#client').TemplateNode} */ (clone)); - } + push_template_node( + is_fragment + ? /** @type {import('#client').TemplateNode[]} */ ([...clone.childNodes]) + : /** @type {import('#client').TemplateNode} */ (clone) + ); return clone; }; @@ -115,12 +104,8 @@ export function svg_template(content, flags) { var node; return () => { - var effect = /** @type {import('#client').Effect} */ (current_effect); if (hydrating) { - var hydration_content = push_template_node( - effect, - is_fragment ? hydrate_nodes : hydrate_nodes[0] - ); + var hydration_content = push_template_node(is_fragment ? hydrate_nodes : hydrate_nodes[0]); return /** @type {Node} */ (hydration_content); } @@ -139,14 +124,11 @@ export function svg_template(content, flags) { var clone = clone_node(node, true); - if (is_fragment) { - push_template_node( - effect, - /** @type {import('#client').TemplateNode[]} */ ([...clone.childNodes]) - ); - } else { - push_template_node(effect, /** @type {import('#client').TemplateNode} */ (clone)); - } + push_template_node( + is_fragment + ? /** @type {import('#client').TemplateNode[]} */ ([...clone.childNodes]) + : /** @type {import('#client').TemplateNode} */ (clone) + ); return clone; }; @@ -213,8 +195,7 @@ function run_scripts(node) { */ /*#__NO_SIDE_EFFECTS__*/ export function text(anchor) { - var effect = /** @type {import('#client').Effect} */ (current_effect); - if (!hydrating) return push_template_node(effect, empty()); + if (!hydrating) return push_template_node(empty()); var node = hydrate_nodes[0]; @@ -224,7 +205,7 @@ export function text(anchor) { anchor.before((node = empty())); } - return push_template_node(effect, node); + return push_template_node(node); } export const comment = template('', TEMPLATE_FRAGMENT); diff --git a/packages/svelte/src/internal/client/types.d.ts b/packages/svelte/src/internal/client/types.d.ts index 7b1c4333c8cc..7530b07f3238 100644 --- a/packages/svelte/src/internal/client/types.d.ts +++ b/packages/svelte/src/internal/client/types.d.ts @@ -67,6 +67,8 @@ export type EachItem = { i: number | Source; /** key */ k: unknown; + /** anchor for items inserted before this */ + o: Comment | Text; prev: EachItem | EachState; next: EachItem | null; };