From a91b0687866c8b2f78692010702bb811bf27b9a8 Mon Sep 17 00:00:00 2001 From: Fedor Nezhivoi Date: Tue, 14 Jul 2026 16:40:30 +0700 Subject: [PATCH 1/8] fix: abort deriveds own AbortSignal when it disconnects (#18400) Fixes https://github.com/sveltejs/svelte/issues/18301 --------- Co-authored-by: Fedor Nezhivoi Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> Co-authored-by: Simon Holthausen --- .changeset/early-crabs-rest.md | 5 +++++ .../internal/client/reactivity/deriveds.js | 9 +++++++-- .../svelte/src/internal/client/runtime.js | 8 ++++++++ .../abort-signal-derived-destroy/Child.svelte | 20 +++++++++++++++++++ .../abort-signal-derived-destroy/_config.js | 14 +++++++++++++ .../abort-signal-derived-destroy/main.svelte | 14 +++++++++++++ .../abort-signal-derived-set-state/_config.js | 2 ++ .../main.svelte | 8 ++++---- 8 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 .changeset/early-crabs-rest.md create mode 100644 packages/svelte/tests/runtime-runes/samples/abort-signal-derived-destroy/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/abort-signal-derived-destroy/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/abort-signal-derived-destroy/main.svelte diff --git a/.changeset/early-crabs-rest.md b/.changeset/early-crabs-rest.md new file mode 100644 index 0000000000..d3f290a5c2 --- /dev/null +++ b/.changeset/early-crabs-rest.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: abort deriveds own AbortSignal when it disconnects diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index fd64f3b45d..6b48bc8a23 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -28,6 +28,7 @@ import { skipped_deps, new_deps } from '../runtime.js'; +import { without_reactive_context } from '../dom/elements/bindings/shared.js'; import { equals, safe_equals } from './equality.js'; import * as e from '../errors.js'; import * as w from '../warnings.js'; @@ -450,14 +451,18 @@ export function freeze_derived_effects(derived) { // if the effect has a teardown function or abort signal, call it if (e.teardown || e.ac) { e.teardown?.(); - e.ac?.abort(STALE_REACTION); + if (e.ac !== null) { + without_reactive_context(() => { + /** @type {AbortController} */ (e.ac).abort(STALE_REACTION); + e.ac = null; + }); + } // make it a noop so it doesn't get called again if the derived // is unfrozen. we don't set it to `null`, because the existence // of a teardown function is what determines whether the // effect runs again during unfreezing (but not for teardown-only effects) if (e.fn !== null) e.teardown = noop; - e.ac = null; remove_reactions(e, 0); destroy_effect_children(e); diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 188d16a820..2ebce07916 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -408,6 +408,14 @@ function remove_reaction(signal, dependency) { update_derived_status(derived); } + // Call abort controller, noone's listening to this derived anymore + if (derived.ac !== null) { + without_reactive_context(() => { + /** @type {AbortController} */ (derived.ac).abort(STALE_REACTION); + derived.ac = null; + }); + } + // freeze any effects inside this derived freeze_derived_effects(derived); diff --git a/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-destroy/Child.svelte b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-destroy/Child.svelte new file mode 100644 index 0000000000..5a837e5ea4 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-destroy/Child.svelte @@ -0,0 +1,20 @@ + + +{der} diff --git a/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-destroy/_config.js b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-destroy/_config.js new file mode 100644 index 0000000000..3c6ec98fe6 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-destroy/_config.js @@ -0,0 +1,14 @@ +import { ok, test } from '../../test'; +import { flushSync } from 'svelte'; + +export default test({ + async test({ assert, target, errors }) { + const btn = target.querySelector('button'); + + flushSync(() => { + btn?.click(); + }); + assert.htmlEqual(target.innerHTML, '1 '); + assert.deepEqual(errors, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-destroy/main.svelte b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-destroy/main.svelte new file mode 100644 index 0000000000..57c467a80c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-destroy/main.svelte @@ -0,0 +1,14 @@ + + +{aborted} + + + +{#if count % 2 === 0} + +{/if} diff --git a/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-set-state/_config.js b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-set-state/_config.js index 2dacf188d7..aa38dd8280 100644 --- a/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-set-state/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-set-state/_config.js @@ -4,9 +4,11 @@ import { flushSync } from 'svelte'; export default test({ async test({ assert, target, errors }) { const btn = target.querySelector('button'); + flushSync(() => { btn?.click(); }); + assert.htmlEqual(target.innerHTML, '1:1 '); assert.deepEqual(errors, []); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-set-state/main.svelte b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-set-state/main.svelte index ebefe38fb2..e76cecbac9 100644 --- a/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-set-state/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-set-state/main.svelte @@ -9,9 +9,9 @@ const signal = getAbortSignal(); signal.addEventListener("abort", () => { - try{ + try { aborted++; - }catch(e){ + } catch(e) { console.error(e); } }); @@ -19,6 +19,6 @@ }) -{der} +{der}:{aborted} - \ No newline at end of file + From edeef1f9f713ab1ffdba698ff6d92cce8da1c5b4 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 14 Jul 2026 11:41:20 +0200 Subject: [PATCH 2/8] chore: add some failing tests (#18528) During my explorations I collected these new tests which are currently failing. Two of them work on the incremental-batches branch, all of them (adjusted for new behavior) pass on my uncommitted entangle batches branch, and all of them also pass on another uncommitted experimental branch of overlaying deriveds which will likely not land. --- .../_config.js | 42 +++++++++++++++++++ .../main.svelte | 23 ++++++++++ .../async-derived-not-overfiring/_config.js | 35 ++++++++++++++++ .../async-derived-not-overfiring/main.svelte | 29 +++++++++++++ .../async-derived-not-underfiring/_config.js | 28 +++++++++++++ .../async-derived-not-underfiring/main.svelte | 22 ++++++++++ 6 files changed, 179 insertions(+) create mode 100644 packages/svelte/tests/runtime-runes/samples/async-derived-log-outside-reactivity/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-derived-log-outside-reactivity/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-derived-not-overfiring/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-derived-not-overfiring/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-derived-not-underfiring/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-derived-not-underfiring/main.svelte diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-log-outside-reactivity/_config.js b/packages/svelte/tests/runtime-runes/samples/async-derived-log-outside-reactivity/_config.js new file mode 100644 index 0000000000..1a7ed0eb92 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-derived-log-outside-reactivity/_config.js @@ -0,0 +1,42 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + skip: true, // TODO fix + async test({ assert, target, logs }) { + await tick(); + + const [a, b, log, resolve] = target.querySelectorAll('button'); + const [p] = target.querySelectorAll('p'); + + a.click(); + await tick(); + assert.htmlEqual(p.innerHTML, '0 0 0'); + + b.click(); + await tick(); + assert.htmlEqual(p.innerHTML, '0 0 0'); + + log.click(); + await tick(); + assert.deepEqual(logs, [0, 2]); + + resolve.click(); + await tick(); + assert.htmlEqual(p.innerHTML, '1 0 1'); + assert.deepEqual(logs, [0, 2, 1]); + + log.click(); + await tick(); + assert.deepEqual(logs, [0, 2, 1, 2]); + + resolve.click(); + await tick(); + assert.htmlEqual(p.innerHTML, '1 1 2'); + assert.deepEqual(logs, [0, 2, 1, 2, 2]); + + log.click(); + await tick(); + assert.deepEqual(logs, [0, 2, 1, 2, 2, 2]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-log-outside-reactivity/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-derived-log-outside-reactivity/main.svelte new file mode 100644 index 0000000000..bf543ba595 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-derived-log-outside-reactivity/main.svelte @@ -0,0 +1,23 @@ + + + + + + + +

{await push(a)} {await push(b)} {d}

diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-not-overfiring/_config.js b/packages/svelte/tests/runtime-runes/samples/async-derived-not-overfiring/_config.js new file mode 100644 index 0000000000..b959ba5407 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-derived-not-overfiring/_config.js @@ -0,0 +1,35 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + skip: true, // TODO fix + async test({ assert, target, logs }) { + await tick(); + + const [a, b, log, resolve] = target.querySelectorAll('button'); + const [div] = target.querySelectorAll('div'); + + assert.deepEqual(logs, ['e1 0', 'e2 0']); + logs.length = 0; + + a.click(); + await tick(); + assert.htmlEqual(div.innerHTML, '

0

0

0

'); + + b.click(); + await tick(); + assert.htmlEqual(div.innerHTML, '

0

1

1

'); + + log.click(); + await tick(); + assert.deepEqual(logs, ['e1 1', 'e2 1', 'runs 2']); // ideally it's only 2 runs, one or two more would also be acceptable but not the 8 that it's today + logs.length = 0; + + resolve.click(); + await tick(); + log.click(); + await tick(); + assert.htmlEqual(div.innerHTML, '

1

2

2

'); + assert.deepEqual(logs, ['e1 2', 'e2 2', 'runs 3']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-not-overfiring/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-derived-not-overfiring/main.svelte new file mode 100644 index 0000000000..784932ab5d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-derived-not-overfiring/main.svelte @@ -0,0 +1,29 @@ + + + + + + + +
+

{await push(a)}

+

{d}

+

{d}

+
diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-not-underfiring/_config.js b/packages/svelte/tests/runtime-runes/samples/async-derived-not-underfiring/_config.js new file mode 100644 index 0000000000..7d31d30b46 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-derived-not-underfiring/_config.js @@ -0,0 +1,28 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + skip: true, // TODO fix + async test({ assert, target, logs }) { + await tick(); + + const [a, b, resolve] = target.querySelectorAll('button'); + const [p] = target.querySelectorAll('p'); + + assert.deepEqual(logs, ['b: 0, d: 0']); + logs.length = 0; + + a.click(); + await tick(); + b.click(); + await tick(); + assert.htmlEqual(p.innerHTML, '0 1'); + assert.deepEqual(logs, ['b: 1, d: 0']); + logs.length = 0; + + resolve.click(); + await tick(); + assert.htmlEqual(p.innerHTML, '2 1'); + assert.deepEqual(logs, ['b: 1, d: 2']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-not-underfiring/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-derived-not-underfiring/main.svelte new file mode 100644 index 0000000000..97d91ae994 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-derived-not-underfiring/main.svelte @@ -0,0 +1,22 @@ + + + + + + +

{await push(d)} {b}

From 199ffebca375b29b65fcac53fc1825b452175a53 Mon Sep 17 00:00:00 2001 From: prashantg-pixi Date: Tue, 14 Jul 2026 15:21:13 +0530 Subject: [PATCH 3/8] fix: clear previous_task reference after abort in Tween (#18541) avoids memory leaks --- .changeset/tween-previous-task-leak.md | 5 +++++ packages/svelte/src/motion/tweened.js | 1 + 2 files changed, 6 insertions(+) create mode 100644 .changeset/tween-previous-task-leak.md diff --git a/.changeset/tween-previous-task-leak.md b/.changeset/tween-previous-task-leak.md new file mode 100644 index 0000000000..6208e7c87a --- /dev/null +++ b/.changeset/tween-previous-task-leak.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: clear previous_task reference after abort in Tween to prevent memory leak on interrupted tweens diff --git a/packages/svelte/src/motion/tweened.js b/packages/svelte/src/motion/tweened.js index a24148d075..460db0df64 100644 --- a/packages/svelte/src/motion/tweened.js +++ b/packages/svelte/src/motion/tweened.js @@ -275,6 +275,7 @@ export class Tween { } previous_task?.abort(); + previous_task = null; } const elapsed = now - start; From a4fd67e361914df612ae432ff75283c2d332ae18 Mon Sep 17 00:00:00 2001 From: adiGuba Date: Tue, 14 Jul 2026 11:54:13 +0200 Subject: [PATCH 4/8] fix: $state.eager() is sometimes incorrect in SSR (#18530) Fix #18529 A simple fix : `context.visit()` was missing on the argument of `$state.eager()`, so the generated code can be incorrect in some case : --- .changeset/long-buttons-hunt.md | 5 +++++ .../phases/3-transform/server/visitors/CallExpression.js | 2 +- .../samples/state-eager/_expected.html | 2 ++ .../server-side-rendering/samples/state-eager/main.svelte | 7 +++++++ 4 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 .changeset/long-buttons-hunt.md create mode 100644 packages/svelte/tests/server-side-rendering/samples/state-eager/_expected.html create mode 100644 packages/svelte/tests/server-side-rendering/samples/state-eager/main.svelte diff --git a/.changeset/long-buttons-hunt.md b/.changeset/long-buttons-hunt.md new file mode 100644 index 0000000000..68d0b60ff4 --- /dev/null +++ b/.changeset/long-buttons-hunt.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure `$state.eager()` is correctly transormed for SSR output diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js index 8525fb6366..ac78f55da9 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js @@ -46,7 +46,7 @@ export function CallExpression(node, context) { } if (rune === '$state.eager') { - return node.arguments[0]; + return context.visit(node.arguments[0]); } if (rune === '$state.snapshot') { diff --git a/packages/svelte/tests/server-side-rendering/samples/state-eager/_expected.html b/packages/svelte/tests/server-side-rendering/samples/state-eager/_expected.html new file mode 100644 index 0000000000..9d3ea2f5b4 --- /dev/null +++ b/packages/svelte/tests/server-side-rendering/samples/state-eager/_expected.html @@ -0,0 +1,2 @@ +
value=0
+
eager=0
\ No newline at end of file diff --git a/packages/svelte/tests/server-side-rendering/samples/state-eager/main.svelte b/packages/svelte/tests/server-side-rendering/samples/state-eager/main.svelte new file mode 100644 index 0000000000..d144914f72 --- /dev/null +++ b/packages/svelte/tests/server-side-rendering/samples/state-eager/main.svelte @@ -0,0 +1,7 @@ + + +
value={value}
+
eager={$state.eager(value)}
From 602a873b6b82bba4e6edc91b039e2f6defbe3fc4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2026 13:59:06 +0200 Subject: [PATCH 5/8] Version Packages (#18497) This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## svelte@5.56.5 ### Patch Changes - chore: drop dead code that make TSGO fail ([#18496](https://github.com/sveltejs/svelte/pull/18496)) - fix: don't (re)connect deriveds when read inside branch/root effects ([#18527](https://github.com/sveltejs/svelte/pull/18527)) - fix: skip unnecessary derived effect in earlier batch ([#18525](https://github.com/sveltejs/svelte/pull/18525)) - fix: avoid declaration tag warning in event handlers ([#18500](https://github.com/sveltejs/svelte/pull/18500)) - fix: abort deriveds own AbortSignal when it disconnects ([#18400](https://github.com/sveltejs/svelte/pull/18400)) - fix: ensure `$state.eager()` is correctly transormed for SSR output ([#18530](https://github.com/sveltejs/svelte/pull/18530)) - fix: correctly transform declaration tags during SSR ([#18492](https://github.com/sveltejs/svelte/pull/18492)) - fix: transform computed keys in keyed `{#each}` destructuring patterns ([#18521](https://github.com/sveltejs/svelte/pull/18521)) - fix: chain preprocessor sourcemaps with an empty `sources[0]` instead of dropping them ([#18518](https://github.com/sveltejs/svelte/pull/18518)) - fix: clear previous_task reference after abort in Tween to prevent memory leak on interrupted tweens ([#18541](https://github.com/sveltejs/svelte/pull/18541)) - fix: don't treat declaration tags as parts inside each blocks ([#18507](https://github.com/sveltejs/svelte/pull/18507)) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/big-months-shout.md | 5 ----- .changeset/common-ways-deny.md | 5 ----- .changeset/curly-wasps-hide.md | 5 ----- .changeset/dull-oranges-fry.md | 5 ----- .changeset/early-crabs-rest.md | 5 ----- .changeset/long-buttons-hunt.md | 5 ----- .changeset/rich-jokes-attack.md | 5 ----- .changeset/shiny-keys-dance.md | 5 ----- .changeset/tame-donkeys-jump.md | 5 ----- .changeset/tween-previous-task-leak.md | 5 ----- .changeset/young-doodles-beam.md | 5 ----- packages/svelte/CHANGELOG.md | 26 ++++++++++++++++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 14 files changed, 28 insertions(+), 57 deletions(-) delete mode 100644 .changeset/big-months-shout.md delete mode 100644 .changeset/common-ways-deny.md delete mode 100644 .changeset/curly-wasps-hide.md delete mode 100644 .changeset/dull-oranges-fry.md delete mode 100644 .changeset/early-crabs-rest.md delete mode 100644 .changeset/long-buttons-hunt.md delete mode 100644 .changeset/rich-jokes-attack.md delete mode 100644 .changeset/shiny-keys-dance.md delete mode 100644 .changeset/tame-donkeys-jump.md delete mode 100644 .changeset/tween-previous-task-leak.md delete mode 100644 .changeset/young-doodles-beam.md diff --git a/.changeset/big-months-shout.md b/.changeset/big-months-shout.md deleted file mode 100644 index 3d8ca0045e..0000000000 --- a/.changeset/big-months-shout.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -chore: drop dead code that make TSGO fail diff --git a/.changeset/common-ways-deny.md b/.changeset/common-ways-deny.md deleted file mode 100644 index 75e9375965..0000000000 --- a/.changeset/common-ways-deny.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: don't (re)connect deriveds when read inside branch/root effects diff --git a/.changeset/curly-wasps-hide.md b/.changeset/curly-wasps-hide.md deleted file mode 100644 index 7e55d77ba0..0000000000 --- a/.changeset/curly-wasps-hide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: skip unnecessary derived effect in earlier batch diff --git a/.changeset/dull-oranges-fry.md b/.changeset/dull-oranges-fry.md deleted file mode 100644 index 0efcc6fa2d..0000000000 --- a/.changeset/dull-oranges-fry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: avoid declaration tag warning in event handlers diff --git a/.changeset/early-crabs-rest.md b/.changeset/early-crabs-rest.md deleted file mode 100644 index d3f290a5c2..0000000000 --- a/.changeset/early-crabs-rest.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: abort deriveds own AbortSignal when it disconnects diff --git a/.changeset/long-buttons-hunt.md b/.changeset/long-buttons-hunt.md deleted file mode 100644 index 68d0b60ff4..0000000000 --- a/.changeset/long-buttons-hunt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure `$state.eager()` is correctly transormed for SSR output diff --git a/.changeset/rich-jokes-attack.md b/.changeset/rich-jokes-attack.md deleted file mode 100644 index 46e7d2d8c8..0000000000 --- a/.changeset/rich-jokes-attack.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: correctly transform declaration tags during SSR diff --git a/.changeset/shiny-keys-dance.md b/.changeset/shiny-keys-dance.md deleted file mode 100644 index 20cf754212..0000000000 --- a/.changeset/shiny-keys-dance.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: transform computed keys in keyed `{#each}` destructuring patterns diff --git a/.changeset/tame-donkeys-jump.md b/.changeset/tame-donkeys-jump.md deleted file mode 100644 index aae38910db..0000000000 --- a/.changeset/tame-donkeys-jump.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: chain preprocessor sourcemaps with an empty `sources[0]` instead of dropping them diff --git a/.changeset/tween-previous-task-leak.md b/.changeset/tween-previous-task-leak.md deleted file mode 100644 index 6208e7c87a..0000000000 --- a/.changeset/tween-previous-task-leak.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"svelte": patch ---- - -fix: clear previous_task reference after abort in Tween to prevent memory leak on interrupted tweens diff --git a/.changeset/young-doodles-beam.md b/.changeset/young-doodles-beam.md deleted file mode 100644 index b68f55d241..0000000000 --- a/.changeset/young-doodles-beam.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: don't treat declaration tags as parts inside each blocks diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 80b61facb0..d80da025c9 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,31 @@ # svelte +## 5.56.5 + +### Patch Changes + +- chore: drop dead code that make TSGO fail ([#18496](https://github.com/sveltejs/svelte/pull/18496)) + +- fix: don't (re)connect deriveds when read inside branch/root effects ([#18527](https://github.com/sveltejs/svelte/pull/18527)) + +- fix: skip unnecessary derived effect in earlier batch ([#18525](https://github.com/sveltejs/svelte/pull/18525)) + +- fix: avoid declaration tag warning in event handlers ([#18500](https://github.com/sveltejs/svelte/pull/18500)) + +- fix: abort deriveds own AbortSignal when it disconnects ([#18400](https://github.com/sveltejs/svelte/pull/18400)) + +- fix: ensure `$state.eager()` is correctly transormed for SSR output ([#18530](https://github.com/sveltejs/svelte/pull/18530)) + +- fix: correctly transform declaration tags during SSR ([#18492](https://github.com/sveltejs/svelte/pull/18492)) + +- fix: transform computed keys in keyed `{#each}` destructuring patterns ([#18521](https://github.com/sveltejs/svelte/pull/18521)) + +- fix: chain preprocessor sourcemaps with an empty `sources[0]` instead of dropping them ([#18518](https://github.com/sveltejs/svelte/pull/18518)) + +- fix: clear previous_task reference after abort in Tween to prevent memory leak on interrupted tweens ([#18541](https://github.com/sveltejs/svelte/pull/18541)) + +- fix: don't treat declaration tags as parts inside each blocks ([#18507](https://github.com/sveltejs/svelte/pull/18507)) + ## 5.56.4 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 34fa68c8a7..a26a3e4a76 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.56.4", + "version": "5.56.5", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index ef468a2f79..80b38c225a 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.56.4'; +export const VERSION = '5.56.5'; export const PUBLIC_VERSION = '5'; From 22e0adb75a07442015feb0465cee1d3a61729e90 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Thu, 16 Jul 2026 10:42:31 +0200 Subject: [PATCH 6/8] fix: rerun derived that had an abort controller on reconnection (#18551) Follow-up to #18400 - we need to mark a derived with an abort signal that is frozen as dirty so it is guaranteed to rerun when it reconnects/is re-requested. Else you could return a stale value, or worse, you returned a promise from the derived which you aborted, and it's now in the rejected state until you update one of its dependencies. --- .changeset/lucky-dolls-yell.md | 5 +++ .../svelte/src/internal/client/runtime.js | 2 ++ .../_config.js | 32 +++++++++++++++++++ .../main.svelte | 32 +++++++++++++++++++ 4 files changed, 71 insertions(+) create mode 100644 .changeset/lucky-dolls-yell.md create mode 100644 packages/svelte/tests/runtime-runes/samples/abort-signal-derived-rerun-on-reconnect/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/abort-signal-derived-rerun-on-reconnect/main.svelte diff --git a/.changeset/lucky-dolls-yell.md b/.changeset/lucky-dolls-yell.md new file mode 100644 index 0000000000..d158f376d1 --- /dev/null +++ b/.changeset/lucky-dolls-yell.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: rerun derived that had an abort controller on reconnection diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 2ebce07916..4458595d35 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -413,6 +413,8 @@ function remove_reaction(signal, dependency) { without_reactive_context(() => { /** @type {AbortController} */ (derived.ac).abort(STALE_REACTION); derived.ac = null; + // ensure it reruns right away next time instead of potentially returning a rejected promise as its value + set_signal_status(derived, DIRTY); }); } diff --git a/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-rerun-on-reconnect/_config.js b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-rerun-on-reconnect/_config.js new file mode 100644 index 0000000000..7caf765e3b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-rerun-on-reconnect/_config.js @@ -0,0 +1,32 @@ +import { test } from '../../test'; +import { tick } from 'svelte'; + +export default test({ + async test({ assert, target }) { + const [increment, toggle, resolve] = target.querySelectorAll('button'); + const [div] = target.querySelectorAll('div'); + + assert.htmlEqual(div.innerHTML, 'loading'); + resolve.click(); + await tick(); + assert.htmlEqual(div.innerHTML, '0'); + + increment.click(); + await tick(); + assert.htmlEqual(div.innerHTML, 'loading'); + + toggle.click(); + await tick(); + assert.htmlEqual(div.innerHTML, ''); + + toggle.click(); + await tick(); + assert.htmlEqual(div.innerHTML, 'loading'); + + resolve.click(); // this one's for clearing the obsolete/aborted one from the queue + await tick(); + resolve.click(); + await tick(); + assert.htmlEqual(div.innerHTML, '2'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-rerun-on-reconnect/main.svelte b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-rerun-on-reconnect/main.svelte new file mode 100644 index 0000000000..a047afdd44 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-rerun-on-reconnect/main.svelte @@ -0,0 +1,32 @@ + + + + + + +
+ {#if show} + {#await double} + loading + {:then value} + {value} + {:catch} + error + {/await} + {/if} +
From d0dbe1a48013b9c405b518511f8330baf3a27bc9 Mon Sep 17 00:00:00 2001 From: Joe Schafer Date: Thu, 16 Jul 2026 04:35:37 -0700 Subject: [PATCH 7/8] perf: skip quadratic blocker analysis when no top-level await (#18548) Skip function reference tracing in `calculate_blockers` when a component has no top-level `await`. Blockers only represent dependencies on top-level async statements. Without a top-level `await`, no binding can have a blocker, so tracing every top-level function cannot affect the generated output. In large components with many functions and transitive assignments, that unnecessary work can become quadratic. --- .changeset/fast-cats-compile.md | 5 +++++ packages/svelte/src/compiler/phases/2-analyze/index.js | 4 ++++ 2 files changed, 9 insertions(+) create mode 100644 .changeset/fast-cats-compile.md diff --git a/.changeset/fast-cats-compile.md b/.changeset/fast-cats-compile.md new file mode 100644 index 0000000000..585ce0ed1d --- /dev/null +++ b/.changeset/fast-cats-compile.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +perf: skip unnecessary blocker analysis when compiling components without top-level await diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index ef20049697..67e9030188 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -1221,6 +1221,10 @@ function calculate_blockers(instance, analysis) { } } + // With no top-level await, no binding can have a blocker and function tracing + // cannot affect the output. + if (!awaited) return; + flush_sync_group(); for (const fn of functions) { From 4a6a85b5f149cc96514ed3bf5e59083b9246d394 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 00:21:52 +0200 Subject: [PATCH 8/8] Version Packages (#18552) This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## svelte@5.56.6 ### Patch Changes - perf: skip unnecessary blocker analysis when compiling components without top-level await ([#18548](https://github.com/sveltejs/svelte/pull/18548)) - fix: rerun derived that had an abort controller on reconnection ([#18551](https://github.com/sveltejs/svelte/pull/18551)) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/fast-cats-compile.md | 5 ----- .changeset/lucky-dolls-yell.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/fast-cats-compile.md delete mode 100644 .changeset/lucky-dolls-yell.md diff --git a/.changeset/fast-cats-compile.md b/.changeset/fast-cats-compile.md deleted file mode 100644 index 585ce0ed1d..0000000000 --- a/.changeset/fast-cats-compile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -perf: skip unnecessary blocker analysis when compiling components without top-level await diff --git a/.changeset/lucky-dolls-yell.md b/.changeset/lucky-dolls-yell.md deleted file mode 100644 index d158f376d1..0000000000 --- a/.changeset/lucky-dolls-yell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: rerun derived that had an abort controller on reconnection diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index d80da025c9..0f26b571d3 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.56.6 + +### Patch Changes + +- perf: skip unnecessary blocker analysis when compiling components without top-level await ([#18548](https://github.com/sveltejs/svelte/pull/18548)) + +- fix: rerun derived that had an abort controller on reconnection ([#18551](https://github.com/sveltejs/svelte/pull/18551)) + ## 5.56.5 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index a26a3e4a76..855ba92a8d 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.56.5", + "version": "5.56.6", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 80b38c225a..05d88ef904 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.56.5'; +export const VERSION = '5.56.6'; export const PUBLIC_VERSION = '5';