Skip to content

Commit 6897fd9

Browse files
KaiSpenceryusukebe
authored andcommitted
feat(dev-server): add adapter and merge env (#93)
* when setting bindings, merge not overwrite * Adds adapter to dev server config
1 parent 551e880 commit 6897fd9

File tree

9 files changed

+699
-27
lines changed

9 files changed

+699
-27
lines changed

.changeset/tiny-dingos-divide.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@hono/vite-dev-server': minor
3+
---
4+
5+
feat: add `adapter` and merge `env`

packages/dev-server/e2e/e2e.test.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,62 @@ test('Should return a vite error page with stack trace - /invalid-error-response
9494
expect(response?.status()).toBe(500)
9595
expect(await response?.text()).toContain('e2e/mock/worker.ts')
9696
})
97+
98+
test('Should set bindings from wrangler.toml [vars]', async ({ page }) => {
99+
const res = await page.goto('/env', { waitUntil: 'domcontentloaded' })
100+
expect(res?.ok()).toBe(true)
101+
const json = await res?.json()
102+
expect(json).toBeTruthy()
103+
expect(json.env).toHaveProperty(
104+
'VARIABLE_FROM_WRANGLER_TOML',
105+
'VARIABLE_FROM_WRANGLER_TOML_VALUE'
106+
)
107+
})
108+
109+
test('Should set bindings from wrangler.toml [[d1_database]]', async ({ page }) => {
110+
const res = await page.goto('/env', { waitUntil: 'domcontentloaded' })
111+
expect(res?.ok()).toBe(true)
112+
const json = await res?.json()
113+
expect(json).toBeTruthy()
114+
expect(json.env).toHaveProperty('DB_FROM_WRANGLER_TOML')
115+
})
116+
117+
test('Should set bindings from root `env` in config', async ({ page }) => {
118+
const res = await page.goto('/env', { waitUntil: 'domcontentloaded' })
119+
expect(res?.ok()).toBe(true)
120+
const json = await res?.json()
121+
expect(json).toBeTruthy()
122+
expect(json.env).toHaveProperty('ENV_FROM_ROOT', 'ENV_FROM_ROOT_VALUE')
123+
})
124+
125+
test('Should set bindings from `cf` in config', async ({ page }) => {
126+
const res = await page.goto('/env', { waitUntil: 'domcontentloaded' })
127+
expect(res?.ok()).toBe(true)
128+
const json = await res?.json()
129+
expect(json).toBeTruthy()
130+
expect(json.env).toHaveProperty('ENV_FROM_DEPRACATED_CF', 'ENV_FROM_DEPRACATED_CF_VALUE')
131+
})
132+
133+
test('Should set bindings from `plugins` in config', async ({ page }) => {
134+
const res = await page.goto('/env', { waitUntil: 'domcontentloaded' })
135+
expect(res?.ok()).toBe(true)
136+
const json = await res?.json()
137+
expect(json).toBeTruthy()
138+
expect(json.env).toHaveProperty('ENV_FROM_PLUGIN', 'ENV_FROM_PLUGIN_VALUE')
139+
})
140+
141+
test('Should set bindings from `plugins` in config (async)', async ({ page }) => {
142+
const res = await page.goto('/env', { waitUntil: 'domcontentloaded' })
143+
expect(res?.ok()).toBe(true)
144+
const json = await res?.json()
145+
expect(json).toBeTruthy()
146+
expect(json.env).toHaveProperty('ENV_FROM_PLUGIN_AS_FUNC', 'ENV_FROM_PLUGIN_AS_FUNC_VALUE')
147+
})
148+
149+
test('Should set bindings from `adapter` in config', async ({ page }) => {
150+
const res = await page.goto('/env', { waitUntil: 'domcontentloaded' })
151+
expect(res?.ok()).toBe(true)
152+
const json = await res?.json()
153+
expect(json).toBeTruthy()
154+
expect(json.env).toHaveProperty('ENV_FROM_ADAPTER', 'ENV_FROM_ADAPTER_VALUE')
155+
})

packages/dev-server/e2e/mock/worker.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,8 @@ app.get('/invalid-error-response', (c) => {
7575
}
7676
})
7777

78+
app.get('/env', (c) => {
79+
return c.json({ env: c.env })
80+
})
81+
7882
export default app
Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,41 @@
11
import { defineConfig } from 'vite'
22
import devServer, { defaultOptions } from '../src'
33
import pages from '../src/cloudflare-pages'
4+
import { getPlatformProxy } from 'wrangler'
45

5-
export default defineConfig({
6-
plugins: [
7-
devServer({
8-
entry: './mock/worker.ts',
9-
exclude: [...defaultOptions.exclude, '/app/**'],
10-
plugins: [
11-
pages({
6+
export default defineConfig(async () => {
7+
const { env, dispose } = await getPlatformProxy()
8+
9+
return {
10+
plugins: [
11+
devServer({
12+
env: {
13+
ENV_FROM_ROOT: 'ENV_FROM_ROOT_VALUE',
14+
},
15+
cf: {
1216
bindings: {
13-
NAME: 'Hono',
17+
ENV_FROM_DEPRACATED_CF: 'ENV_FROM_DEPRACATED_CF_VALUE',
18+
},
19+
},
20+
entry: './mock/worker.ts',
21+
exclude: [...defaultOptions.exclude, '/app/**'],
22+
plugins: [
23+
{ onServerClose: dispose, env },
24+
pages({
25+
bindings: {
26+
NAME: 'Hono',
27+
},
28+
}),
29+
{ env: { ENV_FROM_PLUGIN: 'ENV_FROM_PLUGIN_VALUE' } },
30+
{ env: async () => ({ ENV_FROM_PLUGIN_AS_FUNC: 'ENV_FROM_PLUGIN_AS_FUNC_VALUE' }) },
31+
],
32+
adapter: {
33+
env: {
34+
ENV_FROM_ADAPTER: 'ENV_FROM_ADAPTER_VALUE',
1435
},
15-
}),
16-
],
17-
}),
18-
],
36+
onServerClose: dispose,
37+
},
38+
}),
39+
],
40+
}
1941
})
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[vars]
2+
VARIABLE_FROM_WRANGLER_TOML = "VARIABLE_FROM_WRANGLER_TOML_VALUE"
3+
4+
[[d1_databases]]
5+
binding = "DB_FROM_WRANGLER_TOML"
6+
database_name = "DB_NAME"
7+
database_id = "DB_ID"
8+

packages/dev-server/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@
6464
"rimraf": "^5.0.1",
6565
"tsup": "^7.2.0",
6666
"vite": "^5.1.1",
67-
"vitest": "^0.34.6"
67+
"vitest": "^0.34.6",
68+
"wrangler": "^3.28.4"
6869
},
6970
"peerDependencies": {
7071
"hono": "*"

packages/dev-server/src/cloudflare-pages/cloudflare-pages.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export const getEnv: GetEnv<Options> = (options) => async () => {
3333

3434
const env = {
3535
...(await mf.getBindings()),
36+
...options.bindings, // Do not overwrite existing bindings.
3637
// `env.ASSETS.fetch()` function for Cloudflare Pages.
3738
ASSETS: {
3839
async fetch(input: RequestInfo | URL, init?: RequestInit | undefined) {

packages/dev-server/src/dev-server.ts

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,44 @@ export type DevServerOptions = {
1212
exclude?: (string | RegExp)[]
1313
env?: Env | EnvFunc
1414
plugins?: Plugin[]
15+
/**
16+
* This can be used to inject environment variables into the worker from your wrangler.toml for example,
17+
* by making use of the helper function `getPlatformProxy` from `wrangler`.
18+
*
19+
* @example
20+
*
21+
* ```ts
22+
* import { defineConfig } from 'vite'
23+
* import devServer from '@hono/vite-dev-server'
24+
* import getPlatformProxy from 'wrangler'
25+
*
26+
* export default defineConfig(async () => {
27+
* const { env, dispose } = await getPlatformProxy()
28+
* return {
29+
* plugins: [
30+
* devServer({
31+
* adapter: {
32+
* env,
33+
* onServerClose: dispose
34+
* },
35+
* }),
36+
* ],
37+
* }
38+
* })
39+
* ```
40+
*
41+
*
42+
*/
43+
adapter?: {
44+
/**
45+
* Environment variables to be injected into the worker
46+
*/
47+
env?: Env
48+
/**
49+
* Function called when the vite dev server is closed
50+
*/
51+
onServerClose?: () => Promise<void>
52+
}
1553
} & {
1654
/**
1755
* @deprecated
@@ -21,7 +59,7 @@ export type DevServerOptions = {
2159
cf?: Parameters<typeof cloudflarePagesGetEnv>[0]
2260
}
2361

24-
export const defaultOptions: Required<Omit<DevServerOptions, 'env' | 'cf'>> = {
62+
export const defaultOptions: Required<Omit<DevServerOptions, 'env' | 'cf' | 'adapter'>> = {
2563
entry: './src/index.ts',
2664
export: 'default',
2765
injectClientScript: true,
@@ -82,21 +120,30 @@ export function devServer(options?: DevServerOptions): VitePlugin {
82120

83121
if (options?.env) {
84122
if (typeof options.env === 'function') {
85-
env = await options.env()
123+
env = { ...env, ...(await options.env()) }
86124
} else {
87-
env = options.env
125+
env = { ...env, ...options.env }
126+
}
127+
}
128+
if (options?.cf) {
129+
env = {
130+
...env,
131+
...(await cloudflarePagesGetEnv(options.cf)()),
88132
}
89-
} else if (options?.cf) {
90-
env = await cloudflarePagesGetEnv(options.cf)()
91133
}
92-
93134
if (options?.plugins) {
94135
for (const plugin of options.plugins) {
95136
if (plugin.env) {
96-
env = typeof plugin.env === 'function' ? await plugin.env() : plugin.env
137+
env = {
138+
...env,
139+
...(typeof plugin.env === 'function' ? await plugin.env() : plugin.env),
140+
}
97141
}
98142
}
99143
}
144+
if (options?.adapter?.env) {
145+
env = { ...env, ...options.adapter.env }
146+
}
100147

101148
const response = await app.fetch(request, env, {
102149
waitUntil: async (fn) => fn,
@@ -150,6 +197,9 @@ export function devServer(options?: DevServerOptions): VitePlugin {
150197
}
151198
}
152199
}
200+
if (options?.adapter?.onServerClose) {
201+
await options.adapter.onServerClose()
202+
}
153203
})
154204
},
155205
}

0 commit comments

Comments
 (0)