Files

144 lines
6.2 KiB
JavaScript

import { AsyncLocalStorage } from "node:async_hooks";
import { IncomingMessage } from "node:http";
import { isRunnableDevEnvironment } from "vite";
import { ASTRO_VITE_ENVIRONMENT_NAMES, devPrerenderMiddlewareSymbol } from "../core/constants.js";
import { getViteErrorPayload } from "../core/errors/dev/index.js";
import { AstroError, AstroErrorData } from "../core/errors/index.js";
import { createViteLoader } from "../core/module-loader/index.js";
import { matchAllRoutes } from "../core/routing/match.js";
import { SERIALIZED_MANIFEST_ID } from "../manifest/serialized.js";
import { ASTRO_DEV_SERVER_APP_ID } from "../vite-plugin-app/index.js";
import { baseMiddleware } from "./base.js";
import { createController } from "./controller.js";
import { recordServerError } from "./error.js";
import { setRouteError } from "./server-state.js";
import { routeGuardMiddleware } from "./route-guard.js";
import { secFetchMiddleware } from "./sec-fetch.js";
import { trailingSlashMiddleware } from "./trailing-slash.js";
function createVitePluginAstroServer({
settings,
logger
}) {
return {
name: "astro:server",
applyToEnvironment(environment) {
return environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr;
},
async configureServer(viteServer) {
const ssrEnvironment = viteServer.environments[ASTRO_VITE_ENVIRONMENT_NAMES.ssr];
const prerenderEnvironment = viteServer.environments[ASTRO_VITE_ENVIRONMENT_NAMES.prerender];
const runnableSsrEnvironment = isRunnableDevEnvironment(ssrEnvironment) ? ssrEnvironment : void 0;
const runnablePrerenderEnvironment = isRunnableDevEnvironment(prerenderEnvironment) ? prerenderEnvironment : void 0;
async function createHandler(environment) {
const loader = createViteLoader(viteServer, environment);
const { default: createAstroServerApp } = await environment.runner.import(ASTRO_DEV_SERVER_APP_ID);
const controller = createController({ loader });
const { handler } = await createAstroServerApp(controller, settings, loader, logger);
const { manifest } = await environment.runner.import(SERIALIZED_MANIFEST_ID);
return { controller, handler, loader, manifest, environment };
}
const ssrHandler = runnableSsrEnvironment ? await createHandler(runnableSsrEnvironment) : void 0;
const prerenderHandler = runnablePrerenderEnvironment ? await createHandler(runnablePrerenderEnvironment) : void 0;
const localStorage = new AsyncLocalStorage();
function handleUnhandledRejection(rejection) {
const error = AstroError.is(rejection) ? rejection : new AstroError({
...AstroErrorData.UnhandledRejection,
message: AstroErrorData.UnhandledRejection.message(rejection?.stack || rejection)
});
const store = localStorage.getStore();
const handlers = [];
if (ssrHandler) handlers.push(ssrHandler);
if (prerenderHandler) handlers.push(prerenderHandler);
for (const currentHandler of handlers) {
if (store instanceof IncomingMessage) {
setRouteError(currentHandler.controller.state, store.url, error);
}
const { errorWithMetadata } = recordServerError(
currentHandler.loader,
currentHandler.manifest,
logger,
error
);
setTimeout(
async () => currentHandler.loader.webSocketSend(await getViteErrorPayload(errorWithMetadata)),
200
);
}
}
if (ssrHandler || prerenderHandler) {
process.on("unhandledRejection", handleUnhandledRejection);
viteServer.httpServer?.on("close", () => {
process.off("unhandledRejection", handleUnhandledRejection);
});
}
return () => {
const shouldHandlePrerenderInCore = Boolean(
viteServer[devPrerenderMiddlewareSymbol]
);
viteServer.middlewares.stack.unshift({
route: "",
handle: baseMiddleware(settings, logger)
});
viteServer.middlewares.stack.unshift({
route: "",
handle: trailingSlashMiddleware(settings)
});
viteServer.middlewares.stack.unshift({
route: "",
handle: routeGuardMiddleware(settings)
});
viteServer.middlewares.stack.unshift({
route: "",
handle: secFetchMiddleware(logger, settings.config.security?.allowedDomains)
});
if (prerenderHandler && shouldHandlePrerenderInCore) {
viteServer.middlewares.use(
async function astroDevPrerenderHandler(request, response, next) {
if (request.url === void 0 || !request.method) {
response.writeHead(500, "Incomplete request");
response.end();
return;
}
if (request.url.startsWith("/@") || request.url.startsWith("/__")) {
return next();
}
if (request.url.includes("/node_modules/")) {
return next();
}
try {
const pathname = decodeURI(new URL(request.url, "http://localhost").pathname);
const { routes } = await prerenderHandler.environment.runner.import("virtual:astro:routes");
const routesList = { routes: routes.map((r) => r.routeData) };
const matches = matchAllRoutes(pathname, routesList);
if (!matches.some((route) => route.prerender)) {
return next();
}
localStorage.run(request, () => {
prerenderHandler.handler(request, response);
});
} catch (err) {
next(err);
}
}
);
}
if (ssrHandler) {
viteServer.middlewares.use(async function astroDevHandler(request, response) {
if (request.url === void 0 || !request.method) {
response.writeHead(500, "Incomplete request");
response.end();
return;
}
localStorage.run(request, () => {
ssrHandler.handler(request, response);
});
});
}
};
}
};
}
export {
createVitePluginAstroServer as default
};