Skip to content

Commit c06a84e

Browse files
fix: keep ?fresh-partial an implementation detail (#3776)
When encountering redirect during route change with partials enabled, url in address bar (history api) is being left as ?fresh-partial=true, this doesn't seem right --------- Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
1 parent b9a314b commit c06a84e

2 files changed

Lines changed: 100 additions & 3 deletions

File tree

packages/fresh/src/runtime/client/partials.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ if (!history.state) {
8383
history.replaceState(state, document.title);
8484
}
8585

86-
function maybeUpdateHistory(nextUrl: URL) {
86+
function maybePushHistory(nextUrl: URL) {
8787
// Only add history entry when URL is new. Still apply
8888
// the partials because sometimes users click a link to
8989
// "refresh" the current page.
@@ -106,6 +106,12 @@ function maybeUpdateHistory(nextUrl: URL) {
106106
}
107107
}
108108

109+
function maybeReplaceHistory(nextUrl: URL) {
110+
if (nextUrl.href !== globalThis.location.href) {
111+
history.replaceState(history.state, "", nextUrl.href);
112+
}
113+
}
114+
109115
document.addEventListener("click", async (e) => {
110116
let el = e.target;
111117
if (el && (el instanceof HTMLElement || el instanceof SVGElement)) {
@@ -154,7 +160,7 @@ document.addEventListener("click", async (e) => {
154160

155161
const nextUrl = new URL(el.href);
156162
try {
157-
maybeUpdateHistory(nextUrl);
163+
maybePushHistory(nextUrl);
158164

159165
const partialUrl = new URL(
160166
partial ? partial : nextUrl.href,
@@ -361,6 +367,7 @@ async function fetchPartials(
361367
actualUrl = nextUrl;
362368
}
363369
}
370+
actualUrl.searchParams.delete(PARTIAL_SEARCH_PARAM);
364371

365372
try {
366373
await applyPartials(res);
@@ -375,7 +382,7 @@ async function fetchPartials(
375382
}
376383

377384
if (shouldNavigate) {
378-
maybeUpdateHistory(actualUrl);
385+
maybeReplaceHistory(actualUrl);
379386
}
380387
}
381388

packages/fresh/tests/partials_test.tsx

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3498,3 +3498,93 @@ Deno.test({
34983498
});
34993499
},
35003500
});
3501+
3502+
Deno.test({
3503+
name: "partials - redirect does not create back-button trap",
3504+
fn: async () => {
3505+
const app = testApp()
3506+
.get("/target", (ctx) => {
3507+
return ctx.render(
3508+
<Doc>
3509+
<Partial name="foo">
3510+
<h1 class="done">target page</h1>
3511+
</Partial>
3512+
</Doc>,
3513+
);
3514+
})
3515+
.get("/redirect", (ctx) => ctx.redirect("/target"))
3516+
.get("/", (ctx) => {
3517+
return ctx.render(
3518+
<Doc>
3519+
<div f-client-nav>
3520+
<a href="/redirect" class="nav">go</a>
3521+
<Partial name="foo">
3522+
<h1 class="init">home</h1>
3523+
</Partial>
3524+
</div>
3525+
</Doc>,
3526+
);
3527+
});
3528+
3529+
await withBrowserApp(app, async (page, address) => {
3530+
await page.goto(address, { waitUntil: "load" });
3531+
await page.locator(".init").wait();
3532+
3533+
// Click link that redirects
3534+
await page.locator(".nav").click();
3535+
await page.locator(".done").wait();
3536+
3537+
// Should show the redirect target URL, not the intermediate
3538+
await page.waitForFunction(() => {
3539+
return window.location.pathname === "/target";
3540+
});
3541+
3542+
// Going back should return to home, not to /redirect
3543+
await page.evaluate(() => window.history.go(-1));
3544+
await page.locator(".init").wait();
3545+
await page.waitForFunction(() => {
3546+
return window.location.pathname === "/";
3547+
});
3548+
});
3549+
},
3550+
});
3551+
3552+
Deno.test({
3553+
name: "partials - redirect does not leak ?fresh-partial in URL",
3554+
fn: async () => {
3555+
const app = testApp()
3556+
.get("/target", (ctx) => {
3557+
return ctx.render(
3558+
<Doc>
3559+
<Partial name="foo">
3560+
<h1 class="done">target page</h1>
3561+
</Partial>
3562+
</Doc>,
3563+
);
3564+
})
3565+
.get("/redirect", (ctx) => ctx.redirect("/target"))
3566+
.get("/", (ctx) => {
3567+
return ctx.render(
3568+
<Doc>
3569+
<div f-client-nav>
3570+
<a href="/redirect" class="nav">go</a>
3571+
<Partial name="foo">
3572+
<h1 class="init">home</h1>
3573+
</Partial>
3574+
</div>
3575+
</Doc>,
3576+
);
3577+
});
3578+
3579+
await withBrowserApp(app, async (page, address) => {
3580+
await page.goto(address, { waitUntil: "load" });
3581+
await page.locator(".init").wait();
3582+
3583+
await page.locator(".nav").click();
3584+
await page.locator(".done").wait();
3585+
3586+
const url = await page.evaluate(() => window.location.href);
3587+
expect(url).not.toContain("fresh-partial");
3588+
});
3589+
},
3590+
});

0 commit comments

Comments
 (0)