UNPKG

@modern-js/server-core

Version:

A Progressive React Framework for modern web development.

286 lines (285 loc) • 11.4 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var render_exports = {}; __export(render_exports, { createRender: () => createRender }); module.exports = __toCommonJS(render_exports); var import_universal = require("@modern-js/utils/universal"); var import_trie_router = require("hono/router/trie-router"); var import_constants = require("../../constants"); var import_utils = require("../../utils"); var import_utils2 = require("../../utils"); var import_csrRscRender = require("./csrRscRender"); var import_dataHandler = require("./dataHandler"); var import_renderRscHandler = require("./renderRscHandler"); var import_serverActionHandler = require("./serverActionHandler"); var import_ssrRender = require("./ssrRender"); const DYNAMIC_ROUTE_REG = /\/:./; function getRouter(routes) { const dynamicRoutes = []; const normalRoutes = []; routes.forEach((route) => { if (DYNAMIC_ROUTE_REG.test(route.urlPath)) { dynamicRoutes.push(route); } else { normalRoutes.push(route); } }); const finalRoutes = [ ...normalRoutes.sort(import_utils2.sortRoutes), ...dynamicRoutes.sort(import_utils2.sortRoutes) ]; const router = new import_trie_router.TrieRouter(); for (const route of finalRoutes) { const { urlPath: originUrlPath } = route; const urlPath = originUrlPath.endsWith("/") ? `${originUrlPath}*` : `${originUrlPath}/*`; router.add("*", urlPath, route); } return router; } function matchRoute(router, pathname, entryName) { const matched = router.match("*", pathname); if (entryName && matched[0].length > 1) { const matches = matched[0]; const result = matches.find(([route]) => route.entryName === entryName); return result || []; } else { const result = matched[0][0]; return result || []; } } function getHeadersWithoutCookie(headers) { const _headers = { ...headers, cookie: void 0 }; delete _headers.cookie; return _headers; } async function createRender({ routes, pwd, metaName, staticGenerate, cacheConfig, forceCSR, config, onFallback }) { const router = getRouter(routes); return async (req, { logger, reporter, metrics, monitors, nodeReq, templates, serverManifest, rscClientManifest, rscSSRManifest, rscServerManifest, locals, matchEntryName, matchPathname, loaderContext }) => { const forMatchpathname = matchPathname !== null && matchPathname !== void 0 ? matchPathname : (0, import_utils2.getPathname)(req); const [routeInfo, params] = matchRoute(router, forMatchpathname, matchEntryName); const framework = (0, import_universal.cutNameByHyphen)(metaName || "modern-js"); const fallbackHeader = `x-${framework}-ssr-fallback`; let fallbackReason = null; const fallbackWrapper = async (reason, error) => { fallbackReason = reason; return onFallback === null || onFallback === void 0 ? void 0 : onFallback(reason, { logger, reporter, metrics }, error); }; if (!routeInfo) { return new Response((0, import_utils2.createErrorHtml)(404), { status: 404, headers: { "content-type": "text/html; charset=UTF-8" } }); } const html = templates[(0, import_utils.uniqueKeyByRoute)(routeInfo)]; if (!html) { return new Response((0, import_utils2.createErrorHtml)(404), { status: 404, headers: { "content-type": "text/html; charset=UTF-8" } }); } const renderMode = await getRenderMode(req, fallbackHeader, routeInfo.isSSR, forceCSR, nodeReq, fallbackWrapper); const headerData = (0, import_utils2.parseHeaders)(req); const onError = (e, key) => { monitors === null || monitors === void 0 ? void 0 : monitors.error(`SSR Error - ${key || (e instanceof Error ? e.name : e)}, error = %s, req.url = %s, req.headers = %o`, e instanceof Error ? e.stack || e.message : e, forMatchpathname, getHeadersWithoutCookie(headerData)); }; const onTiming = (name, dur) => { monitors === null || monitors === void 0 ? void 0 : monitors.timing(name, dur, "SSR"); }; const renderOptions = { pwd, html, routeInfo, staticGenerate: staticGenerate || false, config, nodeReq, cacheConfig, reporter, serverRoutes: routes, params, logger, metrics, monitors, locals, rscClientManifest, rscSSRManifest, rscServerManifest, serverManifest, loaderContext: loaderContext || /* @__PURE__ */ new Map(), onError, onTiming }; if (fallbackReason) { renderOptions.html = injectFallbackReasonToHtml({ html: renderOptions.html, reason: fallbackReason, framework }); } let response; switch (renderMode) { case "data": response = await (0, import_dataHandler.dataHandler)(req, renderOptions) || await renderHandler(req, renderOptions, "ssr", fallbackWrapper, framework); break; case "rsc-tree": response = await (0, import_renderRscHandler.renderRscHandler)(req, renderOptions); break; case "rsc-action": response = await (0, import_serverActionHandler.serverActionHandler)(req, renderOptions); break; case "ssr": case "csr": response = await renderHandler(req, renderOptions, renderMode, fallbackWrapper, framework); break; default: throw new Error(`Unknown render mode: ${renderMode}`); } if (fallbackReason) { response.headers.set(fallbackHeader, `1;reason=${fallbackReason}`); } return response; }; } async function renderHandler(request, options, mode, fallbackWrapper, framework) { var _options_config_server; let response = null; const { serverManifest } = options; const ssrByRouteIds = (_options_config_server = options.config.server) === null || _options_config_server === void 0 ? void 0 : _options_config_server.ssrByRouteIds; const runtimeEnv = (0, import_utils2.getRuntimeEnv)(); if (serverManifest.nestedRoutesJson && ssrByRouteIds && (ssrByRouteIds === null || ssrByRouteIds === void 0 ? void 0 : ssrByRouteIds.length) > 0 && runtimeEnv === "node") { const { nestedRoutesJson } = serverManifest; const routes = nestedRoutesJson === null || nestedRoutesJson === void 0 ? void 0 : nestedRoutesJson[options.routeInfo.entryName]; if (routes) { const urlPath = "node:url"; const { pathToFileURL } = await import(urlPath); const { matchRoutes } = await import(pathToFileURL(require.resolve("@modern-js/runtime-utils/remix-router")).href); const url = new URL(request.url); const matchedRoutes = matchRoutes(routes, url.pathname, options.routeInfo.urlPath); if (!matchedRoutes) { response = await csrRender(request, options); } else { var _lastMatch_route; const lastMatch = matchedRoutes[matchedRoutes.length - 1]; if (!(lastMatch === null || lastMatch === void 0 ? void 0 : (_lastMatch_route = lastMatch.route) === null || _lastMatch_route === void 0 ? void 0 : _lastMatch_route.id) || !ssrByRouteIds.includes(lastMatch.route.id)) { response = await csrRender(request, options); } } } } if (mode === "ssr" && !response) { try { response = await (0, import_ssrRender.ssrRender)(request, options); } catch (e) { options.onError(e, import_utils2.ErrorDigest.ERENDER); await fallbackWrapper("error", e); response = await csrRender(request, { ...options, html: injectFallbackReasonToHtml({ html: options.html, reason: "error", framework }) }); } } else { response = await csrRender(request, options); } const { routeInfo } = options; applyExtendHeaders(response, routeInfo); return response; function applyExtendHeaders(r, route) { Object.entries(route.responseHeaders || {}).forEach(([k, v]) => { r.headers.set(k, v); }); } } async function getRenderMode(req, fallbackHeader, isSSR, forceCSR, nodeReq, onFallback) { const query = (0, import_utils2.parseQuery)(req); if (req.headers.get("x-rsc-action")) { return "rsc-action"; } if (req.headers.get("x-rsc-tree")) { return "rsc-tree"; } if (isSSR) { if (query.__loader) { return "data"; } const fallbackHeaderValue = req.headers.get(fallbackHeader) || (nodeReq === null || nodeReq === void 0 ? void 0 : nodeReq.headers[fallbackHeader]); if (forceCSR && (query.csr || fallbackHeaderValue)) { if (query.csr) { await (onFallback === null || onFallback === void 0 ? void 0 : onFallback("query")); } else { var _fallbackHeaderValue_split_; const reason = fallbackHeaderValue === null || fallbackHeaderValue === void 0 ? void 0 : (_fallbackHeaderValue_split_ = fallbackHeaderValue.split(";")[1]) === null || _fallbackHeaderValue_split_ === void 0 ? void 0 : _fallbackHeaderValue_split_.split("=")[1]; await (onFallback === null || onFallback === void 0 ? void 0 : onFallback(reason ? `header,${reason}` : "header")); } return "csr"; } return "ssr"; } else { return "csr"; } } function injectFallbackReasonToHtml({ html, reason, framework }) { const tag = `<script id="__${framework}_ssr_fallback_reason__" type="application/json">${JSON.stringify({ reason })}</script>`; return html.replace(/<\/head>/, `${tag}</head>`); } async function csrRender(request, options) { const { html, rscClientManifest } = options; if (!rscClientManifest || process.env.MODERN_DISABLE_INJECT_RSC_DATA) { return new Response(html, { status: 200, headers: new Headers({ "content-type": "text/html; charset=UTF-8", [import_constants.X_MODERNJS_RENDER]: "client" }) }); } else { return (0, import_csrRscRender.csrRscRender)(request, options); } } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { createRender });