This commit is contained in:
Wes Bos 2023-02-21 16:11:11 -05:00
parent a9fd7d5d13
commit 72c7df8a66
3 changed files with 56 additions and 62 deletions

View file

@ -29,6 +29,7 @@
"@types/react": "^18.0.28", "@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11", "@types/react-dom": "^18.0.11",
"country-emoji": "^1.5.6", "country-emoji": "^1.5.6",
"isbot": "^3.6.6",
"joi": "^17.8.1", "joi": "^17.8.1",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",

View file

@ -22,6 +22,7 @@ specifiers:
eslint: ^8.34.0 eslint: ^8.34.0
eslint-config-wesbos: ^3.2.3 eslint-config-wesbos: ^3.2.3
husky: ^8.0.3 husky: ^8.0.3
isbot: ^3.6.6
joi: ^17.8.1 joi: ^17.8.1
lint-staged: ^13.1.2 lint-staged: ^13.1.2
normalize.css: ^8.0.1 normalize.css: ^8.0.1
@ -54,6 +55,7 @@ dependencies:
'@types/react': 18.0.28 '@types/react': 18.0.28
'@types/react-dom': 18.0.11 '@types/react-dom': 18.0.11
country-emoji: 1.5.6 country-emoji: 1.5.6
isbot: 3.6.6
joi: 17.8.1 joi: 17.8.1
normalize.css: 8.0.1 normalize.css: 8.0.1
prop-types: 15.8.1 prop-types: 15.8.1
@ -4895,6 +4897,11 @@ packages:
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
dev: true dev: true
/isbot/3.6.6:
resolution: {integrity: sha512-98aGl1Spbx1led422YFrusDJ4ZutSNOymb2avZ2V4BCCjF3MqAF2k+J2zoaLYahubaFkb+3UyvbVDVlk/Ngrew==}
engines: {node: '>=12'}
dev: false
/isexe/2.0.0: /isexe/2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}

View file

@ -1,84 +1,70 @@
import { PassThrough } from 'stream';
import type { EntryContext } from '@remix-run/node'; import type { EntryContext } from '@remix-run/node';
import { Response } from '@remix-run/node';
import { RemixServer } from '@remix-run/react'; import { RemixServer } from '@remix-run/react';
import { renderToPipeableStream } from 'react-dom/server'; import { renderToReadableStream } from 'react-dom/server';
const ABORT_DELAY = 5000; const ABORT_DELAY = 5000;
type CachedResponse = { type CachedResponse = {
html:string; html: string;
date: Date; date: Date;
} }
const cache = new Map<string, CachedResponse>(); const cache = new Map<string, CachedResponse>();
export default async function handleRequest(
export default function handleRequest(
request: Request, request: Request,
responseStatusCode: number, responseStatusCode: number,
responseHeaders: Headers, responseHeaders: Headers,
remixContext: EntryContext remixContext: EntryContext
) { ) {
console.log('😆');
console.log(request.url); console.log(request.url);
// check if we have a cached response in memory // // check if we have a cached response in memory
const cachedResponse = cache.get(request.url); // const cachedResponse = cache.get(request.url);
if (cachedResponse) { // if (cachedResponse) {
console.log('Serving from cache', request.url); // console.log('Serving from cache', request.url);
// if we have a cached response, check if it's less than 5 seconds old // // if we have a cached response, check if it's less than 5 seconds old
const now = new Date(); // const now = new Date();
const diff = now.getTime() - cachedResponse.date.getTime(); // const diff = now.getTime() - cachedResponse.date.getTime();
if (true || diff < 5000) { // if (true || diff < 5000) {
// if it's less than 5 seconds old, return the cached response // // if it's less than 5 seconds old, return the cached response
responseHeaders.set('Content-Type', 'text/html'); // responseHeaders.set('Content-Type', 'text/html');
return new Response(cachedResponse.html, { // return new Response(cachedResponse.html, {
headers: responseHeaders, // headers: responseHeaders,
status: responseStatusCode, // status: responseStatusCode,
}); // });
} // }
} // }
return new Promise((resolve, reject) => { let didError = false;
let didError = false; const chunks: Uint8Array[] = [];
const chunks: Uint8Array[] = [];
const { pipe, abort } = renderToPipeableStream( const body = await renderToReadableStream(
<RemixServer context={remixContext} url={request.url} />, <RemixServer context={remixContext} url={request.url} />,
{ {
onShellReady: () => { onError: (error: unknown) => {
const body = new PassThrough(); didError = true;
console.error(error);
body
.on('data', (data) => {
chunks.push(data);
})
.on('end', () => {
const html = Buffer.concat(chunks).toString('utf8');
cache.set(request.url, { html: html.replace('Rendered Fresh', `Served from Cache ${new Date().toString()}`), date: new Date() });
})
responseHeaders.set('Content-Type', 'text/html');
resolve(
new Response(body, {
headers: responseHeaders,
status: didError ? 500 : responseStatusCode,
})
);
pipe(body);
},
onShellError: (err: unknown) => {
reject(err);
},
onError: (error: unknown) => {
didError = true;
console.error(error);
}
} }
); }
);
console.log(body);
// body
// .on('data', (data) => {
// console.log('data', data);
// chunks.push(data);
// })
// .on('end', () => {
// const html = Buffer.concat(chunks).toString('utf8');
// cache.set(request.url, { html: html.replace('Rendered Fresh', `Served from Cache ${new Date().toString()}`), date: new Date() });
// })
const headers = new Headers(responseHeaders);
headers.set("Content-Type", "text/html");
new Response(body, {
headers,
status: didError ? 500 : responseStatusCode,
})
setTimeout(abort, ABORT_DELAY);
});
} }