From 32d51bd52fdc413253ae58e93eda801fe8d50c71 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 30 Apr 2026 15:16:29 -0400 Subject: [PATCH 1/7] fix: get twoslash to work properly with JSDoc --- packages/site-kit/src/lib/docs/Tooltip.svelte | 6 +---- .../site-kit/src/lib/markdown/renderer.ts | 26 ++++++++++++++----- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/packages/site-kit/src/lib/docs/Tooltip.svelte b/packages/site-kit/src/lib/docs/Tooltip.svelte index 42cb9ac555..1b58d3aeee 100644 --- a/packages/site-kit/src/lib/docs/Tooltip.svelte +++ b/packages/site-kit/src/lib/docs/Tooltip.svelte @@ -95,7 +95,7 @@ font: var(--sk-font-body-small); } - .tags { + .twoslash-popup-docs-tags { display: grid; grid-template-columns: auto 1fr; column-gap: 1rem; @@ -105,10 +105,6 @@ .param { font: var(--sk-font-mono); } - - .tag { - min-width: 8rem; - } } /* Disable highlight styles inside tooltips (popup content) */ diff --git a/packages/site-kit/src/lib/markdown/renderer.ts b/packages/site-kit/src/lib/markdown/renderer.ts index e3ba7a4a58..3df1430dfb 100644 --- a/packages/site-kit/src/lib/markdown/renderer.ts +++ b/packages/site-kit/src/lib/markdown/renderer.ts @@ -449,7 +449,12 @@ export async function render_content_markdown( [, prelude = '// ---cut---\n', source] = match; const banner = twoslashBanner?.(filename, source); - if (banner) prelude = '// @filename: injected.d.ts\n' + banner + '\n' + prelude; + if (banner) + prelude = + '// @filename: injected.d.ts\n' + + banner + + (options.file ? `\n// @filename: ${options.file.split('/').pop()}\n` : '\n') + + prelude; } source = source.replace( @@ -1206,7 +1211,17 @@ async function syntax_highlight({ for (const match of html.matchAll( /([^]+?)<\/span>([^]+?)<\/span><\/span>/g )) { + const start = match.index; + const end = match.index + match[0].length; + const tag = match[1]; + + if (tag === '@type') { + // remove `@type` tags altogether + replacements.push({ start, end, content: '' }); + continue; + } + let value = match[2]; let content = `
${tag}
`; @@ -1234,11 +1249,7 @@ async function syntax_highlight({ content += '
'; - replacements.push({ - start: match.index, - end: match.index + match[0].length, - content: '
' + content + '
' - }); + replacements.push({ start, end, content }); } while (replacements.length > 0) { @@ -1246,6 +1257,9 @@ async function syntax_highlight({ html = html.slice(0, start) + content + html.slice(end); } + // if no tags, remove this
to avoid an unnecessary flex gap + html = html.replace('
', ''); + html = injectReferenceLinks(html, references, extractImportedSymbols(source)); } } catch (e) { From d4d9c449c8302943a8222d19055c5a61425a9f76 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 30 Apr 2026 15:42:15 -0400 Subject: [PATCH 2/7] fix --- .../blog/2023-03-09-zero-config-type-safety.md | 13 +++++++++++++ apps/svelte.dev/src/lib/server/renderer.ts | 2 ++ 2 files changed, 15 insertions(+) diff --git a/apps/svelte.dev/content/blog/2023-03-09-zero-config-type-safety.md b/apps/svelte.dev/content/blog/2023-03-09-zero-config-type-safety.md index caef68418d..30f49326dd 100644 --- a/apps/svelte.dev/content/blog/2023-03-09-zero-config-type-safety.md +++ b/apps/svelte.dev/content/blog/2023-03-09-zero-config-type-safety.md @@ -44,6 +44,12 @@ This is where our automatic type generation comes in. Every route directory has ```js /// file: src/routes/blog/[slug]/+page.server.ts +const database = { + getPost(slug: string | undefined): Promise { + return Promise.resolve('hello world'); + } +}; +// ---cut--- ---import type { ServerLoadEvent } from '@sveltejs/kit';--- +++import type { PageServerLoadEvent } from './$types';+++ @@ -133,7 +139,14 @@ src/ Thanks to the automatic type generation we get advanced type safety. Wouldn't it be great though if we could just omit writing the types at all? As of today you can do exactly that: ```js +// @errors: 7006 /// file: src/routes/blog/[slug]/+page.server.ts +const database = { + getPost(slug: string | undefined): Promise { + return Promise.resolve('hello world'); + } +}; +// ---cut--- ---import type { PageServerLoadEvent } from './$types';--- export async function load(event---: PageServerLoadEvent---) { diff --git a/apps/svelte.dev/src/lib/server/renderer.ts b/apps/svelte.dev/src/lib/server/renderer.ts index 59b4373126..2811753147 100644 --- a/apps/svelte.dev/src/lib/server/renderer.ts +++ b/apps/svelte.dev/src/lib/server/renderer.ts @@ -56,8 +56,10 @@ export const render_content = ( `import type * as Kit from '@sveltejs/kit';`, `export type PageLoad = Kit.Load>;`, `export type PageServerLoad = Kit.ServerLoad>;`, + `export type PageServerLoadEvent = Parameters[0];`, `export type LayoutLoad = Kit.Load>;`, `export type LayoutServerLoad = Kit.ServerLoad>;`, + `export type LayoutServerLoadEvent = Parameters[0];`, `export type RequestHandler = Kit.RequestHandler>;`, `export type Action = Kit.Action>;`, `export type Actions = Kit.Actions>;`, From 0a9d2d1aadf3f2c8ec47b570149748cc28590cf6 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 30 Apr 2026 15:49:32 -0400 Subject: [PATCH 3/7] fix redactions --- packages/site-kit/src/lib/markdown/renderer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/site-kit/src/lib/markdown/renderer.ts b/packages/site-kit/src/lib/markdown/renderer.ts index 3df1430dfb..bf5fee6978 100644 --- a/packages/site-kit/src/lib/markdown/renderer.ts +++ b/packages/site-kit/src/lib/markdown/renderer.ts @@ -1135,7 +1135,7 @@ async function syntax_highlight({ const redactions: string[] = []; const sub = delimiter_substitutes['---']; - const pattern = new RegExp(`${sub}(?:[^ ]|[^ ][^]+?[^ ])${sub}`, 'g'); + const pattern = new RegExp(`${sub}([^ ]|[^ ][^]+?[^ ])${sub}`, 'g'); const redacted = source.replace(pattern, (_, content) => { redactions.push(content); @@ -1171,7 +1171,7 @@ async function syntax_highlight({ html = html.replace( new RegExp(` {${delimiter_substitutes['---'].length + 1},}`, 'g'), - () => redactions.shift()! + () => `${redactions.shift()!}` ); if (check) { From f1ffacd37cebf91cbaef2f54cde6388e08f7497d Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 30 Apr 2026 16:00:30 -0400 Subject: [PATCH 4/7] fixes --- .../20-core-concepts/60-remote-functions.md | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 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 aa6f0c83cb..d87f175e0a 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 @@ -524,7 +524,14 @@ In addition to declarative schema validation, you can programmatically mark fiel - It accepts multiple arguments that can be strings (for issues relating to the form as a whole — these will only show up in `fields.allIssues()`) or standard-schema-compliant issues (for those relating to a specific field). Use the `issue` parameter for type-safe creation of such issues: ```js +// @errors: 18046 /// file: src/routes/shop/data.remote.js +// @filename: ambient.d.ts +declare module '$lib/server/database' { + export function buy(qty: number): Promise +} +// @filename: index.js +// ---cut--- import * as v from 'valibot'; import { invalid } from '@sveltejs/kit'; import { form } from '$app/server'; @@ -1166,7 +1173,7 @@ As long as _you're_ not passing invalid data to your remote functions, there are In the second case, we don't want to give the attacker any help, so SvelteKit will generate a generic [400 Bad Request](https://http.dog/400) response. You can control the message by implementing the [`handleValidationError`](hooks#Server-hooks-handleValidationError) server hook, which, like [`handleError`](hooks#Shared-hooks-handleError), must return an [`App.Error`](errors#Type-safety) (which defaults to `{ message: string }`): ```js -/// file: src/hooks.server.ts +/// file: src/hooks.server.js /** @type {import('@sveltejs/kit').HandleValidationError} */ export function handleValidationError({ event, issues }) { return { @@ -1193,13 +1200,25 @@ Inside `query`, `form` and `command` you can use [`getRequestEvent`]($app-server ```ts /// file: user.remote.ts +// @filename: ambient.d.ts +interface User { + name: string; + avatar: string; +} + +declare module '$lib/server/database' { + export function findUser(sessionId: string | undefined): Promise; +} + +// @filename: index.js +// ---cut--- import { getRequestEvent, query } from '$app/server'; import { findUser } from '$lib/server/database'; export const getProfile = query(async () => { const user = await getUser(); - return { + return user && { name: user.name, avatar: user.avatar }; From 5d657ca119747d546b2aa19fe0d1c98d8e2fc849 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 30 Apr 2026 16:23:38 -0400 Subject: [PATCH 5/7] various annoying fixes --- .../docs/kit/30-advanced/68-observability.md | 3 +- .../content/docs/svelte/07-misc/02-testing.md | 36 +++++++++++++++++-- apps/svelte.dev/src/lib/server/renderer.ts | 2 +- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/apps/svelte.dev/content/docs/kit/30-advanced/68-observability.md b/apps/svelte.dev/content/docs/kit/30-advanced/68-observability.md index 24b41f3b80..cddc4ce5c9 100644 --- a/apps/svelte.dev/content/docs/kit/30-advanced/68-observability.md +++ b/apps/svelte.dev/content/docs/kit/30-advanced/68-observability.md @@ -75,6 +75,7 @@ To view your first trace, you'll need to set up a local collector. We'll use [Ja - Create `src/instrumentation.server.js` with the following: ```js +// @errors: 2307 /// file: src/instrumentation.server.js import { NodeSDK } from '@opentelemetry/sdk-node'; import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'; @@ -98,4 +99,4 @@ Now, server-side requests will begin generating traces, which you can view in Ja ## `@opentelemetry/api` -SvelteKit uses `@opentelemetry/api` to generate its spans. This is declared as an optional peer dependency so that users not needing traces see no impact on install size or runtime performance. In most cases, if you're configuring your application to collect SvelteKit's spans, you'll end up installing a library like `@opentelemetry/sdk-node` or `@vercel/otel`, which in turn depend on `@opentelemetry/api`, which will satisfy SvelteKit's dependency as well. If you see an error from SvelteKit telling you it can't find `@opentelemetry/api`, it may just be because you haven't set up your trace collection yet. If you _have_ done that and are still seeing the error, you can install `@opentelemetry/api` yourself. +SvelteKit uses `@opentelemetry/api` to generate its spans. This is declared as an optional peer dependency so that users not needing traces see no impact on install size or runtime performance. In most cases, if you're configuring your application to collect SvelteKit's spans, you'll end up installing a library like `@opentelemetry/sdk-node` or `@vercel/otel`, which in turn depend on `@opentelemetry/api`, which will satisfy SvelteKit's dependency as well. If you see an error from SvelteKit telling you it can't find `@opentelemetry/api`, it may just be because you haven't set up your trace collection yet. If you _have_ done that and are still seeing the error, you can install `@opentelemetry/api` yourself. 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 45537686f9..756b6267a1 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 @@ -39,6 +39,21 @@ You can now write unit tests for code inside your `.js/.ts` files: ```js /// file: multiplier.svelte.test.js +// @filename: multiplier.svelte.ts +export function multiplier(initial: number, k: number) { + let count = $state(initial); + + return { + get value() { + return count * k; + }, + set: (c: number) => { + count = c; + } + }; +} +// @filename: multiplier.svelte.test.js +// ---cut--- import { flushSync } from 'svelte'; import { expect, test } from 'vitest'; import { multiplier } from './multiplier.svelte.js'; @@ -81,6 +96,16 @@ Since Vitest processes your test files the same way as your source files, you ca ```js /// file: multiplier.svelte.test.js +// @filename: multiplier.svelte.ts +export function multiplier(getCount: () => number, k: number) { + return { + get value() { + return getCount() * k; + } + }; +} +// @filename: multiplier.svelte.test.js +// ---cut--- import { flushSync } from 'svelte'; import { expect, test } from 'vitest'; import { multiplier } from './multiplier.svelte.js'; @@ -116,6 +141,10 @@ If the code being tested uses effects, you need to wrap the test inside `$effect ```js /// file: logger.svelte.test.js +// @filename: logger.svelte.ts +export function logger(fn: () => void) {} +// @filename: logger.svelte.test.js +// ---cut--- import { flushSync } from 'svelte'; import { expect, test } from 'vitest'; import { logger } from './logger.svelte.js'; @@ -214,7 +243,7 @@ test('Component', () => { expect(document.body.innerHTML).toBe(''); // Click the button, then flush the changes so you can synchronously write expectations - document.body.querySelector('button').click(); + document.body.querySelector('button')?.click(); flushSync(); expect(document.body.innerHTML).toBe(''); @@ -227,6 +256,7 @@ test('Component', () => { While the process is very straightforward, it is also low level and somewhat brittle, as the precise structure of your component may change frequently. Tools like [@testing-library/svelte](https://testing-library.com/docs/svelte-testing-library/intro/) can help streamline your tests. The above test could be rewritten like this: ```js +// @errors: 2339 /// file: component.test.js import { render, screen } from '@testing-library/svelte'; import userEvent from '@testing-library/user-event'; @@ -271,9 +301,9 @@ You can create stories for component variations and test interactions with the [ } }); - + - + { diff --git a/apps/svelte.dev/src/lib/server/renderer.ts b/apps/svelte.dev/src/lib/server/renderer.ts index 2811753147..e286623e3f 100644 --- a/apps/svelte.dev/src/lib/server/renderer.ts +++ b/apps/svelte.dev/src/lib/server/renderer.ts @@ -45,7 +45,7 @@ export const render_content = ( injected.push( `declare module '$env/dynamic/private' { export const env: Record }`, `declare module '$env/dynamic/public' { export const env: Record }`, - `declare module '$env/static/private' { export const API_KEY: string }`, + `declare module '$env/static/private' { export const API_KEY: string; export const VERCEL_COMMIT_REF: string }`, `declare module '$env/static/public' { export const PUBLIC_BASE_URL: string }` ); } From fafe9832043cf8834730afd93e3864155b0b91c4 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 30 Apr 2026 16:30:39 -0400 Subject: [PATCH 6/7] more --- .../docs/kit/25-build-and-deploy/50-adapter-static.md | 6 ++++-- .../docs/kit/25-build-and-deploy/55-single-page-apps.md | 1 + .../docs/kit/25-build-and-deploy/60-adapter-cloudflare.md | 1 + .../docs/kit/25-build-and-deploy/80-adapter-netlify.md | 1 + 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/svelte.dev/content/docs/kit/25-build-and-deploy/50-adapter-static.md b/apps/svelte.dev/content/docs/kit/25-build-and-deploy/50-adapter-static.md index b148f3576e..703964d277 100644 --- a/apps/svelte.dev/content/docs/kit/25-build-and-deploy/50-adapter-static.md +++ b/apps/svelte.dev/content/docs/kit/25-build-and-deploy/50-adapter-static.md @@ -12,6 +12,7 @@ This will prerender your entire site as a collection of static files. If you'd l Install with `npm i -D @sveltejs/adapter-static`, then add the adapter to your `svelte.config.js`: ```js +// @errors: 2307 /// file: svelte.config.js import adapter from '@sveltejs/adapter-static'; @@ -56,6 +57,7 @@ Some platforms have zero-config support (more to come in future): On these platforms, you should omit the adapter options so that `adapter-static` can provide the optimal configuration: ```js +// @errors: 2307 /// file: svelte.config.js import adapter from '@sveltejs/adapter-static'; @@ -81,7 +83,7 @@ The directory to write static assets (the contents of `static`, plus client-side ### fallback -To create a [single page app (SPA)](single-page-apps) you must specify the name of the fallback page to be generated by SvelteKit, which is used as the entry point for URLs that have not been prerendered. This is commonly `200.html`, but can vary depending on your deployment platform. You should avoid `index.html` where possible to avoid conflicting with a prerendered homepage. +To create a [single page app (SPA)](single-page-apps) you must specify the name of the fallback page to be generated by SvelteKit, which is used as the entry point for URLs that have not been prerendered. This is commonly `200.html`, but can vary depending on your deployment platform. You should avoid `index.html` where possible to avoid conflicting with a prerendered homepage. > This option has large negative performance and SEO impacts. It is only recommended in certain circumstances such as wrapping the site in a mobile app. See the [single page apps](single-page-apps) documentation for more details and alternatives. @@ -102,7 +104,7 @@ You'll also want to generate a fallback `404.html` page to replace the default 4 A config for GitHub Pages might look like the following: ```js -// @errors: 2322 +// @errors: 2307 /// file: svelte.config.js import adapter from '@sveltejs/adapter-static'; diff --git a/apps/svelte.dev/content/docs/kit/25-build-and-deploy/55-single-page-apps.md b/apps/svelte.dev/content/docs/kit/25-build-and-deploy/55-single-page-apps.md index 4210a1b5ed..b8e7d5a226 100644 --- a/apps/svelte.dev/content/docs/kit/25-build-and-deploy/55-single-page-apps.md +++ b/apps/svelte.dev/content/docs/kit/25-build-and-deploy/55-single-page-apps.md @@ -20,6 +20,7 @@ export const ssr = false; If you don't have any server-side logic (i.e. `+page.server.js`, `+layout.server.js` or `+server.js` files) you can use [`adapter-static`](adapter-static) to create your SPA. Install `adapter-static` with `npm i -D @sveltejs/adapter-static` and add it to your `svelte.config.js` with the `fallback` option: ```js +// @errors: 2307 /// file: svelte.config.js import adapter from '@sveltejs/adapter-static'; diff --git a/apps/svelte.dev/content/docs/kit/25-build-and-deploy/60-adapter-cloudflare.md b/apps/svelte.dev/content/docs/kit/25-build-and-deploy/60-adapter-cloudflare.md index 27eef1690a..eb8882852d 100644 --- a/apps/svelte.dev/content/docs/kit/25-build-and-deploy/60-adapter-cloudflare.md +++ b/apps/svelte.dev/content/docs/kit/25-build-and-deploy/60-adapter-cloudflare.md @@ -18,6 +18,7 @@ This adapter will be installed by default when you use [`adapter-auto`](adapter- Install with `npm i -D @sveltejs/adapter-cloudflare`, then add the adapter to your `svelte.config.js`: ```js +// @errors: 2307 /// file: svelte.config.js import adapter from '@sveltejs/adapter-cloudflare'; 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 68befc3715..d91dd80951 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 @@ -12,6 +12,7 @@ This adapter will be installed by default when you use [`adapter-auto`](adapter- Install with `npm i -D @sveltejs/adapter-netlify`, then add the adapter to your `svelte.config.js`: ```js +// @errors: 2307 /// file: svelte.config.js import adapter from '@sveltejs/adapter-netlify'; From 2db3164aee4564049fb422086bf7b9033be73eea Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 30 Apr 2026 16:45:42 -0400 Subject: [PATCH 7/7] more --- .../docs/kit/25-build-and-deploy/50-adapter-static.md | 2 +- .../docs/kit/25-build-and-deploy/80-adapter-netlify.md | 1 + .../docs/kit/40-best-practices/10-accessibility.md | 9 +++++---- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/svelte.dev/content/docs/kit/25-build-and-deploy/50-adapter-static.md b/apps/svelte.dev/content/docs/kit/25-build-and-deploy/50-adapter-static.md index 703964d277..9ff6db32dd 100644 --- a/apps/svelte.dev/content/docs/kit/25-build-and-deploy/50-adapter-static.md +++ b/apps/svelte.dev/content/docs/kit/25-build-and-deploy/50-adapter-static.md @@ -104,7 +104,7 @@ You'll also want to generate a fallback `404.html` page to replace the default 4 A config for GitHub Pages might look like the following: ```js -// @errors: 2307 +// @errors: 2307 2322 /// file: svelte.config.js import adapter from '@sveltejs/adapter-static'; 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 d91dd80951..549c600d25 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 @@ -55,6 +55,7 @@ New projects will use the current Node LTS version by default. However, if you'r 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 +// @errors: 2307 /// file: svelte.config.js import adapter from '@sveltejs/adapter-netlify'; diff --git a/apps/svelte.dev/content/docs/kit/40-best-practices/10-accessibility.md b/apps/svelte.dev/content/docs/kit/40-best-practices/10-accessibility.md index 655f9a82df..12357186c0 100644 --- a/apps/svelte.dev/content/docs/kit/40-best-practices/10-accessibility.md +++ b/apps/svelte.dev/content/docs/kit/40-best-practices/10-accessibility.md @@ -66,12 +66,13 @@ If your content is available in multiple languages, you should set the `lang` at ```js /// file: src/hooks.server.js -/** - * @param {import('@sveltejs/kit').RequestEvent} event - */ -function get_lang(event) { +// @filename: utils.ts +export function get_lang(event: import('@sveltejs/kit').RequestEvent) { return 'en'; } + +// @filename: hooks.server.js +import { get_lang } from './utils'; // ---cut--- /** @type {import('@sveltejs/kit').Handle} */ export function handle({ event, resolve }) {