| 1 | # @fixtures/vocs |
| 2 | |
| 3 | E2E fixture for [vocs](https://vocs.dev) (the minimal React documentation |
| 4 | framework) on Cloudflare Workers. |
| 5 | |
| 6 | Vocs 2.x is built on **waku**: its `vocs()` vite plugin (public export |
| 7 | `vocs/vite`) composes waku's own `waku/vite-plugins` (environments, |
| 8 | adapter-alias, static-build, ...) with vocs's mdx/config/patch plugins, and it |
| 9 | peer-depends on `waku ^1.0.0-beta.6`. It is _not_ a fully static site: page |
| 10 | bodies are prerendered RSC elements, but the document shell is SSR'd per |
| 11 | request and there are dynamic API routes (`/api/search`, `/api/og`, |
| 12 | `/api/mcp`, `/api/feedback`) — so it runs as a worker. |
| 13 | |
| 14 | Vocs does not use waku's `unstable_combinedPlugins`, so the |
| 15 | `@alchemy.run/frontend-frameworks/waku` Framework layer can't drive it directly. Instead, |
| 16 | `@alchemy.run/frontend-frameworks/vocs` provides a first-class Vocs framework |
| 17 | integration and target contract. Its Cloudflare target composes the established |
| 18 | Waku adapter and RSC environment primitives while owning Vocs-specific target |
| 19 | selection, runtime requirements, config bridging, and public exports. |
| 20 | |
| 21 | There is no `vite.config.ts` and no `wrangler.jsonc`: `e2e.config.ts` carries |
| 22 | the entire worker configuration in memory; `vocs.config.ts` is vocs's own |
| 23 | (platform-agnostic) docs config. |
| 24 | |
| 25 | ## What it exercises |
| 26 | |
| 27 | - **Worker SSR** — the docs shell (sidebar, layout) is rendered by the worker |
| 28 | at request time in both dev (workerd module-runner) and preview (miniflare). |
| 29 | - **MDX pages** — `src/pages/*.mdx` with sidebar navigation. |
| 30 | - **Client interactivity** — `src/components/Counter.tsx` is a |
| 31 | `"use client"` component embedded in MDX, hydrated in the browser. |
| 32 | - **Static assets** — `public/hello.txt` rides along in `dist/public`, next to |
| 33 | vocs's build-time artifacts (`llms.txt`, `llms-full.txt`). |
| 34 | |
| 35 | ## Workerd bridges (and why vocs is pinned exactly) |
| 36 | |
| 37 | Upstream vocs only ships node/vercel/netlify adapters — nothing targets a |
| 38 | no-fs runtime. Runtime config resolution needs a small bridge, implemented as |
| 39 | the `workerdConfigBridge` Vite plugin in the public Vocs integration: |
| 40 | |
| 41 | 1. **Runtime config resolution** — vocs's server code calls |
| 42 | `Config.resolve({ server: true })` per request; in production that branch |
| 43 | dynamically imports an on-disk `dist/server/vocs.config.js` via |
| 44 | `import.meta.dirname` (Node server layout), which crashes in workerd. The |
| 45 | bridge rewrites that branch to import a virtual module which re-exports |
| 46 | the project's original `vocs.config.*`. Vite therefore loads and bundles |
| 47 | the config itself, preserving its imports and functions; Alchemy does not |
| 48 | separately resolve or serialize it. |
| 49 | |
| 50 | The transform hard-fails with a descriptive error if the installed vocs no |
| 51 | longer matches the expected internals — which is why `vocs` is pinned to an |
| 52 | exact version. On a bump, re-check the guarded patterns in |
| 53 | `packages/frontend-frameworks/src/vocs/Vocs.ts`. |
| 54 | |
| 55 | `nodejs_compat` (not just `nodejs_als`) is required: vocs's server bundle |
| 56 | imports `node:fs` and friends (guarded with try/catch at runtime). |
| 57 | |
| 58 | ## Commands |
| 59 | |
| 60 | ```sh |
| 61 | bun run dev # vocs dev over workerd (port 3105) |
| 62 | bun run build # programmatic vocs build -> dist/ + dist/build.json |
| 63 | bun run preview # miniflare over dist/build.json |
| 64 | bun run test # playwright: live (built worker) + dev |
| 65 | ``` |