fix root cause for issue

pull/12182/head
Dominic Gannaway 3 months ago
parent 4284067f28
commit e071734d2b

@ -1646,7 +1646,11 @@ export const template_visitors = {
add_template(template_name, args);
body.push(b.var(id, b.call(template_name)), ...state.before_init, ...state.init);
body.push(
b.var(id, b.call(template_name, b.id('$$anchor'))),
...state.before_init,
...state.init
);
close = b.stmt(b.call('$.append', b.id('$$anchor'), id));
} else if (is_single_child_not_needing_template) {
context.visit(trimmed[0], state);
@ -1684,7 +1688,7 @@ export const template_visitors = {
if (use_comment_template) {
// special case — we can use `$.comment` instead of creating a unique template
body.push(b.var(id, b.call('$.comment')));
body.push(b.var(id, b.call('$.comment', b.id('$$anchor'))));
} else {
let flags = TEMPLATE_FRAGMENT;
@ -1697,7 +1701,7 @@ export const template_visitors = {
b.literal(flags)
]);
body.push(b.var(id, b.call(template_name)));
body.push(b.var(id, b.call(template_name, b.id('$$anchor'))));
}
body.push(...state.before_init, ...state.init);

@ -486,7 +486,10 @@ 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 === '') || dom.nodeType === 8) {
if (
(dom.nodeType === 3 && /** @type {Text} */ (dom).data === '') ||
(dom.nodeType === 8 && /** @type {Comment} */ (dom).data !== '[')
) {
var adjusted = effect.first;
var next;
while (adjusted !== null) {
@ -511,10 +514,7 @@ function get_adjusted_first_node(dom, effect) {
function get_first_node(effect) {
var dom = effect.dom;
if (is_array(dom)) {
var adjusted_dom = get_adjusted_first_node(dom[0], effect);
// If we have a sibling that contains the adjusted_dom, then use that instead.
var sibling = dom[1];
return sibling?.contains(adjusted_dom) ? sibling : adjusted_dom;
return get_adjusted_first_node(dom[0], effect);
}
return get_adjusted_first_node(/** @type {import('#client').TemplateNode} **/ (dom), effect);
}

@ -82,7 +82,7 @@ function html_to_dom(target, effect, value, svg, mathml) {
var child = /** @type {Text | Element | Comment} */ (node.firstChild);
target.before(child);
if (effect !== null) {
push_template_node(child, effect);
push_template_node(child, null, effect);
}
return child;
}
@ -98,7 +98,7 @@ function html_to_dom(target, effect, value, svg, mathml) {
}
if (effect !== null) {
push_template_node(nodes, effect);
push_template_node(nodes, null, effect);
}
return nodes;

@ -140,7 +140,7 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
swap_block_dom(element_effect, prev_element, element);
prev_element.remove();
} else {
push_template_node(element, element_effect);
push_template_node(element, null, element_effect);
}
if (render_fn) {

@ -1,7 +1,7 @@
import { hydrate_anchor, hydrate_nodes, hydrating, set_hydrate_nodes } from '../hydration.js';
import { empty } from '../operations.js';
import { block } from '../../reactivity/effects.js';
import { HYDRATION_END, HYDRATION_START } from '../../../../constants.js';
import { HYDRATION_START } from '../../../../constants.js';
/**
* @type {Node | undefined}

@ -9,10 +9,12 @@ import { queue_micro_task } from './task.js';
/**
* @template {import("#client").TemplateNode | import("#client").TemplateNode[]} T
* @param {T} dom
* @param {import("#client").TemplateNode | null} anchor
* @param {import("#client").Effect} effect
*/
export function push_template_node(
dom,
anchor,
effect = /** @type {import('#client').Effect} */ (current_effect)
) {
var current_dom = effect.dom;
@ -22,11 +24,20 @@ export function push_template_node(
if (!is_array(current_dom)) {
current_dom = effect.dom = [current_dom];
}
const anchor_index = anchor !== null ? current_dom.indexOf(anchor) : null;
if (is_array(dom)) {
current_dom.push(...dom);
if (anchor_index !== null) {
current_dom.splice(anchor_index, 0, ...dom);
} else {
current_dom.push(...dom);
}
} else {
current_dom.push(dom);
if (anchor_index !== null) {
current_dom.splice(anchor_index, 0, dom);
} else {
current_dom.push(dom);
}
}
}
return dom;
@ -45,9 +56,9 @@ export function template(content, flags) {
/** @type {Node} */
var node;
return () => {
return (/** @type {Element | Comment | null} */ prev_anchor) => {
if (hydrating) {
push_template_node(is_fragment ? hydrate_nodes : hydrate_start);
push_template_node(is_fragment ? hydrate_nodes : hydrate_start, prev_anchor);
return hydrate_start;
}
@ -61,7 +72,8 @@ export function template(content, flags) {
push_template_node(
is_fragment
? /** @type {import('#client').TemplateNode[]} */ ([...clone.childNodes])
: /** @type {import('#client').TemplateNode} */ (clone)
: /** @type {import('#client').TemplateNode} */ (clone),
prev_anchor
);
return clone;
@ -106,9 +118,9 @@ export function ns_template(content, flags, ns = 'svg') {
/** @type {Element | DocumentFragment} */
var node;
return () => {
return (/** @type {Element | Comment | null} */ prev_anchor) => {
if (hydrating) {
push_template_node(is_fragment ? hydrate_nodes : hydrate_start);
push_template_node(is_fragment ? hydrate_nodes : hydrate_start, prev_anchor);
return hydrate_start;
}
@ -130,7 +142,8 @@ export function ns_template(content, flags, ns = 'svg') {
push_template_node(
is_fragment
? /** @type {import('#client').TemplateNode[]} */ ([...clone.childNodes])
: /** @type {import('#client').TemplateNode} */ (clone)
: /** @type {import('#client').TemplateNode} */ (clone),
prev_anchor
);
return clone;
@ -208,7 +221,7 @@ function run_scripts(node) {
*/
/*#__NO_SIDE_EFFECTS__*/
export function text(anchor) {
if (!hydrating) return push_template_node(empty());
if (!hydrating) return push_template_node(empty(), null);
var node = hydrate_start;
@ -218,21 +231,24 @@ export function text(anchor) {
anchor.before((node = empty()));
}
push_template_node(node);
push_template_node(node, null);
return node;
}
export function comment() {
/**
* @param {Element | Comment | null} prev_anchor
*/
export function comment(prev_anchor) {
// we're not delegating to `template` here for performance reasons
if (hydrating) {
push_template_node(hydrate_nodes);
push_template_node(hydrate_nodes, prev_anchor);
return hydrate_start;
}
var frag = document.createDocumentFragment();
var anchor = empty();
frag.append(anchor);
push_template_node([anchor]);
push_template_node([anchor], prev_anchor);
return frag;
}

@ -0,0 +1,27 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
html: `<ul><li>test (1)</li> <span style="background-color: red; width: 20px; height: 20px; display: inline-block;"></span><li>test 2 (2)</li><li>test 3 (3)</li></ul><button>Swap items 1 &amp; 3</button>`,
async test({ assert, target }) {
const [btn1] = target.querySelectorAll('button');
flushSync(() => {
btn1.click();
});
flushSync(() => {
btn1.click();
});
flushSync(() => {
btn1.click();
});
assert.htmlEqual(
target.innerHTML,
`<ul><li>test (1)</li><span style="background-color: red; width: 20px; height: 20px; display: inline-block;"></span><li>test 2 (2)</li><li>test 3 (3)</li></ul><button>Swap items 1 &amp; 3</button>`
);
}
});

@ -0,0 +1,26 @@
<script>
const items = $state([
{ name: 'test', id: 1, color: 'red' },
{ name: 'test 2', id: 2 },
{ name: 'test 3', id: 3 },
]);
const onclick = () => {
const from = 0;
const to = 2;
items.splice(to, 0, items.splice(from, 1)[0]);
};
</script>
{#snippet renderItem(item)}
<li>
{item.name} ({item.id})
</li>
{#if item.color}<span style="background-color: {item.color}; width: 20px; height: 20px; display: inline-block;"></span>{/if}
{/snippet}
<ul>
{#each items as item (item.id)}
{@render renderItem(item)}
{/each}
</ul>
<button {onclick}>Swap items 1 & 3</button>

@ -34,7 +34,7 @@ async function createServer() {
.replace(`<!--ssr-html-->`, appHtml)
.replace(`<!--ssr-head-->`, headHtml);
res.status(200).set({ 'Content-Type': 'text/html' }).end(html);
res.end(html);
});
return { app, vite };

Loading…
Cancel
Save