mirror of
https://github.com/Paris-est-Ludique/intranet.git
synced 2025-06-13 02:54:20 +02:00
87 lines
3.1 KiB
TypeScript
87 lines
3.1 KiB
TypeScript
import path from "path"
|
|
import { renderToString } from "react-dom/server"
|
|
import { StaticRouter } from "react-router-dom"
|
|
import { renderRoutes, matchRoutes } from "react-router-config"
|
|
import { Provider } from "react-redux"
|
|
import { ChunkExtractor } from "@loadable/server"
|
|
import { Helmet } from "react-helmet"
|
|
import chalk from "chalk"
|
|
import { Request, Response, NextFunction } from "express"
|
|
import { Action } from "@reduxjs/toolkit"
|
|
|
|
import createStore from "../store"
|
|
import renderHtml from "./renderHtml"
|
|
import routes from "../routes"
|
|
import { getCookieJWT } from "../services/auth"
|
|
|
|
export default async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
|
const { jwt, id } = getCookieJWT(req.headers.cookie)
|
|
const { store } = createStore({ url: req.url, jwt, id })
|
|
|
|
// The method for loading data from server-side
|
|
const loadBranchData = (): Promise<any> => {
|
|
const branch = matchRoutes(routes, req.path)
|
|
const promises = branch.map(({ route, match }) => {
|
|
if (route.loadData) {
|
|
return Promise.all(
|
|
route
|
|
.loadData({
|
|
params: match.params,
|
|
getState: store.getState,
|
|
req,
|
|
res,
|
|
})
|
|
.map((item: Action) => store.dispatch(item))
|
|
)
|
|
}
|
|
|
|
return Promise.resolve(null)
|
|
})
|
|
|
|
return Promise.all(promises)
|
|
}
|
|
|
|
try {
|
|
// Load data from server-side first
|
|
await loadBranchData()
|
|
|
|
const statsFile = path.resolve(process.cwd(), "public/loadable-stats")
|
|
const extractor = new ChunkExtractor({ statsFile })
|
|
|
|
const staticContext: Record<string, any> = {}
|
|
const App = extractor.collectChunks(
|
|
<Provider store={store}>
|
|
{/* Setup React-Router server-side rendering */}
|
|
<StaticRouter location={req.path} context={staticContext}>
|
|
{renderRoutes(routes)}
|
|
</StaticRouter>
|
|
</Provider>
|
|
)
|
|
|
|
const initialState = store.getState()
|
|
const htmlContent = renderToString(App)
|
|
// head must be placed after "renderToString"
|
|
// see: https://github.com/nfl/react-helmet#server-usage
|
|
const head = Helmet.renderStatic()
|
|
|
|
// Check if the render result contains a redirect, if so we need to set
|
|
// the specific status and redirect header and end the response
|
|
if (staticContext.url) {
|
|
res.status(301).setHeader("Location", staticContext.url)
|
|
res.end()
|
|
|
|
return
|
|
}
|
|
|
|
// Pass the route and initial state into html template, the "statusCode" comes from <NotFound />
|
|
res.status(staticContext.statusCode === "404" ? 404 : 200).send(
|
|
renderHtml(head, extractor, htmlContent, initialState)
|
|
)
|
|
} catch (error) {
|
|
res.status(404).send("Not Found :(")
|
|
console.error(chalk.red(`==> 😭 Rendering routes error: ${error}`))
|
|
}
|
|
|
|
next()
|
|
}
|