Skip to content

refactor(router-core): split loadMatches into its own file #4969

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

Merged
merged 7 commits into from
Aug 16, 2025

Conversation

Sheraff
Copy link
Contributor

@Sheraff Sheraff commented Aug 16, 2025

After the "flattening" PR #4961, we now have a very clear separate responsibility the Router class is handling: loading matches.

This PR proposes we split the code that handles "loading matches" into its own load-matches.ts file, with the entry points being

  • loadMatches
  • loadRouteChunk

In future PRs, we might consider splitting other responsibilities out of the 3k LoC router.ts file

Copy link

nx-cloud bot commented Aug 16, 2025

View your CI Pipeline Execution ↗ for commit 8497e35

Command Status Duration Result
nx affected --targets=test:eslint,test:unit,tes... ✅ Succeeded 4m 45s View ↗
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 1m 32s View ↗

☁️ Nx Cloud last updated this comment at 2025-08-16 17:27:51 UTC

Copy link

pkg-pr-new bot commented Aug 16, 2025

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/arktype-adapter@4969

@tanstack/directive-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/directive-functions-plugin@4969

@tanstack/eslint-plugin-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/eslint-plugin-router@4969

@tanstack/history

npm i https://pkg.pr.new/TanStack/router/@tanstack/history@4969

@tanstack/react-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router@4969

@tanstack/react-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-devtools@4969

@tanstack/react-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-ssr-query@4969

@tanstack/react-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start@4969

@tanstack/react-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-client@4969

@tanstack/react-start-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-plugin@4969

@tanstack/react-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-server@4969

@tanstack/router-cli

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-cli@4969

@tanstack/router-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-core@4969

@tanstack/router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools@4969

@tanstack/router-devtools-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools-core@4969

@tanstack/router-generator

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-generator@4969

@tanstack/router-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-plugin@4969

@tanstack/router-ssr-query-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-ssr-query-core@4969

@tanstack/router-utils

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-utils@4969

@tanstack/router-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-vite-plugin@4969

@tanstack/server-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/server-functions-plugin@4969

@tanstack/solid-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router@4969

@tanstack/solid-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-devtools@4969

@tanstack/solid-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start@4969

@tanstack/solid-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-client@4969

@tanstack/solid-start-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-plugin@4969

@tanstack/solid-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-server@4969

@tanstack/start-client-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-client-core@4969

@tanstack/start-plugin-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-plugin-core@4969

@tanstack/start-server-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-core@4969

@tanstack/start-server-functions-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-functions-client@4969

@tanstack/start-server-functions-fetcher

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-functions-fetcher@4969

@tanstack/start-server-functions-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-functions-server@4969

@tanstack/start-storage-context

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-storage-context@4969

@tanstack/valibot-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/valibot-adapter@4969

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/TanStack/router/@tanstack/virtual-file-routes@4969

@tanstack/zod-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/zod-adapter@4969

commit: 8497e35

coderabbitai[bot]

This comment was marked as off-topic.

@TanStack TanStack deleted a comment from coderabbitai bot Aug 16, 2025
Copy link

@coderabbitai coderabbitai bot left a 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/src/load-matches.ts (1)

279-310: Prevent stale/duplicate pending timers in setupPendingTimeout

If setupPendingTimeout is called more than once for the same match, the current guard will skip creating a new timer and leave any previously scheduled timer intact. This can lead to suboptimal UX if conditions change and we need to re-arm the pending timer. Clearing any existing timeout before assigning a new one ensures only a single, up-to-date timer per match.

Apply this diff:

 const match = inner.router.getMatch(matchId)!
-if (match._nonReactive.pendingTimeout !== undefined) return
@@
-  if (shouldPending) {
-    const pendingTimeout = setTimeout(() => {
-      // Update the match and prematurely resolve the loadMatches promise so that
-      // the pending component can start rendering
-      triggerOnReady(inner)
-    }, pendingMs)
-    match._nonReactive.pendingTimeout = pendingTimeout
-  }
+  if (shouldPending) {
+    // Clear any existing timeout before setting a new one
+    if (match._nonReactive.pendingTimeout !== undefined) {
+      clearTimeout(match._nonReactive.pendingTimeout)
+    }
+    match._nonReactive.pendingTimeout = setTimeout(() => {
+      // Update the match and prematurely resolve the loadMatches promise so that
+      // the pending component can start rendering
+      triggerOnReady(inner)
+    }, pendingMs)
+  }
🧹 Nitpick comments (2)
packages/router-core/src/load-matches.ts (2)

584-606: Guard against negative parent index when setting parentMatchPromise

When index is 0, inner.matchPromises[index - 1] is undefined by intent. Make that explicit to avoid accidental negative indexing if this code is ever adapted.

-  const parentMatchPromise = inner.matchPromises[index - 1] as any
+  const parentMatchPromise =
+    index > 0 ? inner.matchPromises[index - 1] : undefined

874-877: Avoid mutating the caller’s arg object in loadMatches

Object.assign(arg, …) mutates the incoming arg object. Prefer creating a fresh inner object for clarity and to avoid side effects.

-  const inner: InnerLoadContext = Object.assign(arg, {
-    matchPromises: [],
-  })
+  const inner: InnerLoadContext = {
+    ...arg,
+    matchPromises: [],
+  }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 78ef1c9 and 8497e35.

📒 Files selected for processing (3)
  • packages/router-core/src/index.ts (1 hunks)
  • packages/router-core/src/load-matches.ts (1 hunks)
  • packages/router-core/src/router.ts (4 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
packages/router-core/src/load-matches.ts (4)
packages/router-core/src/index.ts (15)
  • AnyRouter (213-213)
  • ParsedLocation (72-72)
  • UpdateMatchFn (239-239)
  • AnyRouteMatch (90-90)
  • NotFoundError (378-378)
  • isRedirect (373-373)
  • isNotFound (379-379)
  • AnyRoute (163-163)
  • rootRouteId (111-111)
  • createControlledPromise (279-279)
  • BeforeLoadContextOptions (181-181)
  • ControlledPromise (302-302)
  • LoaderFnContext (176-176)
  • MakeRouteMatch (89-89)
  • componentTypes (42-42)
packages/router-core/src/router.ts (2)
  • AnyRouter (692-692)
  • UpdateMatchFn (659-662)
packages/router-core/src/route.ts (1)
  • SsrContextOptions (945-964)
packages/router-core/src/utils.ts (1)
  • isPromise (477-485)
packages/router-core/src/router.ts (2)
packages/router-core/src/load-matches.ts (2)
  • loadMatches (865-915)
  • loadRouteChunk (917-956)
packages/router-core/src/Matches.ts (1)
  • AnyRouteMatch (239-239)
⏰ 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 (9)
packages/router-core/src/load-matches.ts (2)

917-956: loadRouteChunk return value can be undefined; ensure callers are OK with that

This function may return undefined (no components to preload), which is fine at runtime, but note that RouterCore’s LoadRouteChunkFn is typed as Promise<Array>. If you rely on that type elsewhere, it may be misleading.

  • Option A (typing only): Relax LoadRouteChunkFn to Promise<void | Array>.
  • Option B (behavioral): Always return a promise (e.g., route._componentsPromise ?? Promise.resolve()).

If you want to go with Option B, minimally adjust the final return:

-  return route._componentsPromise
+  return route._componentsPromise ?? Promise.resolve()

101-145: Redirect propagation looks correct; keep in mind error mutation

Setting err.options._fromLocation and redirectHandled before resolveRedirect ensures proper context and idempotency. The mutation of the err variable inside this block is intentional and safe given the isRedirect guard.

packages/router-core/src/index.ts (2)

42-43: Public re-export of componentTypes moved to load-matches

This aligns the public surface with the new module boundaries. Looks good.


42-43: No lingering componentTypes imports found

I ran the provided rg search across the repo and confirmed there are no remaining import/export statements pulling componentTypes from any path containing “router.” All references now resolve to ./load-matches, so no compatibility re-export or additional updates are needed.

packages/router-core/src/router.ts (5)

37-37: Delegation to load-matches module is clean and avoids runtime cycles

Importing loadMatches, loadRouteChunk, and routeNeedsPreload centralizes the loading lifecycle outside the RouterCore. Since load-matches only imports types from router, there’s no runtime circular dependency.


1890-1896: Passing updateMatch by reference is safe (arrow property preserves this binding)

RouterCore.updateMatch is defined as an arrow function property, so its this binding is preserved when passing it down. The onReady workflow is also properly awaited later.


664-665: LoadRouteChunkFn return type may be too specific

LoadRouteChunkFn is currently typed as Promise<Array>, but loadRouteChunk can resolve to undefined. Consider relaxing the type to Promise<void | Array> or Promise to reflect actual behavior and avoid confusing type expectations.

If you choose to relax the type, apply:

-export type LoadRouteChunkFn = (route: AnyRoute) => Promise<Array<void>>
+export type LoadRouteChunkFn = (route: AnyRoute) => Promise<void | Array<void>>

2218-2231: Preload updateMatch override preserves active match consistency

The conditional updater prevents overwriting active matches during preload, routing updates for inactive matches through the store. This matches the intended SWR semantics for preloading.


1248-1265: Using routeNeedsPreload at match creation preserves previous behavior

Initializing status to 'pending' when any loader/beforeLoad/lazy/preloadable component exists mirrors the old logic, now sourced from the external helper. Good separation.

@TanStack TanStack deleted a comment from coderabbitai bot Aug 16, 2025
@Sheraff Sheraff merged commit dbf89e5 into main Aug 16, 2025
6 checks passed
@Sheraff Sheraff deleted the refactor-router-core-split-load-matches-to-new-file branch August 16, 2025 17:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants