diff --git a/.changeset/soft-moons-wear.md b/.changeset/soft-moons-wear.md new file mode 100644 index 0000000000..eeb2b14b8c --- /dev/null +++ b/.changeset/soft-moons-wear.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: reset context after waiting on blockers of `@const` expressions diff --git a/packages/svelte/src/compiler/phases/2-analyze/types.d.ts b/packages/svelte/src/compiler/phases/2-analyze/types.d.ts index 7941113a7f..354f1c0856 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/types.d.ts +++ b/packages/svelte/src/compiler/phases/2-analyze/types.d.ts @@ -38,7 +38,7 @@ export interface AnalysisState { /** Collected info about async `{@const }` declarations */ async_consts?: { id: Identifier; - /** How many `@const` declarations there are (already) in this scope */ + /** How many `$.run(...)` entries are already allocated in this scope */ declaration_count: number; }; } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/ConstTag.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/ConstTag.js index 22ffc24c1e..4f07249a39 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ConstTag.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ConstTag.js @@ -61,7 +61,8 @@ export function ConstTag(node, context) { // keep the counter in sync with the number of thunks pushed in ConstTag in transform // TODO 6.0 once non-async and non-runes mode is gone investigate making this more robust // via something like the approach in https://github.com/sveltejs/svelte/pull/18032 - const length = run.declaration_count++; + const length = run.declaration_count + (blockers.length > 0 ? 1 : 0); + run.declaration_count += blockers.length > 0 ? 2 : 1; const blocker = b.member(run.id, b.literal(length), true); for (const binding of bindings) { binding.blocker = blocker; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ConstTag.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ConstTag.js index ac8f120d3d..bf559cd24d 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ConstTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ConstTag.js @@ -96,22 +96,15 @@ function add_const_declaration(state, id, expression, metadata) { state.consts.push(b.let(id)); - /** @type {Statement | undefined} */ - let promise_stmt; - if (blockers.length === 1) { - promise_stmt = b.stmt(b.await(b.member(/** @type {Expression} */ (blockers[0]), 'promise'))); + run.thunks.push(b.thunk(b.member(/** @type {Expression} */ (blockers[0]), 'promise'))); } else if (blockers.length > 0) { - promise_stmt = b.stmt(b.await(b.call('$.wait', b.array(blockers)))); + run.thunks.push(b.thunk(b.call('$.wait', b.array(blockers)))); } // keep the number of thunks pushed in sync with ConstTag in analysis phase const assignment = b.assignment('=', id, expression); - if (promise_stmt) { - run.thunks.push(b.thunk(b.block([promise_stmt, b.stmt(assignment)]), true)); - } else { - run.thunks.push(b.thunk(assignment, metadata.expression.has_await)); - } + run.thunks.push(b.thunk(assignment, metadata.expression.has_await)); } else { state.consts.push(b.const(id, expression)); state.consts.push(...after); diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/ConstTag.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/ConstTag.js index 87d60442f6..9420bdd6d2 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/ConstTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/ConstTag.js @@ -28,22 +28,15 @@ export function ConstTag(node, context) { context.state.init.push(b.let(identifier.name)); } - /** @type {Statement | undefined} */ - let promise_stmt; - if (blockers.length === 1) { - promise_stmt = b.stmt(b.await(/** @type {Expression} */ (blockers[0]))); + run.thunks.push(b.thunk(/** @type {Expression} */ (blockers[0]))); } else if (blockers.length > 0) { - promise_stmt = b.stmt(b.await(b.call('Promise.all', b.array(blockers)))); + run.thunks.push(b.thunk(b.call('Promise.all', b.array(blockers)))); } // keep the number of thunks pushed in sync with ConstTag in analysis phase const assignment = b.assignment('=', id, init); - if (promise_stmt) { - run.thunks.push(b.thunk(b.block([promise_stmt, b.stmt(assignment)]), true)); - } else { - run.thunks.push(b.thunk(assignment, node.metadata.expression.has_await)); - } + run.thunks.push(b.thunk(assignment, node.metadata.expression.has_await)); } else { context.state.init.push(b.const(id, init)); } diff --git a/packages/svelte/tests/runtime-runes/samples/async-context-after-await-const/_config.js b/packages/svelte/tests/runtime-runes/samples/async-context-after-await-const/_config.js new file mode 100644 index 0000000000..6c202c1ed6 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-context-after-await-const/_config.js @@ -0,0 +1,11 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +// Tests that context is restored after await (const has to wait on a blocker), so that getContext etc work correctly +export default test({ + mode: ['hydrate', 'async-server', 'client'], + async test({ assert, target }) { + await tick(); + assert.htmlEqual(target.innerHTML, 'hi'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-context-after-await-const/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-context-after-await-const/main.svelte new file mode 100644 index 0000000000..be2d0dc7be --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-context-after-await-const/main.svelte @@ -0,0 +1,11 @@ + + +{#if true} + {@const foo = bar} + {foo} +{/if} diff --git a/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/client/index.svelte.js index 4f06d9ddbf..79b8ad0040 100644 --- a/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/client/index.svelte.js @@ -2,6 +2,8 @@ import 'svelte/internal/disclose-version'; import 'svelte/internal/flags/async'; import * as $ from 'svelte/internal/client'; +var root = $.from_html(` `, 1); + export default function Async_in_derived($$anchor, $$props) { $.push($$props, true); @@ -21,7 +23,7 @@ export default function Async_in_derived($$anchor, $$props) { } ]); - var fragment = $.comment(); + var fragment = root(); var node = $.first_child(fragment); { @@ -49,6 +51,23 @@ export default function Async_in_derived($$anchor, $$props) { }); } + var node_1 = $.sibling(node, 2); + + { + var consequent_1 = ($$anchor) => { + let x; + + var promises_1 = $.run([ + () => $$promises[2].promise, + () => x = $.derived(() => $.get(no2)) + ]); + }; + + $.if(node_1, ($$render) => { + if (true) $$render(consequent_1); + }); + } + $.append($$anchor, fragment); $.pop(); } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/server/index.svelte.js index 0cebc412ef..651a92f9a8 100644 --- a/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/server/index.svelte.js @@ -42,6 +42,17 @@ export default function Async_in_derived($$renderer, $$props) { $$renderer.push(''); } + $$renderer.push(` `); + + if (true) { + $$renderer.push(''); + + let x; + var promises_1 = $$renderer.run([() => $$promises[2], () => x = no2()]); + } else { + $$renderer.push(''); + } + $$renderer.push(``); }); } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/async-in-derived/index.svelte b/packages/svelte/tests/snapshot/samples/async-in-derived/index.svelte index bda88fd3ae..b105bef189 100644 --- a/packages/svelte/tests/snapshot/samples/async-in-derived/index.svelte +++ b/packages/svelte/tests/snapshot/samples/async-in-derived/index.svelte @@ -19,3 +19,7 @@ return await 1; })()} {/if} + +{#if true} + {@const x = no2} +{/if}