Skip to content
Merged
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
42 changes: 39 additions & 3 deletions e2e/react-router/basepath-file-based/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,21 @@
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.

import { Route as rootRouteImport } from './routes/__root'
import { Route as RedirectReloadRouteImport } from './routes/redirectReload'
import { Route as RedirectRouteImport } from './routes/redirect'
import { Route as AboutRouteImport } from './routes/about'
import { Route as IndexRouteImport } from './routes/index'

const RedirectReloadRoute = RedirectReloadRouteImport.update({
id: '/redirectReload',
path: '/redirectReload',
getParentRoute: () => rootRouteImport,
} as any)
const RedirectRoute = RedirectRouteImport.update({
id: '/redirect',
path: '/redirect',
getParentRoute: () => rootRouteImport,
} as any)
const AboutRoute = AboutRouteImport.update({
id: '/about',
path: '/about',
Expand All @@ -26,31 +38,53 @@ const IndexRoute = IndexRouteImport.update({
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/about': typeof AboutRoute
'/redirect': typeof RedirectRoute
'/redirectReload': typeof RedirectReloadRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/about': typeof AboutRoute
'/redirect': typeof RedirectRoute
'/redirectReload': typeof RedirectReloadRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
'/about': typeof AboutRoute
'/redirect': typeof RedirectRoute
'/redirectReload': typeof RedirectReloadRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/about'
fullPaths: '/' | '/about' | '/redirect' | '/redirectReload'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/about'
id: '__root__' | '/' | '/about'
to: '/' | '/about' | '/redirect' | '/redirectReload'
id: '__root__' | '/' | '/about' | '/redirect' | '/redirectReload'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
AboutRoute: typeof AboutRoute
RedirectRoute: typeof RedirectRoute
RedirectReloadRoute: typeof RedirectReloadRoute
}

declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/redirectReload': {
id: '/redirectReload'
path: '/redirectReload'
fullPath: '/redirectReload'
preLoaderRoute: typeof RedirectReloadRouteImport
parentRoute: typeof rootRouteImport
}
'/redirect': {
id: '/redirect'
path: '/redirect'
fullPath: '/redirect'
preLoaderRoute: typeof RedirectRouteImport
parentRoute: typeof rootRouteImport
}
'/about': {
id: '/about'
path: '/about'
Expand All @@ -71,6 +105,8 @@ declare module '@tanstack/react-router' {
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
AboutRoute: AboutRoute,
RedirectRoute: RedirectRoute,
RedirectReloadRoute: RedirectReloadRoute,
}
export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren)
Expand Down
20 changes: 20 additions & 0 deletions e2e/react-router/basepath-file-based/src/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,26 @@ function App() {
}
>
Navigate to /about with document reload
</button>{' '}
<button
data-testid="to-redirect-btn"
onClick={() =>
navigate({
to: '/redirect',
})
}
>
Navigate to /redirect
</button>{' '}
<button
data-testid="to-redirect-reload-btn"
onClick={() =>
navigate({
to: '/redirectReload',
})
}
>
Navigate to /redirectReload
</button>
</div>
)
Expand Down
12 changes: 12 additions & 0 deletions e2e/react-router/basepath-file-based/src/routes/redirect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { createFileRoute, redirect } from '@tanstack/react-router'

export const Route = createFileRoute('/redirect')({
beforeLoad: () => {
throw redirect({ to: '/about' })
},
component: RouteComponent,
})

function RouteComponent() {
return <div>Hello "/redirect"!</div>
}
12 changes: 12 additions & 0 deletions e2e/react-router/basepath-file-based/src/routes/redirectReload.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { createFileRoute, redirect } from '@tanstack/react-router'

export const Route = createFileRoute('/redirectReload')({
beforeLoad: () => {
throw redirect({ to: '/about', reloadDocument: true })
},
component: RouteComponent,
})

function RouteComponent() {
return <div>Hello "/redirectReload"!</div>
}
22 changes: 22 additions & 0 deletions e2e/react-router/basepath-file-based/tests/reload-document.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,25 @@ test('navigate() respects basepath for when reloadDocument=true', async ({
await page.waitForURL('/app/')
await expect(page.getByTestId(`home-component`)).toBeInViewport()
})

test('redirect respects basepath', async ({ page }) => {
await page.goto(`/app/`)
await expect(page.getByTestId(`home-component`)).toBeInViewport()

const redirectBtn = page.getByTestId(`to-redirect-btn`)
await redirectBtn.click()
await page.waitForURL('/app/about')
await expect(page.getByTestId(`about-component`)).toBeInViewport()
})

test('redirect respects basepath with reloadDocument = true on redirect', async ({
page,
}) => {
await page.goto(`/app/`)
await expect(page.getByTestId(`home-component`)).toBeInViewport()

const redirectBtn = page.getByTestId(`to-redirect-reload-btn`)
await redirectBtn.click()
await page.waitForURL('/app/about')
await expect(page.getByTestId(`about-component`)).toBeInViewport()
})
2 changes: 2 additions & 0 deletions packages/router-core/src/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,8 @@ export interface NavigateOptionProps {
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/NavigateOptionsType#href)
*/
href?: string
/** @internal */
publicHref?: string
}

export type ToOptions<
Expand Down
29 changes: 22 additions & 7 deletions packages/router-core/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2029,20 +2029,35 @@ export class RouterCore<
*
* @link https://tanstack.com/router/latest/docs/framework/react/api/router/NavigateOptionsType
*/
navigate: NavigateFn = async ({ to, reloadDocument, href, ...rest }) => {
if (!reloadDocument && href) {
navigate: NavigateFn = async ({
to,
reloadDocument,
href,
publicHref,
...rest
}) => {
let hrefIsUrl = false

if (href) {
try {
new URL(`${href}`)
reloadDocument = true
hrefIsUrl = true
} catch {}
}

if (hrefIsUrl && !reloadDocument) {
reloadDocument = true
}

if (reloadDocument) {
if (!href) {
if (!href || (!publicHref && !hrefIsUrl)) {
const location = this.buildLocation({ to, ...rest } as any)
href = location.url.href
href = href ?? location.url.href
publicHref = publicHref ?? location.url.href
}

const reloadHref = !hrefIsUrl && publicHref ? publicHref : href

// Check blockers for external URLs unless ignoreBlocker is true
if (!rest.ignoreBlocker) {
// Cast to access internal getBlockers method
Expand All @@ -2063,9 +2078,9 @@ export class RouterCore<
}

if (rest.replace) {
window.location.replace(href)
window.location.replace(reloadHref)
} else {
window.location.href = href
window.location.href = reloadHref
}
return Promise.resolve()
}
Expand Down