aboutsummaryrefslogtreecommitdiffhomepage
path: root/frontend/app/entry.server.tsx
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2024-07-27 19:02:58 +0900
committernsfisis <nsfisis@gmail.com>2024-07-27 19:02:58 +0900
commitb70fa3abf9c96871cc3e39090e9a00da4ad87b8b (patch)
tree848da44374c48edec603fb014c59ad0fbb9307e6 /frontend/app/entry.server.tsx
parente487036c5cc9f5b59e9efbaec132a2fd60896317 (diff)
parent7cbf34204233df7c7e9e4b24998e25dbd8d2a423 (diff)
downloadphperkaigi-2025-albatross-b70fa3abf9c96871cc3e39090e9a00da4ad87b8b.tar.gz
phperkaigi-2025-albatross-b70fa3abf9c96871cc3e39090e9a00da4ad87b8b.tar.zst
phperkaigi-2025-albatross-b70fa3abf9c96871cc3e39090e9a00da4ad87b8b.zip
Merge branch 'remix'
Diffstat (limited to 'frontend/app/entry.server.tsx')
-rw-r--r--frontend/app/entry.server.tsx124
1 files changed, 124 insertions, 0 deletions
diff --git a/frontend/app/entry.server.tsx b/frontend/app/entry.server.tsx
new file mode 100644
index 0000000..77b6f15
--- /dev/null
+++ b/frontend/app/entry.server.tsx
@@ -0,0 +1,124 @@
+import { PassThrough } from "node:stream";
+
+import type { EntryContext } from "@remix-run/node";
+import { createReadableStreamFromReadable } from "@remix-run/node";
+import { RemixServer } from "@remix-run/react";
+import { isbot } from "isbot";
+import { renderToPipeableStream } from "react-dom/server";
+
+const ABORT_DELAY = 5_000;
+
+export default function handleRequest(
+ request: Request,
+ responseStatusCode: number,
+ responseHeaders: Headers,
+ remixContext: EntryContext,
+) {
+ return isbot(request.headers.get("user-agent") || "")
+ ? handleBotRequest(
+ request,
+ responseStatusCode,
+ responseHeaders,
+ remixContext,
+ )
+ : handleBrowserRequest(
+ request,
+ responseStatusCode,
+ responseHeaders,
+ remixContext,
+ );
+}
+
+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;
+ 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;
+ if (shellRendered) {
+ console.error(error);
+ }
+ },
+ },
+ );
+
+ setTimeout(abort, ABORT_DELAY);
+ });
+}