Sam Denty
commited on
Commit
·
50885b0
1
Parent(s):
b17d6a5
fix(browser-extensions): don't render directly in body
Browse files- app/entry.client.tsx +1 -1
- app/entry.server.tsx +46 -2
- app/root.tsx +21 -14
- package.json +1 -0
- pnpm-lock.yaml +18 -0
app/entry.client.tsx
CHANGED
@@ -3,5 +3,5 @@ import { startTransition } from 'react';
|
|
3 |
import { hydrateRoot } from 'react-dom/client';
|
4 |
|
5 |
startTransition(() => {
|
6 |
-
hydrateRoot(document
|
7 |
});
|
|
|
3 |
import { hydrateRoot } from 'react-dom/client';
|
4 |
|
5 |
startTransition(() => {
|
6 |
+
hydrateRoot(document.getElementById('root')!, <RemixBrowser />);
|
7 |
});
|
app/entry.server.tsx
CHANGED
@@ -2,6 +2,9 @@ import type { AppLoadContext, EntryContext } from '@remix-run/cloudflare';
|
|
2 |
import { RemixServer } from '@remix-run/react';
|
3 |
import { isbot } from 'isbot';
|
4 |
import { renderToReadableStream } from 'react-dom/server';
|
|
|
|
|
|
|
5 |
|
6 |
export default async function handleRequest(
|
7 |
request: Request,
|
@@ -10,7 +13,7 @@ export default async function handleRequest(
|
|
10 |
remixContext: EntryContext,
|
11 |
_loadContext: AppLoadContext,
|
12 |
) {
|
13 |
-
const
|
14 |
signal: request.signal,
|
15 |
onError(error: unknown) {
|
16 |
console.error(error);
|
@@ -18,8 +21,49 @@ export default async function handleRequest(
|
|
18 |
},
|
19 |
});
|
20 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
if (isbot(request.headers.get('user-agent') || '')) {
|
22 |
-
await
|
23 |
}
|
24 |
|
25 |
responseHeaders.set('Content-Type', 'text/html');
|
|
|
2 |
import { RemixServer } from '@remix-run/react';
|
3 |
import { isbot } from 'isbot';
|
4 |
import { renderToReadableStream } from 'react-dom/server';
|
5 |
+
import { renderHeadToString } from 'remix-island';
|
6 |
+
import { Head } from './root';
|
7 |
+
import { themeStore } from '~/lib/stores/theme';
|
8 |
|
9 |
export default async function handleRequest(
|
10 |
request: Request,
|
|
|
13 |
remixContext: EntryContext,
|
14 |
_loadContext: AppLoadContext,
|
15 |
) {
|
16 |
+
const readable = await renderToReadableStream(<RemixServer context={remixContext} url={request.url} />, {
|
17 |
signal: request.signal,
|
18 |
onError(error: unknown) {
|
19 |
console.error(error);
|
|
|
21 |
},
|
22 |
});
|
23 |
|
24 |
+
const body = new ReadableStream({
|
25 |
+
start(controller) {
|
26 |
+
const head = renderHeadToString({ request, remixContext, Head });
|
27 |
+
|
28 |
+
controller.enqueue(
|
29 |
+
new Uint8Array(
|
30 |
+
new TextEncoder().encode(
|
31 |
+
`<!DOCTYPE html><html lang="en" data-theme="${themeStore.value}"><head>${head}</head><body><div id="root" class="w-full h-full">`,
|
32 |
+
),
|
33 |
+
),
|
34 |
+
);
|
35 |
+
|
36 |
+
const reader = readable.getReader();
|
37 |
+
|
38 |
+
function read() {
|
39 |
+
reader
|
40 |
+
.read()
|
41 |
+
.then(({ done, value }) => {
|
42 |
+
if (done) {
|
43 |
+
controller.enqueue(new Uint8Array(new TextEncoder().encode(`</div></body></html>`)));
|
44 |
+
controller.close();
|
45 |
+
|
46 |
+
return;
|
47 |
+
}
|
48 |
+
|
49 |
+
controller.enqueue(value);
|
50 |
+
read();
|
51 |
+
})
|
52 |
+
.catch((error) => {
|
53 |
+
controller.error(error);
|
54 |
+
readable.cancel();
|
55 |
+
});
|
56 |
+
}
|
57 |
+
read();
|
58 |
+
},
|
59 |
+
|
60 |
+
cancel() {
|
61 |
+
readable.cancel();
|
62 |
+
},
|
63 |
+
});
|
64 |
+
|
65 |
if (isbot(request.headers.get('user-agent') || '')) {
|
66 |
+
await readable.allReady;
|
67 |
}
|
68 |
|
69 |
responseHeaders.set('Content-Type', 'text/html');
|
app/root.tsx
CHANGED
@@ -4,6 +4,8 @@ import { Links, Meta, Outlet, Scripts, ScrollRestoration } from '@remix-run/reac
|
|
4 |
import tailwindReset from '@unocss/reset/tailwind-compat.css?url';
|
5 |
import { themeStore } from './lib/stores/theme';
|
6 |
import { stripIndents } from './utils/stripIndent';
|
|
|
|
|
7 |
|
8 |
import reactToastifyStyles from 'react-toastify/dist/ReactToastify.css?url';
|
9 |
import globalStyles from './styles/index.scss?url';
|
@@ -50,24 +52,29 @@ const inlineThemeCode = stripIndents`
|
|
50 |
}
|
51 |
`;
|
52 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
53 |
export function Layout({ children }: { children: React.ReactNode }) {
|
54 |
const theme = useStore(themeStore);
|
55 |
|
|
|
|
|
|
|
|
|
56 |
return (
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
<Links />
|
63 |
-
<script dangerouslySetInnerHTML={{ __html: inlineThemeCode }} />
|
64 |
-
</head>
|
65 |
-
<body>
|
66 |
-
{children}
|
67 |
-
<ScrollRestoration />
|
68 |
-
<Scripts />
|
69 |
-
</body>
|
70 |
-
</html>
|
71 |
);
|
72 |
}
|
73 |
|
|
|
4 |
import tailwindReset from '@unocss/reset/tailwind-compat.css?url';
|
5 |
import { themeStore } from './lib/stores/theme';
|
6 |
import { stripIndents } from './utils/stripIndent';
|
7 |
+
import { createHead } from 'remix-island';
|
8 |
+
import { useEffect } from 'react';
|
9 |
|
10 |
import reactToastifyStyles from 'react-toastify/dist/ReactToastify.css?url';
|
11 |
import globalStyles from './styles/index.scss?url';
|
|
|
52 |
}
|
53 |
`;
|
54 |
|
55 |
+
export const Head = createHead(() => (
|
56 |
+
<>
|
57 |
+
<meta charSet="utf-8" />
|
58 |
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
59 |
+
<Meta />
|
60 |
+
<Links />
|
61 |
+
<script dangerouslySetInnerHTML={{ __html: inlineThemeCode }} />
|
62 |
+
</>
|
63 |
+
));
|
64 |
+
|
65 |
export function Layout({ children }: { children: React.ReactNode }) {
|
66 |
const theme = useStore(themeStore);
|
67 |
|
68 |
+
useEffect(() => {
|
69 |
+
document.querySelector('html')?.setAttribute('data-theme', theme);
|
70 |
+
}, [theme]);
|
71 |
+
|
72 |
return (
|
73 |
+
<>
|
74 |
+
{children}
|
75 |
+
<ScrollRestoration />
|
76 |
+
<Scripts />
|
77 |
+
</>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
78 |
);
|
79 |
}
|
80 |
|
package.json
CHANGED
@@ -73,6 +73,7 @@
|
|
73 |
"remark-gfm": "^4.0.0",
|
74 |
"remix-utils": "^7.6.0",
|
75 |
"shiki": "^1.9.1",
|
|
|
76 |
"unist-util-visit": "^5.0.0"
|
77 |
},
|
78 |
"devDependencies": {
|
|
|
73 |
"remark-gfm": "^4.0.0",
|
74 |
"remix-utils": "^7.6.0",
|
75 |
"shiki": "^1.9.1",
|
76 |
+
"remix-island": "^0.2.0",
|
77 |
"unist-util-visit": "^5.0.0"
|
78 |
},
|
79 |
"devDependencies": {
|
pnpm-lock.yaml
CHANGED
@@ -155,6 +155,9 @@ importers:
|
|
155 |
remark-gfm:
|
156 |
specifier: ^4.0.0
|
157 |
version: 4.0.0
|
|
|
|
|
|
|
158 |
remix-utils:
|
159 |
specifier: ^7.6.0
|
160 |
version: 7.6.0(@remix-run/[email protected](@cloudflare/[email protected])([email protected]))(@remix-run/[email protected]([email protected]))(@remix-run/[email protected]([email protected]([email protected]))([email protected])([email protected]))(@remix-run/[email protected])([email protected])([email protected])
|
@@ -4331,6 +4334,14 @@ packages:
|
|
4331 | |
4332 |
resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
|
4333 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4334 | |
4335 |
resolution: {integrity: sha512-BPhCUEy+nwrhDDDg2v3+LFSszV6tluMbeSkbffj2o4tqZxt5Kn69Y9sNpGxYLAj8gjqeYDuxjv55of+gYnnykA==}
|
4336 |
engines: {node: '>=18.0.0'}
|
@@ -10133,6 +10144,13 @@ snapshots:
|
|
10133 |
mdast-util-to-markdown: 2.1.0
|
10134 |
unified: 11.0.5
|
10135 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10136 |
[email protected](@remix-run/[email protected](@cloudflare/[email protected])([email protected]))(@remix-run/[email protected]([email protected]))(@remix-run/[email protected]([email protected]([email protected]))([email protected])([email protected]))(@remix-run/[email protected])([email protected])([email protected]):
|
10137 |
dependencies:
|
10138 |
type-fest: 4.21.0
|
|
|
155 |
remark-gfm:
|
156 |
specifier: ^4.0.0
|
157 |
version: 4.0.0
|
158 |
+
remix-island:
|
159 |
+
specifier: ^0.2.0
|
160 |
+
version: 0.2.0(@remix-run/[email protected]([email protected]([email protected]))([email protected])([email protected]))(@remix-run/[email protected]([email protected]))([email protected]([email protected]))([email protected])
|
161 |
remix-utils:
|
162 |
specifier: ^7.6.0
|
163 |
version: 7.6.0(@remix-run/[email protected](@cloudflare/[email protected])([email protected]))(@remix-run/[email protected]([email protected]))(@remix-run/[email protected]([email protected]([email protected]))([email protected])([email protected]))(@remix-run/[email protected])([email protected])([email protected])
|
|
|
4334 | |
4335 |
resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
|
4336 |
|
4337 | |
4338 |
+
resolution: {integrity: sha512-NujWtmulgupxNOMiWKAj8lg56eYsy09aV/2pML8rov8N8LmY1UnSml4XYad+KHLy/pgZ1D9UxAmjI6GBJydTUg==}
|
4339 |
+
peerDependencies:
|
4340 |
+
'@remix-run/react': '>= 1'
|
4341 |
+
'@remix-run/server-runtime': '>= 1'
|
4342 |
+
react: '>= 16.8'
|
4343 |
+
react-dom: '>= 16.8'
|
4344 |
+
|
4345 | |
4346 |
resolution: {integrity: sha512-BPhCUEy+nwrhDDDg2v3+LFSszV6tluMbeSkbffj2o4tqZxt5Kn69Y9sNpGxYLAj8gjqeYDuxjv55of+gYnnykA==}
|
4347 |
engines: {node: '>=18.0.0'}
|
|
|
10144 |
mdast-util-to-markdown: 2.1.0
|
10145 |
unified: 11.0.5
|
10146 |
|
10147 | |
10148 |
+
dependencies:
|
10149 |
+
'@remix-run/react': 2.10.2([email protected]([email protected]))([email protected])([email protected])
|
10150 |
+
'@remix-run/server-runtime': 2.10.2([email protected])
|
10151 |
+
react: 18.3.1
|
10152 |
+
react-dom: 18.3.1([email protected])
|
10153 |
+
|
10154 |
[email protected](@remix-run/[email protected](@cloudflare/[email protected])([email protected]))(@remix-run/[email protected]([email protected]))(@remix-run/[email protected]([email protected]([email protected]))([email protected])([email protected]))(@remix-run/[email protected])([email protected])([email protected]):
|
10155 |
dependencies:
|
10156 |
type-fest: 4.21.0
|