From f3a819362ede6dfe5ad9e212c8423b632fe18668 Mon Sep 17 00:00:00 2001 From: 7nik Date: Mon, 14 Apr 2025 16:43:59 +0300 Subject: [PATCH 01/35] add reset button to REPL --- packages/repl/src/lib/Output/Output.svelte | 24 ++++++++++++++++++++ packages/repl/src/lib/Output/Viewer.svelte | 4 ++++ packages/repl/src/lib/Output/reset-dark.svg | 4 ++++ packages/repl/src/lib/Output/reset-light.svg | 4 ++++ 4 files changed, 36 insertions(+) create mode 100644 packages/repl/src/lib/Output/reset-dark.svg create mode 100644 packages/repl/src/lib/Output/reset-light.svg diff --git a/packages/repl/src/lib/Output/Output.svelte b/packages/repl/src/lib/Output/Output.svelte index bccdfc064b..1950854529 100644 --- a/packages/repl/src/lib/Output/Output.svelte +++ b/packages/repl/src/lib/Output/Output.svelte @@ -66,6 +66,8 @@ let current = $derived(workspace.current_compiled); + let resultTab: Viewer; + // TODO this effect is a bit of a code smell $effect(() => { if (current?.error) { @@ -189,6 +191,15 @@ {:else} + @@ -199,6 +210,7 @@
{ + if (ready) apply_bundle(bundle); + }; + $effect(() => { if (injectedCSS && proxy && ready) { proxy.eval( diff --git a/packages/repl/src/lib/Output/reset-dark.svg b/packages/repl/src/lib/Output/reset-dark.svg new file mode 100644 index 0000000000..7ab0bb4529 --- /dev/null +++ b/packages/repl/src/lib/Output/reset-dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/repl/src/lib/Output/reset-light.svg b/packages/repl/src/lib/Output/reset-light.svg new file mode 100644 index 0000000000..55faa02fa3 --- /dev/null +++ b/packages/repl/src/lib/Output/reset-light.svg @@ -0,0 +1,4 @@ + + + + From b72abad19fe4e251c8a6df0fd0e2567fbfc8411a Mon Sep 17 00:00:00 2001 From: 7nik Date: Thu, 17 Apr 2025 01:00:40 +0300 Subject: [PATCH 02/35] reuse existing icon --- packages/repl/src/lib/Output/Output.svelte | 16 ++++++++-------- packages/repl/src/lib/Output/reset-dark.svg | 4 ---- packages/repl/src/lib/Output/reset-light.svg | 4 ---- 3 files changed, 8 insertions(+), 16 deletions(-) delete mode 100644 packages/repl/src/lib/Output/reset-dark.svg delete mode 100644 packages/repl/src/lib/Output/reset-light.svg diff --git a/packages/repl/src/lib/Output/Output.svelte b/packages/repl/src/lib/Output/Output.svelte index 1950854529..f687228487 100644 --- a/packages/repl/src/lib/Output/Output.svelte +++ b/packages/repl/src/lib/Output/Output.svelte @@ -198,8 +198,8 @@ onclick={() => { view = 'result'; resultTab.reset(); - }} - > + }}> @@ -292,14 +292,14 @@ } &.reset-result { - width: 2em; + height: 100%; vertical-align: bottom; - background: url(./reset-light.svg) 50% 50% no-repeat; - background-size: 1em; - margin-left: -1ch; + margin-left: -1em; cursor: pointer; - :root.dark & { - background-image: url(./reset-dark.svg); + + .icon { + background: currentColor; + mask: url(icons/refresh) 50% / 1.2em no-repeat; } } } diff --git a/packages/repl/src/lib/Output/reset-dark.svg b/packages/repl/src/lib/Output/reset-dark.svg deleted file mode 100644 index 7ab0bb4529..0000000000 --- a/packages/repl/src/lib/Output/reset-dark.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/repl/src/lib/Output/reset-light.svg b/packages/repl/src/lib/Output/reset-light.svg deleted file mode 100644 index 55faa02fa3..0000000000 --- a/packages/repl/src/lib/Output/reset-light.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - From 77798bad2f9388f866ee37f70dfaea48827d3d71 Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Sat, 6 Sep 2025 01:33:53 -0700 Subject: [PATCH 03/35] fix: remove trailing `=` signs from hash urls (#1513) * fix: remove trailing `=` signs from hash urls * Update apps/svelte.dev/src/routes/(authed)/playground/[id]/gzip.js --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- apps/svelte.dev/src/routes/(authed)/playground/[id]/gzip.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/svelte.dev/src/routes/(authed)/playground/[id]/gzip.js b/apps/svelte.dev/src/routes/(authed)/playground/[id]/gzip.js index d74a36b6a9..76b5baf571 100644 --- a/apps/svelte.dev/src/routes/(authed)/playground/[id]/gzip.js +++ b/apps/svelte.dev/src/routes/(authed)/playground/[id]/gzip.js @@ -6,7 +6,8 @@ export async function compress_and_encode_text(input) { const { done, value } = await reader.read(); if (done) { reader.releaseLock(); - return btoa(buffer).replaceAll('+', '-').replaceAll('/', '_'); + // Some sites like discord don't like it when links end with = + return btoa(buffer).replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, ''); } else { for (let i = 0; i < value.length; i++) { // decoding as utf-8 will make btoa reject the string From daaef5662d79d801e9ae5e960fab2f165754d140 Mon Sep 17 00:00:00 2001 From: Patrick Date: Sat, 6 Sep 2025 10:35:02 +0200 Subject: [PATCH 04/35] fix: scrollbar background on search results (#1519) Co-authored-by: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> --- packages/site-kit/src/lib/search/SearchBox.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/site-kit/src/lib/search/SearchBox.svelte b/packages/site-kit/src/lib/search/SearchBox.svelte index 8950621681..a59ec7b4a3 100644 --- a/packages/site-kit/src/lib/search/SearchBox.svelte +++ b/packages/site-kit/src/lib/search/SearchBox.svelte @@ -408,6 +408,7 @@ It appears when the user clicks on the `Search` component or presses the corresp .results { overflow: auto; overscroll-behavior-y: none; + scrollbar-color: var(--sk-scrollbar) var(--sk-bg-2); } .results-container { From effa1cb06a187d08e2f6da18d02e785b816d0fdd Mon Sep 17 00:00:00 2001 From: Thiago Lino Gomes Date: Sat, 6 Sep 2025 05:42:14 -0300 Subject: [PATCH 05/35] feat: add #snippet and @render completions (#1377) Co-authored-by: Tee Ming Co-authored-by: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> --- packages/site-kit/src/lib/codemirror/index.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/site-kit/src/lib/codemirror/index.js b/packages/site-kit/src/lib/codemirror/index.js index 24152606a3..be75f54d3c 100644 --- a/packages/site-kit/src/lib/codemirror/index.js +++ b/packages/site-kit/src/lib/codemirror/index.js @@ -25,13 +25,18 @@ const logic_block_snippets = [ label: '#await :then', type: 'keyword' }), - snippetCompletion('#key ${}}\n\n{/key', { label: '#key', type: 'keyword' }) + snippetCompletion('#key ${}}\n\n{/key', { label: '#key', type: 'keyword' }), + snippetCompletion('#snippet ${}()}\n\n{/snippet', { + label: '#snippet', + type: 'keyword' + }) ]; const special_tag_snippets = [ snippetCompletion('@html ${}', { label: '@html', type: 'keyword' }), snippetCompletion('@debug ${}', { label: '@debug', type: 'keyword' }), - snippetCompletion('@const ${}', { label: '@const', type: 'keyword' }) + snippetCompletion('@const ${}', { label: '@const', type: 'keyword' }), + snippetCompletion('@render ${}()', { label: '@render', type: 'keyword' }) ]; /** From c018e496992ff025b3c55fb714355677f366dae4 Mon Sep 17 00:00:00 2001 From: Daniel Ding <41363844+BioniCosmos@users.noreply.github.com> Date: Sat, 6 Sep 2025 16:44:07 +0800 Subject: [PATCH 06/35] =?UTF-8?q?fix:=20Address=20bugs=20in=20SvelteKit=20?= =?UTF-8?q?=E2=80=9CPreloading=E2=80=9D=20tutorial=20example=20(#1334)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the tutorial code: kit/preload Correctly capture the previous navigation state. Co-authored-by: Tee Ming --- .../01-preload/+assets/app-a/src/routes/+layout.svelte | 2 +- .../01-preload/+assets/app-b/src/routes/+layout.svelte | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/svelte.dev/content/tutorial/04-advanced-sveltekit/03-link-options/01-preload/+assets/app-a/src/routes/+layout.svelte b/apps/svelte.dev/content/tutorial/04-advanced-sveltekit/03-link-options/01-preload/+assets/app-a/src/routes/+layout.svelte index ea968e6658..911c53891b 100644 --- a/apps/svelte.dev/content/tutorial/04-advanced-sveltekit/03-link-options/01-preload/+assets/app-a/src/routes/+layout.svelte +++ b/apps/svelte.dev/content/tutorial/04-advanced-sveltekit/03-link-options/01-preload/+assets/app-a/src/routes/+layout.svelte @@ -11,7 +11,7 @@ if (navigating.to) { start = Date.now(); end = null; - previous = navigating; + previous = { ...navigating }; } else { end = Date.now(); } diff --git a/apps/svelte.dev/content/tutorial/04-advanced-sveltekit/03-link-options/01-preload/+assets/app-b/src/routes/+layout.svelte b/apps/svelte.dev/content/tutorial/04-advanced-sveltekit/03-link-options/01-preload/+assets/app-b/src/routes/+layout.svelte index 5acef701c7..f1a5b48ed5 100644 --- a/apps/svelte.dev/content/tutorial/04-advanced-sveltekit/03-link-options/01-preload/+assets/app-b/src/routes/+layout.svelte +++ b/apps/svelte.dev/content/tutorial/04-advanced-sveltekit/03-link-options/01-preload/+assets/app-b/src/routes/+layout.svelte @@ -11,7 +11,7 @@ if (navigating.to) { start = Date.now(); end = null; - previous = navigating; + previous = { ...navigating }; } else { end = Date.now(); } From b8b8734ab1d0bd47ce7ae29187dceae19854c84a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 6 Sep 2025 11:06:28 +0100 Subject: [PATCH 07/35] Sync `kit` docs (#1526) sync kit docs Co-authored-by: svelte-docs-bot[bot] <196124396+svelte-docs-bot[bot]@users.noreply.github.com> --- apps/svelte.dev/content/docs/kit/30-advanced/70-packaging.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/svelte.dev/content/docs/kit/30-advanced/70-packaging.md b/apps/svelte.dev/content/docs/kit/30-advanced/70-packaging.md index 42bbcceaa0..d54406a3ff 100644 --- a/apps/svelte.dev/content/docs/kit/30-advanced/70-packaging.md +++ b/apps/svelte.dev/content/docs/kit/30-advanced/70-packaging.md @@ -140,7 +140,7 @@ Setting the `sideEffects` field in `package.json` can help the bundler to be mor } ``` -> If the scripts in your library have side effects, ensure that you update the `sideEffects` field. All scripts are marked as side effect free by default in newly created projects. If a file with side effects is incorrectly marked as having no side effects, it can result in broken functionality. +> [!NOTE] If the scripts in your library have side effects, ensure that you update the `sideEffects` field. All scripts are marked as side effect free by default in newly created projects. If a file with side effects is incorrectly marked as having no side effects, it can result in broken functionality. If your package has files with side effects, you can specify them in an array: From 115f840cd135a4eff9db7077707aed0eeb6b718a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 6 Sep 2025 12:40:32 -0700 Subject: [PATCH 08/35] Sync `svelte` docs (#1522) sync svelte docs Co-authored-by: svelte-docs-bot[bot] <196124396+svelte-docs-bot[bot]@users.noreply.github.com> --- .../content/docs/svelte/07-misc/02-testing.md | 46 ++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/apps/svelte.dev/content/docs/svelte/07-misc/02-testing.md b/apps/svelte.dev/content/docs/svelte/07-misc/02-testing.md index 8c4a9184b9..daf1867cf2 100644 --- a/apps/svelte.dev/content/docs/svelte/07-misc/02-testing.md +++ b/apps/svelte.dev/content/docs/svelte/07-misc/02-testing.md @@ -161,9 +161,9 @@ export function logger(getValue) { ### Component testing -It is possible to test your components in isolation using Vitest. +It is possible to test your components in isolation, which allows you to render them in a browser (real or simulated), simulate behavior, and make assertions, without spinning up your whole app. -> [!NOTE] Before writing component tests, think about whether you actually need to test the component, or if it's more about the logic _inside_ the component. If so, consider extracting out that logic to test it in isolation, without the overhead of a component +> [!NOTE] Before writing component tests, think about whether you actually need to test the component, or if it's more about the logic _inside_ the component. If so, consider extracting out that logic to test it in isolation, without the overhead of a component. To get started, install jsdom (a library that shims DOM APIs): @@ -247,6 +247,48 @@ test('Component', async () => { When writing component tests that involve two-way bindings, context or snippet props, it's best to create a wrapper component for your specific test and interact with that. `@testing-library/svelte` contains some [examples](https://testing-library.com/docs/svelte-testing-library/example). +### Component testing with Storybook + +[Storybook](https://storybook.js.org) is a tool for developing and documenting UI components, and it can also be used to test your components. They're run with Vitest's browser mode, which renders your components in a real browser for the most realistic testing environment. + +To get started, first install Storybook ([using Svelte's CLI](/docs/cli/storybook)) in your project via `npx sv add storybook` and choose the recommended configuration that includes testing features. If you're already using Storybook, and for more information on Storybook's testing capabilities, follow the [Storybook testing docs](https://storybook.js.org/docs/writing-tests?renderer=svelte) to get started. + +You can create stories for component variations and test interactions with the [play function](https://storybook.js.org/docs/writing-tests/interaction-testing?renderer=svelte#writing-interaction-tests), which allows you to simulate behavior and make assertions using the Testing Library and Vitest APIs. Here's an example of two stories that can be tested, one that renders an empty LoginForm component and one that simulates a user filling out the form: + +```svelte +/// file: LoginForm.stories.svelte + + + + + { + // Simulate a user filling out the form + await userEvent.type(canvas.getByTestId('email'), 'email@provider.com'); + await userEvent.type(canvas.getByTestId('password'), 'a-random-password'); + await userEvent.click(canvas.getByRole('button')); + + // Run assertions + await expect(args.onSubmit).toHaveBeenCalledTimes(1); + await expect(canvas.getByText('You’re in!')).toBeInTheDocument(); + }} +/> +``` + ## E2E tests using Playwright E2E (short for 'end to end') tests allow you to test your full application through the eyes of the user. This section uses [Playwright](https://playwright.dev/) as an example, but you can also use other solutions like [Cypress](https://www.cypress.io/) or [NightwatchJS](https://nightwatchjs.org/). From f64c3afddc76806f1c50cb5a280b871bdf1338ce Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Mon, 8 Sep 2025 18:15:42 -0700 Subject: [PATCH 09/35] fix: change github icon to link to gh organization (#1525) * feat: relative github links * make it work with tutorials, dedupe logic * lint * more * link to org --- packages/site-kit/src/lib/nav/MobileMenu.svelte | 4 +++- packages/site-kit/src/lib/nav/Nav.svelte | 14 +++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/site-kit/src/lib/nav/MobileMenu.svelte b/packages/site-kit/src/lib/nav/MobileMenu.svelte index 98fa16fe1b..a1c6a2cd29 100644 --- a/packages/site-kit/src/lib/nav/MobileMenu.svelte +++ b/packages/site-kit/src/lib/nav/MobileMenu.svelte @@ -147,7 +147,9 @@
diff --git a/packages/site-kit/src/lib/nav/Nav.svelte b/packages/site-kit/src/lib/nav/Nav.svelte index b71147ad06..d6dd5b5e0f 100644 --- a/packages/site-kit/src/lib/nav/Nav.svelte +++ b/packages/site-kit/src/lib/nav/Nav.svelte @@ -6,7 +6,7 @@ Top navigation bar for the application. It provides a slot for the left side, th import { overlay_open, on_this_page_open } from '../stores'; import { search } from '../state/search.svelte'; import Icon from '../components/Icon.svelte'; - import { page } from '$app/stores'; + import { page } from '$app/state'; import ThemeToggle from '../components/ThemeToggle.svelte'; import MobileMenu from './MobileMenu.svelte'; import type { NavigationLink } from '../types'; @@ -86,7 +86,7 @@ Top navigation bar for the application. It provides a slot for the left side, th {link.title} @@ -99,8 +99,8 @@ Top navigation bar for the application. It provides a slot for the left side, th @@ -113,7 +113,7 @@ Top navigation bar for the application. It provides a slot for the left side, th {:else} {link.title} @@ -133,7 +133,7 @@ Top navigation bar for the application. It provides a slot for the left side, th - + @@ -169,7 +169,7 @@ Top navigation bar for the application. It provides a slot for the left side, th open = !open; if (open) { - const segment = $page.url.pathname.split('/')[1]; + const segment = page.url.pathname.split('/')[1]; current = links.find((link) => link.slug === segment); } }} From 6df6cc1b8ecdd294c290e93155c67d1721c43102 Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Tue, 9 Sep 2025 02:29:27 -0700 Subject: [PATCH 10/35] fix: make repl console scrollable (#1477) * fix: make repl console scrollable * try this * try this * i think i fixed it? * apply suggestion from review Co-authored-by: Tee Ming * Apply suggestion from @eltigerchino --------- Co-authored-by: Tee Ming --- packages/repl/src/lib/Output/PaneWithPanel.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/repl/src/lib/Output/PaneWithPanel.svelte b/packages/repl/src/lib/Output/PaneWithPanel.svelte index a550263f2f..c5cc4cf965 100644 --- a/packages/repl/src/lib/Output/PaneWithPanel.svelte +++ b/packages/repl/src/lib/Output/PaneWithPanel.svelte @@ -122,6 +122,7 @@ .panel-body { overflow: auto; + max-height: calc(100% - var(--sk-pane-controls-height)); } .panel-heading { From 710d9ace654afe07e2a4d3aaa77c8ea4ecf755a6 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 10 Sep 2025 09:26:13 -0400 Subject: [PATCH 11/35] chore: bump kit (#1529) --- apps/svelte.dev/package.json | 2 +- pnpm-lock.yaml | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/apps/svelte.dev/package.json b/apps/svelte.dev/package.json index 88a9f6797e..1030ff7177 100644 --- a/apps/svelte.dev/package.json +++ b/apps/svelte.dev/package.json @@ -52,7 +52,7 @@ "@supabase/supabase-js": "^2.43.4", "@sveltejs/adapter-vercel": "^5.10.2", "@sveltejs/enhanced-img": "^0.8.1", - "@sveltejs/kit": "^2.37.0", + "@sveltejs/kit": "^2.38.0", "@sveltejs/site-kit": "workspace:*", "@sveltejs/vite-plugin-svelte": "^6.1.3", "@types/cookie": "^0.6.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2f2912f035..48b2360845 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -31,7 +31,7 @@ importers: version: 2.0.0(svelte@5.38.2) '@sveltejs/amp': specifier: ^1.1.5 - version: 1.1.5(@sveltejs/kit@2.37.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4))) + version: 1.1.5(@sveltejs/kit@2.38.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4))) '@sveltejs/repl': specifier: workspace:* version: link:../../packages/repl @@ -52,7 +52,7 @@ importers: version: 1.3.2 '@vercel/speed-insights': specifier: ^1.1.0 - version: 1.1.0(@sveltejs/kit@2.37.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2) + version: 1.1.0(@sveltejs/kit@2.38.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2) '@webcontainer/api': specifier: ^1.1.5 version: 1.1.9 @@ -113,13 +113,13 @@ importers: version: 2.43.4 '@sveltejs/adapter-vercel': specifier: ^5.10.2 - version: 5.10.2(@sveltejs/kit@2.37.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(rollup@4.46.4) + version: 5.10.2(@sveltejs/kit@2.38.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(rollup@4.46.4) '@sveltejs/enhanced-img': specifier: ^0.8.1 version: 0.8.1(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(rollup@4.46.4)(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)) '@sveltejs/kit': - specifier: ^2.37.0 - version: 2.37.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)) + specifier: ^2.38.0 + version: 2.38.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)) '@sveltejs/site-kit': specifier: workspace:* version: link:../../packages/site-kit @@ -1343,8 +1343,8 @@ packages: '@opentelemetry/api': optional: true - '@sveltejs/kit@2.37.0': - resolution: {integrity: sha512-xgKtpjQ6Ry4mdShd01ht5AODUsW7+K1iValPDq7QX8zI1hWOKREH9GjG8SRCN5tC4K7UXmMhuQam7gbLByVcnw==} + '@sveltejs/kit@2.38.0': + resolution: {integrity: sha512-iLmykJOv4PAZvuC0niq1HUoK/LZdfsTW1CpkPAAnroYeYiV7Bp73Eeh/As8u0Y1n/2IDM+p9cdoHYufcpvkXkQ==} engines: {node: '>=18.13'} hasBin: true peerDependencies: @@ -4052,9 +4052,9 @@ snapshots: dependencies: '@sveltejs/kit': 2.34.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@24.3.0)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)(vite@7.0.4(@types/node@24.3.0)(lightningcss@1.30.1)(tsx@4.20.4)) - '@sveltejs/adapter-vercel@5.10.2(@sveltejs/kit@2.37.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(rollup@4.46.4)': + '@sveltejs/adapter-vercel@5.10.2(@sveltejs/kit@2.38.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(rollup@4.46.4)': dependencies: - '@sveltejs/kit': 2.37.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)) + '@sveltejs/kit': 2.38.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)) '@vercel/nft': 0.30.0(rollup@4.46.4) esbuild: 0.25.9 transitivePeerDependencies: @@ -4062,9 +4062,9 @@ snapshots: - rollup - supports-color - '@sveltejs/amp@1.1.5(@sveltejs/kit@2.37.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))': + '@sveltejs/amp@1.1.5(@sveltejs/kit@2.38.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))': dependencies: - '@sveltejs/kit': 2.37.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)) + '@sveltejs/kit': 2.38.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)) '@sveltejs/enhanced-img@0.8.1(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(rollup@4.46.4)(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4))': dependencies: @@ -4117,7 +4117,7 @@ snapshots: svelte: 5.38.2 vite: 7.0.4(@types/node@24.3.0)(lightningcss@1.30.1)(tsx@4.20.4) - '@sveltejs/kit@2.37.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4))': + '@sveltejs/kit@2.38.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4))': dependencies: '@standard-schema/spec': 1.0.0 '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0) @@ -4299,9 +4299,9 @@ snapshots: - rollup - supports-color - '@vercel/speed-insights@1.1.0(@sveltejs/kit@2.37.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)': + '@vercel/speed-insights@1.1.0(@sveltejs/kit@2.38.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)': optionalDependencies: - '@sveltejs/kit': 2.37.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)) + '@sveltejs/kit': 2.38.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)))(svelte@5.38.2)(vite@7.0.4(@types/node@20.19.6)(lightningcss@1.30.1)(tsx@4.20.4)) svelte: 5.38.2 '@vitest/expect@3.2.4': From 39c81d05c9587ba7abf14dbd68895d5f18907b73 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 12:05:33 -0400 Subject: [PATCH 12/35] Sync `kit` docs (#1528) sync kit docs Co-authored-by: svelte-docs-bot[bot] <196124396+svelte-docs-bot[bot]@users.noreply.github.com> Co-authored-by: Rich Harris --- .../20-core-concepts/60-remote-functions.md | 53 +++++++++++++++++++ .../docs/kit/98-reference/20-$app-server.md | 45 ++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/apps/svelte.dev/content/docs/kit/20-core-concepts/60-remote-functions.md b/apps/svelte.dev/content/docs/kit/20-core-concepts/60-remote-functions.md index 91b29cbd54..2d6364e690 100644 --- a/apps/svelte.dev/content/docs/kit/20-core-concepts/60-remote-functions.md +++ b/apps/svelte.dev/content/docs/kit/20-core-concepts/60-remote-functions.md @@ -173,6 +173,59 @@ Any query can be re-fetched via its `refresh` method, which retrieves the latest > [!NOTE] Queries are cached while they're on the page, meaning `getPosts() === getPosts()`. This means you don't need a reference like `const posts = getPosts()` in order to update the query. +## query.batch + +`query.batch` works like `query` except that it batches requests that happen within the same macrotask. This solves the so-called n+1 problem: rather than each query resulting in a separate database call (for example), simultaneous queries are grouped together. + +On the server, the callback receives an array of the arguments the function was called with. It must return a function of the form `(input: Input, index: number) => Output`. SvelteKit will then call this with each of the input arguments to resolve the individual calls with their results. + +```js +/// file: weather.remote.js +// @filename: ambient.d.ts +declare module '$lib/server/database' { + export function sql(strings: TemplateStringsArray, ...values: any[]): Promise; +} +// @filename: index.js +// ---cut--- +import * as v from 'valibot'; +import { query } from '$app/server'; +import * as db from '$lib/server/database'; + +export const getWeather = query.batch(v.string(), async (cities) => { + const weather = await db.sql` + SELECT * FROM weather + WHERE city = ANY(${cities}) + `; + const lookup = new Map(weather.map(w => [w.city, w])); + + return (city) => lookup.get(city); +}); +``` + +```svelte + + + +

Weather

+ +{#each cities.slice(0, limit) as city} +

{city.name}

+ +{/each} + +{#if cities.length > limit} + +{/if} +``` + ## form The `form` function makes it easy to write data to the server. It takes a callback that receives the current [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)... diff --git a/apps/svelte.dev/content/docs/kit/98-reference/20-$app-server.md b/apps/svelte.dev/content/docs/kit/98-reference/20-$app-server.md index be0376ac33..4d26a75b77 100644 --- a/apps/svelte.dev/content/docs/kit/98-reference/20-$app-server.md +++ b/apps/svelte.dev/content/docs/kit/98-reference/20-$app-server.md @@ -263,4 +263,49 @@ function read(asset: string): Response; +## query + +
+ +```dts +namespace query { + /** + * Creates a batch query function that collects multiple calls and executes them in a single request + * + * See [Remote functions](https://svelte.dev/docs/kit/remote-functions#query.batch) for full documentation. + * + * @since 2.35 + */ + function batch( + validate: 'unchecked', + fn: ( + args: Input[] + ) => MaybePromise<(arg: Input, idx: number) => Output> + ): RemoteQueryFunction; + /** + * Creates a batch query function that collects multiple calls and executes them in a single request + * + * See [Remote functions](https://svelte.dev/docs/kit/remote-functions#query.batch) for full documentation. + * + * @since 2.35 + */ + function batch( + schema: Schema, + fn: ( + args: StandardSchemaV1.InferOutput[] + ) => MaybePromise< + ( + arg: StandardSchemaV1.InferOutput, + idx: number + ) => Output + > + ): RemoteQueryFunction< + StandardSchemaV1.InferInput, + Output + >; +} +``` + +
+ From 27ac63b22280e00f6b4ffac85a047970e571feb4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 15:12:17 -0400 Subject: [PATCH 13/35] Sync `kit` docs (#1530) sync kit docs Co-authored-by: svelte-docs-bot[bot] <196124396+svelte-docs-bot[bot]@users.noreply.github.com> --- .../content/docs/kit/25-build-and-deploy/80-adapter-netlify.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/svelte.dev/content/docs/kit/25-build-and-deploy/80-adapter-netlify.md b/apps/svelte.dev/content/docs/kit/25-build-and-deploy/80-adapter-netlify.md index 3fec8f9a69..6388bb0ca7 100644 --- a/apps/svelte.dev/content/docs/kit/25-build-and-deploy/80-adapter-netlify.md +++ b/apps/svelte.dev/content/docs/kit/25-build-and-deploy/80-adapter-netlify.md @@ -51,7 +51,7 @@ New projects will use the current Node LTS version by default. However, if you'r ## Netlify Edge Functions -SvelteKit supports [Netlify Edge Functions](https://docs.netlify.com/netlify-labs/experimental-features/edge-functions/). If you pass the option `edge: true` to the `adapter` function, server-side rendering will happen in a Deno-based edge function that's deployed close to the site visitor. If set to `false` (the default), the site will deploy to Node-based Netlify Functions. +SvelteKit supports [Netlify Edge Functions](https://docs.netlify.com/build/edge-functions/overview/). If you pass the option `edge: true` to the `adapter` function, server-side rendering will happen in a Deno-based edge function that's deployed close to the site visitor. If set to `false` (the default), the site will deploy to Node-based Netlify Functions. ```js /// file: svelte.config.js From 743318ee4338be58e3c946e9367baf3ba6cdd3e2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 20:21:36 -0700 Subject: [PATCH 14/35] Sync `svelte` docs (#1531) sync svelte docs Co-authored-by: svelte-docs-bot[bot] <196124396+svelte-docs-bot[bot]@users.noreply.github.com> --- .../content/docs/svelte/98-reference/21-svelte-compiler.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/svelte.dev/content/docs/svelte/98-reference/21-svelte-compiler.md b/apps/svelte.dev/content/docs/svelte/98-reference/21-svelte-compiler.md index 4771fc483b..dbac589b93 100644 --- a/apps/svelte.dev/content/docs/svelte/98-reference/21-svelte-compiler.md +++ b/apps/svelte.dev/content/docs/svelte/98-reference/21-svelte-compiler.md @@ -790,7 +790,7 @@ cssHash?: CssHashGetter; A function that takes a `{ hash, css, name, filename }` argument and returns the string that is used as a classname for scoped CSS. -It defaults to returning `svelte-${hash(css)}`. +It defaults to returning `svelte-${hash(filename ?? css)}`. From c1a14421cc557ca36a3cc2612c7349bc8197ceb2 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Fri, 12 Sep 2025 11:12:10 +0200 Subject: [PATCH 15/35] fix: avoid TS type strip errors returnType for example can itself contain other AST-like nodes, which would be traversed by context.next(), leading to "cannot override across a split point" magic string errors --- packages/repl/src/lib/workers/typescript-strip-types.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/repl/src/lib/workers/typescript-strip-types.ts b/packages/repl/src/lib/workers/typescript-strip-types.ts index 17cc079424..06d6e78fdc 100644 --- a/packages/repl/src/lib/workers/typescript-strip-types.ts +++ b/packages/repl/src/lib/workers/typescript-strip-types.ts @@ -83,6 +83,12 @@ const visitors: Visitors = { ts_blank_space(context, { start: node.start, end: node.start + node.accessibility.length }); } + delete node.typeAnnotation; + delete node.typeParameters; + delete node.typeArguments; + delete node.returnType; + delete node.accessibility; + context.next(); }, Decorator(node, context) { From 092b49ed236f42f2e103cb5cc0e2a5b1ee92a8c1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 12 Sep 2025 11:32:12 -0700 Subject: [PATCH 16/35] Sync `cli` docs (#1533) sync cli docs Co-authored-by: svelte-docs-bot[bot] <196124396+svelte-docs-bot[bot]@users.noreply.github.com> --- .../content/docs/cli/20-commands/10-sv-create.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/svelte.dev/content/docs/cli/20-commands/10-sv-create.md b/apps/svelte.dev/content/docs/cli/20-commands/10-sv-create.md index 1034091a21..ea238aecbd 100644 --- a/apps/svelte.dev/content/docs/cli/20-commands/10-sv-create.md +++ b/apps/svelte.dev/content/docs/cli/20-commands/10-sv-create.md @@ -13,6 +13,16 @@ npx sv create [options] [path] ## Options +### `--from-playground ` + +Create a SvelteKit project from a Svelte Playground URL. This downloads all playground files, detects external dependencies, and sets up a complete SvelteKit project structure with everything ready to go. + +Example: + +```sh +npx sv create --from-playground=https://svelte.dev/playground/hello-world +``` + ### `--template ` Which project template to use: From cf4e68e2b084c8691d4828364ea2305a7e097f8a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 12 Sep 2025 15:23:06 -0400 Subject: [PATCH 17/35] Sync `cli` docs (#1534) sync cli docs Co-authored-by: svelte-docs-bot[bot] <196124396+svelte-docs-bot[bot]@users.noreply.github.com> --- apps/svelte.dev/content/docs/cli/20-commands/10-sv-create.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/svelte.dev/content/docs/cli/20-commands/10-sv-create.md b/apps/svelte.dev/content/docs/cli/20-commands/10-sv-create.md index ea238aecbd..21ebdf4f3d 100644 --- a/apps/svelte.dev/content/docs/cli/20-commands/10-sv-create.md +++ b/apps/svelte.dev/content/docs/cli/20-commands/10-sv-create.md @@ -15,12 +15,12 @@ npx sv create [options] [path] ### `--from-playground ` -Create a SvelteKit project from a Svelte Playground URL. This downloads all playground files, detects external dependencies, and sets up a complete SvelteKit project structure with everything ready to go. +Create a SvelteKit project from a [playground](/playground) URL. This downloads all playground files, detects external dependencies, and sets up a complete SvelteKit project structure with everything ready to go. Example: ```sh -npx sv create --from-playground=https://svelte.dev/playground/hello-world +npx sv create --from-playground="https://svelte.dev/playground/hello-world" ``` ### `--template ` From 77cff059d4728af456d92239b407300496076e1c Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Sat, 13 Sep 2025 00:47:31 +0200 Subject: [PATCH 18/35] fix: avoid more TS type strip errors - fix `let a!: number;` - fix `function a(x?: number)` --- .../src/lib/workers/typescript-strip-types.ts | 52 +++++++++++++------ 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/packages/repl/src/lib/workers/typescript-strip-types.ts b/packages/repl/src/lib/workers/typescript-strip-types.ts index 06d6e78fdc..fcefb99512 100644 --- a/packages/repl/src/lib/workers/typescript-strip-types.ts +++ b/packages/repl/src/lib/workers/typescript-strip-types.ts @@ -9,24 +9,32 @@ const ParserWithTS = acorn.Parser.extend(tsPlugin()); * @param {FunctionExpression | FunctionDeclaration} node * @param {Context} context */ -function remove_this_param( +function remove_this_param_and_optional( node: acorn.FunctionExpression | acorn.FunctionDeclaration, context: Context ) { - const param = node.params[0] as any; - if (param?.type === 'Identifier' && param.name === 'this') { - if (param.typeAnnotation) { - // the type annotation is blanked by another visitor, do it in two parts to prevent an overwrite error - ts_blank_space(context, { start: param.start, end: param.typeAnnotation.start }); - ts_blank_space(context, { - start: param.typeAnnotation.end, - end: node.params[1]?.start || param.end - }); - } else { - ts_blank_space(context, { - start: param.start, - end: node.params[1]?.start || param.end - }); + for (const param of node.params as Array>) { + if (param?.type === 'Identifier') { + if (param.name === 'this') { + if (param.typeAnnotation) { + // the type annotation is blanked by another visitor, do it in two parts to prevent an overwrite error + ts_blank_space(context, { start: param.start, end: param.typeAnnotation.start }); + ts_blank_space(context, { + start: param.typeAnnotation.end, + end: node.params[1]?.start || param.end + }); + } else { + ts_blank_space(context, { + start: param.start, + end: node.params[1]?.start || param.end + }); + } + } else if (param.optional) { + const question_start = context.state.ms.original.indexOf('?', param.start); + if (question_start !== -1 && question_start < param.end) { + ts_blank_space(context, { start: question_start, end: question_start + 1 }); + } + } } } return context.next(); @@ -207,8 +215,8 @@ const visitors: Visitors = { ts_blank_space(context, { start: node.start, end: node.expression.start }); context.visit(node.expression); }, - FunctionExpression: remove_this_param, - FunctionDeclaration: remove_this_param, + FunctionExpression: remove_this_param_and_optional, + FunctionDeclaration: remove_this_param_and_optional, TSDeclareFunction(node, context) { ts_blank_space(context, node); return empty; @@ -245,6 +253,16 @@ const visitors: Visitors = { } context.next(); }, + VariableDeclarator(node, context) { + if (node.definite && node.id.type === 'Identifier') { + const definite_start = context.state.ms.original.indexOf( + '!', + node.id.start + node.id.name.length + ); + ts_blank_space(context, { start: definite_start, end: definite_start + 1 }); + } + context.next(); + }, TSModuleDeclaration(node, context) { if (!node.body) { ts_blank_space(context, node); From dd3a7395815678d73e83123879c1168682511794 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 13 Sep 2025 12:09:18 -0700 Subject: [PATCH 19/35] Sync `kit` docs (#1535) sync kit docs Co-authored-by: svelte-docs-bot[bot] <196124396+svelte-docs-bot[bot]@users.noreply.github.com> Co-authored-by: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> --- .../docs/kit/98-reference/10-@sveltejs-kit.md | 364 ++++++++++++++---- 1 file changed, 279 insertions(+), 85 deletions(-) diff --git a/apps/svelte.dev/content/docs/kit/98-reference/10-@sveltejs-kit.md b/apps/svelte.dev/content/docs/kit/98-reference/10-@sveltejs-kit.md index 5b13fb4d7f..70fbba02d2 100644 --- a/apps/svelte.dev/content/docs/kit/98-reference/10-@sveltejs-kit.md +++ b/apps/svelte.dev/content/docs/kit/98-reference/10-@sveltejs-kit.md @@ -494,39 +494,24 @@ The argument passed to [`afterNavigate`](/docs/kit/$app-navigation#afterNavigate
```dts -interface AfterNavigate extends Omit {/*…*/} -``` - -
- -```dts -type: Exclude; -``` - -
- -The type of navigation: -- `enter`: The app has hydrated/started -- `form`: The user submitted a `
` -- `link`: Navigation was triggered by a link click -- `goto`: Navigation was triggered by a `goto(...)` call or a redirect -- `popstate`: Navigation was triggered by back/forward navigation - -
-
- -
- -```dts -willUnload: false; +type AfterNavigate = (Navigation | NavigationEnter) & { + /** + * The type of navigation: + * - `enter`: The app has hydrated/started + * - `form`: The user submitted a `` + * - `link`: Navigation was triggered by a link click + * - `goto`: Navigation was triggered by a `goto(...)` call or a redirect + * - `popstate`: Navigation was triggered by back/forward navigation + */ + type: Exclude; + /** + * Since `afterNavigate` callbacks are called after a navigation completes, they will never be called with a navigation that unloads the page. + */ + willUnload: false; +}; ``` -
- -Since `afterNavigate` callbacks are called after a navigation completes, they will never be called with a navigation that unloads the page. -
-
## AwaitedActions @@ -553,21 +538,15 @@ The argument passed to [`beforeNavigate`](/docs/kit/$app-navigation#beforeNaviga
```dts -interface BeforeNavigate extends Navigation {/*…*/} -``` - -
- -```dts -cancel: () => void; +type BeforeNavigate = Navigation & { + /** + * Call this to prevent the navigation from starting. + */ + cancel: () => void; +}; ``` -
- -Call this to prevent the navigation from starting. -
-
## Builder @@ -1581,7 +1560,21 @@ type LoadProperties<
```dts -interface Navigation {/*…*/} +type Navigation = + | NavigationExternal + | NavigationFormSubmit + | NavigationPopState + | NavigationLink; +``` + +
+ +## NavigationBase + +
+ +```dts +interface NavigationBase {/*…*/} ```
@@ -1613,17 +1606,12 @@ Where navigation is going to/has gone to
```dts -type: Exclude; +willUnload: boolean; ```
-The type of navigation: -- `form`: The user submitted a `` -- `leave`: The app is being left either because the tab is being closed or a navigation to a different document is occurring -- `link`: Navigation was triggered by a link click -- `goto`: Navigation was triggered by a `goto(...)` call or a redirect -- `popstate`: Navigation was triggered by back/forward navigation +Whether or not the navigation will result in the page being unloaded (i.e. not a client-side navigation)
@@ -1631,12 +1619,39 @@ The type of navigation:
```dts -willUnload: boolean; +complete: Promise; ```
-Whether or not the navigation will result in the page being unloaded (i.e. not a client-side navigation) +A promise that resolves once the navigation is complete, and rejects if the navigation +fails or is aborted. In the case of a `willUnload` navigation, the promise will never resolve + +
+
+ +## NavigationEnter + +
+ +```dts +interface NavigationEnter extends NavigationBase {/*…*/} +``` + +
+ +```dts +type: 'enter'; +``` + +
+ +The type of navigation: +- `form`: The user submitted a `` +- `leave`: The app is being left either because the tab is being closed or a navigation to a different document is occurring +- `link`: Navigation was triggered by a link click +- `goto`: Navigation was triggered by a `goto(...)` call or a redirect +- `popstate`: Navigation was triggered by back/forward navigation
@@ -1644,7 +1659,7 @@ Whether or not the navigation will result in the page being unloaded (i.e. not a
```dts -delta?: number; +delta?: undefined; ```
@@ -1657,13 +1672,12 @@ In case of a history back/forward navigation, the number of steps to go back/for
```dts -complete: Promise; +event?: undefined; ```
-A promise that resolves once the navigation is complete, and rejects if the navigation -fails or is aborted. In the case of a `willUnload` navigation, the promise will never resolve +Dispatched `Event` object when navigation occured by `popstate` or `link`.
@@ -1732,6 +1746,201 @@ The URL of the current page
+## NavigationExternal + +
+ +```dts +interface NavigationExternal extends NavigationBase {/*…*/} +``` + +
+ +```dts +type: Exclude; +``` + +
+ +The type of navigation: +- `form`: The user submitted a `` +- `leave`: The app is being left either because the tab is being closed or a navigation to a different document is occurring +- `link`: Navigation was triggered by a link click +- `goto`: Navigation was triggered by a `goto(...)` call or a redirect +- `popstate`: Navigation was triggered by back/forward navigation + +
+
+ +
+ +```dts +delta?: undefined; +``` + +
+ +In case of a history back/forward navigation, the number of steps to go back/forward + +
+
+ +## NavigationFormSubmit + +
+ +```dts +interface NavigationFormSubmit extends NavigationBase {/*…*/} +``` + +
+ +```dts +type: 'form'; +``` + +
+ +The type of navigation: +- `form`: The user submitted a `` +- `leave`: The app is being left either because the tab is being closed or a navigation to a different document is occurring +- `link`: Navigation was triggered by a link click +- `goto`: Navigation was triggered by a `goto(...)` call or a redirect +- `popstate`: Navigation was triggered by back/forward navigation + +
+
+ +
+ +```dts +event: SubmitEvent; +``` + +
+ +The `SubmitEvent` that caused the navigation + +
+
+ +
+ +```dts +delta?: undefined; +``` + +
+ +In case of a history back/forward navigation, the number of steps to go back/forward + +
+
+ +## NavigationLink + +
+ +```dts +interface NavigationLink extends NavigationBase {/*…*/} +``` + +
+ +```dts +type: 'link'; +``` + +
+ +The type of navigation: +- `form`: The user submitted a `` +- `leave`: The app is being left either because the tab is being closed or a navigation to a different document is occurring +- `link`: Navigation was triggered by a link click +- `goto`: Navigation was triggered by a `goto(...)` call or a redirect +- `popstate`: Navigation was triggered by back/forward navigation + +
+
+ +
+ +```dts +event: PointerEvent; +``` + +
+ +The `PointerEvent` that caused the navigation + +
+
+ +
+ +```dts +delta?: undefined; +``` + +
+ +In case of a history back/forward navigation, the number of steps to go back/forward + +
+
+ +## NavigationPopState + +
+ +```dts +interface NavigationPopState extends NavigationBase {/*…*/} +``` + +
+ +```dts +type: 'popstate'; +``` + +
+ +The type of navigation: +- `form`: The user submitted a `` +- `leave`: The app is being left either because the tab is being closed or a navigation to a different document is occurring +- `link`: Navigation was triggered by a link click +- `goto`: Navigation was triggered by a `goto(...)` call or a redirect +- `popstate`: Navigation was triggered by back/forward navigation + +
+
+ +
+ +```dts +delta: number; +``` + +
+ +In case of a history back/forward navigation, the number of steps to go back/forward + +
+
+ +
+ +```dts +event: PopStateEvent; +``` + +
+ +The `PopStateEvent` that caused the navigation + +
+
+ ## NavigationTarget Information about the target of a specific navigation. @@ -1842,38 +2051,23 @@ The argument passed to [`onNavigate`](/docs/kit/$app-navigation#onNavigate) call
```dts -interface OnNavigate extends Navigation {/*…*/} -``` - -
- -```dts -type: Exclude; -``` - -
- -The type of navigation: -- `form`: The user submitted a `` -- `link`: Navigation was triggered by a link click -- `goto`: Navigation was triggered by a `goto(...)` call or a redirect -- `popstate`: Navigation was triggered by back/forward navigation - -
-
- -
- -```dts -willUnload: false; +type OnNavigate = Navigation & { + /** + * The type of navigation: + * - `form`: The user submitted a `` + * - `link`: Navigation was triggered by a link click + * - `goto`: Navigation was triggered by a `goto(...)` call or a redirect + * - `popstate`: Navigation was triggered by back/forward navigation + */ + type: Exclude; + /** + * Since `onNavigate` callbacks are called immediately before a client-side navigation, they will never be called with a navigation that unloads the page. + */ + willUnload: false; +}; ``` -
- -Since `onNavigate` callbacks are called immediately before a client-side navigation, they will never be called with a navigation that unloads the page. -
-
## Page From 5278c3ca688120ce43fb0dfb9dead9ae3ad33969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Fabrycki?= Date: Sat, 13 Sep 2025 23:48:21 +0200 Subject: [PATCH 20/35] fix(llms): preserve newlines in generated `llms-medium.txt` file (#1532) * fix(llms): preserve newlines in generated `llms-medium.txt` file * chore(llms): remove `normalize_whitespace` functionality --- apps/svelte.dev/src/lib/server/llms.ts | 8 +------- apps/svelte.dev/src/routes/llms-medium.txt/+server.ts | 3 +-- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/apps/svelte.dev/src/lib/server/llms.ts b/apps/svelte.dev/src/lib/server/llms.ts index 69ae4fe761..549d39592a 100644 --- a/apps/svelte.dev/src/lib/server/llms.ts +++ b/apps/svelte.dev/src/lib/server/llms.ts @@ -14,7 +14,6 @@ interface MinimizeOptions { remove_details_blocks: boolean; remove_playground_links: boolean; remove_prettier_ignore: boolean; - normalize_whitespace: boolean; } interface Topic { @@ -27,8 +26,7 @@ const defaults: MinimizeOptions = { remove_note_blocks: false, remove_details_blocks: false, remove_playground_links: false, - remove_prettier_ignore: false, - normalize_whitespace: false + remove_prettier_ignore: false }; export function generate_llm_content(options: GenerateLlmContentOptions): string { @@ -105,10 +103,6 @@ function minimize_content(content: string, options?: Partial): .join('\n'); } - if (settings.normalize_whitespace) { - minimized = minimized.replace(/\s+/g, ' '); - } - minimized = minimized.trim(); return minimized; diff --git a/apps/svelte.dev/src/routes/llms-medium.txt/+server.ts b/apps/svelte.dev/src/routes/llms-medium.txt/+server.ts index 12281067e3..600d4709fc 100644 --- a/apps/svelte.dev/src/routes/llms-medium.txt/+server.ts +++ b/apps/svelte.dev/src/routes/llms-medium.txt/+server.ts @@ -29,8 +29,7 @@ export function GET() { remove_note_blocks: true, remove_details_blocks: true, remove_playground_links: true, - remove_prettier_ignore: true, - normalize_whitespace: true + remove_prettier_ignore: true } }); const content = `This is the abridged developer documentation for Svelte and SvelteKit.\n\n${main_content}`; From 359443c15ff7d03d9b7d1155d34901bd5efc4fec Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Mon, 15 Sep 2025 13:30:18 +0200 Subject: [PATCH 21/35] fix: better "bundling aborted" check error object might be augmented, see https://github.com/rollup/rollup/blob/76a3b8ede4729a71eb522fc29f7d550a4358827b/docs/plugin-development/index.md#thiserror, hence only check that the specific abort property we set is there --- packages/repl/src/lib/workers/bundler/index.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/repl/src/lib/workers/bundler/index.ts b/packages/repl/src/lib/workers/bundler/index.ts index 6e84324a03..1a35db2918 100644 --- a/packages/repl/src/lib/workers/bundler/index.ts +++ b/packages/repl/src/lib/workers/bundler/index.ts @@ -76,7 +76,11 @@ self.addEventListener('message', async (event: MessageEvent) console.log('[bundle worker result]', result); - if (JSON.stringify(result.error) === JSON.stringify(ABORT)) return; + // error object might be augmented, see https://github.com/rollup/rollup/blob/76a3b8ede4729a71eb522fc29f7d550a4358827b/docs/plugin-development/index.md#thiserror, + // hence only check that the specific abort property we set is there + if ((result.error as any)?.svelte_bundler_aborted === ABORT.svelte_bundler_aborted) { + return; + } if (result && uid === current_id) postMessage(result); }); } catch (e) { @@ -111,7 +115,7 @@ function get_svelte(svelte_version: string) { return ready; } -const ABORT = { aborted: true }; +const ABORT = { svelte_bundler_aborted: true }; let previous: { key: string; From ce13069ccb2619bfba1b3ab2cc2b48da5349a8ff Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Mon, 15 Sep 2025 14:07:19 +0200 Subject: [PATCH 22/35] fix: prevent duplicate bundles - on startup we had two requests each time due to the svelte version changes in #1319, and the file was always marked as changed. Fixed by not notifying when the version info comes from the bundle worker - migrate had two bundle requests, one from the direct `rebundle()` call, the second one because `update_file` invoked `on_update` that also invoked `rebundle()` -> remove the direct invocation - `reset` always did two request, because `this.set` already calls `#onreset` -> deduplicate --- .../repl/src/lib/Input/ComponentSelector.svelte | 2 +- packages/repl/src/lib/Repl.svelte | 4 +--- packages/repl/src/lib/Workspace.svelte.ts | 13 +++++++------ 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/repl/src/lib/Input/ComponentSelector.svelte b/packages/repl/src/lib/Input/ComponentSelector.svelte index 5f93f255b4..812685e5a5 100644 --- a/packages/repl/src/lib/Input/ComponentSelector.svelte +++ b/packages/repl/src/lib/Input/ComponentSelector.svelte @@ -182,7 +182,7 @@ (workspace.svelte_version = ev.currentTarget.value || 'latest')} + onchange={(ev) => workspace.set_svelte_version(ev.currentTarget.value || 'latest', true)} /> diff --git a/packages/repl/src/lib/Repl.svelte b/packages/repl/src/lib/Repl.svelte index 4565c80f82..bca019c8cf 100644 --- a/packages/repl/src/lib/Repl.svelte +++ b/packages/repl/src/lib/Repl.svelte @@ -127,8 +127,6 @@ ...workspace.current!, contents: migration!.code }); - - rebundle(); } let width = $state(0); @@ -142,7 +140,7 @@ ? new Bundler({ svelte_version: svelteVersion, onversion: (version) => { - workspace.svelte_version = version; + workspace.set_svelte_version(version); onversion?.(version); }, onstatus: (message) => { diff --git a/packages/repl/src/lib/Workspace.svelte.ts b/packages/repl/src/lib/Workspace.svelte.ts index dd89b8460c..aae450b6c2 100644 --- a/packages/repl/src/lib/Workspace.svelte.ts +++ b/packages/repl/src/lib/Workspace.svelte.ts @@ -407,14 +407,13 @@ export class Workspace { selected?: string ) { this.states.clear(); - this.set(new_files, selected); + const bundle = this.set(new_files, selected); this.mark_saved(); this.#tailwind = options.tailwind; this.#aliases = options.aliases; - const bundle = this.#onreset(new_files); const diagnostics = this.#reset_diagnostics(); return Promise.all([bundle, diagnostics]) @@ -458,7 +457,7 @@ export class Workspace { } } - this.#onreset?.(this.files); + return this.#onreset?.(this.files); } unlink(view: EditorView) { @@ -507,10 +506,12 @@ export class Workspace { return this.#svelte_version; } - set svelte_version(value) { + set_svelte_version(value, notify = false) { this.#svelte_version = value; - this.#update_file(this.#current); - this.#reset_diagnostics(); + if (notify) { + this.#update_file(this.#current); + this.#reset_diagnostics(); + } } get vim() { From a08675545712701bf25f19f509884a3f49feb281 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Mon, 15 Sep 2025 14:27:19 +0200 Subject: [PATCH 23/35] fix: type check --- packages/repl/src/lib/Workspace.svelte.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/repl/src/lib/Workspace.svelte.ts b/packages/repl/src/lib/Workspace.svelte.ts index aae450b6c2..ee06862519 100644 --- a/packages/repl/src/lib/Workspace.svelte.ts +++ b/packages/repl/src/lib/Workspace.svelte.ts @@ -506,7 +506,7 @@ export class Workspace { return this.#svelte_version; } - set_svelte_version(value, notify = false) { + set_svelte_version(value: string, notify = false) { this.#svelte_version = value; if (notify) { this.#update_file(this.#current); From 23c36e26cc675f26957c33415c1bdf219ad703a1 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Mon, 15 Sep 2025 23:58:07 +0200 Subject: [PATCH 24/35] fix: ensure reset sets option before bundling --- packages/repl/src/lib/Workspace.svelte.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/repl/src/lib/Workspace.svelte.ts b/packages/repl/src/lib/Workspace.svelte.ts index ee06862519..a0a027778c 100644 --- a/packages/repl/src/lib/Workspace.svelte.ts +++ b/packages/repl/src/lib/Workspace.svelte.ts @@ -407,13 +407,13 @@ export class Workspace { selected?: string ) { this.states.clear(); + this.#tailwind = options.tailwind; + this.#aliases = options.aliases; + const bundle = this.set(new_files, selected); this.mark_saved(); - this.#tailwind = options.tailwind; - this.#aliases = options.aliases; - const diagnostics = this.#reset_diagnostics(); return Promise.all([bundle, diagnostics]) From 7685fb8faf03476934d6aee57c66d266a38f768e Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Tue, 16 Sep 2025 01:26:15 +0200 Subject: [PATCH 25/35] fix: better commonjs support walk children of assignment expressions as they could have dynamic requires Should help with https://github.com/techniq/layerchart/issues/512 --- packages/repl/src/lib/workers/bundler/plugins/commonjs.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/repl/src/lib/workers/bundler/plugins/commonjs.ts b/packages/repl/src/lib/workers/bundler/plugins/commonjs.ts index 20d569c945..7840688f59 100644 --- a/packages/repl/src/lib/workers/bundler/plugins/commonjs.ts +++ b/packages/repl/src/lib/workers/bundler/plugins/commonjs.ts @@ -42,6 +42,9 @@ const plugin: Plugin = { context.next(); }, AssignmentExpression: (node, context) => { + // walk children to find nested requires + context.next(); + if (node.operator !== '=') return; if (node.left.type !== 'MemberExpression') return; if (node.left.object.type !== 'Identifier' || node.left.object.name !== 'exports') return; @@ -53,8 +56,6 @@ const plugin: Plugin = { `export const ${node.left.property.name} = module.exports.${node.left.property.name};` ); } - - context.next(); } }); From 3a10751aaeebf0e99724639c55bcb331b9221713 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 21:50:07 -0400 Subject: [PATCH 26/35] Sync `kit` docs (#1538) sync kit docs Co-authored-by: svelte-docs-bot[bot] <196124396+svelte-docs-bot[bot]@users.noreply.github.com> --- .../content/docs/kit/10-getting-started/30-project-structure.md | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/svelte.dev/content/docs/kit/10-getting-started/30-project-structure.md b/apps/svelte.dev/content/docs/kit/10-getting-started/30-project-structure.md index 345b9efe7d..4795c42ff5 100644 --- a/apps/svelte.dev/content/docs/kit/10-getting-started/30-project-structure.md +++ b/apps/svelte.dev/content/docs/kit/10-getting-started/30-project-structure.md @@ -53,6 +53,7 @@ The `src` directory contains the meat of your project. Everything except `src/ro - `error.html` is the page that is rendered when everything else fails. It can contain the following placeholders: - `%sveltekit.status%` — the HTTP status - `%sveltekit.error.message%` — the error message + - `%sveltekit.version%` — the deployment version, which can be specified with the [`version`](configuration#version) configuration - `hooks.client.js` contains your client [hooks](hooks) - `hooks.server.js` contains your server [hooks](hooks) - `service-worker.js` contains your [service worker](service-workers) From 3395b3949e25b39cd5b84c162564fa5b5a9641d7 Mon Sep 17 00:00:00 2001 From: "jyc.dev" Date: Tue, 16 Sep 2025 13:44:15 +0200 Subject: [PATCH 27/35] feat: add new cmd in the Playground `sv create --from-playground` (#1537) * feat: add new cmd in the Playground `sv create --from-playground` * css > alert * title & aria-label Co-authored-by: Rich Harris * button text Co-authored-by: Rich Harris * remove implicit size Co-authored-by: Rich Harris * position & mask Co-authored-by: Rich Harris --------- Co-authored-by: Rich Harris --- .../src/lib/Input/ComponentSelector.svelte | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/packages/repl/src/lib/Input/ComponentSelector.svelte b/packages/repl/src/lib/Input/ComponentSelector.svelte index 812685e5a5..cc01153ecb 100644 --- a/packages/repl/src/lib/Input/ComponentSelector.svelte +++ b/packages/repl/src/lib/Input/ComponentSelector.svelte @@ -189,6 +189,19 @@ {#if download} {/if} + + @@ -360,4 +373,42 @@ stroke-linejoin: round; fill: none; } + + .copy-button { + position: relative; + + &::before, + &::after { + content: ''; + display: block; + position: absolute; + width: 100%; + height: 100%; + right: 0; + top: 0; + background: currentColor; + mask: no-repeat calc(100% - 1rem) 50% / 1.6rem 1.6rem; + transition: opacity 0.2s; + transition-delay: 0.6s; + } + + &::before { + mask-image: url(icons/copy-to-clipboard); + } + + &::after { + mask-image: url(icons/check); + opacity: 0; + } + + &:active::before { + opacity: 0; + transition: none; + } + + &:active::after { + opacity: 1; + transition: none; + } + } From 3ec9a05eb9a6f30981e612817aa203cc6ab9ef73 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 16 Sep 2025 18:19:41 -0400 Subject: [PATCH 28/35] chore: bump kit (#1540) * chore: bump kit * Sync `kit` docs (#1539) sync kit docs Co-authored-by: svelte-docs-bot[bot] <196124396+svelte-docs-bot[bot]@users.noreply.github.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: svelte-docs-bot[bot] <196124396+svelte-docs-bot[bot]@users.noreply.github.com> --- .../20-core-concepts/60-remote-functions.md | 282 +++++++++++++++--- .../docs/kit/98-reference/10-@sveltejs-kit.md | 105 ++++++- .../docs/kit/98-reference/20-$app-server.md | 36 ++- apps/svelte.dev/package.json | 2 +- pnpm-lock.yaml | 28 +- 5 files changed, 376 insertions(+), 77 deletions(-) diff --git a/apps/svelte.dev/content/docs/kit/20-core-concepts/60-remote-functions.md b/apps/svelte.dev/content/docs/kit/20-core-concepts/60-remote-functions.md index 2d6364e690..ae9fd05324 100644 --- a/apps/svelte.dev/content/docs/kit/20-core-concepts/60-remote-functions.md +++ b/apps/svelte.dev/content/docs/kit/20-core-concepts/60-remote-functions.md @@ -228,7 +228,7 @@ export const getWeather = query.batch(v.string(), async (cities) => { ## form -The `form` function makes it easy to write data to the server. It takes a callback that receives the current [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)... +The `form` function makes it easy to write data to the server. It takes a callback that receives `data` constructed from the submitted [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)... ```ts @@ -260,30 +260,28 @@ export const getPosts = query(async () => { /* ... */ }); export const getPost = query(v.string(), async (slug) => { /* ... */ }); -export const createPost = form(async (data) => { - // Check the user is logged in - const user = await auth.getUser(); - if (!user) error(401, 'Unauthorized'); - - const title = data.get('title'); - const content = data.get('content'); - - // Check the data is valid - if (typeof title !== 'string' || typeof content !== 'string') { - error(400, 'Title and content are required'); +export const createPost = form( + v.object({ + title: v.pipe(v.string(), v.nonEmpty()), + content:v.pipe(v.string(), v.nonEmpty()) + }), + async ({ title, content }) => { + // Check the user is logged in + const user = await auth.getUser(); + if (!user) error(401, 'Unauthorized'); + + const slug = title.toLowerCase().replace(/ /g, '-'); + + // Insert into the database + await db.sql` + INSERT INTO post (slug, title, content) + VALUES (${slug}, ${title}, ${content}) + `; + + // Redirect to the newly created page + redirect(303, `/blog/${slug}`); } - - const slug = title.toLowerCase().replace(/ /g, '-'); - - // Insert into the database - await db.sql` - INSERT INTO post (slug, title, content) - VALUES (${slug}, ${title}, ${content}) - `; - - // Redirect to the newly created page - redirect(303, `/blog/${slug}`); -}); +); ``` ...and returns an object that can be spread onto a `` element. The callback is called whenever the form is submitted. @@ -311,7 +309,184 @@ export const createPost = form(async (data) => { ``` -The form object contains `method` and `action` properties that allow it to work without JavaScript (i.e. it submits data and reloads the page). It also has an `onsubmit` handler that progressively enhances the form when JavaScript is available, submitting data *without* reloading the entire page. +As with `query`, if the callback uses the submitted `data`, it should be [validated](#query-Query-arguments) by passing a [Standard Schema](https://standardschema.dev) as the first argument to `form`. The one difference is to `query` is that the schema inputs must all be of type `string` or `File`, since that's all the original `FormData` provides. You can however coerce the value into a different type — how to do that depends on the validation library you use. + +```ts +/// file: src/routes/count.remote.js +import * as v from 'valibot'; +import { form } from '$app/server'; + +export const setCount = form( + v.object({ + // Valibot: + count: v.pipe(v.string(), v.transform((s) => Number(s)), v.number()), + // Zod: + // count: v.coerce.number() + }), + async ({ count }) => { + // ... + } +); +``` + +The `name` attributes on the form controls must correspond to the properties of the schema — `title` and `content` in this case. If you schema contains objects, use object notation: + +```svelte + + + +{#each jobs as job, idx} + + +{/each} +``` + +To indicate a repeated field, use a `[]` suffix: + +```svelte + + + +``` + +If you'd like type safety and autocomplete when setting `name` attributes, use the form object's `field` method: + +```svelte + +``` + +This will error during typechecking if `title` does not exist on your schema. + +The form object contains `method` and `action` properties that allow it to work without JavaScript (i.e. it submits data and reloads the page). It also has an [attachment](/docs/svelte/@attach) that progressively enhances the form when JavaScript is available, submitting data *without* reloading the entire page. + +### Validation + +If the submitted data doesn't pass the schema, the callback will not run. Instead, the form object's `issues` object will be populated: + +```svelte +
+ + + + + +
+``` + +You don't need to wait until the form is submitted to validate the data — you can call `validate()` programmatically, for example in an `oninput` callback (which will validate the data on every keystroke) or an `onchange` callback: + +```svelte +
createPost.validate()}> + +
+``` + +By default, issues will be ignored if they belong to form controls that haven't yet been interacted with. To validate _all_ inputs, call `validate({ includeUntouched: true })`. + +For client-side validation, you can specify a _preflight_ schema which will populate `issues` and prevent data being sent to the server if the data doesn't validate: + +```svelte + + +

Create a new post

+ +
+ +
+``` + +> [!NOTE] The preflight schema can be the same object as your server-side schema, if appropriate, though it won't be able to do server-side checks like 'this value already exists in the database'. Note that you cannot export a schema from a `.remote.ts` or `.remote.js` file, so the schema must either be exported from a shared module, or from a ` {#snippet main()} - + {#key iframe_key} + + {/key}
{#if bundle?.error} From e616f941cb69259a53b0384b6402bc296fb38082 Mon Sep 17 00:00:00 2001 From: 7nik Date: Thu, 18 Sep 2025 13:05:05 +0300 Subject: [PATCH 34/35] fix lint --- packages/repl/src/lib/Output/Viewer.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/repl/src/lib/Output/Viewer.svelte b/packages/repl/src/lib/Output/Viewer.svelte index 1eef726e78..6e75e9b10e 100644 --- a/packages/repl/src/lib/Output/Viewer.svelte +++ b/packages/repl/src/lib/Output/Viewer.svelte @@ -112,7 +112,7 @@ }); $effect(() => { - proxy.iframe = iframe; + proxy!.iframe = iframe; iframe!.addEventListener('load', () => { proxy?.handle_links(); ready = true; From ee31a1792a5f0d2ea9805dcec827e8ea346fefd7 Mon Sep 17 00:00:00 2001 From: 7nik Date: Thu, 18 Sep 2025 13:07:27 +0300 Subject: [PATCH 35/35] fix check --- packages/repl/src/lib/Output/Viewer.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/repl/src/lib/Output/Viewer.svelte b/packages/repl/src/lib/Output/Viewer.svelte index 6e75e9b10e..ff08a8afe7 100644 --- a/packages/repl/src/lib/Output/Viewer.svelte +++ b/packages/repl/src/lib/Output/Viewer.svelte @@ -112,7 +112,7 @@ }); $effect(() => { - proxy!.iframe = iframe; + proxy!.iframe = iframe!; iframe!.addEventListener('load', () => { proxy?.handle_links(); ready = true;