From cd3a11edff529463376a661fae632122ac49a21a Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Fri, 24 Oct 2025 00:26:41 +0200 Subject: [PATCH 1/2] fix: take into account static blocks when determining transition locality (#17018) The "is this a transparent effect we can ignore" logic for determining whether or not to play a local transition didn't account for pruned block effects. This fix marks the child branch as transparent if the block effect was, and during traversal then checks if the branch is the child of a block - if not that means it's the child of a pruned block effect. fixes #16826 --- .changeset/easy-paths-take.md | 5 +++++ .../svelte/src/internal/client/constants.js | 5 ++++- .../src/internal/client/reactivity/effects.js | 10 ++++++++- .../transition-if-nested-static/_config.js | 22 +++++++++++++++++++ .../transition-if-nested-static/main.svelte | 18 +++++++++++++++ 5 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 .changeset/easy-paths-take.md create mode 100644 packages/svelte/tests/runtime-runes/samples/transition-if-nested-static/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/transition-if-nested-static/main.svelte diff --git a/.changeset/easy-paths-take.md b/.changeset/easy-paths-take.md new file mode 100644 index 0000000000..1378322abe --- /dev/null +++ b/.changeset/easy-paths-take.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: take into account static blocks when determining transition locality diff --git a/packages/svelte/src/internal/client/constants.js b/packages/svelte/src/internal/client/constants.js index 1f35add2a8..6818fd9d30 100644 --- a/packages/svelte/src/internal/client/constants.js +++ b/packages/svelte/src/internal/client/constants.js @@ -14,7 +14,10 @@ export const DESTROYED = 1 << 14; // Flags exclusive to effects export const EFFECT_RAN = 1 << 15; -/** 'Transparent' effects do not create a transition boundary */ +/** + * 'Transparent' effects do not create a transition boundary. + * This is on a block effect 99% of the time but may also be on a branch effect if its parent block effect was pruned + */ export const EFFECT_TRANSPARENT = 1 << 16; export const INSPECT_EFFECT = 1 << 17; export const HEAD_EFFECT = 1 << 18; diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index bfbb95a8db..9b54598f9e 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -149,6 +149,9 @@ function create_effect(type, fn, sync, push = true) { (e.f & EFFECT_PRESERVED) === 0 ) { e = e.first; + if ((type & BLOCK_EFFECT) !== 0 && (type & EFFECT_TRANSPARENT) !== 0 && e !== null) { + e.f |= EFFECT_TRANSPARENT; + } } if (e !== null) { @@ -604,7 +607,12 @@ export function pause_children(effect, transitions, local) { while (child !== null) { var sibling = child.next; - var transparent = (child.f & EFFECT_TRANSPARENT) !== 0 || (child.f & BRANCH_EFFECT) !== 0; + var transparent = + (child.f & EFFECT_TRANSPARENT) !== 0 || + // If this is a branch effect without a block effect parent, + // it means the parent block effect was pruned. In that case, + // transparency information was transferred to the branch effect. + ((child.f & BRANCH_EFFECT) !== 0 && (effect.f & BLOCK_EFFECT) !== 0); // TODO we don't need to call pause_children recursively with a linked list in place // it's slightly more involved though as we have to account for `transparent` changing // through the tree. diff --git a/packages/svelte/tests/runtime-runes/samples/transition-if-nested-static/_config.js b/packages/svelte/tests/runtime-runes/samples/transition-if-nested-static/_config.js new file mode 100644 index 0000000000..900d6daff8 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/transition-if-nested-static/_config.js @@ -0,0 +1,22 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + const btn = target.querySelector('button'); + + btn?.click(); + flushSync(); + assert.htmlEqual( + target.innerHTML, + ` + +
Should not transition out
+ ` + ); + + btn?.click(); + flushSync(); + assert.htmlEqual(target.innerHTML, ''); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/transition-if-nested-static/main.svelte b/packages/svelte/tests/runtime-runes/samples/transition-if-nested-static/main.svelte new file mode 100644 index 0000000000..84f6ee77af --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/transition-if-nested-static/main.svelte @@ -0,0 +1,18 @@ + + + + + +{#if showText} + {#if show} +
+ Should not transition out +
+ {/if} +{/if} From bd697c12c6ac31c178e8002d5f11a32161733ad2 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Fri, 24 Oct 2025 00:27:17 +0200 Subject: [PATCH 2/2] fix: flush pending changes after rendering `failed` snippet (#16995) fixes #16730 --- .changeset/slimy-turtles-yell.md | 5 +++++ .../internal/client/dom/blocks/boundary.js | 2 +- .../samples/error-boundary-23/_config.js | 12 +++++++++++ .../samples/error-boundary-23/main.svelte | 20 +++++++++++++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 .changeset/slimy-turtles-yell.md create mode 100644 packages/svelte/tests/runtime-runes/samples/error-boundary-23/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/error-boundary-23/main.svelte diff --git a/.changeset/slimy-turtles-yell.md b/.changeset/slimy-turtles-yell.md new file mode 100644 index 0000000000..e3f3a66264 --- /dev/null +++ b/.changeset/slimy-turtles-yell.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: flush pending changes after rendering `failed` snippet diff --git a/packages/svelte/src/internal/client/dom/blocks/boundary.js b/packages/svelte/src/internal/client/dom/blocks/boundary.js index 3da9204571..72e64b1a3a 100644 --- a/packages/svelte/src/internal/client/dom/blocks/boundary.js +++ b/packages/svelte/src/internal/client/dom/blocks/boundary.js @@ -30,7 +30,6 @@ import { skip_nodes, set_hydrate_node } from '../hydration.js'; -import { get_next_sibling } from '../operations.js'; import { queue_micro_task } from '../task.js'; import * as e from '../../errors.js'; import * as w from '../../warnings.js'; @@ -402,6 +401,7 @@ export class Boundary { if (failed) { queue_micro_task(() => { this.#failed_effect = this.#run(() => { + Batch.ensure(); this.#is_creating_fallback = true; try { diff --git a/packages/svelte/tests/runtime-runes/samples/error-boundary-23/_config.js b/packages/svelte/tests/runtime-runes/samples/error-boundary-23/_config.js new file mode 100644 index 0000000000..7a6a66eb66 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/error-boundary-23/_config.js @@ -0,0 +1,12 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target, logs }) { + const btn = target.querySelector('button'); + btn?.click(); + await tick(); + + assert.deepEqual(logs, ['attachment']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/error-boundary-23/main.svelte b/packages/svelte/tests/runtime-runes/samples/error-boundary-23/main.svelte new file mode 100644 index 0000000000..c1fe20d931 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/error-boundary-23/main.svelte @@ -0,0 +1,20 @@ + + + + {fail ? error() : 'all good'} + + + {#snippet failed()} +
oops!
+ {/snippet} +