Created
January 4, 2025 09:04
-
-
Save jacobparis/cae382910e6c4a5018de38adadc3dd32 to your computer and use it in GitHub Desktop.
entry files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { RemixBrowser } from "@remix-run/react"; | |
import { startTransition, StrictMode } from "react"; | |
import { hydrateRoot } from "react-dom/client"; | |
startTransition(() => { | |
hydrateRoot( | |
document, | |
<StrictMode> | |
<RemixBrowser /> | |
</StrictMode> | |
); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This file is optional | |
// You can delete it and get it back using `npx remix reveal` | |
// Currently included just to set the ABORT_DELAY | |
import { PassThrough } from 'node:stream'; | |
import { | |
type AppLoadContext, | |
type EntryContext, | |
createReadableStreamFromReadable, | |
} from '@remix-run/node'; | |
import { RemixServer } from '@remix-run/react'; | |
import * as isbotModule from 'isbot'; | |
import { renderToPipeableStream } from 'react-dom/server'; | |
const ABORT_DELAY = 15_000; | |
export default function handleRequest( | |
request: Request, | |
responseStatusCode: number, | |
responseHeaders: Headers, | |
remixContext: EntryContext, | |
loadContext: AppLoadContext, | |
) { | |
let prohibitOutOfOrderStreaming = | |
isBotRequest(request.headers.get('user-agent')) || remixContext.isSpaMode; | |
return prohibitOutOfOrderStreaming | |
? handleBotRequest(request, responseStatusCode, responseHeaders, remixContext) | |
: handleBrowserRequest(request, responseStatusCode, responseHeaders, remixContext); | |
} | |
// We have some Remix apps in the wild already running with isbot@3 so we need | |
// to maintain backwards compatibility even though we want new apps to use | |
// isbot@4. That way, we can ship this as a minor Semver update to @remix-run/dev. | |
function isBotRequest(userAgent: string | null) { | |
if (!userAgent) { | |
return false; | |
} | |
// isbot >= 3.8.0, >4 | |
if ('isbot' in isbotModule && typeof isbotModule.isbot === 'function') { | |
return isbotModule.isbot(userAgent); | |
} | |
// isbot < 3.8.0 | |
if ('default' in isbotModule && typeof isbotModule.default === 'function') { | |
return isbotModule.default(userAgent); | |
} | |
return false; | |
} | |
function handleBotRequest( | |
request: Request, | |
responseStatusCode: number, | |
responseHeaders: Headers, | |
remixContext: EntryContext, | |
) { | |
return new Promise((resolve, reject) => { | |
let shellRendered = false; | |
const { pipe, abort } = renderToPipeableStream( | |
<RemixServer context={remixContext} url={request.url} abortDelay={ABORT_DELAY} />, | |
{ | |
onAllReady() { | |
shellRendered = true; | |
const body = new PassThrough(); | |
const stream = createReadableStreamFromReadable(body); | |
responseHeaders.set('Content-Type', 'text/html'); | |
resolve( | |
new Response(stream, { | |
headers: responseHeaders, | |
status: responseStatusCode, | |
}), | |
); | |
pipe(body); | |
}, | |
onShellError(error: unknown) { | |
reject(error); | |
}, | |
onError(error: unknown) { | |
responseStatusCode = 500; | |
// Log streaming rendering errors from inside the shell. Don't log | |
// errors encountered during initial shell rendering since they'll | |
// reject and get logged in handleDocumentRequest. | |
if (shellRendered) { | |
console.error(error); | |
} | |
}, | |
}, | |
); | |
setTimeout(abort, ABORT_DELAY); | |
}); | |
} | |
function handleBrowserRequest( | |
request: Request, | |
responseStatusCode: number, | |
responseHeaders: Headers, | |
remixContext: EntryContext, | |
) { | |
return new Promise((resolve, reject) => { | |
let shellRendered = false; | |
const { pipe, abort } = renderToPipeableStream( | |
<RemixServer context={remixContext} url={request.url} abortDelay={ABORT_DELAY} />, | |
{ | |
onShellReady() { | |
shellRendered = true; | |
const body = new PassThrough(); | |
const stream = createReadableStreamFromReadable(body); | |
responseHeaders.set('Content-Type', 'text/html'); | |
resolve( | |
new Response(stream, { | |
headers: responseHeaders, | |
status: responseStatusCode, | |
}), | |
); | |
pipe(body); | |
}, | |
onShellError(error: unknown) { | |
reject(error); | |
}, | |
onError(error: unknown) { | |
responseStatusCode = 500; | |
// Log streaming rendering errors from inside the shell. Don't log | |
// errors encountered during initial shell rendering since they'll | |
// reject and get logged in handleDocumentRequest. | |
if (shellRendered) { | |
console.error(error); | |
} | |
}, | |
}, | |
); | |
setTimeout(abort, ABORT_DELAY); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment