summaryrefslogtreecommitdiff
path: root/packages/integrations/vercel/src/serverless/middleware.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/integrations/vercel/src/serverless/middleware.ts')
-rw-r--r--packages/integrations/vercel/src/serverless/middleware.ts81
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);
+}`;
+}