Skip to content

Commit 700efec

Browse files
fix rewrite
1 parent b6e14a8 commit 700efec

4 files changed

Lines changed: 40 additions & 8 deletions

File tree

packages/react-router/src/Transitioner.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,12 @@ export function Transitioner() {
5252
_includeValidateSearch: true,
5353
})
5454

55+
// Check if the current URL matches the canonical form.
56+
// Compare publicHref (browser-facing URL) for consistency with
57+
// the server-side redirect check in router.beforeLoad.
5558
if (
56-
trimPathRight(router.latestLocation.href) !==
57-
trimPathRight(nextLocation.href)
59+
trimPathRight(router.latestLocation.publicHref) !==
60+
trimPathRight(nextLocation.publicHref)
5861
) {
5962
router.commitLocation({ ...nextLocation, replace: true })
6063
}

packages/router-core/src/router.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,6 +1177,7 @@ export class RouterCore<
11771177
// Before we do any processing, we need to allow rewrites to modify the URL
11781178
// build up the full URL by combining the href from history with the router's origin
11791179
const fullUrl = new URL(href, this.origin)
1180+
11801181
const url = executeRewriteInput(this.rewrite, fullUrl)
11811182

11821183
const parsedSearch = this.options.parseSearch(url.search)
@@ -1187,11 +1188,33 @@ export class RouterCore<
11871188

11881189
const fullPath = url.href.replace(url.origin, '')
11891190

1191+
// Save the internal pathname for route matching (before output rewrite)
1192+
const internalPathname = url.pathname
1193+
1194+
// Compute publicHref by applying the output rewrite.
1195+
//
1196+
// The publicHref represents the URL as it should appear in the browser.
1197+
// This must match what buildLocation computes for the same logical route,
1198+
// otherwise the server-side redirect check will see a mismatch and trigger
1199+
// an infinite redirect loop.
1200+
//
1201+
// We always apply the output rewrite (not conditionally) because the
1202+
// incoming URL may have already been transformed by external middleware
1203+
// before reaching the router. In that case, the input rewrite has nothing
1204+
// to do, but we still need the output rewrite to reconstruct the correct
1205+
// public-facing URL.
1206+
//
1207+
// Clone the URL to avoid mutating the one used for route matching.
1208+
const urlForOutput = new URL(url.href)
1209+
const rewrittenUrl = executeRewriteOutput(this.rewrite, urlForOutput)
1210+
const publicHref =
1211+
rewrittenUrl.pathname + rewrittenUrl.search + rewrittenUrl.hash
1212+
11901213
return {
11911214
href: fullPath,
1192-
publicHref: href,
1215+
publicHref,
11931216
url: url,
1194-
pathname: decodePath(url.pathname),
1217+
pathname: decodePath(internalPathname),
11951218
searchStr,
11961219
search: replaceEqualDeep(previousLocation?.search, parsedSearch) as any,
11971220
hash: url.hash.split('#').reverse()[0] ?? '',

packages/solid-router/src/Transitioner.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,12 @@ export function Transitioner() {
5555
_includeValidateSearch: true,
5656
})
5757

58+
// Check if the current URL matches the canonical form.
59+
// Compare publicHref (browser-facing URL) for consistency with
60+
// the server-side redirect check in router.beforeLoad.
5861
if (
59-
trimPathRight(router.latestLocation.href) !==
60-
trimPathRight(nextLocation.href)
62+
trimPathRight(router.latestLocation.publicHref) !==
63+
trimPathRight(nextLocation.publicHref)
6164
) {
6265
router.commitLocation({ ...nextLocation, replace: true })
6366
}

packages/vue-router/src/Transitioner.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,12 @@ export function useTransitionerSetup() {
123123
_includeValidateSearch: true,
124124
})
125125

126+
// Check if the current URL matches the canonical form.
127+
// Compare publicHref (browser-facing URL) for consistency with
128+
// the server-side redirect check in router.beforeLoad.
126129
if (
127-
trimPathRight(router.latestLocation.href) !==
128-
trimPathRight(nextLocation.href)
130+
trimPathRight(router.latestLocation.publicHref) !==
131+
trimPathRight(nextLocation.publicHref)
129132
) {
130133
router.commitLocation({ ...nextLocation, replace: true })
131134
}

0 commit comments

Comments
 (0)