Skip to content

Commit 18831b8

Browse files
authored
Merge pull request #4 from JoviDeCroock/feat/adapter-by-reference
feat: pass adapters by reference instead of string
2 parents 6baa017 + 61b150d commit 18831b8

19 files changed

Lines changed: 295 additions & 307 deletions

File tree

.github/workflows/ci.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ jobs:
1818

1919
- name: Install pnpm
2020
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0
21+
with:
22+
version: 10.28.1
2123

2224
- name: Install Node.js
2325
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
@@ -39,3 +41,9 @@ jobs:
3941

4042
- name: Test
4143
run: pnpm test
44+
45+
- name: Install Playwright browsers
46+
run: pnpm exec playwright install --with-deps chromium
47+
48+
- name: E2E tests
49+
run: pnpm e2e

docs/ADAPTERS.md

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,21 @@ are served and how ISG revalidation state is tracked.
2626

2727
## Adapter Interface
2828

29-
Each adapter exports two things:
29+
Each adapter exports three things:
3030

31-
### 1. Request handler factory
31+
### 1. Adapter factory (for the Vite plugin)
32+
33+
```typescript
34+
// Example: Node adapter
35+
import { nodeAdapter } from "@viact/adapter-node";
36+
37+
viact({ adapter: nodeAdapter() });
38+
```
39+
40+
The factory returns a `ViactAdapter` object that the Vite plugin uses to
41+
generate the server entry module.
42+
43+
### 2. Request handler factory
3244

3345
```typescript
3446
// Example: Node adapter
@@ -37,14 +49,14 @@ export function createNodeRequestHandler<TContext>(
3749
): (req: IncomingMessage, res: ServerResponse) => Promise<void>;
3850
```
3951

40-
### 2. Entry module generator (for the Vite plugin)
52+
### 3. Entry module generator (for custom adapters)
4153

4254
```typescript
4355
export function createNodeServerEntryModule(options?: NodeServerEntryModuleOptions): string;
4456
```
4557

46-
The Vite plugin calls the entry module generator to create a virtual module
47-
(`virtual:viact/node-server`) that bootstraps the server.
58+
The adapter factory calls the entry module generator internally to create a virtual module
59+
(`virtual:viact/server`) that bootstraps the server.
4860

4961
---
5062

@@ -114,7 +126,7 @@ the executable production server entry.
114126
- **Default request context**: generated worker entries pass `{ env,
115127
executionContext }` to viact so loaders, actions, and middleware can access
116128
bindings without extra wiring.
117-
- **Build output**: `viact({ adapter: "cloudflare" })` makes `viact build`
129+
- **Build output**: `viact({ adapter: cloudflareAdapter() })` makes `viact build`
118130
emit a Worker bundle in `dist/server/server.js`. You own a `wrangler.jsonc`
119131
in your project root that points at the output — this lets you add KV, D1,
120132
R2, cron, and any other Cloudflare bindings without losing them on rebuild.
@@ -163,7 +175,7 @@ export default {
163175

164176
- **Edge runtime handler**: generated server entries export a default `fetch`-style
165177
handler that Vercel bundles as an Edge Function.
166-
- **Build Output API v3**: `viact({ adapter: "vercel" })` makes `viact build`
178+
- **Build Output API v3**: `viact({ adapter: vercelAdapter() })` makes `viact build`
167179
emit `.vercel/output/config.json`, `.vercel/output/static/`, and
168180
`.vercel/output/functions/render.func/`.
169181
- **Clean URL routing**: prerendered SSG pages are copied into
@@ -202,19 +214,51 @@ export default async function handle(request, context) {
202214
203215
## Writing a Custom Adapter
204216
205-
An adapter needs to:
217+
A custom adapter exports a factory function that returns a `ViactAdapter` object:
218+
219+
```typescript
220+
import type { ViactAdapter } from "@viact/vite-plugin";
221+
222+
export function myAdapter(options?: MyOptions): ViactAdapter {
223+
return {
224+
id: "my-platform",
225+
serverImports: 'import { handleViactRequest, resolveApp, resolveApiRoutes } from "viact";',
226+
createServerEntryModule() {
227+
// Return JavaScript source code that will be appended to the
228+
// generated virtual:viact/server module.
229+
return `
230+
export default async function handle(request) {
231+
return handleViactRequest({
232+
app: resolvedApp,
233+
registry,
234+
request,
235+
apiRoutes,
236+
clientEntryUrl: clientEntryUrl ?? undefined,
237+
cssManifest,
238+
jsManifest,
239+
});
240+
}
241+
`;
242+
},
243+
};
244+
}
245+
```
246+
247+
The generated server entry module has access to `resolvedApp`, `registry`,
248+
`apiRoutes`, `clientEntryUrl`, `cssManifest`, and `jsManifest` -- your
249+
`createServerEntryModule()` code can reference these directly.
250+
251+
At the runtime level, an adapter also typically needs to:
206252
207253
1. **Accept a platform request** and convert it to a Web `Request` object
208-
2. **Check for static assets** serve files from `dist/client/` with appropriate
254+
2. **Check for static assets** -- serve files from `dist/client/` with appropriate
209255
headers (content-type, cache-control with immutable for hashed assets)
210-
3. **Check for prerendered pages** SSG and ISG routes have HTML files on disk.
256+
3. **Check for prerendered pages** -- SSG and ISG routes have HTML files on disk.
211257
For ISG, implement staleness checking.
212-
4. **Delegate dynamic requests** to `handleViactRequest()` from `@viact/framework`
258+
4. **Delegate dynamic requests** to `handleViactRequest()` from `viact`
213259
5. **Convert the Web `Response`** back to the platform's response format
214-
6. **Provide a context factory** create app-level context from platform-specific
260+
6. **Provide a context factory** -- create app-level context from platform-specific
215261
inputs (env bindings, headers, etc.)
216-
7. **Export an entry module generator** — a function that returns JavaScript source
217-
code for the Vite plugin to use as a virtual module
218262
219263
### Context factory pattern
220264

examples/basic/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"type": "module",
66
"dependencies": {
77
"@viact/adapter-node": "workspace:*",
8+
"@viact/adapter-vercel": "workspace:*",
89
"viact": "workspace:*"
910
},
1011
"devDependencies": {

examples/basic/vite.config.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
import { defineConfig } from "vite";
22
import { viact } from "@viact/vite-plugin";
33

4-
export default defineConfig({
5-
plugins: [
6-
viact({
7-
adapter:
8-
process.env.VIACT_ADAPTER === "vercel"
9-
? "vercel"
10-
: process.env.VIACT_ADAPTER === "node"
11-
? "node"
12-
: "cloudflare",
13-
}),
14-
],
15-
});
4+
async function resolveAdapter() {
5+
if (process.env.VIACT_ADAPTER === "vercel") {
6+
const { vercelAdapter } = await import("@viact/adapter-vercel");
7+
return vercelAdapter();
8+
}
9+
10+
if (process.env.VIACT_ADAPTER === "node") {
11+
const { nodeAdapter } = await import("@viact/adapter-node");
12+
return nodeAdapter();
13+
}
14+
15+
const { cloudflareAdapter } = await import("@viact/adapter-cloudflare");
16+
return cloudflareAdapter();
17+
}
18+
19+
export default defineConfig(async () => ({
20+
plugins: [viact({ adapter: await resolveAdapter() })],
21+
}));

examples/cloudflare/vite.config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { defineConfig } from "vite";
22
import { viact } from "@viact/vite-plugin";
3+
import { cloudflareAdapter } from "@viact/adapter-cloudflare";
34

45
export default defineConfig({
5-
plugins: [viact({ adapter: "cloudflare" })],
6+
plugins: [viact({ adapter: cloudflareAdapter() })],
67
});

examples/docs/src/routes/docs/adapters.md

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@ Deploy to Cloudflare's global edge network. Static assets are served from the `A
3434
```ts [vite.config.ts]
3535
import { defineConfig } from "vite";
3636
import { viact } from "@viact/vite-plugin";
37+
import { cloudflareAdapter } from "@viact/adapter-cloudflare";
3738

3839
export default defineConfig({
39-
plugins: [viact({ adapter: "cloudflare" })],
40+
plugins: [viact({ adapter: cloudflareAdapter() })],
4041
});
4142
```
4243

@@ -96,7 +97,8 @@ Deploy using Vercel's Build Output API v3. SSG pages are served from the static
9697

9798
```ts
9899
// vite.config.ts
99-
viact({ adapter: "vercel" })
100+
import { vercelAdapter } from "@viact/adapter-vercel";
101+
viact({ adapter: vercelAdapter() })
100102

101103
// package.json
102104
"@viact/adapter-vercel": "*"
@@ -130,7 +132,8 @@ Run viact as a standard Node.js HTTP server. The adapter handles static file ser
130132

131133
```ts
132134
// vite.config.ts
133-
viact({ adapter: "node" })
135+
import { nodeAdapter } from "@viact/adapter-node";
136+
viact({ adapter: nodeAdapter() })
134137

135138
// package.json
136139
"@viact/adapter-node": "*"
@@ -172,11 +175,39 @@ The context object is available as `args.context` in every loader, action, middl
172175

173176
## Writing a Custom Adapter
174177

175-
A custom adapter needs to:
178+
A custom adapter exports a factory function that returns a `ViactAdapter` object:
179+
180+
```ts
181+
import type { ViactAdapter } from "@viact/vite-plugin";
182+
183+
export function myAdapter(): ViactAdapter {
184+
return {
185+
id: "my-platform",
186+
serverImports: 'import { handleViactRequest, resolveApp, resolveApiRoutes } from "viact";',
187+
createServerEntryModule() {
188+
return `
189+
export default async function handle(request) {
190+
return handleViactRequest({
191+
app: resolvedApp,
192+
registry,
193+
request,
194+
apiRoutes,
195+
clientEntryUrl: clientEntryUrl ?? undefined,
196+
cssManifest,
197+
jsManifest,
198+
});
199+
}
200+
`;
201+
},
202+
};
203+
}
204+
```
205+
206+
At the runtime level, an adapter also typically needs to:
176207

177208
1. Accept a platform request and convert it to a Web `Request`
178-
2. Check for static assets serve files from `dist/client/` with appropriate headers
179-
3. Check for prerendered pages serve SSG/ISG HTML (with staleness checking for ISG)
209+
2. Check for static assets -- serve files from `dist/client/` with appropriate headers
210+
3. Check for prerendered pages -- serve SSG/ISG HTML (with staleness checking for ISG)
180211
4. Delegate dynamic requests to `handleViactRequest()` from `viact`
181212
5. Convert the Web `Response` back to the platform's response format
182213
6. Provide a context factory for platform-specific values

0 commit comments

Comments
 (0)