-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
fix(router-core): apply rewrite to href property in buildLocation #5202
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
base: main
Are you sure you want to change the base?
fix(router-core): apply rewrite to href property in buildLocation #5202
Conversation
…ation Signed-off-by: leesb971204 <[email protected]>
WalkthroughparseLocation now sources internal href from the rewritten URL (origin removed) and sets url to the full rewritten href; publicHref remains pathname+search+hash. Documentation examples updated to use rewriteBasepath({ basepath }) and composeRewrites. Two new tests validate basepath rewrite and backward-compat behavior. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Client
participant Router
participant Rewriter
Client->>Router: parseLocation(rawLocation)
Router->>Rewriter: apply rewrite rules(rawLocation)
Rewriter-->>Router: rewrittenUrl { href, origin, pathname, search, hash }
Router->>Router: href = rewrittenUrl.href.replace(rewrittenUrl.origin, '')
Router->>Router: url = rewrittenUrl.href
Router->>Router: publicHref = rewrittenUrl.pathname + rewrittenUrl.search + rewrittenUrl.hash
Router-->>Client: Location { href, url, publicHref, ... }
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (5 passed)
✨ Finishing touches
🧪 Generate unit tests
Comment |
|
Command | Status | Duration | Result |
---|---|---|---|
nx affected --targets=test:eslint,test:unit,tes... |
❌ Failed | 5m 6s | View ↗ |
nx run-many --target=build --exclude=examples/*... |
✅ Succeeded | 1m 21s | View ↗ |
☁️ Nx Cloud last updated this comment at 2025-09-29 00:33:52
UTC
More templates
@tanstack/arktype-adapter
@tanstack/directive-functions-plugin
@tanstack/eslint-plugin-router
@tanstack/history
@tanstack/nitro-v2-vite-plugin
@tanstack/react-router
@tanstack/react-router-devtools
@tanstack/react-router-ssr-query
@tanstack/react-start
@tanstack/react-start-client
@tanstack/react-start-server
@tanstack/router-cli
@tanstack/router-core
@tanstack/router-devtools
@tanstack/router-devtools-core
@tanstack/router-generator
@tanstack/router-plugin
@tanstack/router-ssr-query-core
@tanstack/router-utils
@tanstack/router-vite-plugin
@tanstack/server-functions-plugin
@tanstack/solid-router
@tanstack/solid-router-devtools
@tanstack/solid-start
@tanstack/solid-start-client
@tanstack/solid-start-server
@tanstack/start-client-core
@tanstack/start-plugin-core
@tanstack/start-server-core
@tanstack/start-static-server-functions
@tanstack/start-storage-context
@tanstack/valibot-adapter
@tanstack/virtual-file-routes
@tanstack/zod-adapter
commit: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
packages/router-core/src/router.ts (3)
1683-1690
: Minor: Buildhref
from URL parts instead of stripping originSafer and clearer to concatenate
pathname + search + hash
than usingreplace
onhref
(avoids accidental substring removal if origin appears elsewhere).- href: rewrittenUrl.href.replace(rewrittenUrl.origin, ''), + href: rewrittenUrl.pathname + rewrittenUrl.search + rewrittenUrl.hash,
1781-1783
: Align equality check with newhref
semantics (comparepublicHref
)
buildLocation.href
is now output‑rewritten whilelatestLocation.href
(from parse) remains input‑rewritten. Comparing them will false‑negative under basepath. ComparepublicHref
on both sides to retain the “no-op if nothing changed” optimization.- const isSameUrl = - trimPathRight(this.latestLocation.href) === trimPathRight(next.href) + const isSameUrl = + trimPathRight(this.latestLocation.publicHref) === + trimPathRight(next.publicHref)
1939-1945
: SSR parity: compare and redirect usingpublicHref
Same reasoning as above: avoid comparing input‑rewritten
href
to output‑rewrittenhref
. UsepublicHref
for both the comparison and redirect target to prevent unnecessary redirects (or loops) when a basepath rewrite is active.- if ( - trimPath(normalizeUrl(this.latestLocation.href)) !== - trimPath(normalizeUrl(nextLocation.href)) - ) { - throw redirect({ href: nextLocation.href }) - } + if ( + trimPath(normalizeUrl(this.latestLocation.publicHref)) !== + trimPath(normalizeUrl(nextLocation.publicHref)) + ) { + throw redirect({ href: nextLocation.publicHref }) + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/router-core/src/router.ts
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use TypeScript in strict mode with extensive type safety across the codebase
Files:
packages/router-core/src/router.ts
packages/router-core/**
📄 CodeRabbit inference engine (AGENTS.md)
Keep framework-agnostic core router logic in packages/router-core/
Files:
packages/router-core/src/router.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Preview
- GitHub Check: Test
Signed-off-by: leesb971204 <[email protected]>
thanks for the PR! also, we should add tests for this (ideally some unit tests) |
I’m not really sure — honestly, I’m not very familiar with TanStack Start. I’ll go ahead and add a unit test in the meantime! |
…ot route Signed-off-by: leesb971204 <[email protected]>
…ref-rewrite - Typecheck: `tsc -b`
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
packages/react-router/tests/router.test.tsx (2)
2701-2732
: Good coverage of basepath on root; add a test for “/” → basepath redirectThis validates internal “/” vs browser “/my-app/” when landing on “/my-app/”. To guard against regressions for the original bug (missing redirect from “/” to basepath), add a companion test starting at “/” and asserting history ends at “/my-app/” while router stays at “/”.
Apply this diff near this test:
+ it('should redirect from "/" to basepath and keep internal pathname "/"', async () => { + const rootRoute = createRootRoute({ component: () => <Outlet /> }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () => <div data-testid="home">Home</div>, + }) + const routeTree = rootRoute.addChildren([indexRoute]) + + const history = createMemoryHistory({ initialEntries: ['/'] }) + const router = createRouter({ + routeTree, + history, + rewrite: rewriteBasepath({ basepath: '/my-app' }), + }) + + render(<RouterProvider router={router} />) + + await waitFor(() => { + expect(screen.getByTestId('home')).toBeInTheDocument() + }) + + expect(router.state.location.pathname).toBe('/') + // Allow either trailing or non-trailing slash if normalization changes: + expect(['/my-app', '/my-app/']).toContain(history.location.pathname) + })
2734-2765
: Back-compat test looks good; also assert navigation applies output rewrite with basepath optionThis confirms basepath option parity on initial load. Add a navigation test mirroring the Link-based case to ensure output rewrite occurs with basepath option too.
Apply this diff near this block:
+ it('basepath option: Link navigation applies output rewrite', async () => { + const rootRoute = createRootRoute({ component: () => <Outlet /> }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () => ( + <div> + <Link to="/about" data-testid="about-link">About</Link> + </div> + ), + }) + const aboutRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/about', + component: () => <div data-testid="about">About</div>, + }) + const routeTree = rootRoute.addChildren([indexRoute, aboutRoute]) + + const history = createMemoryHistory({ initialEntries: ['/my-app/'] }) + const router = createRouter({ routeTree, history, basepath: '/my-app' }) + + render(<RouterProvider router={router} />) + const aboutLink = await screen.findByTestId('about-link') + fireEvent.click(aboutLink) + + await waitFor(() => { + expect(screen.getByTestId('about')).toBeInTheDocument() + }) + expect(router.state.location.pathname).toBe('/about') + expect(history.location.pathname).toBe('/my-app/about') + })
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/react-router/tests/router.test.tsx
(1 hunks)packages/router-core/src/router.ts
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/router-core/src/router.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use TypeScript in strict mode with extensive type safety across the codebase
Files:
packages/react-router/tests/router.test.tsx
packages/{react-router,solid-router}/**
📄 CodeRabbit inference engine (AGENTS.md)
Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/
Files:
packages/react-router/tests/router.test.tsx
🧬 Code graph analysis (1)
packages/react-router/tests/router.test.tsx (5)
packages/react-router/src/route.tsx (2)
createRootRoute
(566-618)createRoute
(310-390)packages/react-router/src/Match.tsx (1)
Outlet
(309-359)packages/history/src/index.ts (1)
createMemoryHistory
(568-614)packages/react-router/src/router.ts (1)
createRouter
(80-82)packages/react-router/src/RouterProvider.tsx (1)
RouterProvider
(47-56)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Test
- GitHub Check: Preview
fixes #5200
Problem
When using the new rewrite system with basepath, redirects from
/
to/basepath
were not working in client-side navigation.Root Cause
The
buildLocation
function was returning the originalfullPath
for thehref
property instead of applying the output rewrite. This caused the Transitioner component to compare non-rewritten URLs, preventing proper redirect detection.Summary by CodeRabbit
Bug Fixes
Documentation
Tests