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 { PassThrough } from "node:stream" ;
8
+
9
+ import type { AppLoadContext , EntryContext } from "@remix-run/node" ;
10
+ import { createReadableStreamFromReadable } from "@remix-run/node" ;
11
+ import { RemixServer } from "@remix-run/react" ;
12
+ import { isbot } from "isbot" ;
13
+ import { renderToPipeableStream } from "react-dom/server" ;
14
+
15
+ const ABORT_DELAY = 5_000 ;
16
+
17
+ export default function handleRequest (
18
+ request : Request ,
19
+ responseStatusCode : number ,
20
+ responseHeaders : Headers ,
21
+ remixContext : EntryContext ,
22
+ // This is ignored so we can keep it in the template for visibility. Feel
23
+ // free to delete this parameter in your app if you're not using it!
24
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
25
+ loadContext : AppLoadContext
26
+ ) {
27
+ return isbot ( request . headers . get ( "user-agent" ) || "" )
28
+ ? handleBotRequest (
29
+ request ,
30
+ responseStatusCode ,
31
+ responseHeaders ,
32
+ remixContext
33
+ )
34
+ : handleBrowserRequest (
35
+ request ,
36
+ responseStatusCode ,
37
+ responseHeaders ,
38
+ remixContext
39
+ ) ;
40
+ }
41
+
42
+ function handleBotRequest (
43
+ request : Request ,
44
+ responseStatusCode : number ,
45
+ responseHeaders : Headers ,
46
+ remixContext : EntryContext
47
+ ) {
48
+ return new Promise ( ( resolve , reject ) => {
49
+ let shellRendered = false ;
50
+ const { pipe, abort } = renderToPipeableStream (
51
+ < RemixServer
52
+ context = { remixContext }
53
+ url = { request . url }
54
+ abortDelay = { ABORT_DELAY }
55
+ /> ,
56
+ {
57
+ onAllReady ( ) {
58
+ shellRendered = true ;
59
+ const body = new PassThrough ( ) ;
60
+ const stream = createReadableStreamFromReadable ( body ) ;
61
+
62
+ responseHeaders . set ( "Content-Type" , "text/html" ) ;
63
+
64
+ resolve (
65
+ new Response ( stream , {
66
+ headers : responseHeaders ,
67
+ status : responseStatusCode ,
68
+ } )
69
+ ) ;
70
+
71
+ pipe ( body ) ;
72
+ } ,
73
+ onShellError ( error : unknown ) {
74
+ reject ( error ) ;
75
+ } ,
76
+ onError ( error : unknown ) {
77
+ responseStatusCode = 500 ;
78
+ // Log streaming rendering errors from inside the shell. Don't log
79
+ // errors encountered during initial shell rendering since they'll
80
+ // reject and get logged in handleDocumentRequest.
81
+ if ( shellRendered ) {
82
+ console . error ( error ) ;
83
+ }
84
+ } ,
85
+ }
86
+ ) ;
87
+
88
+ setTimeout ( abort , ABORT_DELAY ) ;
89
+ } ) ;
90
+ }
91
+
92
+ function handleBrowserRequest (
93
+ request : Request ,
94
+ responseStatusCode : number ,
95
+ responseHeaders : Headers ,
96
+ remixContext : EntryContext
97
+ ) {
98
+ return new Promise ( ( resolve , reject ) => {
99
+ let shellRendered = false ;
100
+ const { pipe, abort } = renderToPipeableStream (
101
+ < RemixServer
102
+ context = { remixContext }
103
+ url = { request . url }
104
+ abortDelay = { ABORT_DELAY }
105
+ /> ,
106
+ {
107
+ onShellReady ( ) {
108
+ shellRendered = true ;
109
+ const body = new PassThrough ( ) ;
110
+ const stream = createReadableStreamFromReadable ( body ) ;
111
+
112
+ responseHeaders . set ( "Content-Type" , "text/html" ) ;
113
+
114
+ resolve (
115
+ new Response ( stream , {
116
+ headers : responseHeaders ,
117
+ status : responseStatusCode ,
118
+ } )
119
+ ) ;
120
+
121
+ pipe ( body ) ;
122
+ } ,
123
+ onShellError ( error : unknown ) {
124
+ reject ( error ) ;
125
+ } ,
126
+ onError ( error : unknown ) {
127
+ responseStatusCode = 500 ;
128
+ // Log streaming rendering errors from inside the shell. Don't log
129
+ // errors encountered during initial shell rendering since they'll
130
+ // reject and get logged in handleDocumentRequest.
131
+ if ( shellRendered ) {
132
+ console . error ( error ) ;
133
+ }
134
+ } ,
135
+ }
136
+ ) ;
137
+
138
+ setTimeout ( abort , ABORT_DELAY ) ;
139
+ } ) ;
140
+ }
0 commit comments