Skip to content

Commit 5de9a21

Browse files
authored
Ensure ancestor pathless/index routes are loaded via manifest requests (#13203)
1 parent a354db5 commit 5de9a21

File tree

3 files changed

+101
-1
lines changed

3 files changed

+101
-1
lines changed

.changeset/plenty-jeans-lie.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"react-router": patch
3+
---
4+
5+
Load ancestor pathless/index routes in lazy route discovery for upwards non-eager-discoery routing

integration/fog-of-war-test.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,4 +1328,78 @@ test.describe("Fog of War", () => {
13281328
)
13291329
).toEqual(["root", "routes/a", "routes/_index", "routes/a.b"]);
13301330
});
1331+
1332+
test("loads ancestor index routes on navigations", async ({ page }) => {
1333+
let fixture = await createFixture({
1334+
files: {
1335+
...getFiles(),
1336+
"app/root.tsx": js`
1337+
import * as React from "react";
1338+
import { Link, Links, Meta, Outlet, Scripts } from "react-router";
1339+
export default function Root() {
1340+
let [showLink, setShowLink] = React.useState(false);
1341+
return (
1342+
<html lang="en">
1343+
<head>
1344+
<Meta />
1345+
<Links />
1346+
</head>
1347+
<body>
1348+
<Link to="/" discover="none">Home</Link><br/>
1349+
<Link to="/a" discover="none">/a</Link><br/>
1350+
<Link to="/a/b" discover="none">/a/b</Link><br/>
1351+
<Link to="/a/b/c" discover="none">/a/b/c</Link><br/>
1352+
<Outlet />
1353+
<Scripts />
1354+
</body>
1355+
</html>
1356+
);
1357+
}
1358+
`,
1359+
"app/routes/a._index.tsx": js`
1360+
export default function Index() {
1361+
return <h3 id="a-index">A INDEX</h3>;
1362+
}
1363+
`,
1364+
"app/routes/a.b._index.tsx": js`
1365+
export default function Index() {
1366+
return <h3 id="b-index">B INDEX</h3>;
1367+
}
1368+
`,
1369+
},
1370+
});
1371+
let appFixture = await createAppFixture(fixture);
1372+
let app = new PlaywrightFixture(appFixture, page);
1373+
1374+
await app.goto("/", true);
1375+
expect(
1376+
await page.evaluate(() =>
1377+
Object.keys((window as any).__reactRouterManifest.routes)
1378+
)
1379+
).toEqual(["root", "routes/_index"]);
1380+
1381+
await app.clickLink("/a/b/c");
1382+
await page.waitForSelector("#c");
1383+
1384+
// /a/b is not discovered yet even thought it's rendered
1385+
expect(
1386+
await page.evaluate(() =>
1387+
Object.keys((window as any).__reactRouterManifest.routes)
1388+
)
1389+
).toEqual([
1390+
"root",
1391+
"routes/_index",
1392+
"routes/a",
1393+
"routes/a._index",
1394+
"routes/a.b",
1395+
"routes/a.b._index",
1396+
"routes/a.b.c",
1397+
]);
1398+
1399+
await app.clickLink("/a/b");
1400+
await page.waitForSelector("#b-index");
1401+
1402+
await app.clickLink("/a");
1403+
await page.waitForSelector("#a-index");
1404+
});
13311405
});

packages/react-router/lib/server-runtime/server.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,28 @@ async function handleManifestRequest(
318318
let patches: Record<string, EntryRoute> = {};
319319

320320
if (url.searchParams.has("p")) {
321-
for (let path of url.searchParams.getAll("p")) {
321+
let paths = new Set<string>();
322+
323+
// In addition to responding with the patches for the requested paths, we
324+
// need to include patches for each partial path so that we pick up any
325+
// pathless/index routes below ancestor segments. So if we
326+
// get a request for `/parent/child`, we need to look for a match on `/parent`
327+
// so that if a `parent._index` route exists we return it so it's available
328+
// for client side matching if the user routes back up to `/parent`.
329+
// This is the same thing we do on initial load in <Scripts> via
330+
// `getPartialManifest()`
331+
url.searchParams.getAll("p").forEach((path) => {
332+
if (!path.startsWith("/")) {
333+
path = `/${path}`;
334+
}
335+
let segments = path.split("/").slice(1);
336+
segments.forEach((_, i) => {
337+
let partialPath = segments.slice(0, i + 1).join("/");
338+
paths.add(`/${partialPath}`);
339+
});
340+
});
341+
342+
for (let path of paths) {
322343
let matches = matchServerRoutes(routes, path, build.basename);
323344
if (matches) {
324345
for (let match of matches) {

0 commit comments

Comments
 (0)