Skip to content

Commit ebc8241

Browse files
authored
Handle prerendering paths missing a leading slash (#12684)
1 parent aeb2e17 commit ebc8241

File tree

3 files changed

+63
-4
lines changed

3 files changed

+63
-4
lines changed

.changeset/lazy-elephants-tell.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@react-router/dev": patch
3+
---
4+
5+
Fix mismatch in prerendering html/data files when path is missing a leading slash

integration/vite-prerender-test.ts

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,59 @@ test.describe("Prerendering", () => {
438438
});
439439
});
440440

441+
test("Adds leading slashes if omitted in config", async () => {
442+
fixture = await createFixture({
443+
prerender: true,
444+
files: {
445+
...files,
446+
"react-router.config.ts": js`
447+
export default {
448+
async prerender() {
449+
await new Promise(r => setTimeout(r, 1));
450+
return ['/', 'about'];
451+
},
452+
}
453+
`,
454+
"vite.config.ts": js`
455+
import { defineConfig } from "vite";
456+
import { reactRouter } from "@react-router/dev/vite";
457+
458+
export default defineConfig({
459+
build: { manifest: true },
460+
plugins: [
461+
reactRouter()
462+
],
463+
});
464+
`,
465+
},
466+
});
467+
appFixture = await createAppFixture(fixture);
468+
469+
let clientDir = path.join(fixture.projectDir, "build", "client");
470+
expect(listAllFiles(clientDir).sort()).toEqual([
471+
"__manifest",
472+
"_root.data",
473+
"about.data",
474+
"about/index.html",
475+
"favicon.ico",
476+
"index.html",
477+
]);
478+
479+
let res = await fixture.requestDocument("/");
480+
let html = await res.text();
481+
expect(html).toMatch("<title>Index Title: Index Loader Data</title>");
482+
expect(html).toMatch("<h1>Root</h1>");
483+
expect(html).toMatch('<h2 data-route="true">Index</h2>');
484+
expect(html).toMatch('<p data-loader-data="true">Index Loader Data</p>');
485+
486+
res = await fixture.requestDocument("/about");
487+
html = await res.text();
488+
expect(html).toMatch("<title>About Title: About Loader Data</title>");
489+
expect(html).toMatch("<h1>Root</h1>");
490+
expect(html).toMatch('<h2 data-route="true">About</h2>');
491+
expect(html).toMatch('<p data-loader-data="true">About Loader Data</p>');
492+
});
493+
441494
test("Hydrates into a navigable app", async ({ page }) => {
442495
fixture = await createFixture({
443496
prerender: true,
@@ -588,7 +641,7 @@ test.describe("Prerendering", () => {
588641
"vite.config.ts": js`
589642
import { defineConfig } from "vite";
590643
import { reactRouter } from "@react-router/dev/vite";
591-
644+
592645
export default defineConfig({
593646
build: { manifest: true },
594647
plugins: [reactRouter()],
@@ -602,7 +655,7 @@ test.describe("Prerendering", () => {
602655
data: "한글 데이터 - UTF-8 문자",
603656
};
604657
}
605-
658+
606659
export default function Comp() {
607660
let data = useLoaderData();
608661
return (
@@ -622,7 +675,7 @@ test.describe("Prerendering", () => {
622675
data: "非プリレンダリングデータ - UTF-8文字",
623676
};
624677
}
625-
678+
626679
export default function Comp() {
627680
let data = useLoaderData();
628681
return (

packages/react-router-dev/vite/plugin.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1902,7 +1902,8 @@ async function handlePrerender(
19021902
"X-React-Router-Prerender": "yes",
19031903
};
19041904
for (let path of routesToPrerender) {
1905-
let matches = matchRoutes(routes, path);
1905+
// Ensure we have a leading slash for matching
1906+
let matches = matchRoutes(routes, `/${path}/`.replace(/^\/\/+/, "/"));
19061907
let hasLoaders = matches?.some((m) => m.route.loader);
19071908
let data: string | undefined;
19081909
if (hasLoaders) {

0 commit comments

Comments
 (0)