mirror of
https://github.com/stackblitz/bolt.new
synced 2025-02-06 04:48:04 +00:00
fix(browser-extensions): don't render directly in body
This commit is contained in:
parent
f6d79c5610
commit
6a9cb78fd6
@ -3,5 +3,5 @@ import { startTransition } from 'react';
|
|||||||
import { hydrateRoot } from 'react-dom/client';
|
import { hydrateRoot } from 'react-dom/client';
|
||||||
|
|
||||||
startTransition(() => {
|
startTransition(() => {
|
||||||
hydrateRoot(document, <RemixBrowser />);
|
hydrateRoot(document.getElementById('root')!, <RemixBrowser />);
|
||||||
});
|
});
|
||||||
|
@ -2,6 +2,9 @@ import type { AppLoadContext, EntryContext } from '@remix-run/cloudflare';
|
|||||||
import { RemixServer } from '@remix-run/react';
|
import { RemixServer } from '@remix-run/react';
|
||||||
import { isbot } from 'isbot';
|
import { isbot } from 'isbot';
|
||||||
import { renderToReadableStream } from 'react-dom/server';
|
import { renderToReadableStream } from 'react-dom/server';
|
||||||
|
import { renderHeadToString } from 'remix-island';
|
||||||
|
import { Head } from './root';
|
||||||
|
import { themeStore } from '~/lib/stores/theme';
|
||||||
|
|
||||||
export default async function handleRequest(
|
export default async function handleRequest(
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -10,7 +13,7 @@ export default async function handleRequest(
|
|||||||
remixContext: EntryContext,
|
remixContext: EntryContext,
|
||||||
_loadContext: AppLoadContext,
|
_loadContext: AppLoadContext,
|
||||||
) {
|
) {
|
||||||
const body = await renderToReadableStream(<RemixServer context={remixContext} url={request.url} />, {
|
const readable = await renderToReadableStream(<RemixServer context={remixContext} url={request.url} />, {
|
||||||
signal: request.signal,
|
signal: request.signal,
|
||||||
onError(error: unknown) {
|
onError(error: unknown) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@ -18,8 +21,49 @@ export default async function handleRequest(
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const body = new ReadableStream({
|
||||||
|
start(controller) {
|
||||||
|
const head = renderHeadToString({ request, remixContext, Head });
|
||||||
|
|
||||||
|
controller.enqueue(
|
||||||
|
new Uint8Array(
|
||||||
|
new TextEncoder().encode(
|
||||||
|
`<!DOCTYPE html><html lang="en" data-theme="${themeStore.value}"><head>${head}</head><body><div id="root" class="w-full h-full">`,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const reader = readable.getReader();
|
||||||
|
|
||||||
|
function read() {
|
||||||
|
reader
|
||||||
|
.read()
|
||||||
|
.then(({ done, value }) => {
|
||||||
|
if (done) {
|
||||||
|
controller.enqueue(new Uint8Array(new TextEncoder().encode(`</div></body></html>`)));
|
||||||
|
controller.close();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.enqueue(value);
|
||||||
|
read();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
controller.error(error);
|
||||||
|
readable.cancel();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
read();
|
||||||
|
},
|
||||||
|
|
||||||
|
cancel() {
|
||||||
|
readable.cancel();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (isbot(request.headers.get('user-agent') || '')) {
|
if (isbot(request.headers.get('user-agent') || '')) {
|
||||||
await body.allReady;
|
await readable.allReady;
|
||||||
}
|
}
|
||||||
|
|
||||||
responseHeaders.set('Content-Type', 'text/html');
|
responseHeaders.set('Content-Type', 'text/html');
|
||||||
|
27
app/root.tsx
27
app/root.tsx
@ -4,6 +4,8 @@ import { Links, Meta, Outlet, Scripts, ScrollRestoration } from '@remix-run/reac
|
|||||||
import tailwindReset from '@unocss/reset/tailwind-compat.css?url';
|
import tailwindReset from '@unocss/reset/tailwind-compat.css?url';
|
||||||
import { themeStore } from './lib/stores/theme';
|
import { themeStore } from './lib/stores/theme';
|
||||||
import { stripIndents } from './utils/stripIndent';
|
import { stripIndents } from './utils/stripIndent';
|
||||||
|
import { createHead } from 'remix-island';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
import reactToastifyStyles from 'react-toastify/dist/ReactToastify.css?url';
|
import reactToastifyStyles from 'react-toastify/dist/ReactToastify.css?url';
|
||||||
import globalStyles from './styles/index.scss?url';
|
import globalStyles from './styles/index.scss?url';
|
||||||
@ -50,24 +52,29 @@ const inlineThemeCode = stripIndents`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export function Layout({ children }: { children: React.ReactNode }) {
|
export const Head = createHead(() => (
|
||||||
const theme = useStore(themeStore);
|
<>
|
||||||
|
|
||||||
return (
|
|
||||||
<html lang="en" data-theme={theme}>
|
|
||||||
<head>
|
|
||||||
<meta charSet="utf-8" />
|
<meta charSet="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<Meta />
|
<Meta />
|
||||||
<Links />
|
<Links />
|
||||||
<script dangerouslySetInnerHTML={{ __html: inlineThemeCode }} />
|
<script dangerouslySetInnerHTML={{ __html: inlineThemeCode }} />
|
||||||
</head>
|
</>
|
||||||
<body>
|
));
|
||||||
|
|
||||||
|
export function Layout({ children }: { children: React.ReactNode }) {
|
||||||
|
const theme = useStore(themeStore);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.querySelector('html')?.setAttribute('data-theme', theme);
|
||||||
|
}, [theme]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
{children}
|
{children}
|
||||||
<ScrollRestoration />
|
<ScrollRestoration />
|
||||||
<Scripts />
|
<Scripts />
|
||||||
</body>
|
</>
|
||||||
</html>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,6 +73,7 @@
|
|||||||
"remark-gfm": "^4.0.0",
|
"remark-gfm": "^4.0.0",
|
||||||
"remix-utils": "^7.6.0",
|
"remix-utils": "^7.6.0",
|
||||||
"shiki": "^1.9.1",
|
"shiki": "^1.9.1",
|
||||||
|
"remix-island": "^0.2.0",
|
||||||
"unist-util-visit": "^5.0.0"
|
"unist-util-visit": "^5.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -155,6 +155,9 @@ importers:
|
|||||||
remark-gfm:
|
remark-gfm:
|
||||||
specifier: ^4.0.0
|
specifier: ^4.0.0
|
||||||
version: 4.0.0
|
version: 4.0.0
|
||||||
|
remix-island:
|
||||||
|
specifier: ^0.2.0
|
||||||
|
version: 0.2.0(@remix-run/react@2.10.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.2))(@remix-run/server-runtime@2.10.2(typescript@5.5.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
remix-utils:
|
remix-utils:
|
||||||
specifier: ^7.6.0
|
specifier: ^7.6.0
|
||||||
version: 7.6.0(@remix-run/cloudflare@2.10.2(@cloudflare/workers-types@4.20240620.0)(typescript@5.5.2))(@remix-run/node@2.10.2(typescript@5.5.2))(@remix-run/react@2.10.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.2))(@remix-run/router@1.17.1)(react@18.3.1)(zod@3.23.8)
|
version: 7.6.0(@remix-run/cloudflare@2.10.2(@cloudflare/workers-types@4.20240620.0)(typescript@5.5.2))(@remix-run/node@2.10.2(typescript@5.5.2))(@remix-run/react@2.10.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.2))(@remix-run/router@1.17.1)(react@18.3.1)(zod@3.23.8)
|
||||||
@ -4331,6 +4334,14 @@ packages:
|
|||||||
remark-stringify@11.0.0:
|
remark-stringify@11.0.0:
|
||||||
resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
|
resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
|
||||||
|
|
||||||
|
remix-island@0.2.0:
|
||||||
|
resolution: {integrity: sha512-NujWtmulgupxNOMiWKAj8lg56eYsy09aV/2pML8rov8N8LmY1UnSml4XYad+KHLy/pgZ1D9UxAmjI6GBJydTUg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@remix-run/react': '>= 1'
|
||||||
|
'@remix-run/server-runtime': '>= 1'
|
||||||
|
react: '>= 16.8'
|
||||||
|
react-dom: '>= 16.8'
|
||||||
|
|
||||||
remix-utils@7.6.0:
|
remix-utils@7.6.0:
|
||||||
resolution: {integrity: sha512-BPhCUEy+nwrhDDDg2v3+LFSszV6tluMbeSkbffj2o4tqZxt5Kn69Y9sNpGxYLAj8gjqeYDuxjv55of+gYnnykA==}
|
resolution: {integrity: sha512-BPhCUEy+nwrhDDDg2v3+LFSszV6tluMbeSkbffj2o4tqZxt5Kn69Y9sNpGxYLAj8gjqeYDuxjv55of+gYnnykA==}
|
||||||
engines: {node: '>=18.0.0'}
|
engines: {node: '>=18.0.0'}
|
||||||
@ -10133,6 +10144,13 @@ snapshots:
|
|||||||
mdast-util-to-markdown: 2.1.0
|
mdast-util-to-markdown: 2.1.0
|
||||||
unified: 11.0.5
|
unified: 11.0.5
|
||||||
|
|
||||||
|
remix-island@0.2.0(@remix-run/react@2.10.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.2))(@remix-run/server-runtime@2.10.2(typescript@5.5.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||||
|
dependencies:
|
||||||
|
'@remix-run/react': 2.10.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.2)
|
||||||
|
'@remix-run/server-runtime': 2.10.2(typescript@5.5.2)
|
||||||
|
react: 18.3.1
|
||||||
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
|
|
||||||
remix-utils@7.6.0(@remix-run/cloudflare@2.10.2(@cloudflare/workers-types@4.20240620.0)(typescript@5.5.2))(@remix-run/node@2.10.2(typescript@5.5.2))(@remix-run/react@2.10.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.2))(@remix-run/router@1.17.1)(react@18.3.1)(zod@3.23.8):
|
remix-utils@7.6.0(@remix-run/cloudflare@2.10.2(@cloudflare/workers-types@4.20240620.0)(typescript@5.5.2))(@remix-run/node@2.10.2(typescript@5.5.2))(@remix-run/react@2.10.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.2))(@remix-run/router@1.17.1)(react@18.3.1)(zod@3.23.8):
|
||||||
dependencies:
|
dependencies:
|
||||||
type-fest: 4.21.0
|
type-fest: 4.21.0
|
||||||
|
Loading…
Reference in New Issue
Block a user