Skip to content

support unencoded UTF-8 routes in prerender config with ssr:false #13699

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions contributors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@
- rtzll
- rubeonline
- ruidi-huang
- rururux
- ryanflorence
- ryanhiebert
- saengmotmi
Expand Down
80 changes: 80 additions & 0 deletions integration/vite-prerender-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2370,6 +2370,86 @@ test.describe("Prerendering", () => {
expect(requests).toEqual(["/page.data"]);
});

test("Navigates prerendered multibyte path routes", async ({ page }) => {
fixture = await createFixture({
prerender: true,
files: {
"react-router.config.ts": reactRouterConfig({
ssr: false, // turn off fog of war since we're serving with a static server
prerender: ["/", "/page", "/ページ"],
}),
"vite.config.ts": files["vite.config.ts"],
"app/root.tsx": js`
import * as React from "react";
import { Link, Outlet, Scripts } from "react-router";

export function Layout({ children }) {
return (
<html lang="en">
<head />
<body>
<nav>
<Link to="/page">Page</Link><br/>
<Link to="/ページ">ページ</Link><br/>
</nav>
{children}
<Scripts />
</body>
</html>
);
}

export default function Root({ loaderData }) {
return <Outlet />
}
`,
"app/routes/_index.tsx": js`
export default function Index() {
return <h1 data-index>Index</h1>
}
`,
"app/routes/page.tsx": js`
export function loader() {
return "PAGE DATA"
}
export default function Page({ loaderData }) {
return <h1 data-page>{loaderData}</h1>;
}
`,
"app/routes/ページ.tsx": js`
export function loader() {
return "ページ データ";
}
export default function Page({ loaderData }) {
return <h1 data-multibyte-page>{loaderData}</h1>;
}
`,
},
});
appFixture = await createAppFixture(fixture);

let encodedMultibytePath = encodeURIComponent("ページ");
let requests = captureRequests(page);
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/", true);
await page.waitForSelector("[data-index]");

await app.clickLink("/page");
await page.waitForSelector("[data-page]");
expect(await (await page.$("[data-page]"))?.innerText()).toBe(
"PAGE DATA"
);
expect(requests).toEqual(["/page.data"]);
clearRequests(requests);

await app.clickLink("/ページ");
await page.waitForSelector("[data-multibyte-page]");
expect(await (await page.$("[data-multibyte-page]"))?.innerText()).toBe(
"ページ データ"
);
expect(requests).toEqual([`/${encodedMultibytePath}.data`]);
})

test("Returns a 404 if navigating to a non-prerendered param value", async ({
page,
}) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/react-router/lib/server-runtime/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export const createRequestHandler: CreateRequestHandlerFunction = (
let url = new URL(request.url);

let normalizedBasename = _build.basename || "/";
let normalizedPath = url.pathname;
let normalizedPath = decodeURIComponent(url.pathname);
if (stripBasename(normalizedPath, normalizedBasename) === "/_root.data") {
normalizedPath = normalizedBasename;
} else if (normalizedPath.endsWith(".data")) {
Expand Down