|
| 1 | +import { fileURLToPath } from "node:url"; |
| 2 | +import type { AstroConfig, AstroIntegration, IntegrationResolvedRoute } from "astro"; |
| 3 | +import { getAstroMiddlewarePath } from "./utils/astro"; |
| 4 | +import { buildMiddlewareMatcherRegexp } from "./utils/matcher"; |
| 5 | +import { generateEdgeMiddlewareFile } from "./utils/middleware"; |
| 6 | +import { |
| 7 | + generateFunctionConfig, |
| 8 | + getFunctionDir, |
| 9 | + insertMiddlewareRoute, |
| 10 | +} from "./utils/vercelOutput"; |
| 11 | + |
| 12 | +const INTEGRATION_NAME = "vercel-middleware"; |
| 13 | +const VERCEL_ADAPTER_LINK = "[@astrojs/vercel](https://www.npmjs.com/package/@astrojs/vercel)"; |
| 14 | +const VERCEL_MIDDLEWARE_FUNCTION_NAME = "_middleware"; |
| 15 | + |
| 16 | +export default function vercelMiddlewareIntegration() { |
| 17 | + let astroConfig: AstroConfig; |
| 18 | + let resolvedRoutes: IntegrationResolvedRoute[]; |
| 19 | + |
| 20 | + const integration: AstroIntegration = { |
| 21 | + name: INTEGRATION_NAME, |
| 22 | + hooks: { |
| 23 | + "astro:routes:resolved": ({ routes, logger }) => { |
| 24 | + logger.info("Resolving routes for Vercel middleware matcher…"); |
| 25 | + resolvedRoutes = routes; |
| 26 | + }, |
| 27 | + "astro:config:done": ({ config }) => { |
| 28 | + astroConfig = config; |
| 29 | + }, |
| 30 | + "astro:build:done": async ({ logger }) => { |
| 31 | + if ( |
| 32 | + !astroConfig.integrations.some((integration) => integration.name === "@astrojs/vercel") |
| 33 | + ) { |
| 34 | + logger.error(`${VERCEL_ADAPTER_LINK} must be installed to use ${INTEGRATION_NAME}.`); |
| 35 | + return; |
| 36 | + } |
| 37 | + |
| 38 | + if (astroConfig.adapter?.name !== "@astrojs/vercel") { |
| 39 | + logger.error( |
| 40 | + `${VERCEL_ADAPTER_LINK} must be used as adapter for proper ${INTEGRATION_NAME} work.`, |
| 41 | + ); |
| 42 | + return; |
| 43 | + } |
| 44 | + |
| 45 | + const rootDir = fileURLToPath(astroConfig.root); |
| 46 | + |
| 47 | + logger.info("Looking for Astro middleware…"); |
| 48 | + const astroMiddlewarePath = await getAstroMiddlewarePath(rootDir); |
| 49 | + |
| 50 | + if (!astroMiddlewarePath) { |
| 51 | + logger.warn("Astro middleware not found. Skipping Vercel middleware build."); |
| 52 | + return; |
| 53 | + } |
| 54 | + |
| 55 | + logger.info(`Found middleware file at: ${astroMiddlewarePath}`); |
| 56 | + logger.info("Building Vercel middleware…"); |
| 57 | + logger.info("Compiling edge middleware file…"); |
| 58 | + const functionDir = getFunctionDir(rootDir, VERCEL_MIDDLEWARE_FUNCTION_NAME); |
| 59 | + const middlewareEntrypoint = await generateEdgeMiddlewareFile( |
| 60 | + rootDir, |
| 61 | + astroMiddlewarePath, |
| 62 | + functionDir, |
| 63 | + ); |
| 64 | + logger.info("Creating edge middleware Vercel config file…"); |
| 65 | + await generateFunctionConfig(functionDir, middlewareEntrypoint); |
| 66 | + |
| 67 | + logger.info("Collecting routes which must be handled by middleware…"); |
| 68 | + const matcher = buildMiddlewareMatcherRegexp(resolvedRoutes); |
| 69 | + |
| 70 | + logger.info("Inserting generated middleware into vercel output config…"); |
| 71 | + await insertMiddlewareRoute(rootDir, matcher, VERCEL_MIDDLEWARE_FUNCTION_NAME); |
| 72 | + |
| 73 | + logger.info("Successfully created middleware function for Vercel deployment."); |
| 74 | + }, |
| 75 | + }, |
| 76 | + }; |
| 77 | + |
| 78 | + return integration; |
| 79 | +} |
0 commit comments