one
Version:
One is a new React Framework that makes Vite serve both native and web.
192 lines (189 loc) • 8.27 kB
JavaScript
import { default as FSExtra } from "fs-extra";
import { extname, join } from "node:path";
import { getServerEntry } from "vxrn/serve";
import { LOADER_JS_POSTFIX_UNCACHED, PRELOAD_JS_POSTFIX } from "../constants.mjs";
import { compileManifest, getURLfromRequestURL, runMiddlewares } from "../createHandleRequest.mjs";
import { getPathFromLoaderPath } from "../utils/cleanUrl.mjs";
import { toAbsolute } from "../utils/toAbsolute.mjs";
import { serveStaticAssets } from "vxrn";
async function oneServe(oneOptions, buildInfo, app) {
const {
resolveAPIRoute,
resolveLoaderRoute,
resolvePageRoute
} = await import("../createHandleRequest.mjs"),
{
isResponse
} = await import("../utils/isResponse.mjs"),
{
isStatusRedirect
} = await import("../utils/isStatus.mjs"),
isAPIRequest = /* @__PURE__ */new WeakMap(),
redirects = oneOptions.web?.redirects;
if (redirects) for (const redirect of redirects) app.get(redirect.source, context => {
const destinationUrl = redirect.destination.replace(/:\w+/g, param => {
const paramName = param.substring(1);
return context.req.param(paramName) || "";
});
return context.redirect(destinationUrl, redirect.permanent ? 301 : 302);
});
if (!buildInfo) throw new Error("No build info found, have you run build?");
const {
routeToBuildInfo,
routeMap
} = buildInfo,
serverOptions = {
...oneOptions,
root: "."
},
render = (await import(getServerEntry(serverOptions))).default.render,
apiCJS = oneOptions.build?.api?.outputFormat === "cjs",
requestHandlers = {
async handleAPI({
route
}) {
return await import(join(process.cwd(), "dist", "api", route.page.replace("[", "_").replace("]", "_") + (apiCJS ? ".cjs" : ".js")));
},
async loadMiddleware(route) {
return await import(toAbsolute(route.contextKey));
},
async handleLoader({
request,
route,
url,
loaderProps
}) {
const exports = await import(toAbsolute(join("./", "dist/server", route.file))),
{
loader
} = exports;
if (!loader) return console.warn("No loader found in exports", route.file), null;
const json = await loader(loaderProps);
return `export function loader() { return ${JSON.stringify(json)} }`;
},
async handlePage({
route,
url,
loaderProps
}) {
const buildInfo2 = routeToBuildInfo[route.file];
if (route.type === "ssr") {
if (!buildInfo2) throw console.error("Error in route", route), new Error(`No buildinfo found for ${url}, route: ${route.file}, in keys:
${Object.keys(routeToBuildInfo).join(`
`)}`);
try {
const loaderData = await (await import(toAbsolute(buildInfo2.serverJsPath))).loader?.(loaderProps),
preloads2 = buildInfo2.preloads,
headers = new Headers();
headers.set("content-type", "text/html");
const rendered = await render({
mode: route.type,
loaderData,
loaderProps,
path: loaderProps?.path || "/",
preloads: preloads2,
css: buildInfo2.css
});
return new Response(rendered, {
headers,
status: route.isNotFound ? 404 : 200
});
} catch (err) {
console.error(`[one] Error rendering SSR route ${route.file}
${err?.stack ?? err}
url: ${url}`);
}
} else {
const htmlPath = routeMap[url.pathname] || routeMap[buildInfo2?.cleanPath];
if (htmlPath) {
const html = await FSExtra.readFile(join("dist/client", htmlPath), "utf-8"),
headers = new Headers();
return headers.set("content-type", "text/html"), new Response(html, {
headers,
status: route.isNotFound ? 404 : 200
});
}
}
}
};
function createHonoHandler(route) {
return async (context, next) => {
try {
const request = context.req.raw;
if (route.page.endsWith("/+not-found") || Reflect.ownKeys(route.routeKeys).length > 0) {
const staticAssetResponse = await serveStaticAssets({
context
});
if (staticAssetResponse) return await runMiddlewares(requestHandlers, request, route, async () => staticAssetResponse);
}
if (extname(request.url) === ".js" || extname(request.url) === ".css") return next();
const url = getURLfromRequestURL(request),
response = await (() => {
if (url.pathname.endsWith(LOADER_JS_POSTFIX_UNCACHED)) {
const originalUrl = getPathFromLoaderPath(url.pathname),
finalUrl = new URL(originalUrl, url.origin);
return resolveLoaderRoute(requestHandlers, request, finalUrl, route);
}
switch (route.type) {
case "api":
return resolveAPIRoute(requestHandlers, request, url, route);
case "ssg":
case "spa":
case "ssr":
return resolvePageRoute(requestHandlers, request, url, route);
}
})();
if (response) {
if (isResponse(response)) {
if (isStatusRedirect(response.status)) {
const location = `${response.headers.get("location") || ""}`;
return response.headers.forEach((value, key) => {
context.header(key, value);
}), context.redirect(location, response.status);
}
if (isAPIRequest.get(request)) try {
return !response.headers.has("cache-control") && !response.headers.has("Cache-Control") && response.headers.set("cache-control", "no-store"), response;
} catch (err) {
console.info(`Error updating cache header on api route "${context.req.path}" to no-store, it is ${response.headers.get("cache-control")}, continue`, err);
}
return response;
}
return next();
}
} catch (err) {
console.error(` [one] Error handling request: ${err.stack}`);
}
return next();
};
}
const compiledManifest = compileManifest(buildInfo.manifest);
for (const route of compiledManifest.apiRoutes) app.get(route.urlPath, createHonoHandler(route)), app.put(route.urlPath, createHonoHandler(route)), app.post(route.urlPath, createHonoHandler(route)), app.delete(route.urlPath, createHonoHandler(route)), app.patch(route.urlPath, createHonoHandler(route)), route.urlPath !== route.urlCleanPath && (app.get(route.urlCleanPath, createHonoHandler(route)), app.put(route.urlCleanPath, createHonoHandler(route)), app.post(route.urlCleanPath, createHonoHandler(route)), app.delete(route.urlCleanPath, createHonoHandler(route)), app.patch(route.urlCleanPath, createHonoHandler(route)));
for (const route of compiledManifest.pageRoutes) app.get(route.urlPath, createHonoHandler(route)), route.urlPath !== route.urlCleanPath && app.get(route.urlCleanPath, createHonoHandler(route));
const {
preloads
} = buildInfo;
app.get("*", async (c, next) => {
if (c.req.path.endsWith(PRELOAD_JS_POSTFIX) && !preloads[c.req.path]) return c.header("Content-Type", "text/javascript"), c.status(200), c.body("");
if (c.req.path.endsWith(LOADER_JS_POSTFIX_UNCACHED)) {
const request = c.req.raw,
url = getURLfromRequestURL(request),
originalUrl = getPathFromLoaderPath(c.req.path);
for (const route of compiledManifest.pageRoutes) {
if (route.file === "" || !route.compiledRegex.test(originalUrl)) continue;
const loaderRoute = {
...route,
file: route.loaderServerPath || c.req.path
},
finalUrl = new URL(originalUrl, url.origin);
try {
return await resolveLoaderRoute(requestHandlers, request, finalUrl, loaderRoute);
} catch (err) {
return console.error(`Error running loader: ${err}`), next();
}
}
}
return next();
});
}
export { oneServe };
//# sourceMappingURL=oneServe.mjs.map