UNPKG

astro

Version:

Astro is a modern site builder with web best practices, performance, and DX front-of-mind.

131 lines (130 loc) 3.95 kB
import { renderComponent, renderTemplate } from "../../runtime/server/index.js"; import { isAstroComponentFactory } from "../../runtime/server/render/astro/factory.js"; import { createSlotValueFromString } from "../../runtime/server/render/slot.js"; import { decryptString } from "../encryption.js"; import { getPattern } from "../routing/manifest/pattern.js"; const SERVER_ISLAND_ROUTE = "/_server-islands/[name]"; const SERVER_ISLAND_COMPONENT = "_server-islands.astro"; const SERVER_ISLAND_BASE_PREFIX = "_server-islands"; function getServerIslandRouteData(config) { const segments = [ [{ content: "_server-islands", dynamic: false, spread: false }], [{ content: "name", dynamic: true, spread: false }] ]; const route = { type: "page", component: SERVER_ISLAND_COMPONENT, generate: () => "", params: ["name"], segments, pattern: getPattern(segments, config.base, config.trailingSlash), prerender: false, isIndex: false, fallbackRoutes: [], route: SERVER_ISLAND_ROUTE, origin: "internal" }; return route; } function injectServerIslandRoute(config, routeManifest) { routeManifest.routes.unshift(getServerIslandRouteData(config)); } function badRequest(reason) { return new Response(null, { status: 400, statusText: "Bad request: " + reason }); } async function getRequestData(request) { switch (request.method) { case "GET": { const url = new URL(request.url); const params = url.searchParams; if (!params.has("s") || !params.has("e") || !params.has("p")) { return badRequest("Missing required query parameters."); } const rawSlots = params.get("s"); try { return { componentExport: params.get("e"), encryptedProps: params.get("p"), slots: JSON.parse(rawSlots) }; } catch { return badRequest("Invalid slots format."); } } case "POST": { try { const raw = await request.text(); const data = JSON.parse(raw); return data; } catch { return badRequest("Request format is invalid."); } } default: { return new Response(null, { status: 405 }); } } } function createEndpoint(manifest) { const page = async (result) => { const params = result.params; if (!params.name) { return new Response(null, { status: 400, statusText: "Bad request" }); } const componentId = params.name; const data = await getRequestData(result.request); if (data instanceof Response) { return data; } const imp = manifest.serverIslandMap?.get(componentId); if (!imp) { return new Response(null, { status: 404, statusText: "Not found" }); } const key = await manifest.key; const encryptedProps = data.encryptedProps; const propString = encryptedProps === "" ? "{}" : await decryptString(key, encryptedProps); const props = JSON.parse(propString); const componentModule = await imp(); let Component = componentModule[data.componentExport]; const slots = {}; for (const prop in data.slots) { slots[prop] = createSlotValueFromString(data.slots[prop]); } result.response.headers.set("X-Robots-Tag", "noindex"); if (isAstroComponentFactory(Component)) { const ServerIsland = Component; Component = function(...args) { return ServerIsland.apply(this, args); }; Object.assign(Component, ServerIsland); Component.propagation = "self"; } return renderTemplate`${renderComponent(result, "Component", Component, props, slots)}`; }; page.isAstroComponentFactory = true; const instance = { default: page, partial: true }; return instance; } export { SERVER_ISLAND_BASE_PREFIX, SERVER_ISLAND_COMPONENT, SERVER_ISLAND_ROUTE, createEndpoint, getServerIslandRouteData, injectServerIslandRoute };