From 1cd06451aff585a0d654bfd39c1abb7c26c239e2 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 17 Mar 2026 09:52:06 -0400 Subject: [PATCH] fix: remove nodes in boundary when work is pending and HMR is active (#17932) Fixes https://github.com/sveltejs/svelte/issues/17918#issuecomment-4054067024. The issue here was that in `boundary.#render`, if `this.#pending_count > 0` we yoink the content out of the DOM so we can replace it with the `pending` fragment. This works by taking everything from `effect.nodes.start` to `effect.nodes.end` and putting it in a `DocumentFragment`. With HMR, that doesn't work, because the effect with the nodes is buried inside the HMR effect. This fixes it. Draft because I'd like to try this out in a few more places before merging. --- .changeset/tangy-women-shout.md | 5 +++++ packages/svelte/src/internal/client/dev/hmr.js | 13 +++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 .changeset/tangy-women-shout.md diff --git a/.changeset/tangy-women-shout.md b/.changeset/tangy-women-shout.md new file mode 100644 index 0000000000..f62d799d93 --- /dev/null +++ b/.changeset/tangy-women-shout.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: remove nodes in boundary when work is pending and HMR is active diff --git a/packages/svelte/src/internal/client/dev/hmr.js b/packages/svelte/src/internal/client/dev/hmr.js index 9fa4e6ccbd..071901aafc 100644 --- a/packages/svelte/src/internal/client/dev/hmr.js +++ b/packages/svelte/src/internal/client/dev/hmr.js @@ -6,6 +6,8 @@ import { block, branch, destroy_effect } from '../reactivity/effects.js'; import { set, source } from '../reactivity/sources.js'; import { set_should_intro } from '../render.js'; import { get } from '../runtime.js'; +import { assign_nodes } from '../dom/template.js'; +import { create_comment } from '../dom/operations.js'; /** * @template {(anchor: Comment, props: any) => any} Component @@ -27,6 +29,13 @@ export function hmr(fn) { let ran = false; + // Surround the wrapped effects with comments and assign the nodes + // on the wrapping effects so the parent can properly do DOM operations. + let start = create_comment(); + let end = create_comment(); + + anchor.before(start); + block(() => { if (component === (component = get(current))) { return; @@ -61,6 +70,10 @@ export function hmr(fn) { anchor = hydrate_node; } + anchor.before(end); + + assign_nodes(start, end); + return instance; }