├── pnpm-workspace.yaml ├── .prettierignore ├── renovate.json ├── .prettierrc ├── .changeset ├── config.json └── README.md ├── .gitignore ├── definitions ├── optional-parameters.md ├── layout-reset.md ├── page-reset-layout.md ├── parent.md ├── json.md ├── error.md ├── redirect.md ├── $env.static.public.md ├── fail.md ├── $env.dynamic.public.md ├── rest-parameters.md ├── $env.dynamic.private.md ├── layout-groups.md ├── param-matchers.md ├── $env.static.private.md ├── depends.md ├── locals.md ├── $app.forms.md ├── error-boundaries.md ├── $props.md ├── +layout.svelte.md ├── $app.stores.md ├── setHeaders.md ├── $app.paths.md ├── +layout.ts.md ├── cookies.md ├── +layout.server.ts.md ├── $app.state.md ├── $derived.md ├── $state.raw.md ├── onclick.md ├── snippets.md ├── +page.svelte.md ├── +server.ts.md ├── actions.md ├── enhance.md ├── $app.server.md ├── $state.snapshot.md ├── +page.ts.md ├── $effect.root.md ├── $effect.md ├── $app.navigation.md ├── $effect.pending.md ├── component-events.md ├── load.md ├── $bindable.md ├── hooks.server.md ├── +page.server.ts.md ├── $effect.pre.md ├── global-state.md ├── await-expressions.md ├── $state.md ├── $host.md ├── common-mistakes.md ├── remote-functions.md ├── $inspect.md ├── snippet.md ├── $effect.tracking.md ├── event-modifiers.md ├── migration-patterns.md ├── snapshot.md ├── custom-events.md ├── $derived.by.md ├── lifecycle-equivalents.md └── event-delegation.md ├── tsconfig.json ├── src ├── config.ts ├── types.ts ├── index.ts ├── db-loader.ts ├── build-database.ts ├── db-definition-loader.ts └── tools │ └── definition-tools.ts ├── LICENSE ├── package.json ├── docs ├── runes │ ├── props.md │ ├── state.md │ ├── derived.md │ ├── effect.md │ └── overview.md ├── overview.md ├── features │ ├── events.md │ ├── snippets.md │ ├── component-events.md │ ├── await-expressions.md │ └── remote-functions.md ├── patterns │ ├── global-state.md │ └── migration.md └── common-mistakes │ ├── props-mistakes.md │ └── state-mistakes.md ├── CHANGELOG.md └── README.md /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | onlyBuiltDependencies: 2 | - better-sqlite3 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Package Managers 2 | package-lock.json 3 | pnpm-lock.yaml 4 | yarn.lock -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:recommended"] 4 | } 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "all", 5 | "printWidth": 70, 6 | "proseWrap": "always" 7 | } 8 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@3.0.5/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "public", 8 | "baseBranch": "main", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [] 11 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules/ 3 | .pnpm-store/ 4 | 5 | # Build output 6 | dist/ 7 | build/ 8 | 9 | # Environment variables 10 | .env 11 | .env.local 12 | .env.*.local 13 | 14 | # IDE 15 | .vscode/ 16 | .idea/ 17 | *.swp 18 | *.swo 19 | 20 | # Logs 21 | *.log 22 | npm-debug.log* 23 | pnpm-debug.log* 24 | 25 | # Testing 26 | coverage/ 27 | 28 | # Database files 29 | *.db 30 | *.db-journal 31 | 32 | # OS 33 | .DS_Store 34 | Thumbs.db -------------------------------------------------------------------------------- /definitions/optional-parameters.md: -------------------------------------------------------------------------------- 1 | # optional-parameters Definition 2 | 3 | **Definition:** Wrap a dynamic segment in double brackets `[[param]]` 4 | to make it optional. 5 | 6 | **Syntax:** `src/routes/[[lang]]/home/+page.svelte` matches `/home` 7 | and `/en/home`. 8 | 9 | Notes: 10 | 11 | - An optional parameter cannot follow a rest parameter (e.g., 12 | `[...rest]/[[opt]]`). 13 | 14 | ## Related 15 | 16 | - `rest-parameters`, `param-matchers` 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ES2020", 5 | "moduleResolution": "node", 6 | "esModuleInterop": true, 7 | "strict": true, 8 | "outDir": "dist", 9 | "rootDir": "src", 10 | "declaration": true, 11 | "skipLibCheck": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "resolveJsonModule": true 14 | }, 15 | "include": ["src/**/*"], 16 | "exclude": ["node_modules", "dist"] 17 | } 18 | -------------------------------------------------------------------------------- /definitions/layout-reset.md: -------------------------------------------------------------------------------- 1 | # layout-reset Definition 2 | 3 | **Definition:** Use `+layout@.svelte` (or `+layout@.svelte` 4 | for root) to reset the layout hierarchy for all child routes. 5 | 6 | **Syntax:** `+layout@[id].svelte`, `+layout@item.svelte`, 7 | `+layout@(group).svelte`, `+layout@.svelte` 8 | 9 | **Returns:** Resets the ancestor used for the layout and its 10 | descendants. 11 | 12 | ## Related 13 | 14 | - `page-reset-layout`, `layout-groups`, `advanced-routing` 15 | -------------------------------------------------------------------------------- /definitions/page-reset-layout.md: -------------------------------------------------------------------------------- 1 | # page-reset-layout Definition 2 | 3 | **Definition:** Use `+page@.svelte` (or `+page@.svelte` for 4 | root) to reset which ancestor layout a page inherits from. 5 | 6 | **Syntax:** `+page@[id].svelte`, `+page@item.svelte`, 7 | `+page@(group).svelte`, `+page@.svelte` 8 | 9 | **Returns:** The page uses the chosen ancestor layout instead of the 10 | nearest one. 11 | 12 | ## Related 13 | 14 | - `layout-reset`, `layout-groups`, `advanced-routing` 15 | -------------------------------------------------------------------------------- /definitions/parent.md: -------------------------------------------------------------------------------- 1 | # parent Definition 2 | 3 | **Definition:** Function available in `load` that returns a promise 4 | resolving to parent data (layout→child). 5 | 6 | **Syntax:** `const data = await parent()` 7 | 8 | **Returns:** The parent loader’s returned data object. 9 | 10 | ## Example 11 | 12 | ```ts 13 | export const load = async ({ parent }) => { 14 | const { user } = await parent(); 15 | return { user }; 16 | }; 17 | ``` 18 | 19 | ## Related 20 | 21 | - `load`, `+layout.svelte` 22 | -------------------------------------------------------------------------------- /definitions/json.md: -------------------------------------------------------------------------------- 1 | # json Definition 2 | 3 | **Definition:** Helper to create a JSON `Response` with proper 4 | headers. 5 | 6 | **Syntax:** `json(data: unknown, init?: ResponseInit)` 7 | 8 | **Parameters:** 9 | 10 | - `data` — serializable data 11 | - `init` — headers/status 12 | 13 | **Returns:** `Response` 14 | 15 | ## Example 16 | 17 | ```ts 18 | import { json } from '@sveltejs/kit'; 19 | export const GET = () => json({ ok: true }, { status: 200 }); 20 | ``` 21 | 22 | ## Related 23 | 24 | - `+server.ts`, `error`, `redirect` 25 | -------------------------------------------------------------------------------- /definitions/error.md: -------------------------------------------------------------------------------- 1 | # error Definition 2 | 3 | **Definition:** Throw an HTTP error from `load`/actions/`+server` 4 | handlers. 5 | 6 | **Syntax (Kit 2+):** `throw error(status, messageOrBody)` 7 | 8 | Important: 9 | 10 | - You must `throw error(...)` — do not call it without throwing. 11 | - Use inside server/universal `load`, actions, and `+server` handlers. 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { error } from '@sveltejs/kit'; 17 | if (!item) throw error(404, 'Not found'); 18 | ``` 19 | 20 | ## Related 21 | 22 | - `redirect`, `json` 23 | -------------------------------------------------------------------------------- /definitions/redirect.md: -------------------------------------------------------------------------------- 1 | # redirect Definition 2 | 3 | **Definition:** Throw an HTTP redirect with status and location. 4 | 5 | **Syntax (Kit 2+):** `throw redirect(status, location)` 6 | 7 | Important: 8 | 9 | - You must `throw redirect(...)` — do not call it without throwing. 10 | - Use inside server/universal `load`, actions, and `+server` handlers. 11 | 12 | ## Example 13 | 14 | ```ts 15 | import { redirect } from '@sveltejs/kit'; 16 | throw redirect(303, '/login'); 17 | ``` 18 | 19 | ## Related 20 | 21 | - `error`, `json` 22 | - `actions` 23 | -------------------------------------------------------------------------------- /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by 4 | `@changesets/cli`, a build tool that works with multi-package repos, 5 | or single-package repos to help you version and publish your code. You 6 | can find the full documentation for it 7 | [in our repository](https://github.com/changesets/changesets) 8 | 9 | We have a quick list of common questions to get you started engaging 10 | with this project in 11 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 12 | -------------------------------------------------------------------------------- /definitions/$env.static.public.md: -------------------------------------------------------------------------------- 1 | # $env/static/public Definition 2 | 3 | **Definition:** Build-time (static) public environment variables safe 4 | for client usage. Includes only variables with `publicPrefix` (default 5 | `PUBLIC_`). 6 | 7 | **Syntax:** `import { PUBLIC_... } from '$env/static/public'` 8 | 9 | **Notes:** 10 | 11 | - Values are statically replaced at build time. 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { PUBLIC_BASE_URL } from '$env/static/public'; 17 | ``` 18 | 19 | ## Related 20 | 21 | - `$env/static/private`, `$env/dynamic/public` 22 | -------------------------------------------------------------------------------- /definitions/fail.md: -------------------------------------------------------------------------------- 1 | # fail Definition 2 | 3 | **Definition:** Helper to return a failed action result with an HTTP 4 | status and data. Does not throw. 5 | 6 | **Syntax:** `fail(status: number, data?: unknown)` 7 | 8 | **Parameters:** 9 | 10 | - `status` — HTTP status code (e.g., 400, 422) 11 | - `data` — serializable error payload 12 | 13 | **Returns:** An `ActionFailure` consumed by SvelteKit. 14 | 15 | ## Example 16 | 17 | ```ts 18 | import { fail } from '@sveltejs/kit'; 19 | return fail(400, { field: 'email', message: 'Email required' }); 20 | ``` 21 | 22 | ## Related 23 | 24 | - `actions`, `enhance` 25 | -------------------------------------------------------------------------------- /definitions/$env.dynamic.public.md: -------------------------------------------------------------------------------- 1 | # $env/dynamic/public Definition 2 | 3 | **Definition:** Runtime (dynamic) public environment variables safe 4 | for client usage. Includes only variables with `publicPrefix` (default 5 | `PUBLIC_`). 6 | 7 | **Syntax:** `import { env } from '$env/dynamic/public'` 8 | 9 | **Notes:** 10 | 11 | - Public dynamic vars are sent from server to client; prefer 12 | `$env/static/public` when possible. 13 | 14 | ## Example 15 | 16 | ```ts 17 | import { env } from '$env/dynamic/public'; 18 | const base = env.PUBLIC_BASE_URL; 19 | ``` 20 | 21 | ## Related 22 | 23 | - `$env/static/public`, `$env/dynamic/private` 24 | -------------------------------------------------------------------------------- /definitions/rest-parameters.md: -------------------------------------------------------------------------------- 1 | # rest-parameters Definition 2 | 3 | **Definition:** Route segment syntax `[...name]` captures the 4 | remainder of a path into `params.name`, possibly empty. 5 | 6 | **Syntax:** `src/routes/a/[...rest]/z/+page.svelte` matches `/a/z`, 7 | `/a/b/z`, `/a/b/c/z`, ... 8 | 9 | **Returns:** `params.rest` with `/`-joined remainder. 10 | 11 | ## Example 12 | 13 | ```txt 14 | /[org]/[repo]/tree/[branch]/[...file] 15 | ``` 16 | 17 | `/sveltejs/kit/tree/main/documentation/docs/file.md` → 18 | `{ org, repo, branch, file: 'documentation/docs/file.md' }` 19 | 20 | ## Related 21 | 22 | - `optional-parameters`, `param-matchers`, `advanced-routing` 23 | -------------------------------------------------------------------------------- /definitions/$env.dynamic.private.md: -------------------------------------------------------------------------------- 1 | # $env/dynamic/private Definition 2 | 3 | **Definition:** Runtime (dynamic) private environment variables 4 | available on the server. Excludes variables with `publicPrefix` and 5 | includes those with `privatePrefix` (if configured). 6 | 7 | **Syntax:** `import { env } from '$env/dynamic/private'` 8 | 9 | **Notes:** 10 | 11 | - Server-only; cannot be imported in client code. 12 | - In dev, includes vars from `.env`; in prod, depends on adapter. 13 | 14 | ## Example 15 | 16 | ```ts 17 | import { env } from '$env/dynamic/private'; 18 | const secret = env.SECRET_API_KEY; 19 | ``` 20 | 21 | ## Related 22 | 23 | - `$env/static/private`, `$env/dynamic/public` 24 | -------------------------------------------------------------------------------- /definitions/layout-groups.md: -------------------------------------------------------------------------------- 1 | # layout-groups Definition 2 | 3 | **Definition:** Parentheses-named directories `(group)` organize 4 | routes under separate layout hierarchies without affecting the URL 5 | pathname. 6 | 7 | **Syntax:** `src/routes/(app)/...` and `src/routes/(marketing)/...` 8 | 9 | **Returns:** Layouts inside a group apply only to routes in that 10 | group. 11 | 12 | ## Example 13 | 14 | ```txt 15 | src/routes/ 16 | ├ (app)/ 17 | │ ├ dashboard/ 18 | │ ├ item/ 19 | │ └ +layout.svelte 20 | ├ (marketing)/ 21 | │ ├ about/ 22 | │ ├ testimonials/ 23 | │ └ +layout.svelte 24 | └ +layout.svelte 25 | ``` 26 | 27 | ## Related 28 | 29 | - `page-reset-layout`, `layout-reset`, `advanced-routing` 30 | -------------------------------------------------------------------------------- /definitions/param-matchers.md: -------------------------------------------------------------------------------- 1 | # param-matchers Definition 2 | 3 | **Definition:** Validate dynamic segments with matchers implemented in 4 | `src/params/`. Use `[param=name]` in route path. 5 | 6 | **Syntax:** 7 | 8 | - `src/params/fruit.(js|ts)` exports `match(param): boolean` 9 | - `src/routes/fruits/[page=fruit]/+page.svelte` 10 | 11 | ## Example 12 | 13 | ```ts 14 | // src/params/fruit.ts 15 | import type { ParamMatcher } from '@sveltejs/kit'; 16 | export const match = ((param: string): param is 'apple' | 'orange' => 17 | param === 'apple' || param === 'orange') satisfies ParamMatcher; 18 | ``` 19 | 20 | ## Related 21 | 22 | - `advanced-routing`, `optional-parameters`, `rest-parameters` 23 | -------------------------------------------------------------------------------- /definitions/$env.static.private.md: -------------------------------------------------------------------------------- 1 | # $env/static/private Definition 2 | 3 | **Definition:** Build-time (static) private environment variables 4 | injected by Vite from `.env` and `process.env`. Excludes 5 | `publicPrefix` and includes `privatePrefix` (if configured). 6 | 7 | **Syntax:** `import { NAME } from '$env/static/private'` 8 | 9 | **Notes:** 10 | 11 | - Values are statically replaced at build time; enables dead code 12 | elimination. 13 | - Declare all referenced variables (even if empty) to avoid undefined 14 | at build. 15 | 16 | ## Example 17 | 18 | ```ts 19 | import { API_KEY } from '$env/static/private'; 20 | ``` 21 | 22 | ## Related 23 | 24 | - `$env/dynamic/private`, `$env/static/public` 25 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Configuration for the MCP Svelte Docs server 3 | */ 4 | 5 | /** 6 | * Configuration interface 7 | */ 8 | export interface Config { 9 | // Add any configuration options here 10 | debug: boolean; 11 | } 12 | 13 | /** 14 | * Default configuration 15 | */ 16 | const default_config: Config = { 17 | debug: false, 18 | }; 19 | 20 | /** 21 | * Get the server configuration 22 | * @returns The server configuration 23 | */ 24 | export function get_config(): Config { 25 | // Get configuration from environment variables 26 | const debug = process.env.DEBUG === 'true'; 27 | 28 | // Merge with default configuration 29 | return { 30 | ...default_config, 31 | debug, 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Types for Svelte 5 documentation 3 | */ 4 | 5 | export interface Feature { 6 | name: string; 7 | description: string; 8 | examples: string[]; 9 | bestPractices?: string[]; 10 | } 11 | 12 | export interface Pattern { 13 | name: string; 14 | description: string; 15 | examples: string[]; 16 | bestPractices?: string[]; 17 | } 18 | 19 | export interface CommonMistake { 20 | name: string; 21 | description: string; 22 | mistake: string; 23 | correction: string; 24 | explanation: string; 25 | } 26 | 27 | // Empty arrays to replace the imported content 28 | export const features: Feature[] = []; 29 | export const patterns: Pattern[] = []; 30 | export const mistakes: CommonMistake[] = []; 31 | -------------------------------------------------------------------------------- /definitions/depends.md: -------------------------------------------------------------------------------- 1 | # depends Definition 2 | 3 | **Definition:** Declare a cache dependency key that ties `load` 4 | results to invalidation. 5 | 6 | **Syntax:** `depends(...keys: string[])` 7 | 8 | **Parameters:** 9 | 10 | - `keys` — strings like `app:resource` used by 11 | `invalidate`/`invalidateAll`. For custom identifiers, use a 12 | lowercase prefix followed by `:` to conform to URI rules. 13 | 14 | **Returns:** Void — registers dependency for the current `load`. 15 | 16 | ## Example 17 | 18 | ```ts 19 | export const load = ({ depends }) => { 20 | depends('app:posts'); 21 | return { 22 | /* data */ 23 | }; 24 | }; 25 | ``` 26 | 27 | ## Related 28 | 29 | - `$app/navigation.invalidate`, `invalidateAll` 30 | - `load` 31 | -------------------------------------------------------------------------------- /definitions/locals.md: -------------------------------------------------------------------------------- 1 | # locals Definition 2 | 3 | **Definition:** Per-request mutable object available on the server for 4 | storing authenticated user/session or request-scoped data. 5 | 6 | **Access:** `event.locals` in server `load`, form actions, and 7 | `+server` endpoints. 8 | 9 | **Typing:** Extend `App.Locals` in `src/app.d.ts` to define shape. 10 | 11 | ## Example 12 | 13 | ```ts 14 | // src/hooks.server.ts 15 | export const handle = async ({ event, resolve }) => { 16 | event.locals.user = await getUser(event.cookies.get('sessionid')); 17 | return resolve(event); 18 | }; 19 | 20 | // +page.server.ts 21 | export const load = ({ locals }) => ({ user: locals.user }); 22 | ``` 23 | 24 | ## Related 25 | 26 | - `hooks.server`, `cookies` 27 | -------------------------------------------------------------------------------- /definitions/$app.forms.md: -------------------------------------------------------------------------------- 1 | # $app/forms Definition 2 | 3 | **Definition:** Client helpers for progressive form enhancement and 4 | action results. 5 | 6 | **Syntax:** 7 | `import { enhance, applyAction, deserialize } from '$app/forms'` 8 | 9 | **Functions:** 10 | 11 | - `enhance(form, submit?)` — use:enhance or programmatic; optional 12 | callback per submit 13 | - `applyAction(result)` — apply an `ActionResult` to the current page 14 | - `deserialize(text)` — parse ActionResult from a fetch response 15 | 16 | ## Example 17 | 18 | ```svelte 19 | 22 | 23 |
24 | 25 |
26 | ``` 27 | 28 | ## Related 29 | 30 | - `enhance`, `actions`, `fail` 31 | -------------------------------------------------------------------------------- /definitions/error-boundaries.md: -------------------------------------------------------------------------------- 1 | # error-boundaries Definition 2 | 3 | **Definition:** Add `+error.svelte` to render a custom error page for 4 | errors thrown during `load`/rendering in the corresponding route 5 | subtree. 6 | 7 | **Syntax:** `src/routes/+error.svelte`, or nested 8 | `.../route/+error.svelte` 9 | 10 | **Notes:** 11 | 12 | - SvelteKit walks up to the nearest `+error.svelte` if a child doesn’t 13 | exist. 14 | - Errors in `+server` handlers and `handle` do not use 15 | `+error.svelte`; they return JSON or fallback error page. 16 | - For 404s with no matched route, root `+error.svelte` is used. 17 | 18 | ## Example 19 | 20 | ```svelte 21 | 24 |

{page.status}: {page.error.message}

25 | ``` 26 | 27 | ## Related 28 | 29 | - `advanced-routing`, `hooks.server.handleError` 30 | -------------------------------------------------------------------------------- /definitions/$props.md: -------------------------------------------------------------------------------- 1 | # $props Definition 2 | 3 | **Definition:** Declares component props (replaces export let from 4 | Svelte 4) 5 | **Syntax:** `$props(): any` 6 | **Parameters:** None 7 | **Returns:** Object containing all component props 8 | **Variants:** 9 | 10 | - `$props.id(): string` - Returns unique ID for the props object 11 | 12 | ## Examples 13 | 14 | ```js 15 | // Basic props destructuring 16 | let { name, age = 25 } = $props(); 17 | 18 | // With TypeScript 19 | interface Props { 20 | name: string; 21 | age?: number; 22 | onClick?: () => void; 23 | } 24 | let { name, age = 25, onClick } = $props(); 25 | 26 | // Rest props 27 | let { name, ...rest } = $props(); 28 | ``` 29 | 30 | ## Related 31 | 32 | - `$bindable` - For two-way binding props 33 | - `$state` - For local component state 34 | - `component-events` - For component communication patterns 35 | -------------------------------------------------------------------------------- /definitions/+layout.svelte.md: -------------------------------------------------------------------------------- 1 | # +layout.svelte Definition 2 | 3 | **Definition:** SvelteKit layout component that wraps pages and nested 4 | layouts. Receives `data` from its layout `load` and a `children` 5 | snippet to render. 6 | 7 | **Syntax:** `src/routes/(...)/+layout.svelte` 8 | 9 | **Props typing (Kit ≥2.16):** 10 | `let { data, children }: import('./$types').LayoutProps = $props();` 11 | 12 | **Returns:** A wrapper component for shared UI/state across a route 13 | segment. 14 | 15 | ## Example 16 | 17 | ```svelte 18 | 22 | 23 | 24 | {@render children()} 25 | ``` 26 | 27 | ## Related 28 | 29 | - `+page.svelte` — rendered inside the nearest layout 30 | - `load` — layout data loader 31 | - `+layout.ts` — options and typing 32 | -------------------------------------------------------------------------------- /definitions/$app.stores.md: -------------------------------------------------------------------------------- 1 | # $app/stores Definition 2 | 3 | **Definition:** Client-accessible Svelte stores reflecting navigation 4 | state. 5 | 6 | Note: Deprecated in SvelteKit 2.12+ in favor of `$app/state` (use 7 | identical APIs via runes). Existing apps may still use `$app/stores`. 8 | 9 | **Syntax:** `import { page, navigating, updated } from '$app/stores'` 10 | 11 | **Stores:** 12 | 13 | - `page` — `Readable` with params, route, data 14 | - `navigating` — `Readable` 15 | - `updated` — `Readable & { check(): Promise }` 16 | 17 | ## Example 18 | 19 | ```svelte 20 | 24 |

{title}

25 | {#if $navigating}

Loading…

{/if} 26 | ``` 27 | 28 | ## Related 29 | 30 | - `$app/state`, `$app/navigation`, `load` 31 | -------------------------------------------------------------------------------- /definitions/setHeaders.md: -------------------------------------------------------------------------------- 1 | # setHeaders Definition 2 | 3 | **Definition:** Helper available in `load` (server and universal) to 4 | set HTTP headers on the response during SSR. 5 | 6 | **Syntax:** `setHeaders(headers: Record)` 7 | 8 | Important: 9 | 10 | - No effect in the browser; only works during SSR/build. 11 | - You cannot set `set-cookie` — use `cookies.set(...)` in server 12 | `load`/actions. 13 | - Each header can be set at most once across all `load` functions. 14 | 15 | ## Example 16 | 17 | ```ts 18 | export const load: import('./$types').PageLoad = async ({ 19 | fetch, 20 | setHeaders, 21 | }) => { 22 | const res = await fetch('/api/data'); 23 | setHeaders({ 24 | 'cache-control': res.headers.get('cache-control') ?? 'max-age=60', 25 | }); 26 | return { data: await res.json() }; 27 | }; 28 | ``` 29 | 30 | ## Related 31 | 32 | - `load`, `cookies`, `+server.ts` 33 | -------------------------------------------------------------------------------- /definitions/$app.paths.md: -------------------------------------------------------------------------------- 1 | # $app/paths Definition 2 | 3 | **Definition:** Helpers for base/assets paths and resolving URLs or 4 | route IDs respecting `config.kit.paths`. 5 | 6 | **Syntax:** `import { asset, resolve } from '$app/paths'` 7 | 8 | **API:** 9 | 10 | - `asset(file)` — resolve URL for assets in `static/` honoring 11 | `paths.assets` (2.26+) 12 | - `resolve(pathOrRoute, params?)` — resolve pathname or route ID with 13 | params honoring base path (2.26+) 14 | - Deprecated: `assets`, `base`, `resolveRoute` 15 | 16 | ## Example 17 | 18 | ```svelte 19 | 24 | 25 | logo 26 | Post 27 | ``` 28 | 29 | ## Related 30 | 31 | - `configuration#paths`, `$app/navigation` 32 | -------------------------------------------------------------------------------- /definitions/+layout.ts.md: -------------------------------------------------------------------------------- 1 | # +layout.ts Definition 2 | 3 | **Definition:** Companion module for a layout that declares options 4 | and exposes `LayoutData` typing. 5 | 6 | **Syntax:** `src/routes/(...)/+layout.ts` (or `.js`) 7 | 8 | **Parameters:** 9 | 10 | - `export const prerender` — `true | 'auto' | false` 11 | - `export const csr` — `boolean` 12 | - `export const ssr` — `boolean` 13 | - `export const trailingSlash` — `'always' | 'never' | 'ignore'` 14 | - `export const config` — adapter-specific options merged top-level 15 | with child pages 16 | 17 | **Returns:** Controls how the layout and its children render. 18 | 19 | ## Example 20 | 21 | ```ts 22 | export const prerender = 'auto'; 23 | export type LayoutLoad = import('./$types').LayoutLoad; 24 | export type LayoutData = import('./$types').LayoutData; 25 | ``` 26 | 27 | ## Related 28 | 29 | - `+layout.svelte` — layout component 30 | - `load` — layout loader 31 | -------------------------------------------------------------------------------- /definitions/cookies.md: -------------------------------------------------------------------------------- 1 | # cookies Definition 2 | 3 | **Definition:** Server utilities for reading/writing cookies on 4 | requests. 5 | 6 | **Syntax:** in server `load`/endpoints: `event.cookies` 7 | 8 | **API:** 9 | 10 | - `get(name)` — read a cookie (optionally `getAll()`) 11 | - `set(name, value, { path, ...options })` — set cookie (path is 12 | required) 13 | - `delete(name, { path, ...options })` — delete cookie (path must 14 | match) 15 | 16 | **Returns:** Cookie helpers bound to the current request. 17 | 18 | ## Example 19 | 20 | ```ts 21 | export const load = ({ cookies }) => { 22 | const theme = cookies.get('theme') ?? 'light'; 23 | cookies.set('seen', '1', { path: '/', httpOnly: true }); 24 | return { theme }; 25 | }; 26 | ``` 27 | 28 | ## Related 29 | 30 | - `locals`, `+server.ts`, `hooks.server` 31 | 32 | Notes: 33 | 34 | - `event.fetch` forwards cookies for same-origin requests; to forward 35 | to other origins, add headers in `handleFetch`. 36 | -------------------------------------------------------------------------------- /definitions/+layout.server.ts.md: -------------------------------------------------------------------------------- 1 | # +layout.server.ts Definition 2 | 3 | **Definition:** Server-only layout module exporting a `load` that runs 4 | on the server for a layout segment. Useful for auth checks and shared 5 | server data. 6 | 7 | **Syntax:** `src/routes/(...)/+layout.server.ts` (or `.js`) 8 | 9 | **Exports:** 10 | 11 | - `export const load: LayoutServerLoad` — server-only loader (must 12 | return serializable data) 13 | - Page options: `prerender`, `ssr`, `csr` defaults for children 14 | 15 | **Returns:** Data merges downward to child layouts and pages. 16 | 17 | ## Example 18 | 19 | ```ts 20 | // +layout.server.ts 21 | import type { LayoutServerLoad } from './$types'; 22 | import { error } from '@sveltejs/kit'; 23 | 24 | export const load: LayoutServerLoad = async ({ locals }) => { 25 | if (!locals.user) throw error(401, 'not logged in'); 26 | return { user: locals.user }; 27 | }; 28 | ``` 29 | 30 | ## Related 31 | 32 | - `+layout.svelte`, `+layout.ts`, `load`, `hooks.server` 33 | -------------------------------------------------------------------------------- /definitions/$app.state.md: -------------------------------------------------------------------------------- 1 | # $app/state Definition 2 | 3 | **Definition:** Read-only state module providing `page`, `navigating`, 4 | and `updated` for reactive routing and version info. Supersedes 5 | `$app/stores` in Kit ≥2.12. 6 | 7 | **Syntax:** `import { page, navigating, updated } from '$app/state'` 8 | 9 | **API:** 10 | 11 | - `page` — reactive object for `url`, `params`, `data`, `form`, 12 | `route`, `status`, `error` 13 | - `navigating` — navigation info (`from`, `to`, `type`, `delta`, null 14 | on server or idle) 15 | - `updated` — object with `current: boolean` and 16 | `check(): Promise` 17 | 18 | Note: `$app/state` values update reactively only via runes (e.g. 19 | `$derived`). 20 | 21 | ## Example 22 | 23 | ```svelte 24 | 28 | 29 |

{path}

30 | {#if navigating?.to} 31 |

Navigating…

32 | {/if} 33 | ``` 34 | 35 | ## Related 36 | 37 | - `$app/navigation`, `$app/stores` 38 | -------------------------------------------------------------------------------- /definitions/$derived.md: -------------------------------------------------------------------------------- 1 | # $derived Definition 2 | 3 | **Definition:** Creates computed values that automatically update when 4 | dependencies change 5 | **Syntax:** `$derived(expression: T): T` 6 | **Parameters:** 7 | 8 | - `expression` - The expression to derive from 9 | **Returns:** Computed value that updates when dependencies change 10 | **Variants:** 11 | - `$derived.by(fn: () => T): T` - Complex derivation using function 12 | 13 | ## Examples 14 | 15 | ```js 16 | // Basic derived value 17 | let count = $state(0); 18 | const doubled = $derived(count * 2); 19 | 20 | // Complex derivation with function 21 | const expensive = $derived.by(() => { 22 | return heavyComputation(count); 23 | }); 24 | 25 | // Multiple dependencies 26 | let firstName = $state('John'); 27 | let lastName = $state('Doe'); 28 | const fullName = $derived(`${firstName} ${lastName}`); 29 | ``` 30 | 31 | ## Related 32 | 33 | - `$state` - For reactive state that derived values depend on 34 | - `$effect` - For side effects responding to derived changes 35 | - `$derived.by` - For complex derivation logic 36 | -------------------------------------------------------------------------------- /definitions/$state.raw.md: -------------------------------------------------------------------------------- 1 | # $state.raw Definition 2 | 3 | **Definition:** Non-deeply-reactive state for values that should only 4 | be reactive when reassigned 5 | **Syntax:** `$state.raw(initial: T): T` 6 | **Parameters:** 7 | 8 | - `initial` - The initial value 9 | **Returns:** Non-deeply-reactive proxy of the initial value 10 | **Variants:** 11 | - Part of `$state` rune family 12 | 13 | ## Examples 14 | 15 | ```js 16 | // Array that only triggers reactivity on reassignment 17 | let numbers = $state.raw([1, 2, 3]); 18 | 19 | // This triggers reactivity (reassignment) 20 | numbers = [...numbers, 4]; 21 | 22 | // This does NOT trigger reactivity (mutation) 23 | numbers[0] = 99; 24 | 25 | // Object example 26 | let config = $state.raw({ theme: 'dark', lang: 'en' }); 27 | 28 | // Triggers reactivity 29 | config = { ...config, theme: 'light' }; 30 | 31 | // Does NOT trigger reactivity 32 | config.theme = 'light'; 33 | ``` 34 | 35 | ## Related 36 | 37 | - `$state` - For deeply reactive state 38 | - `$state.snapshot` - For static snapshots of reactive state 39 | - `$derived` - For computed values that can depend on raw state 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Scott Spence 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /definitions/onclick.md: -------------------------------------------------------------------------------- 1 | # onclick Definition 2 | 3 | **Definition:** Standard HTML event attributes replace on: 4 | directives 5 | **Syntax:** `onclick={handler}` (instead of `on:click={handler}`) 6 | **Parameters:** 7 | 8 | - `handler` - Function to handle the click event **Returns:** void 9 | **Examples:** 10 | - `onclick={handleClick}` 11 | - `onsubmit={handleSubmit}` 12 | - `oninput={handleInput}` 13 | 14 | ## Examples 15 | 16 | ```svelte 17 | 28 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | 40 | ``` 41 | 42 | ## Related 43 | 44 | - `component-events` - For component-to-component communication 45 | - `$effect` - For responding to state changes from events 46 | - `on:click` - Svelte 4 pattern that onclick replaces 47 | -------------------------------------------------------------------------------- /definitions/snippets.md: -------------------------------------------------------------------------------- 1 | # snippets Definition 2 | 3 | **Definition:** Reusable template fragments that replace named slots 4 | **Syntax:** 5 | 6 | ```svelte 7 | {#snippet name(param1, param2)} 8 | 9 | {/snippet} 10 | 11 | {@render name(arg1, arg2)} 12 | ``` 13 | 14 | **Parameters:** 15 | 16 | - `name` - Identifier for the snippet 17 | - `param1, param2` - Optional parameters passed to snippet 18 | **Returns:** Renderable template fragment **Interface:** 19 | `Snippet` 20 | 21 | ## Examples 22 | 23 | ```svelte 24 | 25 | {#snippet card(title, content)} 26 |
27 |

{title}

28 |

{content}

29 |
30 | {/snippet} 31 | 32 | 33 | {@render card('Hello', 'World')} 34 | 35 | 36 | 39 | 40 | {@render header?.()} 41 | ``` 42 | 43 | ## Related 44 | 45 | - `$props` - For passing snippets as component props 46 | - `Snippet` - TypeScript interface for snippet functions 47 | - `slots` - Svelte 4 pattern that snippets replace 48 | -------------------------------------------------------------------------------- /definitions/+page.svelte.md: -------------------------------------------------------------------------------- 1 | # +page.svelte Definition 2 | 3 | **Definition:** SvelteKit route component rendered for a page. Lives 4 | alongside `+page.ts`/`+page.js` and optional `+page.server.ts`/`.js`. 5 | Receives `data` (and `form`, when relevant) from its `load`/actions 6 | and participates in the layout tree. 7 | 8 | **Syntax:** `src/routes/(...)/+page.svelte` 9 | 10 | **Props typing (Kit ≥2.16):** 11 | `let { data, form }: import('./$types').PageProps = $props();` 12 | 13 | **Returns:** A client-rendered component hydrated according to project 14 | `ssr/csr` settings. 15 | 16 | ## Example 17 | 18 | ```svelte 19 | 26 | 27 |

Welcome {user.name}

28 | {#each posts as post} 29 | {post.title} 30 | {/each} 31 | ``` 32 | 33 | ## Related 34 | 35 | - `+layout.svelte` — parent layout component 36 | - `load` — page data loader 37 | - `+page.ts` — page options and types 38 | - `error` / `redirect` — control flow from loaders 39 | -------------------------------------------------------------------------------- /definitions/+server.ts.md: -------------------------------------------------------------------------------- 1 | # +server.ts Definition 2 | 3 | **Definition:** Route module that exports HTTP method handlers for 4 | endpoints. Runs only on the server. 5 | 6 | **Syntax:** `src/routes/(...)/+server.ts` (or `.js`) 7 | 8 | **Parameters:** 9 | 10 | - `export const GET/POST/PATCH/PUT/DELETE/OPTIONS` — request handlers 11 | - `export const fallback` — optional handler for all other methods 12 | - `RequestHandler` — type for handlers, with `locals`, `cookies`, etc. 13 | 14 | **Returns:** Sends a `Response` using Web Fetch APIs or helper `json`. 15 | 16 | ## Example 17 | 18 | ```ts 19 | import type { RequestHandler } from './$types'; 20 | import { json, error } from '@sveltejs/kit'; 21 | 22 | export const GET: RequestHandler = async ({ url, locals }) => { 23 | const id = url.searchParams.get('id'); 24 | if (!id) throw error(400, 'id required'); 25 | const item = await locals.db.get(id); 26 | return json(item); 27 | }; 28 | ``` 29 | 30 | ## Related 31 | 32 | - `json` — helper for JSON responses 33 | - `cookies`, `locals` — request utilities 34 | - Note: `+layout` options don’t affect `+server` routes 35 | - Notes: `HEAD` responses mirror `GET` body length; content 36 | negotiation prefers page for Accept: text/html 37 | -------------------------------------------------------------------------------- /definitions/actions.md: -------------------------------------------------------------------------------- 1 | # actions Definition 2 | 3 | **Definition:** Server-only form actions defined in `+page.server.ts` 4 | to handle POSTed form data and mutations. 5 | 6 | **Syntax:** 7 | `export const actions = { name: async (event) => { ... } }` 8 | 9 | **Parameters:** 10 | 11 | - `event.request` — form data via `await request.formData()` 12 | - `event.locals`, `event.cookies` 13 | - `fail(status, data)` — return validation errors 14 | - `redirect(status, location)` — navigate after success 15 | 16 | **Returns:** Action result is available via the page `form` prop and 17 | `page.form` until the next update. 18 | 19 | ## Example 20 | 21 | ```ts 22 | // +page.server.ts 23 | import type { Actions } from './$types'; 24 | import { fail, redirect } from '@sveltejs/kit'; 25 | 26 | export const actions: Actions = { 27 | login: async ({ request, locals }) => { 28 | const data = await request.formData(); 29 | const email = data.get('email'); 30 | if (!email) return fail(400, { message: 'Email required' }); 31 | await locals.auth.login(email as string); 32 | throw redirect(303, '/dashboard'); 33 | }, 34 | }; 35 | ``` 36 | 37 | ## Related 38 | 39 | - `enhance` — progressive enhancement for forms 40 | - `fail`, `redirect` 41 | -------------------------------------------------------------------------------- /definitions/enhance.md: -------------------------------------------------------------------------------- 1 | # enhance Definition 2 | 3 | **Definition:** Client-side helper to progressively enhance `
` 4 | submissions by calling actions via fetch and updating UI without a 5 | full navigation. 6 | 7 | **Syntax:** `import { enhance } from '$app/forms'` 8 | 9 | **Parameters:** 10 | 11 | - `use:enhance` — enhance a `` 12 | - Callback (SubmitFunction): 13 | `({ action, formData, formElement, controller, submitter, cancel }) => ( { formData, formElement, action, result, update } ) => void` 14 | - `update({ reset?, invalidateAll? })` — run default post-submit 15 | behavior 16 | - Requires `method="POST"` and an action in `+page.server.*` 17 | 18 | **Returns:** Unsubscribable cleanup function. 19 | 20 | ## Example 21 | 22 | ```svelte 23 | 27 | 28 | { 29 | return async ({ result, update }) => { 30 | await update(); // default behavior (updates form/page.form, invalidates) 31 | }; 32 | }}> 33 | 34 |
35 | ``` 36 | 37 | ## Related 38 | 39 | - `actions`, `fail` 40 | - `$app/forms` 41 | -------------------------------------------------------------------------------- /definitions/$app.server.md: -------------------------------------------------------------------------------- 1 | # $app/server Definition 2 | 3 | **Definition:** Server-only utilities for remote functions and server 4 | helpers: `getRequestEvent`, `read`, and creators for `query`, `form`, 5 | `command`, `prerender` (experimental ≥2.27). 6 | 7 | **Syntax:** 8 | `import { getRequestEvent, read, query, form, command, prerender } from '$app/server'` 9 | 10 | **API:** 11 | 12 | - `getRequestEvent()` — current RequestEvent (sync in environments 13 | without AsyncLocalStorage) 14 | - `read(asset)` — read imported asset contents as a Response 15 | - `query/schema` — create server query (supports validation, batch, 16 | `.refresh()/.set()`) 17 | - `form` — create spreadable form object with progressive enhancement 18 | - `command` — create server mutation callable from code (not during 19 | render) 20 | - `prerender` — create build-time data function (with `inputs`, 21 | `dynamic`) 22 | 23 | ## Example 24 | 25 | ```ts 26 | import { getRequestEvent, query } from '$app/server'; 27 | 28 | export const me = query(async () => { 29 | const { cookies, locals } = getRequestEvent(); 30 | return await locals.userFrom(cookies.get('sessionid')); 31 | }); 32 | ``` 33 | 34 | ## Related 35 | 36 | - `remote-functions`, `hooks.server.handleValidationError` 37 | -------------------------------------------------------------------------------- /definitions/$state.snapshot.md: -------------------------------------------------------------------------------- 1 | # $state.snapshot Definition 2 | 3 | **Definition:** Static snapshot of reactive state 4 | **Syntax:** `$state.snapshot(state: T): Snapshot` 5 | **Parameters:** 6 | 7 | - `state` - The reactive state to snapshot 8 | **Returns:** Static snapshot that doesn't trigger reactivity 9 | **Variants:** 10 | - Part of `$state` rune family 11 | 12 | ## Examples 13 | 14 | ```js 15 | // Create reactive state 16 | let user = $state({ name: 'Alice', posts: ['Hello', 'World'] }); 17 | 18 | // Create static snapshot 19 | let userSnapshot = $state.snapshot(user); 20 | 21 | // Snapshot is static - won't trigger reactivity when accessed 22 | console.log(userSnapshot.name); // 'Alice' - no reactivity 23 | 24 | // Original state still reactive 25 | user.name = 'Bob'; // Triggers reactivity 26 | 27 | // Snapshot remains unchanged 28 | console.log(userSnapshot.name); // Still 'Alice' 29 | 30 | // Use in effects to avoid infinite loops 31 | $effect(() => { 32 | const snapshot = $state.snapshot(user); 33 | localStorage.setItem('user', JSON.stringify(snapshot)); 34 | }); 35 | ``` 36 | 37 | ## Related 38 | 39 | - `$state` - For reactive state that can be snapshotted 40 | - `$state.raw` - For non-deeply-reactive state 41 | - `$effect` - Often used with snapshots to avoid infinite loops 42 | -------------------------------------------------------------------------------- /definitions/+page.ts.md: -------------------------------------------------------------------------------- 1 | # +page.ts Definition 2 | 3 | **Definition:** Companion module for a page that declares page 4 | options, and (in TS) exposes helper types via `./$types`. 5 | 6 | **Syntax:** `src/routes/(...)/+page.ts` (or `+page.js`) 7 | 8 | **Parameters:** 9 | 10 | - `export const prerender` — `true | 'auto' | false` 11 | - `export const csr` — `boolean` (enable/disable client-side 12 | hydration) 13 | - `export const ssr` — `boolean` (enable/disable server rendering) 14 | - `export const trailingSlash` — `'always' | 'never' | 'ignore'` 15 | - `export const entries` — function to enumerate dynamic entries for 16 | prerendering 17 | - `export const config` — adapter-specific options merged from parent 18 | layout 19 | 20 | Notes: 21 | 22 | - Pages with form actions cannot be prerendered. 23 | - `entries` applies to dynamic routes to seed the prerender crawler. 24 | 25 | **Returns:** Controls how the page is rendered and navigated. 26 | 27 | ## Example 28 | 29 | ```ts 30 | export const prerender = true; 31 | export const csr = true; 32 | export const ssr = true; 33 | export const trailingSlash = 'never'; 34 | 35 | export type PageLoad = import('./$types').PageLoad; 36 | export type PageData = import('./$types').PageData; 37 | ``` 38 | 39 | ## Related 40 | 41 | - `+page.svelte` — page component 42 | - `load` — page loader 43 | -------------------------------------------------------------------------------- /definitions/$effect.root.md: -------------------------------------------------------------------------------- 1 | # $effect.root Definition 2 | 3 | **Definition:** Creates a non-tracked scope that doesn't 4 | auto-cleanup 5 | **Syntax:** `$effect.root(fn: () => void | (() => void)): () => void` 6 | **Parameters:** 7 | 8 | - `fn` - Function to run, optionally returns cleanup function 9 | **Returns:** Cleanup function to manually destroy the effect root 10 | **Variants:** 11 | - Part of `$effect` rune family 12 | 13 | ## Examples 14 | 15 | ```js 16 | // Create manually controlled effect scope 17 | const destroy = $effect.root(() => { 18 | $effect(() => { 19 | console.log('This effect runs inside the root'); 20 | }); 21 | 22 | return () => { 23 | console.log('Effect root cleanup'); 24 | }; 25 | }); 26 | 27 | // Later, manually cleanup 28 | destroy(); 29 | 30 | // Creating effects outside component initialization 31 | let count = $state(0); 32 | 33 | const cleanup = $effect.root(() => { 34 | $effect(() => { 35 | console.log('Count changed:', count); 36 | }); 37 | 38 | return () => { 39 | console.log('Cleaning up count watcher'); 40 | }; 41 | }); 42 | 43 | // Cleanup when needed 44 | function stopWatching() { 45 | cleanup(); 46 | } 47 | ``` 48 | 49 | ## Related 50 | 51 | - `$effect` - For regular effects with auto-cleanup 52 | - `$effect.pre` - For effects that run before DOM updates 53 | - `$state` - For reactive state that can trigger effects 54 | -------------------------------------------------------------------------------- /definitions/$effect.md: -------------------------------------------------------------------------------- 1 | # $effect Definition 2 | 3 | **Definition:** Performs side effects that respond to reactive state 4 | changes 5 | **Syntax:** `$effect(fn: () => void | (() => void)): void` 6 | **Parameters:** 7 | 8 | - `fn` - Function to run, optionally returns cleanup function 9 | **Returns:** void 10 | **Variants:** 11 | - `$effect.pre(fn: () => void | (() => void)): void` - Runs before DOM 12 | updates 13 | - `$effect.pending(): number` - Returns number of pending effects 14 | - `$effect.tracking(): boolean` - Returns if currently tracking 15 | dependencies 16 | - `$effect.root(fn: () => void | (() => void)): () => void` - Creates 17 | effect root 18 | 19 | ## Examples 20 | 21 | ```js 22 | // Basic effect 23 | let count = $state(0); 24 | $effect(() => { 25 | console.log(`Count is ${count}`); 26 | }); 27 | 28 | // Effect with cleanup 29 | $effect(() => { 30 | const timer = setInterval(() => count++, 1000); 31 | 32 | return () => { 33 | clearInterval(timer); 34 | }; 35 | }); 36 | 37 | // Pre-DOM effect 38 | $effect.pre(() => { 39 | // Runs before DOM updates 40 | console.log('Before DOM update'); 41 | }); 42 | ``` 43 | 44 | ## Related 45 | 46 | - `$state` - For reactive state that triggers effects 47 | - `$derived` - For computed values that can trigger effects 48 | - `$effect.pre` - For effects that run before DOM updates 49 | - `$effect.root` - For creating effect boundaries 50 | -------------------------------------------------------------------------------- /definitions/$app.navigation.md: -------------------------------------------------------------------------------- 1 | # $app/navigation Definition 2 | 3 | **Definition:** Client utilities for navigation and invalidation. 4 | 5 | **Syntax:** 6 | `import { goto, invalidate, invalidateAll, beforeNavigate, afterNavigate, onNavigate, preloadData, preloadCode, refreshAll, pushState, replaceState, disableScrollHandling } from '$app/navigation'` 7 | 8 | **Functions:** 9 | 10 | - `goto(href, opts?)` — programmatic navigation 11 | - `invalidate(urlOrPredicate)` — re-run `load` for resources 12 | - `invalidateAll()` — re-run all active `load` functions 13 | - `beforeNavigate(cb)` / `afterNavigate(cb)` — navigation lifecycle 14 | (call during component init) 15 | - `onNavigate(cb)` — pre-navigation hook (call during component init); 16 | can await before completing and return cleanup 17 | - `preloadData(href)` — preload route code and run load 18 | - `preloadCode(pathname)` — preload route modules only 19 | - `refreshAll({ includeLoadFunctions? })` — refresh remote functions 20 | and optionally rerun load 21 | - `pushState(url, state)` / `replaceState(url, state)` — shallow 22 | routing state 23 | - `disableScrollHandling()` — disable router scroll handling for the 24 | update 25 | 26 | ## Example 27 | 28 | ```ts 29 | import { goto, invalidate } from '$app/navigation'; 30 | await goto('/dashboard'); 31 | await invalidate((url) => url.pathname.startsWith('/api')); 32 | ``` 33 | 34 | ## Related 35 | 36 | - `$app/stores`, `$app/forms` 37 | -------------------------------------------------------------------------------- /definitions/$effect.pending.md: -------------------------------------------------------------------------------- 1 | # $effect.pending Definition 2 | 3 | **Definition:** Returns number of pending promises in the current 4 | boundary 5 | **Syntax:** `$effect.pending(): number` 6 | **Parameters:** None 7 | **Returns:** Number of pending promises, not including child 8 | boundaries 9 | **Variants:** 10 | 11 | - Part of `$effect` rune family 12 | 13 | ## Examples 14 | 15 | ```js 16 | // Monitor pending async operations 17 | let a = $state(1); 18 | let b = $state(2); 19 | 20 | async function add(x, y) { 21 | await new Promise(resolve => setTimeout(resolve, 1000)); 22 | return x + y; 23 | } 24 | 25 | // In template or effect 26 | $effect(() => { 27 | const pending = $effect.pending(); 28 | if (pending > 0) { 29 | console.log(`${pending} promises are pending`); 30 | } 31 | }); 32 | 33 | // Usage with await expressions 34 | {#if $effect.pending()} 35 |

Loading... ({$effect.pending()} operations pending)

36 | {/if} 37 | 38 |

{a} + {b} = {await add(a, b)}

39 | 40 | // Conditional rendering based on pending state 41 | {#if $effect.pending() > 0} 42 |
43 | Processing {$effect.pending()} operations... 44 |
45 | {/if} 46 | ``` 47 | 48 | ## Related 49 | 50 | - `await-expressions` - Async template expressions that create pending 51 | promises 52 | - `$effect` - For effects that can monitor pending state 53 | - `$effect.tracking` - For checking if code is in tracking context 54 | -------------------------------------------------------------------------------- /definitions/component-events.md: -------------------------------------------------------------------------------- 1 | # component-events Definition 2 | 3 | **Definition:** Callback props replace event dispatching 4 | **Pattern:** 5 | 6 | ```svelte 7 | 8 | let { onMessage } = $props(); 9 | 10 | 11 | console.log(data)} /> 12 | ``` 13 | 14 | **Parameters:** 15 | 16 | - Callback functions passed as props **Returns:** void (callbacks 17 | handle the communication) 18 | 19 | ## Examples 20 | 21 | ```svelte 22 | 23 | 32 | 33 |
34 | 35 | 36 | 37 | 38 |
39 | 40 | 41 | 50 | 51 | 55 | ``` 56 | 57 | ## Related 58 | 59 | - `$props` - For receiving callback props 60 | - `onclick` - For DOM event handling 61 | - `createEventDispatcher` - Svelte 4 pattern that callbacks replace 62 | -------------------------------------------------------------------------------- /definitions/load.md: -------------------------------------------------------------------------------- 1 | # load Definition 2 | 3 | **Definition:** Data loading function for pages and layouts. Page 4 | `load` runs in the browser (and server on the first render); layout 5 | `load` runs where it is invoked. A `load` in `+page.server.ts`/`.js` 6 | or `+layout.server.ts`/`.js` runs only on the server. 7 | 8 | **Syntax:** 9 | 10 | - Page: 11 | `export const load: PageLoad = ({ fetch, params, data, depends, parent }) => { ... }` 12 | - Layout: 13 | `export const load: LayoutLoad = ({ fetch, params, data, depends, parent }) => { ... }` 14 | - Server: 15 | `export const load: PageServerLoad | LayoutServerLoad = ({ cookies, locals, setHeaders, fetch }) => { ... }` 16 | 17 | **Parameters:** 18 | 19 | - `fetch` — SvelteKit-aware fetch 20 | - `params` — route parameters 21 | - `data` — parent data (layout→child) 22 | - `depends` — declare cache dependencies 23 | - `parent` — await parent data 24 | - `cookies`, `locals`, `setHeaders` — server-only 25 | 26 | **Returns:** Object merged into `data` for the corresponding 27 | component. 28 | 29 | ## Example 30 | 31 | ```ts 32 | // +page.ts 33 | export const load: import('./$types').PageLoad = async ({ 34 | fetch, 35 | params, 36 | depends, 37 | }) => { 38 | depends('app:posts'); 39 | const res = await fetch(`/api/posts?tag=${params.tag}`); 40 | return { posts: await res.json() }; 41 | }; 42 | ``` 43 | 44 | ## Related 45 | 46 | - `depends`, `parent`, `setHeaders` 47 | - `+page.svelte`, `+layout.svelte` 48 | - `error`, `redirect` 49 | -------------------------------------------------------------------------------- /definitions/$bindable.md: -------------------------------------------------------------------------------- 1 | # $bindable Definition 2 | 3 | **Definition:** Creates bindable prop for two-way data binding 4 | **Syntax:** `$bindable(fallback?: T): T` 5 | **Parameters:** 6 | 7 | - `fallback` - Optional fallback value 8 | **Returns:** Bindable value of type T 9 | **Variants:** 10 | - Used within component props for two-way binding 11 | 12 | ## Examples 13 | 14 | ```js 15 | 16 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 |

Text: {text}

29 | 30 | 31 | 38 | 39 | 42 | 43 | 44 | 47 | 48 | 49 |

Number: {number}

50 | 51 | 52 | 59 | 60 | 61 | ``` 62 | 63 | ## Related 64 | 65 | - `$props` - For declaring component props 66 | - `$state` - For local reactive state 67 | - `bind:` - Svelte binding syntax used with bindable props 68 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mcp-svelte-docs", 3 | "version": "0.0.22", 4 | "description": "MCP server for Svelte docs", 5 | "type": "module", 6 | "main": "dist/index.js", 7 | "types": "dist/index.d.ts", 8 | "bin": { 9 | "mcp-svelte-docs": "./dist/index.js" 10 | }, 11 | "files": [ 12 | "dist", 13 | "README.md", 14 | "LICENSE" 15 | ], 16 | "scripts": { 17 | "build": "tsc && node dist/build-database.js && chmod +x dist/index.js", 18 | "start": "node dist/index.js", 19 | "dev": "node dist/index.js", 20 | "format": "prettier --write .", 21 | "format:check": "prettier --check .", 22 | "changeset": "changeset", 23 | "version": "changeset version", 24 | "release": "pnpm run build && changeset publish" 25 | }, 26 | "keywords": [ 27 | "mcp", 28 | "model-context-protocol" 29 | ], 30 | "author": "Scott Spence", 31 | "license": "MIT", 32 | "repository": { 33 | "type": "git", 34 | "url": "https://github.com/spences10/mcp-svelte-docs.git" 35 | }, 36 | "bugs": { 37 | "url": "https://github.com/spences10/mcp-svelte-docs/issues" 38 | }, 39 | "homepage": "https://github.com/spences10/mcp-svelte-docs#readme", 40 | "dependencies": { 41 | "@tmcp/adapter-valibot": "^0.1.4", 42 | "@tmcp/transport-stdio": "^0.4.0", 43 | "better-sqlite3": "^12.4.1", 44 | "tmcp": "^1.16.1", 45 | "valibot": "^1.1.0" 46 | }, 47 | "devDependencies": { 48 | "@changesets/cli": "^2.29.7", 49 | "@types/better-sqlite3": "^7.6.13", 50 | "@types/node": "^24.9.2", 51 | "prettier": "^3.6.2", 52 | "typescript": "^5.9.3" 53 | } 54 | } -------------------------------------------------------------------------------- /definitions/hooks.server.md: -------------------------------------------------------------------------------- 1 | # hooks.server Definition 2 | 3 | **Definition:** Server hook module that customizes request handling. 4 | 5 | **Syntax:** `src/hooks.server.ts` 6 | 7 | **Exports:** 8 | 9 | - `handle` — wraps all requests 10 | - `handleFetch` — intercepts server-side fetch 11 | - `handleError` — central error logging/formatting 12 | - `init` — optional async initialization on server startup 13 | (ServerInit) 14 | - `handleValidationError` — customize remote function validation 15 | errors 16 | 17 | ## Example 18 | 19 | ```ts 20 | // src/hooks.server.ts 21 | import type { Handle, HandleFetch, HandleError } from '@sveltejs/kit'; 22 | 23 | export const handle: Handle = async ({ event, resolve }) => { 24 | event.locals.user = await getUserFromSession(event.cookies); 25 | return resolve(event); 26 | }; 27 | 28 | export const handleFetch: HandleFetch = async ({ 29 | request, 30 | fetch, 31 | }) => { 32 | return fetch(request); 33 | }; 34 | 35 | export const handleError: HandleError = ({ error, event }) => { 36 | console.error('Error', event.route.id, error); 37 | }; 38 | 39 | // Optional: run once before first request is handled 40 | import type { 41 | ServerInit, 42 | HandleValidationError, 43 | } from '@sveltejs/kit'; 44 | 45 | export const init: ServerInit = async () => { 46 | // warm caches, connect to services, etc. 47 | }; 48 | 49 | export const handleValidationError: HandleValidationError = ({ 50 | issues, 51 | event, 52 | }) => { 53 | return { message: 'Invalid request' }; 54 | }; 55 | ``` 56 | 57 | ## Related 58 | 59 | - `locals`, `cookies` 60 | - `+server.ts` 61 | -------------------------------------------------------------------------------- /definitions/+page.server.ts.md: -------------------------------------------------------------------------------- 1 | # +page.server.ts Definition 2 | 3 | **Definition:** Server-only page module exporting a `load` that runs 4 | on the server and optional form `actions`. Suitable for accessing 5 | databases, private env, and cookies. 6 | 7 | **Syntax:** `src/routes/(...)/+page.server.ts` (or `.js`) 8 | 9 | **Exports:** 10 | 11 | - `export const load: PageServerLoad` — server-only loader (must 12 | return serializable data) 13 | - `export const actions` — form actions map (`default` or named) 14 | - Page options: `prerender`, `ssr`, `csr` 15 | 16 | **Returns:** Data is serialized and sent to the client; during client 17 | navigation, fetched from the server. 18 | 19 | ## Example 20 | 21 | ```ts 22 | // +page.server.ts 23 | import type { PageServerLoad, Actions } from './$types'; 24 | import { error, redirect, fail } from '@sveltejs/kit'; 25 | 26 | export const load: PageServerLoad = async ({ 27 | params, 28 | cookies, 29 | locals, 30 | }) => { 31 | const session = cookies.get('sessionid'); 32 | if (!session) throw redirect(307, '/login'); 33 | const user = await locals.db.getUser(session); 34 | if (!user) throw error(401, 'not logged in'); 35 | return { user }; 36 | }; 37 | 38 | export const actions: Actions = { 39 | default: async ({ request, locals }) => { 40 | const data = await request.formData(); 41 | const name = data.get('name'); 42 | if (!name) return fail(400, { missing: true }); 43 | await locals.db.updateName(name as string); 44 | return { success: true }; 45 | }, 46 | }; 47 | ``` 48 | 49 | ## Related 50 | 51 | - `+page.svelte`, `+page.ts`, `actions`, `$env/*` 52 | -------------------------------------------------------------------------------- /definitions/$effect.pre.md: -------------------------------------------------------------------------------- 1 | # $effect.pre Definition 2 | 3 | **Definition:** Runs before DOM updates 4 | **Syntax:** `$effect.pre(fn: () => void | (() => void)): void` 5 | **Parameters:** 6 | 7 | - `fn` - Function to run, optionally returns cleanup function 8 | **Returns:** void 9 | **Variants:** 10 | - Part of `$effect` rune family 11 | 12 | ## Examples 13 | 14 | ```js 15 | // Measure DOM before updates 16 | let element; 17 | let count = $state(0); 18 | 19 | $effect.pre(() => { 20 | if (element) { 21 | const rect = element.getBoundingClientRect(); 22 | console.log('Before update:', rect.height); 23 | } 24 | }); 25 | 26 | $effect(() => { 27 | if (element) { 28 | const rect = element.getBoundingClientRect(); 29 | console.log('After update:', rect.height); 30 | } 31 | }); 32 | 33 | // Reading values before they change 34 | let items = $state(['a', 'b', 'c']); 35 | 36 | $effect.pre(() => { 37 | console.log('Items before change:', items.length); 38 | }); 39 | 40 | // Cleanup in pre-effect 41 | $effect.pre(() => { 42 | console.log('Pre-effect running'); 43 | 44 | return () => { 45 | console.log('Pre-effect cleanup'); 46 | }; 47 | }); 48 | 49 | // Useful for animations or transitions 50 | let isVisible = $state(true); 51 | 52 | $effect.pre(() => { 53 | if (!isVisible && element) { 54 | // Start exit animation before DOM update 55 | element.style.transition = 'opacity 0.3s'; 56 | element.style.opacity = '0'; 57 | } 58 | }); 59 | ``` 60 | 61 | ## Related 62 | 63 | - `$effect` - For effects that run after DOM updates 64 | - `$effect.root` - For creating effect boundaries 65 | - `$state` - For reactive state that triggers effects 66 | -------------------------------------------------------------------------------- /definitions/global-state.md: -------------------------------------------------------------------------------- 1 | # global-state Definition 2 | 3 | **Definition:** Advanced patterns for sharing reactive state across 4 | multiple components using Svelte 5 runes 5 | **Syntax:** Context-based or module-based state sharing patterns 6 | **Parameters:** 7 | 8 | - Context patterns use `setContext(key, state)` and `getContext(key)` 9 | - Module patterns export reactive `$state` objects directly 10 | **Returns:** Shared reactive state accessible across component 11 | boundaries 12 | **Patterns:** 13 | - Context API with `$state` for component trees 14 | - Module-level `$state` for global application state 15 | - Custom store patterns using `$state` internally 16 | 17 | ## Examples 18 | 19 | ```js 20 | // Context-based global state 21 | // parent.svelte 22 | import { setContext } from 'svelte'; 23 | 24 | let globalUser = $state({ name: 'Alice', id: 1 }); 25 | setContext('user', globalUser); 26 | 27 | // child.svelte 28 | import { getContext } from 'svelte'; 29 | 30 | let user = getContext('user'); 31 | // user.name is reactive across components 32 | 33 | // Module-based global state 34 | // store.js 35 | export let appState = $state({ 36 | theme: 'dark', 37 | user: null, 38 | notifications: [], 39 | }); 40 | 41 | // component.svelte 42 | import { appState } from './store.js'; 43 | 44 | // Direct access to global reactive state 45 | appState.theme = 'light'; // Updates everywhere 46 | ``` 47 | 48 | ## Related 49 | 50 | - `$state` - Creates the underlying reactive state 51 | - `setContext` - Provides context-based state sharing 52 | - `getContext` - Consumes context-based state 53 | - `$derived` - For computed values based on global state 54 | - `$effect` - For side effects responding to global state changes 55 | -------------------------------------------------------------------------------- /definitions/await-expressions.md: -------------------------------------------------------------------------------- 1 | # await-expressions Definition 2 | 3 | **Definition:** Experimental async template expressions for handling 4 | promises directly in markup 5 | **Syntax:** `{await promise}` or `{await promise then result}` or 6 | `{await promise catch error}` 7 | **Parameters:** 8 | 9 | - `promise` - Promise to await in template 10 | - `result` - Variable name for resolved value (optional) 11 | - `error` - Variable name for caught error (optional) **Returns:** 12 | Rendered content based on promise state 13 | **Status:** Experimental feature in Svelte 5 14 | 15 | ## Examples 16 | 17 | ```svelte 18 | 19 | 27 | 28 | 29 |

User: {await fetchUser(userId)}

30 | 31 | 32 | {#await fetchUser(userId)} 33 |

Loading user...

34 | {:then user} 35 |

Hello {user.name}!

36 | {:catch error} 37 |

Error: {error.message}

38 | {/await} 39 | 40 | 41 |

Welcome {await fetchUser(userId).then(u => u.name)}!

42 | 43 | 44 |
45 |

{await getTitle()}

46 |

{await getDescription()}

47 |
{await getFooter()}
48 |
49 | 50 | 51 | {#if userId} 52 |

User data: {await fetchUser(userId)}

53 | {/if} 54 | ``` 55 | 56 | ## Related 57 | 58 | - `$effect.pending` - For monitoring pending async operations 59 | - `$state` - For reactive state that can trigger new awaits 60 | - `{#await}` - Traditional await block syntax 61 | -------------------------------------------------------------------------------- /docs/runes/props.md: -------------------------------------------------------------------------------- 1 | # $props Rune 2 | 3 | ## Description 4 | 5 | The $props rune is used to declare component props in Svelte 5, 6 | replacing the export let syntax from Svelte 4. 7 | 8 | ## Basic Syntax 9 | 10 | ```js 11 | // Basic props 12 | let { name, age = 21 } = $props(); 13 | 14 | // Rename property 15 | let { class: className } = $props(); 16 | 17 | // Rest props 18 | let { id, ...rest } = $props(); 19 | 20 | // Get all props 21 | let props = $props(); 22 | 23 | // Bindable props 24 | let { value = $bindable(0) } = $props(); 25 | ``` 26 | 27 | ## Examples 28 | 29 | ### Basic Props 30 | 31 | ```svelte 32 | 35 | 36 |
37 |

Name: {name}

38 |

Age: {age}

39 |
40 | ``` 41 | 42 | ### With TypeScript 43 | 44 | ```svelte 45 | 48 | ``` 49 | 50 | ### Wrapper Component 51 | 52 | ```svelte 53 | 56 | 57 |
58 | 59 |
60 | ``` 61 | 62 | ### Bindable Props 63 | 64 | ```svelte 65 | 72 | 73 |
74 |

Value: {value}

75 | 76 |
77 | 78 | 79 | 80 | ``` 81 | 82 | ## Best Practices 83 | 84 | - Use destructuring to declare the props you expect 85 | - Provide default values for optional props 86 | - Use TypeScript to type your props 87 | - For bindable props, use the $bindable rune 88 | - Use the spread operator to forward attributes to underlying elements 89 | -------------------------------------------------------------------------------- /definitions/$state.md: -------------------------------------------------------------------------------- 1 | # $state Definition 2 | 3 | **Definition:** Declares reactive state. When the value changes, the 4 | UI updates. Arrays and plain objects become deeply reactive proxies; 5 | primitives (like numbers/strings/booleans) remain normal values. 6 | **Also see:** `$state.raw`, `$state.snapshot` 7 | 8 | ## Examples 9 | 10 | ```ts 11 | // Basic reactive state 12 | let count = $state(0); 13 | count++; // triggers an update 14 | 15 | // Object/array state (deeply reactive) 16 | let user = $state({ name: 'Alice', age: 30 }); 17 | user.name = 'Bob'; // triggers an update of only the places that read `user.name` 18 | 19 | // You can declare without an initial value with generics 20 | let data = $state(); // undefined initially 21 | ``` 22 | 23 | ## Notes 24 | 25 | - Deep reactivity: Arrays and simple objects are proxied deeply; 26 | property changes and array methods (e.g. `push`) trigger granular 27 | updates. 28 | - Raw state: Use `$state.raw` to avoid deep reactivity and update only 29 | on reassignment for performance with large data. 30 | - Snapshots: Use `$state.snapshot(obj)` when passing reactive objects 31 | to APIs that don’t expect proxies (e.g., `structuredClone`, 32 | logging). 33 | - Classes: Use `$state` in class fields (or first assignment in 34 | `constructor`) for reactive class instances; methods should be bound 35 | if used as handlers. 36 | - Destructuring: Values destructured from reactive objects are not 37 | reactive; derive reactive pieces explicitly (e.g., 38 | `const name = $derived(user.name)`). 39 | - Passing to functions: JavaScript passes values, not variables — pass 40 | getters or functions if you need ‘current’ values. 41 | 42 | ## Related 43 | 44 | - `$derived` - For computed values based on state 45 | - `$effect` - For side effects responding to state changes 46 | - `$state.raw` - For non-deeply-reactive state 47 | - `$state.snapshot` - For static snapshots 48 | -------------------------------------------------------------------------------- /definitions/$host.md: -------------------------------------------------------------------------------- 1 | # $host Definition 2 | 3 | **Definition:** Returns reference to the host element in custom 4 | elements 5 | **Syntax:** `$host(): El` 6 | **Parameters:** None 7 | **Returns:** Host element reference 8 | **Variants:** 9 | 10 | - Used specifically in custom elements/web components 11 | 12 | ## Examples 13 | 14 | ```js 15 | 16 | 17 | 18 | 49 | 50 | 51 | 52 | 53 | 62 | 63 | 64 | 65 | ``` 66 | 67 | ## Related 68 | 69 | - `svelte:options` - For configuring custom elements 70 | - `$effect` - Often used with $host for host element manipulation 71 | - Custom Elements - Web standard that $host is designed for 72 | -------------------------------------------------------------------------------- /definitions/common-mistakes.md: -------------------------------------------------------------------------------- 1 | # common-mistakes Definition 2 | 3 | **Definition:** Anti-patterns and debugging guide for common Svelte 5 4 | mistakes 5 | **Syntax:** Patterns to avoid and correct alternatives 6 | **Categories:** 7 | 8 | - Reactivity pitfalls with `$state` and `$derived` 9 | - Improper `$effect` usage and cleanup 10 | - Migration errors from Svelte 4 patterns 11 | **Returns:** Understanding of correct Svelte 5 patterns 12 | **Purpose:** Debugging guide and best practice reference 13 | 14 | ## Examples 15 | 16 | ```js 17 | // ❌ MISTAKE: Destructuring $state loses reactivity 18 | let user = $state({ name: 'Alice' }); 19 | let { name } = user; // 'name' is not reactive! 20 | 21 | // ✅ CORRECT: Access properties directly 22 | user.name; // Reactive access 23 | 24 | // ❌ MISTAKE: Using $effect for derived values 25 | let count = $state(0); 26 | let double = $state(0); 27 | $effect(() => { 28 | double = count * 2; // Wrong - use $derived instead 29 | }); 30 | 31 | // ✅ CORRECT: Use $derived for computed values 32 | let double = $derived(count * 2); 33 | 34 | // ❌ MISTAKE: Missing cleanup in $effect 35 | $effect(() => { 36 | let interval = setInterval(() => {}, 1000); 37 | // Missing cleanup causes memory leak! 38 | }); 39 | 40 | // ✅ CORRECT: Return cleanup function 41 | $effect(() => { 42 | let interval = setInterval(() => {}, 1000); 43 | return () => clearInterval(interval); 44 | }); 45 | 46 | // ❌ MISTAKE: Using $state.raw unnecessarily 47 | let items = $state.raw([1, 2, 3]); 48 | items.push(4); // No reactivity! 49 | 50 | // ✅ CORRECT: Use regular $state for arrays/objects 51 | let items = $state([1, 2, 3]); 52 | items.push(4); // Reactive 53 | ``` 54 | 55 | ## Related 56 | 57 | - `$state` - For reactive state management 58 | - `$derived` - For computed values, not `$effect` 59 | - `$effect` - For side effects, not derived values 60 | - `$state.raw` - Only when you need non-reactive state 61 | - `migration-patterns` - Svelte 4 to 5 conversion guide 62 | -------------------------------------------------------------------------------- /definitions/remote-functions.md: -------------------------------------------------------------------------------- 1 | # remote-functions Definition 2 | 3 | **Definition:** Experimental, opt-in feature (Kit ≥2.27) for type-safe 4 | client→server functions defined in `.remote.(js|ts)` using 5 | `$app/server` helpers: `query`, `form`, `command`, `prerender`. 6 | 7 | **Opt-in:** `kit.experimental.remoteFunctions = true` (and optionally 8 | Svelte compiler `experimental.async = true` for `await` in 9 | components.) 10 | 11 | **Flavours:** 12 | 13 | - `query` — read dynamic data; callable from components; supports 14 | argument validation; has `.refresh()/.set()` 15 | - `form` — write via spreadable form object; progressively enhances; 16 | supports single-flight updates 17 | - `command` — write from arbitrary code; cannot redirect; can 18 | `.updates(...)` 19 | - `prerender` — build-time data (with `inputs` and `dynamic` options) 20 | 21 | ## Example 22 | 23 | ```ts 24 | // src/routes/data.remote.ts 25 | import * as v from 'valibot'; 26 | import { query, form } from '$app/server'; 27 | import { error, redirect } from '@sveltejs/kit'; 28 | import * as db from '$lib/server/database'; 29 | 30 | export const getPosts = query(async () => db.listPosts()); 31 | export const getPost = query(v.string(), async (slug) => 32 | db.getPost(slug), 33 | ); 34 | export const createPost = form(async (data) => { 35 | const title = data.get('title'); 36 | if (typeof title !== 'string') throw error(400, 'title required'); 37 | await db.createPost(title); 38 | throw redirect(303, '/blog'); 39 | }); 40 | ``` 41 | 42 | ## Notes 43 | 44 | - Remote files live under `src/` and export functions; client calls 45 | fetch generated endpoints. 46 | - Use `getRequestEvent()` inside to access cookies/locals. 47 | - Validation uses Standard Schema (e.g., Zod/Valibot). Use 48 | `'unchecked'` to skip. 49 | - `redirect(...)` allowed in `query/form/prerender`, not in `command`. 50 | 51 | ## Related 52 | 53 | - `$app/server`, `$app/navigation.refreshAll`, `await-expressions`, 54 | `$derived` 55 | -------------------------------------------------------------------------------- /definitions/$inspect.md: -------------------------------------------------------------------------------- 1 | # $inspect Definition 2 | 3 | **Definition:** Development tool for inspecting reactive state 4 | changes 5 | **Syntax:** `$inspect(...values: T)` 6 | **Parameters:** 7 | 8 | - `...values` - Values to inspect 9 | **Returns:** Object with `with` method for custom inspection 10 | **Variants:** 11 | - `$inspect.trace(name?: string): void` - Traces reactive updates with 12 | optional label 13 | 14 | ## Examples 15 | 16 | ```js 17 | // Basic inspection 18 | let count = $state(0); 19 | let doubled = $derived(count * 2); 20 | 21 | $inspect(count, doubled); 22 | // Logs to console whenever count or doubled changes 23 | 24 | // With custom inspection 25 | let user = $state({ name: 'Alice', age: 30 }); 26 | 27 | $inspect(user).with((value) => { 28 | console.log('User changed:', JSON.stringify(value, null, 2)); 29 | }); 30 | 31 | // Inspect multiple values 32 | let a = $state(1); 33 | let b = $state(2); 34 | let sum = $derived(a + b); 35 | 36 | $inspect(a, b, sum); 37 | 38 | // Trace reactive updates 39 | $inspect.trace('user-updates'); 40 | 41 | // Named trace for debugging 42 | $inspect.trace('component-lifecycle'); 43 | 44 | // Conditional inspection (development only) 45 | if (import.meta.env.DEV) { 46 | $inspect(count, doubled); 47 | } 48 | 49 | // Inspect complex state changes 50 | let items = $state([]); 51 | let filter = $state(''); 52 | let filteredItems = $derived( 53 | items.filter((item) => item.includes(filter)), 54 | ); 55 | 56 | $inspect(items, filter, filteredItems).with( 57 | (items, filter, filtered) => { 58 | console.group('Filter Debug'); 59 | console.log('Items:', items); 60 | console.log('Filter:', filter); 61 | console.log('Filtered:', filtered); 62 | console.groupEnd(); 63 | }, 64 | ); 65 | ``` 66 | 67 | ## Related 68 | 69 | - `$state` - For reactive state that can be inspected 70 | - `$derived` - For computed values that can be inspected 71 | - `$effect` - For side effects, use effects instead of inspect for 72 | production logic 73 | -------------------------------------------------------------------------------- /docs/runes/state.md: -------------------------------------------------------------------------------- 1 | # $state Rune 2 | 3 | ## Description 4 | 5 | The $state rune is used to declare reactive state in Svelte 5. 6 | 7 | ## Basic Syntax 8 | 9 | ```js 10 | // Create reactive state 11 | let count = $state(0); 12 | let user = $state({ name: 'Alice', age: 30 }); 13 | 14 | // Access and update directly 15 | count++; 16 | user.name = 'Bob'; 17 | ``` 18 | 19 | ## Examples 20 | 21 | ### Basic Counter 22 | 23 | ```svelte 24 | 31 | 32 | 35 | ``` 36 | 37 | ### Object State 38 | 39 | ```svelte 40 | 47 | 48 |
49 |

{user.name} is {user.age} years old

50 | 51 |
52 | ``` 53 | 54 | ### With TypeScript 55 | 56 | ```svelte 57 | 69 | ``` 70 | 71 | ## Raw State 72 | 73 | For values that should only be reactive when reassigned (not when 74 | their properties change): 75 | 76 | ```js 77 | let numbers = $state.raw([1, 2, 3]); 78 | 79 | // This triggers reactivity (reassignment) 80 | numbers = [...numbers, 4]; 81 | 82 | // This does NOT trigger reactivity (mutation) 83 | numbers[0] = 99; 84 | ``` 85 | 86 | ## Best Practices 87 | 88 | - Use $state for any value that needs to trigger UI updates when 89 | changed 90 | - For large arrays or objects that don't need deep reactivity, 91 | consider using $state.raw 92 | - Don't export $state variables directly from modules, use 93 | getter/setter functions instead 94 | - When using TypeScript, you can specify the type: let count = 95 | $state(0) 96 | -------------------------------------------------------------------------------- /definitions/snippet.md: -------------------------------------------------------------------------------- 1 | # Snippet Definition 2 | 3 | **Definition:** TypeScript interface for snippet functions that can be 4 | passed as props or rendered 5 | **Interface:** `Snippet` 6 | **Parameters:** 7 | 8 | - `Parameters` - Tuple type defining parameter types passed to snippet 9 | **Returns:** Callable snippet function that renders template 10 | content 11 | **Usage:** Type snippet props and ensure type safety for snippet 12 | parameters 13 | **Generic:** Supports parameterized snippets with typed arguments 14 | 15 | ## Examples 16 | 17 | ```ts 18 | // Basic snippet interface 19 | import type { Snippet } from 'svelte'; 20 | 21 | interface Props { 22 | header: Snippet; 23 | content: Snippet<[string, number]>; // Takes string and number params 24 | } 25 | 26 | let { header, content }: Props = $props(); 27 | 28 | // Component usage 29 | 39 | 40 | 41 | {@render header()} 42 | {@render content('Hello', 42)} 43 | {@render itemSnippet({ id: 1, name: 'Alice' })} 44 | 45 | 46 | {#if optionalSnippet} 47 | {@render optionalSnippet()} 48 | {/if} 49 | 50 | // Parent component passing typed snippets 51 | 52 | {#snippet header()} 53 |

Title

54 | {/snippet} 55 | 56 | {#snippet content(text, count)} 57 |

{text} - Count: {count}

58 | {/snippet} 59 |
60 | ``` 61 | 62 | ## Related 63 | 64 | - `snippets` - Creating and using snippet templates 65 | - `$props` - Receiving snippet props in components 66 | - `@render` - Rendering snippets with parameters 67 | - `component-events` - Alternative to snippets for component 68 | communication 69 | -------------------------------------------------------------------------------- /definitions/$effect.tracking.md: -------------------------------------------------------------------------------- 1 | # $effect.tracking Definition 2 | 3 | **Definition:** Returns whether code is running inside a tracking 4 | context 5 | **Syntax:** `$effect.tracking(): boolean` 6 | **Parameters:** None 7 | **Returns:** `true` if inside a tracking context (effect or template), 8 | `false` otherwise 9 | **Variants:** 10 | 11 | - Part of `$effect` rune family 12 | 13 | ## Examples 14 | 15 | ```js 16 | // Check tracking context in different locations 17 | console.log('In component setup:', $effect.tracking()); // false 18 | 19 | $effect(() => { 20 | console.log('In effect:', $effect.tracking()); // true 21 | }); 22 | 23 | // In template (always tracked) 24 |

In template: {$effect.tracking()}

25 | 26 | // Advanced usage for abstractions 27 | function createSubscriber(callback) { 28 | if ($effect.tracking()) { 29 | // Only create reactive subscription if we're being tracked 30 | $effect(() => { 31 | const unsubscribe = subscribe(callback); 32 | return unsubscribe; 33 | }); 34 | } else { 35 | // Just call once if not in tracking context 36 | callback(); 37 | } 38 | } 39 | 40 | // Conditional reactive behavior 41 | function smartLogger(message) { 42 | if ($effect.tracking()) { 43 | // Reactive logging - will re-run when dependencies change 44 | console.log('Reactive:', message); 45 | } else { 46 | // One-time logging 47 | console.log('Static:', message); 48 | } 49 | } 50 | 51 | // Usage in custom reactive utilities 52 | function createReactiveValue(initialValue) { 53 | let value = $state(initialValue); 54 | 55 | return { 56 | get() { 57 | if ($effect.tracking()) { 58 | // Return reactive value when being tracked 59 | return value; 60 | } else { 61 | // Return static snapshot when not tracked 62 | return $state.snapshot(value); 63 | } 64 | }, 65 | set(newValue) { 66 | value = newValue; 67 | } 68 | }; 69 | } 70 | ``` 71 | 72 | ## Related 73 | 74 | - `$effect` - For effects that run in tracking context 75 | - `$effect.pending` - For checking pending promises 76 | - Template expressions - Always run in tracking context 77 | -------------------------------------------------------------------------------- /definitions/event-modifiers.md: -------------------------------------------------------------------------------- 1 | # event-modifiers Definition 2 | 3 | **Definition:** Event modifier syntax for controlling event behavior 4 | in Svelte 5 5 | **Syntax:** `on:event|modifier` or `on:event|modifier1|modifier2` 6 | **Modifiers:** 7 | 8 | - `preventDefault` - Calls `event.preventDefault()` 9 | - `stopPropagation` - Calls `event.stopPropagation()` 10 | - `capture` - Adds event listener during capture phase 11 | - `once` - Removes listener after first trigger 12 | - `passive` - Never calls `preventDefault()` for performance 13 | - `nonpassive` - Explicitly non-passive (default) 14 | - `self` - Only trigger if `event.target` is element itself 15 | **Returns:** Modified event handling behavior 16 | **Chaining:** Multiple modifiers can be combined with `|` 17 | 18 | ## Examples 19 | 20 | ```svelte 21 | 22 |
23 | 24 |
25 | 26 | 27 |
28 | 31 |
32 | 33 | 34 |
35 | Capture clicks during capture phase 36 |
37 | 38 | 39 | 42 | 43 | 44 |
45 | Scrollable content 46 |
47 | 48 | 49 |
50 | Child element 51 | Only clicking div itself triggers handler 52 |
53 | 54 | 55 |
56 | Combined modifiers 57 |
58 | ``` 59 | 60 | ## Related 61 | 62 | - `onclick` - Basic event handling patterns 63 | - `custom-events` - Creating and dispatching custom events 64 | - `event-delegation` - Advanced event handling patterns 65 | - `component-events` - Component-to-component event communication 66 | -------------------------------------------------------------------------------- /definitions/migration-patterns.md: -------------------------------------------------------------------------------- 1 | # migration-patterns Definition 2 | 3 | **Definition:** Common Svelte 4 to Svelte 5 conversion patterns and 4 | direct equivalents 5 | **Syntax:** Before/after code comparisons for systematic migration 6 | **Categories:** 7 | 8 | - State management: `let` → `$state` 9 | - Reactivity: `$:` → `$derived` or `$effect` 10 | - Props: `export let` → `$props()` 11 | - Lifecycle: `onMount` → `$effect` 12 | - Events: `on:click` → `onclick` 13 | - Slots: ``/named slots → `{#snippet}`/`{@render}` 14 | - Component events: `createEventDispatcher` → event props (see 15 | `component-events`, `custom-events`) 16 | - Bindings: `bind:` remains; add `$bindable` for 2‑way prop binding 17 | **Returns:** Direct migration patterns for upgrading components 18 | **Purpose:** Systematic conversion guide from Svelte 4 patterns 19 | 20 | ## Examples 21 | 22 | ```ts 23 | // STATE MIGRATION 24 | // Svelte 4 25 | let count = 0; 26 | let items = []; 27 | 28 | // Svelte 5 29 | let count = $state(0); 30 | let items = $state([]); 31 | 32 | // REACTIVE DECLARATIONS 33 | // Svelte 4 34 | let doubled; 35 | $: doubled = count * 2; 36 | 37 | // Svelte 5 38 | let doubled = $derived(count * 2); 39 | 40 | // PROPS MIGRATION 41 | // Svelte 4 42 | export let name = 'default'; 43 | export let optional; 44 | 45 | // Svelte 5 46 | let { name = 'default', optional } = $props(); 47 | 48 | // LIFECYCLE MIGRATION 49 | // Svelte 4 50 | import { onMount, onDestroy } from 'svelte'; 51 | 52 | onMount(() => { 53 | console.log('mounted'); 54 | return () => { 55 | console.log('cleanup'); 56 | }; 57 | }); 58 | 59 | // Svelte 5 60 | $effect(() => { 61 | console.log('mounted'); 62 | return () => { 63 | console.log('cleanup'); 64 | }; 65 | }); 66 | 67 | // SIDE EFFECTS 68 | // Svelte 4 69 | $: { 70 | document.title = `Count: ${count}`; 71 | } 72 | 73 | // Svelte 5 74 | $effect(() => { 75 | document.title = `Count: ${count}`; 76 | }); 77 | ``` 78 | 79 | ## Related 80 | 81 | - `$state` - Replaces `let` for reactive variables 82 | - `$derived` - Replaces reactive declarations `$:` 83 | - `$props` - Replaces `export let` prop declarations 84 | - `$effect` - Replaces `onMount`, `onDestroy`, side-effect `$:` 85 | - `common-mistakes` - Pitfalls when migrating patterns 86 | -------------------------------------------------------------------------------- /definitions/snapshot.md: -------------------------------------------------------------------------------- 1 | # Snapshot Definition 2 | 3 | **Definition:** TypeScript utility type representing the return type 4 | of `$state.snapshot()` 5 | **Type:** `Snapshot = ReturnType>` - 6 | Immutable copy of reactive state 7 | **Parameters:** 8 | 9 | - `T` - The original reactive state type being snapshotted 10 | **Returns:** Static, non-reactive copy of the state value 11 | **Purpose:** Serialization, comparison, debugging, and preventing 12 | reactivity 13 | **Immutability:** Snapshot values cannot trigger reactive updates 14 | 15 | ## Examples 16 | 17 | ```ts 18 | import type { Snapshot } from 'svelte'; 19 | 20 | // Create reactive state 21 | let user = $state({ name: 'Alice', age: 30, hobbies: ['reading'] }); 22 | 23 | // Create typed snapshot 24 | let userSnapshot: Snapshot = $state.snapshot(user); 25 | 26 | // Snapshot is immutable and non-reactive 27 | console.log(userSnapshot.name); // 'Alice' 28 | userSnapshot.name = 'Bob'; // ❌ TypeScript error - read-only 29 | 30 | // Safe for serialization 31 | const serialized = JSON.stringify(userSnapshot); 32 | localStorage.setItem('user', serialized); 33 | 34 | // Comparison without reactivity 35 | function hasUserChanged( 36 | current: typeof user, 37 | previous: Snapshot, 38 | ): boolean { 39 | return ( 40 | current.name !== previous.name || 41 | current.age !== previous.age || 42 | current.hobbies.length !== previous.hobbies.length 43 | ); 44 | } 45 | 46 | // Debugging state at specific moments 47 | let debugSnapshots: Snapshot[] = []; 48 | 49 | $effect(() => { 50 | // Capture snapshot on every change for debugging 51 | debugSnapshots.push($state.snapshot(user)); 52 | console.log('State history:', debugSnapshots); 53 | }); 54 | 55 | // Type-safe snapshot operations 56 | function createBackup(state: T): Snapshot { 57 | return $state.snapshot(state); 58 | } 59 | 60 | let backup = createBackup(user); // Type: Snapshot<{ name: string; age: number; hobbies: string[] }> 61 | ``` 62 | 63 | ## Related 64 | 65 | - `$state.snapshot` - Function that creates snapshots 66 | - `$state` - Original reactive state being snapshotted 67 | - `$state.raw` - Alternative for non-reactive state (but still 68 | mutable) 69 | - `$derived` - For reactive computations based on state 70 | -------------------------------------------------------------------------------- /docs/runes/derived.md: -------------------------------------------------------------------------------- 1 | # $derived Rune 2 | 3 | ## Description 4 | 5 | The $derived rune is used to compute values based on reactive state. 6 | It automatically tracks dependencies and updates when they change. 7 | 8 | ## Basic Syntax 9 | 10 | ```js 11 | // Compute derived value 12 | const fullName = $derived(`${firstName} ${lastName}`); 13 | 14 | // Complex derivation 15 | const filteredItems = $derived( 16 | items.filter((item) => item.price > minPrice), 17 | ); 18 | ``` 19 | 20 | ## Examples 21 | 22 | ### Simple Derivation 23 | 24 | ```svelte 25 | 34 | 35 | 36 |

Count: {count}

37 |

Doubled: {doubled}

38 |

Is even: {isEven ? 'Yes' : 'No'}

39 | ``` 40 | 41 | ### With TypeScript 42 | 43 | ```svelte 44 | 53 | ``` 54 | 55 | ### Complex Derivation with $derived.by 56 | 57 | ```svelte 58 | 74 | 75 | 76 |

Numbers: {numbers.join(', ')}

77 |

Sum: {stats.sum}

78 |

Average: {stats.avg.toFixed(2)}

79 |

Max: {stats.max}

80 |

Min: {stats.min}

81 | ``` 82 | 83 | ## Best Practices 84 | 85 | - Use $derived for values that can be computed from other state 86 | - Keep derivations simple and focused on a single computation 87 | - Avoid side effects in $derived expressions - use $effect for side 88 | effects 89 | - For complex derivations with multiple steps, use $derived.by 90 | - $derived values are read-only - you cannot assign to them 91 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { ValibotJsonSchemaAdapter } from '@tmcp/adapter-valibot'; 4 | import { StdioTransport } from '@tmcp/transport-stdio'; 5 | import { McpServer } from 'tmcp'; 6 | 7 | import { readFileSync } from 'node:fs'; 8 | import { dirname, join } from 'node:path'; 9 | import { fileURLToPath } from 'node:url'; 10 | 11 | import { register_definition_tools } from './tools/definition-tools.js'; 12 | 13 | // Get package info for server metadata 14 | const __filename = fileURLToPath(import.meta.url); 15 | const __dirname = dirname(__filename); 16 | const pkg = JSON.parse( 17 | readFileSync(join(__dirname, '..', 'package.json'), 'utf8'), 18 | ); 19 | const { name, version } = pkg; 20 | 21 | /** 22 | * Main class for the Svelte Docs MCP server 23 | * Pure definition-first architecture using SQLite database 24 | */ 25 | class SvelteDocsServer { 26 | private server: McpServer; 27 | private adapter: ValibotJsonSchemaAdapter; 28 | 29 | constructor() { 30 | // Initialize the adapter 31 | this.adapter = new ValibotJsonSchemaAdapter(); 32 | 33 | // Initialize the server with metadata 34 | this.server = new McpServer( 35 | { 36 | name, 37 | version, 38 | description: 39 | 'MCP server for Svelte docs - Definition-first architecture', 40 | }, 41 | { 42 | adapter: this.adapter, 43 | capabilities: { 44 | tools: { listChanged: true }, 45 | }, 46 | }, 47 | ); 48 | 49 | // Handle process termination 50 | process.on('SIGINT', () => { 51 | process.exit(0); 52 | }); 53 | 54 | process.on('SIGTERM', () => { 55 | process.exit(0); 56 | }); 57 | } 58 | 59 | /** 60 | * Initialize the server with definition tools only 61 | */ 62 | private async initialize(): Promise { 63 | try { 64 | // Register definition tools (single svelte_definition tool) 65 | register_definition_tools(this.server); 66 | } catch (error) { 67 | process.exit(1); 68 | } 69 | } 70 | 71 | /** 72 | * Run the server 73 | */ 74 | public async run(): Promise { 75 | try { 76 | // Initialize the server 77 | await this.initialize(); 78 | 79 | // Setup transport 80 | const transport = new StdioTransport(this.server); 81 | transport.listen(); 82 | } catch (error) { 83 | process.exit(1); 84 | } 85 | } 86 | } 87 | 88 | // Create and run the server 89 | const server = new SvelteDocsServer(); 90 | server.run().catch((error) => { 91 | process.exit(1); 92 | }); 93 | -------------------------------------------------------------------------------- /definitions/custom-events.md: -------------------------------------------------------------------------------- 1 | # custom-events Definition 2 | 3 | **Definition:** Creating and dispatching custom events for 4 | component-to-component communication in Svelte 5 5 | **Syntax:** Event callback props or direct event dispatching 6 | patterns 7 | **Methods:** 8 | 9 | - Callback props: `onclick: (event) => void` prop pattern 10 | - Direct dispatch: Using native `dispatchEvent()` with custom events 11 | - Event forwarding: `on:event` forwarding to parent components 12 | **Returns:** Custom event communication between components 13 | **Replaces:** Svelte 4's `createEventDispatcher()` function 14 | 15 | ## Examples 16 | 17 | ```svelte 18 | 19 | 27 | 28 | 31 | 32 | 33 | 42 | 43 | 47 | 48 | 49 | 62 | 63 |
dispatchCustom({ value: 42 })}> 64 | Click to dispatch custom event 65 |
66 | 67 | 68 | 71 | 72 | 73 | 76 | 77 | 78 | 81 | ``` 82 | 83 | ## Related 84 | 85 | - `component-events` - Patterns for component communication 86 | - `onclick` - Basic event handling 87 | - `event-modifiers` - Modifying event behavior 88 | - `$props` - Receiving event callback props 89 | -------------------------------------------------------------------------------- /docs/overview.md: -------------------------------------------------------------------------------- 1 | # Svelte 5 Overview 2 | 3 | ## Introduction 4 | 5 | Svelte 5 introduces a new reactivity model based on "runes" - special 6 | symbols that influence how the Svelte compiler works. This represents 7 | a significant evolution from Svelte 4's implicit reactivity model. 8 | 9 | ## Key Features 10 | 11 | ### Runes 12 | 13 | Runes are special symbols that start with `$` and influence how the 14 | Svelte compiler works: 15 | 16 | - **$state** - For reactive state 17 | - **$derived** - For computed values 18 | - **$props** - For component props 19 | - **$effect** - For side effects 20 | - **$state.raw** - For non-deeply-reactive state 21 | - **$derived.by** - For complex derivations 22 | - **$bindable** - For two-way binding 23 | 24 | ### Snippets 25 | 26 | Snippets replace slots from Svelte 4, providing a more flexible way to 27 | compose components: 28 | 29 | ```svelte 30 | {#snippet header()} 31 |

My Title

32 | {/snippet} 33 | 34 | {@render header()} 35 | ``` 36 | 37 | ### Event Handling 38 | 39 | Svelte 5 uses standard HTML attributes for event handling: 40 | 41 | ```svelte 42 | 43 | ``` 44 | 45 | ### Component Events 46 | 47 | Component events use callback props instead of event dispatching: 48 | 49 | ```svelte 50 | 51 | 52 | 53 | 54 | console.log(text)} /> 55 | ``` 56 | 57 | ## Major Changes from Svelte 4 58 | 59 | 1. **Explicit Reactivity**: Reactivity is now explicit with runes, 60 | rather than implicit with the `let` keyword 61 | 2. **Runes Replace Special Syntax**: Runes replace special syntax like 62 | `$:` and `export let` 63 | 3. **Snippets Replace Slots**: The slot system has been replaced with 64 | snippets 65 | 4. **Standard Event Attributes**: Event directives (`on:click`) are 66 | replaced with standard HTML attributes (`onclick`) 67 | 5. **Callback Props**: Component events now use callback props instead 68 | of event dispatching 69 | 70 | ## Benefits 71 | 72 | - **Consistency**: The same reactivity model works both inside and 73 | outside components 74 | - **Explicitness**: Reactivity is now explicit, making code easier to 75 | understand 76 | - **Performance**: The new system is more efficient, especially for 77 | large applications 78 | - **TypeScript Integration**: Better TypeScript support with proper 79 | typing of reactive values 80 | - **Portability**: Reactive code can be shared between components more 81 | easily 82 | -------------------------------------------------------------------------------- /definitions/$derived.by.md: -------------------------------------------------------------------------------- 1 | # $derived.by Definition 2 | 3 | **Definition:** Complex derivation using function for expensive 4 | computations 5 | **Syntax:** `$derived.by(fn: () => T): T` 6 | **Parameters:** 7 | 8 | - `fn` - Function that returns the derived value 9 | **Returns:** Computed value that updates when dependencies change 10 | **Variants:** 11 | - Part of `$derived` rune family 12 | 13 | ## Examples 14 | 15 | ```ts 16 | // Expensive computation that should be memoized 17 | let items: number[] = $state([1, 2, 3, 4, 5]); 18 | let filter: 'even' | 'odd' = $state('even'); 19 | 20 | const expensiveResult = $derived.by(() => { 21 | console.log('Computing expensive result...'); 22 | 23 | return items 24 | .filter((item) => 25 | filter === 'even' ? item % 2 === 0 : item % 2 === 1, 26 | ) 27 | .map((item) => item * item) 28 | .reduce((sum, item) => sum + item, 0); 29 | }); 30 | 31 | // Complex async-like patterns (though still synchronous) 32 | const processedData = $derived.by(() => { 33 | if (!items.length) return null; 34 | 35 | let result = items.slice(); 36 | 37 | // Multiple processing steps 38 | result = result.sort((a, b) => a - b); 39 | result = result.map((x) => x * 2); 40 | 41 | return { 42 | processed: result, 43 | sum: result.reduce((a, b) => a + b, 0), 44 | average: result.reduce((a, b) => a + b, 0) / result.length, 45 | }; 46 | }); 47 | 48 | // When you need multiple statements 49 | const validation = $derived.by(() => { 50 | const errors: string[] = []; 51 | 52 | if (items.length === 0) { 53 | errors.push('Items cannot be empty'); 54 | } 55 | 56 | if (items.some((item) => item < 0)) { 57 | errors.push('Items must be positive'); 58 | } 59 | 60 | return { 61 | isValid: errors.length === 0, 62 | errors, 63 | }; 64 | }); 65 | 66 | // Override a derived temporarily (e.g., optimistic UI) 67 | let likes = $derived(post.likes); 68 | async function like() { 69 | likes += 1; // override 70 | try { 71 | await sendLike(); 72 | } catch { 73 | likes -= 1; // rollback on error 74 | } 75 | } 76 | 77 | // Destructuring: each piece remains reactive 78 | let { a, b } = $derived(getPair()); // each remains reactive 79 | 80 | // Dependency control 81 | let total2 = $derived.by(() => { 82 | // anything read synchronously is a dependency 83 | return items.reduce((s, n) => s + n, 0); 84 | }); 85 | 86 | // To exempt values from tracking, use untrack(...) 87 | ``` 88 | 89 | ## Related 90 | 91 | - `$derived` - For simple derived expressions 92 | - `$state` - For reactive state that derived values depend on 93 | - `$effect` - For side effects, not computed values 94 | -------------------------------------------------------------------------------- /docs/runes/effect.md: -------------------------------------------------------------------------------- 1 | # $effect Rune 2 | 3 | ## Description 4 | 5 | The $effect rune is used to run side effects when reactive values 6 | change. It's similar to useEffect in React or onMount/onDestroy in 7 | Svelte 4. 8 | 9 | ## Basic Syntax 10 | 11 | ```js 12 | // Basic effect 13 | $effect(() => { 14 | console.log(`Count is now: ${count}`); 15 | }); 16 | 17 | // Effect with cleanup 18 | $effect(() => { 19 | const interval = setInterval(() => count++, 1000); 20 | return () => clearInterval(interval); 21 | }); 22 | 23 | // Pre-update effect (runs before DOM updates) 24 | $effect.pre(() => { 25 | // Do something before DOM updates 26 | }); 27 | 28 | // One-time effect (like onMount) 29 | $effect( 30 | () => { 31 | // Runs once during initialization 32 | }, 33 | { priority: 1 }, 34 | ); 35 | ``` 36 | 37 | ## Examples 38 | 39 | ### Basic Effect 40 | 41 | ```svelte 42 | 53 | 54 | 55 | ``` 56 | 57 | ### Effect with Cleanup 58 | 59 | ```svelte 60 | 76 | 77 |

Count: {count}

78 | ``` 79 | 80 | ### Conditional Effect 81 | 82 | ```svelte 83 | 93 | 94 | 97 | {#if isVisible} 98 |

Count: {count}

99 | 100 | {/if} 101 | ``` 102 | 103 | ## Best Practices 104 | 105 | - Use $effect for side effects like logging, DOM manipulation, or API 106 | calls 107 | - Return a cleanup function if your effect creates resources that need 108 | to be cleaned up 109 | - Keep effects focused on a single responsibility 110 | - Avoid changing state in effects unless you have safeguards against 111 | infinite loops 112 | - Use $effect.pre for effects that need to run before DOM updates 113 | -------------------------------------------------------------------------------- /definitions/lifecycle-equivalents.md: -------------------------------------------------------------------------------- 1 | # lifecycle-equivalents Definition 2 | 3 | **Definition:** Legacy Svelte 4 lifecycle functions (`onMount`, 4 | `onDestroy`, `beforeUpdate`, `afterUpdate`) mapped to Svelte 5 5 | `$effect` patterns. Prefer `$effect` in Svelte 5. **Syntax:** 6 | `$effect` replacements for `onMount`, `onDestroy`, `beforeUpdate`, 7 | `afterUpdate` 8 | **Conversions:** 9 | 10 | - `onMount` → `$effect` with empty dependency 11 | - `onDestroy` → `$effect` return cleanup function 12 | - `beforeUpdate` → `$effect.pre` 13 | - `afterUpdate` → `$effect` after state changes 14 | **Returns:** Equivalent lifecycle behavior using rune patterns 15 | **Migration:** Direct 1:1 replacements for systematic conversion 16 | 17 | ## Examples 18 | 19 | ```ts 20 | // onMount equivalent 21 | // Svelte 4 (legacy) 22 | import { onMount } from 'svelte'; 23 | onMount(() => { 24 | console.log('Component mounted'); 25 | }); 26 | 27 | // Svelte 5 (preferred) 28 | $effect(() => { 29 | console.log('Component mounted'); 30 | }); 31 | 32 | // onDestroy equivalent 33 | // Svelte 4 (legacy) 34 | import { onDestroy } from 'svelte'; 35 | onDestroy(() => { 36 | console.log('Component destroyed'); 37 | }); 38 | 39 | // Svelte 5 (preferred) 40 | $effect(() => { 41 | return () => { 42 | console.log('Component destroyed'); 43 | }; 44 | }); 45 | 46 | // Combined onMount + onDestroy 47 | // Svelte 4 48 | onMount(() => { 49 | const interval = setInterval(() => {}, 1000); 50 | return () => clearInterval(interval); 51 | }); 52 | 53 | // Svelte 5 54 | $effect(() => { 55 | const interval = setInterval(() => {}, 1000); 56 | return () => clearInterval(interval); 57 | }); 58 | 59 | // beforeUpdate equivalent 60 | // Svelte 4 (legacy) 61 | import { beforeUpdate } from 'svelte'; 62 | beforeUpdate(() => { 63 | console.log('Before DOM updates'); 64 | }); 65 | 66 | // Svelte 5 (preferred) 67 | $effect.pre(() => { 68 | console.log('Before DOM updates'); 69 | }); 70 | 71 | // afterUpdate equivalent 72 | // Svelte 4 (legacy) 73 | import { afterUpdate } from 'svelte'; 74 | afterUpdate(() => { 75 | console.log('After DOM updates'); 76 | }); 77 | 78 | // Svelte 5 (preferred) 79 | let count = $state(0); 80 | $effect(() => { 81 | count; // Track state dependency 82 | console.log('After state updates'); 83 | }); 84 | 85 | // Conditional lifecycle (runs only when condition met) 86 | // Svelte 4 87 | let condition = false; 88 | $: if (condition) { 89 | // Runs when condition becomes true 90 | doSomething(); 91 | } 92 | 93 | // Svelte 5 94 | let condition = $state(false); 95 | $effect(() => { 96 | if (condition) { 97 | doSomething(); 98 | } 99 | }); 100 | ``` 101 | 102 | ## Related 103 | 104 | - `$effect` - Main lifecycle replacement rune 105 | - `$effect.pre` - beforeUpdate equivalent 106 | - `migration-patterns` - Complete Svelte 4→5 conversion guide 107 | - `$effect` - Primary lifecycle mechanism in Svelte 5 108 | - `common-mistakes` - Pitfalls when converting lifecycle code 109 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # mcp-svelte-docs 2 | 3 | ## 0.0.22 4 | 5 | ### Patch Changes 6 | 7 | - 6e6a2cb: remove cleanup and logging 8 | 9 | ## 0.0.21 10 | 11 | ### Patch Changes 12 | 13 | - a7d9758: update defninitions 14 | 15 | ## 0.0.20 16 | 17 | ### Patch Changes 18 | 19 | - d43e63d: add more defintions update readme 20 | 21 | ## 0.0.19 22 | 23 | ### Patch Changes 24 | 25 | - 417c747: remove markdown loaders from package tools 26 | - 53c4b33: wire up db definition loader 27 | 28 | ## 0.0.18 29 | 30 | ### Patch Changes 31 | 32 | - add back defnitions and docs for now 33 | 34 | ## 0.0.17 35 | 36 | ### Patch Changes 37 | 38 | - 89cb89d: more effect definitions, await and remote function 39 | definitions 40 | - 6c7e8ee: improve discoverability on docs 41 | - 32c92e6: add definition tools as primary interface for Svelte docs 42 | - 7bc6c23: update definitions, run prettier format 43 | - 9eee928: ship SQLite instead of .md files 44 | - cfbd7a5: refactor of handlers 45 | - 8fe8e34: replace @mcp sdk with tmcp 46 | 47 | ## 0.0.16 48 | 49 | ### Patch Changes 50 | 51 | - dc6abd6: add await and remote functions docs 52 | 53 | ## 0.0.15 54 | 55 | ### Patch Changes 56 | 57 | - a20fefa: update docs 58 | 59 | ## 0.0.14 60 | 61 | ### Patch Changes 62 | 63 | - 963cbb0: add docs folder 64 | 65 | ## 0.0.13 66 | 67 | ### Patch Changes 68 | 69 | - 52616b4: now common definitions being used 70 | - update readme 71 | 72 | ## 0.0.12 73 | 74 | ### Patch Changes 75 | 76 | - effba1e: we go, no, to rag 77 | 78 | ## 0.0.11 79 | 80 | ### Patch Changes 81 | 82 | - feat: Enhance documentation search with advanced categorization and 83 | term weighting 84 | 85 | ## 0.0.10 86 | 87 | ### Patch Changes 88 | 89 | - feat: Implement comprehensive Svelte documentation server with 90 | advanced search and resource management 91 | 92 | ## 0.0.9 93 | 94 | ### Patch Changes 95 | 96 | - update search and documentation 97 | 98 | ## 0.0.8 99 | 100 | ### Patch Changes 101 | 102 | - Refactor index.ts to modularize database and document handling. 103 | Removed legacy database client and caching functions, integrating 104 | new document-fetching and processing utilities. Enhanced chunking 105 | and metadata management for large documents, improving performance 106 | and maintainability. This update sets the stage for more efficient 107 | document retrieval and search capabilities. 108 | 109 | ## 0.0.7 110 | 111 | ### Patch Changes 112 | 113 | - Update README.md to improve clarity on text search features and 114 | introduce a roadmap section outlining future enhancements, including 115 | semantic search implementation using embeddings. 116 | 117 | ## 0.0.6 118 | 119 | ### Patch Changes 120 | 121 | - Update README.md to reflect new server link and badge for 122 | documentation access 123 | 124 | ## 0.0.5 125 | 126 | ### Patch Changes 127 | 128 | - fix: update database client configuration to support environment 129 | variables for URL and authentication token 130 | 131 | ## 0.0.4 132 | 133 | ### Patch Changes 134 | 135 | - init 136 | -------------------------------------------------------------------------------- /docs/features/events.md: -------------------------------------------------------------------------------- 1 | # Event Handling 2 | 3 | ## Description 4 | 5 | Svelte 5 uses standard HTML attributes for event handling instead of 6 | the directive syntax used in Svelte 4. 7 | 8 | ## Basic Syntax 9 | 10 | ```js 11 | // DOM events 12 | 13 | 14 | // With function shorthand 15 | 16 | 17 | // Event with capture 18 | 19 | 20 | // Preventing default 21 |
{ 22 | e.preventDefault(); 23 | // Handle form submission 24 | }}> 25 | ``` 26 | 27 | ## Examples 28 | 29 | ### Basic Event Handling 30 | 31 | ```svelte 32 | 39 | 40 | 43 | ``` 44 | 45 | ### Inline Event Handlers 46 | 47 | ```svelte 48 | 51 | 52 | 55 | ``` 56 | 57 | ### Event Object Access 58 | 59 | ```svelte 60 | 68 | 69 |
70 | The mouse position is {position.x} x {position.y} 71 |
72 | ``` 73 | 74 | ### Form Events 75 | 76 | ```svelte 77 | 86 | 87 | 88 | name = e.target.value} 92 | /> 93 | 94 |
95 | 96 | {#if submitted} 97 |

You submitted: {name}

98 | {/if} 99 | ``` 100 | 101 | ## Event Modifiers 102 | 103 | In Svelte 5, event modifiers are replaced with wrapper functions: 104 | 105 | ```svelte 106 | 119 | 120 |
121 | 122 |
123 | ``` 124 | 125 | ## Best Practices 126 | 127 | - Use standard HTML attributes for event handling (onclick, onsubmit, 128 | etc.) 129 | - For event modifiers, use wrapper functions instead of the pipe 130 | syntax from Svelte 4 131 | - For multiple handlers, combine them into a single function 132 | - Keep event handlers simple, moving complex logic to separate 133 | functions 134 | - Remember that event names are lowercase in HTML (onclick, not 135 | onClick) 136 | -------------------------------------------------------------------------------- /docs/runes/overview.md: -------------------------------------------------------------------------------- 1 | # Svelte 5 Runes Overview 2 | 3 | ## What Are Runes? 4 | 5 | Runes are special symbols in Svelte 5 that influence how the compiler 6 | works. They start with a `$` character and are used to make reactivity 7 | explicit. Runes replace the special syntax from Svelte 4 (like `$:` 8 | for reactive declarations and `export let` for props). 9 | 10 | ## Core Runes 11 | 12 | ### $state 13 | 14 | Creates reactive state variables: 15 | 16 | ```js 17 | let count = $state(0); 18 | let user = $state({ name: 'Alice', age: 30 }); 19 | 20 | // Access and update directly 21 | count++; 22 | user.name = 'Bob'; 23 | ``` 24 | 25 | ### $derived 26 | 27 | Creates computed values that update automatically when dependencies 28 | change: 29 | 30 | ```js 31 | let count = $state(0); 32 | const doubled = $derived(count * 2); 33 | const isEven = $derived(count % 2 === 0); 34 | ``` 35 | 36 | ### $props 37 | 38 | Declares component props: 39 | 40 | ```js 41 | let { name, age = 21 } = $props(); 42 | let { class: className } = $props(); // Rename props 43 | let { id, ...rest } = $props(); // Rest props 44 | ``` 45 | 46 | ### $effect 47 | 48 | Runs side effects when reactive values change: 49 | 50 | ```js 51 | $effect(() => { 52 | console.log(`Count is now: ${count}`); 53 | 54 | // Optional cleanup function 55 | return () => { 56 | console.log('Cleaning up'); 57 | }; 58 | }); 59 | ``` 60 | 61 | ## Additional Runes 62 | 63 | ### $state.raw 64 | 65 | Creates state that's only reactive when the entire value is 66 | reassigned: 67 | 68 | ```js 69 | let items = $state.raw([1, 2, 3]); 70 | 71 | // This triggers reactivity (reassignment) 72 | items = [...items, 4]; 73 | 74 | // This does NOT trigger reactivity (mutation) 75 | items[0] = 99; 76 | ``` 77 | 78 | ### $derived.by 79 | 80 | Creates complex derived values using a function: 81 | 82 | ```js 83 | const stats = $derived.by(() => { 84 | const sum = numbers.reduce((a, b) => a + b, 0); 85 | const avg = sum / numbers.length; 86 | return { sum, avg }; 87 | }); 88 | ``` 89 | 90 | ### $effect.pre 91 | 92 | Runs effects before DOM updates: 93 | 94 | ```js 95 | $effect.pre(() => { 96 | // Runs before DOM updates 97 | }); 98 | ``` 99 | 100 | ### $bindable 101 | 102 | Enables two-way binding for props: 103 | 104 | ```js 105 | let { value = $bindable(0) } = $props(); 106 | ``` 107 | 108 | ## Benefits of Runes 109 | 110 | 1. **Consistency**: The same reactivity model works both inside and 111 | outside components 112 | 2. **Explicitness**: Reactivity is now explicit, making code easier to 113 | understand 114 | 3. **Portability**: Reactive code can be shared between components 115 | more easily 116 | 4. **TypeScript Integration**: Better TypeScript support with proper 117 | typing of reactive values 118 | 119 | ## Best Practices 120 | 121 | - Use the appropriate rune for each use case 122 | - Keep reactive code simple and focused 123 | - Use TypeScript for better type safety 124 | - Avoid circular dependencies between reactive values 125 | - Remember that runes only work in `.svelte` files or 126 | `.svelte.js`/`.svelte.ts` files 127 | -------------------------------------------------------------------------------- /src/db-loader.ts: -------------------------------------------------------------------------------- 1 | import Database from 'better-sqlite3'; 2 | import { dirname, join } from 'node:path'; 3 | import { fileURLToPath } from 'node:url'; 4 | 5 | // Get the directory of this module 6 | const __filename = fileURLToPath(import.meta.url); 7 | const __dirname = dirname(__filename); 8 | 9 | /** 10 | * Interface for a documentation item (same as markdown-loader) 11 | */ 12 | export interface DocItem { 13 | id: string; 14 | title: string; 15 | content: string; 16 | category: string; 17 | path: string; 18 | } 19 | 20 | let db: Database.Database | null = null; 21 | 22 | /** 23 | * Initialize database connection 24 | */ 25 | function initDb(): Database.Database { 26 | if (!db) { 27 | const dbPath = join(__dirname, 'definitions.db'); 28 | db = Database(dbPath, { readonly: true }); 29 | } 30 | return db; 31 | } 32 | 33 | /** 34 | * Load all documentation items from database 35 | */ 36 | export function load_markdown_docs(): DocItem[] { 37 | const database = initDb(); 38 | const stmt = database.prepare( 39 | 'SELECT id, title, content, category, path FROM definitions ORDER BY category, id', 40 | ); 41 | return stmt.all() as DocItem[]; 42 | } 43 | 44 | /** 45 | * Search for documentation items by query 46 | */ 47 | export function search_docs(query: string): DocItem[] { 48 | const database = initDb(); 49 | const normalizedQuery = `%${query.toLowerCase()}%`; 50 | 51 | const stmt = database.prepare(` 52 | SELECT id, title, content, category, path 53 | FROM definitions 54 | WHERE LOWER(title) LIKE ? 55 | OR LOWER(content) LIKE ? 56 | OR LOWER(category) LIKE ? 57 | OR LOWER(id) LIKE ? 58 | ORDER BY category, id 59 | `); 60 | 61 | return stmt.all( 62 | normalizedQuery, 63 | normalizedQuery, 64 | normalizedQuery, 65 | normalizedQuery, 66 | ) as DocItem[]; 67 | } 68 | 69 | /** 70 | * Get documentation items by category 71 | */ 72 | export function get_docs_by_category(category: string): DocItem[] { 73 | const database = initDb(); 74 | const stmt = database.prepare( 75 | 'SELECT id, title, content, category, path FROM definitions WHERE category = ? ORDER BY id', 76 | ); 77 | return stmt.all(category) as DocItem[]; 78 | } 79 | 80 | /** 81 | * Get a documentation item by ID 82 | */ 83 | export function get_doc_by_id(id: string): DocItem | undefined { 84 | const database = initDb(); 85 | const stmt = database.prepare( 86 | 'SELECT id, title, content, category, path FROM definitions WHERE id = ?', 87 | ); 88 | return stmt.get(id) as DocItem | undefined; 89 | } 90 | 91 | /** 92 | * Get a documentation item by category and ID 93 | */ 94 | export function get_doc_by_category_and_id( 95 | category: string, 96 | id: string, 97 | ): DocItem | undefined { 98 | const database = initDb(); 99 | const stmt = database.prepare( 100 | 'SELECT id, title, content, category, path FROM definitions WHERE category = ? AND id = ?', 101 | ); 102 | return stmt.get(category, id) as DocItem | undefined; 103 | } 104 | 105 | /** 106 | * Close database connection 107 | */ 108 | export function closeDb(): void { 109 | if (db) { 110 | db.close(); 111 | db = null; 112 | } 113 | } 114 | 115 | // Cleanup on process exit 116 | process.on('exit', closeDb); 117 | process.on('SIGINT', closeDb); 118 | process.on('SIGTERM', closeDb); 119 | -------------------------------------------------------------------------------- /docs/features/snippets.md: -------------------------------------------------------------------------------- 1 | # Snippets 2 | 3 | ## Description 4 | 5 | Snippets are a new feature in Svelte 5 that replace slots from 6 | Svelte 4. They allow you to define reusable chunks of markup that can 7 | be passed to components. 8 | 9 | ## Basic Syntax 10 | 11 | ```js 12 | // Child component 13 | let { header, default: children, item } = $props(); 14 | 15 | {@render header?.()} 16 | {@render children?.()} 17 | {#each items as item} 18 | {@render item?.(item)} 19 | {/each} 20 | 21 | // Parent component 22 | 23 | {#snippet header()} 24 |

Title

25 | {/snippet} 26 |

Default content

27 | {#snippet item(data)} 28 |
{data.name}
29 | {/snippet} 30 |
31 | ``` 32 | 33 | ## Examples 34 | 35 | ### Basic Snippet 36 | 37 | ```svelte 38 | {#snippet figure(image)} 39 |
40 | {image.caption} 46 |
{image.caption}
47 |
48 | {/snippet} 49 | 50 | {@render figure(headerImage)} 51 | {@render figure(footerImage)} 52 | ``` 53 | 54 | ### Passing Snippets to Components 55 | 56 | ```svelte 57 | 58 | 61 | 62 | 63 | 64 | {@render header?.()} 65 | 66 | 67 | {#each data as item} 68 | {@render row?.(item)} 69 | {/each} 70 | 71 |
72 | 73 | 74 | 82 | 83 | {#snippet header()} 84 | fruit 85 | qty 86 | price 87 | total 88 | {/snippet} 89 | 90 | {#snippet row(fruit)} 91 | {fruit.name} 92 | {fruit.qty} 93 | {fruit.price} 94 | {fruit.qty * fruit.price} 95 | {/snippet} 96 | 97 | 98 | ``` 99 | 100 | ### Recursive Snippets 101 | 102 | ```svelte 103 | 119 | 120 | {#snippet tree(items)} 121 |
    122 | {#each items as item} 123 |
  • 124 | {item.name} 125 | {#if item.children} 126 | {@render tree(item.children)} 127 | {/if} 128 |
  • 129 | {/each} 130 |
131 | {/snippet} 132 | 133 | {@render tree(items)} 134 | ``` 135 | 136 | ## Best Practices 137 | 138 | - Use snippets to reduce duplication in your templates 139 | - Snippets can be passed as props to components 140 | - Snippets have lexical scoping rules - they are only visible in the 141 | same scope they are defined in 142 | - Use parameters to make snippets more flexible 143 | - Snippets can reference other snippets and even themselves (for 144 | recursion) 145 | -------------------------------------------------------------------------------- /src/build-database.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import Database from 'better-sqlite3'; 4 | import { 5 | existsSync, 6 | readFileSync, 7 | readdirSync, 8 | statSync, 9 | unlinkSync, 10 | } from 'node:fs'; 11 | import { basename, extname, join } from 'node:path'; 12 | 13 | const DB_PATH = 'dist/definitions.db'; 14 | 15 | interface Doc { 16 | id: string; 17 | title: string; 18 | content: string; 19 | category: string; 20 | path: string; 21 | } 22 | 23 | // Simple markdown loader (copy of the TS version) 24 | function load_markdown_docs(baseDir: string): Doc[] { 25 | const docs: Doc[] = []; 26 | 27 | function process_directory(dir: string, category: string): void { 28 | const files = readdirSync(dir, { withFileTypes: true }); 29 | 30 | for (const file of files) { 31 | const full_path = join(dir, file.name); 32 | 33 | if (file.isDirectory()) { 34 | process_directory(full_path, file.name); 35 | } else if (file.isFile() && extname(file.name) === '.md') { 36 | const content = readFileSync(full_path, 'utf-8'); 37 | const id = basename(file.name, '.md'); 38 | 39 | const title_match = content.match(/^# (.+)$/m); 40 | const title = title_match ? title_match[1] : id; 41 | 42 | docs.push({ 43 | id, 44 | title, 45 | content, 46 | category, 47 | path: full_path, 48 | }); 49 | } 50 | } 51 | } 52 | 53 | process_directory(baseDir, 'root'); 54 | return docs; 55 | } 56 | 57 | // Remove existing database 58 | if (existsSync(DB_PATH)) { 59 | unlinkSync(DB_PATH); 60 | console.log('🗑️ Removed existing database'); 61 | } 62 | 63 | // Create new database 64 | const db = Database(DB_PATH); 65 | console.log('📝 Created new database'); 66 | 67 | // Create schema 68 | db.exec(` 69 | CREATE TABLE definitions ( 70 | id TEXT PRIMARY KEY, 71 | title TEXT NOT NULL, 72 | content TEXT NOT NULL, 73 | category TEXT NOT NULL, 74 | path TEXT NOT NULL, 75 | created_at INTEGER DEFAULT (strftime('%s', 'now')) 76 | ); 77 | 78 | CREATE INDEX idx_category ON definitions(category); 79 | CREATE INDEX idx_title ON definitions(title); 80 | CREATE INDEX idx_id_category ON definitions(id, category); 81 | `); 82 | 83 | console.log('🏗️ Created database schema'); 84 | 85 | // Prepare insert statement 86 | const insert = db.prepare(` 87 | INSERT INTO definitions (id, title, content, category, path) 88 | VALUES (?, ?, ?, ?, ?) 89 | `); 90 | 91 | // Load markdown docs 92 | const docs = load_markdown_docs(join(process.cwd(), 'definitions')); 93 | console.log(`📚 Loaded ${docs.length} markdown documents`); 94 | 95 | // Insert all docs 96 | const insert_many = db.transaction((docs: Doc[]) => { 97 | for (const doc of docs) { 98 | insert.run( 99 | doc.id, 100 | doc.title, 101 | doc.content, 102 | doc.category, 103 | doc.path, 104 | ); 105 | } 106 | }); 107 | 108 | insert_many(docs); 109 | 110 | // Get stats 111 | const stats = db 112 | .prepare( 113 | 'SELECT COUNT(*) as count, category FROM definitions GROUP BY category', 114 | ) 115 | .all() as Array<{ count: number; category: string }>; 116 | console.log('📊 Database populated:'); 117 | stats.forEach((stat) => { 118 | console.log(` ${stat.category}: ${stat.count} definitions`); 119 | }); 120 | 121 | // Close database 122 | db.close(); 123 | 124 | // Show file size 125 | const size = statSync(DB_PATH).size; 126 | console.log(`💾 Database size: ${(size / 1024).toFixed(1)}KB`); 127 | 128 | console.log('✅ Database build complete!'); 129 | -------------------------------------------------------------------------------- /docs/features/component-events.md: -------------------------------------------------------------------------------- 1 | # Component Events 2 | 3 | ## Description 4 | 5 | In Svelte 5, component events are handled using callback props instead 6 | of the event dispatching mechanism used in Svelte 4. 7 | 8 | ## Basic Syntax 9 | 10 | ```js 11 | // Child component 12 | let { onMessage } = $props(); 13 | 14 | 15 | // Parent component 16 | console.log(text)} /> 17 | ``` 18 | 19 | ## Examples 20 | 21 | ### Basic Component Events 22 | 23 | ```svelte 24 | 25 | 32 | 33 | 36 | 37 | 38 | 47 | 48 | 49 | 50 |
51 |

Messages:

52 |
    53 | {#each messages as message} 54 |
  • {message}
  • 55 | {/each} 56 |
57 |
58 | ``` 59 | 60 | ### Passing Data with Events 61 | 62 | ```svelte 63 | 64 | 67 | 68 |
    69 | {#each items as item} 70 |
  • onSelect(item)}> 71 | {item.name} 72 |
  • 73 | {/each} 74 |
75 | 76 | 77 | 92 | 93 |
94 | 95 | 96 | {#if selectedItem} 97 |
98 |

Selected: {selectedItem.name}

99 |

Price: ${selectedItem.price}

100 |
101 | {/if} 102 |
103 | ``` 104 | 105 | ### Multiple Event Callbacks 106 | 107 | ```svelte 108 | 109 | 119 | 120 |
121 |
122 | 123 | name = e.target.value} 128 | /> 129 |
130 | 131 |
132 | 133 | email = e.target.value} 138 | /> 139 |
140 | 141 |
142 | 143 | 144 |
145 | 146 | ``` 147 | 148 | ## Best Practices 149 | 150 | - Use callback props instead of event dispatching for component 151 | communication 152 | - Name callback props with an "on" prefix (e.g., onSubmit, onChange) 153 | - Pass data as an object to allow for future extensibility 154 | - For multiple events, use multiple callback props 155 | - Consider using TypeScript to type your callback props 156 | -------------------------------------------------------------------------------- /docs/patterns/global-state.md: -------------------------------------------------------------------------------- 1 | # Global State Patterns 2 | 3 | ## Description 4 | 5 | Svelte 5 provides several approaches to managing global state outside 6 | of components. These patterns leverage the new runes system for 7 | reactivity. 8 | 9 | ## Module-Level State 10 | 11 | ### Basic Module State 12 | 13 | ```js 14 | // store.js 15 | import { state } from 'svelte'; 16 | 17 | export const count = state(0); 18 | 19 | export function increment() { 20 | count.value++; 21 | } 22 | 23 | export function decrement() { 24 | count.value--; 25 | } 26 | ``` 27 | 28 | ```svelte 29 | 30 | 33 | 34 | 35 | {count.value} 36 | 37 | ``` 38 | 39 | ### Custom Store Pattern 40 | 41 | ```js 42 | // userStore.js 43 | import { state } from 'svelte'; 44 | 45 | function createUserStore() { 46 | const user = state(null); 47 | const loading = state(false); 48 | const error = state(null); 49 | 50 | async function login(username, password) { 51 | loading.value = true; 52 | error.value = null; 53 | 54 | try { 55 | // Simulate API call 56 | const response = await fetch('/api/login', { 57 | method: 'POST', 58 | body: JSON.stringify({ username, password }), 59 | headers: { 'Content-Type': 'application/json' }, 60 | }); 61 | 62 | if (!response.ok) throw new Error('Login failed'); 63 | 64 | const userData = await response.json(); 65 | user.value = userData; 66 | } catch (err) { 67 | error.value = err.message; 68 | } finally { 69 | loading.value = false; 70 | } 71 | } 72 | 73 | function logout() { 74 | user.value = null; 75 | } 76 | 77 | return { 78 | get user() { 79 | return user.value; 80 | }, 81 | get loading() { 82 | return loading.value; 83 | }, 84 | get error() { 85 | return error.value; 86 | }, 87 | login, 88 | logout, 89 | }; 90 | } 91 | 92 | export const userStore = createUserStore(); 93 | ``` 94 | 95 | ## Reactivity Outside Components 96 | 97 | Using runes in `.svelte.js` or `.svelte.ts` files: 98 | 99 | ```js 100 | // counter.svelte.js 101 | export function createCounter() { 102 | let count = $state(0); 103 | 104 | return { 105 | get count() { 106 | return count; 107 | }, 108 | increment: () => count++, 109 | }; 110 | } 111 | ``` 112 | 113 | ```svelte 114 | 119 | 120 | 123 | ``` 124 | 125 | ## Context API 126 | 127 | For component tree state sharing: 128 | 129 | ```svelte 130 | 131 | 142 | 143 |
144 | 145 |
146 | 147 | 148 | 153 | 154 | 157 | ``` 158 | 159 | ## Best Practices 160 | 161 | - Choose the simplest pattern that meets your needs 162 | - For small applications, module-level state is often sufficient 163 | - For larger applications, consider custom stores with clear APIs 164 | - Use context for state that should be shared down a component tree 165 | - Keep state logic separate from UI components 166 | - Consider using TypeScript for better type safety 167 | -------------------------------------------------------------------------------- /docs/common-mistakes/props-mistakes.md: -------------------------------------------------------------------------------- 1 | # Common Props Mistakes 2 | 3 | ## Forgetting to Destructure Props 4 | 5 | ### Incorrect 6 | 7 | ```svelte 8 | 16 | 17 |
Hello, {props.name}
18 | ``` 19 | 20 | ### Correct 21 | 22 | ```svelte 23 | 31 | 32 |
Hello, {name}
33 | ``` 34 | 35 | ### Explanation 36 | 37 | In Svelte 5, props need to be destructured to access them reactively. 38 | If you use the entire props object, changes to individual props won't 39 | trigger reactivity. 40 | 41 | ## Not Providing Default Values 42 | 43 | ### Incorrect 44 | 45 | ```svelte 46 | 52 | ``` 53 | 54 | ### Correct 55 | 56 | ```svelte 57 | 63 | ``` 64 | 65 | ### Explanation 66 | 67 | Always provide default values for optional props to prevent errors 68 | when they're not provided. This makes your components more robust and 69 | easier to use. 70 | 71 | ## Forgetting to Use $bindable for Two-Way Binding 72 | 73 | ### Incorrect 74 | 75 | ```svelte 76 | 84 | 85 | 86 | ``` 87 | 88 | ### Correct 89 | 90 | ```svelte 91 | 99 | 100 | 101 | ``` 102 | 103 | ### Explanation 104 | 105 | To enable two-way binding in Svelte 5, you need to use the $bindable 106 | rune. This allows the child component to update the parent's value 107 | when the prop changes. 108 | 109 | ## Modifying Props Directly 110 | 111 | ### Incorrect 112 | 113 | ```svelte 114 | 122 | ``` 123 | 124 | ### Correct 125 | 126 | ```svelte 127 | 136 | ``` 137 | 138 | ### Explanation 139 | 140 | Props should be treated as immutable. Instead of modifying them 141 | directly, create a new object and use a callback prop to inform the 142 | parent component of the change. 143 | 144 | ## Not Using TypeScript for Complex Props 145 | 146 | ### Incorrect 147 | 148 | ```svelte 149 | 155 | ``` 156 | 157 | ### Correct 158 | 159 | ```svelte 160 | 172 | ``` 173 | 174 | ### Explanation 175 | 176 | For complex props, using TypeScript provides better type checking and 177 | helps prevent runtime errors. It also improves developer experience 178 | with better autocompletion and documentation. 179 | -------------------------------------------------------------------------------- /definitions/event-delegation.md: -------------------------------------------------------------------------------- 1 | # event-delegation Definition 2 | 3 | Note: This page describes a common JavaScript pattern and is not an 4 | official Svelte/SvelteKit API reference. For canonical event handling 5 | docs see Svelte’s template syntax (e.g. `onclick`) and events 6 | reference. 7 | 8 | **Definition:** Advanced event handling patterns using event 9 | delegation for performance and dynamic content 10 | **Syntax:** Single parent event listener (e.g., `onclick`) handling 11 | events from multiple child elements 12 | **Benefits:** 13 | 14 | - Better performance for many similar elements 15 | - Automatic handling of dynamically added elements 16 | - Reduced memory usage from fewer event listeners 17 | **Techniques:** `event.target`, `event.currentTarget`, element 18 | matching, event bubbling 19 | **Use Cases:** Tables, lists, dynamic content, performance 20 | optimization 21 | 22 | ## Examples 23 | 24 | ```svelte 25 | 72 | 73 | 74 |
75 | {#each items as item (item.id)} 76 |
81 | {#if item.type === 'button'} 82 | 85 | {:else} 86 | 87 | {item.name} 88 | 89 | {/if} 90 |
91 | {/each} 92 |
93 | 94 | 95 | 96 | 97 | 113 | 114 |
115 | 116 | {#each tableData as row (row.id)} 117 | 118 | 119 | 120 | 121 | 122 | {/each} 123 | 124 |
{row.name}EditDelete
125 | ``` 126 | 127 | ## Related 128 | 129 | - `event-modifiers` - Controlling event behavior 130 | - `custom-events` - Creating custom event patterns 131 | - `onclick` - Basic event handling 132 | - `$state` - Managing dynamic content state 133 | -------------------------------------------------------------------------------- /docs/common-mistakes/state-mistakes.md: -------------------------------------------------------------------------------- 1 | # Common State Management Mistakes 2 | 3 | ## Exporting State Variables Directly 4 | 5 | ### Incorrect 6 | 7 | ```js 8 | // counter.svelte.js 9 | let count = $state(0); 10 | 11 | export { count }; 12 | ``` 13 | 14 | ### Correct 15 | 16 | ```js 17 | // counter.svelte.js 18 | let count = $state(0); 19 | 20 | export function getCount() { 21 | return count; 22 | } 23 | 24 | export function setCount(value) { 25 | count = value; 26 | } 27 | ``` 28 | 29 | ### Explanation 30 | 31 | When you export a stateful variable directly, the reactivity is lost 32 | when it's imported elsewhere. This is because the importing module 33 | only gets the current value, not the reactive binding. Instead, export 34 | functions that access and modify the state. 35 | 36 | ## Using Object Getters/Setters 37 | 38 | ### Incorrect 39 | 40 | ```js 41 | // counter.svelte.js 42 | let count = $state(0); 43 | 44 | export default { 45 | count, // Direct export of state variable 46 | increment: () => count++, 47 | }; 48 | ``` 49 | 50 | ### Correct 51 | 52 | ```js 53 | // counter.svelte.js 54 | let count = $state(0); 55 | 56 | export default { 57 | get count() { 58 | return count; 59 | }, 60 | set count(value) { 61 | count = value; 62 | }, 63 | increment: () => count++, 64 | }; 65 | ``` 66 | 67 | ### Explanation 68 | 69 | Using getters and setters ensures that the state is always accessed 70 | and modified through the reactive system. This maintains reactivity 71 | when the object is used in other components. 72 | 73 | ## Mutating State Objects Directly 74 | 75 | ### Incorrect 76 | 77 | ```svelte 78 | 86 | ``` 87 | 88 | ### Correct 89 | 90 | ```svelte 91 | 99 | ``` 100 | 101 | ### Explanation 102 | 103 | While Svelte 5's $state does track deep updates, it's still a good 104 | practice to avoid direct mutations and instead create new objects. 105 | This makes state changes more predictable and helps prevent bugs, 106 | especially with more complex state structures. 107 | 108 | ## Forgetting to Use $state.raw for Large Collections 109 | 110 | ### Incorrect 111 | 112 | ```svelte 113 | 121 | ``` 122 | 123 | ### Correct 124 | 125 | ```svelte 126 | 134 | ``` 135 | 136 | ### Explanation 137 | 138 | For large collections, using $state.raw can be more performant because 139 | it only triggers reactivity when the entire value is reassigned, not 140 | when individual properties are changed. However, you must remember to 141 | create a new array or object when making changes. 142 | 143 | ## Circular Dependencies in Derived Values 144 | 145 | ### Incorrect 146 | 147 | ```svelte 148 | 159 | ``` 160 | 161 | ### Correct 162 | 163 | ```svelte 164 | 178 | ``` 179 | 180 | ### Explanation 181 | 182 | Creating circular dependencies between reactive values can lead to 183 | infinite update loops. Always ensure that your reactive flow is 184 | unidirectional, with clear inputs and outputs. 185 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mcp-svelte-docs 2 | 3 | A Model Context Protocol (MCP) server providing authoritative Svelte 5 4 | and SvelteKit definitions extracted directly from TypeScript 5 | declarations. Get precise syntax, parameters, and examples for all 6 | Svelte 5 concepts through a single, unified interface. 7 | 8 | ## Architecture 9 | 10 | **Definition-First Approach**: Rather than multiple specialized tools, 11 | this server provides one powerful `svelte_definition` tool that 12 | accesses 28+ comprehensive definitions covering: 13 | 14 | - **All Svelte 5 runes** ($state, $derived, $props, $effect variants) 15 | - **Modern features** (snippets, await expressions, remote functions) 16 | - **Event handling** (DOM events, custom events, component 17 | communication) 18 | - **Migration guidance** (Svelte 4 to 5 patterns and best practices) 19 | - **TypeScript interfaces** (Snippet, Snapshot types) 20 | - **Advanced patterns** (global state, common mistakes, lifecycle 21 | equivalents) 22 | 23 | ## Available Tool 24 | 25 | ### `svelte_definition` 26 | 27 | **Single, powerful tool** for all Svelte 5 and SvelteKit concepts: 28 | 29 | ```typescript 30 | svelte_definition(identifier: string, format?: "syntax"|"quick"|"full") 31 | ``` 32 | 33 | **Examples:** 34 | 35 | - `svelte_definition("$state")` - Complete $state documentation 36 | - `svelte_definition("snippets", "quick")` - Snippet overview with 37 | example 38 | - `svelte_definition("onclick", "syntax")` - Just the TypeScript 39 | signature 40 | - `svelte_definition("migration-patterns")` - Svelte 4 → 5 migration 41 | guide 42 | 43 | **Response Formats:** 44 | 45 | - `"syntax"` - TypeScript signature only (~50 words) 46 | - `"quick"` - Definition + minimal example (~200 words) 47 | - `"full"` - Complete documentation with examples (~500-1000 words, 48 | default) 49 | 50 | ### Available Identifiers (28+) 51 | 52 | **Core Runes:** `$state`, `$state.raw`, `$state.snapshot`, `$derived`, 53 | `$derived.by`, `$props`, `$bindable`, `$effect`, `$effect.pre`, 54 | `$effect.root`, `$effect.pending`, `$effect.tracking` 55 | 56 | **Development Tools:** `$inspect`, `$host` 57 | 58 | **Features & Patterns:** `snippets`, `onclick`, `component-events`, 59 | `migration-patterns`, `await-expressions`, `remote-functions`, 60 | `global-state`, `common-mistakes`, `lifecycle-equivalents` 61 | 62 | **Event Handling:** `custom-events`, `event-delegation`, 63 | `event-modifiers` 64 | 65 | **TypeScript Interfaces:** `snippet`, `snapshot` 66 | 67 | ## Key Features 68 | 69 | ### 🎯 **Authoritative & TypeScript-First** 70 | 71 | - **Direct from Source**: Definitions extracted from official Svelte 5 72 | TypeScript declarations 73 | - **Always Current**: Reflects the actual API, not outdated tutorials 74 | - **Type-Safe**: Includes precise parameter types, return values, and 75 | constraints 76 | 77 | ### ⚡ **Single Interface, Complete Coverage** 78 | 79 | - **One Tool**: `svelte_definition` replaces 16+ specialized tools 80 | - **28+ Definitions**: Every Svelte 5 rune, feature, and pattern 81 | covered 82 | - **Consistent Responses**: Same interface whether you need `$state` 83 | or `remote-functions` 84 | 85 | ### 🚀 **Modern Svelte 5 & SvelteKit Support** 86 | 87 | - **Await Expressions**: Async operations directly in templates 88 | (`await-expressions`) 89 | - **Remote Functions**: Type-safe client-server communication 90 | (`remote-functions`) 91 | - **All Runes**: Complete `$effect` family, `$state` variants, 92 | `$derived.by`, `$bindable` 93 | - **Advanced Patterns**: Event handling, global state, component 94 | communication 95 | 96 | ### 📚 **Smart Error Recovery** 97 | 98 | - **Fuzzy Matching**: Suggests correct identifiers for typos 99 | - **Related Concepts**: Points to similar definitions when searches 100 | fail 101 | - **Migration Help**: Converts Svelte 4 patterns to Svelte 5 102 | equivalents 103 | 104 | ## Config 105 | 106 | Claude Desktop (via WSL) 107 | 108 | ```json 109 | { 110 | "mcpServers": { 111 | "mcp-svelte-docs": { 112 | "command": "wsl.exe", 113 | "args": ["bash", "-c", "npx -y mcp-svelte-docs"] 114 | } 115 | } 116 | } 117 | ``` 118 | 119 | Cursor 120 | 121 | [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=mcp-svelte-docs&config=eyJjb21tYW5kIjoibnB4IC15IG1jcC1zdmVsdGUtZG9jcyJ9) 122 | 123 | Windsurf (via WSL) 124 | 125 | ```json 126 | { 127 | "mcpServers": { 128 | "mcp-svelte-docs": { 129 | "command": "wsl.exe", 130 | "args": ["bash", "-c", "npx -y mcp-svelte-docs"] 131 | } 132 | } 133 | } 134 | ``` 135 | 136 | Windows (without WSL) 137 | 138 | ```json 139 | { 140 | "mcpServers": { 141 | "mcp-svelte-docs": { 142 | "command": "npx", 143 | "args": ["-y", "mcp-svelte-docs"] 144 | } 145 | } 146 | } 147 | ``` 148 | 149 | macOS / Linux 150 | 151 | ```json 152 | { 153 | "mcpServers": { 154 | "mcp-svelte-docs": { 155 | "command": "npx", 156 | "args": ["-y", "mcp-svelte-docs"] 157 | } 158 | } 159 | } 160 | ``` 161 | 162 | ## Contributing 163 | 164 | Contributions are welcome! Please feel free to submit a Pull Request. 165 | 166 | ## License 167 | 168 | MIT License - see the [LICENSE](LICENSE) file for details. 169 | 170 | ## Acknowledgments 171 | 172 | Built on: 173 | 174 | - [Model Context Protocol](https://github.com/modelcontextprotocol) 175 | - [Svelte](https://svelte.dev) 176 | -------------------------------------------------------------------------------- /docs/patterns/migration.md: -------------------------------------------------------------------------------- 1 | # Svelte 4 to Svelte 5 Migration Patterns 2 | 3 | ## Description 4 | 5 | This guide shows common patterns for migrating from Svelte 4 to Svelte 6 | 5, focusing on the key syntax changes. 7 | 8 | ## Reactive State 9 | 10 | ### Svelte 4 11 | 12 | ```svelte 13 | 20 | 21 | 24 | ``` 25 | 26 | ### Svelte 5 27 | 28 | ```svelte 29 | 36 | 37 | 40 | ``` 41 | 42 | ## Reactive Declarations 43 | 44 | ### Svelte 4 45 | 46 | ```svelte 47 | 52 | ``` 53 | 54 | ### Svelte 5 55 | 56 | ```svelte 57 | 62 | ``` 63 | 64 | ## Component Props 65 | 66 | ### Svelte 4 67 | 68 | ```svelte 69 | 73 | ``` 74 | 75 | ### Svelte 5 76 | 77 | ```svelte 78 | 81 | ``` 82 | 83 | ## Event Handling 84 | 85 | ### Svelte 4 86 | 87 | ```svelte 88 | 89 | 90 | ``` 91 | 92 | ### Svelte 5 93 | 94 | ```svelte 95 | 96 | 97 | ``` 98 | 99 | ## Lifecycle Methods 100 | 101 | ### Svelte 4 102 | 103 | ```svelte 104 | 117 | ``` 118 | 119 | ### Svelte 5 120 | 121 | ```svelte 122 | 135 | ``` 136 | 137 | ## Slots 138 | 139 | ### Svelte 4 140 | 141 | ```svelte 142 | 143 |
144 |
145 | Default header 146 |
147 |
148 | Default content 149 |
150 | 153 |
154 | 155 | 156 | 157 |

My Card

158 |

Some content

159 | 160 |
161 | ``` 162 | 163 | ### Svelte 5 164 | 165 | ```svelte 166 | 167 | 170 | 171 |
172 |
173 | {@render header?.() ??
Default header
} 174 |
175 |
176 | {@render content?.() ??
Default content
} 177 |
178 | 181 |
182 | 183 | 184 | 185 | {#snippet header()} 186 |

My Card

187 | {/snippet} 188 |

Some content

189 | {#snippet footer()} 190 | 191 | {/snippet} 192 |
193 | ``` 194 | 195 | ## Component Events 196 | 197 | ### Svelte 4 198 | 199 | ```svelte 200 | 201 | 209 | 210 | 211 | 212 | 213 | alert(e.detail)} /> 214 | ``` 215 | 216 | ### Svelte 5 217 | 218 | ```svelte 219 | 220 | 227 | 228 | 229 | 230 | 231 | alert(message)} /> 232 | ``` 233 | 234 | ## Store Subscriptions 235 | 236 | ### Svelte 4 237 | 238 | ```svelte 239 | 248 | 249 | 252 | ``` 253 | 254 | ### Svelte 5 255 | 256 | ```svelte 257 | 266 | 267 | 270 | ``` 271 | 272 | ## Best Practices 273 | 274 | - Migrate one component at a time 275 | - Start with leaf components (those with no children) 276 | - Use the Svelte Language Tools to help with migration 277 | - Test thoroughly after each component migration 278 | - Consider using the official migration tool when available 279 | -------------------------------------------------------------------------------- /src/db-definition-loader.ts: -------------------------------------------------------------------------------- 1 | import Database from 'better-sqlite3'; 2 | import { dirname, join } from 'node:path'; 3 | import { fileURLToPath } from 'node:url'; 4 | 5 | // Get the directory of this module 6 | const __filename = fileURLToPath(import.meta.url); 7 | const __dirname = dirname(__filename); 8 | 9 | /** 10 | * Interface for a definition item (same as definition-loader) 11 | */ 12 | export interface DefinitionItem { 13 | identifier: string; 14 | title: string; 15 | content: string; 16 | related: string[]; 17 | path: string; 18 | } 19 | 20 | let db: Database.Database | null = null; 21 | 22 | /** 23 | * Initialize database connection 24 | */ 25 | function initDb(): Database.Database { 26 | if (!db) { 27 | const dbPath = join(__dirname, 'definitions.db'); 28 | db = Database(dbPath, { readonly: true }); 29 | } 30 | return db; 31 | } 32 | 33 | /** 34 | * Extract related definitions from the content 35 | */ 36 | function extract_related_definitions(content: string): string[] { 37 | const related: string[] = []; 38 | 39 | // Look for the Related section 40 | const related_section_match = content.match( 41 | /## Related\n([\s\S]*?)(?=\n##|$)/, 42 | ); 43 | if (related_section_match) { 44 | const related_content = related_section_match[1]; 45 | 46 | // Extract identifiers from markdown links and code blocks 47 | const identifier_matches = related_content.matchAll(/`([^`]+)`/g); 48 | for (const match of identifier_matches) { 49 | const identifier = match[1]; 50 | if (identifier && !related.includes(identifier)) { 51 | related.push(identifier); 52 | } 53 | } 54 | } 55 | 56 | return related; 57 | } 58 | 59 | /** 60 | * Load all definitions from database 61 | */ 62 | export function load_definitions(): DefinitionItem[] { 63 | const database = initDb(); 64 | const stmt = database.prepare( 65 | 'SELECT id, title, content, path FROM definitions ORDER BY id', 66 | ); 67 | const rows = stmt.all() as Array<{ 68 | id: string; 69 | title: string; 70 | content: string; 71 | path: string; 72 | }>; 73 | 74 | return rows.map((row) => ({ 75 | identifier: row.id, 76 | title: row.title, 77 | content: row.content, 78 | related: extract_related_definitions(row.content), 79 | path: row.path, 80 | })); 81 | } 82 | 83 | /** 84 | * Get a definition by identifier 85 | */ 86 | export function get_definition_by_identifier( 87 | identifier: string, 88 | ): DefinitionItem | undefined { 89 | const database = initDb(); 90 | const stmt = database.prepare( 91 | 'SELECT id, title, content, path FROM definitions WHERE id = ?', 92 | ); 93 | const row = stmt.get(identifier) as 94 | | { id: string; title: string; content: string; path: string } 95 | | undefined; 96 | 97 | if (!row) return undefined; 98 | 99 | return { 100 | identifier: row.id, 101 | title: row.title, 102 | content: row.content, 103 | related: extract_related_definitions(row.content), 104 | path: row.path, 105 | }; 106 | } 107 | 108 | /** 109 | * Search for definitions by query (fuzzy matching) 110 | */ 111 | export function search_definitions(query: string): DefinitionItem[] { 112 | const database = initDb(); 113 | const normalizedQuery = `%${query.toLowerCase()}%`; 114 | 115 | // Get all potentially matching rows with scoring 116 | const stmt = database.prepare(` 117 | SELECT id, title, content, path, 118 | CASE 119 | WHEN LOWER(id) = LOWER(?) THEN 100 120 | WHEN LOWER(id) LIKE LOWER(? || '%') THEN 80 121 | WHEN LOWER(id) LIKE ? THEN 60 122 | WHEN LOWER(title) LIKE ? THEN 40 123 | WHEN LOWER(content) LIKE ? THEN 20 124 | ELSE 0 125 | END as score 126 | FROM definitions 127 | WHERE score > 0 128 | ORDER BY score DESC, id 129 | `); 130 | 131 | const rows = stmt.all( 132 | query, 133 | query, 134 | normalizedQuery, 135 | normalizedQuery, 136 | normalizedQuery, 137 | ) as Array<{ 138 | id: string; 139 | title: string; 140 | content: string; 141 | path: string; 142 | score: number; 143 | }>; 144 | 145 | return rows.map((row) => ({ 146 | identifier: row.id, 147 | title: row.title, 148 | content: row.content, 149 | related: extract_related_definitions(row.content), 150 | path: row.path, 151 | })); 152 | } 153 | 154 | /** 155 | * Get all available definition identifiers 156 | */ 157 | export function get_all_identifiers(): string[] { 158 | const database = initDb(); 159 | const stmt = database.prepare( 160 | 'SELECT id FROM definitions ORDER BY id', 161 | ); 162 | return stmt.all().map((row: any) => row.id); 163 | } 164 | 165 | /** 166 | * Suggest similar identifiers for typos and partial matches 167 | */ 168 | export function suggest_similar_identifiers(query: string): string[] { 169 | const suggestions = search_definitions(query); 170 | // Return top 5 suggestions 171 | return suggestions.slice(0, 5).map((def) => def.identifier); 172 | } 173 | 174 | /** 175 | * Get definitions by category (based on identifier patterns) 176 | */ 177 | export function get_definitions_by_category( 178 | category: string, 179 | ): DefinitionItem[] { 180 | const database = initDb(); 181 | let whereClause = ''; 182 | 183 | switch (category.toLowerCase()) { 184 | case 'runes': 185 | whereClause = "WHERE id LIKE '$%'"; 186 | break; 187 | case 'events': 188 | whereClause = 189 | "WHERE (id LIKE '%click%' OR id LIKE '%event%' OR id LIKE 'on%')"; 190 | break; 191 | case 'features': 192 | whereClause = 193 | "WHERE id NOT LIKE '$%' AND id NOT LIKE '%event%' AND id NOT LIKE 'on%'"; 194 | break; 195 | default: 196 | return []; 197 | } 198 | 199 | const stmt = database.prepare( 200 | `SELECT id, title, content, path FROM definitions ${whereClause} ORDER BY id`, 201 | ); 202 | const rows = stmt.all() as Array<{ 203 | id: string; 204 | title: string; 205 | content: string; 206 | path: string; 207 | }>; 208 | 209 | return rows.map((row) => ({ 210 | identifier: row.id, 211 | title: row.title, 212 | content: row.content, 213 | related: extract_related_definitions(row.content), 214 | path: row.path, 215 | })); 216 | } 217 | 218 | /** 219 | * Close database connection 220 | */ 221 | export function closeDb(): void { 222 | if (db) { 223 | db.close(); 224 | db = null; 225 | } 226 | } 227 | 228 | // Cleanup on process exit 229 | process.on('exit', closeDb); 230 | process.on('SIGINT', closeDb); 231 | process.on('SIGTERM', closeDb); 232 | -------------------------------------------------------------------------------- /docs/features/await-expressions.md: -------------------------------------------------------------------------------- 1 | # Svelte Await Expressions 2 | 3 | Svelte 5.36+ introduces experimental support for using `await` 4 | expressions directly in your components, enabling powerful 5 | asynchronous patterns with automatic loading states and error 6 | handling. 7 | 8 | ## Overview 9 | 10 | As of Svelte 5.36, you can use the `await` keyword in three places 11 | where it was previously unavailable: 12 | 13 | - At the top level of your component's ` 50 | 51 | 52 | 53 | 54 |

{a} + {b} = {await add(a, b)}

55 | ``` 56 | 57 | ### In $derived Declarations 58 | 59 | ```svelte 60 | 68 | 69 |

Data: {JSON.stringify(data)}

70 | ``` 71 | 72 | ## Boundaries Requirement 73 | 74 | Currently, you can only use `await` inside a `` with 75 | a `pending` snippet: 76 | 77 | ```svelte 78 | 79 | 80 | 81 | {#snippet pending()} 82 |

Loading...

83 | {/snippet} 84 |
85 | ``` 86 | 87 | This restriction will be lifted once Svelte supports asynchronous 88 | server-side rendering. 89 | 90 | > **Note**: In the Svelte playground, your app is rendered inside a 91 | > boundary with an empty pending snippet automatically. 92 | 93 | ## Synchronized Updates 94 | 95 | When an `await` expression depends on state, changes to that state are 96 | synchronized - the UI won't update until the asynchronous work 97 | completes, preventing inconsistent states: 98 | 99 | ```svelte 100 | 108 | 109 | 110 |

Count: {await slowAdd(count)}

111 | ``` 112 | 113 | Updates can overlap - a fast update will be reflected while an earlier 114 | slow update is still ongoing. 115 | 116 | ## Concurrency 117 | 118 | Svelte runs independent `await` expressions in parallel: 119 | 120 | ```svelte 121 | 122 |

{await fetchUserData()}

123 |

{await fetchSettings()}

124 | ``` 125 | 126 | This parallel execution applies to: 127 | 128 | - Independent expressions in markup 129 | - Independent `$derived` expressions (after initial sequential 130 | creation) 131 | 132 | Sequential `await` expressions in ` 162 | 163 | {#if isLoading} 164 |

Loading...

165 | {/if} 166 | ``` 167 | 168 | ### Using settled() 169 | 170 | The `settled()` function returns a promise that resolves when all 171 | state changes and resulting asynchronous work complete: 172 | 173 | ```svelte 174 | 188 | ``` 189 | 190 | ## Error Handling 191 | 192 | Errors in `await` expressions bubble to the nearest error boundary: 193 | 194 | ```svelte 195 | 196 |
197 | {await mayThrowError()} 198 |
199 | 200 | {#snippet error(err)} 201 |

Error: {err.message}

202 | {/snippet} 203 | 204 | {#snippet pending()} 205 |

Loading...

206 | {/snippet} 207 |
208 | ``` 209 | 210 | ## Common Patterns 211 | 212 | ### Data Fetching 213 | 214 | ```svelte 215 | 225 | 226 |

Welcome, {user.name}!

227 | ``` 228 | 229 | ### Dependent Async Operations 230 | 231 | ```svelte 232 | 237 | 238 |

Final result: {result}

239 | ``` 240 | 241 | ### Form Submission with Loading 242 | 243 | ```svelte 244 | 261 | 262 |
263 | 264 | 267 |
268 | ``` 269 | 270 | ## Performance Considerations 271 | 272 | ### Avoid Waterfalls 273 | 274 | Be careful of sequential `await` expressions that could run in 275 | parallel: 276 | 277 | ```svelte 278 | 279 | 283 | 284 | 285 | 296 | ``` 297 | 298 | Svelte will show an `await_waterfall` warning for sequential dependent 299 | awaits. 300 | 301 | ## Caveats and Limitations 302 | 303 | 1. **Experimental Feature**: APIs may change in breaking ways outside 304 | semver major releases 305 | 2. **SSR Limitation**: Server-side rendering is synchronous - only 306 | `pending` snippets render during SSR 307 | 3. **Boundary Requirement**: Currently requires `` 308 | with `pending` snippet 309 | 4. **Effect Order**: Block effects run before `$effect.pre` when 310 | `experimental.async` is enabled 311 | 312 | ## Migration from {#await} 313 | 314 | The traditional `{#await}` block syntax still works, but await 315 | expressions provide more flexibility: 316 | 317 | ```svelte 318 | 319 | {#await promise} 320 |

Loading...

321 | {:then value} 322 |

Value: {value}

323 | {:catch error} 324 |

Error: {error.message}

325 | {/await} 326 | 327 | 328 | 329 |

Value: {await promise}

330 | 331 | {#snippet pending()} 332 |

Loading...

333 | {/snippet} 334 | 335 | {#snippet error(err)} 336 |

Error: {err.message}

337 | {/snippet} 338 |
339 | ``` 340 | 341 | ## Best Practices 342 | 343 | 1. **Use boundaries**: Always wrap await expressions in 344 | `` 345 | 2. **Handle errors**: Provide error snippets for better user 346 | experience 347 | 3. **Show loading states**: Use `pending` snippets and 348 | `$effect.pending()` 349 | 4. **Avoid waterfalls**: Use `Promise.all()` for independent async 350 | operations 351 | 5. **Use `settled()`**: Wait for complex updates to complete before 352 | proceeding 353 | 6. **Test thoroughly**: Experimental feature - test edge cases 354 | carefully 355 | -------------------------------------------------------------------------------- /src/tools/definition-tools.ts: -------------------------------------------------------------------------------- 1 | import { McpServer } from 'tmcp'; 2 | import * as v from 'valibot'; 3 | 4 | import { 5 | DefinitionItem, 6 | get_all_identifiers, 7 | get_definition_by_identifier, 8 | load_definitions, 9 | suggest_similar_identifiers, 10 | } from '../db-definition-loader.js'; 11 | 12 | // Load all definitions 13 | let definitions: DefinitionItem[] = []; 14 | 15 | // Input validation schema 16 | const DefinitionSchema = v.object({ 17 | identifier: v.pipe( 18 | v.string(), 19 | v.minLength(1), 20 | v.description( 21 | 'Svelte 5 identifier: $state, $derived, $props, $effect, snippets, onclick, component-events', 22 | ), 23 | ), 24 | format: v.pipe( 25 | v.optional(v.picklist(['syntax', 'quick', 'full']), 'full'), 26 | v.description( 27 | 'Output detail level: "syntax" (signature only), "quick" (with example), "full" (complete docs)', 28 | ), 29 | ), 30 | }); 31 | 32 | type FormatType = 'syntax' | 'quick' | 'full'; 33 | 34 | /** 35 | * Format definition content based on the requested format 36 | */ 37 | function format_definition_content( 38 | content: string, 39 | format: FormatType, 40 | ): string { 41 | switch (format) { 42 | case 'syntax': 43 | return extract_syntax_only(content); 44 | case 'quick': 45 | return extract_definition_and_example(content); 46 | case 'full': 47 | default: 48 | return content; 49 | } 50 | } 51 | 52 | /** 53 | * Extract just the syntax information from a definition 54 | */ 55 | function extract_syntax_only(content: string): string { 56 | const lines = content.split('\n'); 57 | const syntax_lines: string[] = []; 58 | 59 | // Include title 60 | const title_line = lines.find((line) => line.startsWith('# ')); 61 | if (title_line) { 62 | syntax_lines.push(title_line); 63 | syntax_lines.push(''); 64 | } 65 | 66 | // Extract key definition lines 67 | for (const line of lines) { 68 | if ( 69 | line.startsWith('**Definition:**') || 70 | line.startsWith('**Syntax:**') || 71 | line.startsWith('**Parameters:**') || 72 | line.startsWith('**Returns:**') || 73 | line.startsWith('**Variants:**') || 74 | line.startsWith('**Pattern:**') || 75 | line.startsWith('**Interface:**') || 76 | line.startsWith('**Examples:**') 77 | ) { 78 | syntax_lines.push(line); 79 | } 80 | // Include bullet points under these sections 81 | else if (line.startsWith('- ') && syntax_lines.length > 0) { 82 | syntax_lines.push(line); 83 | } 84 | // Include code blocks that look like syntax 85 | else if ( 86 | line.startsWith('```') || 87 | line.includes('$state') || 88 | line.includes('$derived') || 89 | line.includes('$props') || 90 | line.includes('$effect') 91 | ) { 92 | syntax_lines.push(line); 93 | } 94 | } 95 | 96 | return syntax_lines.join('\n').trim(); 97 | } 98 | 99 | /** 100 | * Extract definition and a minimal example 101 | */ 102 | function extract_definition_and_example(content: string): string { 103 | const lines = content.split('\n'); 104 | const quick_lines: string[] = []; 105 | let in_examples = false; 106 | let example_lines = 0; 107 | 108 | for (const line of lines) { 109 | // Always include title and definition sections 110 | if ( 111 | line.startsWith('# ') || 112 | line.startsWith('**Definition:**') || 113 | line.startsWith('**Syntax:**') || 114 | line.startsWith('**Parameters:**') || 115 | line.startsWith('**Returns:**') || 116 | line.startsWith('**Variants:**') || 117 | line.startsWith('**Pattern:**') || 118 | line.startsWith('**Interface:**') || 119 | line.startsWith('- ') 120 | ) { 121 | quick_lines.push(line); 122 | } 123 | // Start including examples but limit them 124 | else if (line.startsWith('## Examples')) { 125 | in_examples = true; 126 | quick_lines.push(line); 127 | } 128 | // Stop at Related section 129 | else if (line.startsWith('## Related')) { 130 | break; 131 | } 132 | // Include limited examples 133 | else if (in_examples && example_lines < 15) { 134 | quick_lines.push(line); 135 | if (line.trim()) { 136 | example_lines++; 137 | } 138 | } 139 | } 140 | 141 | return quick_lines.join('\n').trim(); 142 | } 143 | 144 | /** 145 | * Create error message with helpful suggestions 146 | */ 147 | function create_definition_error( 148 | identifier: string, 149 | definitions: DefinitionItem[], 150 | ): Error { 151 | const suggestions = suggest_similar_identifiers(identifier); 152 | const all_identifiers = get_all_identifiers(); 153 | 154 | let error_message = `Definition for '${identifier}' not found.`; 155 | 156 | if (suggestions.length > 0) { 157 | error_message += `\n\nDid you mean:\n${suggestions.map((s) => `• ${s}`).join('\n')}`; 158 | } 159 | 160 | // Add category-based suggestions 161 | const category_suggestions: string[] = []; 162 | if ( 163 | identifier.startsWith('$') || 164 | identifier.includes('state') || 165 | identifier.includes('derived') 166 | ) { 167 | category_suggestions.push( 168 | 'Runes: $state, $derived, $props, $effect', 169 | ); 170 | } 171 | if (identifier.includes('click') || identifier.includes('event')) { 172 | category_suggestions.push('Events: onclick, component-events'); 173 | } 174 | if (identifier.includes('snippet') || identifier.includes('slot')) { 175 | category_suggestions.push('Features: snippets'); 176 | } 177 | 178 | if (category_suggestions.length > 0) { 179 | error_message += `\n\nRelated categories:\n${category_suggestions.map((s) => `• ${s}`).join('\n')}`; 180 | } 181 | 182 | // Show available identifiers if not too many 183 | if (all_identifiers.length <= 20) { 184 | error_message += `\n\nAvailable identifiers:\n${all_identifiers.map((s) => `• ${s}`).join('\n')}`; 185 | } else { 186 | error_message += `\n\nUse format="syntax" for quicker responses, or try: $state, $derived, $props, $effect, snippets, onclick, component-events`; 187 | } 188 | 189 | return new Error(error_message); 190 | } 191 | 192 | /** 193 | * Handle definition lookup requests 194 | */ 195 | async function definition_handler(args: any) { 196 | try { 197 | // Validate input 198 | const { identifier, format = 'full' } = v.parse( 199 | DefinitionSchema, 200 | args, 201 | ); 202 | 203 | // Find the definition 204 | const definition = get_definition_by_identifier(identifier); 205 | 206 | if (!definition) { 207 | throw create_definition_error(identifier, definitions); 208 | } 209 | 210 | // Validate that definition content exists 211 | if ( 212 | !definition.content || 213 | definition.content.trim().length === 0 214 | ) { 215 | throw new Error( 216 | `Definition for '${identifier}' exists but appears to be empty. Please try again later or contact support.`, 217 | ); 218 | } 219 | 220 | // Format the content based on requested format 221 | const formatted_content = format_definition_content( 222 | definition.content, 223 | format, 224 | ); 225 | 226 | // Add format info for context 227 | let response_content = formatted_content; 228 | if (format === 'syntax') { 229 | response_content += `\n\n*Use format="quick" or format="full" for more details.*`; 230 | } else if (format === 'quick') { 231 | response_content += `\n\n*Use format="full" for complete documentation.*`; 232 | } 233 | 234 | return { 235 | content: [ 236 | { 237 | type: 'text' as const, 238 | text: response_content, 239 | }, 240 | ], 241 | }; 242 | } catch (error) { 243 | // Re-throw with additional context for debugging 244 | const errorMessage = 245 | error instanceof Error 246 | ? error.message 247 | : 'Unknown error occurred'; 248 | throw new Error(`Error in svelte_definition: ${errorMessage}`); 249 | } 250 | } 251 | 252 | /** 253 | * Register the definition tool with the MCP server 254 | * 255 | * This single tool replaces 13+ specialized tools with a unified interface 256 | * that provides authoritative, TypeScript-based Svelte 5 definitions. 257 | * 258 | * Usage Examples: 259 | * 260 | * Core runes: 261 | * - svelte_definition({ identifier: "$state" }) 262 | * - svelte_definition({ identifier: "$derived" }) 263 | * - svelte_definition({ identifier: "$props" }) 264 | * - svelte_definition({ identifier: "$effect" }) 265 | * 266 | * Features: 267 | * - svelte_definition({ identifier: "snippets" }) 268 | * - svelte_definition({ identifier: "onclick" }) 269 | * - svelte_definition({ identifier: "component-events" }) 270 | * 271 | * Format control: 272 | * - svelte_definition({ identifier: "$state", format: "syntax" }) // ~50 words 273 | * - svelte_definition({ identifier: "$state", format: "quick" }) // ~200 words 274 | * - svelte_definition({ identifier: "$state", format: "full" }) // ~500-1000 words 275 | * 276 | * Error handling: 277 | * - svelte_definition({ identifier: "$sate" }) // → "Did you mean $state?" 278 | * - svelte_definition({ identifier: "unknown" }) // → Lists available definitions 279 | */ 280 | export function register_definition_tools( 281 | server: McpServer, 282 | ): void { 283 | // Load all definitions 284 | definitions = load_definitions(); 285 | 286 | if (definitions.length === 0) { 287 | console.warn( 288 | 'Warning: No definitions loaded. Definition tool will not work properly.', 289 | ); 290 | } else { 291 | console.log( 292 | `Loaded ${definitions.length} definitions:`, 293 | get_all_identifiers(), 294 | ); 295 | } 296 | 297 | // Register the single definition tool 298 | server.tool( 299 | { 300 | name: 'svelte_definition', 301 | description: 302 | 'Lookup Svelte 5 & SvelteKit definitions from TypeScript declarations. Covers all runes ($state, $derived, $props, $effect), features (snippets, onclick, component-events), and patterns. Supports syntax/quick/full format for varying detail levels.', 303 | schema: DefinitionSchema, 304 | }, 305 | definition_handler, 306 | ); 307 | } 308 | -------------------------------------------------------------------------------- /docs/features/remote-functions.md: -------------------------------------------------------------------------------- 1 | # SvelteKit Remote Functions 2 | 3 | Remote functions enable type-safe communication between client and 4 | server in SvelteKit applications. They run exclusively on the server, 5 | providing access to server-only resources like environment variables, 6 | databases, and file systems. 7 | 8 | ## Overview 9 | 10 | Remote functions are server-side functions that can be called from 11 | client-side code with full type safety. They're defined in 12 | `.remote.js` or `.remote.ts` files and come in four types: 13 | 14 | - **query**: Read dynamic data from the server 15 | - **form**: Handle form submissions and data mutations 16 | - **command**: Execute server actions without form elements 17 | - **prerender**: Generate data at build time for static content 18 | 19 | This feature is **experimental** and requires opt-in configuration. 20 | 21 | ## Configuration 22 | 23 | Enable remote functions in your `svelte.config.js`: 24 | 25 | ```javascript 26 | export default { 27 | compilerOptions: { 28 | experimental: { async: true }, // Required for await expressions 29 | }, 30 | kit: { 31 | experimental: { 32 | remoteFunctions: true, // Enable remote functions 33 | }, 34 | }, 35 | }; 36 | ``` 37 | 38 | ## File Structure 39 | 40 | Remote functions are defined in `.remote.ts` or `.remote.js` files 41 | alongside your routes: 42 | 43 | ``` 44 | src/routes/ 45 | ├── +page.svelte 46 | ├── todos.remote.ts // Remote functions for todos 47 | ├── login/ 48 | │ ├── +page.svelte 49 | │ └── login.remote.ts // Remote functions for login 50 | └── api/ 51 | └── data.remote.ts // Shared remote functions 52 | ``` 53 | 54 | ## Function Types 55 | 56 | ### Query Functions 57 | 58 | Query functions read data from the server and can be used with await 59 | expressions: 60 | 61 | ```typescript 62 | // todos.remote.ts 63 | import { query } from '$app/server'; 64 | 65 | export const getTodos = query(async () => { 66 | const todos = await db.todo.findMany(); 67 | return todos; 68 | }); 69 | ``` 70 | 71 | ```svelte 72 | 73 | 78 | 79 | 80 |
    81 | {#each await todos as todo} 82 |
  • {todo.text}
  • 83 | {/each} 84 |
85 | ``` 86 | 87 | ### Form Functions 88 | 89 | Form functions handle form submissions and mutations: 90 | 91 | ```typescript 92 | // todos.remote.ts 93 | import { form } from '$app/server'; 94 | 95 | export const addTodo = form(async (data) => { 96 | const text = data.get('text') as string; 97 | if (!text) { 98 | return 'Todo text cannot be empty.'; // Return error message 99 | } 100 | 101 | await db.todo.create({ 102 | data: { text, done: false }, 103 | }); 104 | 105 | // Trigger refresh of related queries 106 | await getTodos().refresh(); 107 | }); 108 | ``` 109 | 110 | ```svelte 111 | 112 | 115 | 116 | 117 |
118 | 119 | 120 |
121 | ``` 122 | 123 | ### Command Functions 124 | 125 | Command functions execute server actions without requiring form 126 | elements: 127 | 128 | ```typescript 129 | // todos.remote.ts 130 | import { command } from '$app/server'; 131 | import { z } from 'zod'; 132 | 133 | export const toggleTodo = command(z.string(), async (id) => { 134 | const todo = await db.todo.findUnique({ where: { id } }); 135 | if (!todo) throw new Error('Todo not found'); 136 | 137 | await db.todo.update({ 138 | where: { id }, 139 | data: { done: !todo.done }, 140 | }); 141 | }); 142 | ``` 143 | 144 | ```svelte 145 | 146 | 162 | 163 | {#each await todos as todo} 164 | handleToggle(todo.id)} 168 | /> 169 | {todo.text} 170 | {/each} 171 | ``` 172 | 173 | ### Prerender Functions 174 | 175 | Prerender functions generate data at build time for static content: 176 | 177 | ```typescript 178 | // blog.remote.ts 179 | import { prerender } from '$app/server'; 180 | 181 | export const getBlogPosts = prerender(async () => { 182 | const posts = await fetchFromCMS(); 183 | return posts; 184 | }); 185 | ``` 186 | 187 | ```svelte 188 | 189 | 195 | 196 | {#each await posts as post} 197 |
198 |

{post.title}

199 |

{post.excerpt}

200 |
201 | {/each} 202 | ``` 203 | 204 | ## Advanced Patterns 205 | 206 | ### Authentication and Authorization 207 | 208 | ```typescript 209 | // auth.remote.ts 210 | import { query, form, getRequestEvent } from '$app/server'; 211 | import { redirect } from '@sveltejs/kit'; 212 | 213 | export const getCurrentUser = query(async () => { 214 | const event = getRequestEvent(); 215 | const token = event.cookies.get('auth-token'); 216 | 217 | if (!token) { 218 | redirect(303, '/login'); 219 | } 220 | 221 | return await validateToken(token); 222 | }); 223 | 224 | export const login = form(async (data) => { 225 | const email = data.get('email') as string; 226 | const password = data.get('password') as string; 227 | 228 | const user = await authenticateUser(email, password); 229 | if (!user) { 230 | return 'Invalid credentials'; 231 | } 232 | 233 | const event = getRequestEvent(); 234 | event.cookies.set('auth-token', user.token, { 235 | path: '/', 236 | httpOnly: true, 237 | secure: true, 238 | }); 239 | 240 | redirect(303, '/dashboard'); 241 | }); 242 | ``` 243 | 244 | ### Error Handling 245 | 246 | ```typescript 247 | // todos.remote.ts 248 | export const deleteTodo = form(async (data) => { 249 | const id = data.get('id') as string; 250 | 251 | // Simulate random errors for demo 252 | if (Math.random() < 0.2) { 253 | throw new Error('Random server error'); 254 | } 255 | 256 | await db.todo.delete({ where: { id } }); 257 | }); 258 | ``` 259 | 260 | ```svelte 261 | 262 | 265 | 266 | {#each await todos as todo} 267 | {@const remove = deleteTodo.for(todo.id)} 268 | 269 |
{ 271 | try { 272 | await submit(); 273 | } catch (error) { 274 | // Handle error without showing error page 275 | console.error('Delete failed:', error.message); 276 | } 277 | })} 278 | > 279 | {#if remove.error} 280 | {remove.error.message} 281 | {/if} 282 | 283 |
284 | {/each} 285 | ``` 286 | 287 | ### Optimistic Updates 288 | 289 | ```svelte 290 | 308 | 309 | {#each await todos as todo} 310 | 318 | {/each} 319 | ``` 320 | 321 | ### Data Validation 322 | 323 | Use Zod for type-safe validation: 324 | 325 | ```typescript 326 | // profile.remote.ts 327 | import { form } from '$app/server'; 328 | import { z } from 'zod'; 329 | 330 | const updateProfileSchema = z.object({ 331 | name: z.string().min(2).max(50), 332 | email: z.string().email(), 333 | age: z.number().min(13).max(120), 334 | }); 335 | 336 | export const updateProfile = form(async (data) => { 337 | const result = updateProfileSchema.safeParse({ 338 | name: data.get('name'), 339 | email: data.get('email'), 340 | age: Number(data.get('age')), 341 | }); 342 | 343 | if (!result.success) { 344 | return result.error.format(); // Return validation errors 345 | } 346 | 347 | await db.user.update({ 348 | where: { id: userId }, 349 | data: result.data, 350 | }); 351 | }); 352 | ``` 353 | 354 | ## Server-Only Access 355 | 356 | Remote functions have access to server-only resources: 357 | 358 | ```typescript 359 | // api.remote.ts 360 | import { query } from '$app/server'; 361 | import { DATABASE_URL } from '$env/static/private'; 362 | 363 | export const getServerStats = query(async () => { 364 | // Access environment variables 365 | const dbUrl = DATABASE_URL; 366 | 367 | // Access file system 368 | const logs = await fs.readFile('/var/log/app.log', 'utf8'); 369 | 370 | // Access database directly 371 | const stats = await db.$queryRaw` 372 | SELECT COUNT(*) as total_users FROM users 373 | `; 374 | 375 | return { stats, logCount: logs.split('\n').length }; 376 | }); 377 | ``` 378 | 379 | ## Integration with SvelteKit Features 380 | 381 | ### Loading States 382 | 383 | ```svelte 384 | 390 | 391 | 392 | {#if isLoading} 393 |
Loading...
394 | {/if} 395 | 396 |
    397 | {#each await todos as todo} 398 |
  • {todo.text}
  • 399 | {/each} 400 |
401 | 402 | {#snippet pending()} 403 |
Loading todos...
404 | {/snippet} 405 | 406 | {#snippet error(err)} 407 |
Failed to load: {err.message}
408 | {/snippet} 409 |
410 | ``` 411 | 412 | ### Progressive Enhancement 413 | 414 | ```svelte 415 | 418 | 419 | 420 |
421 | 422 | 423 |
424 | 425 | 426 |
{ 428 | // Custom handling before submission 429 | const text = formData.get('text'); 430 | if (!text?.trim()) { 431 | alert('Please enter todo text'); 432 | return; 433 | } 434 | 435 | await submit(); 436 | 437 | // Clear form on success 438 | event.target.reset(); 439 | })} 440 | > 441 | 442 | 443 |
444 | ``` 445 | 446 | ## Performance Considerations 447 | 448 | ### Query Caching and Refreshing 449 | 450 | ```typescript 451 | // data.remote.ts 452 | export const getExpensiveData = query(async () => { 453 | // This will be cached and reused 454 | const data = await expensiveOperation(); 455 | return data; 456 | }); 457 | ``` 458 | 459 | ```svelte 460 | 470 | 471 | 472 |
{await data}
473 | ``` 474 | 475 | ### Concurrent Operations 476 | 477 | ```svelte 478 | 488 | 489 |
490 |

{(await userData).name}

491 |

Posts: {(await userPosts).length}

492 |

Friends: {(await userFriends).length}

493 |
494 | ``` 495 | 496 | ## Security Best Practices 497 | 498 | 1. **Validate all inputs**: Use schemas like Zod for type safety 499 | 2. **Authenticate requests**: Check user permissions in query/command 500 | functions 501 | 3. **Sanitize data**: Clean user input before database operations 502 | 4. **Rate limiting**: Implement rate limiting for expensive operations 503 | 5. **Error handling**: Don't expose sensitive server information in 504 | errors 505 | 506 | ```typescript 507 | // secure.remote.ts 508 | import { query, getRequestEvent } from '$app/server'; 509 | import { z } from 'zod'; 510 | 511 | export const getSecureData = query(async () => { 512 | const event = getRequestEvent(); 513 | const user = await authenticateRequest(event); 514 | 515 | if (!user || !user.hasPermission('read:data')) { 516 | throw new Error('Unauthorized'); 517 | } 518 | 519 | return await fetchSecureData(user.id); 520 | }); 521 | ``` 522 | 523 | ## Common Patterns 524 | 525 | ### Master-Detail Views 526 | 527 | ```typescript 528 | // products.remote.ts 529 | export const getProducts = query(async () => { 530 | return await db.product.findMany(); 531 | }); 532 | 533 | export const getProduct = query(async (id: string) => { 534 | const product = await db.product.findUnique({ 535 | where: { id }, 536 | include: { reviews: true }, 537 | }); 538 | if (!product) throw new Error('Product not found'); 539 | return product; 540 | }); 541 | ``` 542 | 543 | ```svelte 544 | 545 | 550 | 551 | {#each await products as product} 552 | {product.name} 553 | {/each} 554 | ``` 555 | 556 | ### Real-time Updates 557 | 558 | ```svelte 559 | 569 | ``` 570 | 571 | ## Best Practices 572 | 573 | 1. **Keep functions focused**: Each remote function should have a 574 | single responsibility 575 | 2. **Use appropriate types**: Choose query/form/command/prerender 576 | based on use case 577 | 3. **Handle errors gracefully**: Provide meaningful error messages and 578 | fallbacks 579 | 4. **Implement loading states**: Use boundaries and pending snippets 580 | 5. **Validate inputs**: Always validate and sanitize user inputs 581 | 6. **Consider performance**: Cache expensive operations and use 582 | concurrent requests 583 | 7. **Test thoroughly**: Remote functions are experimental - test edge 584 | cases 585 | --------------------------------------------------------------------------------