diff --git a/.changeset/hot-pandas-tan.md b/.changeset/hot-pandas-tan.md new file mode 100644 index 0000000..2896900 --- /dev/null +++ b/.changeset/hot-pandas-tan.md @@ -0,0 +1,5 @@ +--- +"vite-plugin-static-copy": patch +--- + +send headers set by `server.headers` in dev diff --git a/src/middleware.ts b/src/middleware.ts index 599c133..5628ab8 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -12,7 +12,7 @@ import { parse } from '@polka/url' import { lookup } from 'mrmime' import type { Stats } from 'node:fs' import { statSync, createReadStream, existsSync } from 'node:fs' -import type { Connect } from 'vite' +import type { Connect, ServerOptions } from 'vite' import type { IncomingMessage, OutgoingHttpHeaders, @@ -205,17 +205,13 @@ function sendStatic( createReadStream(file, opts).pipe(res) } -async function sendTransform( +function sendTransform( req: IncomingMessage, res: ServerResponse, file: string, - transform: TransformOptionObject -): Promise { - const transformedContent = await getTransformedContent(file, transform) - if (transformedContent === null) { - return false - } - + transform: TransformOptionObject, + transformedContent: string | Buffer +): void { const transformHeaders = getTransformHeaders( file, transform.encoding, @@ -225,7 +221,7 @@ async function sendTransform( if (req.headers['if-none-match'] === transformHeaders['ETag']) { res.writeHead(304) res.end() - return true + return } const code = 200 @@ -233,7 +229,28 @@ async function sendTransform( res.writeHead(code, headers) res.end(transformedContent) - return true + return +} + +function setHeaders( + res: ServerResponse, + pathname: string, + headers: OutgoingHttpHeaders | undefined +) { + // Matches js, jsx, ts, tsx. + // The reason this is done, is that the .ts file extension is reserved + // for the MIME type video/mp2t. In almost all cases, we can expect + // these files to be TypeScript files, and for Vite to serve them with + // this Content-Type. + if (/\.[tj]sx?$/.test(pathname)) { + res.setHeader('Content-Type', 'application/javascript') + } + + if (headers) { + for (const name in headers) { + res.setHeader(name, headers[name]!) + } + } } function return404(res: ServerResponse, next: Connect.NextFunction) { @@ -246,7 +263,11 @@ function return404(res: ServerResponse, next: Connect.NextFunction) { } export function serveStaticCopyMiddleware( - { root, publicDir }: { root: string; publicDir: string }, + { + root, + publicDir, + server + }: { root: string; publicDir: string; server: ServerOptions }, fileMap: FileMap ): Connect.NextHandleFunction { // Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...` @@ -267,30 +288,29 @@ export function serveStaticCopyMiddleware( return } - // Matches js, jsx, ts, tsx. - // The reason this is done, is that the .ts file extension is reserved - // for the MIME type video/mp2t. In almost all cases, we can expect - // these files to be TypeScript files, and for Vite to serve them with - // this Content-Type. - if (/\.[tj]sx?$/.test(pathname)) { - res.setHeader('Content-Type', 'application/javascript') - } - const transformOption = resolveTransformOption(data.transform) if (transformOption) { - const sent = await sendTransform( - req, - res, + const transformedContent = await getTransformedContent( data.filepath, transformOption ) - if (!sent) { + if (transformedContent === null) { return404(res, next) return } + + setHeaders(res, pathname, server.headers) + sendTransform( + req, + res, + data.filepath, + transformOption, + transformedContent + ) return } + setHeaders(res, pathname, server.headers) sendStatic(req, res, data.filepath, data.stats) } catch (e) { if (e instanceof Error) { diff --git a/test/fixtures/vite.other.config.ts b/test/fixtures/vite.other.config.ts index a39598a..74c483c 100644 --- a/test/fixtures/vite.other.config.ts +++ b/test/fixtures/vite.other.config.ts @@ -3,6 +3,12 @@ import { viteStaticCopy } from 'vite-plugin-static-copy' export default defineConfig({ appType: 'custom', // disable SPA/MPA fallback + server: { + headers: { + 'Cross-Origin-Embedder-Policy': 'require-corp', + 'Cross-Origin-Opener-Policy': 'same-origin' + } + }, plugins: [ viteStaticCopy({ targets: [ diff --git a/test/tests.test.ts b/test/tests.test.ts index 3ae0575..838f01f 100644 --- a/test/tests.test.ts +++ b/test/tests.test.ts @@ -77,6 +77,15 @@ describe('serve', () => { expect(res.status).toBe(200) expect(res.headers.get('Access-Control-Allow-Origin')).toBe('*') }) + + test.concurrent('headers', async () => { + const res = await fetchFromServer(server, '/fixture1/foo.txt') + expect(res.status).toBe(200) + expect(res.headers.get('Cross-Origin-Embedder-Policy')).toBe( + 'require-corp' + ) + expect(res.headers.get('Cross-Origin-Opener-Policy')).toBe('same-origin') + }) }) })