| 1 | # @fixtures/waku |
| 2 | |
| 3 | E2E fixture for `@alchemy.run/frontend-frameworks/waku` — the wrangler-free [Waku](https://waku.gg) |
| 4 | integration for Cloudflare Workers. |
| 5 | |
| 6 | There is no `vite.config.ts` and no `wrangler.jsonc`: `e2e.config.ts` |
| 7 | selects the framework and carries the entire worker configuration in memory |
| 8 | via the target-scoped carriage (`target.cloudflare.worker` for the dev/build |
| 9 | worker config, `target.cloudflare.preview` for the miniflare preview |
| 10 | server). A real `waku.config.ts` DOES exist — the integration loads it |
| 11 | natively (the same `vite.runnerImport("/waku.config")` semantics as waku's |
| 12 | own CLI) and the smoke test asserts both of its user-observable settings: |
| 13 | the `rscBase: "custom-rsc"` override and a user vite plugin's virtual |
| 14 | module 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 | |
| 47 | Every behavior is asserted in both Playwright modes (`live` = built worker |
| 48 | under miniflare, `dev` = workerd-backed vite dev server) from one shared |
| 49 | worker-scoped server fixture per mode — no per-test servers. |
| 50 | |
| 51 | ## Commands |
| 52 | |
| 53 | ```sh |
| 54 | bun run dev # waku dev over workerd (port 3101) |
| 55 | bun run build # programmatic waku build -> dist/ + dist/build.json |
| 56 | bun run preview # miniflare over dist/build.json |
| 57 | bun run test # playwright: live (built worker) + dev |
| 58 | ``` |
| 59 | |
| 60 | ## SSG-in-workerd note |
| 61 | |
| 62 | SSG rendering happens inside **workerd**: the SSG step of `waku build` |
| 63 | boots a `vite preview` server over the same resolved config as the build, |
| 64 | and the cloudflare vite plugin's preview mode serves the freshly built |
| 65 | worker through workerd. A page with a _top-level_ |
| 66 | `import { env } from "cloudflare:workers"` (see `src/pages/ssg-env.tsx`) |
| 67 | builds and prerenders with real bindings — vs upstream without |
| 68 | `@cloudflare/vite-plugin`, where SSG falls back to Node and the same import |
| 69 | breaks the build. The guarded dynamic-import pattern in `src/env.ts` is |
| 70 | kept as the portable variant for modules that must also load outside a |
| 71 | Cloudflare environment. |