UNPKG

@modern-js/server-core

Version:

A Progressive React Framework for modern web development.

186 lines (185 loc) 6.36 kB
import { time } from "@modern-js/runtime-utils/time"; import { isArray, isFunction } from "@modern-js/utils"; import { ServerTimings } from "../../constants"; import { getLoaderCtx } from "../../helper"; import { transformResponse } from "../../utils"; import { createBaseHookContext } from "./base"; import { createAfterStreamingRenderContext, createCustomMiddlewaresCtx, getAfterMatchCtx, getAfterRenderCtx } from "./context"; const noop = () => { }; const isHtmlResponse = (response) => { const contentType = response.headers.get("content-type"); return contentType === null || contentType === void 0 ? void 0 : contentType.includes("text/html"); }; class CustomServer { getHookMiddleware(entryName, routes) { return async (c, next) => { const routeInfo = routes.find((route) => route.entryName === entryName); const monitors = c.get("monitors"); const baseHookCtx = createBaseHookContext(c); const afterMatchCtx = getAfterMatchCtx(entryName, baseHookCtx); const getCost = time(); await this.hooks.afterMatch.call(afterMatchCtx); const cost = getCost(); cost && (monitors === null || monitors === void 0 ? void 0 : monitors.timing(ServerTimings.SERVER_HOOK_AFTER_MATCH, cost)); const { url, status } = afterMatchCtx.router; if (url) { return c.redirect(url, status); } const { current } = afterMatchCtx.router; if (current !== entryName) { const rewriteRoute = routes.find((route) => route.entryName === current); if (rewriteRoute) { c.set("matchPathname", rewriteRoute.urlPath); c.set("matchEntryName", current); } } if (c.finalized) { return void 0; } await next(); if (c.finalized && (!c.res.body || !isHtmlResponse(c.res) || [ 404, 500 ].includes(c.res.status))) { return void 0; } if (c.res) { c.status(c.res.status); } if (routeInfo.isStream) { const afterStreamingRenderContext = createAfterStreamingRenderContext(baseHookCtx, routeInfo); c.res = transformResponse(c.res, async (chunk) => { const context = afterStreamingRenderContext(chunk); const result = await this.hooks.afterStreamingRender.call(context); if (typeof result === "string") { return result; } return result.chunk; }); } else { const afterRenderCtx = await getAfterRenderCtx(c, baseHookCtx, routeInfo); const getCost2 = time(); await this.hooks.afterRender.call(afterRenderCtx); const cost2 = getCost2(); cost2 && (monitors === null || monitors === void 0 ? void 0 : monitors.timing(ServerTimings.SERVER_HOOK_AFTER_RENDER, cost2)); if (afterRenderCtx.response.private_overrided) { return void 0; } const newBody = afterRenderCtx.template.get(); c.res = c.body(newBody); } }; } async getServerMiddleware() { const serverMiddleware = await this.serverMiddlewarePromise; if (!serverMiddleware || !isFunction(serverMiddleware) && !isArray(serverMiddleware)) { return; } if (Array.isArray(serverMiddleware)) { const unstableMiddlewares = getServerMidFromUnstableMid(serverMiddleware); return unstableMiddlewares; } return async (c, next) => { var _c_env_node_res, _c_env_node, _c_env; const monitors = c.get("monitors"); const locals = {}; const resArgs = { headers: new Headers() }; const customMiddlewareCtx = createCustomMiddlewaresCtx(c, locals, resArgs); const getCost = time(); await serverMiddleware(customMiddlewareCtx); const cost = getCost(); cost && (monitors === null || monitors === void 0 ? void 0 : monitors.timing(ServerTimings.SERVER_MIDDLEWARE, cost)); c.set("locals", locals); if (isRedirect(resArgs.headers, resArgs.status)) { return c.redirect(resArgs.headers.get("Location") || "", resArgs.status || 302); } if ((_c_env = c.env) === null || _c_env === void 0 ? void 0 : (_c_env_node = _c_env.node) === null || _c_env_node === void 0 ? void 0 : (_c_env_node_res = _c_env_node.res) === null || _c_env_node_res === void 0 ? void 0 : _c_env_node_res.headersSent) { return void 0; } if (!c.finalized) { return next(); } }; } constructor(hooks, pwd) { this.hooks = hooks; const webExtension = []; this.serverMiddlewarePromise = hooks.prepareWebServer.call({ pwd, config: { middleware: webExtension } }); } } function getServerMidFromUnstableMid(serverMiddleware) { return serverMiddleware.map((middleware) => { return async (c, next) => { const context = await createMiddlewareContextFromHono(c); return middleware(context, next); }; }); } function isRedirect(headers, code) { return [ 301, 302, 307, 308 ].includes(code || 0) || headers.get("Location"); } async function createMiddlewareContextFromHono(c) { const loaderContext = getLoaderCtx(c); const rawRequest = c.req.raw; const method = rawRequest.method.toUpperCase(); if (![ "GET", "HEAD" ].includes(method) && !rawRequest.body && c.env.node.req) { const streamModulePath = "../../adapters/node/polyfills/stream.js"; const { createReadableStreamFromReadable } = await import(streamModulePath); const init = { body: createReadableStreamFromReadable(c.env.node.req), headers: rawRequest.headers, signal: rawRequest.signal, method: rawRequest.method }; init.duplex = "half"; c.req.raw = new Request(rawRequest.url, init); } return { get request() { return c.req.raw; }, set request(request) { c.req.raw = request; }, get response() { return c.res; }, set response(newRes) { c.res = newRes; }, get route() { return c.get("route"); }, get(key) { return loaderContext.get(key); }, set(key, value) { return loaderContext.set(key, value); }, status: c.status.bind(c), header: c.header.bind(c), body: c.body.bind(c), html: c.html.bind(c), redirect: c.redirect.bind(c) }; } export { CustomServer, getServerMidFromUnstableMid };