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
|