1- /**
2- * By default, Remix will handle generating the HTTP Response for you.
3- * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨
4- * For more information, see https://remix.run/file-conventions/entry.server
5- */
6-
7- import { resolve } from 'node:path'
81import { PassThrough } from 'node:stream'
92
103import { createReadableStreamFromReadable } from '@react-router/node'
11- import { createInstance } from 'i18next'
12- import Backend from 'i18next-fs-backend'
134import { isbot } from 'isbot'
14- import { renderToPipeableStream } from 'react-dom/server'
15- import { I18nextProvider , initReactI18next } from 'react-i18next'
16- import { type AppLoadContext , type EntryContext , ServerRouter } from 'react-router'
17- import i18n from './i18n.ts' // your i18n configuration file
18- import i18nextServer from './i18next.server'
5+ import { type RenderToPipeableStreamOptions , renderToPipeableStream } from 'react-dom/server'
6+ import { I18nextProvider } from 'react-i18next'
7+ import { ServerRouter , type EntryContext , type unstable_RouterContextProvider } from 'react-router'
8+ import { getInstance } from './middleware/i18next'
199
20- const ABORT_DELAY = 5000
10+ export const streamTimeout = 5_000
2111
22- export default async function handleRequest (
12+ export default function handleRequest (
2313 request : Request ,
2414 responseStatusCode : number ,
2515 responseHeaders : Headers ,
26- reactRouterContext : EntryContext ,
27- // This is ignored so we can keep it in the template for visibility. Feel
28- // free to delete this parameter in your app if you're not using it!
29- // eslint-disable-next-line @typescript-eslint/no-unused-vars
30- loadContext : AppLoadContext ,
16+ entryContext : EntryContext ,
17+ routerContext : unstable_RouterContextProvider ,
3118) {
32- return isbot ( request . headers . get ( 'user-agent' ) || '' )
33- ? handleBotRequest ( request , responseStatusCode , responseHeaders , reactRouterContext )
34- : handleBrowserRequest ( request , responseStatusCode , responseHeaders , reactRouterContext )
35- }
36-
37- async function handleBotRequest (
38- request : Request ,
39- responseStatusCode : number ,
40- responseHeaders : Headers ,
41- reactRouterContext : EntryContext ,
42- ) {
43- const instance = createInstance ( )
44- const lng = await i18nextServer . getLocale ( request )
45- const ns = i18nextServer . getRouteNamespaces ( reactRouterContext )
46-
47- await instance
48- . use ( initReactI18next )
49- . use ( Backend )
50- . init ( {
51- ...i18n ,
52- lng,
53- ns, // The namespaces the routes about to render wants to use
54- backend : { loadPath : resolve ( `./public/locales/${ i18n . jsonFileSchema } ` ) } ,
55- } )
56-
5719 return new Promise ( ( resolve , reject ) => {
5820 let shellRendered = false
59- const { pipe, abort } = renderToPipeableStream (
60- < I18nextProvider i18n = { instance } >
61- < ServerRouter context = { reactRouterContext } url = { request . url } />
62- </ I18nextProvider > ,
63- {
64- onAllReady ( ) {
65- shellRendered = true
66- const body = new PassThrough ( )
67- const stream = createReadableStreamFromReadable ( body )
68-
69- responseHeaders . set ( 'Content-Type' , 'text/html' )
70-
71- resolve (
72- new Response ( stream , {
73- headers : responseHeaders ,
74- status : responseStatusCode ,
75- } ) ,
76- )
21+ let userAgent = request . headers . get ( 'user-agent' )
7722
78- pipe ( body )
79- } ,
80- onShellError ( error : unknown ) {
81- reject ( error )
82- } ,
83- onError ( error : unknown ) {
84- responseStatusCode = 500
85- // Log streaming rendering errors from inside the shell. Don't log
86- // errors encountered during initial shell rendering since they'll
87- // reject and get logged in handleDocumentRequest.
88- if ( shellRendered ) {
89- console . error ( error )
90- }
91- } ,
92- } ,
93- )
23+ let readyOption : keyof RenderToPipeableStreamOptions =
24+ ( userAgent && isbot ( userAgent ) ) || entryContext . isSpaMode ? 'onAllReady' : 'onShellReady'
9425
95- setTimeout ( abort , ABORT_DELAY )
96- } )
97- }
98-
99- async function handleBrowserRequest (
100- request : Request ,
101- responseStatusCode : number ,
102- responseHeaders : Headers ,
103- reactRouterContext : EntryContext ,
104- ) {
105- const instance = createInstance ( )
106- const lng = await i18nextServer . getLocale ( request )
107- const ns = i18nextServer . getRouteNamespaces ( reactRouterContext )
108-
109- await instance
110- . use ( initReactI18next ) // Tell our instance to use react-i18next
111- . use ( Backend ) // Setup our backend
112- . init ( {
113- ...i18n , // spread the configuration
114- lng, // The locale we detected above
115- ns, // The namespaces the routes about to render wants to use
116- backend : { loadPath : resolve ( `./public/locales/${ i18n . jsonFileSchema } ` ) } ,
117- } )
118-
119- return new Promise ( ( resolve , reject ) => {
120- let shellRendered = false
121- const { pipe, abort } = renderToPipeableStream (
122- < I18nextProvider i18n = { instance } >
123- < ServerRouter context = { reactRouterContext } url = { request . url } />
26+ let { pipe, abort } = renderToPipeableStream (
27+ < I18nextProvider i18n = { getInstance ( routerContext ) } >
28+ < ServerRouter context = { entryContext } url = { request . url } />
12429 </ I18nextProvider > ,
12530 {
126- onShellReady ( ) {
31+ [ readyOption ] ( ) {
12732 shellRendered = true
128- const body = new PassThrough ( )
129- const stream = createReadableStreamFromReadable ( body )
33+ let body = new PassThrough ( )
34+ let stream = createReadableStreamFromReadable ( body )
13035
13136 responseHeaders . set ( 'Content-Type' , 'text/html' )
13237
@@ -144,16 +49,11 @@ async function handleBrowserRequest(
14449 } ,
14550 onError ( error : unknown ) {
14651 responseStatusCode = 500
147- // Log streaming rendering errors from inside the shell. Don't log
148- // errors encountered during initial shell rendering since they'll
149- // reject and get logged in handleDocumentRequest.
150- if ( shellRendered ) {
151- console . error ( error )
152- }
52+ if ( shellRendered ) console . error ( error )
15353 } ,
15454 } ,
15555 )
15656
157- setTimeout ( abort , ABORT_DELAY )
57+ setTimeout ( abort , streamTimeout + 1000 )
15858 } )
15959}
0 commit comments