1# @fixtures/waku
2
3E2E fixture for `@alchemy.run/frontend-frameworks/waku` — the wrangler-free [Waku](https://waku.gg)
4integration for Cloudflare Workers.
5
6There is no `vite.config.ts` and no `wrangler.jsonc`: `e2e.config.ts`
7selects the framework and carries the entire worker configuration in memory
8via the target-scoped carriage (`target.cloudflare.worker` for the dev/build
9worker config, `target.cloudflare.preview` for the miniflare preview
10server). A real `waku.config.ts` DOES exist — the integration loads it
11natively (the same `vite.runnerImport("/waku.config")` semantics as waku's
12own CLI) and the smoke test asserts both of its user-observable settings:
13the `rscBase: "custom-rsc"` override and a user vite plugin's virtual
14module rendered by `src/pages/config-marker.tsx`.
15
16## What it exercises
17
18- **SSR / RSC** — `src/pages/index.tsx` is a dynamic page rendered by the
19 worker at request time, reading the `MESSAGE` text binding through
20 `cloudflare:workers` (workerd module-runner in dev, miniflare in preview).
21- **Server actions** — `src/actions.ts` is a `"use server"` module driven by
22 `useActionState` in `src/components/GreetingForm.tsx`: the form submission
23 round-trips through the RSC endpoint and executes inside the worker (the
24 action reads the `MESSAGE` binding to prove it).
25- **Dynamic route params** — `src/pages/items/[id].tsx` receives the `[id]`
26 segment as a prop and renders at request time.
27- **API routes** — `src/pages/_api/echo.ts` exports `GET`/`POST` handlers,
28 served at `/echo` (waku strips the `_api` prefix).
29- **SSG** — `src/pages/about.tsx` is a static page prerendered at build time
30 into `dist/public` (HTML + RSC payload), exercising waku's
31 `__WAKU_START_PREVIEW_SERVER__` build path.
32- **SSG inside workerd** — `src/pages/ssg-env.tsx` has a **top-level**
33 `import { env } from "cloudflare:workers"` and is prerendered at build
34 time: the build only succeeds because SSG renders through the cloudflare
35 vite plugin's preview mode (the freshly built worker under workerd), and
36 the emitted HTML bakes in the real `MESSAGE` binding value.
37- **Middleware** — `src/middleware/headers.ts` is a managed-mode middleware
38 (collected by waku's server entry via `import.meta.glob`) that sets an
39 `x-waku-middleware` response header, asserted in both modes.
40- **Client interactivity** — `src/components/Counter.tsx` is a
41 `"use client"` component hydrated in the browser.
42- **Client state across navigation** — `src/components/NavCounter.tsx` lives
43 in the (static) layout; its state must survive waku's client navigation
44 because the router keeps the layout mounted while swapping pages.
45- **Static assets** — `public/hello.txt` rides along in `dist/public`.
46
47Every behavior is asserted in both Playwright modes (`live` = built worker
48under miniflare, `dev` = workerd-backed vite dev server) from one shared
49worker-scoped server fixture per mode — no per-test servers.
50
51## Commands
52
53```sh
54bun run dev # waku dev over workerd (port 3101)
55bun run build # programmatic waku build -> dist/ + dist/build.json
56bun run preview # miniflare over dist/build.json
57bun run test # playwright: live (built worker) + dev
58```
59
60## SSG-in-workerd note
61
62SSG rendering happens inside **workerd**: the SSG step of `waku build`
63boots a `vite preview` server over the same resolved config as the build,
64and the cloudflare vite plugin's preview mode serves the freshly built
65worker through workerd. A page with a _top-level_
66`import { env } from "cloudflare:workers"` (see `src/pages/ssg-env.tsx`)
67builds and prerenders with real bindings — vs upstream without
68`@cloudflare/vite-plugin`, where SSG falls back to Node and the same import
69breaks the build. The guarded dynamic-import pattern in `src/env.ts` is
70kept as the portable variant for modules that must also load outside a
71Cloudflare environment.