Skip to content

Commit

Permalink
Minor SPA mode updates
Browse files Browse the repository at this point in the history
  • Loading branch information
brophdawg11 committed Jan 9, 2024
1 parent 60b1c7a commit 771ce47
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 4 deletions.
7 changes: 7 additions & 0 deletions .changeset/lazy-oranges-reply.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@remix-run/dev": patch
"@remix-run/react": patch
---

- [REMOVE] Error if no `<Scripts>` included in root `<HydrateFallback>`
- [REMOVE] Don't render SSR scroll restoration script in SPA mode
97 changes: 97 additions & 0 deletions integration/spa-mode-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,103 @@ test.describe("SPA Mode", () => {
"See https://remix.run/future/spa-mode for more information."
);
});

test("errors on a non-200 status from entry.server.tsx", async () => {
let cwd = await createProject({
"vite.config.ts": js`
import { defineConfig } from "vite";
import { unstable_vitePlugin as remix } from "@remix-run/dev";
export default defineConfig({
plugins: [remix({ unstable_ssr: false })],
});
`,
"app/entry.server.tsx": js`
import { RemixServer } from "@remix-run/react";
import { renderToString } from "react-dom/server";
export default function handleRequest(
request,
responseStatusCode,
responseHeaders,
remixContext
) {
const html = renderToString(
<RemixServer context={remixContext} url={request.url} />
);
return new Response(html, {
headers: { "Content-Type": "text/html" },
status: 500,
});
}
`,
"app/root.tsx": js`
import { Links, Meta, Outlet, Scripts } from "@remix-run/react";
export default function Root() {
return (
<html lang="en">
<head>
<Meta />
<Links />
</head>
<body>
<Outlet />
<Scripts />
</body>
</html>
);
}
export function HydrateFallback() {
return (
<html lang="en">
<head>
<Meta />
<Links />
</head>
<body>
<h1>Loading...</h1>
<Scripts />
</body>
</html>
);
}
`,
});
let result = viteBuild({ cwd });
let stderr = result.stderr.toString("utf8");
expect(stderr).toMatch(
"SPA Mode: Received a 500 status code from `entry.server.tsx` while " +
"generating the `index.html` file."
);
expect(stderr).toMatch("<h1>Loading...</h1>");
});

test("errors if you do not include <Scripts> in your root <HydrateFallback>", async () => {
let cwd = await createProject({
"vite.config.ts": js`
import { defineConfig } from "vite";
import { unstable_vitePlugin as remix } from "@remix-run/dev";
export default defineConfig({
plugins: [remix({ unstable_ssr: false })],
});
`,
"app/root.tsx": String.raw`
export function HydrateFallback() {
return <h1>Loading</h1>
}
`,
});
let result = viteBuild({ cwd });
let stderr = result.stderr.toString("utf8");
expect(stderr).toMatch(
"SPA Mode: Did you forget to include <Scripts/> in your `root.tsx` " +
"`HydrateFallback` component? Your `index.html` file cannot hydrate " +
"into a SPA without `<Scripts />`."
);
});
});

test.describe("javascript disabled", () => {
Expand Down
22 changes: 19 additions & 3 deletions packages/remix-dev/vite/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1490,11 +1490,27 @@ async function handleSpaMode(
let { createRequestHandler: createHandler } = await import("@remix-run/node");
let handler = createHandler(build, viteConfig.mode);
let response = await handler(new Request("http://localhost/"));
invariant(response.status === 200, "Error generating the index.html file");
let html = await response.text();
if (response.status !== 200) {
throw new Error(
`SPA Mode: Received a ${response.status} status code from ` +
`\`entry.server.tsx\` while generating the \`index.html\` file.\n${html}`
);
}

if (
!html.includes("window.__remixContext =") ||
!html.includes("window.__remixRouteModules =")
) {
throw new Error(
"SPA Mode: Did you forget to include <Scripts/> in your `root.tsx` " +
"`HydrateFallback` component? Your `index.html` file cannot hydrate " +
"into a SPA without `<Scripts />`."
);
}

// Write out the index.html file for the SPA
let htmlPath = path.join(assetsBuildDirectory, "index.html");
await fse.writeFile(htmlPath, await response.text());
await fse.writeFile(path.join(assetsBuildDirectory, "index.html"), html);

viteConfig.logger.info(
"SPA Mode: index.html has been written to your " +
Expand Down
2 changes: 1 addition & 1 deletion packages/remix-react/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export const RemixContext = React.createContext<RemixContextObject | undefined>(
);
RemixContext.displayName = "Remix";

function useRemixContext(): RemixContextObject {
export function useRemixContext(): RemixContextObject {
let context = React.useContext(RemixContext);
invariant(context, "You must render this element inside a <Remix> element");
return context;
Expand Down
8 changes: 8 additions & 0 deletions packages/remix-react/scroll-restoration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from "react-router-dom";

import type { ScriptProps } from "./components";
import { useRemixContext } from "./components";

let STORAGE_KEY = "positions";

Expand All @@ -22,6 +23,7 @@ export function ScrollRestoration({
}: ScriptProps & {
getKey?: ScrollRestorationPropsRR["getKey"];
}) {
let { isSpaMode } = useRemixContext();
let location = useLocation();
let matches = useMatches();

Expand All @@ -47,6 +49,12 @@ export function ScrollRestoration({
[]
);

// In SPA Mode, there's nothing to restore on initial render since we didn't
// render anything on the server
if (isSpaMode) {
return null;
}

let restoreScroll = ((STORAGE_KEY: string, restoreKey: string) => {
if (!window.history.state || !window.history.state.key) {
let key = Math.random().toString(32).slice(2);
Expand Down
1 change: 1 addition & 0 deletions templates/spa/app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export function HydrateFallback() {
<body>
<p>Loading...</p>
<Scripts />
<LiveReload />
</body>
</html>
);
Expand Down

0 comments on commit 771ce47

Please sign in to comment.