Skip to content

Commit 2ec78e0

Browse files
committed
Migrate to SvelteKit 3
1 parent 9582000 commit 2ec78e0

94 files changed

Lines changed: 465 additions & 408 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ARCHITECTURE.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ Guidelines:
7777

7878
## SvelteKit configuration
7979

80-
The app uses Svelte's experimental async features and SvelteKit's remote functions. All SvelteKit configuration is passed to the `sveltekit()` plugin in `vite.config.ts` (the newer configuration style, expected to become the default in SvelteKit 3); `svelte.config.js` stays an empty stub so tools that require its presence keep working. The "svelte.config.js is ignored" warning printed by dev/build/check commands is expected with this setup.
80+
The app targets the SvelteKit 3 prerelease line, beginning with `@sveltejs/kit@3.0.0-next.12`. All SvelteKit configuration is passed to the `sveltekit()` plugin in `vite.config.ts`; SvelteKit 3 no longer supports `svelte.config.js`. Internal library imports use the package-level `#lib` subpath alias declared in `package.json`, replacing SvelteKit 2's generated `$lib` alias. The app uses Svelte's experimental async features, SvelteKit remote functions, and explicit environment variables.
8181

8282
```ts
8383
sveltekit({
@@ -95,14 +95,16 @@ sveltekit({
9595

9696
**Experimental async** allows `await` in Svelte markup and top-level `await` in `<script>` tags, enabling components to load data inline without separate `+page.server.js` load functions.
9797

98+
SvelteKit 3 removes adapter-node's runtime `ORIGIN` handling. Editable continues to use its declared `ORIGIN` variable for canonical metadata, while deployed Node servers derive request origins from trusted reverse-proxy headers by setting `PROTOCOL_HEADER=x-forwarded-proto` and `HOST_HEADER=x-forwarded-host`. Production Node deployments assume HTTPS. A direct plain-HTTP adapter-node smoke test must provide an `x-forwarded-proto: http` header when that protocol header is configured.
99+
98100
**Remote functions** (`$app/server`) allow server-side functions to be called directly from components via `query()` and `action()`. This replaces traditional REST endpoints for document loading and saving:
99101

100102
- **`src/lib/api.remote.ts`** — server-side functions for document and asset operations, called directly from components. Uses `query()` for reads and `action()` for writes. Access to `locals` (e.g. for auth checks) via `getRequestEvent()`.
101103

102104
**Server initialization**`src/hooks.server.ts` exports an `init()` function (SvelteKit's `ServerInit` hook) that runs once on server startup. This is where database migration runs:
103105

104106
```js
105-
import migrate from '$lib/server/migrate.js';
107+
import migrate from '#lib/server/migrate.js';
106108

107109
export async function init() {
108110
migrate();
@@ -381,10 +383,9 @@ Behavior rules:
381383
neither variable is needed and neither is checked
382384

383385
Variables consumed through SvelteKit are declared explicitly in `src/env.ts` via
384-
`defineEnvVars`, enabled by `experimental.explicitEnvironmentVariables` in
385-
`vite.config.ts`. This is the SvelteKit 3 model, opted into early. Server code
386-
imports named bindings from `$app/env/private` instead of `$env/dynamic/private`,
387-
and the latter now throws when imported.
386+
`defineEnvVars`; SvelteKit 3 detects this declaration file automatically. Server
387+
code imports named bindings from `$app/env/private` instead of
388+
`$env/dynamic/private`, and the latter throws when imported.
388389

389390
A declared variable is required and non-empty unless its `schema` says otherwise.
390391
`VERCEL` and `NODE_ENV` are declared optional because each may legitimately be

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
2020
- Do NOT think 4 steps ahead or add extra features/improvements
2121
- Only implement the specific change requested
2222
- You can suggest what the next step could be, but don't implement it
23-
- For anything that runs from the home route (`/`) in no-backend / Vercel mode, do NOT add top-level imports of backend-only modules like `$lib/api.remote.js` or anything that pulls in `$lib/server/db.js`
23+
- For anything that runs from the home route (`/`) in no-backend / Vercel mode, do NOT add top-level imports of backend-only modules like `#lib/api.remote.js` or anything that pulls in `#lib/server/db.js`
2424
- In home-route server files, import backend-only code lazily inside the `has_backend` guard so static/Vercel deployments do not evaluate database code at module load time
2525

2626
**Refactoring Guidelines:**

IMPLEMENTATION_PLAN.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# Implementation plan
22

3+
## SvelteKit 3 prerelease migration
4+
5+
- Upgrade to `@sveltejs/kit@3.0.0-next.12`, TypeScript 6, and SvelteKit-3-compatible prerelease versions of the Node and Vercel adapters while retaining compatible Svelte, Vite, and Vite plugin ranges.
6+
- Delete `svelte.config.js`; keep all framework and compiler configuration in the `sveltekit()` Vite plugin.
7+
- Replace generated `$lib` imports with a package `#lib` subpath alias declared in `package.json`.
8+
- Replace deprecated `invalidateAll()` calls with `refreshAll()`.
9+
- Pass the current pathname explicitly to the page-browser remote query because SvelteKit 3 forbids reading `event.url` inside queries.
10+
- Update dynamic `$app/paths.resolve` calls to use SvelteKit 3 pathname inputs without a leading slash, while retaining route ids for known routes.
11+
- Preserve canonical metadata through Editable's explicit `ORIGIN` variable, and configure Node deployments to trust `x-forwarded-proto` and `x-forwarded-host` now that adapter-node no longer reads `ORIGIN`.
12+
- Validate with `pnpm check`, `pnpm build`, and a Vercel-mode build; do not run the test suite automatically.
13+
314
## TypeScript conversion
415

516
Convert the whole codebase from JS+JSDoc to TypeScript per the "Language: TypeScript" section in [ARCHITECTURE.md](ARCHITECTURE.md). Non-strict; svelte-check must stay at 0 errors.

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ Pass those paths to primitives and use the same paths to read values when layout
126126
<script lang="ts">
127127
import { get_svedit_context } from '../svedit_context.js';
128128
import type { DocumentPath } from 'svedit';
129-
import type { Nodes } from '$lib/document_schema.js';
129+
import type { Nodes } from '#lib/document_schema.js';
130130
131131
const svedit = get_svedit_context();
132132
let { path }: { path: DocumentPath } = $props();
@@ -589,7 +589,7 @@ Create `src/routes/components/Hero.svelte`. It reads the node at `path`, renders
589589
<script lang="ts">
590590
import { Node, TextProperty } from 'svedit';
591591
import type { DocumentPath } from 'svedit';
592-
import type { Nodes } from '$lib/document_schema.js';
592+
import type { Nodes } from '#lib/document_schema.js';
593593
import { get_svedit_context } from '../svedit_context.js';
594594
import MediaProperty from './MediaProperty.svelte';
595595
import { TW_LIMITER, TW_PAGE_PADDING_X } from '../tailwind_theme.js';
@@ -712,7 +712,7 @@ fly secrets set \
712712
ADMIN_PASSWORD='pick-a-strong-password'
713713
```
714714

715-
`ORIGIN` must exactly match the URL you use in the browser, including the scheme and subdomain (for example, `https://example.com` and `https://www.example.com` are different origins). An incorrect value causes login and other write requests to fail with `403 Forbidden` before the password is checked. Update this secret whenever you switch to a custom domain, and access the site through that canonical URL.
715+
`ORIGIN` should exactly match the canonical URL you use in the browser, including the scheme and subdomain (for example, `https://example.com` and `https://www.example.com` are different origins), so generated canonical and social metadata stays correct. Update this secret whenever you switch to a custom domain.
716716

717717
Optionally set `ASSET_GRACE_PERIOD_DAYS` (default 7): unreferenced asset files are kept on disk this many days after losing their last reference. This is also the safe window for rolling back a database backup against the live assets folder without ending up with dead image references.
718718

docker-compose.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ services:
2525
# (fly.toml sets the same for Fly deployments).
2626
DATA_DIR: /data
2727
BODY_SIZE_LIMIT: '${BODY_SIZE_LIMIT:-30000000}'
28+
PROTOCOL_HEADER: x-forwarded-proto
29+
HOST_HEADER: x-forwarded-host
2830
volumes:
2931
# Host data location: ./data next to this file by default (local and
3032
# hand-managed runs); vps-deploy.sh pins it to /data via .deploy_env so

fly.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ primary_region = "fra"
1010

1111
[env]
1212
DATA_DIR = "/data"
13+
PROTOCOL_HEADER = "fly-forwarded-proto"
1314

1415
[deploy]
1516
strategy = "immediate"

package.json

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
"version": "2.0.0",
55
"packageManager": "pnpm@11.15.1",
66
"type": "module",
7+
"imports": {
8+
"#lib": "./src/lib/index.js",
9+
"#lib/*": "./src/lib/*"
10+
},
711
"engines": {
812
"node": ">=24"
913
},
@@ -38,9 +42,9 @@
3842
"devDependencies": {
3943
"@eslint/compat": "^2.0.3",
4044
"@eslint/js": "^10.0.1",
41-
"@sveltejs/adapter-vercel": "^6.3.4",
42-
"@sveltejs/kit": "^2.63.0",
43-
"@sveltejs/vite-plugin-svelte": "^7.1.2",
45+
"@sveltejs/adapter-vercel": "7.0.0-next.3",
46+
"@sveltejs/kit": "3.0.0-next.12",
47+
"@sveltejs/vite-plugin-svelte": "^7.2.0",
4448
"@tailwindcss/forms": "^0.5.11",
4549
"@tailwindcss/vite": "^4.2.2",
4650
"@types/node": "^25",
@@ -53,17 +57,17 @@
5357
"prettier": "^3.8.1",
5458
"prettier-plugin-svelte": "^3.5.1",
5559
"prettier-plugin-tailwindcss": "^0.7.2",
56-
"svelte": "^5.56.2",
60+
"svelte": "^5.56.7",
5761
"svelte-check": "^4.6.0",
5862
"tailwindcss": "^4.2.2",
59-
"typescript": "^5.9.3",
60-
"vite": "^8.0.1",
63+
"typescript": "^6.0.3",
64+
"vite": "^8.1.5",
6165
"vitest": "^4.1.10"
6266
},
6367
"dependencies": {
6468
"@jsquash/webp": "^1.5.0",
6569
"@mediabunny/aac-encoder": "^1.50.9",
66-
"@sveltejs/adapter-node": "^5.5.7",
70+
"@sveltejs/adapter-node": "6.0.0-next.6",
6771
"aws4fetch": "^1.0.20",
6872
"mdast-util-from-markdown": "^2.0.3",
6973
"mediabunny": "^1.50.9",

0 commit comments

Comments
 (0)