UNPKG

one

Version:

One is a new React Framework that makes Vite serve both native and web.

174 lines (171 loc) 8.2 kB
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"; import { compileManifest, getURLfromRequestURL, runMiddlewares } from "../createHandleRequest"; import { getPathFromLoaderPath } from "../utils/cleanUrl"; import { toAbsolute } from "../utils/toAbsolute"; import { serveStaticAssets } from "vxrn"; import { isRolldown } from "../utils/isRolldown"; async function oneServe(oneOptions, buildInfo, app) { const { resolveAPIRoute, resolveLoaderRoute, resolvePageRoute } = await import("../createHandleRequest"), { isResponse } = await import("../utils/isResponse"), { isStatusRedirect } = await import("../utils/isStatus"), 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", useRolldown = await isRolldown(), requestHandlers = { async handleAPI({ route }) { const fileName = useRolldown ? route.page.slice(1) : route.page.slice(1).replace(/\[/g, "_").replace(/\]/g, "_"); return await import(join(process.cwd(), "dist", "api", fileName + (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.js.map