diff options
author | 2024-08-29 07:37:24 +0200 | |
---|---|---|
committer | 2024-08-29 07:46:34 +0200 | |
commit | c1f560dcd90a262a103b8644b1e2eb8da87f2e3a (patch) | |
tree | d8f984474569500beac2adc0c9ea0781bd3f5f47 /packages/integrations/vercel/src/serverless/middleware.ts | |
parent | 93a1db68cd9cf3bb2a4d9f7a8af13cbd881eb701 (diff) | |
parent | 9543bdca342bf68d6db705b11c8201d3374ccbe0 (diff) | |
download | astro-c1f560dcd90a262a103b8644b1e2eb8da87f2e3a.tar.gz astro-c1f560dcd90a262a103b8644b1e2eb8da87f2e3a.tar.zst astro-c1f560dcd90a262a103b8644b1e2eb8da87f2e3a.zip |
Merge branch 'vercel' into main
Diffstat (limited to 'packages/integrations/vercel/src/serverless/middleware.ts')
-rw-r--r-- | packages/integrations/vercel/src/serverless/middleware.ts | 124 |
1 files changed, 124 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..07d0843bf --- /dev/null +++ b/packages/integrations/vercel/src/serverless/middleware.ts @@ -0,0 +1,124 @@ +import { existsSync } from 'node:fs'; +import { builtinModules } from 'node:module'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import type { AstroIntegrationLogger } from 'astro'; +import { + ASTRO_LOCALS_HEADER, + ASTRO_MIDDLEWARE_SECRET_HEADER, + ASTRO_PATH_HEADER, + NODE_PATH, +} from './adapter.js'; + +/** + * 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, + root: URL, + vercelEdgeMiddlewareHandlerPath: URL, + outPath: URL, + middlewareSecret: string, + logger: AstroIntegrationLogger, +): Promise<URL> { + const code = edgeMiddlewareTemplate( + astroMiddlewareEntryPointPath, + vercelEdgeMiddlewareHandlerPath, + middlewareSecret, + logger, + ); + // https://vercel.com/docs/concepts/functions/edge-middleware#create-edge-middleware + const bundledFilePath = fileURLToPath(outPath); + const esbuild = await import('esbuild'); + await esbuild.build({ + stdin: { + contents: code, + resolveDir: fileURLToPath(root), + }, + target: 'es2020', + platform: 'browser', + // https://runtime-keys.proposal.wintercg.org/#edge-light + conditions: ['edge-light', 'worker', 'browser'], + outfile: bundledFilePath, + allowOverwrite: true, + format: 'esm', + bundle: true, + minify: false, + // ensure node built-in modules are namespaced with `node:` + plugins: [ + { + name: 'esbuild-namespace-node-built-in-modules', + setup(build) { + const filter = new RegExp(builtinModules.map((mod) => `(^${mod}$)`).join('|')); + build.onResolve({ filter }, (args) => ({ path: 'node:' + args.path, external: true })); + }, + }, + ], + }); + return pathToFileURL(bundledFilePath); +} + +function edgeMiddlewareTemplate( + astroMiddlewareEntryPointPath: URL, + vercelEdgeMiddlewareHandlerPath: URL, + middlewareSecret: string, + logger: AstroIntegrationLogger, +) { + const middlewarePath = JSON.stringify( + fileURLToPath(astroMiddlewareEntryPointPath).replace(/\\/g, '/'), + ); + const filePathEdgeMiddleware = fileURLToPath(vercelEdgeMiddlewareHandlerPath); + let handlerTemplateImport = ''; + let handlerTemplateCall = '{}'; + if (existsSync(filePathEdgeMiddleware + '.js') || existsSync(filePathEdgeMiddleware + '.ts')) { + logger.warn( + 'Usage of `vercel-edge-middleware.js` is deprecated. You can now use the `waitUntil(promise)` function directly as `ctx.locals.waitUntil(promise)`.', + ); + const stringified = JSON.stringify(filePathEdgeMiddleware.replace(/\\/g, '/')); + handlerTemplateImport = `import handler from ${stringified}`; + handlerTemplateCall = `await handler({ request, context })`; + } else { + } + return ` + ${handlerTemplateImport} +import { onRequest } from ${middlewarePath}; +import { createContext, trySerializeLocals } from 'astro/middleware'; +export default async function middleware(request, context) { + const ctx = createContext({ + request, + params: {} + }); + ctx.locals = { vercel: { edge: context }, ...${handlerTemplateCall} }; + const { origin } = new URL(request.url); + const next = async () => { + const { vercel, ...locals } = ctx.locals; + const response = await fetch(new URL('/${NODE_PATH}', request.url), { + headers: { + ...Object.fromEntries(request.headers.entries()), + '${ASTRO_MIDDLEWARE_SECRET_HEADER}': '${middlewareSecret}', + '${ASTRO_PATH_HEADER}': request.url.replace(origin, ''), + '${ASTRO_LOCALS_HEADER}': trySerializeLocals(locals) + } + }); + return new Response(response.body, { + status: response.status, + statusText: response.statusText, + headers: response.headers, + }); + }; + + const response = await onRequest(ctx, next); + // Append cookies from Astro.cookies + for(const setCookieHeaderValue of ctx.cookies.headers()) { + response.headers.append('set-cookie', setCookieHeaderValue); + } + return response; +}`; +} |