Layout scaffolding for Web apps.
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  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/docs/en/main/file-conventions/entry.server
  5. */
  6. import { PassThrough } from "node:stream";
  7. import type { EntryContext } from "@remix-run/node";
  8. import { Response } from "@remix-run/node";
  9. import { RemixServer } from "@remix-run/react";
  10. import isbot from "isbot";
  11. import { renderToPipeableStream } from "react-dom/server";
  12. const ABORT_DELAY = 5_000;
  13. export default function handleRequest(
  14. request: Request,
  15. responseStatusCode: number,
  16. responseHeaders: Headers,
  17. remixContext: EntryContext
  18. ) {
  19. return isbot(request.headers.get("user-agent"))
  20. ? handleBotRequest(
  21. request,
  22. responseStatusCode,
  23. responseHeaders,
  24. remixContext
  25. )
  26. : handleBrowserRequest(
  27. request,
  28. responseStatusCode,
  29. responseHeaders,
  30. remixContext
  31. );
  32. }
  33. function handleBotRequest(
  34. request: Request,
  35. responseStatusCode: number,
  36. responseHeaders: Headers,
  37. remixContext: EntryContext
  38. ) {
  39. return new Promise((resolve, reject) => {
  40. const { pipe, abort } = renderToPipeableStream(
  41. <RemixServer
  42. context={remixContext}
  43. url={request.url}
  44. abortDelay={ABORT_DELAY}
  45. />,
  46. {
  47. onAllReady() {
  48. const body = new PassThrough();
  49. responseHeaders.set("Content-Type", "text/html");
  50. resolve(
  51. new Response(body, {
  52. headers: responseHeaders,
  53. status: responseStatusCode,
  54. })
  55. );
  56. pipe(body);
  57. },
  58. onShellError(error: unknown) {
  59. reject(error);
  60. },
  61. onError(error: unknown) {
  62. responseStatusCode = 500;
  63. console.error(error);
  64. },
  65. }
  66. );
  67. setTimeout(abort, ABORT_DELAY);
  68. });
  69. }
  70. function handleBrowserRequest(
  71. request: Request,
  72. responseStatusCode: number,
  73. responseHeaders: Headers,
  74. remixContext: EntryContext
  75. ) {
  76. return new Promise((resolve, reject) => {
  77. const { pipe, abort } = renderToPipeableStream(
  78. <RemixServer
  79. context={remixContext}
  80. url={request.url}
  81. abortDelay={ABORT_DELAY}
  82. />,
  83. {
  84. onShellReady() {
  85. const body = new PassThrough();
  86. responseHeaders.set("Content-Type", "text/html");
  87. resolve(
  88. new Response(body, {
  89. headers: responseHeaders,
  90. status: responseStatusCode,
  91. })
  92. );
  93. pipe(body);
  94. },
  95. onShellError(error: unknown) {
  96. reject(error);
  97. },
  98. onError(error: unknown) {
  99. console.error(error);
  100. responseStatusCode = 500;
  101. },
  102. }
  103. );
  104. setTimeout(abort, ABORT_DELAY);
  105. });
  106. }