-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
fix(router-core): Catch errors thrown during hydrate #5686
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
Conversation
WalkthroughAdds a new file route Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Hydrator as SSR Hydrator
participant Route
participant Logger
Client->>Hydrator: Begin hydration (window.$_TSR)
loop per match
Hydrator->>Route: compute parentContext & __routeContext (route.context)
Hydrator->>Route: build assetContext (matches, params, loaderData, etc.)
Hydrator->>Route: evaluate head/scripts with assetContext
alt head/scripts success
Route-->>Hydrator: head/meta/scripts/styles
Hydrator->>Hydrator: assign meta/links/scripts/styles to match
else notFound() thrown
Route-->>Hydrator: throw notFound()
Hydrator->>Hydrator: catch NotFound -> mark match.error = isNotFound
Hydrator->>Logger: console.error("NotFound during hydration", ...)
Note right of Hydrator: continue to next match
else other error thrown
Route-->>Hydrator: throw Error
Hydrator->>Hydrator: catch Error -> set match.error
Hydrator->>Logger: console.error("Error during hydration", ...)
Hydrator->>Client: rethrow (abort)
end
end
Hydrator-->>Client: Hydration complete (matches populated)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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: 2
🧹 Nitpick comments (3)
packages/router-core/tests/hydrate.test.ts (3)
89-93: Consider more complete mock matches.The mock matches use a minimal structure with only
id,routeId,index, and_nonReactive. While this may be sufficient for the serialization adapter test, consider whether additional properties (e.g.,params,loaderDeps,context,abortController) are needed to accurately represent real matches and avoid potential runtime errors if thehydratefunction accesses these properties.
215-254: LGTM with suggestions for enhanced coverage.The test correctly validates that errors thrown during route context hydration (specifically from the
headhook) are caught and logged toconsole.errorrather than propagating. This aligns with the PR objective.Consider adding assertions to verify that:
- The
hydratefunction completes successfully despite the error (doesn't reject the promise)- Other matches continue to be processed when one match throws an error
13-255: Consider expanding test coverage for additional hydration scenarios.The current tests provide good coverage of core hydration mechanics and error handling. Consider adding tests for:
- CSP nonce meta tag handling
pendingMinMsand_forcePendinglogic for pending matches- SPA mode detection (
lastMatchIdmismatch)- Custom
router.options.hydratecallback invocation- Route chunk loading with
loadRouteChunkThese scenarios are exercised in the
hydrateimplementation but not currently validated by tests.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
e2e/react-start/basic/src/routeTree.gen.ts(10 hunks)e2e/react-start/basic/src/routes/not-found/index.tsx(1 hunks)e2e/react-start/basic/src/routes/not-found/via-head.tsx(1 hunks)e2e/react-start/basic/tests/not-found.spec.ts(3 hunks)e2e/react-start/basic/vite.config.ts(1 hunks)e2e/solid-start/basic/src/routeTree.gen.ts(10 hunks)e2e/solid-start/basic/src/routes/not-found/index.tsx(1 hunks)e2e/solid-start/basic/src/routes/not-found/via-head.tsx(1 hunks)e2e/solid-start/basic/tests/not-found.spec.ts(3 hunks)packages/router-core/src/ssr/ssr-client.ts(1 hunks)packages/router-core/tests/hydrate.test.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use TypeScript in strict mode with extensive type safety across the codebase
Files:
e2e/react-start/basic/src/routes/not-found/index.tsxpackages/router-core/src/ssr/ssr-client.tse2e/react-start/basic/vite.config.tse2e/react-start/basic/tests/not-found.spec.tse2e/solid-start/basic/src/routes/not-found/index.tsxpackages/router-core/tests/hydrate.test.tse2e/solid-start/basic/tests/not-found.spec.tse2e/react-start/basic/src/routes/not-found/via-head.tsxe2e/solid-start/basic/src/routeTree.gen.tse2e/solid-start/basic/src/routes/not-found/via-head.tsxe2e/react-start/basic/src/routeTree.gen.ts
**/src/routes/**
📄 CodeRabbit inference engine (AGENTS.md)
Place file-based routes under src/routes/ directories
Files:
e2e/react-start/basic/src/routes/not-found/index.tsxe2e/solid-start/basic/src/routes/not-found/index.tsxe2e/react-start/basic/src/routes/not-found/via-head.tsxe2e/solid-start/basic/src/routes/not-found/via-head.tsx
e2e/**
📄 CodeRabbit inference engine (AGENTS.md)
Store end-to-end tests under the e2e/ directory
Files:
e2e/react-start/basic/src/routes/not-found/index.tsxe2e/react-start/basic/vite.config.tse2e/react-start/basic/tests/not-found.spec.tse2e/solid-start/basic/src/routes/not-found/index.tsxe2e/solid-start/basic/tests/not-found.spec.tse2e/react-start/basic/src/routes/not-found/via-head.tsxe2e/solid-start/basic/src/routeTree.gen.tse2e/solid-start/basic/src/routes/not-found/via-head.tsxe2e/react-start/basic/src/routeTree.gen.ts
packages/router-core/**
📄 CodeRabbit inference engine (AGENTS.md)
Keep framework-agnostic core router logic in packages/router-core/
Files:
packages/router-core/src/ssr/ssr-client.tspackages/router-core/tests/hydrate.test.ts
🧬 Code graph analysis (5)
e2e/react-start/basic/src/routes/not-found/index.tsx (2)
e2e/react-start/basic/src/routes/not-found/via-head.tsx (1)
Route(3-15)e2e/react-start/basic/src/routes/not-found/route.tsx (1)
Route(4-8)
e2e/solid-start/basic/src/routes/not-found/index.tsx (3)
e2e/react-start/basic/src/routes/not-found/index.tsx (1)
Route(3-41)e2e/react-start/basic/src/routes/not-found/via-head.tsx (1)
Route(3-15)e2e/solid-start/basic/src/routes/not-found/via-head.tsx (1)
Route(3-15)
packages/router-core/tests/hydrate.test.ts (1)
packages/router-core/src/ssr/ssr-client.ts (2)
TsrSsrGlobal(17-28)hydrate(59-279)
e2e/react-start/basic/src/routes/not-found/via-head.tsx (2)
e2e/react-start/basic/src/routes/not-found/index.tsx (1)
Route(3-41)e2e/solid-start/basic/src/routes/not-found/via-head.tsx (1)
Route(3-15)
e2e/solid-start/basic/src/routes/not-found/via-head.tsx (1)
e2e/solid-start/basic/src/routes/not-found/index.tsx (1)
Route(3-41)
🔇 Additional comments (21)
e2e/react-start/basic/vite.config.ts (1)
23-23: LGTM!The addition of
/not-found/via-headto the prerender exclusion list is correct and consistent with other not-found routes. Routes that triggernotFound()should not be prerendered.e2e/solid-start/basic/src/routes/not-found/index.tsx (1)
28-37: LGTM!The via-head link addition follows the established pattern and uses proper Solid.js syntax (
class, signal accessorpreload()).e2e/react-start/basic/src/routes/not-found/index.tsx (1)
28-37: LGTM!The via-head link addition follows the established pattern and uses proper React syntax.
e2e/solid-start/basic/tests/not-found.spec.ts (2)
12-12: LGTM!The whitelisted error for route context hydration aligns with the PR's objective to improve SSR hydration error handling when
notFound()is thrown.
29-29: LGTM!Expanding the test matrices to include
'head'provides coverage for the new/not-found/via-headroute that triggersnotFound()during the head phase.Also applies to: 60-60
e2e/react-start/basic/tests/not-found.spec.ts (1)
12-12: LGTM!Test updates mirror the Solid implementation and properly extend coverage for the new via-head route with appropriate error whitelisting.
Also applies to: 29-29, 61-61
e2e/react-start/basic/src/routes/not-found/via-head.tsx (2)
3-15: Route correctly implements head-phase notFound() for testing.The route structure properly tests the scenario where
notFound()is thrown during theheadphase. ThenotFoundComponentwill be rendered instead of the regularcomponent.
17-23: RouteComponent exists for completeness.While
RouteComponentwon't render in practice (sincehead()always throwsnotFound()), it's appropriate to define it for test completeness. Thedata-serverattribute helps verify SSR/hydration behavior in tests.e2e/solid-start/basic/src/routes/not-found/via-head.tsx (1)
1-23: LGTM!The Solid implementation correctly mirrors the React version with appropriate framework-specific imports. The route properly tests
notFound()thrown during theheadphase.e2e/solid-start/basic/src/routeTree.gen.ts (1)
35-35: LGTM!The generated route tree correctly integrates the new
NotFoundViaHeadRoutewith proper imports, type definitions, and route registrations throughout the file.Also applies to: 167-171, 261-261, 295-295, 335-335, 375-375, 409-409, 448-448, 655-661, 765-765, 772-772
e2e/react-start/basic/src/routeTree.gen.ts (5)
173-177: Route constant definition is consistent.
idandpathuse '/via-head' and parent isNotFoundRouteRoute, matching sibling routes.
281-282: Types and public route maps updated coherently.Additions for '/not-found/via-head' are present and consistent across FileRoutesByFullPath, FileRoutesByTo, FileRoutesById, and FileRouteTypes unions.
Also applies to: 317-318, 358-359, 401-402, 437-438, 477-478
688-694: Module augmentation entry matches generator conventions.
fullPath,path,preLoaderRoute, andparentRoutealign with the new route; no discrepancies spotted.
819-820: Children wiring under NotFoundRouteRoute is correct.
NotFoundViaHeadRouteadded to both the interface and value object. Mirrors sibling route patterns.Also applies to: 826-827
37-37: Import and route wiring verified successfully.The source file
e2e/react-start/basic/src/routes/not-found/via-head.tsxexists and correctly exportsRoutewith aheadhook that callsnotFound(). The import in routeTree.gen.ts is correctly wired.packages/router-core/tests/hydrate.test.ts (6)
54-58: LGTM!The test correctly validates the error handling when
window.$_TSRis missing.
60-72: LGTM!The test correctly validates the error handling when
window.$_TSR.routeris missing.
118-138: LGTM!The test correctly validates that no transformer map is created when serialization adapters are empty, while initialization still completes.
140-160: LGTM!The test correctly validates that the manifest is propagated from
window.$_TSR.routerintorouter.ssr.
162-213: LGTM!The test correctly validates that matches are hydrated with server-provided loader data and status flags, including the case where some matches lack dehydrated data.
4-10: I need to examine what this test file is actually testing to determine if importing from react-router is correct.The review comment is incorrect and should be disregarded.
The utilities being imported (
createMemoryHistory,createRootRoute,createRoute,createRouter,notFound) are framework-specific APIs that belong in@tanstack/react-router, notpackages/router-core. These are standard React Router imports, not router-core utilities. OnlynotFoundhas a framework-agnostic version in router-core; the others are inherently React-specific and cannot be imported from router-core because they don't exist there.If the concern is about framework coupling in router-core tests, that's a separate architectural question worth discussing, but the specific directive to import from
packages/router-core/srcis not actionable because these utilities do not and should not exist there by design.Likely an incorrect or invalid review comment.
|
|
||
| const routeTree = rootRoute.addChildren([indexRoute.addChildren([otherRoute])]) | ||
|
|
||
| mockRouter = createRouter({ routeTree, history, isServer: true }) |
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.
Verify the isServer: true flag for client hydration tests.
The hydrate function is designed for client-side hydration, yet the router is created with isServer: true. This contradicts the test scenario and may affect router behavior. Consider using isServer: false or omitting the flag entirely to accurately simulate the client environment.
🤖 Prompt for AI Agents
In packages/router-core/tests/hydrate.test.ts around line 46, the test creates
the router with isServer: true which contradicts the client-side hydration
scenario; update the router instantiation to use isServer: false (or remove the
flag) so the router is created as a client environment, ensuring hydrate runs
against client-mode behavior and any server-only branches are not triggered.
|
View your CI Pipeline Execution ↗ for commit dad79fe
☁️ Nx Cloud last updated this comment at |
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
♻️ Duplicate comments (2)
packages/router-core/src/ssr/ssr-client.ts (1)
220-222: Error handling concern already raised in previous review.The indiscriminate error swallowing in this catch handler has been comprehensively flagged in the previous review, including concerns about silent failures, lack of state tracking, and inconsistent router state.
packages/router-core/tests/hydrate.test.ts (1)
48-48: isServer flag concern already raised in previous review.The use of
isServer: truefor client-side hydration tests has been flagged in the previous review as contradicting the test scenario.
🧹 Nitpick comments (2)
packages/router-core/tests/hydrate.test.ts (2)
1-11: Consider importing from source instead of dist.The import on lines 4-10 uses
../../react-router/dist/esm, which is unusual for unit tests. Typically, tests import from source files to ensure they're testing the actual implementation rather than the built artifacts.Additionally, ESLint flags import ordering issues that could be addressed.
Apply this diff to import from source and fix ordering:
-import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, + notFound, +} from '../../react-router/src' import { hydrate } from '../src/ssr/ssr-client' import type { TsrSsrGlobal } from '../src/ssr/ssr-client' -import { - createMemoryHistory, - createRootRoute, - createRoute, - createRouter, - notFound, -} from '../../react-router/dist/esm' import type { AnyRouteMatch } from '../src'
217-259: Consider expanding error handling test coverage.The test validates that
notFound()errors during hydration are caught and logged. However, it only covers one error type and doesn't verify:
- Router state remains consistent after the error
- Hydration completes successfully despite the error
- Other error types (e.g., TypeError, ReferenceError) are handled similarly
Consider adding test cases for:
it('should handle unexpected errors during route context hydration', async () => { // Test TypeError, ReferenceError, etc. }) it('should continue hydration after error in one match', async () => { // Verify that other matches complete hydration successfully })Additionally, verify that the router state is checked after error handling to ensure consistency.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
e2e/react-start/basic/src/routes/not-found/via-head.tsx(1 hunks)e2e/solid-start/basic/src/routes/not-found/via-head.tsx(1 hunks)packages/router-core/src/ssr/ssr-client.ts(1 hunks)packages/router-core/tests/hydrate.test.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- e2e/solid-start/basic/src/routes/not-found/via-head.tsx
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use TypeScript in strict mode with extensive type safety across the codebase
Files:
packages/router-core/tests/hydrate.test.tse2e/react-start/basic/src/routes/not-found/via-head.tsxpackages/router-core/src/ssr/ssr-client.ts
packages/router-core/**
📄 CodeRabbit inference engine (AGENTS.md)
Keep framework-agnostic core router logic in packages/router-core/
Files:
packages/router-core/tests/hydrate.test.tspackages/router-core/src/ssr/ssr-client.ts
**/src/routes/**
📄 CodeRabbit inference engine (AGENTS.md)
Place file-based routes under src/routes/ directories
Files:
e2e/react-start/basic/src/routes/not-found/via-head.tsx
e2e/**
📄 CodeRabbit inference engine (AGENTS.md)
Store end-to-end tests under the e2e/ directory
Files:
e2e/react-start/basic/src/routes/not-found/via-head.tsx
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/router-core/** : Keep framework-agnostic core router logic in packages/router-core/
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{react-router,solid-router}/** : Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/
📚 Learning: 2025-10-08T08:11:47.088Z
Learnt from: nlynzaad
PR: TanStack/router#5402
File: packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts:19-21
Timestamp: 2025-10-08T08:11:47.088Z
Learning: Test snapshot files in the router-generator tests directory (e.g., files matching the pattern `packages/router-generator/tests/generator/**/routeTree*.snapshot.ts` or `routeTree*.snapshot.js`) should not be modified or have issues flagged, as they are fixtures used to verify the generator's output and are intentionally preserved as-is.
Applied to files:
packages/router-core/tests/hydrate.test.tse2e/react-start/basic/src/routes/not-found/via-head.tsxpackages/router-core/src/ssr/ssr-client.ts
📚 Learning: 2025-10-01T18:30:26.591Z
Learnt from: schiller-manuel
PR: TanStack/router#5330
File: packages/router-core/src/router.ts:2231-2245
Timestamp: 2025-10-01T18:30:26.591Z
Learning: In `packages/router-core/src/router.ts`, the `resolveRedirect` method intentionally strips the router's origin from redirect URLs when they match (e.g., `https://foo.com/bar` → `/bar` for same-origin redirects) while preserving the full URL for cross-origin redirects. This logic should not be removed or simplified to use `location.publicHref` directly.
Applied to files:
packages/router-core/tests/hydrate.test.ts
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/router-core/** : Keep framework-agnostic core router logic in packages/router-core/
Applied to files:
packages/router-core/tests/hydrate.test.ts
📚 Learning: 2025-10-01T18:31:35.420Z
Learnt from: schiller-manuel
PR: TanStack/router#5330
File: e2e/react-start/custom-basepath/src/routeTree.gen.ts:58-61
Timestamp: 2025-10-01T18:31:35.420Z
Learning: Do not review files named `routeTree.gen.ts` in TanStack Router repositories, as these are autogenerated files that should not be manually modified.
Applied to files:
e2e/react-start/basic/src/routes/not-found/via-head.tsx
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{react-router,solid-router}/** : Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/
Applied to files:
e2e/react-start/basic/src/routes/not-found/via-head.tsx
📚 Learning: 2025-09-28T21:41:45.233Z
Learnt from: nlynzaad
PR: TanStack/router#5284
File: e2e/react-start/basic/server.js:50-0
Timestamp: 2025-09-28T21:41:45.233Z
Learning: In Express v5, catch-all routes must use named wildcards. Use `/*splat` to match everything except root path, or `/{*splat}` (with braces) to match including root path. The old `*` syntax is not allowed and will cause "Missing parameter name" errors. This breaking change requires explicit naming of wildcard parameters.
Applied to files:
packages/router-core/src/ssr/ssr-client.ts
🧬 Code graph analysis (2)
packages/router-core/tests/hydrate.test.ts (1)
packages/router-core/src/ssr/ssr-client.ts (2)
TsrSsrGlobal(17-28)hydrate(59-279)
e2e/react-start/basic/src/routes/not-found/via-head.tsx (1)
e2e/react-start/basic/src/routes/not-found/index.tsx (1)
Route(3-41)
🪛 ESLint
packages/router-core/tests/hydrate.test.ts
[error] 1-1: Member 'expect' of the import declaration should be sorted alphabetically.
(sort-imports)
[error] 4-10: ../../react-router/dist/esm import should occur before type import of ../src/ssr/ssr-client
(import/order)
🔇 Additional comments (4)
packages/router-core/tests/hydrate.test.ts (1)
56-215: Comprehensive test coverage for core hydration scenarios.These tests effectively cover:
- Missing bootstrap data validation
- Serialization adapter initialization and buffer execution
- Manifest propagation to router.ssr
- Match hydration with server-provided data
The test structure is clear and assertions are appropriate.
e2e/react-start/basic/src/routes/not-found/via-head.tsx (3)
1-1: LGTM!The imports are correct and appropriate for creating a file-based route that tests
notFound()behavior.
17-23: LGTM!The
RouteComponentis well-implemented with appropriate test attributes. Thedata-server={typeof window}is a clever way to distinguish between server and client rendering contexts. While this component should not actually render (since theheadhook always throwsnotFound()), it serves as a valid test fixture to ensure thenotFoundComponenttakes precedence.
3-15: Implementation correctly matches all test expectations.The route implementation is correct:
- The
headhook throwsnotFound()immediately, preventingRouteComponentfrom rendering- The
notFoundComponenthasdata-testid="via-head-notFound-component"matching test expectations for visibility- The
RouteComponenthasdata-testid="via-head-route-component"matching test expectations for non-visibility- Both navigation and direct visit scenarios in the test suite will pass
- The hydration error
'Error during route context hydration: {isNotFound: true}'is properly whitelisted in the tests
|
Hi @schiller-manuel, do you need me to do anything more for this PR to merge? 🙏 |
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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
♻️ Duplicate comments (1)
packages/router-core/tests/hydrate.test.ts (1)
18-49: Re‑checkisServer: truefor a client‑sidehydratetest setup.The router used for
hydrateis created withisServer: true. Sincehydrateis the client‑side hydration entry, it may be more representative to run these tests with a client‑mode router (isServer: falseor omitting the flag), unless there is a specific reason to exercise the server branch here. Please confirm this is intentional and not masking any client‑only behavior.
🧹 Nitpick comments (1)
packages/router-core/tests/hydrate.test.ts (1)
217-261: Consider also asserting onmatch.errorfor stronger NotFound guarantees.This test nicely verifies that
notFound()thrown fromheadis caught and logged with the expected message and an error carryingisNotFound: true. To fully lock in the new behavior, you could additionally assert that the match’s error was normalized:const match = mockRouter.state.matches[0] as AnyRouteMatch expect(match.error).toEqual({ isNotFound: true })That would protect both the logging and
match.errorcontract of the NotFound hydration path.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/router-core/tests/hydrate.test.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-10-08T08:11:47.088Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5402
File: packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts:19-21
Timestamp: 2025-10-08T08:11:47.088Z
Learning: Test snapshot files in the router-generator tests directory (e.g., files matching the pattern `packages/router-generator/tests/generator/**/routeTree*.snapshot.ts` or `routeTree*.snapshot.js`) should not be modified or have issues flagged, as they are fixtures used to verify the generator's output and are intentionally preserved as-is.
Applied to files:
packages/router-core/tests/hydrate.test.ts
📚 Learning: 2025-11-02T16:16:24.898Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5732
File: packages/start-client-core/src/client/hydrateStart.ts:6-9
Timestamp: 2025-11-02T16:16:24.898Z
Learning: In packages/start-client-core/src/client/hydrateStart.ts, the `import/no-duplicates` ESLint disable is necessary for imports from `#tanstack-router-entry` and `#tanstack-start-entry` because both aliases resolve to the same placeholder file (`fake-start-entry.js`) in package.json during static analysis, even though they resolve to different files at runtime.
Applied to files:
packages/router-core/tests/hydrate.test.ts
📚 Learning: 2025-10-01T18:30:26.591Z
Learnt from: schiller-manuel
Repo: TanStack/router PR: 5330
File: packages/router-core/src/router.ts:2231-2245
Timestamp: 2025-10-01T18:30:26.591Z
Learning: In `packages/router-core/src/router.ts`, the `resolveRedirect` method intentionally strips the router's origin from redirect URLs when they match (e.g., `https://foo.com/bar` → `/bar` for same-origin redirects) while preserving the full URL for cross-origin redirects. This logic should not be removed or simplified to use `location.publicHref` directly.
Applied to files:
packages/router-core/tests/hydrate.test.ts
🧬 Code graph analysis (1)
packages/router-core/tests/hydrate.test.ts (1)
packages/router-core/src/ssr/ssr-client.ts (2)
TsrSsrGlobal(19-34)hydrate(65-301)
🔇 Additional comments (2)
packages/router-core/tests/hydrate.test.ts (2)
56-140: Good coverage of bootstrap invariants and serialization adapter paths.The tests around missing
window.$_TSR, missingwindow.$_TSR.router, and adapter initialization accurately mirror thehydrateinvariants and adapter logic (including thetmap, buffer flushing, andinitializedflag). The use of stubs formatchRoutes/state.matcheskeeps these tests focused on the intended behavior without over‑mocking the router internals.
164-215: Match hydration assertions align well withhydrateMatchbehavior.The
should hydrate matchestest correctly verifies that dehydrated match fields (loaderData,status,ssr) are restored onto the client matches viahydrateMatch, using realistici/l/s/ssr/ushapes and the router’smatchespipeline. This is a solid regression check for the core SSR rehydration path.
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/router-core/src/ssr/ssr-client.ts (2)
186-207: Align context reconstruction with core router semantics for consistencyThe re-run of
route.options.contexthere is meant to rebuild route context after__beforeLoadContexthydration, but it currently diverges from the core router implementation:
parentContextdoes not fall back to{}when bothparentMatch?.contextandrouter.options.contextare undefined, unlike the logic inrouter.matchRoutes. (app.unpkg.com)- The
contextFnContextomitssearch, which is included in the main router’sRouteContextOptionswhen invokingroute.options.context. (app.unpkg.com)contextpassed intoroute.options.contextuses onlyparentContext, not the already-mergedmatch.contextthat includes__routeContextand__beforeLoadContextlike router.ts does.For behavioral parity and fewer subtle bugs, consider reshaping this block to follow the same pattern as
router.matchRoutes, roughly:- const parentMatch = router.state.matches[match.index - 1] - const parentContext = parentMatch?.context ?? router.options.context + const parentMatch = router.state.matches[match.index - 1] + const parentMatchId = parentMatch?.id + const parentContext = !parentMatchId + ? ((router.options.context as any) ?? {}) + : (parentMatch?.context ?? router.options.context ?? {}) ... - if (route.options.context) { - const contextFnContext: RouteContextOptions<any, any, any, any> = { - deps: match.loaderDeps, - params: match.params, - context: parentContext ?? {}, - location: router.state.location, - navigate: (opts: any) => - router.navigate({ - ...opts, - _fromLocation: router.state.location, - }), - buildLocation: router.buildLocation, - cause: match.cause, - abortController: match.abortController, - preload: false, - matches, - } - match.__routeContext = - route.options.context(contextFnContext) ?? undefined - } + // Rebuild route context using the same semantics as in matchRoutes + const baseContext = { + ...(parentContext ?? {}), + ...(match.__routeContext ?? {}), + ...(match.__beforeLoadContext ?? {}), + } + + if (route.options.context) { + const contextFnContext: RouteContextOptions<any, any, any, any> = { + search: match.search, + params: match.params, + context: baseContext, + location: router.state.location, + navigate: (opts: any) => + router.navigate({ + ...opts, + _fromLocation: router.state.location, + }), + buildLocation: router.buildLocation, + cause: match.cause, + abortController: match.abortController, + preload: false, + matches, + deps: match.loaderDeps, + } + match.__routeContext = + route.options.context?.(contextFnContext) ?? {} + }(Exact fields/types can be tuned to match current core APIs.)
This keeps SSR hydration’s context behavior in lockstep with the main router’s route-matching pipeline.
234-246: Preserve full NotFound error object instead of replacing with{ isNotFound: true }In the NotFound branch:
if (isNotFound(err)) { match.error = { isNotFound: true } // ... }you discard useful properties from the original
NotFoundError(routeId,data,headers, etc.), which other parts of the system (e.g.CatchNotFoundfallbacks that inspecterror.routeId) may rely on. (app.unpkg.com)To keep behavior consistent with the rest of the router, consider:
- if (isNotFound(err)) { - match.error = { isNotFound: true } + if (isNotFound(err)) { + match.error = err as any console.error( `NotFound error during hydration for routeId: ${match.routeId}`, err, )This still tags the error as not-found (via its
isNotFoundflag) but preserves any additional metadata.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/router-core/src/ssr/ssr-client.ts(2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-11-02T16:16:24.898Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5732
File: packages/start-client-core/src/client/hydrateStart.ts:6-9
Timestamp: 2025-11-02T16:16:24.898Z
Learning: In packages/start-client-core/src/client/hydrateStart.ts, the `import/no-duplicates` ESLint disable is necessary for imports from `#tanstack-router-entry` and `#tanstack-start-entry` because both aliases resolve to the same placeholder file (`fake-start-entry.js`) in package.json during static analysis, even though they resolve to different files at runtime.
Applied to files:
packages/router-core/src/ssr/ssr-client.ts
📚 Learning: 2025-09-28T21:41:45.233Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5284
File: e2e/react-start/basic/server.js:50-0
Timestamp: 2025-09-28T21:41:45.233Z
Learning: In Express v5, catch-all routes must use named wildcards. Use `/*splat` to match everything except root path, or `/{*splat}` (with braces) to match including root path. The old `*` syntax is not allowed and will cause "Missing parameter name" errors. This breaking change requires explicit naming of wildcard parameters.
Applied to files:
packages/router-core/src/ssr/ssr-client.ts
🧬 Code graph analysis (1)
packages/router-core/src/ssr/ssr-client.ts (1)
packages/router-core/src/route.ts (1)
RouteContextOptions(1075-1083)
⏰ 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
🔇 Additional comments (3)
packages/router-core/src/ssr/ssr-client.ts (3)
2-2: Import ofisNotFoundis appropriate and scoped correctlyThe new
isNotFoundimport matches its usage in the per‑match hydration error handling below and keeps SSR‑client logic aligned with core not‑found semantics.
218-233: Per-match head/scripts hydration and error isolation look solidRunning
head()andscripts()per match with an asset context of{ matches, match, params, loaderData }matches the documentedRouteOptions.head/scriptssignatures and ensures failures stay scoped to a single match instead of aborting the whole hydration pass. (tanstack.com)No issues here beyond the context-merging concerns already noted.
212-216: Review comment is factually incorrect about JavaScript runtime behaviorThe core claim in the review comment is wrong. Spreading
undefinedin JavaScript does not throw aTypeError; it produces an empty object{}. This was confirmed by runtime testing.Additionally, the same spread pattern with potentially undefined values exists throughout the codebase (e.g., router.ts lines 1495-1498 with
getParentContext()), and thegetParentContext()function itself explicitly returns potentially undefined values (router.ts line 1300). If this were truly a runtime error, router.ts would already be crashing.The code at ssr-client.ts lines 212-216 works as-is without the suggested nullish coalescing fix.
Likely an incorrect or invalid review comment.
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
♻️ Duplicate comments (1)
packages/router-core/tests/hydrate.test.ts (1)
48-48: Verify theisServer: trueflag for client hydration tests.The router is created with
isServer: true, which contradicts the client-side hydration scenario being tested. Consider usingisServer: falseor omitting this flag to accurately reflect the client environment.
🧹 Nitpick comments (2)
packages/router-core/tests/hydrate.test.ts (2)
217-264: Add test coverage for non-NotFound error rethrow behavior.The current test verifies NotFound error handling, but the
hydratefunction also rethrows non-NotFound errors (seessr-client.tslines 272-278). Add a test case that verifies when a non-NotFound error is thrown during head/scripts execution, it propagates correctly after being logged.Example test structure:
it('should rethrow non-NotFound errors during route context hydration', async () => { const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) const testError = new Error('Unexpected error') mockHead.mockImplementation(() => { throw testError }) const mockMatches = [ { id: '/', routeId: '/', index: 0, ssr: true, _nonReactive: {} }, ] mockRouter.matchRoutes = vi.fn().mockReturnValue(mockMatches) mockRouter.state.matches = mockMatches mockWindow.$_TSR = { router: { manifest: { routes: {} }, dehydratedData: {}, lastMatchId: '/', matches: [ { i: '/', l: { data: 'test' }, s: 'success', ssr: true, u: Date.now(), }, ], }, c: vi.fn(), p: vi.fn(), buffer: [], initialized: false, } await expect(hydrate(mockRouter)).rejects.toThrow('Unexpected error') expect(consoleSpy).toHaveBeenCalledWith( 'Error during hydration for route /:', testError, ) consoleSpy.mockRestore() })
13-265: Consider expanding test coverage for additional hydration scenarios.While the existing tests cover core functionality well, several code paths in
hydrateremain untested:
- CSP nonce extraction from meta tags
- Route chunk loading promises
minPendingPromiseand_forcePendingbehavior for pending routes- Custom
router.options.hydratecallback execution- SPA mode detection and special handling
- Route context reconstruction with parent context merging
Adding tests for these scenarios would improve confidence in the hydration logic, particularly for edge cases like SPA mode transitions.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/router-core/tests/hydrate.test.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-10-08T08:11:47.088Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5402
File: packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts:19-21
Timestamp: 2025-10-08T08:11:47.088Z
Learning: Test snapshot files in the router-generator tests directory (e.g., files matching the pattern `packages/router-generator/tests/generator/**/routeTree*.snapshot.ts` or `routeTree*.snapshot.js`) should not be modified or have issues flagged, as they are fixtures used to verify the generator's output and are intentionally preserved as-is.
Applied to files:
packages/router-core/tests/hydrate.test.ts
📚 Learning: 2025-11-02T16:16:24.898Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5732
File: packages/start-client-core/src/client/hydrateStart.ts:6-9
Timestamp: 2025-11-02T16:16:24.898Z
Learning: In packages/start-client-core/src/client/hydrateStart.ts, the `import/no-duplicates` ESLint disable is necessary for imports from `#tanstack-router-entry` and `#tanstack-start-entry` because both aliases resolve to the same placeholder file (`fake-start-entry.js`) in package.json during static analysis, even though they resolve to different files at runtime.
Applied to files:
packages/router-core/tests/hydrate.test.ts
📚 Learning: 2025-10-01T18:30:26.591Z
Learnt from: schiller-manuel
Repo: TanStack/router PR: 5330
File: packages/router-core/src/router.ts:2231-2245
Timestamp: 2025-10-01T18:30:26.591Z
Learning: In `packages/router-core/src/router.ts`, the `resolveRedirect` method intentionally strips the router's origin from redirect URLs when they match (e.g., `https://foo.com/bar` → `/bar` for same-origin redirects) while preserving the full URL for cross-origin redirects. This logic should not be removed or simplified to use `location.publicHref` directly.
Applied to files:
packages/router-core/tests/hydrate.test.ts
🧬 Code graph analysis (1)
packages/router-core/tests/hydrate.test.ts (1)
packages/router-core/src/ssr/ssr-client.ts (2)
TsrSsrGlobal(19-34)hydrate(65-307)
⏰ 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). (3)
- GitHub Check: autofix
- GitHub Check: Test
- GitHub Check: Preview
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
♻️ Duplicate comments (1)
packages/react-router/tests/hydrate.test.ts (1)
48-48: Verify theisServer: trueflag for this hydrate test routerHydration is a client-side concern; creating the router with
isServer: truemay exercise server-mode behavior and diverge from real client usage unless this is intentional.- mockRouter = createRouter({ routeTree, history, isServer: true }) + mockRouter = createRouter({ routeTree, history })#!/bin/bash # Inspect other hydrate tests and router configurations for consistency rg -n "hydrate\(" packages -S rg -n "isServer:\s*true" packages/react-router/tests -S
🧹 Nitpick comments (3)
packages/react-router/tests/hydrate.test.ts (3)
18-54: Avoid deletingglobal.windowto keep the test environment reusable
beforeEachoverwritesglobal.windowandafterEachdeletes it, which can interfere with other suites (especially if Vitest runs with a jsdom environment). Safer is to capture the previous value and restore it instead of always deleting the property.- let mockWindow: { $_TSR?: TsrSsrGlobal } + let mockWindow: { $_TSR?: TsrSsrGlobal } + let originalWindow: any @@ - beforeEach(() => { - // Reset global window mock - mockWindow = {} - ;(global as any).window = mockWindow + beforeEach(() => { + // Reset global window mock and preserve any existing window + originalWindow = (global as any).window + mockWindow = {} + ;(global as any).window = mockWindow @@ - afterEach(() => { - vi.resetAllMocks() - delete (global as any).window - }) + afterEach(() => { + vi.resetAllMocks() + if (originalWindow !== undefined) { + ;(global as any).window = originalWindow + } else { + delete (global as any).window + } + })
76-140: Optional: assert buffer clearing /hydratedflag for stronger adapter coverageIf
hydrateis expected to clearbufferand/or setwindow.$_TSR.hydrated = true, adding assertions for that behavior in these adapter tests would tighten regression coverage; otherwise this block looks good.
217-264: Consider loosening the assertion on the exact log messageThe NotFound/head test correctly checks that errors are caught,
match.erroris set, andconsole.erroris invoked. To reduce brittleness if the log text ever changes slightly, you could assert with a partial match (e.g.expect.stringContaining('NotFound error during hydration')) instead of the full literal.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/react-router/tests/hydrate.test.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-10-08T08:11:47.088Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5402
File: packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts:19-21
Timestamp: 2025-10-08T08:11:47.088Z
Learning: Test snapshot files in the router-generator tests directory (e.g., files matching the pattern `packages/router-generator/tests/generator/**/routeTree*.snapshot.ts` or `routeTree*.snapshot.js`) should not be modified or have issues flagged, as they are fixtures used to verify the generator's output and are intentionally preserved as-is.
Applied to files:
packages/react-router/tests/hydrate.test.ts
📚 Learning: 2025-11-02T16:16:24.898Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5732
File: packages/start-client-core/src/client/hydrateStart.ts:6-9
Timestamp: 2025-11-02T16:16:24.898Z
Learning: In packages/start-client-core/src/client/hydrateStart.ts, the `import/no-duplicates` ESLint disable is necessary for imports from `#tanstack-router-entry` and `#tanstack-start-entry` because both aliases resolve to the same placeholder file (`fake-start-entry.js`) in package.json during static analysis, even though they resolve to different files at runtime.
Applied to files:
packages/react-router/tests/hydrate.test.ts
📚 Learning: 2025-10-01T18:30:26.591Z
Learnt from: schiller-manuel
Repo: TanStack/router PR: 5330
File: packages/router-core/src/router.ts:2231-2245
Timestamp: 2025-10-01T18:30:26.591Z
Learning: In `packages/router-core/src/router.ts`, the `resolveRedirect` method intentionally strips the router's origin from redirect URLs when they match (e.g., `https://foo.com/bar` → `/bar` for same-origin redirects) while preserving the full URL for cross-origin redirects. This logic should not be removed or simplified to use `location.publicHref` directly.
Applied to files:
packages/react-router/tests/hydrate.test.ts
🧬 Code graph analysis (1)
packages/react-router/tests/hydrate.test.ts (1)
packages/router-core/src/ssr/ssr-client.ts (1)
TsrSsrGlobal(19-34)
🔇 Additional comments (2)
packages/react-router/tests/hydrate.test.ts (2)
56-75: Negative-path coverage for missing SSR bootstrap data looks solidThe first two tests clearly exercise the failure modes when
window.$_TSRorwindow.$_TSR.routerare absent and pin the expected error messages; no issues here.
142-162: Manifest propagation test correctly guardsrouter.ssr.manifestAsserting that
router.ssrmirrors the manifest fromwindow.$_TSR.router.manifestdirectly targets the SSR bootstrap contract; this test looks accurate and sufficient.
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: 1
♻️ Duplicate comments (1)
packages/react-router/tests/hydrate.test.ts (1)
18-49: ReconsiderisServer: truefor a client hydration test routerThe router used for
hydrateis created withisServer: true, even thoughhydraterepresents client-side hydration behavior. This mirrors a concern already raised in the router-core hydrate tests: server-mode configuration may subtly diverge from real client behavior or hit server-only branches.Unless you’re intentionally validating server-mode hydration, consider dropping the flag or using
isServer: falseto better reflect the client environment:- mockRouter = createRouter({ routeTree, history, isServer: true }) + mockRouter = createRouter({ routeTree, history }) + // or: createRouter({ routeTree, history, isServer: false })
🧹 Nitpick comments (2)
packages/react-router/tests/hydrate.test.ts (1)
1-11: Fix import order to satisfy ESLintimport/orderruleThe static analysis hint is correct: the import from
@tanstack/router-core/ssr/clientshould be grouped with external deps before the internal../srcimport.You can fix this with a small reorder:
-import { - createMemoryHistory, - createRootRoute, - createRoute, - createRouter, - notFound, -} from '../src' -import { hydrate } from '@tanstack/router-core/ssr/client' -import type { TsrSsrGlobal } from '@tanstack/router-core/ssr/client' +import { hydrate } from '@tanstack/router-core/ssr/client' +import { + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, + notFound, +} from '../src' +import type { TsrSsrGlobal } from '@tanstack/router-core/ssr/client' import type { AnyRouteMatch } from '../src'packages/solid-router/tests/hydrate.test.ts (1)
217-264: Nice NotFound hydration coverage; consider small reuse helperThe NotFound test both marks the match with
error.isNotFoundand asserts theconsole.errorsignature, which tightly guards the new behavior. The repeated construction ofmockWindow.$_TSRacross tests could optionally be factored into a small helper (e.g.,createTsr(routerOverrides)), but that’s purely for readability.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/react-router/tests/hydrate.test.ts(1 hunks)packages/solid-router/tests/hydrate.test.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-10-08T08:11:47.088Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5402
File: packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts:19-21
Timestamp: 2025-10-08T08:11:47.088Z
Learning: Test snapshot files in the router-generator tests directory (e.g., files matching the pattern `packages/router-generator/tests/generator/**/routeTree*.snapshot.ts` or `routeTree*.snapshot.js`) should not be modified or have issues flagged, as they are fixtures used to verify the generator's output and are intentionally preserved as-is.
Applied to files:
packages/solid-router/tests/hydrate.test.tspackages/react-router/tests/hydrate.test.ts
📚 Learning: 2025-11-02T16:16:24.898Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5732
File: packages/start-client-core/src/client/hydrateStart.ts:6-9
Timestamp: 2025-11-02T16:16:24.898Z
Learning: In packages/start-client-core/src/client/hydrateStart.ts, the `import/no-duplicates` ESLint disable is necessary for imports from `#tanstack-router-entry` and `#tanstack-start-entry` because both aliases resolve to the same placeholder file (`fake-start-entry.js`) in package.json during static analysis, even though they resolve to different files at runtime.
Applied to files:
packages/solid-router/tests/hydrate.test.tspackages/react-router/tests/hydrate.test.ts
📚 Learning: 2025-10-01T18:30:26.591Z
Learnt from: schiller-manuel
Repo: TanStack/router PR: 5330
File: packages/router-core/src/router.ts:2231-2245
Timestamp: 2025-10-01T18:30:26.591Z
Learning: In `packages/router-core/src/router.ts`, the `resolveRedirect` method intentionally strips the router's origin from redirect URLs when they match (e.g., `https://foo.com/bar` → `/bar` for same-origin redirects) while preserving the full URL for cross-origin redirects. This logic should not be removed or simplified to use `location.publicHref` directly.
Applied to files:
packages/react-router/tests/hydrate.test.ts
🧬 Code graph analysis (2)
packages/solid-router/tests/hydrate.test.ts (1)
packages/router-core/src/ssr/ssr-client.ts (1)
TsrSsrGlobal(19-34)
packages/react-router/tests/hydrate.test.ts (1)
packages/router-core/src/ssr/ssr-client.ts (1)
TsrSsrGlobal(19-34)
🪛 ESLint
packages/solid-router/tests/hydrate.test.ts
[error] 9-9: @tanstack/router-core/ssr/client import should occur before import of ../src
(import/order)
packages/react-router/tests/hydrate.test.ts
[error] 9-9: @tanstack/router-core/ssr/client import should occur before import of ../src
(import/order)
🔇 Additional comments (4)
packages/react-router/tests/hydrate.test.ts (1)
217-264: Strong coverage of NotFound errors during hydrationThis test nicely exercises the new per-match error handling by forcing
headto thrownotFound(), asserting the error is attached to the match and that a clearconsole.errorlog is emitted. It should help prevent regressions where NotFound errors bubble and break hydration.packages/solid-router/tests/hydrate.test.ts (3)
18-55: Global window mocking and router setup look solidThe
beforeEach/afterEachcorrectly isolateglobal.window, mocks, and router state per test, which should avoid cross-test leakage.
76-140: Good coverage of serialization adapter initializationThe tests for non-empty vs empty
serializationAdaptersvalidate the adapter registry (tmap), buffer flushing, andinitializedflag behavior in a focused way; this should catch regressions in the SSR client initialization path.
142-215: Match hydration test aligns with dehydrated shapeThe
dehydratedMatches→AnyRouteMatchexpectations forloaderData,status, andssrlook consistent with the compact match format and give good assurance that hydration wires up per-match state correctly.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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 (1)
packages/solid-router/tests/hydrate.test.ts (1)
1-11: Consider fixing remaining ESLint import violations.ESLint still flags three formatting issues:
- Import order:
../srcshould come before the type-only import from@tanstack/router-core/ssr/client- Sort order:
AnyRouteMatchshould be sorted alphabetically within the import list- Type specifier style: Prefer a top-level
import typestatement instead of inlinetype AnyRouteMatchWhile these don't affect functionality, addressing them would satisfy the linter.
Apply this diff to resolve all three issues:
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { hydrate } from '@tanstack/router-core/ssr/client' -import type { TsrSsrGlobal } from '@tanstack/router-core/ssr/client' import { + type AnyRouteMatch, createMemoryHistory, createRootRoute, createRoute, createRouter, notFound, - type AnyRouteMatch, } from '../src' +import { hydrate } from '@tanstack/router-core/ssr/client' +import type { TsrSsrGlobal } from '@tanstack/router-core/ssr/client'
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
e2e/solid-start/basic/vite.config.ts(1 hunks)packages/solid-router/tests/hydrate.test.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-10-01T18:31:35.420Z
Learnt from: schiller-manuel
Repo: TanStack/router PR: 5330
File: e2e/react-start/custom-basepath/src/routeTree.gen.ts:58-61
Timestamp: 2025-10-01T18:31:35.420Z
Learning: Do not review files named `routeTree.gen.ts` in TanStack Router repositories, as these are autogenerated files that should not be manually modified.
Applied to files:
e2e/solid-start/basic/vite.config.ts
📚 Learning: 2025-10-08T08:11:47.088Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5402
File: packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts:19-21
Timestamp: 2025-10-08T08:11:47.088Z
Learning: Test snapshot files in the router-generator tests directory (e.g., files matching the pattern `packages/router-generator/tests/generator/**/routeTree*.snapshot.ts` or `routeTree*.snapshot.js`) should not be modified or have issues flagged, as they are fixtures used to verify the generator's output and are intentionally preserved as-is.
Applied to files:
packages/solid-router/tests/hydrate.test.ts
📚 Learning: 2025-11-02T16:16:24.898Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5732
File: packages/start-client-core/src/client/hydrateStart.ts:6-9
Timestamp: 2025-11-02T16:16:24.898Z
Learning: In packages/start-client-core/src/client/hydrateStart.ts, the `import/no-duplicates` ESLint disable is necessary for imports from `#tanstack-router-entry` and `#tanstack-start-entry` because both aliases resolve to the same placeholder file (`fake-start-entry.js`) in package.json during static analysis, even though they resolve to different files at runtime.
Applied to files:
packages/solid-router/tests/hydrate.test.ts
🧬 Code graph analysis (1)
packages/solid-router/tests/hydrate.test.ts (1)
packages/router-core/src/ssr/ssr-client.ts (1)
TsrSsrGlobal(19-34)
🪛 ESLint
packages/solid-router/tests/hydrate.test.ts
[error] 4-11: ../src import should occur before type import of @tanstack/router-core/ssr/client
(import/order)
[error] 10-10: Member 'AnyRouteMatch' of the import declaration should be sorted alphabetically.
(sort-imports)
[error] 10-10: Prefer using a top-level type-only import instead of inline type specifiers.
(import/consistent-type-specifier-style)
⏰ 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). (1)
- GitHub Check: Test
🔇 Additional comments (4)
e2e/solid-start/basic/vite.config.ts (1)
23-23: LGTM! Correct exclusion of the new not-found route from prerendering.The addition of
'/not-found/via-head'to the prerender exclusion list is appropriate since this route throwsnotFound()in its head function. This is consistent with the exclusion of similar routes like/not-found/via-beforeLoadand/not-found/via-loader.packages/solid-router/tests/hydrate.test.ts (3)
56-215: Excellent test coverage for core hydration scenarios.These test cases comprehensively validate:
- Error handling for missing bootstrap data
- Serialization adapter initialization and mapping
- Manifest propagation
- Match hydration with correct data, status, and SSR flags
The test logic is sound, assertions are precise, and mock setup is appropriate for each scenario.
217-264: Strong validation of NotFound error handling during hydration.This test case directly addresses the PR objective by validating that:
- Errors thrown during route context hydration (specifically
notFound()fromhead()) are caught- The error is properly marked on the match as
{ isNotFound: true }- Diagnostic logging occurs via
console.errorThe test setup correctly simulates the error condition, and the assertions verify both the error capture and logging behavior. This is a critical test for ensuring graceful error handling during SSR hydration.
48-48: No issue found. TheisServer: trueflag is correct for hydration tests.The router is intentionally initialized in server mode because hydration tests simulate the real SSR→client flow: a server-rendered router (with
isServer: true) transitions to client-side hydration via thehydrate()function. The tests verify this hydration mechanism works correctly by mocking the dehydrated SSR state and confirming the hydration process succeeds. This setup is consistent across both React and Solid router test suites and is the standard pattern for SSR hydration testing.
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
♻️ Duplicate comments (1)
packages/react-router/tests/hydrate.test.ts (1)
13-55: Solid test harness; reconsiderisServer: truefor a client hydration scenario.The in-memory history, route tree, and window mocking look good and isolate the hydrate behavior nicely. The one question mark is:
- mockRouter = createRouter({ routeTree, history, isServer: true }) + mockRouter = createRouter({ routeTree, history }) + // or: createRouter({ routeTree, history, isServer: false })For SSR client hydration, the router is usually created in client mode; keeping
isServer: truemay hit server-only branches or subtly change behavior. If the intent is to simulate the browser-side hydrate, it’s worth double‑checking this flag (same concern as noted previously in the core hydrate tests).
🧹 Nitpick comments (2)
packages/solid-router/tests/hydrate.test.ts (2)
164-215: Consider clarifying the partial hydration scenario.The test sets up two
mockMatchesbut only onedehydratedMatch. While the test correctly verifies that the first match is hydrated, it's unclear whether the second match's behavior (presumably remaining unhydrated) is intentional or incidental. Consider adding an assertion or comment to clarify the expected behavior for routes without corresponding dehydrated data.Example assertion for the second match:
const secondMatch = mockMatches[1] as AnyRouteMatch expect(secondMatch.loaderData).toBeUndefined() expect(secondMatch.ssr).toBeUndefined()
13-265: Optional: Consider additional error scenario coverage.The current test suite provides excellent coverage of the happy path and NotFound errors. For even more robust error handling, consider adding tests for:
- Non-NotFound errors thrown during hydration (to verify they're handled differently)
- Multiple matches encountering errors simultaneously
- Errors from other route lifecycle hooks beyond
headThese additions would further strengthen confidence in the error handling, but may be deferred if they're outside the scope of this PR's NotFound focus.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/react-router/tests/hydrate.test.ts(1 hunks)packages/solid-router/tests/hydrate.test.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-10-08T08:11:47.088Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5402
File: packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts:19-21
Timestamp: 2025-10-08T08:11:47.088Z
Learning: Test snapshot files in the router-generator tests directory (e.g., files matching the pattern `packages/router-generator/tests/generator/**/routeTree*.snapshot.ts` or `routeTree*.snapshot.js`) should not be modified or have issues flagged, as they are fixtures used to verify the generator's output and are intentionally preserved as-is.
Applied to files:
packages/react-router/tests/hydrate.test.tspackages/solid-router/tests/hydrate.test.ts
📚 Learning: 2025-11-02T16:16:24.898Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5732
File: packages/start-client-core/src/client/hydrateStart.ts:6-9
Timestamp: 2025-11-02T16:16:24.898Z
Learning: In packages/start-client-core/src/client/hydrateStart.ts, the `import/no-duplicates` ESLint disable is necessary for imports from `#tanstack-router-entry` and `#tanstack-start-entry` because both aliases resolve to the same placeholder file (`fake-start-entry.js`) in package.json during static analysis, even though they resolve to different files at runtime.
Applied to files:
packages/react-router/tests/hydrate.test.tspackages/solid-router/tests/hydrate.test.ts
📚 Learning: 2025-10-01T18:30:26.591Z
Learnt from: schiller-manuel
Repo: TanStack/router PR: 5330
File: packages/router-core/src/router.ts:2231-2245
Timestamp: 2025-10-01T18:30:26.591Z
Learning: In `packages/router-core/src/router.ts`, the `resolveRedirect` method intentionally strips the router's origin from redirect URLs when they match (e.g., `https://foo.com/bar` → `/bar` for same-origin redirects) while preserving the full URL for cross-origin redirects. This logic should not be removed or simplified to use `location.publicHref` directly.
Applied to files:
packages/react-router/tests/hydrate.test.ts
📚 Learning: 2025-10-14T18:59:33.990Z
Learnt from: FatahChan
Repo: TanStack/router PR: 5475
File: e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-beforeLoad.tsx:8-0
Timestamp: 2025-10-14T18:59:33.990Z
Learning: In TanStack Router e2e test files, when a route parameter is validated at the route level (e.g., using zod in validateSearch or param validation), switch statements on that parameter do not require a default case, as the validation ensures only expected values will reach the switch.
Applied to files:
packages/solid-router/tests/hydrate.test.ts
🧬 Code graph analysis (2)
packages/react-router/tests/hydrate.test.ts (1)
packages/router-core/src/ssr/ssr-client.ts (1)
TsrSsrGlobal(19-34)
packages/solid-router/tests/hydrate.test.ts (1)
packages/router-core/src/ssr/ssr-client.ts (1)
TsrSsrGlobal(19-34)
⏰ 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
🔇 Additional comments (12)
packages/react-router/tests/hydrate.test.ts (7)
56-60: Good coverage for missing bootstrap data onwindow.$_TSR.This test clearly asserts that hydrate fails with the expected error when no SSR bootstrap data is present, which is an important guardrail.
62-74: Good negative test for missingwindow.$_TSR.router.The shape of the mocked
$ _TSRmatches the client contract, and the expectation on the specific error message for a missingrouterfield is precise and valuable.
76-118: Well-structured test for serialization adapter initialization and buffer flushing.The adapter mock matches the expected interface, the router is wired with
serializationAdapters, and the assertions thattis aMap, that it holdsfromSerializableunder the adapter key, that the buffer callbacks are invoked, and thatinitializedis set totruecollectively validate the key responsibilities of the hydrate path.
120-140: Clear behavior definition for the “no adapters” case.Verifying that
tremainsundefinedwhileinitializedstill flips totruedocuments the intended contract when no serialization adapters are configured, which will prevent regressions in this edge case.
142-162: Accurate assertion that manifest is surfaced viarouter.ssr.Seeding
window.$_TSR.router.manifestand asserting that hydrate stores it onmockRouter.ssr.manifestcleanly specifies how SSR metadata should be exposed on the client router. This is concise and correct.
164-215: Good verification that dehydrated match data is applied to route matches.The test neatly wires
matchRoutes, seeds dehydrated matches on$ _TSR.router.matches, and then asserts that the first match receivesloaderData,status, andssrfrom the dehydrated payload while preserving itsid. This gives strong confidence that per-match state hydration behaves as intended.
217-264: Effective test of NotFound handling during route context hydration.By having
headthrownotFound(), then asserting thatmatch.erroris normalized to{ isNotFound: true }and that a descriptiveconsole.errorlog is emitted with the routeId and error payload, this test nails the new error-handling behavior and ensures NotFound during hydration is captured locally instead of crashing the whole pipeline.packages/solid-router/tests/hydrate.test.ts (5)
56-74: LGTM! Comprehensive error guard tests.These tests properly verify that the hydration process fails fast with clear error messages when required bootstrap data is missing. The error messages appropriately guide users to file an issue, which is good for debugging unexpected scenarios.
76-140: LGTM! Thorough serialization adapter tests.Excellent coverage of both the standard initialization path (with adapters) and the edge case (empty adapters). The tests verify all key aspects: transformer map creation, adapter registration, buffer execution, and the initialized flag. The separation into two distinct test cases makes the intent clear.
142-162: LGTM! Clear manifest propagation test.This test correctly verifies that the manifest data from the dehydrated router state is properly transferred to the router's SSR property, which is essential for SSR/hydration coordination.
217-264: LGTM! Critical NotFound error handling test.This test directly addresses the core objective of this PR: catching and handling NotFound errors thrown during hydration. The test comprehensively verifies:
- Errors thrown from route
headfunctions are caught during hydration- The error is properly attached to the match with
isNotFound: true- Appropriate debugging information is logged to console.error
- The hydration process continues gracefully instead of crashing
The mock setup and cleanup are handled correctly, ensuring no side effects.
48-48: Need to check the actual test file more carefully to understand the hydrate function and verify the isServer flag usage. Let me search the codebase directly.Based on my verification through code analysis and web search, I found that the router incorrectly identifies whether it's running on server based on
documentavailability, andisServercan be explicitly set to control this. The test setup withisServer: trueis intentionally creating a server-side router context, which is then passed to thehydrate()function to simulate the SSR-to-client transition.Looking at the SSR documentation, on the client Router defaults to
createBrowserHistory, while on the server it usescreateMemoryHistory. The tests usecreateMemoryHistorywithisServer: true, which correctly simulates a server-rendered router that will be hydrated on the client. This is the correct pattern for testing hydration functionality—the router is created in server mode, then thehydrate()function processes the dehydrated state for client-side restoration.The
isServer: trueflag in the hydration tests is correct and intentional. It properly simulates the SSR server context before client-side hydration occurs. No changes are needed.
Summary by CodeRabbit
New Features
Bug Fixes
Tests
Chores