From cf35a22568be2e767c419a6b8ac36fe73c270eb3 Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Tue, 23 Sep 2025 03:04:45 -0700 Subject: [PATCH 01/80] fix: allow `{@html await ...}` and async snippets on the server (#16817) Fixes #16816 Fixes #16811 --------- Co-authored-by: Simon Holthausen Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- .changeset/long-spies-hope.md | 5 +++++ .../compiler/phases/3-transform/server/visitors/HtmlTag.js | 7 ++++++- .../phases/3-transform/server/visitors/SnippetBlock.js | 5 +++++ .../samples/async-html-tag/_expected.html | 1 + .../samples/async-html-tag/main.svelte | 1 + .../samples/async-snippet/_expected.html | 1 + .../samples/async-snippet/main.svelte | 6 ++++++ 7 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 .changeset/long-spies-hope.md create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-html-tag/_expected.html create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-html-tag/main.svelte create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-snippet/_expected.html create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-snippet/main.svelte diff --git a/.changeset/long-spies-hope.md b/.changeset/long-spies-hope.md new file mode 100644 index 0000000000..6bb8184c64 --- /dev/null +++ b/.changeset/long-spies-hope.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: allow `{@html await ...}` and snippets with async content on the server diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/HtmlTag.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/HtmlTag.js index 9e857a9308..9a3d2830ac 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/HtmlTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/HtmlTag.js @@ -9,5 +9,10 @@ import * as b from '#compiler/builders'; */ export function HtmlTag(node, context) { const expression = /** @type {Expression} */ (context.visit(node.expression)); - context.state.template.push(b.call('$.html', expression)); + const call = b.call('$.html', expression); + context.state.template.push( + node.metadata.expression.has_await + ? b.stmt(b.call('$$renderer.push', b.thunk(call, true))) + : call + ); } diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js index 7ae2a8e037..5fc865ec58 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js @@ -3,6 +3,7 @@ /** @import { ComponentContext } from '../types.js' */ import { dev } from '../../../../state.js'; import * as b from '#compiler/builders'; +import { create_async_block } from './shared/utils.js'; /** * @param {AST.SnippetBlock} node @@ -15,6 +16,10 @@ export function SnippetBlock(node, context) { /** @type {BlockStatement} */ (context.visit(node.body)) ); + if (node.body.metadata.has_await) { + fn.body = b.block([create_async_block(fn.body)]); + } + // @ts-expect-error - TODO remove this hack once $$render_inner for legacy bindings is gone fn.___snippet = true; diff --git a/packages/svelte/tests/server-side-rendering/samples/async-html-tag/_expected.html b/packages/svelte/tests/server-side-rendering/samples/async-html-tag/_expected.html new file mode 100644 index 0000000000..5be0be37f2 --- /dev/null +++ b/packages/svelte/tests/server-side-rendering/samples/async-html-tag/_expected.html @@ -0,0 +1 @@ +
this should work
\ No newline at end of file diff --git a/packages/svelte/tests/server-side-rendering/samples/async-html-tag/main.svelte b/packages/svelte/tests/server-side-rendering/samples/async-html-tag/main.svelte new file mode 100644 index 0000000000..2d556e4d5b --- /dev/null +++ b/packages/svelte/tests/server-side-rendering/samples/async-html-tag/main.svelte @@ -0,0 +1 @@ +
{@html await 'this should work'}
\ No newline at end of file diff --git a/packages/svelte/tests/server-side-rendering/samples/async-snippet/_expected.html b/packages/svelte/tests/server-side-rendering/samples/async-snippet/_expected.html new file mode 100644 index 0000000000..5be0be37f2 --- /dev/null +++ b/packages/svelte/tests/server-side-rendering/samples/async-snippet/_expected.html @@ -0,0 +1 @@ +
this should work
\ No newline at end of file diff --git a/packages/svelte/tests/server-side-rendering/samples/async-snippet/main.svelte b/packages/svelte/tests/server-side-rendering/samples/async-snippet/main.svelte new file mode 100644 index 0000000000..a6f2ac7b09 --- /dev/null +++ b/packages/svelte/tests/server-side-rendering/samples/async-snippet/main.svelte @@ -0,0 +1,6 @@ +{#snippet foo()} + {@const x = await 'this should work'} +
{x}
+{/snippet} + +{@render foo()} From 562623d536e9e2c3473d13a5cccce8d14ce98870 Mon Sep 17 00:00:00 2001 From: Brittany Harris <6003769+brittharr@users.noreply.github.com> Date: Tue, 23 Sep 2025 13:55:46 +0100 Subject: [PATCH 02/80] fix: use SSR format compatible with nginx SSI for props.id (#16820) * fix: use SSR format compatible with nginx SSI for props.id * changeset --------- Co-authored-by: Rich Harris --- .changeset/silly-schools-divide.md | 5 +++++ packages/svelte/src/internal/client/dom/template.js | 2 +- packages/svelte/src/internal/server/index.js | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/silly-schools-divide.md diff --git a/.changeset/silly-schools-divide.md b/.changeset/silly-schools-divide.md new file mode 100644 index 0000000000..ff36717063 --- /dev/null +++ b/.changeset/silly-schools-divide.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: use nginx SSI-compatible comments for `$props.id()` diff --git a/packages/svelte/src/internal/client/dom/template.js b/packages/svelte/src/internal/client/dom/template.js index 135ca86610..c2443fda7a 100644 --- a/packages/svelte/src/internal/client/dom/template.js +++ b/packages/svelte/src/internal/client/dom/template.js @@ -365,7 +365,7 @@ export function props_id() { hydrating && hydrate_node && hydrate_node.nodeType === COMMENT_NODE && - hydrate_node.textContent?.startsWith(`#`) + hydrate_node.textContent?.startsWith(`$`) ) { const id = hydrate_node.textContent.substring(1); hydrate_next(); diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index 3ff44f8030..50bb629c4d 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -448,7 +448,7 @@ export function once(get_value) { */ export function props_id(renderer) { const uid = renderer.global.uid(); - renderer.push(''); + renderer.push(''); return uid; } From 8680c29a0a7bdb7993c40e8a34394d95c23d19c8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 23 Sep 2025 08:57:46 -0400 Subject: [PATCH 03/80] Version Packages (#16818) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/long-spies-hope.md | 5 ----- .changeset/silly-schools-divide.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/long-spies-hope.md delete mode 100644 .changeset/silly-schools-divide.md diff --git a/.changeset/long-spies-hope.md b/.changeset/long-spies-hope.md deleted file mode 100644 index 6bb8184c64..0000000000 --- a/.changeset/long-spies-hope.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: allow `{@html await ...}` and snippets with async content on the server diff --git a/.changeset/silly-schools-divide.md b/.changeset/silly-schools-divide.md deleted file mode 100644 index ff36717063..0000000000 --- a/.changeset/silly-schools-divide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"svelte": patch ---- - -fix: use nginx SSI-compatible comments for `$props.id()` diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index fe6e44ffbe..2cf08ae409 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.39.5 + +### Patch Changes + +- fix: allow `{@html await ...}` and snippets with async content on the server ([#16817](https://github.com/sveltejs/svelte/pull/16817)) + +- fix: use nginx SSI-compatible comments for `$props.id()` ([#16820](https://github.com/sveltejs/svelte/pull/16820)) + ## 5.39.4 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 0be1242ffe..a4865da75f 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.39.4", + "version": "5.39.5", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 1ebbbb95dd..55796a2083 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.39.4'; +export const VERSION = '5.39.5'; export const PUBLIC_VERSION = '5'; From 7d9962a57268fbe02b6f5a6327e488e2b189f8ca Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 23 Sep 2025 14:56:08 -0400 Subject: [PATCH 04/80] docs: update await expressions documentation (#16822) * docs: update await expressions documentation * Update documentation/docs/03-template-syntax/19-await-expressions.md Co-authored-by: Tee Ming * Update documentation/docs/03-template-syntax/19-await-expressions.md Co-authored-by: Elliott Johnson * Update documentation/docs/03-template-syntax/19-await-expressions.md Co-authored-by: Tee Ming * Update documentation/docs/03-template-syntax/19-await-expressions.md --------- Co-authored-by: Elliott Johnson Co-authored-by: Tee Ming --- .../19-await-expressions.md | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/documentation/docs/03-template-syntax/19-await-expressions.md b/documentation/docs/03-template-syntax/19-await-expressions.md index 4e5ec28b26..e43d6c81d1 100644 --- a/documentation/docs/03-template-syntax/19-await-expressions.md +++ b/documentation/docs/03-template-syntax/19-await-expressions.md @@ -23,24 +23,6 @@ export default { The experimental flag will be removed in Svelte 6. -## Boundaries - -Currently, you can only use `await` inside a [``](svelte-boundary) with a `pending` snippet: - -```svelte - - - - {#snippet pending()} -

loading...

- {/snippet} -
-``` - -This restriction will be lifted once Svelte supports asynchronous server-side rendering (see [caveats](#Caveats)). - -> [!NOTE] In the [playground](/playground), your app is rendered inside a boundary with an empty pending snippet, so that you can use `await` without having to create one. - ## Synchronized updates When an `await` expression depends on a particular piece of state, changes to that state will not be reflected in the UI until the asynchronous work has completed, so that the UI is not left in an inconsistent state. In other words, in an example like [this](/playground/untitled#H4sIAAAAAAAAE42QsWrDQBBEf2VZUkhYRE4gjSwJ0qVMkS6XYk9awcFpJe5Wdoy4fw-ycdykSPt2dpiZFYVGxgrf2PsJTlPwPWTcO-U-xwIH5zli9bminudNtwEsbl-v8_wYj-x1Y5Yi_8W7SZRFI1ZYxy64WVsjRj0rEDTwEJWUs6f8cKP2Tp8vVIxSPEsHwyKdukmA-j6jAmwO63Y1SidyCsIneA_T6CJn2ZBD00Jk_XAjT4tmQwEv-32eH6AsgYK6wXWOPPTs6Xy1CaxLECDYgb3kSUbq8p5aaifzorCt0RiUZbQcDIJ10ldH8gs3K6X2Xzqbro5zu1KCHaw2QQPrtclvwVSXc2sEC1T-Vqw0LJy-ClRy_uSkx2ogHzn9ADZ1CubKAQAA)... @@ -99,7 +81,9 @@ let b = $derived(await two()); ## Indicating loading states -In addition to the nearest boundary's [`pending`](svelte-boundary#Properties-pending) snippet, you can indicate that asynchronous work is ongoing with [`$effect.pending()`]($effect#$effect.pending). +To render placeholder UI, you can wrap content in a `` with a [`pending`](svelte-boundary#Properties-pending) snippet. This will be shown when the boundary is first created, but not for subsequent updates, which are globally coordinated. + +After the contents of a boundary have resolved for the first time and replaced the `pending` snippet, you can detect subsequent async work with [`$effect.pending()`]($effect#$effect.pending). This is what you would use display a "we're asynchronously validating your input" spinner next to a form field, for example. You can also use [`settled()`](svelte#settled) to get a promise that resolves when the current update is complete: @@ -133,6 +117,24 @@ async function onclick() { Errors in `await` expressions will bubble to the nearest [error boundary](svelte-boundary). +## Server-side rendering + +Svelte supports asynchronous server-side rendering (SSR) with the `render(...)` API. To use it, simply await the return value: + +```js +/// file: server.js +import { render } from 'svelte/server'; +import App from './App.svelte'; + +const { head, body } = +++await+++ render(App); +``` + +> [!NOTE] If you're using a framework like SvelteKit, this is done on your behalf. + +If a `` with a `pending` snippet is encountered during SSR, that snippet will be rendered while the rest of the content is ignored. All `await` expressions encountered outside boundaries with `pending` snippets will resolve and render their contents prior to `await render(...)` returning. + +> [!NOTE] In the future, we plan to add a streaming implementation that renders the content in the background. + ## Caveats As an experimental feature, the details of how `await` is handled (and related APIs like `$effect.pending()`) are subject to breaking changes outside of a semver major release, though we intend to keep such changes to a bare minimum. From 5e6fed6bab2423f7469f29ee9cd002dd698b91a2 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 23 Sep 2025 23:13:16 +0200 Subject: [PATCH 05/80] fix: depend on reads of deriveds created within reaction (async mode) (#16823) * fix: depend on reads of deriveds created within reaction (async mode) As part of https://github.com/sveltejs/kit/pull/14481 we discovered that deriveds created within reactions and reading from them in that same reaction is actually useful in some cases, as such a use case we couldn't imagine yet in #15564 has appeared. We think it's ultimately better to rerun on those cases, so we're going to make this change in async mode (that way the behavior doesn't change unless you have enabled the experimental flag) * fix tests --- .changeset/gold-eels-lay.md | 5 ++ .../internal/client/reactivity/deriveds.js | 4 +- .../samples/derived-in-expression/_config.js | 74 +++++++++++++++++++ .../samples/derived-in-expression/main.svelte | 30 ++++++++ .../samples/untrack-own-deriveds/_config.js | 5 +- .../samples/untrack-own-deriveds/main.svelte | 6 +- 6 files changed, 119 insertions(+), 5 deletions(-) create mode 100644 .changeset/gold-eels-lay.md create mode 100644 packages/svelte/tests/runtime-runes/samples/derived-in-expression/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/derived-in-expression/main.svelte diff --git a/.changeset/gold-eels-lay.md b/.changeset/gold-eels-lay.md new file mode 100644 index 0000000000..ef6ceb4dab --- /dev/null +++ b/.changeset/gold-eels-lay.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: depend on reads of deriveds created within reaction (async mode) diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 299251a2dc..11405a8e66 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -29,7 +29,7 @@ import * as w from '../warnings.js'; import { async_effect, destroy_effect } from './effects.js'; import { inspect_effects, internal_set, set_inspect_effects, source } from './sources.js'; import { get_stack } from '../dev/tracing.js'; -import { tracing_mode_flag } from '../../flags/index.js'; +import { async_mode_flag, tracing_mode_flag } from '../../flags/index.js'; import { Boundary } from '../dom/blocks/boundary.js'; import { component_context } from '../context.js'; import { UNINITIALIZED } from '../../../constants.js'; @@ -231,7 +231,7 @@ export function async_derived(fn, location) { export function user_derived(fn) { const d = derived(fn); - push_reaction_value(d); + if (!async_mode_flag) push_reaction_value(d); return d; } diff --git a/packages/svelte/tests/runtime-runes/samples/derived-in-expression/_config.js b/packages/svelte/tests/runtime-runes/samples/derived-in-expression/_config.js new file mode 100644 index 0000000000..73428e0ff2 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/derived-in-expression/_config.js @@ -0,0 +1,74 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + // In non-async mode we're not reacting to deriveds read in the same context they're defined in + skip_no_async: true, + test({ assert, target, logs }) { + const [a, b] = target.querySelectorAll('button'); + + flushSync(() => a?.click()); + assert.htmlEqual( + target.innerHTML, + ` + + +

1/0

a?.click()); + assert.htmlEqual( + target.innerHTML, + ` + + +

2/0

b?.click()); + assert.htmlEqual( + target.innerHTML, + ` + + +

2/1

b?.click()); + assert.htmlEqual( + target.innerHTML, + ` + + +

2/2

+ let object = $state.raw({ a: 0, b: 0 }); + + function a() { + console.log('a'); + return object.a; + } + + function b() { + console.log('b'); + let double = $derived(object.b) + return double; + } + + $effect(() => { + object.a; + console.log('effect a'); + }) + + $effect(() => { + const b = $derived(object.b); + b; + console.log('effect b'); + }) + + + + + +

{a()}/{b()}

diff --git a/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/_config.js b/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/_config.js index b728c3c0be..b5233b01e8 100644 --- a/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/_config.js @@ -13,10 +13,11 @@ export default test({ target.innerHTML, ` -

1/2

1/2

+

1/2

` ); - assert.deepEqual(logs, [0, 0]); + assert.deepEqual(logs, [0, 0, 0, 0]); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/main.svelte b/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/main.svelte index bbad2cdf4a..c0dd86993a 100644 --- a/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/main.svelte @@ -17,10 +17,14 @@ $effect(() => { foo = new Foo(); }); + + let bar = $derived(new Foo()); - + {#if foo}

{foo.value}/{foo.double}

{/if} + +

{bar.value}/{bar.double}

From 1ef297f25de64f220bce8ff020f38d399b69a647 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 23 Sep 2025 23:47:50 +0200 Subject: [PATCH 06/80] fix: ensure tick resolves within a macrotask (#16825) Race them against each other - in almost all cases requestAnimationFrame will fire first, but e.g. in case the window is not focused or a view transition happens, requestAnimationFrame will be delayed and setTimeout helps us resolve fast enough in that case Fixes #16429 Fixes https://github.com/sveltejs/kit/issues/14220 --- .changeset/wise-bottles-explode.md | 5 +++++ packages/svelte/src/internal/client/runtime.js | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 .changeset/wise-bottles-explode.md diff --git a/.changeset/wise-bottles-explode.md b/.changeset/wise-bottles-explode.md new file mode 100644 index 0000000000..d3b2931272 --- /dev/null +++ b/.changeset/wise-bottles-explode.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure tick resolves within a macrotask diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 22a1890e0f..b8f5f5ffc9 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -500,7 +500,13 @@ export function update_effect(effect) { */ export async function tick() { if (async_mode_flag) { - return new Promise((f) => requestAnimationFrame(() => f())); + return new Promise((f) => { + // Race them against each other - in almost all cases requestAnimationFrame will fire first, + // but e.g. in case the window is not focused or a view transition happens, requestAnimationFrame + // will be delayed and setTimeout helps us resolve fast enough in that case + requestAnimationFrame(() => f()); + setTimeout(() => f()); + }); } await Promise.resolve(); From 0ed0f1ef69a67324ef411b61c6cf088e4abcd57b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 24 Sep 2025 11:08:06 -0400 Subject: [PATCH 07/80] chore: disable inspector in playground (#16836) --- playgrounds/sandbox/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/playgrounds/sandbox/package.json b/playgrounds/sandbox/package.json index 641f91ddfe..84aeab586b 100644 --- a/playgrounds/sandbox/package.json +++ b/playgrounds/sandbox/package.json @@ -5,8 +5,8 @@ "type": "module", "scripts": { "prepare": "node scripts/create-app-svelte.js", - "dev": "vite --host", - "ssr": "node --conditions=development ./ssr-dev.js", + "dev": "SVELTE_INSPECTOR_OPTIONS=false vite --host", + "ssr": "SVELTE_INSPECTOR_OPTIONS=false node --conditions=development ./ssr-dev.js", "build": "vite build --outDir dist/client && vite build --outDir dist/server --ssr ssr-prod.js", "prod": "npm run build && node dist/server/ssr-prod", "preview": "vite preview", From 7b2113e1bfb6da2078abe532b0530e25f8f85285 Mon Sep 17 00:00:00 2001 From: Aaron Ajose Date: Wed, 24 Sep 2025 18:32:59 +0300 Subject: [PATCH 08/80] chore: fix typos (#16830) * chore: fix typos * revert bad change --------- Co-authored-by: Rich Harris --- packages/svelte/src/internal/client/render.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index f5e32a2196..b1165a6e7a 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -143,7 +143,7 @@ export function hydrate(component, options) { e.hydration_failed(); } - // If an error occured above, the operations might not yet have been initialised. + // If an error occurred above, the operations might not yet have been initialised. init_operations(); clear_text_content(target); From c75e86267758a534c76c3d02eae6026171792704 Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Wed, 24 Sep 2025 23:34:05 +0800 Subject: [PATCH 09/80] Update 19-await-expressions.md (#16827) --- documentation/docs/03-template-syntax/19-await-expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/03-template-syntax/19-await-expressions.md b/documentation/docs/03-template-syntax/19-await-expressions.md index e43d6c81d1..d3896a5c4e 100644 --- a/documentation/docs/03-template-syntax/19-await-expressions.md +++ b/documentation/docs/03-template-syntax/19-await-expressions.md @@ -83,7 +83,7 @@ let b = $derived(await two()); To render placeholder UI, you can wrap content in a `` with a [`pending`](svelte-boundary#Properties-pending) snippet. This will be shown when the boundary is first created, but not for subsequent updates, which are globally coordinated. -After the contents of a boundary have resolved for the first time and replaced the `pending` snippet, you can detect subsequent async work with [`$effect.pending()`]($effect#$effect.pending). This is what you would use display a "we're asynchronously validating your input" spinner next to a form field, for example. +After the contents of a boundary have resolved for the first time and have replaced the `pending` snippet, you can detect subsequent async work with [`$effect.pending()`]($effect#$effect.pending). This is what you would use to display a "we're asynchronously validating your input" spinner next to a form field, for example. You can also use [`settled()`](svelte#settled) to get a promise that resolves when the current update is complete: From 24944e61f5ef5aebf00aa6f86f86edaf3ad70986 Mon Sep 17 00:00:00 2001 From: 7nik Date: Wed, 24 Sep 2025 18:35:24 +0300 Subject: [PATCH 10/80] fix: async `class:` + spread attributes were compiled into sync server-side code (#16834) --- .changeset/tasty-snails-dress.md | 5 +++++ .../phases/3-transform/server/visitors/shared/element.js | 5 ++++- .../samples/async-directive-with-spreading/_expected.html | 1 + .../samples/async-directive-with-spreading/main.svelte | 2 ++ 4 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 .changeset/tasty-snails-dress.md create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-directive-with-spreading/_expected.html create mode 100644 packages/svelte/tests/server-side-rendering/samples/async-directive-with-spreading/main.svelte diff --git a/.changeset/tasty-snails-dress.md b/.changeset/tasty-snails-dress.md new file mode 100644 index 0000000000..0fb847c1ff --- /dev/null +++ b/.changeset/tasty-snails-dress.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: async `class:` + spread attributes were compiled into sync server-side code diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js index da90011259..3b5f2423a3 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js @@ -372,7 +372,10 @@ function build_element_spread_attributes( directive.name, directive.expression.type === 'Identifier' && directive.expression.name === directive.name ? b.id(directive.name) - : /** @type {Expression} */ (context.visit(directive.expression)) + : transform( + /** @type {Expression} */ (context.visit(directive.expression)), + directive.metadata.expression + ) ); }); diff --git a/packages/svelte/tests/server-side-rendering/samples/async-directive-with-spreading/_expected.html b/packages/svelte/tests/server-side-rendering/samples/async-directive-with-spreading/_expected.html new file mode 100644 index 0000000000..d46a957bdf --- /dev/null +++ b/packages/svelte/tests/server-side-rendering/samples/async-directive-with-spreading/_expected.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/packages/svelte/tests/server-side-rendering/samples/async-directive-with-spreading/main.svelte b/packages/svelte/tests/server-side-rendering/samples/async-directive-with-spreading/main.svelte new file mode 100644 index 0000000000..ed4270b754 --- /dev/null +++ b/packages/svelte/tests/server-side-rendering/samples/async-directive-with-spreading/main.svelte @@ -0,0 +1,2 @@ +
+
From ac7e16002939f627bd961a9194e497f53ff466f9 Mon Sep 17 00:00:00 2001 From: LeeWxx <99359120+LeeWxx@users.noreply.github.com> Date: Thu, 25 Sep 2025 01:35:51 +0900 Subject: [PATCH 11/80] fix: SSR scoped classes for attribute handling and prevent double-await * test: update renderer.select call order * fix: restore scoped classes on