@@ -19,23 +19,42 @@ export default async function handleRequest(
19
19
remixContext : EntryContext ,
20
20
_loadContext : AppLoadContext ,
21
21
) {
22
+ let isStreamClosing = false
23
+
24
+ const abortController = new AbortController ( )
25
+ request . signal . addEventListener ( 'abort' , ( ) => {
26
+ if ( ! isStreamClosing ) {
27
+ // only signal the abort if the stream is not already closing
28
+ abortController . abort ( request . signal . reason )
29
+ }
30
+ } )
31
+
22
32
// The main difference between this and the default Node.js entrypoint is
23
33
// this use of web streams as opposed to Node.js streams.
24
34
const body = await ReactDOMServer . renderToReadableStream ( < RemixServer context = { remixContext } url = { request . url } /> , {
25
- signal : request . signal ,
35
+ signal : abortController . signal ,
26
36
onError ( error : unknown ) {
27
37
// Log streaming rendering errors from inside the shell
28
38
console . error ( error )
29
39
responseStatusCode = 500
30
40
} ,
31
41
} )
32
42
43
+ // identity transform just to be able to listen for the flush event
44
+ const transformedBody = body . pipeThrough (
45
+ new TransformStream ( {
46
+ flush ( ) {
47
+ isStreamClosing = true
48
+ } ,
49
+ } ) ,
50
+ )
51
+
33
52
if ( isbot ( request . headers . get ( 'user-agent' ) || '' ) ) {
34
53
await body . allReady
35
54
}
36
55
37
56
responseHeaders . set ( 'Content-Type' , 'text/html' )
38
- return new Response ( body , {
57
+ return new Response ( transformedBody , {
39
58
headers : responseHeaders ,
40
59
status : responseStatusCode ,
41
60
} )
0 commit comments