in packages/runtime/src/runServerApp.tsx [127:259]
async function doRender(serverContext: ServerContext, renderOptions: RenderOptions): Promise<Response> {
const { req, res } = serverContext;
const {
app,
basename,
serverOnlyBasename,
createRoutes,
documentOnly,
disableFallback,
assetsManifest,
runtimeModules,
renderMode,
runtimeOptions,
serverData,
} = renderOptions;
const finalBasename = addLeadingSlash(serverOnlyBasename || basename);
const location = getLocation(req.url);
const requestContext = getRequestContext(location, serverContext);
const appConfig = getAppConfig(app);
const routes = createRoutes({
requestContext,
renderMode,
});
let appData: AppData;
const appContext: AppContext = {
appExport: app,
routes,
appConfig,
appData,
loaderData: {},
renderMode,
assetsManifest,
basename: finalBasename,
matches: [],
requestContext,
serverData,
};
const runtime = new Runtime(appContext, runtimeOptions);
runtime.setAppRouter<ServerAppRouterProps>(ServerRouter);
// Load static module before getAppData.
if (runtimeModules.statics) {
await Promise.all(runtimeModules.statics.map(m => runtime.loadModule(m)).filter(Boolean));
}
const documentData = await getDocumentData({
loaderConfig: renderOptions.documentDataLoader,
requestContext,
documentOnly,
});
// @TODO: document should have it's own context, not shared with app.
appContext.documentData = documentData;
// Not to execute [getAppData] when CSR.
if (!documentOnly) {
try {
appData = await getAppData(app, requestContext);
} catch (err) {
console.error('Error: get app data error when SSR.', err);
}
}
// HashRouter loads route modules by the CSR.
if (appConfig?.router?.type === 'hash') {
return renderDocument({ matches: [], routes, renderOptions, documentData });
}
const matches = matchRoutes(routes, location, finalBasename);
const routePath = getCurrentRoutePath(matches);
if (documentOnly) {
return renderDocument({ matches, routePath, routes, renderOptions, documentData });
} else if (!matches.length) {
return handleNotFoundResponse();
}
try {
const routeModules = await loadRouteModules(matches.map(({ route: { id, lazy } }) => ({ id, lazy })));
const loaderData = {};
for (const routeId in routeModules) {
const { loader } = routeModules[routeId];
if (loader) {
const { data, pageConfig } = await loader();
loaderData[routeId] = {
data,
pageConfig,
};
}
}
const revalidate = renderMode === 'SSG' && needRevalidate(matches);
runtime.setAppContext({ ...appContext, revalidate, routeModules, loaderData, routePath, matches, appData });
if (runtimeModules.commons) {
await Promise.all(runtimeModules.commons.map(m => runtime.loadModule(m)).filter(Boolean));
}
/**
Plugin may register response handlers, for example:
```
addResponseHandler((req) => {
if (redirect) {
return {
statusCode: 302,
statusText: 'Found',
headers: {
location: '/redirect',
},
};
}
});
```
*/
const responseHandlers = runtime.getResponseHandlers();
for (const responseHandler of responseHandlers) {
if (typeof responseHandler === 'function') {
const response = await responseHandler(req, res);
if (response) {
return response as Response;
}
}
}
return await renderServerEntry({
runtime,
matches,
location,
renderOptions,
});
} catch (err) {
if (disableFallback) {
throw err;
}
console.error('Warning: render server entry error, downgrade to csr.', err);
return renderDocument({ matches, routePath, renderOptions, routes, downgrade: true, documentData });
}
}