212 lines
6.6 KiB
JavaScript
212 lines
6.6 KiB
JavaScript
![]() |
import mime from "mime";
|
||
|
import { attachToResponse } from "../core/cookies/index.js";
|
||
|
import { call as callEndpoint } from "../core/endpoint/dev/index.js";
|
||
|
import { throwIfRedirectNotAllowed } from "../core/endpoint/index.js";
|
||
|
import { AstroErrorData, isAstroError } from "../core/errors/index.js";
|
||
|
import { warn } from "../core/logger/core.js";
|
||
|
import { loadMiddleware } from "../core/middleware/loadMiddleware.js";
|
||
|
import { preload, renderPage } from "../core/render/dev/index.js";
|
||
|
import { getParamsAndProps } from "../core/render/index.js";
|
||
|
import { createRequest } from "../core/request.js";
|
||
|
import { matchAllRoutes } from "../core/routing/index.js";
|
||
|
import { getSortedPreloadedMatches } from "../prerender/routing.js";
|
||
|
import { isServerLikeOutput } from "../prerender/utils.js";
|
||
|
import { log404 } from "./common.js";
|
||
|
import { handle404Response, writeSSRResult, writeWebResponse } from "./response.js";
|
||
|
const clientLocalsSymbol = Symbol.for("astro.locals");
|
||
|
function getCustom404Route(manifest) {
|
||
|
const route404 = /^\/404\/?$/;
|
||
|
return manifest.routes.find((r) => route404.test(r.route));
|
||
|
}
|
||
|
async function matchRoute(pathname, env, manifest) {
|
||
|
const { logging, settings, routeCache } = env;
|
||
|
const matches = matchAllRoutes(pathname, manifest);
|
||
|
const preloadedMatches = await getSortedPreloadedMatches({ env, matches, settings });
|
||
|
for await (const { preloadedComponent, route: maybeRoute, filePath } of preloadedMatches) {
|
||
|
try {
|
||
|
await getParamsAndProps({
|
||
|
mod: preloadedComponent,
|
||
|
route: maybeRoute,
|
||
|
routeCache,
|
||
|
pathname,
|
||
|
logging,
|
||
|
ssr: isServerLikeOutput(settings.config)
|
||
|
});
|
||
|
return {
|
||
|
route: maybeRoute,
|
||
|
filePath,
|
||
|
resolvedPathname: pathname,
|
||
|
preloadedComponent,
|
||
|
mod: preloadedComponent
|
||
|
};
|
||
|
} catch (e) {
|
||
|
if (isAstroError(e) && e.title === AstroErrorData.NoMatchingStaticPathFound.title) {
|
||
|
continue;
|
||
|
}
|
||
|
throw e;
|
||
|
}
|
||
|
}
|
||
|
const altPathname = pathname.replace(/(index)?\.html$/, "");
|
||
|
if (altPathname !== pathname) {
|
||
|
return await matchRoute(altPathname, env, manifest);
|
||
|
}
|
||
|
if (matches.length) {
|
||
|
const possibleRoutes = matches.flatMap((route) => route.component);
|
||
|
warn(
|
||
|
logging,
|
||
|
"getStaticPaths",
|
||
|
`${AstroErrorData.NoMatchingStaticPathFound.message(
|
||
|
pathname
|
||
|
)}
|
||
|
|
||
|
${AstroErrorData.NoMatchingStaticPathFound.hint(possibleRoutes)}`
|
||
|
);
|
||
|
}
|
||
|
log404(logging, pathname);
|
||
|
const custom404 = getCustom404Route(manifest);
|
||
|
if (custom404) {
|
||
|
const filePath = new URL(`./${custom404.component}`, settings.config.root);
|
||
|
const preloadedComponent = await preload({ env, filePath });
|
||
|
return {
|
||
|
route: custom404,
|
||
|
filePath,
|
||
|
resolvedPathname: pathname,
|
||
|
preloadedComponent,
|
||
|
mod: preloadedComponent
|
||
|
};
|
||
|
}
|
||
|
return void 0;
|
||
|
}
|
||
|
async function handleRoute({
|
||
|
matchedRoute,
|
||
|
url,
|
||
|
pathname,
|
||
|
status = getStatus(matchedRoute),
|
||
|
body,
|
||
|
origin,
|
||
|
env,
|
||
|
manifestData,
|
||
|
incomingRequest,
|
||
|
incomingResponse,
|
||
|
manifest
|
||
|
}) {
|
||
|
const { logging, settings } = env;
|
||
|
if (!matchedRoute) {
|
||
|
return handle404Response(origin, incomingRequest, incomingResponse);
|
||
|
}
|
||
|
if (matchedRoute.route.type === "redirect" && !settings.config.experimental.redirects) {
|
||
|
writeWebResponse(
|
||
|
incomingResponse,
|
||
|
new Response(`To enable redirect set experimental.redirects to \`true\`.`, {
|
||
|
status: 400
|
||
|
})
|
||
|
);
|
||
|
return;
|
||
|
}
|
||
|
const { config } = settings;
|
||
|
const filePath = matchedRoute.filePath;
|
||
|
const { route, preloadedComponent } = matchedRoute;
|
||
|
const buildingToSSR = isServerLikeOutput(config);
|
||
|
const request = createRequest({
|
||
|
url,
|
||
|
headers: buildingToSSR ? incomingRequest.headers : new Headers(),
|
||
|
method: incomingRequest.method,
|
||
|
body,
|
||
|
logging,
|
||
|
ssr: buildingToSSR,
|
||
|
clientAddress: buildingToSSR ? incomingRequest.socket.remoteAddress : void 0,
|
||
|
locals: Reflect.get(incomingRequest, clientLocalsSymbol)
|
||
|
// Allows adapters to pass in locals in dev mode.
|
||
|
});
|
||
|
for (const [name, value] of Object.entries(config.server.headers ?? {})) {
|
||
|
if (value)
|
||
|
incomingResponse.setHeader(name, value);
|
||
|
}
|
||
|
const options = {
|
||
|
env,
|
||
|
filePath,
|
||
|
preload: preloadedComponent,
|
||
|
pathname,
|
||
|
request,
|
||
|
route
|
||
|
};
|
||
|
const middleware = await loadMiddleware(env.loader, env.settings.config.srcDir);
|
||
|
if (middleware) {
|
||
|
options.middleware = middleware;
|
||
|
}
|
||
|
if (route.type === "endpoint") {
|
||
|
const result = await callEndpoint(options);
|
||
|
if (result.type === "response") {
|
||
|
if (result.response.headers.get("X-Astro-Response") === "Not-Found") {
|
||
|
const fourOhFourRoute = await matchRoute("/404", env, manifestData);
|
||
|
return handleRoute({
|
||
|
matchedRoute: fourOhFourRoute,
|
||
|
url: new URL("/404", url),
|
||
|
pathname: "/404",
|
||
|
status: 404,
|
||
|
body,
|
||
|
origin,
|
||
|
env,
|
||
|
manifestData,
|
||
|
incomingRequest,
|
||
|
incomingResponse,
|
||
|
manifest
|
||
|
});
|
||
|
}
|
||
|
throwIfRedirectNotAllowed(result.response, config);
|
||
|
await writeWebResponse(incomingResponse, result.response);
|
||
|
} else {
|
||
|
let contentType = "text/plain";
|
||
|
const filepath = route.pathname || route.segments.map((segment) => segment.map((p) => p.content).join("")).join("/");
|
||
|
const computedMimeType = mime.getType(filepath);
|
||
|
if (computedMimeType) {
|
||
|
contentType = computedMimeType;
|
||
|
}
|
||
|
const response = new Response(Buffer.from(result.body, result.encoding), {
|
||
|
status: 200,
|
||
|
headers: {
|
||
|
"Content-Type": `${contentType};charset=utf-8`
|
||
|
}
|
||
|
});
|
||
|
attachToResponse(response, result.cookies);
|
||
|
await writeWebResponse(incomingResponse, response);
|
||
|
}
|
||
|
} else {
|
||
|
const result = await renderPage(options);
|
||
|
if (result.status === 404) {
|
||
|
const fourOhFourRoute = await matchRoute("/404", env, manifestData);
|
||
|
return handleRoute({
|
||
|
...options,
|
||
|
matchedRoute: fourOhFourRoute,
|
||
|
url: new URL(pathname, url),
|
||
|
status: 404,
|
||
|
body,
|
||
|
origin,
|
||
|
env,
|
||
|
manifestData,
|
||
|
incomingRequest,
|
||
|
incomingResponse,
|
||
|
manifest
|
||
|
});
|
||
|
}
|
||
|
throwIfRedirectNotAllowed(result, config);
|
||
|
let response = result;
|
||
|
if (status && response.status !== status) {
|
||
|
response = new Response(result.body, { ...result, status });
|
||
|
}
|
||
|
return await writeSSRResult(request, response, incomingResponse);
|
||
|
}
|
||
|
}
|
||
|
function getStatus(matchedRoute) {
|
||
|
if (!matchedRoute)
|
||
|
return 404;
|
||
|
if (matchedRoute.route.route === "/404")
|
||
|
return 404;
|
||
|
if (matchedRoute.route.route === "/500")
|
||
|
return 500;
|
||
|
}
|
||
|
export {
|
||
|
handleRoute,
|
||
|
matchRoute
|
||
|
};
|