diff --git a/.changeset/shaky-experts-beg.md b/.changeset/shaky-experts-beg.md new file mode 100644 index 000000000000..0b4b7e625a32 --- /dev/null +++ b/.changeset/shaky-experts-beg.md @@ -0,0 +1,5 @@ +--- +"@cloudflare/vite-plugin": patch +--- + +Ensure that headers set via `server.headers` in the Vite config are added to HTML asset responses in development. diff --git a/packages/vite-plugin-cloudflare/playground/react-spa/__tests__/server-headers/react-spa.spec.ts b/packages/vite-plugin-cloudflare/playground/react-spa/__tests__/server-headers/react-spa.spec.ts new file mode 100644 index 000000000000..801b9b948168 --- /dev/null +++ b/packages/vite-plugin-cloudflare/playground/react-spa/__tests__/server-headers/react-spa.spec.ts @@ -0,0 +1,27 @@ +import { describe, expect, test } from "vitest"; +import { getResponse, isBuild } from "../../../__test-utils__"; + +describe.runIf(!isBuild)( + "adds headers included in Vite's `server.headers` to asset responses in dev", + () => { + test("adds headers to HTML responses", async () => { + const response = await getResponse(); + const headers = await response.allHeaders(); + expect(headers).toMatchObject({ + "custom-string": "string-value", + "custom-string-array": "one, two, three", + "custom-number": "123", + }); + }); + + test("adds headers to non-HTML responses", async () => { + const response = await getResponse("/vite.svg"); + const headers = await response.allHeaders(); + expect(headers).toMatchObject({ + "custom-string": "string-value", + "custom-string-array": "one, two, three", + "custom-number": "123", + }); + }); + } +); diff --git a/packages/vite-plugin-cloudflare/playground/react-spa/package.json b/packages/vite-plugin-cloudflare/playground/react-spa/package.json index eea6e702929d..364778ec5c76 100644 --- a/packages/vite-plugin-cloudflare/playground/react-spa/package.json +++ b/packages/vite-plugin-cloudflare/playground/react-spa/package.json @@ -6,6 +6,7 @@ "build": "vite build --app", "check:types": "tsc --build", "dev": "vite dev", + "dev:server-headers": "vite dev -c ./vite.config.server-headers.ts", "preview": "vite preview" }, "dependencies": { diff --git a/packages/vite-plugin-cloudflare/playground/react-spa/vite.config.server-headers.ts b/packages/vite-plugin-cloudflare/playground/react-spa/vite.config.server-headers.ts new file mode 100644 index 000000000000..af5e31a26bf0 --- /dev/null +++ b/packages/vite-plugin-cloudflare/playground/react-spa/vite.config.server-headers.ts @@ -0,0 +1,14 @@ +import { cloudflare } from "@cloudflare/vite-plugin"; +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; + +export default defineConfig({ + server: { + headers: { + "custom-string": "string-value", + "custom-string-array": ["one", "two", "three"], + "custom-number": 123, + }, + }, + plugins: [react(), cloudflare({ inspectorPort: false, persistState: false })], +}); diff --git a/packages/vite-plugin-cloudflare/src/asset-workers/asset-worker.ts b/packages/vite-plugin-cloudflare/src/asset-workers/asset-worker.ts index 14a2588625a7..13dce2db267f 100644 --- a/packages/vite-plugin-cloudflare/src/asset-workers/asset-worker.ts +++ b/packages/vite-plugin-cloudflare/src/asset-workers/asset-worker.ts @@ -1,10 +1,12 @@ import AssetWorker from "@cloudflare/workers-shared/asset-worker"; import { UNKNOWN_HOST } from "../shared"; import type { Env as _Env } from "@cloudflare/workers-shared/asset-worker"; +import type { ResolvedConfig } from "vite"; interface Env extends _Env { __VITE_HTML_EXISTS__: Fetcher; __VITE_FETCH_HTML__: Fetcher; + __VITE_HEADERS__: string; } export default class CustomAssetWorker extends AssetWorker { @@ -13,6 +15,20 @@ export default class CustomAssetWorker extends AssetWorker { const modifiedResponse = new Response(response.body, response); modifiedResponse.headers.delete("ETag"); modifiedResponse.headers.delete("Cache-Control"); + // Add headers set via `server.headers` in the Vite config + const viteHeaders = JSON.parse( + this.env.__VITE_HEADERS__ + ) as ResolvedConfig["server"]["headers"]; + + for (const [key, value] of Object.entries(viteHeaders)) { + if (Array.isArray(value)) { + for (const item of value) { + modifiedResponse.headers.append(key, item); + } + } else if (value !== undefined) { + modifiedResponse.headers.set(key, String(value)); + } + } return modifiedResponse; } diff --git a/packages/vite-plugin-cloudflare/src/miniflare-options.ts b/packages/vite-plugin-cloudflare/src/miniflare-options.ts index a1be6e0fc842..c5603fc23b66 100644 --- a/packages/vite-plugin-cloudflare/src/miniflare-options.ts +++ b/packages/vite-plugin-cloudflare/src/miniflare-options.ts @@ -292,6 +292,7 @@ export async function getDevMiniflareOptions(config: { ], bindings: { CONFIG: assetsConfig, + __VITE_HEADERS__: JSON.stringify(viteDevServer.config.server.headers), }, serviceBindings: { __VITE_HTML_EXISTS__: async (request) => {