1# @fixtures/sveltekit-spa
2
3E2E fixture for `@alchemy.run/frontend-frameworks/sveltekit` exercising the **pure-SPA path**:
4`ssr = false` in the root `+layout.ts`, the adapter's SPA fallback page, and —
5the nuance under test — `+server.ts` endpoints that **still run server-side**
6even though every page is client-rendered.
7
8Where `packages/frontend-frameworks/fixtures/sveltekit` is SSR-first, this fixture is the inverse:
9
10- `src/routes/+layout.ts` sets `ssr = false` / `prerender = false` for the
11 whole app.
12- `notFoundHandling: "single-page-application"` in `e2e.config.ts` drives the
13 in-memory adapter's fallback generation (`builder.generateFallback` →
14 `index.html`) and must flow through to the deployed assets'
15 `not_found_handling` (mirrored in the miniflare preview's `assetConfig`).
16- A **real user `vite.config.ts`** registers `sveltekit()` with a user alias
17 (`$spa` → `src/lib`) the widgets page imports through and a user
18 `preprocess` that rewrites a marker rendered by the home page — the file
19 must be loaded natively per the user-config principle. There is
20 deliberately NO `svelte.config.js`: kit v3 (`3.0.0-next.9`) hard-errors on
21 its presence ("svelte.config.js is no longer used") — ALL configuration,
22 including Svelte `preprocess`/`compilerOptions`, lives in the
23 `sveltekit(...)` call.
24
25## Status: ENABLED
26
27The user-config wave landed (the real `vite.config.ts` below loads natively)
28and the worker shim implements the SPA not-found deferral, so the suite runs
29ungated: `bun run test` calls `playwright test` directly (12 tests, `live` +
30`dev`).
31
32## How the SPA fallback reaches the worker (the deferral contract)
33
34Upstream `@sveltejs/adapter-cloudflare`'s `worker.js` never defers a 404 to
35the assets layer — kit's `server.respond` renders its own 404 error page for
36unmatched routes, and the configured `assets.not_found_handling` only
37applies to requests the worker never sees. Our shim makes the configured
38`single-page-application` handling reachable (no new option — see
39`packages/sveltekit/src/WorkerShim.ts`): when the adapter is built with
40`notFoundHandling: "single-page-application"`, the shim defers to
41`env.ASSETS.fetch(req)` — where the asset worker applies
42`not_found_handling` and serves the generated `index.html` fallback — iff
43
44- `server.respond` returned **404**, and
45- the request is **navigation-shaped**: `GET`/`HEAD` with `Accept`
46 containing `text/html`, and
47- **no kit route pattern matches** the pathname — the most conservative
48 available signal that the 404 is kit's router-level "no route" error
49 (upstream has no deferral semantics to mirror, and kit marks router 404s
50 with no header). Endpoints only exist on matched routes, so intentional
51 endpoint 404s are never deferred; a pattern match whose param matchers
52 fail keeps kit's own 404 page.
53
54`notFoundHandling: "404-page"` and the default keep exact upstream behavior
55(no deferral). Observable here: a navigation-shaped request for an unknown
56path returns the `index.html` shell with **200** in live/preview, while a
57non-navigation request (no `Accept: text/html`) still gets kit's 404.
58
59## What the app exercises
60
61- **Client-side routing** between three routes (`/`, `/widgets`, `/about`)
62 with a planted `window` marker proving no full navigation occurs.
63- **Deep links**: a direct request for `/widgets` returns the app shell (no
64 widget markup in the raw HTML — asserted via the `sveltekit-spa-shell`
65 marker in `app.html`) and hydrates into the correct view, in both live and
66 dev.
67- **Universal load, server endpoint** (`/widgets` + `/api/widgets`): the
68 `+page.ts` load runs exclusively in the browser (`ssr = false`), while the
69 `+server.ts` endpoint it fetches runs server-side and reads a
70 `Text.local` binding (`FIXTURE_MESSAGE`) from `platform.env`.
71- **SPA fallback / not_found_handling**: an unmatched path
72 (`/definitely/not/a/route`) serves the shell and hydrates into kit's
73 client-side 404 error view (`+error.svelte`).
74- **Direct static asset** serve (`static/robots.txt`).
75- **User config honored**: the `$spa` kit alias and the marker preprocessor
76 (both declared in the user's `vite.config.ts` `sveltekit(...)` call) are
77 observable in the rendered app.
78
79## Commands
80
81```sh
82bun run dev # kit's Vite dev server via the harness (port 3108)
83bun run build # kit build + in-memory adapt() + rolldown pass -> dist/build.json
84bun run preview # miniflare over dist/build.json + .svelte-kit/cloudflare assets
85bun run test # playwright e2e (live + dev)
86```