@astrojs/vercel
Version:
Deploy your site to Vercel
113 lines (111 loc) • 4.02 kB
JavaScript
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
};