Skip to content

feat: Migrate to SvelteKit 3 - #244

Merged
hhvrc merged 11 commits into
developfrom
feature/svelte-kit-3
Oct 2, 2026
Merged

hhvrc merged 11 commits into
developfrom
feature/svelte-kit-3

Conversation

@hhvrc

@hhvrc hhvrc commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

No description provided.

hhvrc added 3 commits August 20, 2026 14:07
Ran `sv migrate sveltekit-3` and worked through the manual tasks it collected.

Config:
- `experimental.tracing` -> top-level `tracing.server`, and
  `experimental.instrumentation` is gone (instrumentation.server.ts is picked
  up automatically now)
- `vitePlugin.inspector` moves to the top level of the sveltekit() options
- `KitConfig` is no longer exported from `@sveltejs/kit`; take `Config` from
  `@sveltejs/kit/vite` instead
- tsconfig extends `$app/tsconfig`, mirrors the new `#lib` subpath imports in
  `paths` (svelte-check resolves .svelte imports itself and only understands
  tsconfig paths, not package.json imports) and keeps type-checking the
  config, plugin and e2e sources

Paths:
- `Path` no longer carries a leading slash, so pathname literals, breadcrumb
  and sidebar entries drop it
- `prefixBase()` still takes a union-typed pathname, which `resolve()` cannot
  (it is generic over a single literal path), and derives the base path from
  PUBLIC_SITE_URL the same way vite.config.ts does — `resolve()` returns a
  path relative to the page being rendered, which cannot be compared against
  page.url.pathname nor back the absolute URLs in the sitemap and canonical
  tags

Environment, hooks and navigation:
- `$env/static|dynamic/*` -> `$app/env/*` through the generated src/env.ts;
  the previously dynamic variables keep an empty-string fallback, which every
  consumer treats exactly like the old `undefined`
- `handleError` discriminates on `kind`, so expected app and framework errors
  (404s and friends, which SvelteKit 3 also routes through the hook) log at
  warn instead of drowning real defects in telemetry
- `replaceState()` -> shallow `goto()`, `keepFocus`/`noScroll` -> `reset`
- parameter matchers consolidated into src/params.ts

Also fixes three bugs the type checker surfaced on the way:
- the logo asset is `logo.svg`, not `Logo.svg`, so the social preview image
  and the welcome screen preload were 404ing
- public-routes.ts cast its computed paths to `Path`, which hid the
  leading-slash change from the compiler — sitemap.xml and llms.txt would
  have emitted `https://home/`-style URLs
- redirectLegacyHashRoute() was handed `resolve('')`, yielding the
  protocol-relative `//home`
`pnpm run check` passes `--tsgo`, which svelte-check only honours when
TypeScript 7 is installed under the `@typescript/native` npm alias — otherwise
it aborts with "requires TypeScript 7 to be installed in the workspace".

It was resolving only because pnpm hoists the alias out of the
packages/svelte-core workspace package, so any lockfile change that dropped it
there silently broke type-checking here. Declare it locally, matching the
version svelte-core pins.

TypeScript itself stays on 6: @sveltejs/kit 3, svelte-check and
typescript-eslint all cap their `typescript` peer below 7.
The (app) auth gate sent the interrupted destination to /login as `next`, but
nothing ever read that parameter: the login flow and the (auth) layout both go
through gotoQueryRedirectOrFallback(), which reads REDIRECT_QUERY_PARAM.
Signing back in after a session expired therefore always landed on /home.

Use the constant on both sides so the two halves cannot drift again.
@hhvrc hhvrc self-assigned this Aug 20, 2026
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 20, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
🔵 In progress
View logs
openshock-app-dev 1ed3158 Oct 02 2026, 09:15 AM

@hhvrc hhvrc changed the title feat: SveletKit 3 feat: SvelteKit 3 Aug 20, 2026
@coderabbitai

coderabbitai Bot commented Aug 20, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 173c591d-db9c-4f97-b09e-14e3a3f390b9

  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@hhvrc hhvrc changed the title feat: SvelteKit 3 feat: Migrate to Svelte Kit 3 Aug 20, 2026
@hhvrc hhvrc changed the title feat: Migrate to Svelte Kit 3 feat: Migrate to SvelteKit 3 Aug 20, 2026
hhvrc added 7 commits August 20, 2026 14:27
All seven conflicts were the same kind: develop reached the same result in
SvelteKit 2 spelling while this branch had already migrated it.

- `asset()`/`resolve()` arguments keep this branch's slashless form; the
  substance of develop's `Logo.svg` -> `logo.svg` casing fix is already here.
- `#lib/...js` imports keep this branch's subpath form over `$lib`.
- develop's redirect-after-reauth fix is the same change as f609688.
- pnpm-lock.yaml follows package.json in dropping formsnap and
  sveltekit-superforms from the root importer; packages/svelte-core keeps both.
package.json keeps this branch's prerelease pins for `@sveltejs/kit` and
both adapters, plus its `#lib` imports map, and takes develop's other
bumps — vite-plugin-svelte, svelte, svelte-check, svelte-sonner and
pnpm 11.22.0.

pnpm-lock.yaml is regenerated from this branch's copy so the SvelteKit 3
prereleases stay pinned. Regenerating left a stale svelte 5.56.8 beside
5.56.9, which is the duplicate that makes every snippet fail to type-check
against a `Snippet` prop; `pnpm dedupe` collapses it, and the
`dedupe --check` step this merge brings in from develop guards it going
forward.

The submodule points at svelte-core's own feature/svelte-kit-3 rather than
its master, and .gitmodules now records that branch, so
`git submodule update --remote` tracks the migration branch here instead of
pulling master back in.
Conflicts came from develop's online-hubs rewrite landing on top of this
branch's SvelteKit 3 migration. Resolutions:

- package.json: keep the SvelteKit 3 pins (kit 3.0.0-next.23,
  adapter-cloudflare 8.0.0-next.6, adapter-node 6.0.0-next.10) and the `#lib`
  imports map, take develop's other bumps, and move packageManager to
  pnpm@12.5.1 so it matches the Dockerfile ARG that came with develop's
  toolchain update (ci-lint.yml checks the two agree).
- pnpm-lock.yaml: rebuilt from develop's copy so only the kit 2 -> 3
  resolutions differ.
- packages/svelte-core: merged master into the package's own
  feature/svelte-kit-3 branch and repointed the submodule at the result.
- online-hubs: took develop's rewrite wholesale, restated its imports in this
  branch's `#lib/...js` style, and kept the branch's leading-slash-free
  `resolve()` paths. develop's removal of the placeholder Edit and Delete
  actions stands.

pnpm check, lint, format:check and test:unit pass in both repos. E2E is
unverified: `vite preview` needs a local.openshock.app hosts entry and
port 443 rights this machine does not have.
@sveltejs/kit 3.0.0-next.23 -> 3.0.0-next.30, adapter-node 6.0.0-next.10 ->
6.0.0-next.15, adapter-cloudflare 8.0.0-next.6 -> 8.0.0-next.7 — all three now
at their `next` tip. `pnpm outdated` never flagged these: it compares against
the `latest` dist-tag, which still points at the stable 2.x/5.x/7.x line, so an
exact prerelease pin always looks current.

adapter-node next.15 drops the `Reading config.kit inside adapters is
deprecated` warning the build used to emit.

svelte-core picks up the restored sidebar/slider customizations and its own
dependency bumps. Because it declares `"@sveltejs/kit": "next"`, its resolution
had run ahead of the frontend's pin and the workspace was installing two
SvelteKit copies; matching the pin collapses that, along with a duplicate
bits-ui, for a net -9 packages.

typescript stays on ~6.0.3 — 7.x comes in separately via the
@typescript/native alias for tsgo.

Verified: check, lint, format:check, test:unit and dedupe --check pass, and both
the node and Cloudflare (WORKERS_CI=1) adapter builds succeed.
Moves off the SvelteKit 3 prereleases now that stable is out:
@sveltejs/kit 3.0.0-next.30 -> 3.0.0, adapter-cloudflare 8.0.0-next.7 ->
8.0.0, adapter-node 6.0.0-next.15 -> 6.0.0. Also bumps typescript-eslint
and wrangler. The three stable pins are added to
minimumReleaseAgeExclude so the 3-day quarantine does not block them.

Bumps packages/svelte-core to a17e5e0, which removes the unused shadcn
ui/form component set along with its formsnap and sveltekit-superforms
peers. Nothing in this app imported ui/form, so no source changes are
needed here. superforms declared a required peer on @sveltejs/kit
1.x || 2.x and was the last genuine SvelteKit 3 peer conflict in the
tree; dropping it removes 45 packages from the install.

Verified with svelte-check (459 files, 0 errors), a production build,
eslint and prettier.
src/env.ts had the structural migration to defineEnvVars but used none of
what the new API offers: every variable was an untyped string, and the
only schema present was a `?? ''` shim preserving the old
$env/dynamic/* behaviour.

Each variable now declares:

- a `description`, carried over from the comments in .env, which surfaces
  as inline documentation on hover at every import site
- a `schema` that validates on build (static) or startup (dynamic), so a
  misconfigured deployment fails immediately with a named error instead
  of throwing at the first request that happens to touch the value
- a transformation, so consumers receive the type they actually want

Flags are coerced to real booleans, which lets every call site drop
isTruthy() and `=== 'true'`. Text values are trimmed in the schema,
removing the .trim() calls in metadata.ts and llms.txt. URLs are parsed
and rejected unless absolute http(s); PUBLIC_BACKEND_API_URL
additionally requires HTTPS with no query or fragment, mirroring the
checks getApiUrl() performs. Those checks stay in url.ts as well, since
url.test.ts mocks $app/env/public and never runs these schemas.

URLs stay strings rather than becoming URL instances, so static values
remain inlinable and dynamic public ones stay devalue-serializable.

The flag helper reimplements isTruthy instead of importing it: SvelteKit
evaluates src/env.ts in a plain Node context to resolve static values,
and the @openshock/svelte-core/utils barrel reaches
clipboard.svelte.ts -> svelte-sonner -> a .svelte file, which Node
cannot load. The module is kept free of app and package imports.

static/dynamic split is unchanged, so no deployment switch changes
meaning. Verified that booleans inline as real literals, that dynamic
flags validate at startup, and that bad values fail the build:

  PUBLIC_DISCORD_INVITE_URL is not a valid absolute URL: nonsense
  PUBLIC_BACKEND_API_URL must be an HTTPS URL
  PUBLIC_SITE_NAME must be set to a non-empty value

svelte-check 459 files 0 errors, 338 unit tests passing, production
build, eslint and prettier all clean.
src/env.ts had its own reimplementation of isTruthy plus a `flag` wrapper
around it. Both are gone: the schema for every boolean variable is now
isTruthy itself, imported from the submodule source.

The reason the copy existed was that `@openshock/svelte-core/utils` is a
barrel that re-exports clipboard.svelte.ts, which imports svelte-sonner
and so reaches a real .svelte file. SvelteKit evaluates src/env.ts in a
plain Node context to resolve `static` values, where that fails with
`Unknown file extension ".svelte"`. The barrel was the problem, not the
package, so importing the file directly sidesteps it and leaves one
definition of isTruthy in the tree.

Exporting isTruthy from src/env.ts and importing it into app code was
tried first and rejected: onboarding-state.ts runs on the client, so
pulling src/env.ts into its module graph shipped the entire `variables`
object to the browser. The client bundle then contained every
description and validator message, including the name and the "INSECURE"
description of PRIVATE_BACKEND_TLS_INSECURE. Confirmed against a build
at the previous commit, where those strings are absent, and confirmed
absent again here.

App code keeps importing isTruthy from @openshock/svelte-core/utils; only
env.ts needs the barrel-free path.

No behaviour change. svelte-check 459 files 0 errors, 338 unit tests,
production build, eslint and prettier clean; static flags still inline as
real boolean literals and dynamic ones still validate at startup.
@hhvrc
hhvrc marked this pull request as ready for review October 2, 2026 09:12
svelte-core's feature/svelte-kit-3 branch was squash-merged into master as
c058e94 ("feat: Migrate to SvelteKit 3 (#18)") and the branch was deleted,
so the previous pointer a17e5e0 is no longer reachable from any remote
branch. Repoints the submodule at c058e94 and retargets the tracked branch
in .gitmodules from the deleted feature/svelte-kit-3 to master, so
`git submodule update --remote` keeps working.

The only content difference between a17e5e0 and c058e94 is a refactor of
isTruthy in src/lib/utils/parse.ts. It accepts the same set of values
(1 / true / yes / y / on, case-insensitive), so the env flags in src/env.ts
that use it as their schema are unaffected. package.json is identical, so
the lockfile does not move.

svelte-check 459 files 0 errors, 338 unit tests, production build, eslint
and prettier clean; static flags still inline as real boolean literals.
@hhvrc
hhvrc merged commit 4ecde1c into develop Oct 2, 2026
10 of 11 checks passed
@hhvrc
hhvrc deleted the feature/svelte-kit-3 branch October 2, 2026 09:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant