astro-ghostcms/.pnpm-store/v3/files/6d/ea6337f82d4085adf18296e3a14...

386 lines
12 KiB
Plaintext

import { fileURLToPath } from "node:url";
import { getInfoOutput } from "../cli/info/index.js";
import { ASTRO_VERSION } from "../core/constants.js";
import { AstroErrorData, isAstroError } from "../core/errors/index.js";
import { req } from "../core/messages.js";
import { sequence } from "../core/middleware/index.js";
import { loadMiddleware } from "../core/middleware/loadMiddleware.js";
import {
createRenderContext,
getParamsAndProps
} from "../core/render/index.js";
import { createRequest } from "../core/request.js";
import { matchAllRoutes } from "../core/routing/index.js";
import { isPage, resolveIdToUrl } from "../core/util.js";
import { normalizeTheLocale } from "../i18n/index.js";
import { createI18nMiddleware, i18nPipelineHook } from "../i18n/middleware.js";
import { getSortedPreloadedMatches } from "../prerender/routing.js";
import { isServerLikeOutput } from "../prerender/utils.js";
import { PAGE_SCRIPT_ID } from "../vite-plugin-scripts/index.js";
import { getStylesForURL } from "./css.js";
import { preload } from "./index.js";
import { getComponentMetadata } from "./metadata.js";
import { handle404Response, writeSSRResult, writeWebResponse } from "./response.js";
import { getScriptsForURL } from "./scripts.js";
const clientLocalsSymbol = Symbol.for("astro.locals");
function isLoggedRequest(url) {
return url !== "/favicon.ico";
}
function getCustom404Route(manifestData) {
const route404 = /^\/404\/?$/;
return manifestData.routes.find((r) => route404.test(r.route));
}
async function matchRoute(pathname, manifestData, pipeline) {
const env = pipeline.getEnvironment();
const { routeCache, logger } = env;
let matches = matchAllRoutes(pathname, manifestData);
const preloadedMatches = await getSortedPreloadedMatches({
pipeline,
matches,
settings: pipeline.getSettings()
});
for await (const { preloadedComponent, route: maybeRoute, filePath } of preloadedMatches) {
try {
await getParamsAndProps({
mod: preloadedComponent,
route: maybeRoute,
routeCache,
pathname,
logger,
ssr: isServerLikeOutput(pipeline.getConfig())
});
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, manifestData, pipeline);
}
if (matches.length) {
const possibleRoutes = matches.flatMap((route) => route.component);
pipeline.logger.warn(
"router",
`${AstroErrorData.NoMatchingStaticPathFound.message(
pathname
)}
${AstroErrorData.NoMatchingStaticPathFound.hint(possibleRoutes)}`
);
}
const custom404 = getCustom404Route(manifestData);
if (custom404) {
const filePath = new URL(`./${custom404.component}`, pipeline.getConfig().root);
const preloadedComponent = await preload({ pipeline, filePath });
return {
route: custom404,
filePath,
resolvedPathname: pathname,
preloadedComponent,
mod: preloadedComponent
};
}
return void 0;
}
async function handleRoute({
matchedRoute,
url,
pathname,
status = getStatus(matchedRoute),
body,
origin,
pipeline,
manifestData,
incomingRequest,
incomingResponse,
manifest
}) {
const timeStart = performance.now();
const env = pipeline.getEnvironment();
const config = pipeline.getConfig();
const moduleLoader = pipeline.getModuleLoader();
const { logger } = env;
if (!matchedRoute && !config.i18n) {
if (isLoggedRequest(pathname)) {
logger.info(null, req({ url: pathname, method: incomingRequest.method, statusCode: 404 }));
}
return handle404Response(origin, incomingRequest, incomingResponse);
}
const buildingToSSR = isServerLikeOutput(config);
let request;
let renderContext;
let mod = void 0;
let options = void 0;
let route;
const middleware = await loadMiddleware(moduleLoader);
if (!matchedRoute) {
if (config.i18n) {
const locales = config.i18n.locales;
const pathNameHasLocale = pathname.split("/").filter(Boolean).some((segment) => {
let found = false;
for (const locale of locales) {
if (typeof locale === "string") {
if (normalizeTheLocale(locale) === normalizeTheLocale(segment)) {
found = true;
break;
}
} else {
if (locale.path === segment) {
found = true;
break;
}
}
}
return found;
});
if (!pathNameHasLocale && pathname !== "/") {
return handle404Response(origin, incomingRequest, incomingResponse);
}
request = createRequest({
url,
headers: buildingToSSR ? incomingRequest.headers : new Headers(),
logger,
ssr: buildingToSSR
});
route = {
component: "",
generate(_data) {
return "";
},
params: [],
pattern: new RegExp(""),
prerender: false,
segments: [],
type: "fallback",
route: "",
fallbackRoutes: []
};
renderContext = await createRenderContext({
request,
pathname,
env,
mod,
route,
locales: manifest.i18n?.locales,
routing: manifest.i18n?.routing,
defaultLocale: manifest.i18n?.defaultLocale
});
} else {
return handle404Response(origin, incomingRequest, incomingResponse);
}
} else {
const filePath = matchedRoute.filePath;
const { preloadedComponent } = matchedRoute;
route = matchedRoute.route;
request = createRequest({
url,
headers: buildingToSSR ? incomingRequest.headers : new Headers(),
method: incomingRequest.method,
body,
logger,
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);
}
options = {
env,
filePath,
preload: preloadedComponent,
pathname,
request,
route
};
if (middleware) {
options.middleware = middleware;
}
mod = options.preload;
const { scripts, links, styles, metadata } = await getScriptsAndStyles({
pipeline,
filePath: options.filePath
});
const i18n = pipeline.getConfig().i18n;
renderContext = await createRenderContext({
request: options.request,
pathname: options.pathname,
scripts,
links,
styles,
componentMetadata: metadata,
route: options.route,
mod,
env,
locales: i18n?.locales,
routing: i18n?.routing,
defaultLocale: i18n?.defaultLocale
});
}
const onRequest = middleware?.onRequest;
if (config.i18n) {
const i18Middleware = createI18nMiddleware(
config.i18n,
config.base,
config.trailingSlash,
config.build.format
);
if (i18Middleware) {
if (onRequest) {
pipeline.setMiddlewareFunction(sequence(i18Middleware, onRequest));
} else {
pipeline.setMiddlewareFunction(i18Middleware);
}
pipeline.onBeforeRenderRoute(i18nPipelineHook);
} else if (onRequest) {
pipeline.setMiddlewareFunction(onRequest);
}
} else if (onRequest) {
pipeline.setMiddlewareFunction(onRequest);
}
let response = await pipeline.renderRoute(renderContext, mod);
if (isLoggedRequest(pathname)) {
const timeEnd = performance.now();
logger.info(
null,
req({
url: pathname,
method: incomingRequest.method,
statusCode: status ?? response.status,
reqTime: timeEnd - timeStart
})
);
}
if (response.status === 404 && has404Route(manifestData)) {
const fourOhFourRoute = await matchRoute("/404", manifestData, pipeline);
if (options && fourOhFourRoute?.route !== options.route)
return handleRoute({
...options,
matchedRoute: fourOhFourRoute,
url: new URL(pathname, url),
status: 404,
body,
origin,
pipeline,
manifestData,
incomingRequest,
incomingResponse,
manifest
});
}
if (route.type === "endpoint") {
await writeWebResponse(incomingResponse, response);
return;
}
if (response.status < 400 && response.status >= 300) {
await writeSSRResult(request, response, incomingResponse);
return;
}
if (status && response.status !== status && (status === 404 || status === 500)) {
response = new Response(response.body, {
status,
headers: response.headers
});
}
await writeSSRResult(request, response, incomingResponse);
}
async function getScriptsAndStyles({ pipeline, filePath }) {
const moduleLoader = pipeline.getModuleLoader();
const settings = pipeline.getSettings();
const mode = pipeline.getEnvironment().mode;
const scripts = await getScriptsForURL(filePath, settings.config.root, moduleLoader);
if (isPage(filePath, settings) && mode === "development") {
scripts.add({
props: { type: "module", src: "/@vite/client" },
children: ""
});
if (settings.config.devToolbar.enabled && await settings.preferences.get("devToolbar.enabled")) {
scripts.add({
props: {
type: "module",
src: await resolveIdToUrl(moduleLoader, "astro/runtime/client/dev-toolbar/entrypoint.js")
},
children: ""
});
const additionalMetadata = {
root: fileURLToPath(settings.config.root),
version: ASTRO_VERSION,
debugInfo: await getInfoOutput({ userConfig: settings.config, print: false })
};
scripts.add({
props: {},
children: `window.__astro_dev_toolbar__ = ${JSON.stringify(additionalMetadata)}`
});
}
}
for (const script of settings.scripts) {
if (script.stage === "head-inline") {
scripts.add({
props: {},
children: script.content
});
} else if (script.stage === "page" && isPage(filePath, settings)) {
scripts.add({
props: { type: "module", src: `/@id/${PAGE_SCRIPT_ID}` },
children: ""
});
}
}
const { urls: styleUrls, styles: importedStyles } = await getStylesForURL(filePath, moduleLoader);
let links = /* @__PURE__ */ new Set();
[...styleUrls].forEach((href) => {
links.add({
props: {
rel: "stylesheet",
href
},
children: ""
});
});
let styles = /* @__PURE__ */ new Set();
importedStyles.forEach(({ id, url, content }) => {
scripts.add({
props: {
type: "module",
src: url
},
children: ""
});
styles.add({
props: {
"data-vite-dev-id": id
},
children: content
});
});
const metadata = await getComponentMetadata(filePath, moduleLoader);
return { scripts, styles, links, metadata };
}
function getStatus(matchedRoute) {
if (!matchedRoute)
return 404;
if (matchedRoute.route.route === "/404")
return 404;
if (matchedRoute.route.route === "/500")
return 500;
}
function has404Route(manifest) {
return manifest.routes.some((route) => route.route === "/404");
}
export {
handleRoute,
matchRoute
};