From 4f49ea896129091175f1945f67fcb29d28ef8d0c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 8 Jan 2026 15:15:13 -0500 Subject: [PATCH] fix: don't revert source to UNINITIALIZED state when time travelling (#17409) * add failing test * WIP * remove comment * changeset * fix: clear batch between runs * changeset * fix * unused --- .changeset/thick-islands-pull.md | 5 ++++ .../internal/client/dom/blocks/boundary.js | 8 +++--- .../src/internal/client/reactivity/batch.js | 3 ++- .../_config.js | 18 +++++++++++++ .../main.svelte | 25 +++++++++++++++++++ 5 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 .changeset/thick-islands-pull.md create mode 100644 packages/svelte/tests/runtime-runes/samples/async-derived-with-effect-and-boundary/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-derived-with-effect-and-boundary/main.svelte diff --git a/.changeset/thick-islands-pull.md b/.changeset/thick-islands-pull.md new file mode 100644 index 0000000000..f987d72260 --- /dev/null +++ b/.changeset/thick-islands-pull.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: don't revert source to UNINITIALIZED state when time travelling diff --git a/packages/svelte/src/internal/client/dom/blocks/boundary.js b/packages/svelte/src/internal/client/dom/blocks/boundary.js index 92f4c0a104..cc4bc34e7b 100644 --- a/packages/svelte/src/internal/client/dom/blocks/boundary.js +++ b/packages/svelte/src/internal/client/dom/blocks/boundary.js @@ -161,6 +161,10 @@ export class Boundary { this.#hydrate_pending_content(); } else { this.#hydrate_resolved_content(); + + if (this.#pending_count === 0) { + this.is_pending = false; + } } } else { var anchor = this.#get_anchor(); @@ -194,10 +198,6 @@ export class Boundary { } catch (error) { this.error(error); } - - // Since server rendered resolved content, we never show pending state - // Even if client-side async operations are still running, the content is already displayed - this.is_pending = false; } #hydrate_pending_content() { diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 6dd90ba1b4..d6d23dc9c5 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -38,6 +38,7 @@ import { invoke_error_boundary } from '../error-handling.js'; import { flush_eager_effects, old_values, set_eager_effects, source, update } from './sources.js'; import { eager_effect, unlink_effect } from './effects.js'; import { defer_effect } from './utils.js'; +import { UNINITIALIZED } from '../../../constants.js'; /** @type {Set} */ const batches = new Set(); @@ -281,7 +282,7 @@ export class Batch { * @param {any} value */ capture(source, value) { - if (!this.previous.has(source)) { + if (value !== UNINITIALIZED && !this.previous.has(source)) { this.previous.set(source, value); } diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-with-effect-and-boundary/_config.js b/packages/svelte/tests/runtime-runes/samples/async-derived-with-effect-and-boundary/_config.js new file mode 100644 index 0000000000..49428f90ad --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-derived-with-effect-and-boundary/_config.js @@ -0,0 +1,18 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + skip_no_async: true, + + async test({ assert, target }) { + await tick(); + + assert.htmlEqual( + target.innerHTML, + ` +

baz: 69

+

+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-with-effect-and-boundary/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-derived-with-effect-and-boundary/main.svelte new file mode 100644 index 0000000000..0e05d0c414 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-derived-with-effect-and-boundary/main.svelte @@ -0,0 +1,25 @@ + + +

baz: {baz}

+ + + {#snippet pending()} +

Loading...

+ {/snippet} + + {#if qux} +

+ {/if} +