From a166761b041922978b35126f60a7b4092fcc1148 Mon Sep 17 00:00:00 2001 From: Magnar Ovedal Myrtveit Date: Thu, 20 Aug 2026 15:15:27 +0200 Subject: [PATCH] fix: treat concise arrow function bodies as implicit returns when calculating blockers (#18613) Fixes #18612. A concise arrow body does not contain a `ReturnStatement` which we traverse in full calculate_blockers (else we bail on functions), so short-cut to `touch` there. --------- Co-authored-by: Simon Holthausen --- .changeset/light-pandas-attack.md | 5 +++++ .../svelte/src/compiler/phases/2-analyze/index.js | 15 +++++++++------ .../async-bind-factory-function-remote/_config.js | 4 ++-- .../main.svelte | 5 +++++ 4 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 .changeset/light-pandas-attack.md diff --git a/.changeset/light-pandas-attack.md b/.changeset/light-pandas-attack.md new file mode 100644 index 0000000000..e6eface376 --- /dev/null +++ b/.changeset/light-pandas-attack.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: treat concise arrow function bodies as implicit returns when calculating blockers diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 67e9030188..d81053a08d 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -1235,12 +1235,15 @@ function calculate_blockers(instance, analysis) { ? /** @type {ESTree.FunctionExpression | ESTree.ArrowFunctionExpression} */ (fn.init) : fn; - trace_references( - init.body, - reads_writes, - reads_writes, - /** @type {Scope} */ (instance.scopes.get(init)) - ); + const fn_scope = /** @type {Scope} */ (instance.scopes.get(init)); + + if (init.body.type === 'BlockStatement') { + trace_references(init.body, reads_writes, reads_writes, fn_scope); + } else { + // A concise arrow body is an implicit return, so treat it like the + // `ReturnStatement` visitor in `trace_references` would. + touch(init.body, fn_scope, reads_writes); + } const max = [...reads_writes].reduce((max, binding) => { if (binding.blocker) { diff --git a/packages/svelte/tests/runtime-runes/samples/async-bind-factory-function-remote/_config.js b/packages/svelte/tests/runtime-runes/samples/async-bind-factory-function-remote/_config.js index 080e2d278c..60e58b27a1 100644 --- a/packages/svelte/tests/runtime-runes/samples/async-bind-factory-function-remote/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/async-bind-factory-function-remote/_config.js @@ -3,12 +3,12 @@ import { test } from '../../test'; export default test({ mode: ['async-server', 'client', 'hydrate'], - ssrHtml: 'true true true true true', + ssrHtml: 'true true true true true true', async test({ assert, target }) { await new Promise((resolve) => setTimeout(resolve, 10)); await tick(); - assert.htmlEqual(target.innerHTML, 'true true true true true'); + assert.htmlEqual(target.innerHTML, 'true true true true true true'); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/async-bind-factory-function-remote/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-bind-factory-function-remote/main.svelte index 5f79a14830..4f8f2f97b8 100644 --- a/packages/svelte/tests/runtime-runes/samples/async-bind-factory-function-remote/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/async-bind-factory-function-remote/main.svelte @@ -21,6 +21,8 @@ const indirect = () => checkedFactory()(); return indirect; } + + const arrow = () => () => checked; @@ -39,3 +41,6 @@ {#if true} {indirectChecked2()()} {/if} +{#if true} + {arrow()()} +{/if}