UNPKG

@astrojs/vercel

Version:
113 lines (111 loc) 4.02 kB
import { existsSync } from "node:fs"; import { builtinModules } from "node:module"; import { fileURLToPath, pathToFileURL } from "node:url"; import { ASTRO_LOCALS_HEADER, ASTRO_MIDDLEWARE_SECRET_HEADER, ASTRO_PATH_HEADER, NODE_PATH } from "../index.js"; async function generateEdgeMiddleware(astroMiddlewareEntryPointPath, root, vercelEdgeMiddlewareHandlerPath, outPath, middlewareSecret, logger) { const code = edgeMiddlewareTemplate( astroMiddlewareEntryPointPath, vercelEdgeMiddlewareHandlerPath, middlewareSecret, logger ); const bundledFilePath = fileURLToPath(outPath); const esbuild = await import("esbuild"); await esbuild.build({ stdin: { contents: code, resolveDir: fileURLToPath(root) }, // Vercel Edge runtime targets ESNext, because Cloudflare Workers update v8 weekly // https://github.com/vercel/vercel/blob/1006f2ae9d67ea4b3cbb1073e79d14d063d42436/packages/next/scripts/build-edge-function-template.js target: "esnext", platform: "browser", // esbuild automatically adds the browser, import and default conditions // https://esbuild.github.io/api/#conditions // https://runtime-keys.proposal.wintercg.org/#edge-light conditions: ["edge-light", "workerd", "worker"], 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, vercelEdgeMiddlewareHandlerPath, middlewareSecret, logger) { 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: {} }); Object.assign(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; }`; } export { generateEdgeMiddleware };