diff options
author | 2023-07-05 16:45:58 +0100 | |
---|---|---|
committer | 2023-07-05 16:45:58 +0100 | |
commit | 9e5fafa2b25b5128084c7072aa282642fcfbb14b (patch) | |
tree | 80e21475f93da004a5eae87e3a0d2d6a4f22cce6 /packages/integrations/vercel/src/serverless/middleware.ts | |
parent | cfd5b2b785ad277b82c380fdf68ead0475ddb42f (diff) | |
download | astro-9e5fafa2b25b5128084c7072aa282642fcfbb14b.tar.gz astro-9e5fafa2b25b5128084c7072aa282642fcfbb14b.tar.zst astro-9e5fafa2b25b5128084c7072aa282642fcfbb14b.zip |
feat: vercel edge middleware support (#7532)
Co-authored-by: Bjorn Lu <bjornlu.dev@gmail.com>
Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>
Diffstat (limited to 'packages/integrations/vercel/src/serverless/middleware.ts')
-rw-r--r-- | packages/integrations/vercel/src/serverless/middleware.ts | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/packages/integrations/vercel/src/serverless/middleware.ts b/packages/integrations/vercel/src/serverless/middleware.ts new file mode 100644 index 000000000..2f05756c6 --- /dev/null +++ b/packages/integrations/vercel/src/serverless/middleware.ts @@ -0,0 +1,81 @@ +import { fileURLToPath, pathToFileURL } from 'node:url'; +import { join } from 'node:path'; +import { ASTRO_LOCALS_HEADER } from './adapter.js'; +import { existsSync } from 'fs'; + +/** + * It generates the Vercel Edge Middleware file. + * + * It creates a temporary file, the edge middleware, with some dynamic info. + * + * Then this file gets bundled with esbuild. The bundle phase will inline the Astro middleware code. + * + * @param astroMiddlewareEntryPoint + * @param outPath + * @returns {Promise<URL>} The path to the bundled file + */ +export async function generateEdgeMiddleware( + astroMiddlewareEntryPointPath: URL, + outPath: string, + vercelEdgeMiddlewareHandlerPath: URL +): Promise<URL> { + const entryPointPathURLAsString = JSON.stringify( + fileURLToPath(astroMiddlewareEntryPointPath).replace(/\\/g, '/') + ); + + const code = edgeMiddlewareTemplate(entryPointPathURLAsString, vercelEdgeMiddlewareHandlerPath); + // https://vercel.com/docs/concepts/functions/edge-middleware#create-edge-middleware + const bundledFilePath = join(outPath, 'middleware.mjs'); + const esbuild = await import('esbuild'); + await esbuild.build({ + stdin: { + contents: code, + resolveDir: process.cwd(), + }, + target: 'es2020', + platform: 'browser', + // https://runtime-keys.proposal.wintercg.org/#edge-light + conditions: ['edge-light', 'worker', 'browser'], + external: ['astro/middleware'], + outfile: bundledFilePath, + allowOverwrite: true, + format: 'esm', + bundle: true, + minify: false, + }); + return pathToFileURL(bundledFilePath); +} + +function edgeMiddlewareTemplate(middlewarePath: string, vercelEdgeMiddlewareHandlerPath: URL) { + const filePathEdgeMiddleware = fileURLToPath(vercelEdgeMiddlewareHandlerPath); + let handlerTemplateImport = ''; + let handlerTemplateCall = '{}'; + if (existsSync(filePathEdgeMiddleware) + '.js' || existsSync(filePathEdgeMiddleware) + '.ts') { + const stringified = JSON.stringify(filePathEdgeMiddleware.replace(/\\/g, '/')); + handlerTemplateImport = `import handler from ${stringified}`; + handlerTemplateCall = `handler({ request, context })`; + } else { + } + return ` + ${handlerTemplateImport} +import { onRequest } from ${middlewarePath}; +import { createContext, trySerializeLocals } from 'astro/middleware'; +export default async function middleware(request, context) { + const url = new URL(request.url); + const ctx = createContext({ + request, + params: {} + }); + ctx.locals = ${handlerTemplateCall}; + const next = async () => { + const response = await fetch(url, { + headers: { + ${JSON.stringify(ASTRO_LOCALS_HEADER)}: trySerializeLocals(ctx.locals) + } + }); + return response; + }; + + return onRequest(ctx, next); +}`; +} |