From 41f7bf32837e751b2874e7d827f7456f8a5bd716 Mon Sep 17 00:00:00 2001 From: Soheil Nikroo <62501144+soheilnikroo@users.noreply.github.com> Date: Sun, 12 Jul 2026 17:01:18 +0330 Subject: [PATCH 01/51] =?UTF-8?q?Update=20README=20for=20WorkOS=20example:?= =?UTF-8?q?=20change=20WORKOS=5FREDIRECT=5FURI=20to=20point=E2=80=A6=20(#7?= =?UTF-8?q?802)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update README for WorkOS example: change WORKOS_REDIRECT_URI to point to /api/auth/callback --- examples/react/start-workos/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/react/start-workos/README.md b/examples/react/start-workos/README.md index d6e222c7ba..52dde9410a 100644 --- a/examples/react/start-workos/README.md +++ b/examples/react/start-workos/README.md @@ -30,7 +30,7 @@ You will need a [WorkOS account](https://dashboard.workos.com/signup). WORKOS_CLIENT_ID= WORKOS_API_KEY= WORKOS_COOKIE_PASSWORD= - WORKOS_REDIRECT_URI=http://localhost:3000/callback + WORKOS_REDIRECT_URI=http://localhost:3000/api/auth/callback ``` `WORKOS_COOKIE_PASSWORD` is the private key used to encrypt the session cookie. It has to be at least 32 characters long. You can use the [1Password generator](https://1password.com/password-generator/) or the `openssl` library to generate a strong password via the command line: ```bash From e65f0c84c12010eb33b3f51b4034d2243e510e29 Mon Sep 17 00:00:00 2001 From: Manuel Schiller <6340397+schiller-manuel@users.noreply.github.com> Date: Mon, 13 Jul 2026 21:44:44 +0200 Subject: [PATCH 02/51] fix deps in example (#7809) --- examples/react/start-rscs/package.json | 2 +- pnpm-lock.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/react/start-rscs/package.json b/examples/react/start-rscs/package.json index 980f43eff9..4e6da2d402 100644 --- a/examples/react/start-rscs/package.json +++ b/examples/react/start-rscs/package.json @@ -27,7 +27,7 @@ "@types/react-dom": "^19.2.2", "@vitejs/plugin-react": "^6.0.1", "@vitejs/plugin-rsc": "^0.5.20", - "nitro": "npm:nitro-nightly@latest", + "nitro": "^3.0.260311-beta", "tailwindcss": "^4.1.18", "typescript": "^5.7.2", "vite": "^8.0.14" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 96847b4044..afd15b62bd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9809,8 +9809,8 @@ importers: specifier: ^0.5.20 version: 0.5.20(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) nitro: - specifier: npm:nitro-nightly@latest - version: nitro-nightly@4.0.0-20251010-091516-7cafddba(@electric-sql/pglite@0.3.2)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@4.0.3)(ioredis@5.9.2)(lru-cache@11.5.1)(mysql2@3.15.3)(rolldown@1.0.2)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) + specifier: ^3.0.260311-beta + version: 3.0.260311-beta(@electric-sql/pglite@0.3.2)(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(dotenv@17.4.2)(giget@2.0.0)(jiti@2.7.0)(lru-cache@11.5.1)(miniflare@4.20260317.0)(mysql2@3.15.3)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) tailwindcss: specifier: ^4.1.18 version: 4.2.2 From e2dd2049cb42eb219d3b447b8605066d19d9c1fa Mon Sep 17 00:00:00 2001 From: Manuel Schiller <6340397+schiller-manuel@users.noreply.github.com> Date: Mon, 13 Jul 2026 21:45:55 +0200 Subject: [PATCH 03/51] fix(router-core): handle window and element scroll restoration independently (#7807) --- .changeset/fix-independent-scroll-targets.md | 13 + .../scroll-restoration/src/routeTree.gen.ts | 71 +++++ .../scroll-restoration/src/router.tsx | 21 +- .../src/routes/(tests)/hash-scroll-about.tsx | 16 +- .../src/routes/(tests)/hash-scroll-repro.tsx | 46 ++++ .../src/routes/(tests)/issue-7687.detail.tsx | 16 ++ .../src/routes/(tests)/issue-7687.index.tsx | 46 ++++ .../src/routes/(tests)/issue-7687.tsx | 49 ++++ .../src/routes/(tests)/ssr-scroll-key.tsx | 17 ++ .../tests/hash-scroll-repro.spec.ts | 119 +++++++-- .../tests/issue-7687.spec.ts | 242 +++++++++++++++++ .../tests/ssr-scroll-key.spec.ts | 74 ++++++ packages/router-core/src/router.ts | 12 +- .../router-core/src/scroll-restoration.ts | 96 +++---- .../tests/scroll-restoration.test.ts | 249 +++++++++++++++++- 15 files changed, 997 insertions(+), 90 deletions(-) create mode 100644 .changeset/fix-independent-scroll-targets.md create mode 100644 e2e/react-start/scroll-restoration/src/routes/(tests)/issue-7687.detail.tsx create mode 100644 e2e/react-start/scroll-restoration/src/routes/(tests)/issue-7687.index.tsx create mode 100644 e2e/react-start/scroll-restoration/src/routes/(tests)/issue-7687.tsx create mode 100644 e2e/react-start/scroll-restoration/tests/issue-7687.spec.ts diff --git a/.changeset/fix-independent-scroll-targets.md b/.changeset/fix-independent-scroll-targets.md new file mode 100644 index 0000000000..d47294ee3e --- /dev/null +++ b/.changeset/fix-independent-scroll-targets.md @@ -0,0 +1,13 @@ +--- +'@tanstack/router-core': patch +--- + +fix(router-core): handle window and element scroll restoration independently + +Window and element scroll targets are now handled independently. Restoring one target no longer suppresses resets for other uncached configured targets, and a restored element is no longer reset when the window has no cached position. + +Hash navigation no longer resets elements configured through `scrollToTopSelectors` and retains precedence over stale window positions through destination invalidations. + +Scroll positions are sampled when leaving a route, preserving live changes made after the most recent scroll event. This also prevents client hydration from undoing nested positions restored by the SSR script. + +Fixes #7687. diff --git a/e2e/react-start/scroll-restoration/src/routeTree.gen.ts b/e2e/react-start/scroll-restoration/src/routeTree.gen.ts index 1df27a0424..7acedcb1d7 100644 --- a/e2e/react-start/scroll-restoration/src/routeTree.gen.ts +++ b/e2e/react-start/scroll-restoration/src/routeTree.gen.ts @@ -21,8 +21,11 @@ import { Route as testsNestedScrollSearchRouteImport } from './routes/(tests)/ne import { Route as testsNestedScrollCarryOverBRouteImport } from './routes/(tests)/nested-scroll-carry-over-b' import { Route as testsNestedScrollCarryOverARouteImport } from './routes/(tests)/nested-scroll-carry-over-a' import { Route as testsNestedScrollAwayRouteImport } from './routes/(tests)/nested-scroll-away' +import { Route as testsIssue7687RouteImport } from './routes/(tests)/issue-7687' import { Route as testsHashScrollReproRouteImport } from './routes/(tests)/hash-scroll-repro' import { Route as testsHashScrollAboutRouteImport } from './routes/(tests)/hash-scroll-about' +import { Route as testsIssue7687IndexRouteImport } from './routes/(tests)/issue-7687.index' +import { Route as testsIssue7687DetailRouteImport } from './routes/(tests)/issue-7687.detail' const IndexRoute = IndexRouteImport.update({ id: '/', @@ -86,6 +89,11 @@ const testsNestedScrollAwayRoute = testsNestedScrollAwayRouteImport.update({ path: '/nested-scroll-away', getParentRoute: () => rootRouteImport, } as any) +const testsIssue7687Route = testsIssue7687RouteImport.update({ + id: '/(tests)/issue-7687', + path: '/issue-7687', + getParentRoute: () => rootRouteImport, +} as any) const testsHashScrollReproRoute = testsHashScrollReproRouteImport.update({ id: '/(tests)/hash-scroll-repro', path: '/hash-scroll-repro', @@ -96,11 +104,22 @@ const testsHashScrollAboutRoute = testsHashScrollAboutRouteImport.update({ path: '/hash-scroll-about', getParentRoute: () => rootRouteImport, } as any) +const testsIssue7687IndexRoute = testsIssue7687IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => testsIssue7687Route, +} as any) +const testsIssue7687DetailRoute = testsIssue7687DetailRouteImport.update({ + id: '/detail', + path: '/detail', + getParentRoute: () => testsIssue7687Route, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/hash-scroll-about': typeof testsHashScrollAboutRoute '/hash-scroll-repro': typeof testsHashScrollReproRoute + '/issue-7687': typeof testsIssue7687RouteWithChildren '/nested-scroll-away': typeof testsNestedScrollAwayRoute '/nested-scroll-carry-over-a': typeof testsNestedScrollCarryOverARoute '/nested-scroll-carry-over-b': typeof testsNestedScrollCarryOverBRoute @@ -112,6 +131,8 @@ export interface FileRoutesByFullPath { '/ssr-scroll-key': typeof testsSsrScrollKeyRoute '/with-loader': typeof testsWithLoaderRoute '/with-search': typeof testsWithSearchRoute + '/issue-7687/detail': typeof testsIssue7687DetailRoute + '/issue-7687/': typeof testsIssue7687IndexRoute } export interface FileRoutesByTo { '/': typeof IndexRoute @@ -128,12 +149,15 @@ export interface FileRoutesByTo { '/ssr-scroll-key': typeof testsSsrScrollKeyRoute '/with-loader': typeof testsWithLoaderRoute '/with-search': typeof testsWithSearchRoute + '/issue-7687/detail': typeof testsIssue7687DetailRoute + '/issue-7687': typeof testsIssue7687IndexRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/(tests)/hash-scroll-about': typeof testsHashScrollAboutRoute '/(tests)/hash-scroll-repro': typeof testsHashScrollReproRoute + '/(tests)/issue-7687': typeof testsIssue7687RouteWithChildren '/(tests)/nested-scroll-away': typeof testsNestedScrollAwayRoute '/(tests)/nested-scroll-carry-over-a': typeof testsNestedScrollCarryOverARoute '/(tests)/nested-scroll-carry-over-b': typeof testsNestedScrollCarryOverBRoute @@ -145,6 +169,8 @@ export interface FileRoutesById { '/(tests)/ssr-scroll-key': typeof testsSsrScrollKeyRoute '/(tests)/with-loader': typeof testsWithLoaderRoute '/(tests)/with-search': typeof testsWithSearchRoute + '/(tests)/issue-7687/detail': typeof testsIssue7687DetailRoute + '/(tests)/issue-7687/': typeof testsIssue7687IndexRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath @@ -152,6 +178,7 @@ export interface FileRouteTypes { | '/' | '/hash-scroll-about' | '/hash-scroll-repro' + | '/issue-7687' | '/nested-scroll-away' | '/nested-scroll-carry-over-a' | '/nested-scroll-carry-over-b' @@ -163,6 +190,8 @@ export interface FileRouteTypes { | '/ssr-scroll-key' | '/with-loader' | '/with-search' + | '/issue-7687/detail' + | '/issue-7687/' fileRoutesByTo: FileRoutesByTo to: | '/' @@ -179,11 +208,14 @@ export interface FileRouteTypes { | '/ssr-scroll-key' | '/with-loader' | '/with-search' + | '/issue-7687/detail' + | '/issue-7687' id: | '__root__' | '/' | '/(tests)/hash-scroll-about' | '/(tests)/hash-scroll-repro' + | '/(tests)/issue-7687' | '/(tests)/nested-scroll-away' | '/(tests)/nested-scroll-carry-over-a' | '/(tests)/nested-scroll-carry-over-b' @@ -195,12 +227,15 @@ export interface FileRouteTypes { | '/(tests)/ssr-scroll-key' | '/(tests)/with-loader' | '/(tests)/with-search' + | '/(tests)/issue-7687/detail' + | '/(tests)/issue-7687/' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute testsHashScrollAboutRoute: typeof testsHashScrollAboutRoute testsHashScrollReproRoute: typeof testsHashScrollReproRoute + testsIssue7687Route: typeof testsIssue7687RouteWithChildren testsNestedScrollAwayRoute: typeof testsNestedScrollAwayRoute testsNestedScrollCarryOverARoute: typeof testsNestedScrollCarryOverARoute testsNestedScrollCarryOverBRoute: typeof testsNestedScrollCarryOverBRoute @@ -300,6 +335,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof testsNestedScrollAwayRouteImport parentRoute: typeof rootRouteImport } + '/(tests)/issue-7687': { + id: '/(tests)/issue-7687' + path: '/issue-7687' + fullPath: '/issue-7687' + preLoaderRoute: typeof testsIssue7687RouteImport + parentRoute: typeof rootRouteImport + } '/(tests)/hash-scroll-repro': { id: '/(tests)/hash-scroll-repro' path: '/hash-scroll-repro' @@ -314,13 +356,42 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof testsHashScrollAboutRouteImport parentRoute: typeof rootRouteImport } + '/(tests)/issue-7687/': { + id: '/(tests)/issue-7687/' + path: '/' + fullPath: '/issue-7687/' + preLoaderRoute: typeof testsIssue7687IndexRouteImport + parentRoute: typeof testsIssue7687Route + } + '/(tests)/issue-7687/detail': { + id: '/(tests)/issue-7687/detail' + path: '/detail' + fullPath: '/issue-7687/detail' + preLoaderRoute: typeof testsIssue7687DetailRouteImport + parentRoute: typeof testsIssue7687Route + } } } +interface testsIssue7687RouteChildren { + testsIssue7687DetailRoute: typeof testsIssue7687DetailRoute + testsIssue7687IndexRoute: typeof testsIssue7687IndexRoute +} + +const testsIssue7687RouteChildren: testsIssue7687RouteChildren = { + testsIssue7687DetailRoute: testsIssue7687DetailRoute, + testsIssue7687IndexRoute: testsIssue7687IndexRoute, +} + +const testsIssue7687RouteWithChildren = testsIssue7687Route._addFileChildren( + testsIssue7687RouteChildren, +) + const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, testsHashScrollAboutRoute: testsHashScrollAboutRoute, testsHashScrollReproRoute: testsHashScrollReproRoute, + testsIssue7687Route: testsIssue7687RouteWithChildren, testsNestedScrollAwayRoute: testsNestedScrollAwayRoute, testsNestedScrollCarryOverARoute: testsNestedScrollCarryOverARoute, testsNestedScrollCarryOverBRoute: testsNestedScrollCarryOverBRoute, diff --git a/e2e/react-start/scroll-restoration/src/router.tsx b/e2e/react-start/scroll-restoration/src/router.tsx index eb81f5bfa9..27d68369db 100644 --- a/e2e/react-start/scroll-restoration/src/router.tsx +++ b/e2e/react-start/scroll-restoration/src/router.tsx @@ -7,13 +7,32 @@ export function getRouter() { const router = createRouter({ routeTree, scrollRestoration: true, - scrollToTopSelectors: ['[data-scroll-restoration-id="carry-over-reset"]'], + scrollToTopSelectors: [ + '[data-scroll-restoration-id="carry-over-reset"]', + '#hash-scroll-nested', + '#hash-scroll-reset-target', + '#issue-7687-reset-probe', + '#issue-7687-scroller', + () => + typeof document === 'undefined' + ? undefined + : document.getElementById('issue-7687-stale-selector'), + '#ssr-scroll-key-nested', + ], getScrollRestorationKey: (location) => { if (location.pathname === '/ssr-scroll-key') { return 'ssr-scroll-key' } if (location.pathname === '/hash-scroll-repro') { + const scrollKey = + 'scrollKey' in location.search ? location.search.scrollKey : undefined + return typeof scrollKey === 'string' + ? `${location.pathname}:${scrollKey}` + : location.pathname + } + + if (location.pathname.startsWith('/issue-7687')) { return location.pathname } diff --git a/e2e/react-start/scroll-restoration/src/routes/(tests)/hash-scroll-about.tsx b/e2e/react-start/scroll-restoration/src/routes/(tests)/hash-scroll-about.tsx index 2c2748e6a5..1ee24f4b1f 100644 --- a/e2e/react-start/scroll-restoration/src/routes/(tests)/hash-scroll-about.tsx +++ b/e2e/react-start/scroll-restoration/src/routes/(tests)/hash-scroll-about.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { createFileRoute } from '@tanstack/react-router' +import { Link, createFileRoute } from '@tanstack/react-router' import { sleep } from '~/utils/posts' export const Route = createFileRoute('/(tests)/hash-scroll-about')({ @@ -17,6 +17,20 @@ function Component() {

This route exists so hover preloading can reproduce the hash scroll bug.

+ + Return to #one + + + Return to the destination key + ) } diff --git a/e2e/react-start/scroll-restoration/src/routes/(tests)/hash-scroll-repro.tsx b/e2e/react-start/scroll-restoration/src/routes/(tests)/hash-scroll-repro.tsx index 1017b63bc3..c2c681247a 100644 --- a/e2e/react-start/scroll-restoration/src/routes/(tests)/hash-scroll-repro.tsx +++ b/e2e/react-start/scroll-restoration/src/routes/(tests)/hash-scroll-repro.tsx @@ -1,8 +1,13 @@ import * as React from 'react' import { Link, createFileRoute, useRouter } from '@tanstack/react-router' +import { z } from 'zod' import { sleep } from '~/utils/posts' export const Route = createFileRoute('/(tests)/hash-scroll-repro')({ + validateSearch: z.object({ + scrollKey: z.string().optional(), + invalidateOnMount: z.boolean().optional(), + }), loader: async () => { await sleep(50) return null @@ -14,7 +19,20 @@ const sectionIds = ['one', 'two', 'three', 'four', 'five'] as const function Component() { const router = useRouter() + const { invalidateOnMount } = Route.useSearch() const [invalidateCount, setInvalidateCount] = React.useState(0) + const invalidatedOnMount = React.useRef(false) + + React.useLayoutEffect(() => { + if (!invalidateOnMount || invalidatedOnMount.current) { + return + } + + invalidatedOnMount.current = true + void router.invalidate().then(() => { + setInvalidateCount((count) => count + 1) + }) + }, [invalidateOnMount, router]) return (
@@ -61,6 +79,24 @@ function Component() { > #four no reset + + #one different key + + + #one and invalidate in layout effect +
@@ -74,6 +110,16 @@ function Component() { ))} +
+ {Array.from({ length: 20 }).map((_, i) => ( +
Hash reset target row {i}
+ ))} +
+
{sectionIds.map((sectionId) => (
+

issue-7687-detail

+ {Array.from({ length: 40 }).map((_, i) => ( +
Detail row {i}
+ ))} +
+ ) +} diff --git a/e2e/react-start/scroll-restoration/src/routes/(tests)/issue-7687.index.tsx b/e2e/react-start/scroll-restoration/src/routes/(tests)/issue-7687.index.tsx new file mode 100644 index 0000000000..82450e43f3 --- /dev/null +++ b/e2e/react-start/scroll-restoration/src/routes/(tests)/issue-7687.index.tsx @@ -0,0 +1,46 @@ +import * as React from 'react' +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/(tests)/issue-7687/')({ + component: Component, +}) + +const visitKey = 'issue-7687-list-visits' + +function ResetProbe() { + const ref = React.useRef(null) + + React.useLayoutEffect(() => { + const visits = Number(window.sessionStorage.getItem(visitKey) ?? 0) + window.sessionStorage.setItem(visitKey, String(visits + 1)) + + if (visits > 0 && ref.current) { + ref.current.scrollTop = 80 + } + }, []) + + return ( +
+ {Array.from({ length: 20 }).map((_, i) => ( +
Reset probe row {i}
+ ))} +
+ ) +} + +function Component() { + return ( +
+

issue-7687-list

+ + {Array.from({ length: 40 }).map((_, i) => ( +
List row {i}
+ ))} +
+ ) +} diff --git a/e2e/react-start/scroll-restoration/src/routes/(tests)/issue-7687.tsx b/e2e/react-start/scroll-restoration/src/routes/(tests)/issue-7687.tsx new file mode 100644 index 0000000000..f7091cd829 --- /dev/null +++ b/e2e/react-start/scroll-restoration/src/routes/(tests)/issue-7687.tsx @@ -0,0 +1,49 @@ +import { Link, Outlet, createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/(tests)/issue-7687')({ + component: Component, +}) + +function Component() { + return ( +
+ + +
+ +
+ +
+ {Array.from({ length: 20 }).map((_, i) => ( +
Stale selector row {i}
+ ))} +
+ +
+
+ ) +} diff --git a/e2e/react-start/scroll-restoration/src/routes/(tests)/ssr-scroll-key.tsx b/e2e/react-start/scroll-restoration/src/routes/(tests)/ssr-scroll-key.tsx index e224ed9137..dd7375660b 100644 --- a/e2e/react-start/scroll-restoration/src/routes/(tests)/ssr-scroll-key.tsx +++ b/e2e/react-start/scroll-restoration/src/routes/(tests)/ssr-scroll-key.tsx @@ -7,9 +7,26 @@ export const Route = createFileRoute('/(tests)/ssr-scroll-key')({ }) function Component() { + const [hydrated, setHydrated] = React.useState(false) + + React.useEffect(() => { + setHydrated(true) + }, []) + return (

ssr-scroll-key

+ {hydrated ? : null} +
+ {Array.from({ length: 20 }).map((_, i) => ( +
Nested SSR row {i}
+ ))} +
) diff --git a/e2e/react-start/scroll-restoration/tests/hash-scroll-repro.spec.ts b/e2e/react-start/scroll-restoration/tests/hash-scroll-repro.spec.ts index d203ea8c26..7a6aa72161 100644 --- a/e2e/react-start/scroll-restoration/tests/hash-scroll-repro.spec.ts +++ b/e2e/react-start/scroll-restoration/tests/hash-scroll-repro.spec.ts @@ -103,43 +103,59 @@ test('hash navigation wins over stale same-tab scroll restoration entries', asyn expect(scrollYAfterHashNavigation).toBeLessThan(staleScrollY) }) -test('hash navigation still runs when only nested scroll entries restore', async ({ +test('hash navigation wins when the destination invalidates in a layout effect', async ({ page, }) => { await goToRepro(page) + const staleScrollY = await scrollUpFromHashTarget(page) - const nestedScrollTop = await page.evaluate(() => { - const nested = document.querySelector('[data-testid="hash-scroll-nested"]') - if (!(nested instanceof HTMLElement)) { - throw new Error('Missing nested scroller') - } + await page.reload() + await page.waitForLoadState('networkidle') + await expect( + page.getByTestId('hash-scroll-repro-invalidate-count'), + ).toHaveText('Invalidate count: 0') + + await page.getByTestId('hash-scroll-layout-invalidate-link').click() + await expect(page).toHaveURL(/invalidateOnMount=true#one$/) + await expect( + page.getByTestId('hash-scroll-repro-invalidate-count'), + ).toHaveText('Invalidate count: 1') + + await expect(page.getByTestId('hash-scroll-section-one')).toBeInViewport() + await expect( + page.getByTestId('hash-scroll-section-five'), + ).not.toBeInViewport() - nested.scrollTop = 80 - window.dispatchEvent(new PageTransitionEvent('pagehide')) - return nested.scrollTop + const scrollYAfterHashNavigation = await page.evaluate(() => window.scrollY) + expect(scrollYAfterHashNavigation).toBeLessThan(staleScrollY) +}) + +test('hash navigation still runs when a configured nested target restores', async ({ + page, +}) => { + await goToRepro(page) + + const nested = page.getByTestId('hash-scroll-nested') + const cachedScrollTop = await nested.evaluate(async (element) => { + element.scrollTop = 80 + element.dispatchEvent(new Event('scroll', { bubbles: true })) + await new Promise((resolve) => requestAnimationFrame(resolve)) + return element.scrollTop }) - expect(nestedScrollTop).toBeGreaterThan(0) + expect(cachedScrollTop).toBeGreaterThan(0) - await page.reload() - await page.waitForLoadState('networkidle') + await page.getByTestId('hash-scroll-about-link').click() await expect( - page.getByTestId('hash-scroll-repro-invalidate-count'), + page.getByRole('heading', { name: 'hash-scroll-about' }), ).toBeVisible() - await page.getByTestId('hash-scroll-section-one-link').click() + await page.getByTestId('hash-scroll-restore-link').click() await expect(page.getByTestId('hash-scroll-section-one')).toBeInViewport() await expect - .poll(async () => { - return page.evaluate(() => { - const nested = document.querySelector( - '[data-testid="hash-scroll-nested"]', - ) - return nested instanceof HTMLElement ? nested.scrollTop : 0 - }) - }) - .toBe(nestedScrollTop) + .poll(async () => nested.evaluate((element) => element.scrollTop)) + .toBe(cachedScrollTop) }) test('hash navigation scrolls when resetScroll is false', async ({ page }) => { @@ -157,3 +173,60 @@ test('hash navigation scrolls when resetScroll is false', async ({ page }) => { .poll(async () => page.evaluate(() => window.scrollY)) .toBeGreaterThan(scrollYBeforeHashNavigation) }) + +test('hash navigation does not reset configured nested targets', async ({ + page, +}) => { + await goToRepro(page) + + const resetTarget = page.getByTestId('hash-scroll-reset-target') + const scrollTop = await resetTarget.evaluate((element) => { + element.scrollTop = 80 + return element.scrollTop + }) + expect(scrollTop).toBeGreaterThan(0) + + await page.getByTestId('hash-scroll-section-one-link').click() + await expect(page).toHaveURL(/#one$/) + await expect(page.getByTestId('hash-scroll-section-one')).toBeInViewport() + await expect + .poll(async () => resetTarget.evaluate((el) => el.scrollTop)) + .toBe(scrollTop) +}) + +test('hash navigation does not carry configured targets into a different key', async ({ + page, +}) => { + await goToRepro(page) + + const resetTarget = page.getByTestId('hash-scroll-reset-target') + const sourceScrollTop = await resetTarget.evaluate(async (element) => { + element.scrollTop = 80 + element.dispatchEvent(new Event('scroll', { bubbles: true })) + await new Promise((resolve) => requestAnimationFrame(resolve)) + return element.scrollTop + }) + expect(sourceScrollTop).toBeGreaterThan(0) + + await page.getByTestId('hash-scroll-different-key-link').click() + await expect(page).toHaveURL(/scrollKey=destination#one$/) + await expect(page.getByTestId('hash-scroll-section-one')).toBeInViewport() + await expect + .poll(async () => resetTarget.evaluate((element) => element.scrollTop)) + .toBe(sourceScrollTop) + + await page.getByTestId('hash-scroll-about-link').click() + await expect( + page.getByRole('heading', { name: 'hash-scroll-about' }), + ).toBeVisible() + + await page.getByTestId('hash-scroll-destination-link').click() + await expect(page).toHaveURL(/scrollKey=destination$/) + await expect + .poll(async () => + page + .getByTestId('hash-scroll-reset-target') + .evaluate((element) => element.scrollTop), + ) + .toBe(0) +}) diff --git a/e2e/react-start/scroll-restoration/tests/issue-7687.spec.ts b/e2e/react-start/scroll-restoration/tests/issue-7687.spec.ts new file mode 100644 index 0000000000..85a5bd5d43 --- /dev/null +++ b/e2e/react-start/scroll-restoration/tests/issue-7687.spec.ts @@ -0,0 +1,242 @@ +import { expect, test } from '@playwright/test' +import type { Page } from '@playwright/test' + +async function goToList(page: Page) { + await page.goto('/issue-7687') + await page.waitForLoadState('networkidle') + await expect( + page.getByRole('heading', { name: 'issue-7687-list' }), + ).toBeVisible() +} + +test('restores a configured nested target independently from the window', async ({ + page, +}) => { + await goToList(page) + + const scroller = page.getByTestId('issue-7687-scroller') + const scrollTop = await scroller.evaluate((element) => { + element.scrollTop = 400 + element.dispatchEvent(new Event('scroll', { bubbles: true })) + return element.scrollTop + }) + + expect(scrollTop).toBeGreaterThan(0) + expect(await page.evaluate(() => window.scrollY)).toBe(0) + + await page.getByTestId('issue-7687-detail-link').click() + await expect( + page.getByRole('heading', { name: 'issue-7687-detail' }), + ).toBeVisible() + await expect.poll(async () => scroller.evaluate((el) => el.scrollTop)).toBe(0) + + const outgoingWindowScrollY = await page.evaluate(() => { + window.scrollTo(0, 600) + window.dispatchEvent(new Event('scroll')) + return window.scrollY + }) + expect(outgoingWindowScrollY).toBeGreaterThan(0) + + await page.getByTestId('issue-7687-list-link').click() + await expect( + page.getByRole('heading', { name: 'issue-7687-list' }), + ).toBeVisible() + + await expect + .poll(async () => scroller.evaluate((el) => el.scrollTop)) + .toBe(scrollTop) + await expect.poll(async () => page.evaluate(() => window.scrollY)).toBe(0) + await expect + .poll(async () => + page.getByTestId('issue-7687-reset-probe').evaluate((el) => el.scrollTop), + ) + .toBe(0) +}) + +test('resets an uncached configured target when the window restores', async ({ + page, +}) => { + await goToList(page) + + const windowScrollY = await page.evaluate(() => { + window.scrollTo(0, 600) + window.dispatchEvent(new Event('scroll')) + return window.scrollY + }) + expect(windowScrollY).toBeGreaterThan(0) + + await page.getByTestId('issue-7687-detail-link').click() + await expect( + page.getByRole('heading', { name: 'issue-7687-detail' }), + ).toBeVisible() + await expect.poll(async () => page.evaluate(() => window.scrollY)).toBe(0) + + await page.getByTestId('issue-7687-list-link').click() + await expect( + page.getByRole('heading', { name: 'issue-7687-list' }), + ).toBeVisible() + + await expect + .poll(async () => page.evaluate(() => window.scrollY)) + .toBe(windowScrollY) + await expect + .poll(async () => + page.getByTestId('issue-7687-reset-probe').evaluate((el) => el.scrollTop), + ) + .toBe(0) +}) + +test('restores and resets nested targets independently on browser back', async ({ + page, +}) => { + await goToList(page) + + const cachedScrollTop = await page + .getByTestId('issue-7687-scroller') + .evaluate(async (element) => { + element.scrollTop = 400 + element.dispatchEvent(new Event('scroll', { bubbles: true })) + await new Promise((resolve) => requestAnimationFrame(resolve)) + return element.scrollTop + }) + expect(cachedScrollTop).toBeGreaterThan(0) + + await page.getByTestId('issue-7687-detail-link').click() + await expect( + page.getByRole('heading', { name: 'issue-7687-detail' }), + ).toBeVisible() + + await page.goBack() + await expect( + page.getByRole('heading', { name: 'issue-7687-list' }), + ).toBeVisible() + + await expect + .poll(async () => + page + .getByTestId('issue-7687-scroller') + .evaluate((element) => element.scrollTop), + ) + .toBe(cachedScrollTop) + await expect + .poll(async () => + page + .getByTestId('issue-7687-reset-probe') + .evaluate((element) => element.scrollTop), + ) + .toBe(0) +}) + +test('resets a live configured target when its cached selector becomes stale', async ({ + page, +}) => { + await goToList(page) + + const target = page.getByTestId('issue-7687-stale-selector') + const cachedScrollTop = await target.evaluate(async (element) => { + element.scrollTop = 80 + element.dispatchEvent(new Event('scroll', { bubbles: true })) + await new Promise((resolve) => requestAnimationFrame(resolve)) + return element.scrollTop + }) + expect(cachedScrollTop).toBeGreaterThan(0) + + await page.getByTestId('issue-7687-detail-link').click() + await expect( + page.getByRole('heading', { name: 'issue-7687-detail' }), + ).toBeVisible() + await expect.poll(async () => target.evaluate((el) => el.scrollTop)).toBe(0) + + const outgoingScrollTop = await target.evaluate(async (element) => { + element.dataset.scrollRestorationId = 'issue-7687-stale-current' + element.scrollTop = 40 + element.dispatchEvent(new Event('scroll', { bubbles: true })) + await new Promise((resolve) => requestAnimationFrame(resolve)) + return element.scrollTop + }) + expect(outgoingScrollTop).toBeGreaterThan(0) + + await page.getByTestId('issue-7687-list-link').click() + await expect( + page.getByRole('heading', { name: 'issue-7687-list' }), + ).toBeVisible() + + await expect.poll(async () => target.evaluate((el) => el.scrollTop)).toBe(0) +}) + +test('resetScroll false preserves window and configured nested targets', async ({ + page, +}) => { + await goToList(page) + + const positions = await page.evaluate(() => { + const scroller = document.querySelector( + '[data-testid="issue-7687-scroller"]', + ) + if (!(scroller instanceof HTMLElement)) { + throw new Error('Missing issue-7687 scroller') + } + + scroller.scrollTop = 400 + scroller.dispatchEvent(new Event('scroll', { bubbles: true })) + window.scrollTo(0, 600) + window.dispatchEvent(new Event('scroll')) + + return { nested: scroller.scrollTop, window: window.scrollY } + }) + + expect(positions.nested).toBeGreaterThan(0) + expect(positions.window).toBeGreaterThan(0) + + await page.getByTestId('issue-7687-detail-no-reset-link').click() + await expect( + page.getByRole('heading', { name: 'issue-7687-detail' }), + ).toBeVisible() + + await expect + .poll(async () => page.evaluate(() => window.scrollY)) + .toBe(positions.window) + await expect + .poll(async () => + page.getByTestId('issue-7687-scroller').evaluate((el) => el.scrollTop), + ) + .toBe(positions.nested) + + await page.getByTestId('issue-7687-list-link').click() + await expect( + page.getByRole('heading', { name: 'issue-7687-list' }), + ).toBeVisible() + + const changedPositions = await page.evaluate(async () => { + const scroller = document.querySelector( + '[data-testid="issue-7687-scroller"]', + ) + if (!(scroller instanceof HTMLElement)) { + throw new Error('Missing issue-7687 scroller') + } + + scroller.scrollTop = 200 + scroller.dispatchEvent(new Event('scroll', { bubbles: true })) + window.scrollTo(0, 300) + window.dispatchEvent(new Event('scroll')) + await new Promise((resolve) => requestAnimationFrame(resolve)) + + return { nested: scroller.scrollTop, window: window.scrollY } + }) + + expect(changedPositions.nested).not.toBe(positions.nested) + expect(changedPositions.window).not.toBe(positions.window) + + await page.getByTestId('issue-7687-detail-link').click() + await expect( + page.getByRole('heading', { name: 'issue-7687-detail' }), + ).toBeVisible() + await expect + .poll(async () => page.evaluate(() => window.scrollY)) + .toBe(positions.window) + await expect + .poll(async () => + page.getByTestId('issue-7687-scroller').evaluate((el) => el.scrollTop), + ) + .toBe(positions.nested) +}) diff --git a/e2e/react-start/scroll-restoration/tests/ssr-scroll-key.spec.ts b/e2e/react-start/scroll-restoration/tests/ssr-scroll-key.spec.ts index 737695a0cf..eb61a279d2 100644 --- a/e2e/react-start/scroll-restoration/tests/ssr-scroll-key.spec.ts +++ b/e2e/react-start/scroll-restoration/tests/ssr-scroll-key.spec.ts @@ -38,3 +38,77 @@ test('SSR scroll restoration uses a custom restoration key', async ({ .poll(async () => page.evaluate(() => window.scrollY)) .toBe(customKeyScrollY) }) + +test('SSR restores a configured nested target without client JavaScript', async ({ + page, +}) => { + const nestedScrollTop = 80 + + await page.addInitScript( + ({ nestedScrollTop, storageKey }) => { + window.sessionStorage.setItem( + storageKey, + JSON.stringify({ + 'ssr-scroll-key': { + '[data-scroll-restoration-id="ssr-scroll-key-nested"]': { + scrollX: 0, + scrollY: nestedScrollTop, + }, + }, + }), + ) + }, + { nestedScrollTop, storageKey }, + ) + await page.route(/\/assets\/.*\.js$/, (route) => route.abort()) + + await page.goto('/ssr-scroll-key', { waitUntil: 'domcontentloaded' }) + await expect( + page.getByRole('heading', { name: 'ssr-scroll-key' }), + ).toBeVisible() + await expect(page.getByTestId('ssr-scroll-key-hydrated')).toHaveCount(0) + await expect + .poll(async () => + page + .getByTestId('ssr-scroll-key-nested') + .evaluate((element) => element.scrollTop), + ) + .toBe(nestedScrollTop) +}) + +test('hydration does not reset a configured nested target restored by SSR', async ({ + page, +}) => { + const nestedScrollTop = 80 + + await page.addInitScript( + ({ nestedScrollTop, storageKey }) => { + window.sessionStorage.setItem( + storageKey, + JSON.stringify({ + 'ssr-scroll-key': { + '[data-scroll-restoration-id="ssr-scroll-key-nested"]': { + scrollX: 0, + scrollY: nestedScrollTop, + }, + }, + }), + ) + }, + { nestedScrollTop, storageKey }, + ) + + await page.goto('/ssr-scroll-key') + await page.waitForLoadState('networkidle') + await expect( + page.getByRole('heading', { name: 'ssr-scroll-key' }), + ).toBeVisible() + await expect(page.getByTestId('ssr-scroll-key-hydrated')).toBeAttached() + await expect + .poll(async () => + page + .getByTestId('ssr-scroll-key-nested') + .evaluate((element) => element.scrollTop), + ) + .toBe(nestedScrollTop) +}) diff --git a/packages/router-core/src/router.ts b/packages/router-core/src/router.ts index 2197dab737..df9251a739 100644 --- a/packages/router-core/src/router.ts +++ b/packages/router-core/src/router.ts @@ -904,11 +904,6 @@ export function getLocationChangeInfo( return { fromLocation, toLocation, pathChanged, hrefChanged, hashChanged } } -export const locationHistoryActions = new WeakMap< - ParsedLocation, - HistoryAction ->() - type LightweightRouteMatchResult = { matchedRoutes: ReadonlyArray fullPath: string @@ -978,6 +973,8 @@ export class RouterCore< )}` _scroll: { next: boolean + // True until the current PUSH/REPLACE renders, so its hash owns window scroll. + hash?: boolean restoring?: boolean restoration?: boolean reset?: boolean @@ -2475,9 +2472,8 @@ export class RouterCore< try { this.beforeLoad() if (historyAction) { - locationHistoryActions.set(this.latestLocation, historyAction) - } else { - locationHistoryActions.delete(this.latestLocation) + this._scroll.hash = + historyAction === 'PUSH' || historyAction === 'REPLACE' } const next = this.latestLocation const prevLocation = this.stores.resolvedLocation.get() diff --git a/packages/router-core/src/scroll-restoration.ts b/packages/router-core/src/scroll-restoration.ts index 6287a6fce0..5e4ae0d3ee 100644 --- a/packages/router-core/src/scroll-restoration.ts +++ b/packages/router-core/src/scroll-restoration.ts @@ -1,5 +1,4 @@ import { isServer } from '@tanstack/router-core/isServer' -import { locationHistoryActions } from './router' import type { AnyRouter } from './router' import type { ParsedLocation } from './location' @@ -133,7 +132,6 @@ export function getElementScrollRestorationEntry( let ignoreScroll = false const windowScrollTarget = 'window' -type ScrollTarget = typeof windowScrollTarget | Element function getElement(selector: string | (() => Element | null | undefined)) { try { @@ -148,8 +146,8 @@ function getScrollToTopElements( scrollToTopSelectors: NonNullable< AnyRouter['options']['scrollToTopSelectors'] >, -): Array { - const elements: Array = [] +) { + const elements = new Set() for (const selector of scrollToTopSelectors) { if (selector === windowScrollTarget) { @@ -158,7 +156,7 @@ function getScrollToTopElements( const element = getElement(selector) if (element) { - elements.push(element) + elements.add(element) } } @@ -180,46 +178,21 @@ export function setupScrollRestoration(router: AnyRouter, force?: boolean) { const getKey = router.options.getScrollRestorationKey || defaultGetScrollRestorationKey - const trackedScrollEntries = new Map() - const setTrackedScrollEntry = ( - target: ScrollTarget, - scrollX: number, - scrollY: number, - ) => { - const entry = - trackedScrollEntries.get(target) || ({} as ScrollRestorationEntry) - entry.scrollX = scrollX - entry.scrollY = scrollY - trackedScrollEntries.set(target, entry) - } - - const onScroll = (event: Event) => { - if (ignoreScroll || !scroll.restoring) { - return - } - - if (event.target === document) { - setTrackedScrollEntry(windowScrollTarget, scrollX, scrollY) - } else { - const target = event.target as Element - setTrackedScrollEntry(target, target.scrollLeft, target.scrollTop) - } - } + const trackedScrollTargets = new Set() // Snapshot the current page's tracked scroll targets before navigation or unload. const snapshotCurrentScrollTargets = (restoreKey: string) => { - if (!scroll.restoring) { - return - } - const keyEntry = (scrollRestorationCache[restoreKey] ||= {} as ScrollRestorationByElement) - for (const [target, position] of trackedScrollEntries) { - if (target === windowScrollTarget) { - keyEntry[windowScrollTarget] = position - } else if (target.isConnected) { - keyEntry[getScrollRestorationSelector(target)] = position + for (const target of trackedScrollTargets) { + if (target === document) { + keyEntry[windowScrollTarget] = { scrollX, scrollY } + } else if ((target as Element).isConnected) { + keyEntry[getScrollRestorationSelector(target as Element)] = { + scrollX: (target as Element).scrollLeft, + scrollY: (target as Element).scrollTop, + } } } } @@ -230,12 +203,21 @@ export function setupScrollRestoration(router: AnyRouter, force?: boolean) { history.scrollRestoration = 'manual' - document.addEventListener('scroll', onScroll, true) + document.addEventListener( + 'scroll', + (event) => { + if (ignoreScroll) { + return + } + trackedScrollTargets.add(event.target as Document | Element) + }, + true, + ) router.subscribe('onBeforeLoad', (event) => { if (event.fromLocation) { snapshotCurrentScrollTargets(getKey(event.fromLocation)) } - trackedScrollEntries.clear() + trackedScrollTargets.clear() }) addEventListener('pagehide', () => { snapshotCurrentScrollTargets( @@ -258,12 +240,11 @@ export function setupScrollRestoration(router: AnyRouter, force?: boolean) { const behavior = router.options.scrollRestorationBehavior const scrollToTopSelectors = router.options.scrollToTopSelectors const shouldResetScroll = scroll.next - let scrollToTopElements: Array | undefined - trackedScrollEntries.clear() - - if (!shouldResetScroll) { - scroll.next = true - } + const hashNavigation = scroll.hash + let scrollToTopElements: Set | undefined + trackedScrollTargets.clear() + scroll.next = true + scroll.hash = false if ( typeof router.options.scrollRestoration === 'function' && @@ -295,7 +276,7 @@ export function setupScrollRestoration(router: AnyRouter, force?: boolean) { if (shouldResetScroll && scrollToTopSelectors) { scrollToTopElements ??= getScrollToTopElements(scrollToTopSelectors) - if (scrollToTopElements.includes(element)) { + if (scrollToTopElements.has(element)) { continue } } @@ -321,11 +302,12 @@ export function setupScrollRestoration(router: AnyRouter, force?: boolean) { let windowRestored = false if (shouldResetScroll) { - const action = locationHistoryActions.get(event.toLocation) + if (!hash && scrollToTopSelectors) { + scrollToTopElements ??= getScrollToTopElements(scrollToTopSelectors) + } + const skipWindowRestore = - hash && - hashScrollIntoViewOptions && - (action === 'PUSH' || action === 'REPLACE') + hash && hashScrollIntoViewOptions && hashNavigation const elementEntries = scroll.restoring ? scrollRestorationCache[cacheKey] @@ -351,21 +333,23 @@ export function setupScrollRestoration(router: AnyRouter, force?: boolean) { if (element) { element.scrollLeft = scrollX element.scrollTop = scrollY + scrollToTopElements?.delete(element) } } } } - if (!windowRestored && !hash) { + if (!hash) { const scrollOptions = { top: 0, left: 0, behavior, } - scrollTo(scrollOptions) - if (scrollToTopSelectors) { - scrollToTopElements ??= getScrollToTopElements(scrollToTopSelectors) + if (!windowRestored) { + scrollTo(scrollOptions) + } + if (scrollToTopElements) { for (const element of scrollToTopElements) { element.scrollTo(scrollOptions) } diff --git a/packages/router-core/tests/scroll-restoration.test.ts b/packages/router-core/tests/scroll-restoration.test.ts index 188d13bb68..c02a8f8783 100644 --- a/packages/router-core/tests/scroll-restoration.test.ts +++ b/packages/router-core/tests/scroll-restoration.test.ts @@ -2,8 +2,15 @@ import { createMemoryHistory } from '@tanstack/history' import { afterEach, describe, expect, test, vi } from 'vitest' import { BaseRootRoute, BaseRoute } from '../src' import { createTestRouter } from './routerTestUtils' +import type { ParsedLocation } from '../src' -function createRouter(options: { scrollRestoration?: boolean } = {}) { +function createRouter( + options: { + scrollRestoration?: boolean + scrollToTopSelectors?: Array Element | null | undefined)> + getScrollRestorationKey?: (location: ParsedLocation) => string + } = {}, +) { const rootRoute = new BaseRootRoute({}) const indexRoute = new BaseRoute({ getParentRoute: () => rootRoute, @@ -18,9 +25,39 @@ function createRouter(options: { scrollRestoration?: boolean } = {}) { } afterEach(() => { + document.body.replaceChildren() + window.sessionStorage.clear() vi.restoreAllMocks() + vi.unstubAllGlobals() }) +function getLocation( + router: ReturnType, + pathname: string, +) { + return { + ...router.latestLocation, + href: pathname, + pathname, + } +} + +function emitNavigation( + router: ReturnType, + type: 'onBeforeLoad' | 'onRendered', + fromLocation: ParsedLocation, + toLocation: ParsedLocation, +) { + router.emit({ + type, + fromLocation, + toLocation, + pathChanged: true, + hrefChanged: true, + hashChanged: false, + }) +} + describe('setupScrollRestoration', () => { test('sets up scroll restoration when scrollRestoration is true', () => { const windowAddEventListener = vi.spyOn(window, 'addEventListener') @@ -46,6 +83,63 @@ describe('setupScrollRestoration', () => { window.history.scrollRestoration = previousScrollRestoration }) + test('snapshots the live position when it changed after the latest scroll event', () => { + const element = document.createElement('div') + element.id = 'unit-live-snapshot-element' + document.body.append(element) + vi.stubGlobal('scrollTo', vi.fn()) + + const router = createRouter({ + scrollRestoration: true, + getScrollRestorationKey: (location) => location.pathname, + }) + const source = getLocation(router, '/unit-live-snapshot-source') + const destination = getLocation(router, '/unit-live-snapshot-destination') + + element.scrollTop = 80 + element.dispatchEvent(new Event('scroll', { bubbles: true })) + element.scrollTop = 120 + + emitNavigation(router, 'onBeforeLoad', source, destination) + element.scrollTop = 0 + emitNavigation(router, 'onRendered', source, destination) + + expect(element.scrollTop).toBe(120) + }) + + test('snapshots the live window position when it changed after the latest scroll event', () => { + const windowScrollTo = vi.fn() + vi.stubGlobal('scrollTo', windowScrollTo) + vi.stubGlobal('scrollX', 0) + vi.stubGlobal('scrollY', 80) + + const router = createRouter({ + scrollRestoration: true, + getScrollRestorationKey: (location) => location.pathname, + }) + const source = getLocation(router, '/unit-live-window-snapshot-source') + const destination = getLocation( + router, + '/unit-live-window-snapshot-destination', + ) + + document.dispatchEvent(new Event('scroll')) + vi.stubGlobal('scrollY', 120) + + emitNavigation(router, 'onBeforeLoad', source, destination) + emitNavigation(router, 'onRendered', source, destination) + windowScrollTo.mockClear() + + emitNavigation(router, 'onBeforeLoad', destination, source) + emitNavigation(router, 'onRendered', destination, source) + + expect(windowScrollTo).toHaveBeenCalledWith({ + top: 120, + left: 0, + behavior: undefined, + }) + }) + test.each([ ['omitted', undefined], ['false', false], @@ -81,4 +175,157 @@ describe('setupScrollRestoration', () => { window.history.scrollRestoration = previousScrollRestoration }, ) + + test('restores a configured element independently from the window', () => { + const element = document.createElement('div') + element.id = 'unit-restored-element' + element.dataset.scrollRestorationId = 'unit-restored-element' + document.body.append(element) + + const elementScrollTo = vi.fn((options: ScrollToOptions) => { + element.scrollTop = options.top ?? element.scrollTop + }) + element.scrollTo = elementScrollTo as typeof element.scrollTo + const windowScrollTo = vi.fn() + vi.stubGlobal('scrollTo', windowScrollTo) + + const router = createRouter({ + scrollRestoration: true, + scrollToTopSelectors: ['#unit-restored-element'], + getScrollRestorationKey: (location) => location.pathname, + }) + const source = getLocation(router, '/unit-element-source') + const destination = getLocation(router, '/unit-element-destination') + + element.scrollTop = 80 + element.dispatchEvent(new Event('scroll', { bubbles: true })) + + emitNavigation(router, 'onBeforeLoad', source, destination) + emitNavigation(router, 'onRendered', source, destination) + expect(element.scrollTop).toBe(0) + + elementScrollTo.mockClear() + windowScrollTo.mockClear() + vi.stubGlobal('scrollX', 0) + vi.stubGlobal('scrollY', 120) + document.dispatchEvent(new Event('scroll')) + emitNavigation(router, 'onBeforeLoad', destination, source) + emitNavigation(router, 'onRendered', destination, source) + + expect(element.scrollTop).toBe(80) + expect(elementScrollTo).not.toHaveBeenCalled() + expect(windowScrollTo).toHaveBeenCalledWith({ + top: 0, + left: 0, + behavior: undefined, + }) + }) + + test('resets an uncached configured element when the window restores', () => { + const element = document.createElement('div') + element.id = 'unit-reset-element' + document.body.append(element) + + const elementScrollTo = vi.fn((options: ScrollToOptions) => { + element.scrollTop = options.top ?? element.scrollTop + }) + element.scrollTo = elementScrollTo as typeof element.scrollTo + + const windowScrollTo = vi.fn() + vi.stubGlobal('scrollX', 0) + vi.stubGlobal('scrollY', 120) + vi.stubGlobal('scrollTo', windowScrollTo) + + const router = createRouter({ + scrollRestoration: true, + scrollToTopSelectors: ['#unit-reset-element'], + getScrollRestorationKey: (location) => location.pathname, + }) + const source = getLocation(router, '/unit-window-source') + const destination = getLocation(router, '/unit-window-destination') + + document.dispatchEvent(new Event('scroll')) + emitNavigation(router, 'onBeforeLoad', source, destination) + emitNavigation(router, 'onRendered', source, destination) + + elementScrollTo.mockClear() + windowScrollTo.mockClear() + element.scrollTop = 80 + + emitNavigation(router, 'onBeforeLoad', destination, source) + emitNavigation(router, 'onRendered', destination, source) + + expect(windowScrollTo).toHaveBeenCalledWith({ + top: 120, + left: 0, + behavior: undefined, + }) + expect(element.scrollTop).toBe(0) + expect(elementScrollTo).toHaveBeenCalledWith({ + top: 0, + left: 0, + behavior: undefined, + }) + }) + + test('resets a configured element when its cached selector becomes stale', () => { + const element = document.createElement('div') + element.id = 'unit-stale-selector-element' + element.dataset.scrollRestorationId = 'unit-stale-selector-source' + document.body.append(element) + + const elementScrollTo = vi.fn((options: ScrollToOptions) => { + element.scrollTop = options.top ?? element.scrollTop + }) + element.scrollTo = elementScrollTo as typeof element.scrollTo + vi.stubGlobal('scrollTo', vi.fn()) + + const router = createRouter({ + scrollRestoration: true, + scrollToTopSelectors: [() => element], + getScrollRestorationKey: (location) => location.pathname, + }) + const source = getLocation(router, '/unit-stale-selector-source') + const destination = getLocation(router, '/unit-stale-selector-destination') + + element.scrollTop = 80 + element.dispatchEvent(new Event('scroll', { bubbles: true })) + emitNavigation(router, 'onBeforeLoad', source, destination) + emitNavigation(router, 'onRendered', source, destination) + expect(element.scrollTop).toBe(0) + + element.dataset.scrollRestorationId = 'unit-stale-selector-current' + element.scrollTop = 40 + elementScrollTo.mockClear() + emitNavigation(router, 'onBeforeLoad', destination, source) + emitNavigation(router, 'onRendered', destination, source) + + expect(element.scrollTop).toBe(0) + expect(elementScrollTo).toHaveBeenCalledWith({ + top: 0, + left: 0, + behavior: undefined, + }) + }) + + test('does not resolve configured selectors for hash navigation without source entries', () => { + const element = document.createElement('div') + document.body.append(element) + const getElement = vi.fn(() => element) + + const router = createRouter({ + scrollRestoration: true, + scrollToTopSelectors: [getElement], + getScrollRestorationKey: (location) => location.pathname, + }) + const source = getLocation(router, '/unit-hash-source') + const destination = { + ...getLocation(router, '/unit-hash-destination'), + hash: 'section', + } + + emitNavigation(router, 'onRendered', source, destination) + + expect(getElement).not.toHaveBeenCalled() + }) }) From caebc57045acb84fbb666c9e4ca352075f42840e Mon Sep 17 00:00:00 2001 From: Manuel Schiller <6340397+schiller-manuel@users.noreply.github.com> Date: Mon, 13 Jul 2026 23:10:34 +0200 Subject: [PATCH 04/51] fix deps (#7810) --- pnpm-workspace.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index de56e54dd7..ffd19c8155 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -10,6 +10,7 @@ trustPolicyExclude: - 'semver@6.3.1' # installed by babel, ecosystem has been on v7 for 7 years now, but babel is horribly out of date, 6.3.1 is the latest v6 and was released 3 years ago - 'undici@6.21.2' # installed by cheerio@1.0.0 -> html-link-extractor@1.0.5 -> markdown-link-extractor@4.0.3, is only 1 year old, v6, v7 and v8 are maintained in parallel - '@netlify/edge-bundler@14.7.0' # existing Netlify dev transitive flagged by pnpm 11.9 trust downgrade checks + - '@platformatic/flame@1.7.0' # trusted publishing failed due to a missing sigstore module in npm, then an established maintainer published it manually - '@netlify/serverless-functions-api@2.7.1' # existing Netlify dev transitive flagged by pnpm 11.9 trust downgrade checks - '@netlify/zip-it-and-ship-it@14.1.11' # existing Netlify dev transitive flagged by pnpm 11.9 trust downgrade checks From 58c005fcb1a0618ec8c5b96b2b5fe3ccab5736c5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 23:31:08 +0200 Subject: [PATCH 05/51] ci: Version Packages (#7811) --- .changeset/fix-independent-scroll-targets.md | 13 - .../package.json | 4 +- .../react/authenticated-routes/package.json | 4 +- .../basic-default-search-params/package.json | 2 +- .../react/basic-devtools-panel/package.json | 2 +- examples/react/basic-file-based/package.json | 4 +- .../basic-non-nested-devtools/package.json | 2 +- .../basic-react-query-file-based/package.json | 4 +- examples/react/basic-react-query/package.json | 2 +- .../react/basic-ssr-file-based/package.json | 4 +- .../package.json | 4 +- .../basic-virtual-file-based/package.json | 4 +- .../package.json | 4 +- examples/react/basic/package.json | 2 +- examples/react/deferred-data/package.json | 2 +- examples/react/i18n-paraglide/package.json | 4 +- .../kitchen-sink-file-based/package.json | 4 +- .../package.json | 4 +- .../kitchen-sink-react-query/package.json | 2 +- examples/react/kitchen-sink/package.json | 2 +- examples/react/large-file-based/package.json | 4 +- examples/react/location-masking/package.json | 2 +- .../react/navigation-blocking/package.json | 2 +- .../package.json | 4 +- .../react/quickstart-file-based/package.json | 4 +- .../quickstart-rspack-file-based/package.json | 4 +- .../package.json | 4 +- examples/react/quickstart/package.json | 2 +- .../router-monorepo-react-query/package.json | 4 +- .../packages/router/package.json | 4 +- .../router-monorepo-simple-lazy/package.json | 4 +- .../packages/router/package.json | 4 +- .../react/router-monorepo-simple/package.json | 4 +- .../packages/router/package.json | 4 +- .../react/scroll-restoration/package.json | 2 +- .../search-validator-adapters/package.json | 4 +- examples/react/start-bare/package.json | 4 +- examples/react/start-basic-auth/package.json | 4 +- .../react/start-basic-authjs/package.json | 4 +- .../react/start-basic-cloudflare/package.json | 4 +- .../start-basic-react-query/package.json | 4 +- .../react/start-basic-static/package.json | 6 +- examples/react/start-basic/package.json | 4 +- examples/react/start-bun/package.json | 6 +- examples/react/start-clerk-basic/package.json | 4 +- .../react/start-convex-trellaux/package.json | 4 +- examples/react/start-counter/package.json | 4 +- .../react/start-i18n-paraglide/package.json | 4 +- examples/react/start-large/package.json | 4 +- examples/react/start-material-ui/package.json | 4 +- examples/react/start-rscs/package.json | 4 +- .../package.json | 4 +- .../react/start-supabase-basic/package.json | 4 +- examples/react/start-tailwind-v4/package.json | 4 +- examples/react/start-trellaux/package.json | 4 +- examples/react/start-workos/package.json | 4 +- examples/react/view-transitions/package.json | 4 +- .../react/with-framer-motion/package.json | 2 +- .../react/with-trpc-react-query/package.json | 4 +- examples/react/with-trpc/package.json | 4 +- .../package.json | 4 +- .../solid/authenticated-routes/package.json | 4 +- .../basic-default-search-params/package.json | 2 +- .../solid/basic-devtools-panel/package.json | 2 +- examples/solid/basic-file-based/package.json | 4 +- .../basic-non-nested-devtools/package.json | 2 +- .../basic-solid-query-file-based/package.json | 4 +- examples/solid/basic-solid-query/package.json | 4 +- .../solid/basic-ssr-file-based/package.json | 4 +- .../package.json | 4 +- .../basic-virtual-file-based/package.json | 4 +- .../package.json | 4 +- examples/solid/basic/package.json | 2 +- examples/solid/deferred-data/package.json | 2 +- examples/solid/i18n-paraglide/package.json | 4 +- .../kitchen-sink-file-based/package.json | 4 +- .../package.json | 4 +- .../kitchen-sink-solid-query/package.json | 2 +- examples/solid/kitchen-sink/package.json | 2 +- examples/solid/large-file-based/package.json | 4 +- examples/solid/location-masking/package.json | 2 +- .../solid/navigation-blocking/package.json | 2 +- .../package.json | 4 +- .../solid/quickstart-file-based/package.json | 4 +- .../quickstart-rspack-file-based/package.json | 4 +- .../package.json | 4 +- examples/solid/quickstart/package.json | 2 +- .../router-monorepo-simple-lazy/package.json | 4 +- .../packages/router/package.json | 4 +- .../solid/router-monorepo-simple/package.json | 4 +- .../packages/router/package.json | 4 +- .../router-monorepo-solid-query/package.json | 4 +- .../packages/router/package.json | 4 +- .../solid/scroll-restoration/package.json | 2 +- .../search-validator-adapters/package.json | 4 +- examples/solid/start-basic-auth/package.json | 4 +- .../solid/start-basic-authjs/package.json | 4 +- .../solid/start-basic-cloudflare/package.json | 4 +- .../solid/start-basic-netlify/package.json | 4 +- examples/solid/start-basic-nitro/package.json | 4 +- .../start-basic-solid-query/package.json | 4 +- .../solid/start-basic-static/package.json | 6 +- examples/solid/start-basic/package.json | 4 +- examples/solid/start-bun/package.json | 6 +- .../start-convex-better-auth/package.json | 4 +- examples/solid/start-counter/package.json | 4 +- .../solid/start-i18n-paraglide/package.json | 4 +- examples/solid/start-large/package.json | 4 +- .../package.json | 4 +- .../solid/start-supabase-basic/package.json | 4 +- examples/solid/start-tailwind-v4/package.json | 4 +- examples/solid/view-transitions/package.json | 4 +- .../solid/with-framer-motion/package.json | 2 +- examples/solid/with-trpc/package.json | 4 +- .../vue/basic-file-based-jsx/package.json | 4 +- .../vue/basic-file-based-sfc/package.json | 4 +- examples/vue/basic/package.json | 2 +- packages/react-router/CHANGELOG.md | 7 + packages/react-router/package.json | 2 +- packages/react-start-client/CHANGELOG.md | 9 + packages/react-start-client/package.json | 2 +- packages/react-start-rsc/CHANGELOG.md | 12 + packages/react-start-rsc/package.json | 2 +- packages/react-start-server/CHANGELOG.md | 9 + packages/react-start-server/package.json | 2 +- packages/react-start/CHANGELOG.md | 13 + packages/react-start/package.json | 2 +- packages/router-cli/CHANGELOG.md | 7 + packages/router-cli/package.json | 2 +- packages/router-core/CHANGELOG.md | 14 + packages/router-core/package.json | 2 +- packages/router-generator/CHANGELOG.md | 7 + packages/router-generator/package.json | 2 +- packages/router-plugin/CHANGELOG.md | 9 + packages/router-plugin/package.json | 2 +- packages/router-vite-plugin/CHANGELOG.md | 7 + packages/router-vite-plugin/package.json | 2 +- packages/solid-router/CHANGELOG.md | 7 + packages/solid-router/package.json | 2 +- packages/solid-start-client/CHANGELOG.md | 9 + packages/solid-start-client/package.json | 2 +- packages/solid-start-server/CHANGELOG.md | 9 + packages/solid-start-server/package.json | 2 +- packages/solid-start/CHANGELOG.md | 12 + packages/solid-start/package.json | 2 +- packages/start-client-core/CHANGELOG.md | 8 + packages/start-client-core/package.json | 2 +- packages/start-plugin-core/CHANGELOG.md | 10 + packages/start-plugin-core/package.json | 2 +- packages/start-server-core/CHANGELOG.md | 9 + packages/start-server-core/package.json | 2 +- .../CHANGELOG.md | 9 + .../package.json | 2 +- packages/start-storage-context/CHANGELOG.md | 7 + packages/start-storage-context/package.json | 2 +- packages/vue-router/CHANGELOG.md | 7 + packages/vue-router/package.json | 2 +- packages/vue-start-client/CHANGELOG.md | 9 + packages/vue-start-client/package.json | 2 +- packages/vue-start-server/CHANGELOG.md | 9 + packages/vue-start-server/package.json | 2 +- packages/vue-start/CHANGELOG.md | 12 + packages/vue-start/package.json | 2 +- pnpm-lock.yaml | 340 +++++------------- 164 files changed, 528 insertions(+), 502 deletions(-) delete mode 100644 .changeset/fix-independent-scroll-targets.md diff --git a/.changeset/fix-independent-scroll-targets.md b/.changeset/fix-independent-scroll-targets.md deleted file mode 100644 index d47294ee3e..0000000000 --- a/.changeset/fix-independent-scroll-targets.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -'@tanstack/router-core': patch ---- - -fix(router-core): handle window and element scroll restoration independently - -Window and element scroll targets are now handled independently. Restoring one target no longer suppresses resets for other uncached configured targets, and a restored element is no longer reset when the window has no cached position. - -Hash navigation no longer resets elements configured through `scrollToTopSelectors` and retains precedence over stale window positions through destination invalidations. - -Scroll positions are sampled when leaving a route, preserving live changes made after the most recent scroll event. This also prevents client hydration from undoing nested positions restored by the SSR script. - -Fixes #7687. diff --git a/examples/react/authenticated-routes-firebase/package.json b/examples/react/authenticated-routes-firebase/package.json index 58c677ed26..ed0c97607c 100644 --- a/examples/react/authenticated-routes-firebase/package.json +++ b/examples/react/authenticated-routes-firebase/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "firebase": "^11.4.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/authenticated-routes/package.json b/examples/react/authenticated-routes/package.json index 5517800c01..b47b6a38a2 100644 --- a/examples/react/authenticated-routes/package.json +++ b/examples/react/authenticated-routes/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-default-search-params/package.json b/examples/react/basic-default-search-params/package.json index 97bfd6d8d4..d3062bbb11 100644 --- a/examples/react/basic-default-search-params/package.json +++ b/examples/react/basic-default-search-params/package.json @@ -11,7 +11,7 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-query": "^5.90.0", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/basic-devtools-panel/package.json b/examples/react/basic-devtools-panel/package.json index 9ff1c8fefb..3147d73064 100644 --- a/examples/react/basic-devtools-panel/package.json +++ b/examples/react/basic-devtools-panel/package.json @@ -11,7 +11,7 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-query-devtools": "^5.67.2", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/basic-file-based/package.json b/examples/react/basic-file-based/package.json index bca55ff7d5..7aecf29cce 100644 --- a/examples/react/basic-file-based/package.json +++ b/examples/react/basic-file-based/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-non-nested-devtools/package.json b/examples/react/basic-non-nested-devtools/package.json index fe6f2a04bd..7ace128a31 100644 --- a/examples/react/basic-non-nested-devtools/package.json +++ b/examples/react/basic-non-nested-devtools/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/basic-react-query-file-based/package.json b/examples/react/basic-react-query-file-based/package.json index 501a22dcc3..e0fa7c1a36 100644 --- a/examples/react/basic-react-query-file-based/package.json +++ b/examples/react/basic-react-query-file-based/package.json @@ -12,9 +12,9 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-query": "^5.90.0", "@tanstack/react-query-devtools": "^5.90.0", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-react-query/package.json b/examples/react/basic-react-query/package.json index b56851bdac..160c426dc1 100644 --- a/examples/react/basic-react-query/package.json +++ b/examples/react/basic-react-query/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-query": "^5.90.0", "@tanstack/react-query-devtools": "^5.90.0", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/basic-ssr-file-based/package.json b/examples/react/basic-ssr-file-based/package.json index 3fa61c784f..c7f7da2e0c 100644 --- a/examples/react/basic-ssr-file-based/package.json +++ b/examples/react/basic-ssr-file-based/package.json @@ -11,8 +11,8 @@ "debug": "node --inspect-brk server" }, "dependencies": { - "@tanstack/react-router": "^1.170.17", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/react-router": "^1.170.18", + "@tanstack/router-plugin": "^1.168.20", "compression": "^1.8.0", "express": "^5.2.1", "get-port": "^7.1.0", diff --git a/examples/react/basic-ssr-streaming-file-based/package.json b/examples/react/basic-ssr-streaming-file-based/package.json index b24243e276..f022ebb718 100644 --- a/examples/react/basic-ssr-streaming-file-based/package.json +++ b/examples/react/basic-ssr-streaming-file-based/package.json @@ -11,8 +11,8 @@ "debug": "node --inspect-brk server" }, "dependencies": { - "@tanstack/react-router": "^1.170.17", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/react-router": "^1.170.18", + "@tanstack/router-plugin": "^1.168.20", "compression": "^1.8.0", "express": "^5.2.1", "get-port": "^7.1.0", diff --git a/examples/react/basic-virtual-file-based/package.json b/examples/react/basic-virtual-file-based/package.json index 922c460906..7cf590ef31 100644 --- a/examples/react/basic-virtual-file-based/package.json +++ b/examples/react/basic-virtual-file-based/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "@tanstack/virtual-file-routes": "^1.162.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/basic-virtual-inside-file-based/package.json b/examples/react/basic-virtual-inside-file-based/package.json index a24bf519d9..255facb7ff 100644 --- a/examples/react/basic-virtual-inside-file-based/package.json +++ b/examples/react/basic-virtual-inside-file-based/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "@tanstack/virtual-file-routes": "^1.162.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/basic/package.json b/examples/react/basic/package.json index d245cc9f0e..f380281baa 100644 --- a/examples/react/basic/package.json +++ b/examples/react/basic/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/deferred-data/package.json b/examples/react/deferred-data/package.json index b55fb41ccc..c1ca6ba2e1 100644 --- a/examples/react/deferred-data/package.json +++ b/examples/react/deferred-data/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/i18n-paraglide/package.json b/examples/react/i18n-paraglide/package.json index d9a9602c02..55079cfebb 100644 --- a/examples/react/i18n-paraglide/package.json +++ b/examples/react/i18n-paraglide/package.json @@ -11,8 +11,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.17", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/react-router": "^1.170.18", + "@tanstack/router-plugin": "^1.168.20", "react": "^19.1.1", "react-dom": "^19.1.1", "tailwindcss": "^4.2.2" diff --git a/examples/react/kitchen-sink-file-based/package.json b/examples/react/kitchen-sink-file-based/package.json index 2e18eb4358..989367745e 100644 --- a/examples/react/kitchen-sink-file-based/package.json +++ b/examples/react/kitchen-sink-file-based/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "immer": "^10.1.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/kitchen-sink-react-query-file-based/package.json b/examples/react/kitchen-sink-react-query-file-based/package.json index f712e86a8f..378ad424c4 100644 --- a/examples/react/kitchen-sink-react-query-file-based/package.json +++ b/examples/react/kitchen-sink-react-query-file-based/package.json @@ -12,9 +12,9 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-query": "^5.90.0", "@tanstack/react-query-devtools": "^5.90.0", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "immer": "^10.1.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/kitchen-sink-react-query/package.json b/examples/react/kitchen-sink-react-query/package.json index 3003408f70..c03d144726 100644 --- a/examples/react/kitchen-sink-react-query/package.json +++ b/examples/react/kitchen-sink-react-query/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-query": "^5.90.0", "@tanstack/react-query-devtools": "^5.90.0", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "immer": "^10.1.1", "react": "^19.0.0", diff --git a/examples/react/kitchen-sink/package.json b/examples/react/kitchen-sink/package.json index 4414b457f2..ebba4eb683 100644 --- a/examples/react/kitchen-sink/package.json +++ b/examples/react/kitchen-sink/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "immer": "^10.1.1", "react": "^19.0.0", diff --git a/examples/react/large-file-based/package.json b/examples/react/large-file-based/package.json index 16fd095515..e57ed59fbc 100644 --- a/examples/react/large-file-based/package.json +++ b/examples/react/large-file-based/package.json @@ -13,9 +13,9 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-query": "^5.90.0", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/location-masking/package.json b/examples/react/location-masking/package.json index 76d00658a4..a43f090245 100644 --- a/examples/react/location-masking/package.json +++ b/examples/react/location-masking/package.json @@ -12,7 +12,7 @@ "@radix-ui/react-dialog": "^1.1.6", "@tailwindcss/vite": "^4.2.2", "@tanstack/react-query": "^5.90.0", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/navigation-blocking/package.json b/examples/react/navigation-blocking/package.json index 757ae9f2e6..ebb1b4d3b1 100644 --- a/examples/react/navigation-blocking/package.json +++ b/examples/react/navigation-blocking/package.json @@ -11,7 +11,7 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-query": "^5.90.0", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/quickstart-esbuild-file-based/package.json b/examples/react/quickstart-esbuild-file-based/package.json index 300650c666..57203d5eec 100644 --- a/examples/react/quickstart-esbuild-file-based/package.json +++ b/examples/react/quickstart-esbuild-file-based/package.json @@ -9,9 +9,9 @@ "start": "dev" }, "dependencies": { - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/quickstart-file-based/package.json b/examples/react/quickstart-file-based/package.json index 31a70cf36c..f89da750cc 100644 --- a/examples/react/quickstart-file-based/package.json +++ b/examples/react/quickstart-file-based/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/quickstart-rspack-file-based/package.json b/examples/react/quickstart-rspack-file-based/package.json index f3525defde..5ae8e4e737 100644 --- a/examples/react/quickstart-rspack-file-based/package.json +++ b/examples/react/quickstart-rspack-file-based/package.json @@ -9,7 +9,7 @@ }, "dependencies": { "@tailwindcss/postcss": "^4.2.2", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "postcss": "^8.5.1", "react": "^19.0.0", @@ -19,7 +19,7 @@ "devDependencies": { "@rsbuild/core": "^2.0.11", "@rsbuild/plugin-react": "^2.0.0", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "typescript": "^6.0.2" diff --git a/examples/react/quickstart-webpack-file-based/package.json b/examples/react/quickstart-webpack-file-based/package.json index 4c778eab28..d5a729b1ff 100644 --- a/examples/react/quickstart-webpack-file-based/package.json +++ b/examples/react/quickstart-webpack-file-based/package.json @@ -7,14 +7,14 @@ "build": "webpack build && tsc --noEmit" }, "dependencies": { - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "react": "^19.0.0", "react-dom": "^19.0.0" }, "devDependencies": { "@swc/core": "^1.15.33", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "html-webpack-plugin": "^5.6.3", diff --git a/examples/react/quickstart/package.json b/examples/react/quickstart/package.json index 00269e8524..983e3a00f6 100644 --- a/examples/react/quickstart/package.json +++ b/examples/react/quickstart/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/router-monorepo-react-query/package.json b/examples/react/router-monorepo-react-query/package.json index 75532f61b1..b5cc722a6b 100644 --- a/examples/react/router-monorepo-react-query/package.json +++ b/examples/react/router-monorepo-react-query/package.json @@ -12,9 +12,9 @@ "dependencies": { "@tanstack/react-query": "^5.90.0", "@tanstack/react-query-devtools": "^5.90.0", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/router-monorepo-react-query/packages/router/package.json b/examples/react/router-monorepo-react-query/packages/router/package.json index 136535b761..56d50596df 100644 --- a/examples/react/router-monorepo-react-query/packages/router/package.json +++ b/examples/react/router-monorepo-react-query/packages/router/package.json @@ -11,8 +11,8 @@ "@router-mono-react-query/post-query": "workspace:*", "@tanstack/history": "^1.162.0", "@tanstack/react-query": "^5.90.0", - "@tanstack/react-router": "^1.170.17", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/react-router": "^1.170.18", + "@tanstack/router-plugin": "^1.168.20", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/router-monorepo-simple-lazy/package.json b/examples/react/router-monorepo-simple-lazy/package.json index dd06440bf3..e259aba770 100644 --- a/examples/react/router-monorepo-simple-lazy/package.json +++ b/examples/react/router-monorepo-simple-lazy/package.json @@ -8,9 +8,9 @@ "dev": "pnpm router build && pnpm post-feature build && pnpm app dev" }, "dependencies": { - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/router-monorepo-simple-lazy/packages/router/package.json b/examples/react/router-monorepo-simple-lazy/packages/router/package.json index 8b0b87a474..c9ff6bb101 100644 --- a/examples/react/router-monorepo-simple-lazy/packages/router/package.json +++ b/examples/react/router-monorepo-simple-lazy/packages/router/package.json @@ -9,8 +9,8 @@ "types": "./dist/index.d.ts", "dependencies": { "@tanstack/history": "^1.162.0", - "@tanstack/react-router": "^1.170.17", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/react-router": "^1.170.18", + "@tanstack/router-plugin": "^1.168.20", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/router-monorepo-simple/package.json b/examples/react/router-monorepo-simple/package.json index 77cf9ecdf5..e126976126 100644 --- a/examples/react/router-monorepo-simple/package.json +++ b/examples/react/router-monorepo-simple/package.json @@ -8,9 +8,9 @@ "dev": "pnpm router build && pnpm post-feature build && pnpm app dev" }, "dependencies": { - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/router-monorepo-simple/packages/router/package.json b/examples/react/router-monorepo-simple/packages/router/package.json index 6bdb5ec943..0eae7a368b 100644 --- a/examples/react/router-monorepo-simple/packages/router/package.json +++ b/examples/react/router-monorepo-simple/packages/router/package.json @@ -9,8 +9,8 @@ "types": "./dist/index.d.ts", "dependencies": { "@tanstack/history": "^1.162.0", - "@tanstack/react-router": "^1.170.17", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/react-router": "^1.170.18", + "@tanstack/router-plugin": "^1.168.20", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/scroll-restoration/package.json b/examples/react/scroll-restoration/package.json index 7a503801e5..caf8329f07 100644 --- a/examples/react/scroll-restoration/package.json +++ b/examples/react/scroll-restoration/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-virtual": "^3.13.0", "react": "^19.0.0", diff --git a/examples/react/search-validator-adapters/package.json b/examples/react/search-validator-adapters/package.json index 234abba717..cfdeba55d5 100644 --- a/examples/react/search-validator-adapters/package.json +++ b/examples/react/search-validator-adapters/package.json @@ -13,9 +13,9 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/arktype-adapter": "^1.167.0", "@tanstack/react-query": "^5.90.0", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "@tanstack/valibot-adapter": "^1.167.0", "@tanstack/zod-adapter": "^1.167.0", "arktype": "^2.1.7", diff --git a/examples/react/start-bare/package.json b/examples/react/start-bare/package.json index 5a15546ef6..d0ff2ea336 100644 --- a/examples/react/start-bare/package.json +++ b/examples/react/start-bare/package.json @@ -10,9 +10,9 @@ "start": "pnpx srvx --prod -s ../client dist/server/server.js" }, "dependencies": { - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.27", + "@tanstack/react-start": "^1.168.28", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-basic-auth/package.json b/examples/react/start-basic-auth/package.json index abee47dc52..4533b4bee1 100644 --- a/examples/react/start-basic-auth/package.json +++ b/examples/react/start-basic-auth/package.json @@ -14,9 +14,9 @@ "@libsql/client": "^0.15.15", "@prisma/adapter-libsql": "^7.0.0", "@prisma/client": "^7.0.0", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.27", + "@tanstack/react-start": "^1.168.28", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-basic-authjs/package.json b/examples/react/start-basic-authjs/package.json index a28f1c313f..ba1628938d 100644 --- a/examples/react/start-basic-authjs/package.json +++ b/examples/react/start-basic-authjs/package.json @@ -11,9 +11,9 @@ }, "dependencies": { "@auth/core": "^0.41.1", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.27", + "@tanstack/react-start": "^1.168.28", "react": "^19.0.0", "react-dom": "^19.0.0", "start-authjs": "^1.0.0", diff --git a/examples/react/start-basic-cloudflare/package.json b/examples/react/start-basic-cloudflare/package.json index f74602ab83..78a01c103a 100644 --- a/examples/react/start-basic-cloudflare/package.json +++ b/examples/react/start-basic-cloudflare/package.json @@ -12,9 +12,9 @@ "postinstall": "WRANGLER_LOG=warn npm run --silent cf-typegen" }, "dependencies": { - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.27", + "@tanstack/react-start": "^1.168.28", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/examples/react/start-basic-react-query/package.json b/examples/react/start-basic-react-query/package.json index 40a18be142..a39283577f 100644 --- a/examples/react/start-basic-react-query/package.json +++ b/examples/react/start-basic-react-query/package.json @@ -12,10 +12,10 @@ "dependencies": { "@tanstack/react-query": "^5.90.0", "@tanstack/react-query-devtools": "^5.90.0", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.27", + "@tanstack/react-start": "^1.168.28", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-basic-static/package.json b/examples/react/start-basic-static/package.json index 8ea4c54edc..c167f5ce79 100644 --- a/examples/react/start-basic-static/package.json +++ b/examples/react/start-basic-static/package.json @@ -10,10 +10,10 @@ "start": "pnpx srvx --prod -s ../client dist/server/server.js" }, "dependencies": { - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.27", - "@tanstack/start-static-server-functions": "^1.167.18", + "@tanstack/react-start": "^1.168.28", + "@tanstack/start-static-server-functions": "^1.167.19", "@vitejs/plugin-react": "^6.0.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/start-basic/package.json b/examples/react/start-basic/package.json index 931df2a45d..9fd4e517a6 100644 --- a/examples/react/start-basic/package.json +++ b/examples/react/start-basic/package.json @@ -10,9 +10,9 @@ "start": "node .output/server/index.mjs" }, "dependencies": { - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.27", + "@tanstack/react-start": "^1.168.28", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.6.0", diff --git a/examples/react/start-bun/package.json b/examples/react/start-bun/package.json index a4ab34310f..c0571fea19 100644 --- a/examples/react/start-bun/package.json +++ b/examples/react/start-bun/package.json @@ -15,11 +15,11 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-devtools": "^0.7.0", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.27", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/react-start": "^1.168.28", + "@tanstack/router-plugin": "^1.168.20", "react": "^19.1.1", "react-dom": "^19.1.1", "tailwindcss": "^4.2.2" diff --git a/examples/react/start-clerk-basic/package.json b/examples/react/start-clerk-basic/package.json index 7e15b08073..7aeffb6a9b 100644 --- a/examples/react/start-clerk-basic/package.json +++ b/examples/react/start-clerk-basic/package.json @@ -11,9 +11,9 @@ }, "dependencies": { "@clerk/tanstack-react-start": "^0.27.14", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.27", + "@tanstack/react-start": "^1.168.28", "@vitejs/plugin-react": "^6.0.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/start-convex-trellaux/package.json b/examples/react/start-convex-trellaux/package.json index f2ec73bfe8..356a7cab2d 100644 --- a/examples/react/start-convex-trellaux/package.json +++ b/examples/react/start-convex-trellaux/package.json @@ -15,10 +15,10 @@ "@convex-dev/react-query": "0.0.0-alpha.8", "@tanstack/react-query": "^5.90.0", "@tanstack/react-query-devtools": "^5.90.0", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.27", + "@tanstack/react-start": "^1.168.28", "concurrently": "^8.2.2", "convex": "^1.19.0", "ky": "^1.7.4", diff --git a/examples/react/start-counter/package.json b/examples/react/start-counter/package.json index a7a8397b74..03a572f79c 100644 --- a/examples/react/start-counter/package.json +++ b/examples/react/start-counter/package.json @@ -10,9 +10,9 @@ "start": "pnpx srvx --prod -s ../client dist/server/server.js" }, "dependencies": { - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.27", + "@tanstack/react-start": "^1.168.28", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/examples/react/start-i18n-paraglide/package.json b/examples/react/start-i18n-paraglide/package.json index 936f5e040d..b4a62068a3 100644 --- a/examples/react/start-i18n-paraglide/package.json +++ b/examples/react/start-i18n-paraglide/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tanstack/react-devtools": "^0.7.0", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.27", + "@tanstack/react-start": "^1.168.28", "react": "^19.1.1", "react-dom": "^19.1.1" }, diff --git a/examples/react/start-large/package.json b/examples/react/start-large/package.json index d7300fc815..f4822d3eba 100644 --- a/examples/react/start-large/package.json +++ b/examples/react/start-large/package.json @@ -13,9 +13,9 @@ }, "dependencies": { "@tanstack/react-query": "^5.90.0", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.27", + "@tanstack/react-start": "^1.168.28", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-material-ui/package.json b/examples/react/start-material-ui/package.json index a7623d10f0..0efed05e33 100644 --- a/examples/react/start-material-ui/package.json +++ b/examples/react/start-material-ui/package.json @@ -15,9 +15,9 @@ "@emotion/styled": "11.14.0", "@fontsource-variable/roboto": "5.2.5", "@mui/material": "6.4.7", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.27", + "@tanstack/react-start": "^1.168.28", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-rscs/package.json b/examples/react/start-rscs/package.json index 4e6da2d402..8d39c00a79 100644 --- a/examples/react/start-rscs/package.json +++ b/examples/react/start-rscs/package.json @@ -10,9 +10,9 @@ "start": "node .output/server/index.mjs" }, "dependencies": { - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.27", + "@tanstack/react-start": "^1.168.28", "dexie": "^4.0.10", "react": "^19.2.0", "react-dom": "^19.2.0", diff --git a/examples/react/start-streaming-data-from-server-functions/package.json b/examples/react/start-streaming-data-from-server-functions/package.json index e5d50b902f..82ddd0726e 100644 --- a/examples/react/start-streaming-data-from-server-functions/package.json +++ b/examples/react/start-streaming-data-from-server-functions/package.json @@ -10,9 +10,9 @@ "start": "pnpx srvx --prod -s ../client dist/server/server.js" }, "dependencies": { - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.27", + "@tanstack/react-start": "^1.168.28", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-supabase-basic/package.json b/examples/react/start-supabase-basic/package.json index 30083cbe1d..c28c4c0972 100644 --- a/examples/react/start-supabase-basic/package.json +++ b/examples/react/start-supabase-basic/package.json @@ -15,9 +15,9 @@ "dependencies": { "@supabase/ssr": "^0.5.2", "@supabase/supabase-js": "^2.48.1", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.27", + "@tanstack/react-start": "^1.168.28", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/start-tailwind-v4/package.json b/examples/react/start-tailwind-v4/package.json index f8e537c9b4..840669580e 100644 --- a/examples/react/start-tailwind-v4/package.json +++ b/examples/react/start-tailwind-v4/package.json @@ -10,9 +10,9 @@ "start": "node .output/server/index.mjs" }, "dependencies": { - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.27", + "@tanstack/react-start": "^1.168.28", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.6.0", diff --git a/examples/react/start-trellaux/package.json b/examples/react/start-trellaux/package.json index 29bf8bba30..58d3386253 100644 --- a/examples/react/start-trellaux/package.json +++ b/examples/react/start-trellaux/package.json @@ -12,10 +12,10 @@ "dependencies": { "@tanstack/react-query": "^5.90.0", "@tanstack/react-query-devtools": "^5.90.0", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.27", + "@tanstack/react-start": "^1.168.28", "ky": "^1.7.4", "msw": "^2.7.0", "react": "^19.0.0", diff --git a/examples/react/start-workos/package.json b/examples/react/start-workos/package.json index 935c7a0a5f..3639294bac 100644 --- a/examples/react/start-workos/package.json +++ b/examples/react/start-workos/package.json @@ -14,9 +14,9 @@ "license": "MIT", "dependencies": { "@radix-ui/themes": "^3.3.0", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.27", + "@tanstack/react-start": "^1.168.28", "@workos/authkit-tanstack-react-start": "^0.5.0", "react": "^19.0.0", "react-dom": "^19.0.0" diff --git a/examples/react/view-transitions/package.json b/examples/react/view-transitions/package.json index a5cc8bc839..4170f718e9 100644 --- a/examples/react/view-transitions/package.json +++ b/examples/react/view-transitions/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/with-framer-motion/package.json b/examples/react/with-framer-motion/package.json index c4d861b689..b690bb1c9a 100644 --- a/examples/react/with-framer-motion/package.json +++ b/examples/react/with-framer-motion/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "framer-motion": "^11.18.2", "react": "^19.0.0", diff --git a/examples/react/with-trpc-react-query/package.json b/examples/react/with-trpc-react-query/package.json index 48e7eaa6cb..8831056e3d 100644 --- a/examples/react/with-trpc-react-query/package.json +++ b/examples/react/with-trpc-react-query/package.json @@ -13,9 +13,9 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-query": "^5.90.0", "@tanstack/react-query-devtools": "^5.90.0", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "@trpc/client": "^11.4.3", "@trpc/server": "^11.4.3", "@trpc/tanstack-react-query": "^11.4.3", diff --git a/examples/react/with-trpc/package.json b/examples/react/with-trpc/package.json index 3474e12a6b..6ec641420c 100644 --- a/examples/react/with-trpc/package.json +++ b/examples/react/with-trpc/package.json @@ -11,9 +11,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "@trpc/client": "^11.4.3", "@trpc/server": "^11.4.3", "express": "^5.2.1", diff --git a/examples/solid/authenticated-routes-firebase/package.json b/examples/solid/authenticated-routes-firebase/package.json index fdb0cd4400..0ed139ce36 100644 --- a/examples/solid/authenticated-routes-firebase/package.json +++ b/examples/solid/authenticated-routes-firebase/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.19", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/router-plugin": "^1.168.20", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "firebase": "^11.4.0", "redaxios": "^0.5.1", diff --git a/examples/solid/authenticated-routes/package.json b/examples/solid/authenticated-routes/package.json index 4e3fac9dec..979f85c8a9 100644 --- a/examples/solid/authenticated-routes/package.json +++ b/examples/solid/authenticated-routes/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.19", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/router-plugin": "^1.168.20", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "redaxios": "^0.5.1", "solid-js": "^1.9.10", diff --git a/examples/solid/basic-default-search-params/package.json b/examples/solid/basic-default-search-params/package.json index f1f806b647..a41e918fe4 100644 --- a/examples/solid/basic-default-search-params/package.json +++ b/examples/solid/basic-default-search-params/package.json @@ -11,7 +11,7 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/solid-query": "^5.90.9", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "redaxios": "^0.5.1", "solid-js": "^1.9.10", diff --git a/examples/solid/basic-devtools-panel/package.json b/examples/solid/basic-devtools-panel/package.json index 6bc2dacd1f..fcc8d093ed 100644 --- a/examples/solid/basic-devtools-panel/package.json +++ b/examples/solid/basic-devtools-panel/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "redaxios": "^0.5.1", "solid-js": "^1.9.10", diff --git a/examples/solid/basic-file-based/package.json b/examples/solid/basic-file-based/package.json index 40e93847d0..b032501a85 100644 --- a/examples/solid/basic-file-based/package.json +++ b/examples/solid/basic-file-based/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "redaxios": "^0.5.1", "solid-js": "^1.9.10", @@ -18,7 +18,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "typescript": "^6.0.2", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/examples/solid/basic-non-nested-devtools/package.json b/examples/solid/basic-non-nested-devtools/package.json index 4cf678441f..64bf1df09f 100644 --- a/examples/solid/basic-non-nested-devtools/package.json +++ b/examples/solid/basic-non-nested-devtools/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "redaxios": "^0.5.1", "solid-js": "^1.9.10", diff --git a/examples/solid/basic-solid-query-file-based/package.json b/examples/solid/basic-solid-query-file-based/package.json index bae59ef0e5..e70255d71e 100644 --- a/examples/solid/basic-solid-query-file-based/package.json +++ b/examples/solid/basic-solid-query-file-based/package.json @@ -13,7 +13,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-query-devtools": "^5.90.0", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "redaxios": "^0.5.1", "solid-js": "^1.9.10", @@ -21,7 +21,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "typescript": "^6.0.2", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/examples/solid/basic-solid-query/package.json b/examples/solid/basic-solid-query/package.json index 8efdd34c10..a3aaeb30da 100644 --- a/examples/solid/basic-solid-query/package.json +++ b/examples/solid/basic-solid-query/package.json @@ -12,14 +12,14 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-query-devtools": "^5.90.0", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "typescript": "^6.0.2", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/examples/solid/basic-ssr-file-based/package.json b/examples/solid/basic-ssr-file-based/package.json index 1e8a57559f..6a298c782e 100644 --- a/examples/solid/basic-ssr-file-based/package.json +++ b/examples/solid/basic-ssr-file-based/package.json @@ -11,8 +11,8 @@ "debug": "node --inspect-brk server" }, "dependencies": { - "@tanstack/solid-router": "^1.170.17", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/solid-router": "^1.170.18", + "@tanstack/router-plugin": "^1.168.20", "compression": "^1.8.0", "express": "^5.2.1", "get-port": "^7.1.0", diff --git a/examples/solid/basic-ssr-streaming-file-based/package.json b/examples/solid/basic-ssr-streaming-file-based/package.json index cbdda9063b..7e8e90bb09 100644 --- a/examples/solid/basic-ssr-streaming-file-based/package.json +++ b/examples/solid/basic-ssr-streaming-file-based/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "compression": "^1.8.0", "express": "^5.2.1", @@ -24,7 +24,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "@types/express": "^5.0.6", "typescript": "^6.0.2", "vite": "^8.0.14", diff --git a/examples/solid/basic-virtual-file-based/package.json b/examples/solid/basic-virtual-file-based/package.json index 17ba921082..461b72f958 100644 --- a/examples/solid/basic-virtual-file-based/package.json +++ b/examples/solid/basic-virtual-file-based/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.19", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/router-plugin": "^1.168.20", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@tanstack/virtual-file-routes": "^1.162.0", "redaxios": "^0.5.1", diff --git a/examples/solid/basic-virtual-inside-file-based/package.json b/examples/solid/basic-virtual-inside-file-based/package.json index e24fd542d1..449c620d69 100644 --- a/examples/solid/basic-virtual-inside-file-based/package.json +++ b/examples/solid/basic-virtual-inside-file-based/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.19", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/router-plugin": "^1.168.20", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@tanstack/virtual-file-routes": "^1.162.0", "redaxios": "^0.5.1", diff --git a/examples/solid/basic/package.json b/examples/solid/basic/package.json index 9b42dafb14..f13e5f3afb 100644 --- a/examples/solid/basic/package.json +++ b/examples/solid/basic/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "redaxios": "^0.5.1", "solid-js": "^1.9.10", diff --git a/examples/solid/deferred-data/package.json b/examples/solid/deferred-data/package.json index 5286f94c06..c9851e71e5 100644 --- a/examples/solid/deferred-data/package.json +++ b/examples/solid/deferred-data/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "redaxios": "^0.5.1", "solid-js": "^1.9.10", diff --git a/examples/solid/i18n-paraglide/package.json b/examples/solid/i18n-paraglide/package.json index f9506ebbdc..a6c4136a40 100644 --- a/examples/solid/i18n-paraglide/package.json +++ b/examples/solid/i18n-paraglide/package.json @@ -11,8 +11,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.17", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/solid-router": "^1.170.18", + "@tanstack/router-plugin": "^1.168.20", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" }, diff --git a/examples/solid/kitchen-sink-file-based/package.json b/examples/solid/kitchen-sink-file-based/package.json index 956a852426..d38afe3bb5 100644 --- a/examples/solid/kitchen-sink-file-based/package.json +++ b/examples/solid/kitchen-sink-file-based/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "immer": "^10.1.1", "redaxios": "^0.5.1", @@ -19,7 +19,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "typescript": "^6.0.2", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/examples/solid/kitchen-sink-solid-query-file-based/package.json b/examples/solid/kitchen-sink-solid-query-file-based/package.json index 78c95f0cdf..fe71bf13e9 100644 --- a/examples/solid/kitchen-sink-solid-query-file-based/package.json +++ b/examples/solid/kitchen-sink-solid-query-file-based/package.json @@ -10,10 +10,10 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-query-devtools": "^5.90.0", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "immer": "^10.1.1", "redaxios": "^0.5.1", diff --git a/examples/solid/kitchen-sink-solid-query/package.json b/examples/solid/kitchen-sink-solid-query/package.json index eca233210a..73b9eb16aa 100644 --- a/examples/solid/kitchen-sink-solid-query/package.json +++ b/examples/solid/kitchen-sink-solid-query/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-query-devtools": "^5.90.0", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "immer": "^10.1.1", "redaxios": "^0.5.1", diff --git a/examples/solid/kitchen-sink/package.json b/examples/solid/kitchen-sink/package.json index 8d588e4f27..25833261e7 100644 --- a/examples/solid/kitchen-sink/package.json +++ b/examples/solid/kitchen-sink/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "immer": "^10.1.1", "redaxios": "^0.5.1", diff --git a/examples/solid/large-file-based/package.json b/examples/solid/large-file-based/package.json index a8b59cae3b..bfdff9eaba 100644 --- a/examples/solid/large-file-based/package.json +++ b/examples/solid/large-file-based/package.json @@ -12,9 +12,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "@tanstack/solid-query": "^5.90.9", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "redaxios": "^0.5.1", "solid-js": "^1.9.10", diff --git a/examples/solid/location-masking/package.json b/examples/solid/location-masking/package.json index cd42e50706..5b0b9df7e3 100644 --- a/examples/solid/location-masking/package.json +++ b/examples/solid/location-masking/package.json @@ -11,7 +11,7 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/solid-query": "^5.90.9", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "solid-js": "^1.9.10", "redaxios": "^0.5.1", diff --git a/examples/solid/navigation-blocking/package.json b/examples/solid/navigation-blocking/package.json index e433b54680..9b2ef78745 100644 --- a/examples/solid/navigation-blocking/package.json +++ b/examples/solid/navigation-blocking/package.json @@ -11,7 +11,7 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/solid-query": "^5.90.9", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "solid-js": "^1.9.10", "redaxios": "^0.5.1", diff --git a/examples/solid/quickstart-esbuild-file-based/package.json b/examples/solid/quickstart-esbuild-file-based/package.json index 425a39e4f6..71bac328eb 100644 --- a/examples/solid/quickstart-esbuild-file-based/package.json +++ b/examples/solid/quickstart-esbuild-file-based/package.json @@ -9,8 +9,8 @@ "start": "dev" }, "dependencies": { - "@tanstack/router-plugin": "^1.168.19", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/router-plugin": "^1.168.20", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "redaxios": "^0.5.1", "solid-js": "^1.9.10", diff --git a/examples/solid/quickstart-file-based/package.json b/examples/solid/quickstart-file-based/package.json index 4774fa54ba..619c6d00f7 100644 --- a/examples/solid/quickstart-file-based/package.json +++ b/examples/solid/quickstart-file-based/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "redaxios": "^0.5.1", "solid-js": "^1.9.10", @@ -18,7 +18,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "typescript": "^6.0.2", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/examples/solid/quickstart-rspack-file-based/package.json b/examples/solid/quickstart-rspack-file-based/package.json index 290a88219a..5b80ce7428 100644 --- a/examples/solid/quickstart-rspack-file-based/package.json +++ b/examples/solid/quickstart-rspack-file-based/package.json @@ -9,7 +9,7 @@ }, "dependencies": { "@tailwindcss/postcss": "^4.2.2", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "postcss": "^8.5.1", "solid-js": "^1.9.10", @@ -19,7 +19,7 @@ "@rsbuild/core": "^2.0.11", "@rsbuild/plugin-babel": "^1.1.2", "@rsbuild/plugin-solid": "^1.1.1", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "typescript": "^6.0.2" } } diff --git a/examples/solid/quickstart-webpack-file-based/package.json b/examples/solid/quickstart-webpack-file-based/package.json index daa70edd1b..e8b66516d7 100644 --- a/examples/solid/quickstart-webpack-file-based/package.json +++ b/examples/solid/quickstart-webpack-file-based/package.json @@ -7,7 +7,7 @@ "build": "webpack build && tsc --noEmit" }, "dependencies": { - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" @@ -15,7 +15,7 @@ "devDependencies": { "@babel/core": "^7.28.5", "@babel/preset-typescript": "^7.27.1", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "babel-loader": "^10.0.0", "babel-preset-solid": "^1.9.10", "css-loader": "^7.1.2", diff --git a/examples/solid/quickstart/package.json b/examples/solid/quickstart/package.json index 6cec309396..b8ee528ba1 100644 --- a/examples/solid/quickstart/package.json +++ b/examples/solid/quickstart/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" diff --git a/examples/solid/router-monorepo-simple-lazy/package.json b/examples/solid/router-monorepo-simple-lazy/package.json index 34cdf6023f..ccbe70b582 100644 --- a/examples/solid/router-monorepo-simple-lazy/package.json +++ b/examples/solid/router-monorepo-simple-lazy/package.json @@ -8,9 +8,9 @@ "dev": "pnpm router build && pnpm post-feature build && pnpm app dev" }, "dependencies": { - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "solid-js": "^1.9.10", "redaxios": "^0.5.1" }, diff --git a/examples/solid/router-monorepo-simple-lazy/packages/router/package.json b/examples/solid/router-monorepo-simple-lazy/packages/router/package.json index 83afc30958..d6f18e536d 100644 --- a/examples/solid/router-monorepo-simple-lazy/packages/router/package.json +++ b/examples/solid/router-monorepo-simple-lazy/packages/router/package.json @@ -9,8 +9,8 @@ "types": "./dist/index.d.ts", "dependencies": { "@tanstack/history": "^1.162.0", - "@tanstack/solid-router": "^1.170.17", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/solid-router": "^1.170.18", + "@tanstack/router-plugin": "^1.168.20", "redaxios": "^0.5.1", "zod": "^4.4.3", "solid-js": "^1.9.10" diff --git a/examples/solid/router-monorepo-simple/package.json b/examples/solid/router-monorepo-simple/package.json index f9fbd5fc29..52a1d9386a 100644 --- a/examples/solid/router-monorepo-simple/package.json +++ b/examples/solid/router-monorepo-simple/package.json @@ -8,9 +8,9 @@ "dev": "pnpm router build && pnpm post-feature build && pnpm app dev" }, "dependencies": { - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "solid-js": "^1.9.10", "redaxios": "^0.5.1" }, diff --git a/examples/solid/router-monorepo-simple/packages/router/package.json b/examples/solid/router-monorepo-simple/packages/router/package.json index 37fb3f8b40..b1ac76acab 100644 --- a/examples/solid/router-monorepo-simple/packages/router/package.json +++ b/examples/solid/router-monorepo-simple/packages/router/package.json @@ -9,8 +9,8 @@ "types": "./dist/index.d.ts", "dependencies": { "@tanstack/history": "^1.162.0", - "@tanstack/solid-router": "^1.170.17", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/solid-router": "^1.170.18", + "@tanstack/router-plugin": "^1.168.20", "redaxios": "^0.5.1", "zod": "^4.4.3", "solid-js": "^1.9.10" diff --git a/examples/solid/router-monorepo-solid-query/package.json b/examples/solid/router-monorepo-solid-query/package.json index a781b16bff..9acd494517 100644 --- a/examples/solid/router-monorepo-solid-query/package.json +++ b/examples/solid/router-monorepo-solid-query/package.json @@ -12,9 +12,9 @@ "dependencies": { "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-query-devtools": "^5.90.0", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "solid-js": "^1.9.10", "redaxios": "^0.5.1" }, diff --git a/examples/solid/router-monorepo-solid-query/packages/router/package.json b/examples/solid/router-monorepo-solid-query/packages/router/package.json index dcdd421751..d96aa3ab09 100644 --- a/examples/solid/router-monorepo-solid-query/packages/router/package.json +++ b/examples/solid/router-monorepo-solid-query/packages/router/package.json @@ -10,8 +10,8 @@ "dependencies": { "@tanstack/history": "^1.162.0", "@tanstack/solid-query": "^5.90.9", - "@tanstack/solid-router": "^1.170.17", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/solid-router": "^1.170.18", + "@tanstack/router-plugin": "^1.168.20", "@router-solid-mono-solid-query/post-query": "workspace:*", "redaxios": "^0.5.1", "zod": "^4.4.3", diff --git a/examples/solid/scroll-restoration/package.json b/examples/solid/scroll-restoration/package.json index 562ec8e1e3..e80c7aea3b 100644 --- a/examples/solid/scroll-restoration/package.json +++ b/examples/solid/scroll-restoration/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@tanstack/solid-virtual": "^3.13.0", "solid-js": "^1.9.10", diff --git a/examples/solid/search-validator-adapters/package.json b/examples/solid/search-validator-adapters/package.json index 9003a5ae4d..17e74dcc7a 100644 --- a/examples/solid/search-validator-adapters/package.json +++ b/examples/solid/search-validator-adapters/package.json @@ -13,9 +13,9 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/arktype-adapter": "^1.167.0", "@tanstack/solid-query": "^5.90.9", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/router-plugin": "^1.168.20", "@tanstack/valibot-adapter": "^1.167.0", "@tanstack/zod-adapter": "^1.167.0", "arktype": "^2.1.7", diff --git a/examples/solid/start-basic-auth/package.json b/examples/solid/start-basic-auth/package.json index 05f435903d..108a260b20 100644 --- a/examples/solid/start-basic-auth/package.json +++ b/examples/solid/start-basic-auth/package.json @@ -14,9 +14,9 @@ "@libsql/client": "^0.15.15", "@prisma/adapter-libsql": "^7.0.0", "@prisma/client": "^7.0.0", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.27", + "@tanstack/solid-start": "^1.168.28", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-authjs/package.json b/examples/solid/start-basic-authjs/package.json index 6f9bf8dc7f..f6fde8367f 100644 --- a/examples/solid/start-basic-authjs/package.json +++ b/examples/solid/start-basic-authjs/package.json @@ -11,9 +11,9 @@ }, "dependencies": { "@auth/core": "^0.41.1", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.27", + "@tanstack/solid-start": "^1.168.28", "solid-js": "^1.9.10", "start-authjs": "^1.0.0", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-cloudflare/package.json b/examples/solid/start-basic-cloudflare/package.json index a5effede65..936fb0d20d 100644 --- a/examples/solid/start-basic-cloudflare/package.json +++ b/examples/solid/start-basic-cloudflare/package.json @@ -12,9 +12,9 @@ "postinstall": "WRANGLER_LOG=warn npm run --silent cf-typegen" }, "dependencies": { - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.27", + "@tanstack/solid-start": "^1.168.28", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-netlify/package.json b/examples/solid/start-basic-netlify/package.json index 64f1b48d11..c727191374 100644 --- a/examples/solid/start-basic-netlify/package.json +++ b/examples/solid/start-basic-netlify/package.json @@ -9,9 +9,9 @@ "preview": "vite preview" }, "dependencies": { - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.27", + "@tanstack/solid-start": "^1.168.28", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-nitro/package.json b/examples/solid/start-basic-nitro/package.json index c3f4d06cbe..a0fad2103f 100644 --- a/examples/solid/start-basic-nitro/package.json +++ b/examples/solid/start-basic-nitro/package.json @@ -9,9 +9,9 @@ "preview": "vite preview" }, "dependencies": { - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.27", + "@tanstack/solid-start": "^1.168.28", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-solid-query/package.json b/examples/solid/start-basic-solid-query/package.json index 20855e3faa..5fe6a67c26 100644 --- a/examples/solid/start-basic-solid-query/package.json +++ b/examples/solid/start-basic-solid-query/package.json @@ -12,10 +12,10 @@ "dependencies": { "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-query-devtools": "^5.90.0", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@tanstack/solid-router-ssr-query": "^1.167.1", - "@tanstack/solid-start": "^1.168.27", + "@tanstack/solid-start": "^1.168.28", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-static/package.json b/examples/solid/start-basic-static/package.json index efd5a53347..636e3cc8db 100644 --- a/examples/solid/start-basic-static/package.json +++ b/examples/solid/start-basic-static/package.json @@ -10,10 +10,10 @@ "start": "pnpx srvx --prod -s ../client dist/server/server.js" }, "dependencies": { - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.27", - "@tanstack/start-static-server-functions": "^1.167.18", + "@tanstack/solid-start": "^1.168.28", + "@tanstack/start-static-server-functions": "^1.167.19", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic/package.json b/examples/solid/start-basic/package.json index 8ad81cb612..2c7d7936d5 100644 --- a/examples/solid/start-basic/package.json +++ b/examples/solid/start-basic/package.json @@ -10,9 +10,9 @@ "start": "node .output/server/index.mjs" }, "dependencies": { - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.27", + "@tanstack/solid-start": "^1.168.28", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-bun/package.json b/examples/solid/start-bun/package.json index f82dff17e6..7ae945fa42 100644 --- a/examples/solid/start-bun/package.json +++ b/examples/solid/start-bun/package.json @@ -15,11 +15,11 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/solid-devtools": "^0.7.0", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@tanstack/solid-router-ssr-query": "^1.167.1", - "@tanstack/solid-start": "^1.168.27", - "@tanstack/router-plugin": "^1.168.19", + "@tanstack/solid-start": "^1.168.28", + "@tanstack/router-plugin": "^1.168.20", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" }, diff --git a/examples/solid/start-convex-better-auth/package.json b/examples/solid/start-convex-better-auth/package.json index cc288155e6..7924c5e68d 100644 --- a/examples/solid/start-convex-better-auth/package.json +++ b/examples/solid/start-convex-better-auth/package.json @@ -13,9 +13,9 @@ "dependencies": { "@convex-dev/better-auth": "^0.9.7", "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.27", + "@tanstack/solid-start": "^1.168.28", "better-auth": "^1.6.19", "clsx": "^2.1.1", "convex": "^1.28.2", diff --git a/examples/solid/start-counter/package.json b/examples/solid/start-counter/package.json index 8e4042ce00..0f5ea8e496 100644 --- a/examples/solid/start-counter/package.json +++ b/examples/solid/start-counter/package.json @@ -10,9 +10,9 @@ "start": "pnpx srvx --prod -s ../client dist/server/server.js" }, "dependencies": { - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.27", + "@tanstack/solid-start": "^1.168.28", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", diff --git a/examples/solid/start-i18n-paraglide/package.json b/examples/solid/start-i18n-paraglide/package.json index 8ca4adebce..d81127d045 100644 --- a/examples/solid/start-i18n-paraglide/package.json +++ b/examples/solid/start-i18n-paraglide/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tanstack/solid-devtools": "^0.7.0", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.27", + "@tanstack/solid-start": "^1.168.28", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-large/package.json b/examples/solid/start-large/package.json index b14f2ae995..0e941745d5 100644 --- a/examples/solid/start-large/package.json +++ b/examples/solid/start-large/package.json @@ -13,9 +13,9 @@ }, "dependencies": { "@tanstack/solid-query": "^5.90.9", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.27", + "@tanstack/solid-start": "^1.168.28", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", diff --git a/examples/solid/start-streaming-data-from-server-functions/package.json b/examples/solid/start-streaming-data-from-server-functions/package.json index 60ab77080e..6ca3946475 100644 --- a/examples/solid/start-streaming-data-from-server-functions/package.json +++ b/examples/solid/start-streaming-data-from-server-functions/package.json @@ -10,9 +10,9 @@ "start": "pnpx srvx --prod -s ../client dist/server/server.js" }, "dependencies": { - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.27", + "@tanstack/solid-start": "^1.168.28", "solid-js": "^1.9.10", "zod": "^4.4.3" }, diff --git a/examples/solid/start-supabase-basic/package.json b/examples/solid/start-supabase-basic/package.json index 604b32cdd7..a003a3eb47 100644 --- a/examples/solid/start-supabase-basic/package.json +++ b/examples/solid/start-supabase-basic/package.json @@ -14,9 +14,9 @@ "dependencies": { "@supabase/ssr": "^0.5.2", "@supabase/supabase-js": "^2.48.1", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.27", + "@tanstack/solid-start": "^1.168.28", "solid-js": "^1.9.9", "redaxios": "^0.5.1" }, diff --git a/examples/solid/start-tailwind-v4/package.json b/examples/solid/start-tailwind-v4/package.json index d53d59325c..57adb50957 100644 --- a/examples/solid/start-tailwind-v4/package.json +++ b/examples/solid/start-tailwind-v4/package.json @@ -10,9 +10,9 @@ "start": "node .output/server/index.mjs" }, "dependencies": { - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.27", + "@tanstack/solid-start": "^1.168.28", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", "zod": "^4.4.3" diff --git a/examples/solid/view-transitions/package.json b/examples/solid/view-transitions/package.json index e3a702f5d5..cf4631b96f 100644 --- a/examples/solid/view-transitions/package.json +++ b/examples/solid/view-transitions/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.19", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/router-plugin": "^1.168.20", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "redaxios": "^0.5.1", "solid-js": "^1.9.10", diff --git a/examples/solid/with-framer-motion/package.json b/examples/solid/with-framer-motion/package.json index 6c16858a6d..898d248ec9 100644 --- a/examples/solid/with-framer-motion/package.json +++ b/examples/solid/with-framer-motion/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "redaxios": "^0.5.1", "solid-js": "^1.9.10", diff --git a/examples/solid/with-trpc/package.json b/examples/solid/with-trpc/package.json index 3166804ece..8b68448842 100644 --- a/examples/solid/with-trpc/package.json +++ b/examples/solid/with-trpc/package.json @@ -11,8 +11,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.19", - "@tanstack/solid-router": "^1.170.17", + "@tanstack/router-plugin": "^1.168.20", + "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@trpc/client": "^11.4.3", "@trpc/server": "^11.4.3", diff --git a/examples/vue/basic-file-based-jsx/package.json b/examples/vue/basic-file-based-jsx/package.json index 7573628b17..fdeefd2f88 100644 --- a/examples/vue/basic-file-based-jsx/package.json +++ b/examples/vue/basic-file-based-jsx/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.19", - "@tanstack/vue-router": "^1.170.16", + "@tanstack/router-plugin": "^1.168.20", + "@tanstack/vue-router": "^1.170.17", "@tanstack/vue-router-devtools": "^1.167.0", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2", diff --git a/examples/vue/basic-file-based-sfc/package.json b/examples/vue/basic-file-based-sfc/package.json index 91581cc30d..bef5799572 100644 --- a/examples/vue/basic-file-based-sfc/package.json +++ b/examples/vue/basic-file-based-sfc/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.19", - "@tanstack/vue-router": "^1.170.16", + "@tanstack/router-plugin": "^1.168.20", + "@tanstack/vue-router": "^1.170.17", "@tanstack/vue-router-devtools": "^1.167.0", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2", diff --git a/examples/vue/basic/package.json b/examples/vue/basic/package.json index 046be6614d..49b88fa665 100644 --- a/examples/vue/basic/package.json +++ b/examples/vue/basic/package.json @@ -9,7 +9,7 @@ "start": "vite" }, "dependencies": { - "@tanstack/vue-router": "^1.170.16", + "@tanstack/vue-router": "^1.170.17", "@tanstack/vue-router-devtools": "^1.167.0", "redaxios": "^0.5.1", "vue": "^3.5.13", diff --git a/packages/react-router/CHANGELOG.md b/packages/react-router/CHANGELOG.md index dc138ae4ed..46909b1699 100644 --- a/packages/react-router/CHANGELOG.md +++ b/packages/react-router/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/react-router +## 1.170.18 + +### Patch Changes + +- Updated dependencies [[`e2dd204`](https://github.com/TanStack/router/commit/e2dd2049cb42eb219d3b447b8605066d19d9c1fa)]: + - @tanstack/router-core@1.171.15 + ## 1.170.17 ### Patch Changes diff --git a/packages/react-router/package.json b/packages/react-router/package.json index 2e63f8d973..9f4bbd6b90 100644 --- a/packages/react-router/package.json +++ b/packages/react-router/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-router", - "version": "1.170.17", + "version": "1.170.18", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-start-client/CHANGELOG.md b/packages/react-start-client/CHANGELOG.md index a3dd36024e..3407c7af4d 100644 --- a/packages/react-start-client/CHANGELOG.md +++ b/packages/react-start-client/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/react-start-client +## 1.168.16 + +### Patch Changes + +- Updated dependencies [[`e2dd204`](https://github.com/TanStack/router/commit/e2dd2049cb42eb219d3b447b8605066d19d9c1fa)]: + - @tanstack/router-core@1.171.15 + - @tanstack/react-router@1.170.18 + - @tanstack/start-client-core@1.170.14 + ## 1.168.15 ### Patch Changes diff --git a/packages/react-start-client/package.json b/packages/react-start-client/package.json index 634c565af9..0385ecf135 100644 --- a/packages/react-start-client/package.json +++ b/packages/react-start-client/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start-client", - "version": "1.168.15", + "version": "1.168.16", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-start-rsc/CHANGELOG.md b/packages/react-start-rsc/CHANGELOG.md index 69627dbebb..48185a4c10 100644 --- a/packages/react-start-rsc/CHANGELOG.md +++ b/packages/react-start-rsc/CHANGELOG.md @@ -1,5 +1,17 @@ # @tanstack/react-start-rsc +## 0.1.27 + +### Patch Changes + +- Updated dependencies [[`e2dd204`](https://github.com/TanStack/router/commit/e2dd2049cb42eb219d3b447b8605066d19d9c1fa)]: + - @tanstack/router-core@1.171.15 + - @tanstack/react-router@1.170.18 + - @tanstack/start-client-core@1.170.14 + - @tanstack/start-plugin-core@1.171.20 + - @tanstack/start-server-core@1.169.17 + - @tanstack/start-storage-context@1.167.17 + ## 0.1.26 ### Patch Changes diff --git a/packages/react-start-rsc/package.json b/packages/react-start-rsc/package.json index 498a5aff80..6e57053107 100644 --- a/packages/react-start-rsc/package.json +++ b/packages/react-start-rsc/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start-rsc", - "version": "0.1.26", + "version": "0.1.27", "description": "React Server Components support for TanStack Start", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-start-server/CHANGELOG.md b/packages/react-start-server/CHANGELOG.md index a56f3cc481..849c59b65b 100644 --- a/packages/react-start-server/CHANGELOG.md +++ b/packages/react-start-server/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/react-start-server +## 1.167.22 + +### Patch Changes + +- Updated dependencies [[`e2dd204`](https://github.com/TanStack/router/commit/e2dd2049cb42eb219d3b447b8605066d19d9c1fa)]: + - @tanstack/router-core@1.171.15 + - @tanstack/react-router@1.170.18 + - @tanstack/start-server-core@1.169.17 + ## 1.167.21 ### Patch Changes diff --git a/packages/react-start-server/package.json b/packages/react-start-server/package.json index 62c02c7268..f7136a3bc4 100644 --- a/packages/react-start-server/package.json +++ b/packages/react-start-server/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start-server", - "version": "1.167.21", + "version": "1.167.22", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-start/CHANGELOG.md b/packages/react-start/CHANGELOG.md index 90a0ad039f..5b38d1acf0 100644 --- a/packages/react-start/CHANGELOG.md +++ b/packages/react-start/CHANGELOG.md @@ -1,5 +1,18 @@ # @tanstack/react-start +## 1.168.28 + +### Patch Changes + +- Updated dependencies []: + - @tanstack/react-router@1.170.18 + - @tanstack/react-start-client@1.168.16 + - @tanstack/react-start-rsc@0.1.27 + - @tanstack/react-start-server@1.167.22 + - @tanstack/start-client-core@1.170.14 + - @tanstack/start-plugin-core@1.171.20 + - @tanstack/start-server-core@1.169.17 + ## 1.168.27 ### Patch Changes diff --git a/packages/react-start/package.json b/packages/react-start/package.json index c015ecfcf2..e1c915b749 100644 --- a/packages/react-start/package.json +++ b/packages/react-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start", - "version": "1.168.27", + "version": "1.168.28", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-cli/CHANGELOG.md b/packages/router-cli/CHANGELOG.md index 7895121dbd..d0933353be 100644 --- a/packages/router-cli/CHANGELOG.md +++ b/packages/router-cli/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/router-cli +## 1.167.19 + +### Patch Changes + +- Updated dependencies []: + - @tanstack/router-generator@1.167.19 + ## 1.167.18 ### Patch Changes diff --git a/packages/router-cli/package.json b/packages/router-cli/package.json index 50aa6f8d4b..7bff76b1b8 100644 --- a/packages/router-cli/package.json +++ b/packages/router-cli/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-cli", - "version": "1.167.18", + "version": "1.167.19", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-core/CHANGELOG.md b/packages/router-core/CHANGELOG.md index a0082099fa..360436487b 100644 --- a/packages/router-core/CHANGELOG.md +++ b/packages/router-core/CHANGELOG.md @@ -1,5 +1,19 @@ # @tanstack/router-core +## 1.171.15 + +### Patch Changes + +- [#7807](https://github.com/TanStack/router/pull/7807) [`e2dd204`](https://github.com/TanStack/router/commit/e2dd2049cb42eb219d3b447b8605066d19d9c1fa) - fix(router-core): handle window and element scroll restoration independently + + Window and element scroll targets are now handled independently. Restoring one target no longer suppresses resets for other uncached configured targets, and a restored element is no longer reset when the window has no cached position. + + Hash navigation no longer resets elements configured through `scrollToTopSelectors` and retains precedence over stale window positions through destination invalidations. + + Scroll positions are sampled when leaving a route, preserving live changes made after the most recent scroll event. This also prevents client hydration from undoing nested positions restored by the SSR script. + + Fixes [#7687](https://github.com/TanStack/router/issues/7687). + ## 1.171.14 ### Patch Changes diff --git a/packages/router-core/package.json b/packages/router-core/package.json index 303e7eca24..8f8f23f639 100644 --- a/packages/router-core/package.json +++ b/packages/router-core/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-core", - "version": "1.171.14", + "version": "1.171.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-generator/CHANGELOG.md b/packages/router-generator/CHANGELOG.md index cf16585672..005d25c061 100644 --- a/packages/router-generator/CHANGELOG.md +++ b/packages/router-generator/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/router-generator +## 1.167.19 + +### Patch Changes + +- Updated dependencies [[`e2dd204`](https://github.com/TanStack/router/commit/e2dd2049cb42eb219d3b447b8605066d19d9c1fa)]: + - @tanstack/router-core@1.171.15 + ## 1.167.18 ### Patch Changes diff --git a/packages/router-generator/package.json b/packages/router-generator/package.json index 8abf6f6d0b..3f2c64a545 100644 --- a/packages/router-generator/package.json +++ b/packages/router-generator/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-generator", - "version": "1.167.18", + "version": "1.167.19", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-plugin/CHANGELOG.md b/packages/router-plugin/CHANGELOG.md index 50dca919fa..e951de2462 100644 --- a/packages/router-plugin/CHANGELOG.md +++ b/packages/router-plugin/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/router-plugin +## 1.168.20 + +### Patch Changes + +- Updated dependencies [[`e2dd204`](https://github.com/TanStack/router/commit/e2dd2049cb42eb219d3b447b8605066d19d9c1fa)]: + - @tanstack/router-core@1.171.15 + - @tanstack/react-router@1.170.18 + - @tanstack/router-generator@1.167.19 + ## 1.168.19 ### Patch Changes diff --git a/packages/router-plugin/package.json b/packages/router-plugin/package.json index 117ef4bb14..b9a1a3fc84 100644 --- a/packages/router-plugin/package.json +++ b/packages/router-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-plugin", - "version": "1.168.19", + "version": "1.168.20", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-vite-plugin/CHANGELOG.md b/packages/router-vite-plugin/CHANGELOG.md index dbd469a8d2..accae2e5f1 100644 --- a/packages/router-vite-plugin/CHANGELOG.md +++ b/packages/router-vite-plugin/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/router-vite-plugin +## 1.167.20 + +### Patch Changes + +- Updated dependencies []: + - @tanstack/router-plugin@1.168.20 + ## 1.167.19 ### Patch Changes diff --git a/packages/router-vite-plugin/package.json b/packages/router-vite-plugin/package.json index aed06c1a13..684a7a7f9f 100644 --- a/packages/router-vite-plugin/package.json +++ b/packages/router-vite-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-vite-plugin", - "version": "1.167.19", + "version": "1.167.20", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/solid-router/CHANGELOG.md b/packages/solid-router/CHANGELOG.md index e916479ced..e63a5a7f25 100644 --- a/packages/solid-router/CHANGELOG.md +++ b/packages/solid-router/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/solid-router +## 1.170.18 + +### Patch Changes + +- Updated dependencies [[`e2dd204`](https://github.com/TanStack/router/commit/e2dd2049cb42eb219d3b447b8605066d19d9c1fa)]: + - @tanstack/router-core@1.171.15 + ## 1.170.17 ### Patch Changes diff --git a/packages/solid-router/package.json b/packages/solid-router/package.json index 1561e3eb4c..96880c4147 100644 --- a/packages/solid-router/package.json +++ b/packages/solid-router/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-router", - "version": "1.170.17", + "version": "1.170.18", "description": "Modern and scalable routing for Solid applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/solid-start-client/CHANGELOG.md b/packages/solid-start-client/CHANGELOG.md index b06ca0c723..7c8f57cdc3 100644 --- a/packages/solid-start-client/CHANGELOG.md +++ b/packages/solid-start-client/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/solid-start-client +## 1.168.16 + +### Patch Changes + +- Updated dependencies [[`e2dd204`](https://github.com/TanStack/router/commit/e2dd2049cb42eb219d3b447b8605066d19d9c1fa)]: + - @tanstack/router-core@1.171.15 + - @tanstack/solid-router@1.170.18 + - @tanstack/start-client-core@1.170.14 + ## 1.168.15 ### Patch Changes diff --git a/packages/solid-start-client/package.json b/packages/solid-start-client/package.json index 10f1c33064..afea6d4454 100644 --- a/packages/solid-start-client/package.json +++ b/packages/solid-start-client/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-start-client", - "version": "1.168.15", + "version": "1.168.16", "description": "Modern and scalable routing for Solid applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/solid-start-server/CHANGELOG.md b/packages/solid-start-server/CHANGELOG.md index fe7eb3ce66..1f6be110c6 100644 --- a/packages/solid-start-server/CHANGELOG.md +++ b/packages/solid-start-server/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/solid-start-server +## 1.167.22 + +### Patch Changes + +- Updated dependencies [[`e2dd204`](https://github.com/TanStack/router/commit/e2dd2049cb42eb219d3b447b8605066d19d9c1fa)]: + - @tanstack/router-core@1.171.15 + - @tanstack/solid-router@1.170.18 + - @tanstack/start-server-core@1.169.17 + ## 1.167.21 ### Patch Changes diff --git a/packages/solid-start-server/package.json b/packages/solid-start-server/package.json index 79f77b1a1b..aa74b81277 100644 --- a/packages/solid-start-server/package.json +++ b/packages/solid-start-server/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-start-server", - "version": "1.167.21", + "version": "1.167.22", "description": "Modern and scalable routing for Solid applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/solid-start/CHANGELOG.md b/packages/solid-start/CHANGELOG.md index 810d640920..22b034ef6b 100644 --- a/packages/solid-start/CHANGELOG.md +++ b/packages/solid-start/CHANGELOG.md @@ -1,5 +1,17 @@ # @tanstack/solid-start +## 1.168.28 + +### Patch Changes + +- Updated dependencies []: + - @tanstack/solid-router@1.170.18 + - @tanstack/solid-start-client@1.168.16 + - @tanstack/solid-start-server@1.167.22 + - @tanstack/start-client-core@1.170.14 + - @tanstack/start-plugin-core@1.171.20 + - @tanstack/start-server-core@1.169.17 + ## 1.168.27 ### Patch Changes diff --git a/packages/solid-start/package.json b/packages/solid-start/package.json index ddbbf925ed..cf7a33210f 100644 --- a/packages/solid-start/package.json +++ b/packages/solid-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-start", - "version": "1.168.27", + "version": "1.168.28", "description": "Modern and scalable routing for Solid applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-client-core/CHANGELOG.md b/packages/start-client-core/CHANGELOG.md index 29696bad29..11bb9192d3 100644 --- a/packages/start-client-core/CHANGELOG.md +++ b/packages/start-client-core/CHANGELOG.md @@ -1,5 +1,13 @@ # @tanstack/start-client-core +## 1.170.14 + +### Patch Changes + +- Updated dependencies [[`e2dd204`](https://github.com/TanStack/router/commit/e2dd2049cb42eb219d3b447b8605066d19d9c1fa)]: + - @tanstack/router-core@1.171.15 + - @tanstack/start-storage-context@1.167.17 + ## 1.170.13 ### Patch Changes diff --git a/packages/start-client-core/package.json b/packages/start-client-core/package.json index e999c97c80..30cc0a8b7a 100644 --- a/packages/start-client-core/package.json +++ b/packages/start-client-core/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-client-core", - "version": "1.170.13", + "version": "1.170.14", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-plugin-core/CHANGELOG.md b/packages/start-plugin-core/CHANGELOG.md index a25d8fc634..03108f0528 100644 --- a/packages/start-plugin-core/CHANGELOG.md +++ b/packages/start-plugin-core/CHANGELOG.md @@ -1,5 +1,15 @@ # @tanstack/start-plugin-core +## 1.171.20 + +### Patch Changes + +- Updated dependencies [[`e2dd204`](https://github.com/TanStack/router/commit/e2dd2049cb42eb219d3b447b8605066d19d9c1fa)]: + - @tanstack/router-core@1.171.15 + - @tanstack/router-generator@1.167.19 + - @tanstack/router-plugin@1.168.20 + - @tanstack/start-server-core@1.169.17 + ## 1.171.19 ### Patch Changes diff --git a/packages/start-plugin-core/package.json b/packages/start-plugin-core/package.json index 1b60d95f05..5cbae2851f 100644 --- a/packages/start-plugin-core/package.json +++ b/packages/start-plugin-core/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-plugin-core", - "version": "1.171.19", + "version": "1.171.20", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-server-core/CHANGELOG.md b/packages/start-server-core/CHANGELOG.md index a07a1b30b7..18b68b4bb9 100644 --- a/packages/start-server-core/CHANGELOG.md +++ b/packages/start-server-core/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/start-server-core +## 1.169.17 + +### Patch Changes + +- Updated dependencies [[`e2dd204`](https://github.com/TanStack/router/commit/e2dd2049cb42eb219d3b447b8605066d19d9c1fa)]: + - @tanstack/router-core@1.171.15 + - @tanstack/start-client-core@1.170.14 + - @tanstack/start-storage-context@1.167.17 + ## 1.169.16 ### Patch Changes diff --git a/packages/start-server-core/package.json b/packages/start-server-core/package.json index 1bbb1010a7..daf3530a7c 100644 --- a/packages/start-server-core/package.json +++ b/packages/start-server-core/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-server-core", - "version": "1.169.16", + "version": "1.169.17", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-static-server-functions/CHANGELOG.md b/packages/start-static-server-functions/CHANGELOG.md index 1191099aa4..27584fb74f 100644 --- a/packages/start-static-server-functions/CHANGELOG.md +++ b/packages/start-static-server-functions/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/start-static-server-functions +## 1.167.19 + +### Patch Changes + +- Updated dependencies []: + - @tanstack/start-client-core@1.170.14 + - @tanstack/react-start@1.168.28 + - @tanstack/solid-start@1.168.28 + ## 1.167.18 ### Patch Changes diff --git a/packages/start-static-server-functions/package.json b/packages/start-static-server-functions/package.json index 61196bffdf..d5f54bec0a 100644 --- a/packages/start-static-server-functions/package.json +++ b/packages/start-static-server-functions/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-static-server-functions", - "version": "1.167.18", + "version": "1.167.19", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-storage-context/CHANGELOG.md b/packages/start-storage-context/CHANGELOG.md index 0d28073b4f..d569880484 100644 --- a/packages/start-storage-context/CHANGELOG.md +++ b/packages/start-storage-context/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/start-storage-context +## 1.167.17 + +### Patch Changes + +- Updated dependencies [[`e2dd204`](https://github.com/TanStack/router/commit/e2dd2049cb42eb219d3b447b8605066d19d9c1fa)]: + - @tanstack/router-core@1.171.15 + ## 1.167.16 ### Patch Changes diff --git a/packages/start-storage-context/package.json b/packages/start-storage-context/package.json index 0fa3359847..0d33fe8d8f 100644 --- a/packages/start-storage-context/package.json +++ b/packages/start-storage-context/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-storage-context", - "version": "1.167.16", + "version": "1.167.17", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/vue-router/CHANGELOG.md b/packages/vue-router/CHANGELOG.md index 6ae02b6762..1638058127 100644 --- a/packages/vue-router/CHANGELOG.md +++ b/packages/vue-router/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/vue-router +## 1.170.17 + +### Patch Changes + +- Updated dependencies [[`e2dd204`](https://github.com/TanStack/router/commit/e2dd2049cb42eb219d3b447b8605066d19d9c1fa)]: + - @tanstack/router-core@1.171.15 + ## 1.170.16 ### Patch Changes diff --git a/packages/vue-router/package.json b/packages/vue-router/package.json index c302ba4600..96dc3fde40 100644 --- a/packages/vue-router/package.json +++ b/packages/vue-router/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/vue-router", - "version": "1.170.16", + "version": "1.170.17", "description": "Modern and scalable routing for Vue applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/vue-start-client/CHANGELOG.md b/packages/vue-start-client/CHANGELOG.md index 54e1239b1f..88d3855bbf 100644 --- a/packages/vue-start-client/CHANGELOG.md +++ b/packages/vue-start-client/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/vue-start-client +## 1.167.19 + +### Patch Changes + +- Updated dependencies [[`e2dd204`](https://github.com/TanStack/router/commit/e2dd2049cb42eb219d3b447b8605066d19d9c1fa)]: + - @tanstack/router-core@1.171.15 + - @tanstack/start-client-core@1.170.14 + - @tanstack/vue-router@1.170.17 + ## 1.167.18 ### Patch Changes diff --git a/packages/vue-start-client/package.json b/packages/vue-start-client/package.json index f191cb60fc..53427881ac 100644 --- a/packages/vue-start-client/package.json +++ b/packages/vue-start-client/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/vue-start-client", - "version": "1.167.18", + "version": "1.167.19", "description": "Modern and scalable routing for Vue applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/vue-start-server/CHANGELOG.md b/packages/vue-start-server/CHANGELOG.md index 379dfdda9e..90583544fd 100644 --- a/packages/vue-start-server/CHANGELOG.md +++ b/packages/vue-start-server/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/vue-start-server +## 1.167.22 + +### Patch Changes + +- Updated dependencies [[`e2dd204`](https://github.com/TanStack/router/commit/e2dd2049cb42eb219d3b447b8605066d19d9c1fa)]: + - @tanstack/router-core@1.171.15 + - @tanstack/start-server-core@1.169.17 + - @tanstack/vue-router@1.170.17 + ## 1.167.21 ### Patch Changes diff --git a/packages/vue-start-server/package.json b/packages/vue-start-server/package.json index bf704e93ea..9d83a25178 100644 --- a/packages/vue-start-server/package.json +++ b/packages/vue-start-server/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/vue-start-server", - "version": "1.167.21", + "version": "1.167.22", "description": "Modern and scalable routing for Vue applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/vue-start/CHANGELOG.md b/packages/vue-start/CHANGELOG.md index 1322cce2bf..9212ab4035 100644 --- a/packages/vue-start/CHANGELOG.md +++ b/packages/vue-start/CHANGELOG.md @@ -1,5 +1,17 @@ # @tanstack/vue-start +## 1.168.27 + +### Patch Changes + +- Updated dependencies []: + - @tanstack/start-client-core@1.170.14 + - @tanstack/start-plugin-core@1.171.20 + - @tanstack/start-server-core@1.169.17 + - @tanstack/vue-router@1.170.17 + - @tanstack/vue-start-client@1.167.19 + - @tanstack/vue-start-server@1.167.22 + ## 1.168.26 ### Patch Changes diff --git a/packages/vue-start/package.json b/packages/vue-start/package.json index 78e746056f..92c9929437 100644 --- a/packages/vue-start/package.json +++ b/packages/vue-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/vue-start", - "version": "1.168.26", + "version": "1.168.27", "description": "Modern and scalable routing for Vue applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index afd15b62bd..a8ccd3ec25 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2305,7 +2305,7 @@ importers: devDependencies: '@platformatic/flame': specifier: latest - version: 1.6.0 + version: 1.7.0 '@vitejs/plugin-react': specifier: ^6.0.1 version: 6.0.1(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -10295,7 +10295,7 @@ importers: specifier: workspace:* version: link:../../../packages/router-plugin '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -10332,7 +10332,7 @@ importers: specifier: workspace:* version: link:../../../packages/router-plugin '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -10372,7 +10372,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -10412,7 +10412,7 @@ importers: specifier: ^5.99.0 version: 5.99.0(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -10446,7 +10446,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -10477,7 +10477,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -10514,7 +10514,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -10557,7 +10557,7 @@ importers: specifier: ^5.90.0 version: 5.90.4(@tanstack/solid-query@5.99.0(solid-js@1.9.12))(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -10597,7 +10597,7 @@ importers: specifier: ^5.90.0 version: 5.90.4(@tanstack/solid-query@5.99.0(solid-js@1.9.12))(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -10634,7 +10634,7 @@ importers: specifier: workspace:* version: link:../../../packages/router-plugin '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router compression: specifier: ^1.8.0 @@ -10674,7 +10674,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -10729,7 +10729,7 @@ importers: specifier: workspace:* version: link:../../../packages/router-plugin '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -10769,7 +10769,7 @@ importers: specifier: workspace:* version: link:../../../packages/router-plugin '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -10806,7 +10806,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -10843,7 +10843,7 @@ importers: specifier: workspace:* version: link:../../../packages/router-plugin '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router solid-js: specifier: 1.9.12 @@ -10874,7 +10874,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -10911,7 +10911,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -10957,7 +10957,7 @@ importers: specifier: ^5.90.0 version: 5.90.4(@tanstack/solid-query@5.99.0(solid-js@1.9.12))(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11003,7 +11003,7 @@ importers: specifier: ^5.90.0 version: 5.90.4(@tanstack/solid-query@5.99.0(solid-js@1.9.12))(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11046,7 +11046,7 @@ importers: specifier: ^5.99.0 version: 5.99.0(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11083,7 +11083,7 @@ importers: specifier: ^5.99.0 version: 5.99.0(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11117,7 +11117,7 @@ importers: specifier: ^5.99.0 version: 5.99.0(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11148,7 +11148,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11176,7 +11176,7 @@ importers: specifier: workspace:* version: link:../../../packages/router-plugin '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11207,7 +11207,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11244,7 +11244,7 @@ importers: specifier: ^4.2.2 version: 4.2.2 '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11278,7 +11278,7 @@ importers: examples/solid/quickstart-webpack-file-based: dependencies: '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11339,7 +11339,7 @@ importers: specifier: workspace:* version: link:../../../packages/router-plugin '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11373,7 +11373,7 @@ importers: specifier: workspace:* version: link:../../../packages/router-plugin '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11413,7 +11413,7 @@ importers: specifier: ^5.90.0 version: 5.90.4(@tanstack/solid-query@5.99.0(solid-js@1.9.12))(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11447,7 +11447,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11487,7 +11487,7 @@ importers: specifier: ^5.99.0 version: 5.99.0(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11533,7 +11533,7 @@ importers: examples/solid/start-basic: dependencies: '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11585,7 +11585,7 @@ importers: specifier: ^7.0.0 version: 7.0.0(prisma@7.0.0(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@6.0.2))(typescript@6.0.2) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11634,7 +11634,7 @@ importers: specifier: ^0.41.1 version: 0.41.1 '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11674,7 +11674,7 @@ importers: examples/solid/start-basic-cloudflare: dependencies: '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11714,7 +11714,7 @@ importers: examples/solid/start-basic-netlify: dependencies: '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11751,7 +11751,7 @@ importers: examples/solid/start-basic-nitro: dependencies: '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11794,7 +11794,7 @@ importers: specifier: ^5.90.0 version: 5.90.4(@tanstack/solid-query@5.99.0(solid-js@1.9.12))(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11837,7 +11837,7 @@ importers: examples/solid/start-basic-static: dependencies: '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11889,7 +11889,7 @@ importers: specifier: ^0.7.0 version: 0.7.14(csstype@3.2.3)(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11953,7 +11953,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12008,7 +12008,7 @@ importers: examples/solid/start-counter: dependencies: '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12051,7 +12051,7 @@ importers: specifier: ^0.7.0 version: 0.7.14(csstype@3.2.3)(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12091,7 +12091,7 @@ importers: specifier: ^5.99.0 version: 5.99.0(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12134,7 +12134,7 @@ importers: examples/solid/start-streaming-data-from-server-functions: dependencies: '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12171,7 +12171,7 @@ importers: specifier: ^2.48.1 version: 2.48.1 '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12208,7 +12208,7 @@ importers: examples/solid/start-tailwind-v4: dependencies: '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12254,7 +12254,7 @@ importers: specifier: workspace:* version: link:../../../packages/router-plugin '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12288,7 +12288,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12328,7 +12328,7 @@ importers: specifier: workspace:* version: link:../../../packages/router-plugin '@tanstack/solid-router': - specifier: ^1.170.17 + specifier: ^1.170.18 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -14505,6 +14505,10 @@ packages: resolution: {integrity: sha512-Th8u7pvoguTfbUx/mlZLHD9TxFCHO+wRAvlEaYFAYAmMvbPLww4YowvTy1UMvpi8LbUbwil3Fo8rKCrilvXHhQ==} engines: {node: '>=16'} + '@datadog/pprof@5.15.1': + resolution: {integrity: sha512-4mI750tX6okNROS4YKvGQjyAQ+VqfzDqzysCytOJhL2E2ktK/q4M0PtC7j70tiirGb/fO9fjma3IMNSRLYX+xQ==} + engines: {node: '>=16'} + '@dependents/detective-less@5.0.1': resolution: {integrity: sha512-Y6+WUMsTFWE5jb20IFP4YGa5IrGY/+a/FbOSjDF/wz9gepU2hwCYSXRHP/vPwBvwcY3SVMASt4yXxbXNXigmZQ==} engines: {node: '>=18'} @@ -17180,6 +17184,11 @@ packages: engines: {node: '>=22.6.0'} hasBin: true + '@platformatic/flame@1.7.0': + resolution: {integrity: sha512-WGCrRcRJBMbSb1y7d8L+VOewBDk0Rfs+JQ1K/LLqPkstsNebLFprzk180P8jgHgT3yXe8TyxTwKVcuZkKviLFA==} + engines: {node: '>=22.6.0'} + hasBin: true + '@playwright/test@1.58.0': resolution: {integrity: sha512-fWza+Lpbj6SkQKCrU6si4iu+fD2dD3gxNHFhUPxsfXBPhnv3rRSQVd0NtBUT9Z/RhF/boCBcuUaMUSTRTopjZg==} engines: {node: '>=18'} @@ -22574,15 +22583,6 @@ packages: crossws: optional: true - h3@2.0.1-rc.2: - resolution: {integrity: sha512-2vS7OETzPDzGQxmmcs6ttu7p0NW25zAdkPXYOr43dn4GZf81uUljJvupa158mcpUGpsQUqIy4O4THWUQT1yVeA==} - engines: {node: '>=20.11.1'} - peerDependencies: - crossws: ^0.4.1 - peerDependenciesMeta: - crossws: - optional: true - h3@2.0.1-rc.20: resolution: {integrity: sha512-28ljodXuUp0fZovdiSRq4G9OgrxCztrJe5VdYzXAB7ueRvI7pIUqLU14Xi3XqdYJ/khXjfpUOOD2EQa6CmBgsg==} engines: {node: '>=20.11.1'} @@ -23856,31 +23856,12 @@ packages: netlify-redirector@0.5.0: resolution: {integrity: sha512-4zdzIP+6muqPCuE8avnrgDJ6KW/2+UpHTRcTbMXCIRxiRmyrX+IZ4WSJGZdHPWF3WmQpXpy603XxecZ9iygN7w==} - nf3@0.1.12: - resolution: {integrity: sha512-qbMXT7RTGh74MYWPeqTIED8nDW70NXOULVHpdWcdZ7IVHVnAsMV9fNugSNnvooipDc1FMOzpis7T9nXJEbJhvQ==} - nf3@0.3.11: resolution: {integrity: sha512-ObKp/SA3f1g1f/OMeDlRWaZmqGgk7A0NnDIbeO7c/MV4r/quMlpP/BsqMGuTi3lUlXbC1On8YH7ICM2u2bIAOw==} nf3@0.3.6: resolution: {integrity: sha512-/XRUUILTAyuy1XunyVQuqGp8aEmZ2TfRTn8Rji+FA4xqv20qzL4jV7Reqbuey2XucKgPeRVcEYGScmJM0UnB6Q==} - nitro-nightly@4.0.0-20251010-091516-7cafddba: - resolution: {integrity: sha512-biADkmoR/Nb9OmUURTfDI2BpGo2IMEt2RbsiMhjqdwz2w3tEm+tx0Hd28LXjBCwid+l8jpqyfmpwc8jzwdng3w==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - rolldown: '*' - vite: ^8.0.14 - xml2js: ^0.6.2 - peerDependenciesMeta: - rolldown: - optional: true - vite: - optional: true - xml2js: - optional: true - nitro@3.0.1-alpha.2: resolution: {integrity: sha512-YviDY5J/trS821qQ1fpJtpXWIdPYiOizC/meHavlm1Hfuhx//H+Egd1+4C5SegJRgtWMnRPW9n//6Woaw81cTQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -24496,6 +24477,11 @@ packages: engines: {node: '>=22.6.0'} hasBin: true + pprof-to-md@0.2.0: + resolution: {integrity: sha512-+xo3aSoDHI+M6DnM5jW6Iva4I0LS3NmAAkci3fGSIj6JiP8hs5o8IWpmkTtoQ62l9joDC8eSSOZE9dt1t38HHw==} + engines: {node: '>=22.6.0'} + hasBin: true + preact-render-to-string@6.5.11: resolution: {integrity: sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw==} peerDependencies: @@ -24886,10 +24872,6 @@ packages: renderkid@3.0.0: resolution: {integrity: sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==} - rendu@0.0.6: - resolution: {integrity: sha512-nZ512Dw0MxKiIYfCVv8DPe6ig4m0Qt3FOYBJEXrammjIYBBPuHaudc0AGfYx+iyOw2q0itAtPywiVZXtTFCsig==} - hasBin: true - repeat-string@1.6.1: resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} engines: {node: '>=0.10'} @@ -25399,11 +25381,6 @@ packages: engines: {node: '>=20.16.0'} hasBin: true - srvx@0.8.16: - resolution: {integrity: sha512-hmcGW4CgroeSmzgF1Ihwgl+Ths0JqAJ7HwjP2X7e3JzY7u4IydLMcdnlqGQiQGUswz+PO9oh/KtCpOISIvs9QQ==} - engines: {node: '>=20.16.0'} - hasBin: true - stable-hash-x@0.2.0: resolution: {integrity: sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==} engines: {node: '>=12.0.0'} @@ -25978,9 +25955,6 @@ packages: resolution: {integrity: sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA==} engines: {node: '>=20.18.1'} - unenv@2.0.0-rc.21: - resolution: {integrity: sha512-Wj7/AMtE9MRnAXa6Su3Lk0LNCfqDYgfwVjwRFVum9U7wsto1imuHqk4kTm7Jni+5A0Hn7dttL6O/zjvUvoo+8A==} - unenv@2.0.0-rc.24: resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} @@ -26096,80 +26070,6 @@ packages: uploadthing: optional: true - unstorage@2.0.0-alpha.3: - resolution: {integrity: sha512-BeoqISVh8jxqnPseHH7/92twe2VkQztrudXg8RFZVbXb4ckkFdpLk1LnNvsUndDltyodBMVxgI6V7JcbJYt2VQ==} - peerDependencies: - '@azure/app-configuration': ^1.8.0 - '@azure/cosmos': ^4.2.0 - '@azure/data-tables': ^13.3.0 - '@azure/identity': ^4.6.0 - '@azure/keyvault-secrets': ^4.9.0 - '@azure/storage-blob': ^12.26.0 - '@capacitor/preferences': ^6.0.3 || ^7.0.0 - '@deno/kv': '>=0.9.0' - '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 - '@planetscale/database': ^1.19.0 - '@upstash/redis': ^1.34.3 - '@vercel/blob': '>=0.27.1' - '@vercel/functions': ^2.2.12 || ^3.0.0 - '@vercel/kv': ^1.0.1 - aws4fetch: ^1.0.20 - chokidar: ^4.0.3 - db0: '>=0.2.1' - idb-keyval: ^6.2.1 - ioredis: ^5.4.2 - lru-cache: ^11.2.2 - mongodb: ^6.20.0 - ofetch: ^1.4.1 - uploadthing: ^7.4.4 - peerDependenciesMeta: - '@azure/app-configuration': - optional: true - '@azure/cosmos': - optional: true - '@azure/data-tables': - optional: true - '@azure/identity': - optional: true - '@azure/keyvault-secrets': - optional: true - '@azure/storage-blob': - optional: true - '@capacitor/preferences': - optional: true - '@deno/kv': - optional: true - '@netlify/blobs': - optional: true - '@planetscale/database': - optional: true - '@upstash/redis': - optional: true - '@vercel/blob': - optional: true - '@vercel/functions': - optional: true - '@vercel/kv': - optional: true - aws4fetch: - optional: true - chokidar: - optional: true - db0: - optional: true - idb-keyval: - optional: true - ioredis: - optional: true - lru-cache: - optional: true - mongodb: - optional: true - ofetch: - optional: true - uploadthing: - optional: true - unstorage@2.0.0-alpha.5: resolution: {integrity: sha512-Sj8btci21Twnd6M+N+MHhjg3fVn6lAPElPmvFTe0Y/wR0WImErUdA1PzlAaUavHylJ7uDiFwlZDQKm0elG4b7g==} peerDependencies: @@ -28130,6 +28030,12 @@ snapshots: pprof-format: 2.2.1 source-map: 0.7.6 + '@datadog/pprof@5.15.1': + dependencies: + node-gyp-build: 4.8.4 + pprof-format: 2.2.1 + source-map: 0.7.6 + '@dependents/detective-less@5.0.1': dependencies: gonzales-pe: 4.3.0 @@ -30656,6 +30562,14 @@ snapshots: pprof-to-md: 0.1.0 react-pprof: 1.4.0 + '@platformatic/flame@1.7.0': + dependencies: + '@datadog/pprof': 5.15.1 + fastify: 5.7.1 + pprof-format: 2.2.1 + pprof-to-md: 0.2.0 + react-pprof: 1.4.0 + '@playwright/test@1.58.0': dependencies: playwright: 1.58.0 @@ -35344,10 +35258,6 @@ snapshots: srvx: 0.11.15 optional: true - crossws@0.4.5(srvx@0.8.16): - optionalDependencies: - srvx: 0.8.16 - css-loader@7.1.2(webpack@5.97.1): dependencies: icss-utils: 5.1.0(postcss@8.5.6) @@ -36920,15 +36830,6 @@ snapshots: optionalDependencies: crossws: 0.4.4(srvx@0.11.15) - h3@2.0.1-rc.2(crossws@0.4.5(srvx@0.8.16)): - dependencies: - cookie-es: 2.0.0 - fetchdts: 0.1.7 - rou3: 0.7.12 - srvx: 0.8.16 - optionalDependencies: - crossws: 0.4.5(srvx@0.8.16) - h3@2.0.1-rc.20(crossws@0.4.5(srvx@0.11.15)): dependencies: rou3: 0.8.1 @@ -38213,63 +38114,10 @@ snapshots: netlify-redirector@0.5.0: {} - nf3@0.1.12: {} - nf3@0.3.11: {} nf3@0.3.6: {} - nitro-nightly@4.0.0-20251010-091516-7cafddba(@electric-sql/pglite@0.3.2)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@4.0.3)(ioredis@5.9.2)(lru-cache@11.5.1)(mysql2@3.15.3)(rolldown@1.0.2)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)): - dependencies: - consola: 3.4.2 - cookie-es: 2.0.0 - crossws: 0.4.5(srvx@0.8.16) - db0: 0.3.4(@electric-sql/pglite@0.3.2)(@libsql/client@0.15.15)(mysql2@3.15.3) - esbuild: 0.25.10 - fetchdts: 0.1.7 - h3: 2.0.1-rc.2(crossws@0.4.5(srvx@0.8.16)) - jiti: 2.7.0 - nf3: 0.1.12 - ofetch: 1.5.1 - ohash: 2.0.11 - rendu: 0.0.6 - rollup: 4.56.0 - srvx: 0.8.16 - undici: 7.27.2 - unenv: 2.0.0-rc.21 - unstorage: 2.0.0-alpha.3(@netlify/blobs@10.1.0)(chokidar@4.0.3)(db0@0.3.4(@electric-sql/pglite@0.3.2)(@libsql/client@0.15.15)(mysql2@3.15.3))(ioredis@5.9.2)(lru-cache@11.5.1)(ofetch@1.5.1) - optionalDependencies: - rolldown: 1.0.2 - vite: 8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@electric-sql/pglite' - - '@libsql/client' - - '@netlify/blobs' - - '@planetscale/database' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - better-sqlite3 - - chokidar - - drizzle-orm - - idb-keyval - - ioredis - - lru-cache - - mongodb - - mysql2 - - sqlite3 - - uploadthing - nitro@3.0.1-alpha.2(@electric-sql/pglite@0.3.2)(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(ioredis@5.9.2)(lru-cache@11.5.1)(mysql2@3.15.3)(rolldown@1.0.2)(rollup@4.56.0)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)): dependencies: consola: 3.4.2 @@ -39200,6 +39048,10 @@ snapshots: dependencies: pprof-format: 2.2.1 + pprof-to-md@0.2.0: + dependencies: + pprof-format: 2.2.1 + preact-render-to-string@6.5.11(preact@10.24.3): dependencies: preact: 10.24.3 @@ -39664,11 +39516,6 @@ snapshots: lodash: 4.17.21 strip-ansi: 6.0.1 - rendu@0.0.6: - dependencies: - cookie-es: 2.0.0 - srvx: 0.8.16 - repeat-string@1.6.1: {} require-directory@2.1.1: {} @@ -40301,8 +40148,6 @@ snapshots: srvx@0.11.15: {} - srvx@0.8.16: {} - stable-hash-x@0.2.0: {} stack-trace@0.0.10: {} @@ -40824,14 +40669,6 @@ snapshots: undici@7.27.2: {} - unenv@2.0.0-rc.21: - dependencies: - defu: 6.1.4 - exsolve: 1.0.8 - ohash: 2.0.11 - pathe: 2.0.3 - ufo: 1.6.3 - unenv@2.0.0-rc.24: dependencies: pathe: 2.0.3 @@ -40933,15 +40770,6 @@ snapshots: db0: 0.3.4(@electric-sql/pglite@0.3.2)(@libsql/client@0.15.15)(mysql2@3.15.3) ioredis: 5.9.2 - unstorage@2.0.0-alpha.3(@netlify/blobs@10.1.0)(chokidar@4.0.3)(db0@0.3.4(@electric-sql/pglite@0.3.2)(@libsql/client@0.15.15)(mysql2@3.15.3))(ioredis@5.9.2)(lru-cache@11.5.1)(ofetch@1.5.1): - optionalDependencies: - '@netlify/blobs': 10.1.0 - chokidar: 4.0.3 - db0: 0.3.4(@electric-sql/pglite@0.3.2)(@libsql/client@0.15.15)(mysql2@3.15.3) - ioredis: 5.9.2 - lru-cache: 11.5.1 - ofetch: 1.5.1 - unstorage@2.0.0-alpha.5(@netlify/blobs@10.1.0)(chokidar@5.0.0)(db0@0.3.4(@electric-sql/pglite@0.3.2)(@libsql/client@0.15.15)(mysql2@3.15.3))(ioredis@5.9.2)(lru-cache@11.5.1)(ofetch@2.0.0-alpha.3): optionalDependencies: '@netlify/blobs': 10.1.0 From 4d4171fdda0ffd36eae1f91564e61ae51b12d845 Mon Sep 17 00:00:00 2001 From: Florian Fehrenbacher Date: Mon, 13 Jul 2026 23:38:26 +0200 Subject: [PATCH 06/51] fix: use relative path for srvx static files (#7799) --- docs/start/framework/react/guide/hosting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/start/framework/react/guide/hosting.md b/docs/start/framework/react/guide/hosting.md index 23fdd63226..aa1806c6bf 100644 --- a/docs/start/framework/react/guide/hosting.md +++ b/docs/start/framework/react/guide/hosting.md @@ -298,7 +298,7 @@ npm install srvx ```json "build": "rsbuild build", - "start": "srvx --prod -s dist/client dist/server/index.js" + "start": "srvx --prod -s ../client dist/server/index.js" ``` Express or any other custom Node.js server works too, as long as it serves the client assets and calls the server entry's `fetch` handler for dynamic requests. From a41f26547c817cc90a342fafdbfddd70db9b203b Mon Sep 17 00:00:00 2001 From: Pierre-Luc Auclair Date: Mon, 13 Jul 2026 18:06:59 -0400 Subject: [PATCH 07/51] chore(deps): bump @playwright/test to ^1.61.0 across the workspace (#7772) --- .../basepath-file-based/package.json | 2 +- .../basic-esbuild-file-based/package.json | 2 +- .../package.json | 2 +- .../basic-file-based/package.json | 2 +- .../basic-react-query-file-based/package.json | 2 +- .../basic-react-query/package.json | 2 +- .../basic-scroll-restoration/package.json | 2 +- .../basic-virtual-file-based/package.json | 2 +- .../package.json | 2 +- e2e/react-router/basic/package.json | 2 +- .../escaped-special-strings/package.json | 2 +- .../generator-cli-only/package.json | 2 +- e2e/react-router/i18n-paraglide/package.json | 2 +- .../js-only-file-based/package.json | 2 +- e2e/react-router/match-params/package.json | 2 +- .../rspack-basic-file-based/package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../sentry-integration/package.json | 2 +- .../view-transitions/package.json | 2 +- e2e/react-start/basic-auth/package.json | 2 +- e2e/react-start/basic-cloudflare/package.json | 2 +- .../basic-react-query/package.json | 2 +- e2e/react-start/basic/package.json | 2 +- e2e/react-start/clerk-basic/package.json | 2 +- e2e/react-start/csp/package.json | 2 +- e2e/react-start/css-inline/package.json | 2 +- e2e/react-start/css-modules/package.json | 2 +- e2e/react-start/custom-basepath/package.json | 2 +- .../custom-server-rsbuild/package.json | 2 +- e2e/react-start/dev-ssr-styles/package.json | 2 +- e2e/react-start/early-hints/package.json | 2 +- e2e/react-start/hmr/package.json | 2 +- e2e/react-start/i18n-paraglide/package.json | 2 +- .../package.json | 2 +- .../import-protection/package.json | 2 +- .../query-integration/package.json | 2 +- .../rsc-deferred-hydration/package.json | 2 +- e2e/react-start/rsc-query/package.json | 2 +- e2e/react-start/rsc-rsbuild/package.json | 2 +- e2e/react-start/rsc/package.json | 2 +- .../scroll-restoration/package.json | 2 +- .../package.json | 2 +- e2e/react-start/server-functions/package.json | 2 +- .../package.json | 2 +- e2e/react-start/server-routes/package.json | 2 +- .../split-base-and-basepath/package.json | 2 +- e2e/react-start/start-manifest/package.json | 2 +- .../static-server-functions/package.json | 2 +- e2e/react-start/streaming-ssr/package.json | 2 +- .../transform-asset-urls/package.json | 2 +- e2e/react-start/virtual-routes/package.json | 2 +- e2e/react-start/website/package.json | 2 +- .../basepath-file-based/package.json | 2 +- .../basic-esbuild-file-based/package.json | 2 +- .../package.json | 2 +- .../basic-file-based/package.json | 2 +- .../basic-scroll-restoration/package.json | 2 +- .../basic-solid-query-file-based/package.json | 2 +- .../basic-solid-query/package.json | 2 +- .../basic-virtual-file-based/package.json | 2 +- .../package.json | 2 +- e2e/solid-router/basic/package.json | 2 +- .../generator-cli-only/package.json | 2 +- .../js-only-file-based/package.json | 2 +- .../rspack-basic-file-based/package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../sentry-integration/package.json | 2 +- .../view-transitions/package.json | 2 +- e2e/solid-start/basic-auth/package.json | 2 +- e2e/solid-start/basic-cloudflare/package.json | 2 +- .../basic-solid-query/package.json | 2 +- e2e/solid-start/basic/package.json | 2 +- e2e/solid-start/csp/package.json | 2 +- e2e/solid-start/css-modules/package.json | 2 +- e2e/solid-start/custom-basepath/package.json | 2 +- .../query-integration/package.json | 2 +- .../scroll-restoration/package.json | 2 +- e2e/solid-start/server-functions/package.json | 2 +- e2e/solid-start/server-routes/package.json | 2 +- .../solid-query-layout-suspense/package.json | 2 +- e2e/solid-start/start-manifest/package.json | 2 +- e2e/solid-start/virtual-routes/package.json | 2 +- e2e/solid-start/website/package.json | 2 +- .../basepath-file-based/package.json | 2 +- .../basic-esbuild-file-based/package.json | 2 +- .../basic-file-based-jsx/package.json | 2 +- .../basic-file-based-sfc/package.json | 2 +- .../basic-scroll-restoration/package.json | 2 +- .../basic-virtual-file-based/package.json | 2 +- .../package.json | 2 +- .../basic-vue-query-file-based/package.json | 2 +- e2e/vue-router/basic-vue-query/package.json | 2 +- e2e/vue-router/basic/package.json | 2 +- .../generator-cli-only/package.json | 2 +- .../js-only-file-based/package.json | 2 +- .../rspack-basic-file-based/package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../sentry-integration/package.json | 2 +- e2e/vue-router/view-transitions/package.json | 2 +- e2e/vue-start/basic-auth/package.json | 2 +- e2e/vue-start/basic-cloudflare/package.json | 2 +- e2e/vue-start/basic-vue-query/package.json | 2 +- e2e/vue-start/basic/package.json | 2 +- e2e/vue-start/css-modules/package.json | 2 +- e2e/vue-start/custom-basepath/package.json | 2 +- e2e/vue-start/query-integration/package.json | 2 +- e2e/vue-start/scroll-restoration/package.json | 2 +- e2e/vue-start/server-functions/package.json | 2 +- e2e/vue-start/server-routes/package.json | 2 +- e2e/vue-start/start-manifest/package.json | 2 +- e2e/vue-start/virtual-routes/package.json | 2 +- e2e/vue-start/website/package.json | 2 +- pnpm-lock.yaml | 488 +++++++++--------- pnpm-workspace.yaml | 2 +- 117 files changed, 360 insertions(+), 360 deletions(-) diff --git a/e2e/react-router/basepath-file-based/package.json b/e2e/react-router/basepath-file-based/package.json index dd0956e96f..a155cc2173 100644 --- a/e2e/react-router/basepath-file-based/package.json +++ b/e2e/react-router/basepath-file-based/package.json @@ -18,7 +18,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", diff --git a/e2e/react-router/basic-esbuild-file-based/package.json b/e2e/react-router/basic-esbuild-file-based/package.json index 93e1263d11..2ff977a4f4 100644 --- a/e2e/react-router/basic-esbuild-file-based/package.json +++ b/e2e/react-router/basic-esbuild-file-based/package.json @@ -19,7 +19,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", diff --git a/e2e/react-router/basic-file-based-code-splitting/package.json b/e2e/react-router/basic-file-based-code-splitting/package.json index 14a8f0373f..f3654137be 100644 --- a/e2e/react-router/basic-file-based-code-splitting/package.json +++ b/e2e/react-router/basic-file-based-code-splitting/package.json @@ -21,7 +21,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", diff --git a/e2e/react-router/basic-file-based/package.json b/e2e/react-router/basic-file-based/package.json index da2df215af..be3d8add30 100644 --- a/e2e/react-router/basic-file-based/package.json +++ b/e2e/react-router/basic-file-based/package.json @@ -23,7 +23,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", diff --git a/e2e/react-router/basic-react-query-file-based/package.json b/e2e/react-router/basic-react-query-file-based/package.json index dd6c71afdd..6548381772 100644 --- a/e2e/react-router/basic-react-query-file-based/package.json +++ b/e2e/react-router/basic-react-query-file-based/package.json @@ -24,7 +24,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", diff --git a/e2e/react-router/basic-react-query/package.json b/e2e/react-router/basic-react-query/package.json index b66abda5fb..81eb9eb67b 100644 --- a/e2e/react-router/basic-react-query/package.json +++ b/e2e/react-router/basic-react-query/package.json @@ -22,7 +22,7 @@ "tailwindcss": "^4.2.2" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", diff --git a/e2e/react-router/basic-scroll-restoration/package.json b/e2e/react-router/basic-scroll-restoration/package.json index ab00360d48..d8ebcb414b 100644 --- a/e2e/react-router/basic-scroll-restoration/package.json +++ b/e2e/react-router/basic-scroll-restoration/package.json @@ -21,7 +21,7 @@ "tailwindcss": "^4.2.2" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", diff --git a/e2e/react-router/basic-virtual-file-based/package.json b/e2e/react-router/basic-virtual-file-based/package.json index 7b9c5945ee..891f1d6328 100644 --- a/e2e/react-router/basic-virtual-file-based/package.json +++ b/e2e/react-router/basic-virtual-file-based/package.json @@ -23,7 +23,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", diff --git a/e2e/react-router/basic-virtual-named-export-config-file-based/package.json b/e2e/react-router/basic-virtual-named-export-config-file-based/package.json index f3dbf81ce0..d3872b01e6 100644 --- a/e2e/react-router/basic-virtual-named-export-config-file-based/package.json +++ b/e2e/react-router/basic-virtual-named-export-config-file-based/package.json @@ -23,7 +23,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", diff --git a/e2e/react-router/basic/package.json b/e2e/react-router/basic/package.json index c7c1661933..a8de9acc3f 100644 --- a/e2e/react-router/basic/package.json +++ b/e2e/react-router/basic/package.json @@ -20,7 +20,7 @@ "tailwindcss": "^4.2.2" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", diff --git a/e2e/react-router/escaped-special-strings/package.json b/e2e/react-router/escaped-special-strings/package.json index 961d2423cd..71a3cb08ca 100644 --- a/e2e/react-router/escaped-special-strings/package.json +++ b/e2e/react-router/escaped-special-strings/package.json @@ -18,7 +18,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", diff --git a/e2e/react-router/generator-cli-only/package.json b/e2e/react-router/generator-cli-only/package.json index 9fbe253126..6bbcccf4bd 100644 --- a/e2e/react-router/generator-cli-only/package.json +++ b/e2e/react-router/generator-cli-only/package.json @@ -21,7 +21,7 @@ "tailwindcss": "^4.2.2" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", diff --git a/e2e/react-router/i18n-paraglide/package.json b/e2e/react-router/i18n-paraglide/package.json index 94a36498ed..31a22eb66b 100644 --- a/e2e/react-router/i18n-paraglide/package.json +++ b/e2e/react-router/i18n-paraglide/package.json @@ -19,7 +19,7 @@ }, "devDependencies": { "@inlang/paraglide-js": "^2.4.0", - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@tanstack/router-plugin": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/react-router/js-only-file-based/package.json b/e2e/react-router/js-only-file-based/package.json index 6505c4a1e4..d18d4da143 100644 --- a/e2e/react-router/js-only-file-based/package.json +++ b/e2e/react-router/js-only-file-based/package.json @@ -20,7 +20,7 @@ "tailwindcss": "^4.2.2" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@tanstack/router-plugin": "workspace:^", "@types/react": "^19.0.8", diff --git a/e2e/react-router/match-params/package.json b/e2e/react-router/match-params/package.json index 033a1bb5fe..3282e5e5c2 100644 --- a/e2e/react-router/match-params/package.json +++ b/e2e/react-router/match-params/package.json @@ -14,7 +14,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", diff --git a/e2e/react-router/rspack-basic-file-based/package.json b/e2e/react-router/rspack-basic-file-based/package.json index 74659b51e1..f54f8f83ca 100644 --- a/e2e/react-router/rspack-basic-file-based/package.json +++ b/e2e/react-router/rspack-basic-file-based/package.json @@ -17,7 +17,7 @@ "redaxios": "^0.5.1" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@rsbuild/core": "^2.0.11", "@rsbuild/plugin-react": "^2.0.0", "@tailwindcss/postcss": "^4.2.2", diff --git a/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/package.json b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/package.json index a7b9eb58d4..18e7851ac9 100644 --- a/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/package.json +++ b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/package.json @@ -17,7 +17,7 @@ "redaxios": "^0.5.1" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@rsbuild/core": "^2.0.11", "@rsbuild/plugin-react": "^2.0.0", "@tailwindcss/postcss": "^4.2.2", diff --git a/e2e/react-router/scroll-restoration-sandbox-vite/package.json b/e2e/react-router/scroll-restoration-sandbox-vite/package.json index 215de36a70..a7fc908019 100644 --- a/e2e/react-router/scroll-restoration-sandbox-vite/package.json +++ b/e2e/react-router/scroll-restoration-sandbox-vite/package.json @@ -25,7 +25,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", diff --git a/e2e/react-router/sentry-integration/package.json b/e2e/react-router/sentry-integration/package.json index d97446da9f..e275758b83 100644 --- a/e2e/react-router/sentry-integration/package.json +++ b/e2e/react-router/sentry-integration/package.json @@ -23,7 +23,7 @@ "tailwindcss": "^4.2.2" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", diff --git a/e2e/react-router/view-transitions/package.json b/e2e/react-router/view-transitions/package.json index 1c675b7258..1f068d690a 100644 --- a/e2e/react-router/view-transitions/package.json +++ b/e2e/react-router/view-transitions/package.json @@ -22,7 +22,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", diff --git a/e2e/react-start/basic-auth/package.json b/e2e/react-start/basic-auth/package.json index 4475cf5adf..5976695006 100644 --- a/e2e/react-start/basic-auth/package.json +++ b/e2e/react-start/basic-auth/package.json @@ -25,7 +25,7 @@ "tailwind-merge": "^3.6.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/react-start/basic-cloudflare/package.json b/e2e/react-start/basic-cloudflare/package.json index 4d90b4124e..45ea13f9da 100644 --- a/e2e/react-start/basic-cloudflare/package.json +++ b/e2e/react-start/basic-cloudflare/package.json @@ -21,7 +21,7 @@ }, "devDependencies": { "@cloudflare/vite-plugin": "^1.29.0", - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/react-start/basic-react-query/package.json b/e2e/react-start/basic-react-query/package.json index 242636c63f..c804a89eb3 100644 --- a/e2e/react-start/basic-react-query/package.json +++ b/e2e/react-start/basic-react-query/package.json @@ -25,7 +25,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/react-start/basic/package.json b/e2e/react-start/basic/package.json index 37a3f4a0e2..f11df33f3f 100644 --- a/e2e/react-start/basic/package.json +++ b/e2e/react-start/basic/package.json @@ -31,7 +31,7 @@ "tailwind-merge": "^3.6.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@rsbuild/core": "^2.0.11", "@rsbuild/plugin-react": "^2.0.0", "@tailwindcss/postcss": "^4.2.2", diff --git a/e2e/react-start/clerk-basic/package.json b/e2e/react-start/clerk-basic/package.json index 2ff6a85a3a..137a0e7fda 100644 --- a/e2e/react-start/clerk-basic/package.json +++ b/e2e/react-start/clerk-basic/package.json @@ -21,7 +21,7 @@ "tailwind-merge": "^3.6.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/react-start/csp/package.json b/e2e/react-start/csp/package.json index 4d3c4fd264..22ffb81237 100644 --- a/e2e/react-start/csp/package.json +++ b/e2e/react-start/csp/package.json @@ -17,7 +17,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", "@types/react": "^19.0.8", diff --git a/e2e/react-start/css-inline/package.json b/e2e/react-start/css-inline/package.json index a7bc5d023f..5e0b6d104d 100644 --- a/e2e/react-start/css-inline/package.json +++ b/e2e/react-start/css-inline/package.json @@ -23,7 +23,7 @@ "srvx": "^0.11.9" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@rsbuild/core": "^2.0.11", "@rsbuild/plugin-react": "^2.0.0", "@tanstack/router-e2e-utils": "workspace:^", diff --git a/e2e/react-start/css-modules/package.json b/e2e/react-start/css-modules/package.json index 1610f62a61..381fa13b3a 100644 --- a/e2e/react-start/css-modules/package.json +++ b/e2e/react-start/css-modules/package.json @@ -35,7 +35,7 @@ }, "devDependencies": { "@cloudflare/vite-plugin": "^1.29.0", - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:*", "@types/node": "^22.10.2", "@types/react": "^19.0.8", diff --git a/e2e/react-start/custom-basepath/package.json b/e2e/react-start/custom-basepath/package.json index f32c9772b9..4bf492acca 100644 --- a/e2e/react-start/custom-basepath/package.json +++ b/e2e/react-start/custom-basepath/package.json @@ -20,7 +20,7 @@ "redaxios": "^0.5.1" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/express": "^5.0.6", diff --git a/e2e/react-start/custom-server-rsbuild/package.json b/e2e/react-start/custom-server-rsbuild/package.json index 75634f79f5..87acfacd05 100644 --- a/e2e/react-start/custom-server-rsbuild/package.json +++ b/e2e/react-start/custom-server-rsbuild/package.json @@ -20,7 +20,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@rsbuild/core": "^2.0.11", "@rsbuild/plugin-react": "^2.0.0", "@tailwindcss/postcss": "^4.2.2", diff --git a/e2e/react-start/dev-ssr-styles/package.json b/e2e/react-start/dev-ssr-styles/package.json index 91c5cc1281..829eddedd3 100644 --- a/e2e/react-start/dev-ssr-styles/package.json +++ b/e2e/react-start/dev-ssr-styles/package.json @@ -37,7 +37,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-core": "workspace:*", "@tanstack/router-e2e-utils": "workspace:*", "@types/node": "^22.10.2", diff --git a/e2e/react-start/early-hints/package.json b/e2e/react-start/early-hints/package.json index 932ab20cdc..a1a0efbd37 100644 --- a/e2e/react-start/early-hints/package.json +++ b/e2e/react-start/early-hints/package.json @@ -19,7 +19,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", "@types/node-forge": "^1.3.14", diff --git a/e2e/react-start/hmr/package.json b/e2e/react-start/hmr/package.json index b3a58125b9..7c5b6133c1 100644 --- a/e2e/react-start/hmr/package.json +++ b/e2e/react-start/hmr/package.json @@ -20,7 +20,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@rsbuild/core": "^2.0.11", "@rsbuild/plugin-react": "^2.0.0", "@tailwindcss/postcss": "^4.2.2", diff --git a/e2e/react-start/i18n-paraglide/package.json b/e2e/react-start/i18n-paraglide/package.json index bfb7b5eb26..39e5d3e137 100644 --- a/e2e/react-start/i18n-paraglide/package.json +++ b/e2e/react-start/i18n-paraglide/package.json @@ -19,7 +19,7 @@ }, "devDependencies": { "@inlang/paraglide-js": "^2.4.0", - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/react-start/import-protection-custom-config/package.json b/e2e/react-start/import-protection-custom-config/package.json index 1175bd4ac0..ff0e9e7bdf 100644 --- a/e2e/react-start/import-protection-custom-config/package.json +++ b/e2e/react-start/import-protection-custom-config/package.json @@ -19,7 +19,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", "@types/react": "^19.0.8", diff --git a/e2e/react-start/import-protection/package.json b/e2e/react-start/import-protection/package.json index 65ad70126b..0efd1c2048 100644 --- a/e2e/react-start/import-protection/package.json +++ b/e2e/react-start/import-protection/package.json @@ -24,7 +24,7 @@ "react-tweet": "^3.3.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@rsbuild/core": "^2.0.11", "@rsbuild/plugin-react": "^2.0.0", "@tanstack/router-e2e-utils": "workspace:^", diff --git a/e2e/react-start/query-integration/package.json b/e2e/react-start/query-integration/package.json index 6a22be7871..a2a325f9c8 100644 --- a/e2e/react-start/query-integration/package.json +++ b/e2e/react-start/query-integration/package.json @@ -24,7 +24,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/react-start/rsc-deferred-hydration/package.json b/e2e/react-start/rsc-deferred-hydration/package.json index e14d919490..e1cc5f28e4 100644 --- a/e2e/react-start/rsc-deferred-hydration/package.json +++ b/e2e/react-start/rsc-deferred-hydration/package.json @@ -31,7 +31,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", "@types/react": "^19.0.8", diff --git a/e2e/react-start/rsc-query/package.json b/e2e/react-start/rsc-query/package.json index 6049fa77a9..c909b53edf 100644 --- a/e2e/react-start/rsc-query/package.json +++ b/e2e/react-start/rsc-query/package.json @@ -22,7 +22,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", "@types/react": "^19.0.8", diff --git a/e2e/react-start/rsc-rsbuild/package.json b/e2e/react-start/rsc-rsbuild/package.json index 6dc5f5f5a1..992ae65684 100644 --- a/e2e/react-start/rsc-rsbuild/package.json +++ b/e2e/react-start/rsc-rsbuild/package.json @@ -27,7 +27,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@rsbuild/core": "^2.0.8", "@rsbuild/plugin-react": "^2.0.0", "@tanstack/router-e2e-utils": "workspace:^", diff --git a/e2e/react-start/rsc/package.json b/e2e/react-start/rsc/package.json index 36f822e660..805286a18e 100644 --- a/e2e/react-start/rsc/package.json +++ b/e2e/react-start/rsc/package.json @@ -39,7 +39,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@rsbuild/core": "^2.0.11", "@rsbuild/plugin-react": "^2.0.0", "@tanstack/eslint-plugin-start": "workspace:^", diff --git a/e2e/react-start/scroll-restoration/package.json b/e2e/react-start/scroll-restoration/package.json index 3dff10b56e..caf6ccbef4 100644 --- a/e2e/react-start/scroll-restoration/package.json +++ b/e2e/react-start/scroll-restoration/package.json @@ -22,7 +22,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/react-start/server-functions-global-middleware/package.json b/e2e/react-start/server-functions-global-middleware/package.json index 9af23a0df5..fbbe413417 100644 --- a/e2e/react-start/server-functions-global-middleware/package.json +++ b/e2e/react-start/server-functions-global-middleware/package.json @@ -18,7 +18,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/react-start/server-functions/package.json b/e2e/react-start/server-functions/package.json index 5efdf6c42a..f523ec2b80 100644 --- a/e2e/react-start/server-functions/package.json +++ b/e2e/react-start/server-functions/package.json @@ -30,7 +30,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@rsbuild/core": "^2.0.11", "@rsbuild/plugin-react": "^2.0.0", "@tailwindcss/postcss": "^4.2.2", diff --git a/e2e/react-start/server-routes-global-middleware/package.json b/e2e/react-start/server-routes-global-middleware/package.json index 0757885b5c..f5c37a00e0 100644 --- a/e2e/react-start/server-routes-global-middleware/package.json +++ b/e2e/react-start/server-routes-global-middleware/package.json @@ -18,7 +18,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/react-start/server-routes/package.json b/e2e/react-start/server-routes/package.json index 41d6c6743b..201ece3dda 100644 --- a/e2e/react-start/server-routes/package.json +++ b/e2e/react-start/server-routes/package.json @@ -25,7 +25,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/js-cookie": "^3.0.6", diff --git a/e2e/react-start/split-base-and-basepath/package.json b/e2e/react-start/split-base-and-basepath/package.json index 5f4e7a14c1..751e75586b 100644 --- a/e2e/react-start/split-base-and-basepath/package.json +++ b/e2e/react-start/split-base-and-basepath/package.json @@ -20,7 +20,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", "@types/react": "^19.0.8", diff --git a/e2e/react-start/start-manifest/package.json b/e2e/react-start/start-manifest/package.json index 7c932cb5d5..ebda3b2160 100644 --- a/e2e/react-start/start-manifest/package.json +++ b/e2e/react-start/start-manifest/package.json @@ -15,7 +15,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", "@types/react": "^19.0.8", diff --git a/e2e/react-start/static-server-functions/package.json b/e2e/react-start/static-server-functions/package.json index 47d8a73156..84d72fee03 100644 --- a/e2e/react-start/static-server-functions/package.json +++ b/e2e/react-start/static-server-functions/package.json @@ -20,7 +20,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:*", "@types/node": "^22.10.2", "@types/react": "^19.0.8", diff --git a/e2e/react-start/streaming-ssr/package.json b/e2e/react-start/streaming-ssr/package.json index 0cbadeae74..d1e585ade8 100644 --- a/e2e/react-start/streaming-ssr/package.json +++ b/e2e/react-start/streaming-ssr/package.json @@ -21,7 +21,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", "@types/react": "^19.0.8", diff --git a/e2e/react-start/transform-asset-urls/package.json b/e2e/react-start/transform-asset-urls/package.json index 40ffdd2569..979f7c4f76 100644 --- a/e2e/react-start/transform-asset-urls/package.json +++ b/e2e/react-start/transform-asset-urls/package.json @@ -40,7 +40,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", "@types/react": "^19.0.8", diff --git a/e2e/react-start/virtual-routes/package.json b/e2e/react-start/virtual-routes/package.json index 0ba0855037..938f770947 100644 --- a/e2e/react-start/virtual-routes/package.json +++ b/e2e/react-start/virtual-routes/package.json @@ -23,7 +23,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/react-start/website/package.json b/e2e/react-start/website/package.json index ad1ab75d5b..2e7c1363bb 100644 --- a/e2e/react-start/website/package.json +++ b/e2e/react-start/website/package.json @@ -22,7 +22,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/solid-router/basepath-file-based/package.json b/e2e/solid-router/basepath-file-based/package.json index a026e7da87..4b27235857 100644 --- a/e2e/solid-router/basepath-file-based/package.json +++ b/e2e/solid-router/basepath-file-based/package.json @@ -17,7 +17,7 @@ "solid-js": "^1.9.10" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "vite-plugin-solid": "^2.11.11", "vite": "^8.0.14" diff --git a/e2e/solid-router/basic-esbuild-file-based/package.json b/e2e/solid-router/basic-esbuild-file-based/package.json index ff950365a5..5fd22b3474 100644 --- a/e2e/solid-router/basic-esbuild-file-based/package.json +++ b/e2e/solid-router/basic-esbuild-file-based/package.json @@ -18,7 +18,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "esbuild": "^0.27.4", "esbuild-plugin-solid": "^0.6.0" diff --git a/e2e/solid-router/basic-file-based-code-splitting/package.json b/e2e/solid-router/basic-file-based-code-splitting/package.json index d18f13a208..a970ce9e5a 100644 --- a/e2e/solid-router/basic-file-based-code-splitting/package.json +++ b/e2e/solid-router/basic-file-based-code-splitting/package.json @@ -20,7 +20,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/e2e/solid-router/basic-file-based/package.json b/e2e/solid-router/basic-file-based/package.json index 3d4b26633a..53dfa340ee 100644 --- a/e2e/solid-router/basic-file-based/package.json +++ b/e2e/solid-router/basic-file-based/package.json @@ -22,7 +22,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "combinate": "^1.1.11", "vite": "^8.0.14", diff --git a/e2e/solid-router/basic-scroll-restoration/package.json b/e2e/solid-router/basic-scroll-restoration/package.json index 94c64398cf..88a97d8c60 100644 --- a/e2e/solid-router/basic-scroll-restoration/package.json +++ b/e2e/solid-router/basic-scroll-restoration/package.json @@ -20,7 +20,7 @@ "tailwindcss": "^4.2.2" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11", diff --git a/e2e/solid-router/basic-solid-query-file-based/package.json b/e2e/solid-router/basic-solid-query-file-based/package.json index 77d3df28d8..fbd773493c 100644 --- a/e2e/solid-router/basic-solid-query-file-based/package.json +++ b/e2e/solid-router/basic-solid-query-file-based/package.json @@ -23,7 +23,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/e2e/solid-router/basic-solid-query/package.json b/e2e/solid-router/basic-solid-query/package.json index aad0845b10..b0cb6d4712 100644 --- a/e2e/solid-router/basic-solid-query/package.json +++ b/e2e/solid-router/basic-solid-query/package.json @@ -21,7 +21,7 @@ "tailwindcss": "^4.2.2" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/e2e/solid-router/basic-virtual-file-based/package.json b/e2e/solid-router/basic-virtual-file-based/package.json index ee1565fd7d..0a0a8eb849 100644 --- a/e2e/solid-router/basic-virtual-file-based/package.json +++ b/e2e/solid-router/basic-virtual-file-based/package.json @@ -22,7 +22,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/e2e/solid-router/basic-virtual-named-export-config-file-based/package.json b/e2e/solid-router/basic-virtual-named-export-config-file-based/package.json index bc222e7d25..803d6a139e 100644 --- a/e2e/solid-router/basic-virtual-named-export-config-file-based/package.json +++ b/e2e/solid-router/basic-virtual-named-export-config-file-based/package.json @@ -22,7 +22,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/e2e/solid-router/basic/package.json b/e2e/solid-router/basic/package.json index 0ccbef8581..0c89f513e7 100644 --- a/e2e/solid-router/basic/package.json +++ b/e2e/solid-router/basic/package.json @@ -19,7 +19,7 @@ "tailwindcss": "^4.2.2" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/e2e/solid-router/generator-cli-only/package.json b/e2e/solid-router/generator-cli-only/package.json index 1d6718af6b..81678d6a70 100644 --- a/e2e/solid-router/generator-cli-only/package.json +++ b/e2e/solid-router/generator-cli-only/package.json @@ -20,7 +20,7 @@ "tailwindcss": "^4.2.2" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "vite-plugin-solid": "^2.11.11", "vite": "^8.0.14" diff --git a/e2e/solid-router/js-only-file-based/package.json b/e2e/solid-router/js-only-file-based/package.json index 755415c99e..fa288bcf4d 100644 --- a/e2e/solid-router/js-only-file-based/package.json +++ b/e2e/solid-router/js-only-file-based/package.json @@ -19,7 +19,7 @@ "tailwindcss": "^4.2.2" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@tanstack/router-plugin": "workspace:^", "vite-plugin-solid": "^2.11.11", diff --git a/e2e/solid-router/rspack-basic-file-based/package.json b/e2e/solid-router/rspack-basic-file-based/package.json index 5641433737..a7f558f57c 100644 --- a/e2e/solid-router/rspack-basic-file-based/package.json +++ b/e2e/solid-router/rspack-basic-file-based/package.json @@ -16,7 +16,7 @@ "solid-js": "^1.9.10" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@rsbuild/core": "^2.0.11", "@rsbuild/plugin-babel": "^1.1.2", "@rsbuild/plugin-solid": "^1.1.1", diff --git a/e2e/solid-router/rspack-basic-virtual-named-export-config-file-based/package.json b/e2e/solid-router/rspack-basic-virtual-named-export-config-file-based/package.json index f298b42ca6..a6ca91d17f 100644 --- a/e2e/solid-router/rspack-basic-virtual-named-export-config-file-based/package.json +++ b/e2e/solid-router/rspack-basic-virtual-named-export-config-file-based/package.json @@ -16,7 +16,7 @@ "solid-js": "^1.9.10" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@rsbuild/core": "^2.0.11", "@rsbuild/plugin-babel": "^1.1.2", "@rsbuild/plugin-solid": "^1.1.1", diff --git a/e2e/solid-router/scroll-restoration-sandbox-vite/package.json b/e2e/solid-router/scroll-restoration-sandbox-vite/package.json index 7b0c690a68..06bc438114 100644 --- a/e2e/solid-router/scroll-restoration-sandbox-vite/package.json +++ b/e2e/solid-router/scroll-restoration-sandbox-vite/package.json @@ -24,7 +24,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/e2e/solid-router/sentry-integration/package.json b/e2e/solid-router/sentry-integration/package.json index b3c8a52b88..84ea4bb90e 100644 --- a/e2e/solid-router/sentry-integration/package.json +++ b/e2e/solid-router/sentry-integration/package.json @@ -22,7 +22,7 @@ "tailwindcss": "^4.2.2" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "vite-plugin-solid": "^2.11.11", "vite": "^8.0.14" diff --git a/e2e/solid-router/view-transitions/package.json b/e2e/solid-router/view-transitions/package.json index 67a28f584c..f047fcec11 100644 --- a/e2e/solid-router/view-transitions/package.json +++ b/e2e/solid-router/view-transitions/package.json @@ -21,7 +21,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "typescript": "^6.0.2", "vite": "^8.0.14", diff --git a/e2e/solid-start/basic-auth/package.json b/e2e/solid-start/basic-auth/package.json index dc99d114fe..2e32dd3cba 100644 --- a/e2e/solid-start/basic-auth/package.json +++ b/e2e/solid-start/basic-auth/package.json @@ -24,7 +24,7 @@ "tailwind-merge": "^3.6.0" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/solid-start/basic-cloudflare/package.json b/e2e/solid-start/basic-cloudflare/package.json index 9026db1619..0433f7e35b 100644 --- a/e2e/solid-start/basic-cloudflare/package.json +++ b/e2e/solid-start/basic-cloudflare/package.json @@ -20,7 +20,7 @@ }, "devDependencies": { "@cloudflare/vite-plugin": "^1.29.0", - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/solid-start/basic-solid-query/package.json b/e2e/solid-start/basic-solid-query/package.json index 210f02afcc..4af9bdad34 100644 --- a/e2e/solid-start/basic-solid-query/package.json +++ b/e2e/solid-start/basic-solid-query/package.json @@ -24,7 +24,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/solid-start/basic/package.json b/e2e/solid-start/basic/package.json index 1993225f61..2092b8b301 100644 --- a/e2e/solid-start/basic/package.json +++ b/e2e/solid-start/basic/package.json @@ -26,7 +26,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@rsbuild/core": "^2.0.11", "@rsbuild/plugin-babel": "^1.1.2", "@rsbuild/plugin-solid": "^1.1.1", diff --git a/e2e/solid-start/csp/package.json b/e2e/solid-start/csp/package.json index 0457f7a908..4124d87aee 100644 --- a/e2e/solid-start/csp/package.json +++ b/e2e/solid-start/csp/package.json @@ -16,7 +16,7 @@ "solid-js": "^1.9.10" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", "srvx": "^0.11.9", diff --git a/e2e/solid-start/css-modules/package.json b/e2e/solid-start/css-modules/package.json index e6d052ba65..9939806556 100644 --- a/e2e/solid-start/css-modules/package.json +++ b/e2e/solid-start/css-modules/package.json @@ -19,7 +19,7 @@ "solid-js": "^1.9.10" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:*", "@types/node": "^22.10.2", "sass": "^1.97.2", diff --git a/e2e/solid-start/custom-basepath/package.json b/e2e/solid-start/custom-basepath/package.json index 363a2e0453..219c87c883 100644 --- a/e2e/solid-start/custom-basepath/package.json +++ b/e2e/solid-start/custom-basepath/package.json @@ -19,7 +19,7 @@ "solid-js": "^1.9.10" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/express": "^5.0.6", diff --git a/e2e/solid-start/query-integration/package.json b/e2e/solid-start/query-integration/package.json index e7c9604edb..df10d5fe32 100644 --- a/e2e/solid-start/query-integration/package.json +++ b/e2e/solid-start/query-integration/package.json @@ -23,7 +23,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/solid-start/scroll-restoration/package.json b/e2e/solid-start/scroll-restoration/package.json index 87951c16ea..d4177790fd 100644 --- a/e2e/solid-start/scroll-restoration/package.json +++ b/e2e/solid-start/scroll-restoration/package.json @@ -21,7 +21,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/solid-start/server-functions/package.json b/e2e/solid-start/server-functions/package.json index ab4a3335b4..978e43f6cd 100644 --- a/e2e/solid-start/server-functions/package.json +++ b/e2e/solid-start/server-functions/package.json @@ -24,7 +24,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/js-cookie": "^3.0.6", diff --git a/e2e/solid-start/server-routes/package.json b/e2e/solid-start/server-routes/package.json index 2a8de94070..5f59515d8e 100644 --- a/e2e/solid-start/server-routes/package.json +++ b/e2e/solid-start/server-routes/package.json @@ -24,7 +24,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/js-cookie": "^3.0.6", diff --git a/e2e/solid-start/solid-query-layout-suspense/package.json b/e2e/solid-start/solid-query-layout-suspense/package.json index 512e6ea7b7..9e97c1a7c0 100644 --- a/e2e/solid-start/solid-query-layout-suspense/package.json +++ b/e2e/solid-start/solid-query-layout-suspense/package.json @@ -16,7 +16,7 @@ "solid-js": "^1.9.10" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", "typescript": "^6.0.2", diff --git a/e2e/solid-start/start-manifest/package.json b/e2e/solid-start/start-manifest/package.json index 262dd23d9a..1cec90d994 100644 --- a/e2e/solid-start/start-manifest/package.json +++ b/e2e/solid-start/start-manifest/package.json @@ -15,7 +15,7 @@ "solid-js": "^1.9.10" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", "srvx": "^0.11.9", diff --git a/e2e/solid-start/virtual-routes/package.json b/e2e/solid-start/virtual-routes/package.json index 93b73fa52f..5f61a98b82 100644 --- a/e2e/solid-start/virtual-routes/package.json +++ b/e2e/solid-start/virtual-routes/package.json @@ -22,7 +22,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/solid-start/website/package.json b/e2e/solid-start/website/package.json index 7379e5ca5f..d8c93ba240 100644 --- a/e2e/solid-start/website/package.json +++ b/e2e/solid-start/website/package.json @@ -21,7 +21,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/vue-router/basepath-file-based/package.json b/e2e/vue-router/basepath-file-based/package.json index 4e3deac1c7..0ed990a8ff 100644 --- a/e2e/vue-router/basepath-file-based/package.json +++ b/e2e/vue-router/basepath-file-based/package.json @@ -22,7 +22,7 @@ "vue": "^3.5.16" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", diff --git a/e2e/vue-router/basic-esbuild-file-based/package.json b/e2e/vue-router/basic-esbuild-file-based/package.json index e5422cd436..e27d749899 100644 --- a/e2e/vue-router/basic-esbuild-file-based/package.json +++ b/e2e/vue-router/basic-esbuild-file-based/package.json @@ -20,7 +20,7 @@ }, "devDependencies": { "@eslint/js": "^9.36.0", - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@typescript-eslint/eslint-plugin": "^8.44.1", "@typescript-eslint/parser": "^8.44.1", diff --git a/e2e/vue-router/basic-file-based-jsx/package.json b/e2e/vue-router/basic-file-based-jsx/package.json index b029b1ab4f..e4d20d72e9 100644 --- a/e2e/vue-router/basic-file-based-jsx/package.json +++ b/e2e/vue-router/basic-file-based-jsx/package.json @@ -22,7 +22,7 @@ }, "devDependencies": { "@eslint/js": "^9.36.0", - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@typescript-eslint/eslint-plugin": "^8.44.1", "@typescript-eslint/parser": "^8.44.1", diff --git a/e2e/vue-router/basic-file-based-sfc/package.json b/e2e/vue-router/basic-file-based-sfc/package.json index 369a0b1fdc..0f1934b9a9 100644 --- a/e2e/vue-router/basic-file-based-sfc/package.json +++ b/e2e/vue-router/basic-file-based-sfc/package.json @@ -21,7 +21,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", diff --git a/e2e/vue-router/basic-scroll-restoration/package.json b/e2e/vue-router/basic-scroll-restoration/package.json index daa2519eed..86c91c62ca 100644 --- a/e2e/vue-router/basic-scroll-restoration/package.json +++ b/e2e/vue-router/basic-scroll-restoration/package.json @@ -20,7 +20,7 @@ "tailwindcss": "^4.2.2" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", diff --git a/e2e/vue-router/basic-virtual-file-based/package.json b/e2e/vue-router/basic-virtual-file-based/package.json index 946addb3bf..3a341e0e7d 100644 --- a/e2e/vue-router/basic-virtual-file-based/package.json +++ b/e2e/vue-router/basic-virtual-file-based/package.json @@ -23,7 +23,7 @@ "vue": "^3.5.16" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", diff --git a/e2e/vue-router/basic-virtual-named-export-config-file-based/package.json b/e2e/vue-router/basic-virtual-named-export-config-file-based/package.json index f73d3ad8b2..08815d8265 100644 --- a/e2e/vue-router/basic-virtual-named-export-config-file-based/package.json +++ b/e2e/vue-router/basic-virtual-named-export-config-file-based/package.json @@ -23,7 +23,7 @@ "vue": "^3.5.16" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", diff --git a/e2e/vue-router/basic-vue-query-file-based/package.json b/e2e/vue-router/basic-vue-query-file-based/package.json index 75395b20f8..2cd6d23ba0 100644 --- a/e2e/vue-router/basic-vue-query-file-based/package.json +++ b/e2e/vue-router/basic-vue-query-file-based/package.json @@ -23,7 +23,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", diff --git a/e2e/vue-router/basic-vue-query/package.json b/e2e/vue-router/basic-vue-query/package.json index be46a064c6..288d96018d 100644 --- a/e2e/vue-router/basic-vue-query/package.json +++ b/e2e/vue-router/basic-vue-query/package.json @@ -21,7 +21,7 @@ "vue": "^3.5.16" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", diff --git a/e2e/vue-router/basic/package.json b/e2e/vue-router/basic/package.json index c779a572e9..ce95750e12 100644 --- a/e2e/vue-router/basic/package.json +++ b/e2e/vue-router/basic/package.json @@ -19,7 +19,7 @@ "vue": "^3.5.16" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue-jsx": "^5.1.5", "typescript": "~5.8.3", diff --git a/e2e/vue-router/generator-cli-only/package.json b/e2e/vue-router/generator-cli-only/package.json index 14cdafcd48..410a14b742 100644 --- a/e2e/vue-router/generator-cli-only/package.json +++ b/e2e/vue-router/generator-cli-only/package.json @@ -20,7 +20,7 @@ "tailwindcss": "^4.2.2" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", diff --git a/e2e/vue-router/js-only-file-based/package.json b/e2e/vue-router/js-only-file-based/package.json index 05a86b7cf1..d7f062038e 100644 --- a/e2e/vue-router/js-only-file-based/package.json +++ b/e2e/vue-router/js-only-file-based/package.json @@ -22,7 +22,7 @@ "vue": "^3.5.16" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", diff --git a/e2e/vue-router/rspack-basic-file-based/package.json b/e2e/vue-router/rspack-basic-file-based/package.json index 5d8a072d87..ba0ae7efde 100644 --- a/e2e/vue-router/rspack-basic-file-based/package.json +++ b/e2e/vue-router/rspack-basic-file-based/package.json @@ -16,7 +16,7 @@ "vue": "^3.5.16" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@rsbuild/core": "^2.0.11", "@rsbuild/plugin-babel": "^1.1.2", "@rsbuild/plugin-vue": "^1.2.7", diff --git a/e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/package.json b/e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/package.json index 11f61237d3..9b55dcc6f9 100644 --- a/e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/package.json +++ b/e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/package.json @@ -16,7 +16,7 @@ "vue": "^3.5.16" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@rsbuild/core": "^2.0.11", "@rsbuild/plugin-babel": "^1.1.2", "@rsbuild/plugin-vue": "^1.2.7", diff --git a/e2e/vue-router/scroll-restoration-sandbox-vite/package.json b/e2e/vue-router/scroll-restoration-sandbox-vite/package.json index a7dc67fd6b..ebcea6fb39 100644 --- a/e2e/vue-router/scroll-restoration-sandbox-vite/package.json +++ b/e2e/vue-router/scroll-restoration-sandbox-vite/package.json @@ -24,7 +24,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", diff --git a/e2e/vue-router/sentry-integration/package.json b/e2e/vue-router/sentry-integration/package.json index cbf6bf8499..897331f1b8 100644 --- a/e2e/vue-router/sentry-integration/package.json +++ b/e2e/vue-router/sentry-integration/package.json @@ -22,7 +22,7 @@ "vue": "^3.5.16" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue-jsx": "^5.1.5", "typescript": "~5.8.3", diff --git a/e2e/vue-router/view-transitions/package.json b/e2e/vue-router/view-transitions/package.json index 493b3e45ce..82f88dbe64 100644 --- a/e2e/vue-router/view-transitions/package.json +++ b/e2e/vue-router/view-transitions/package.json @@ -22,7 +22,7 @@ "vue": "^3.5.16" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@vitejs/plugin-vue": "^6.0.5", "@vitejs/plugin-vue-jsx": "^5.1.5", diff --git a/e2e/vue-start/basic-auth/package.json b/e2e/vue-start/basic-auth/package.json index 3a572536e9..25de0c6b80 100644 --- a/e2e/vue-start/basic-auth/package.json +++ b/e2e/vue-start/basic-auth/package.json @@ -24,7 +24,7 @@ "vue": "^3.5.25" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/vue-start/basic-cloudflare/package.json b/e2e/vue-start/basic-cloudflare/package.json index c7e55f7937..05b2158c0e 100644 --- a/e2e/vue-start/basic-cloudflare/package.json +++ b/e2e/vue-start/basic-cloudflare/package.json @@ -20,7 +20,7 @@ }, "devDependencies": { "@cloudflare/vite-plugin": "^1.29.0", - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/vue-start/basic-vue-query/package.json b/e2e/vue-start/basic-vue-query/package.json index 3189e8e867..6b1335d8bd 100644 --- a/e2e/vue-start/basic-vue-query/package.json +++ b/e2e/vue-start/basic-vue-query/package.json @@ -24,7 +24,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/vue-start/basic/package.json b/e2e/vue-start/basic/package.json index 073627701a..dacbcc2154 100644 --- a/e2e/vue-start/basic/package.json +++ b/e2e/vue-start/basic/package.json @@ -26,7 +26,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@rsbuild/core": "^2.0.11", "@rsbuild/plugin-babel": "^1.0.5", "@rsbuild/plugin-vue": "^1.2.2", diff --git a/e2e/vue-start/css-modules/package.json b/e2e/vue-start/css-modules/package.json index 9b7d36333d..7e28e9d5a8 100644 --- a/e2e/vue-start/css-modules/package.json +++ b/e2e/vue-start/css-modules/package.json @@ -19,7 +19,7 @@ "vue": "^3.5.16" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:*", "@types/node": "^22.10.2", "@vitejs/plugin-vue-jsx": "^5.1.5", diff --git a/e2e/vue-start/custom-basepath/package.json b/e2e/vue-start/custom-basepath/package.json index 759e13b48e..3d0208de01 100644 --- a/e2e/vue-start/custom-basepath/package.json +++ b/e2e/vue-start/custom-basepath/package.json @@ -19,7 +19,7 @@ "vue": "^3.5.25" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/express": "^5.0.6", diff --git a/e2e/vue-start/query-integration/package.json b/e2e/vue-start/query-integration/package.json index d44bd58376..4cb22b48be 100644 --- a/e2e/vue-start/query-integration/package.json +++ b/e2e/vue-start/query-integration/package.json @@ -23,7 +23,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/vue-start/scroll-restoration/package.json b/e2e/vue-start/scroll-restoration/package.json index 871200e852..cbc04f23ec 100644 --- a/e2e/vue-start/scroll-restoration/package.json +++ b/e2e/vue-start/scroll-restoration/package.json @@ -21,7 +21,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/vue-start/server-functions/package.json b/e2e/vue-start/server-functions/package.json index 2e84c6238e..5752c48b73 100644 --- a/e2e/vue-start/server-functions/package.json +++ b/e2e/vue-start/server-functions/package.json @@ -24,7 +24,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/js-cookie": "^3.0.6", diff --git a/e2e/vue-start/server-routes/package.json b/e2e/vue-start/server-routes/package.json index 6e5c7cdced..4f9db26152 100644 --- a/e2e/vue-start/server-routes/package.json +++ b/e2e/vue-start/server-routes/package.json @@ -24,7 +24,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/js-cookie": "^3.0.6", diff --git a/e2e/vue-start/start-manifest/package.json b/e2e/vue-start/start-manifest/package.json index 841aa5da85..358e8e0c04 100644 --- a/e2e/vue-start/start-manifest/package.json +++ b/e2e/vue-start/start-manifest/package.json @@ -15,7 +15,7 @@ "vue": "^3.5.16" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", "@vitejs/plugin-vue-jsx": "^5.1.5", diff --git a/e2e/vue-start/virtual-routes/package.json b/e2e/vue-start/virtual-routes/package.json index 41b1355414..6c2011e1ea 100644 --- a/e2e/vue-start/virtual-routes/package.json +++ b/e2e/vue-start/virtual-routes/package.json @@ -22,7 +22,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/vue-start/website/package.json b/e2e/vue-start/website/package.json index 8ec13f6dbc..bfbb042de5 100644 --- a/e2e/vue-start/website/package.json +++ b/e2e/vue-start/website/package.json @@ -21,7 +21,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.1", + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a8ccd3ec25..a91aaea20f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,7 +15,7 @@ overrides: eslint: ^9.22.0 vite: ^8.0.14 '@types/node': 25.0.9 - '@playwright/test': ^1.57.0 + '@playwright/test': ^1.61.0 '@tanstack/query-core': ^5.99.0 '@tanstack/react-query': ^5.99.0 '@tanstack/solid-query': ^5.99.0 @@ -80,8 +80,8 @@ importers: specifier: 22.7.5 version: 22.7.5(nx@22.7.5(@swc-node/register@1.11.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@swc/core@1.15.33(@swc/helpers@0.5.23))(@swc/types@0.1.26)(typescript@6.0.2))(@swc/core@1.15.33(@swc/helpers@0.5.23))(debug@4.4.3)) '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@swc-node/core': specifier: ^1.14.1 version: 1.14.1(@swc/core@1.15.33(@swc/helpers@0.5.23))(@swc/types@0.1.26) @@ -566,8 +566,8 @@ importers: version: 19.2.3(react@19.2.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -609,8 +609,8 @@ importers: version: 4.2.2 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -652,8 +652,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -698,8 +698,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -747,8 +747,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -796,8 +796,8 @@ importers: version: 4.2.2 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -851,8 +851,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -897,8 +897,8 @@ importers: version: 4.2.2 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -952,8 +952,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -1004,8 +1004,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -1038,8 +1038,8 @@ importers: version: 19.2.3(react@19.2.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -1084,8 +1084,8 @@ importers: version: 4.2.2 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -1124,8 +1124,8 @@ importers: specifier: ^2.4.0 version: 2.4.0(babel-plugin-macros@3.1.0) '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -1176,8 +1176,8 @@ importers: version: 4.2.2 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -1210,8 +1210,8 @@ importers: version: 19.2.3(react@19.2.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -1244,8 +1244,8 @@ importers: version: 0.5.1 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@rsbuild/core': specifier: ^2.0.11 version: 2.0.14 @@ -1296,8 +1296,8 @@ importers: version: 0.5.1 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@rsbuild/core': specifier: ^2.0.11 version: 2.0.14 @@ -1363,8 +1363,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -1415,8 +1415,8 @@ importers: version: 4.2.2 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -1464,8 +1464,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -1519,8 +1519,8 @@ importers: version: 3.6.0 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@rsbuild/core': specifier: ^2.0.11 version: 2.0.14 @@ -1604,8 +1604,8 @@ importers: version: 3.6.0 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -1662,8 +1662,8 @@ importers: specifier: ^1.29.0 version: 1.29.1(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0))(workerd@1.20260317.1)(wrangler@4.75.0) '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -1732,8 +1732,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -1830,8 +1830,8 @@ importers: version: 3.6.0 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -1876,8 +1876,8 @@ importers: version: 19.2.3(react@19.2.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -1922,8 +1922,8 @@ importers: version: 0.11.15 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@rsbuild/core': specifier: ^2.0.11 version: 2.0.14 @@ -1971,8 +1971,8 @@ importers: specifier: ^1.29.0 version: 1.29.1(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0))(workerd@1.20260317.1)(wrangler@4.75.0) '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:* version: link:../../e2e-utils @@ -2032,8 +2032,8 @@ importers: version: 0.5.1 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -2093,8 +2093,8 @@ importers: version: 19.2.3(react@19.2.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@rsbuild/core': specifier: ^2.0.11 version: 2.0.14 @@ -2197,8 +2197,8 @@ importers: version: 19.2.3(react@19.2.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-core': specifier: workspace:* version: link:../../../packages/router-core @@ -2249,8 +2249,8 @@ importers: version: 19.2.3(react@19.2.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -2341,8 +2341,8 @@ importers: version: 19.2.3(react@19.2.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@rsbuild/core': specifier: ^2.0.11 version: 2.0.14 @@ -2402,8 +2402,8 @@ importers: specifier: ^2.4.0 version: 2.4.0(babel-plugin-macros@3.1.0) '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -2451,8 +2451,8 @@ importers: version: 3.3.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@rsbuild/core': specifier: ^2.0.11 version: 2.0.14 @@ -2500,8 +2500,8 @@ importers: version: 19.2.3(react@19.2.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -2561,8 +2561,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -2610,8 +2610,8 @@ importers: version: 19.2.3(react@19.2.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@rsbuild/core': specifier: ^2.0.11 version: 2.0.14 @@ -2671,8 +2671,8 @@ importers: version: 19.2.3(react@19.2.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -2729,8 +2729,8 @@ importers: version: 19.2.3(react@19.2.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -2775,8 +2775,8 @@ importers: version: 19.2.3(react@19.2.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@rsbuild/core': specifier: ^2.0.8 version: 2.0.8 @@ -2830,8 +2830,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -3007,8 +3007,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@rsbuild/core': specifier: ^2.0.11 version: 2.0.14 @@ -3071,8 +3071,8 @@ importers: version: 19.2.3(react@19.2.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -3141,8 +3141,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -3196,8 +3196,8 @@ importers: version: 19.2.3(react@19.2.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -3291,8 +3291,8 @@ importers: version: 19.2.3(react@19.2.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -3334,8 +3334,8 @@ importers: version: 19.2.3(react@19.2.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -3383,8 +3383,8 @@ importers: version: 19.2.3(react@19.2.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:* version: link:../../e2e-utils @@ -3429,8 +3429,8 @@ importers: version: 19.2.3(react@19.2.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -3472,8 +3472,8 @@ importers: version: 19.2.3(react@19.2.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -3530,8 +3530,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -3594,8 +3594,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -3643,8 +3643,8 @@ importers: version: 1.9.12 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -3677,8 +3677,8 @@ importers: version: 4.2.2 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -3711,8 +3711,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -3751,8 +3751,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -3791,8 +3791,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -3828,8 +3828,8 @@ importers: version: 4.2.2 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -3871,8 +3871,8 @@ importers: version: 4.2.2 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -3917,8 +3917,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -3960,8 +3960,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -4003,8 +4003,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -4040,8 +4040,8 @@ importers: version: 4.2.2 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -4074,8 +4074,8 @@ importers: version: 4.2.2 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -4105,8 +4105,8 @@ importers: version: 1.9.12 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@rsbuild/core': specifier: ^2.0.11 version: 2.0.14 @@ -4151,8 +4151,8 @@ importers: version: 1.9.12 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@rsbuild/core': specifier: ^2.0.11 version: 2.0.14 @@ -4212,8 +4212,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -4255,8 +4255,8 @@ importers: version: 4.2.2 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -4295,8 +4295,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -4344,8 +4344,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@rsbuild/core': specifier: ^2.0.11 version: 2.0.14 @@ -4420,8 +4420,8 @@ importers: version: 3.6.0 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -4472,8 +4472,8 @@ importers: specifier: ^1.29.0 version: 1.29.1(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0))(workerd@1.20260317.1)(wrangler@4.75.0) '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -4533,8 +4533,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -4607,8 +4607,8 @@ importers: version: 1.9.12 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -4641,8 +4641,8 @@ importers: version: 1.9.12 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:* version: link:../../e2e-utils @@ -4687,8 +4687,8 @@ importers: version: 1.9.12 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -4794,8 +4794,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -4843,8 +4843,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -4987,8 +4987,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -5054,8 +5054,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -5103,8 +5103,8 @@ importers: version: 1.9.12 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -5171,8 +5171,8 @@ importers: version: 1.9.12 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -5220,8 +5220,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -5275,8 +5275,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -5333,8 +5333,8 @@ importers: version: 3.5.25(typescript@5.8.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -5376,8 +5376,8 @@ importers: version: 3.5.25(typescript@5.8.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -5422,8 +5422,8 @@ importers: specifier: ^9.36.0 version: 9.36.0 '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -5483,8 +5483,8 @@ importers: specifier: ^9.36.0 version: 9.36.0 '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -5544,8 +5544,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -5590,8 +5590,8 @@ importers: version: 3.5.25(typescript@5.9.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -5645,8 +5645,8 @@ importers: version: 3.5.25(typescript@5.8.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -5700,8 +5700,8 @@ importers: version: 3.5.25(typescript@5.8.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -5749,8 +5749,8 @@ importers: version: 3.5.25(typescript@5.8.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -5804,8 +5804,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -5850,8 +5850,8 @@ importers: version: 3.5.25(typescript@5.8.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -5902,8 +5902,8 @@ importers: version: 3.5.25(typescript@6.0.2) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -5933,8 +5933,8 @@ importers: version: 3.5.25(typescript@6.0.2) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@rsbuild/core': specifier: ^2.0.11 version: 2.0.14 @@ -5988,8 +5988,8 @@ importers: version: 3.5.25(typescript@6.0.2) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@rsbuild/core': specifier: ^2.0.11 version: 2.0.14 @@ -6055,8 +6055,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -6107,8 +6107,8 @@ importers: version: 3.5.25(typescript@5.8.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -6156,8 +6156,8 @@ importers: version: 3.5.25(typescript@5.8.3) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -6211,8 +6211,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@rsbuild/core': specifier: ^2.0.11 version: 2.0.14 @@ -6293,8 +6293,8 @@ importers: version: 3.5.25(typescript@6.0.2) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -6348,8 +6348,8 @@ importers: specifier: ^1.29.0 version: 1.29.1(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0))(workerd@1.20260317.1)(wrangler@4.75.0) '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -6449,8 +6449,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -6492,8 +6492,8 @@ importers: version: 3.5.25(typescript@6.0.2) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:* version: link:../../e2e-utils @@ -6538,8 +6538,8 @@ importers: version: 3.5.25(typescript@6.0.2) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -6608,8 +6608,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -6660,8 +6660,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -6816,8 +6816,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -6886,8 +6886,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -6975,8 +6975,8 @@ importers: version: 3.5.25(typescript@6.0.2) devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -7024,8 +7024,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -7082,8 +7082,8 @@ importers: version: 4.4.3 devDependencies: '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 + specifier: ^1.61.0 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) @@ -17189,8 +17189,8 @@ packages: engines: {node: '>=22.6.0'} hasBin: true - '@playwright/test@1.58.0': - resolution: {integrity: sha512-fWza+Lpbj6SkQKCrU6si4iu+fD2dD3gxNHFhUPxsfXBPhnv3rRSQVd0NtBUT9Z/RhF/boCBcuUaMUSTRTopjZg==} + '@playwright/test@1.61.1': + resolution: {integrity: sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==} engines: {node: '>=18'} hasBin: true @@ -24377,13 +24377,13 @@ packages: resolution: {integrity: sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==} engines: {node: '>=16.0.0'} - playwright-core@1.58.0: - resolution: {integrity: sha512-aaoB1RWrdNi3//rOeKuMiS65UCcgOVljU46At6eFcOFPFHWtd2weHRRow6z/n+Lec0Lvu0k9ZPKJSjPugikirw==} + playwright-core@1.61.1: + resolution: {integrity: sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==} engines: {node: '>=18'} hasBin: true - playwright@1.58.0: - resolution: {integrity: sha512-2SVA0sbPktiIY/MCOPX8e86ehA/e+tDNq+e5Y8qjKYti2Z/JG7xnronT/TXTIkKbYGWlCbuucZ6dziEgkoEjQQ==} + playwright@1.61.1: + resolution: {integrity: sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==} engines: {node: '>=18'} hasBin: true @@ -30570,9 +30570,9 @@ snapshots: pprof-to-md: 0.2.0 react-pprof: 1.4.0 - '@playwright/test@1.58.0': + '@playwright/test@1.61.1': dependencies: - playwright: 1.58.0 + playwright: 1.61.1 '@polka/url@1.0.0-next.28': {} @@ -38953,11 +38953,11 @@ snapshots: pvutils: 1.1.3 tslib: 2.8.1 - playwright-core@1.58.0: {} + playwright-core@1.61.1: {} - playwright@1.58.0: + playwright@1.61.1: dependencies: - playwright-core: 1.58.0 + playwright-core: 1.61.1 optionalDependencies: fsevents: 2.3.2 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index ffd19c8155..aca73336f9 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -41,7 +41,7 @@ catalog: eslint: ^9.22.0 vite: ^8.0.14 '@types/node': 25.0.9 - '@playwright/test': ^1.57.0 + '@playwright/test': ^1.61.0 '@tanstack/query-core': ^5.99.0 '@tanstack/react-query': ^5.99.0 '@tanstack/solid-query': ^5.99.0 From 11a6a0d8ba4db37714a6b1d723910bdf7dbe38af Mon Sep 17 00:00:00 2001 From: Flo Date: Tue, 14 Jul 2026 01:44:02 +0200 Subject: [PATCH 08/51] test: add regression coverage for existing router behavior (#7812) --- .../tests/issue-2182-root-pending.test.tsx | 55 ++++++++ .../issue-7367-pending-min-redirect.test.tsx | 78 +++++++++++ ...ue-7457-redirect-chain-first-load.test.tsx | 109 +++++++++++++++ ...-7638-invalidate-transition-error.test.tsx | 130 ++++++++++++++++++ ...ssue-4684-head-on-beforeload-error.test.ts | 86 ++++++++++++ ...search-default-normalization-abort.test.ts | 109 +++++++++++++++ 6 files changed, 567 insertions(+) create mode 100644 packages/react-router/tests/issue-2182-root-pending.test.tsx create mode 100644 packages/react-router/tests/issue-7367-pending-min-redirect.test.tsx create mode 100644 packages/react-router/tests/issue-7457-redirect-chain-first-load.test.tsx create mode 100644 packages/react-router/tests/issue-7638-invalidate-transition-error.test.tsx create mode 100644 packages/router-core/tests/issue-4684-head-on-beforeload-error.test.ts create mode 100644 packages/router-core/tests/issue-6371-search-default-normalization-abort.test.ts diff --git a/packages/react-router/tests/issue-2182-root-pending.test.tsx b/packages/react-router/tests/issue-2182-root-pending.test.tsx new file mode 100644 index 0000000000..9cf3f82cb7 --- /dev/null +++ b/packages/react-router/tests/issue-2182-root-pending.test.tsx @@ -0,0 +1,55 @@ +import * as React from 'react' +import { act, cleanup, render, screen } from '@testing-library/react' +import { afterEach, expect, test, vi } from 'vitest' +import { + RouterProvider, + createControlledPromise, + createMemoryHistory, + createRootRoute, + createRouter, +} from '../src' + +afterEach(() => { + cleanup() + vi.useRealTimers() +}) + +// https://github.com/TanStack/router/issues/2182 +test('root pending fallback remains visible through pendingMinMs', async () => { + vi.useFakeTimers() + + const loaderGate = createControlledPromise() + const rootRoute = createRootRoute({ + pendingMs: 0, + pendingMinMs: 100, + pendingComponent: () =>
Pending
, + loader: () => loaderGate, + component: () =>
Loaded
, + }) + const router = createRouter({ + routeTree: rootRoute, + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + render() + + await act(async () => { + await vi.advanceTimersByTimeAsync(0) + }) + expect(screen.getByTestId('root-pending')).toBeInTheDocument() + expect(screen.queryByTestId('root-content')).not.toBeInTheDocument() + + loaderGate.resolve('loaded') + + await act(async () => { + await vi.advanceTimersByTimeAsync(99) + }) + expect(screen.getByTestId('root-pending')).toBeInTheDocument() + expect(screen.queryByTestId('root-content')).not.toBeInTheDocument() + + await act(async () => { + await vi.advanceTimersByTimeAsync(1) + }) + expect(screen.queryByTestId('root-pending')).not.toBeInTheDocument() + expect(screen.getByTestId('root-content')).toBeInTheDocument() +}) diff --git a/packages/react-router/tests/issue-7367-pending-min-redirect.test.tsx b/packages/react-router/tests/issue-7367-pending-min-redirect.test.tsx new file mode 100644 index 0000000000..666f1d3e4b --- /dev/null +++ b/packages/react-router/tests/issue-7367-pending-min-redirect.test.tsx @@ -0,0 +1,78 @@ +import * as React from 'react' +import { cleanup, render, screen } from '@testing-library/react' +import { afterEach, expect, test, vi } from 'vitest' + +import { + Outlet, + RouterProvider, + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, + redirect, +} from '../src' +import { sleep } from './utils' + +afterEach(() => { + vi.restoreAllMocks() + cleanup() +}) + +// https://github.com/TanStack/router/issues/7367 +// Root route shows a spinner immediately (pendingMs: 0) while beforeLoad +// decides where to send the user, keeps it up for pendingMinMs, and then +// redirects. This used to crash in MatchInnerImpl (white screen) because the +// redirected match was rendered/thrown after its loadPromise was cleared. +test('immediate pending spinner (pendingMs: 0 + pendingMinMs) with root beforeLoad redirect renders the target without render errors', async () => { + const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {}) + let hasRedirected = false + + const rootRoute = createRootRoute({ + component: () => , + pendingMs: 0, + pendingMinMs: 100, + pendingComponent: () =>
loading
, + errorComponent: ({ error }) => ( +
{String(error)}
+ ), + beforeLoad: async () => { + await sleep(50) + if (!hasRedirected) { + hasRedirected = true + throw redirect({ to: '/welcome', replace: true }) + } + }, + }) + + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
Index
, + }) + + const welcomeRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/welcome', + component: () =>
Welcome
, + }) + + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute, welcomeRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + render() + + // pendingMs: 0 — the spinner must show right away. + expect(await screen.findByTestId('pending')).toBeInTheDocument() + + // The redirect must complete: the target renders, no error boundary output + // and no render crash. + expect( + await screen.findByTestId('welcome-page', undefined, { timeout: 5_000 }), + ).toBeInTheDocument() + expect(screen.queryByTestId('pending')).not.toBeInTheDocument() + expect(screen.queryByTestId('root-error')).not.toBeInTheDocument() + expect(router.state.location.pathname).toBe('/welcome') + expect(consoleError).not.toHaveBeenCalled() +}) diff --git a/packages/react-router/tests/issue-7457-redirect-chain-first-load.test.tsx b/packages/react-router/tests/issue-7457-redirect-chain-first-load.test.tsx new file mode 100644 index 0000000000..3583190d79 --- /dev/null +++ b/packages/react-router/tests/issue-7457-redirect-chain-first-load.test.tsx @@ -0,0 +1,109 @@ +import * as React from 'react' +import { cleanup, render, screen } from '@testing-library/react' +import { afterEach, expect, test, vi } from 'vitest' + +import { + Outlet, + RouterProvider, + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, + redirect, +} from '../src' +import { sleep } from './utils' + +afterEach(() => { + vi.restoreAllMocks() + cleanup() +}) + +// https://github.com/TanStack/router/issues/7457 +// A chain of async layout beforeLoad redirects during the very first load +// (search-stripping self-redirect -> layout redirect -> child redirect) used +// to leave a match rendering with a nulled loadPromise, crashing +// MatchInnerImpl with an uncaught `undefined`. Pending UI is enabled for +// every match (defaultPendingMs: 0) to force pending publication mid-chain. +test('chained layout beforeLoad redirects on first load render the final target without throwing from MatchInner', async () => { + const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {}) + + const rootRoute = createRootRoute({ component: () => }) + + const userLayout = createRoute({ + id: 'user', + getParentRoute: () => rootRoute, + validateSearch: (search: Record): { flag?: boolean } => ({ + flag: search.flag === true || search.flag === 'true' ? true : undefined, + }), + beforeLoad: async ({ search }) => { + if (search.flag) { + await sleep(20) + throw redirect({ + to: '.', + replace: true, + search: (prev: any) => ({ ...prev, flag: undefined }), + }) + } + }, + component: () => , + }) + + const dashboardLayout = createRoute({ + id: 'dashboard', + getParentRoute: () => userLayout, + beforeLoad: async () => { + await sleep(30) + throw redirect({ to: '/intro', replace: true }) + }, + component: () => , + }) + + const homeRoute = createRoute({ + getParentRoute: () => dashboardLayout, + path: '/home', + component: () =>
Home
, + }) + + const introLayout = createRoute({ + getParentRoute: () => userLayout, + path: '/intro', + beforeLoad: ({ location }) => { + if (location.pathname !== '/intro/step') { + throw redirect({ to: '/intro/step', replace: true }) + } + }, + component: () => , + }) + + const introIndexRoute = createRoute({ + getParentRoute: () => introLayout, + path: '/', + component: () =>
Intro index
, + }) + + const introStepRoute = createRoute({ + getParentRoute: () => introLayout, + path: 'step', + component: () =>
Intro step
, + }) + + const router = createRouter({ + routeTree: rootRoute.addChildren([ + userLayout.addChildren([ + dashboardLayout.addChildren([homeRoute]), + introLayout.addChildren([introIndexRoute, introStepRoute]), + ]), + ]), + defaultPendingMs: 0, + defaultPendingComponent: () =>
loading
, + history: createMemoryHistory({ initialEntries: ['/home?flag=true'] }), + }) + + render() + + expect( + await screen.findByTestId('intro-step', undefined, { timeout: 5_000 }), + ).toBeInTheDocument() + expect(router.state.location.pathname).toBe('/intro/step') + expect(consoleError).not.toHaveBeenCalled() +}) diff --git a/packages/react-router/tests/issue-7638-invalidate-transition-error.test.tsx b/packages/react-router/tests/issue-7638-invalidate-transition-error.test.tsx new file mode 100644 index 0000000000..eb31703b0a --- /dev/null +++ b/packages/react-router/tests/issue-7638-invalidate-transition-error.test.tsx @@ -0,0 +1,130 @@ +import * as React from 'react' +import { afterEach, expect, test, vi } from 'vitest' +import { + cleanup, + fireEvent, + render, + screen, + waitFor, +} from '@testing-library/react' +import { + Outlet, + RouterProvider, + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, + useRouter, +} from '../src' +import type { ErrorComponentProps } from '../src' + +afterEach(() => { + vi.restoreAllMocks() + cleanup() +}) + +// https://github.com/TanStack/router/issues/7638 +// router.invalidate() called inside React.startTransition while a nested +// route is showing its errorComponent must complete the reload and land back +// on the error UI without crashing React with +// "Rendered more hooks than during the previous render." +function setup({ failVia }: { failVia: 'render' | 'loader' }) { + const rootRoute = createRootRoute({ component: () => }) + + const testRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/test', + component: function TestComponent() { + const router = useRouter() + const [isPending, startTransition] = React.useTransition() + return ( +
+ + +
+ ) + }, + }) + + const childLoader = vi.fn(() => { + if (failVia === 'loader') { + throw new Error('test error') + } + return 'data' + }) + + const testIndexRoute = createRoute({ + getParentRoute: () => testRoute, + path: '/', + loader: childLoader, + component: function ChildComponent() { + if (failVia === 'render') { + throw new Error('test error') + } + return
child content
+ }, + }) + + let errorRenders = 0 + const router = createRouter({ + routeTree: rootRoute.addChildren([testRoute.addChildren([testIndexRoute])]), + history: createMemoryHistory({ initialEntries: ['/test'] }), + defaultErrorComponent: (props: ErrorComponentProps) => { + errorRenders++ + return
error: {props.error.message}
+ }, + }) + + return { router, childLoader, getErrorRenders: () => errorRenders } +} + +test.each(['render', 'loader'] as const)( + 'invalidate() inside startTransition through a nested %s-error route does not crash', + async (failVia) => { + // Error boundaries log caught errors through console.error, and so does a + // hooks-order crash. Capture instead of polluting the test output, then + // inspect the captured calls for the crash signature. + const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {}) + + const { router, childLoader, getErrorRenders } = setup({ failVia }) + render() + + expect(await screen.findByTestId('error-ui')).toBeInTheDocument() + const initialErrorRenders = getErrorRenders() + const initialLoaderCalls = childLoader.mock.calls.length + + fireEvent.click(screen.getByTestId('invalidate')) + + // The invalidated reload must actually complete: the loader re-ran ... + await waitFor(() => { + expect(childLoader.mock.calls.length).toBeGreaterThan(initialLoaderCalls) + }) + + // ... the error UI is rendered again after the reload ... + await waitFor(() => { + expect(screen.getByTestId('error-ui')).toBeInTheDocument() + expect(getErrorRenders()).toBeGreaterThan(initialErrorRenders) + }) + + // React must not have torn the tree down with a hooks-order violation. + const hooksCrash = consoleError.mock.calls.find((call) => + call.some((arg) => + String(arg?.message ?? arg).includes('Rendered more hooks'), + ), + ) + expect(hooksCrash).toBeUndefined() + // The surrounding route (the issue's "frozen" parent) is still mounted + // and interactive. + expect(screen.getByTestId('invalidate')).toBeInTheDocument() + }, +) diff --git a/packages/router-core/tests/issue-4684-head-on-beforeload-error.test.ts b/packages/router-core/tests/issue-4684-head-on-beforeload-error.test.ts new file mode 100644 index 0000000000..05960c8290 --- /dev/null +++ b/packages/router-core/tests/issue-4684-head-on-beforeload-error.test.ts @@ -0,0 +1,86 @@ +import { createMemoryHistory } from '@tanstack/history' +import { describe, expect, test, vi } from 'vitest' +import { BaseRootRoute, BaseRoute } from '../src' +import { createTestRouter } from './routerTestUtils' + +// https://github.com/TanStack/router/issues/4684 +// +// When beforeLoad throws, head() must still execute for the retained lane — +// the root head owns global stylesheets/title that the errorComponent page +// depends on, and the failing route's own head can provide error-specific +// head content. +describe('issue #4684: head executes when beforeLoad throws', () => { + const setup = (isServer: boolean) => { + const beforeLoadError = new Error('beforeLoad failed') + const rootHead = vi.fn(() => ({ + meta: [{ title: 'Root title' }], + links: [{ rel: 'stylesheet', href: '/global.css' }], + })) + const failingHead = vi.fn(({ match }: any) => ({ + meta: [{ title: match.error ? 'Error title' : 'Success title' }], + })) + + const rootRoute = new BaseRootRoute({ head: rootHead }) + const failingRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/fail', + beforeLoad: () => { + throw beforeLoadError + }, + head: failingHead, + errorComponent: () => 'error', + }) + + const router = createTestRouter({ + routeTree: rootRoute.addChildren([failingRoute]), + history: createMemoryHistory({ initialEntries: ['/fail'] }), + isServer, + }) + + return { router, rootRoute, failingRoute, rootHead, failingHead } + } + + test('server load projects root and failing-route heads for the error lane', async () => { + const { router, rootRoute, failingRoute, rootHead, failingHead } = + setup(true) + + await router.load() + + expect(router.state.statusCode).toBe(500) + expect(rootHead).toHaveBeenCalledTimes(1) + expect(failingHead).toHaveBeenCalledTimes(1) + + const rootMatch = router.state.matches.find( + (match) => match.routeId === rootRoute.id, + ) + const failingMatch = router.state.matches.find( + (match) => match.routeId === failingRoute.id, + ) + expect(rootMatch?.meta).toEqual([{ title: 'Root title' }]) + expect(rootMatch?.links).toEqual([ + { rel: 'stylesheet', href: '/global.css' }, + ]) + expect(failingMatch?.status).toBe('error') + expect(failingMatch?.meta).toEqual([{ title: 'Error title' }]) + }) + + test('client load projects root and failing-route heads for the error lane', async () => { + const { router, rootRoute, failingRoute, rootHead, failingHead } = + setup(false) + + await router.load() + + expect(rootHead).toHaveBeenCalledTimes(1) + expect(failingHead).toHaveBeenCalledTimes(1) + + const rootMatch = router.state.matches.find( + (match) => match.routeId === rootRoute.id, + ) + const failingMatch = router.state.matches.find( + (match) => match.routeId === failingRoute.id, + ) + expect(rootMatch?.meta).toEqual([{ title: 'Root title' }]) + expect(failingMatch?.status).toBe('error') + expect(failingMatch?.meta).toEqual([{ title: 'Error title' }]) + }) +}) diff --git a/packages/router-core/tests/issue-6371-search-default-normalization-abort.test.ts b/packages/router-core/tests/issue-6371-search-default-normalization-abort.test.ts new file mode 100644 index 0000000000..a52a7f183d --- /dev/null +++ b/packages/router-core/tests/issue-6371-search-default-normalization-abort.test.ts @@ -0,0 +1,109 @@ +import { describe, expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute, trimPathRight } from '../src' +import { createTestRouter } from './routerTestUtils' + +/** + * Repro for https://github.com/TanStack/router/issues/6371 + * + * Entering a route directly via URL without search params, while + * validateSearch supplies defaults, makes the framework Transitioner commit a + * canonical replace-navigation (URL with the defaulted search) right after + * the mount load already started. That replace supersedes the first load + * pass and aborts its match's abortController. A loader that forwarded that + * signal to fetch() re-throws an AbortError ("signal is aborted without + * reason"): it belongs to the abandoned pass and must not surface as a route + * error; the follow-up canonical load must complete normally. + */ + +const waitForMacrotask = () => + new Promise((resolve) => { + setTimeout(resolve, 0) + }) + +describe('issue #6371: search default normalization aborts the mount load silently', () => { + test('canonical replace after validateSearch defaults does not surface AbortError', async () => { + const invocations: Array<{ resolve: () => void; signal: AbortSignal }> = [] + + const rootRoute = new BaseRootRoute({}) + const aboutRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/about', + validateSearch: (search: Record) => ({ + page: typeof search.page === 'number' ? search.page : 1, + }), + loader: ({ abortController }: { abortController: AbortController }) => + new Promise((resolve, reject) => { + invocations.push({ + resolve: () => resolve('about data'), + signal: abortController.signal, + }) + abortController.signal.addEventListener('abort', () => { + const abortError = new Error('signal is aborted without reason') + abortError.name = 'AbortError' + reject(abortError) + }) + }), + }) + + const router = createTestRouter({ + routeTree: rootRoute.addChildren([aboutRoute]), + history: createMemoryHistory({ initialEntries: ['/about'] }), + }) + + // Mount load for the raw URL (no search params in the href yet). + const initialLoad = router.load() + await vi.waitFor(() => expect(invocations.length).toBe(1)) + + // What the framework Transitioner does on mount: rebuild the canonical + // location with validated search and replace if the public href differs. + const nextLocation = router.buildLocation({ + to: router.latestLocation.pathname, + search: true, + params: true, + hash: true, + state: true, + _includeValidateSearch: true, + } as any) + + expect(nextLocation.publicHref).toContain('page=1') + expect(trimPathRight(router.latestLocation.publicHref)).not.toBe( + trimPathRight(nextLocation.publicHref), + ) + + // In core (no history subscribers) commitLocation starts the second load. + void router.commitLocation({ ...nextLocation, replace: true } as any) + + await vi.waitFor(() => expect(invocations.length).toBe(2)) + + // The superseded mount-load generation was aborted (its fetch canceled). + expect(invocations[0]!.signal.aborted).toBe(true) + + // Give the aborted rejection time to (incorrectly) commit as an error. + await waitForMacrotask() + await waitForMacrotask() + + for (const match of router.state.matches) { + expect(match.status).not.toBe('error') + expect((match.error as Error | undefined)?.name).not.toBe('AbortError') + } + + invocations[1]!.resolve() + // Awaiting the superseded mount load also awaits the superseding chain. + await initialLoad + + // No runaway normalization loop: exactly one aborted + one canonical run. + expect(invocations.length).toBe(2) + + const aboutMatch = router.state.matches.find( + (match) => match.routeId === aboutRoute.id, + )! + expect(aboutMatch.status).toBe('success') + expect(aboutMatch.error).toBeUndefined() + expect(aboutMatch.loaderData).toBe('about data') + expect(aboutMatch.search).toEqual({ page: 1 }) + + expect(router.state.location.search).toEqual({ page: 1 }) + expect(router.latestLocation.publicHref).toContain('page=1') + }) +}) From 0b178a79e2e872df0107bd7f0faa891c4c9815ef Mon Sep 17 00:00:00 2001 From: Flo Date: Wed, 15 Jul 2026 09:48:19 +0200 Subject: [PATCH 09/51] test: add more regression coverage for existing router behavior (#7822) --- e2e/react-start/basic/src/routes/posts.tsx | 8 + e2e/react-start/basic/tests/not-found.spec.ts | 18 +++ .../tests/errorComponent.test.tsx | 73 +++++++++ ...ssue-2905-root-beforeload-pending.test.tsx | 59 +++++++ ...8-router-provider-context-preload.test.tsx | 113 +++++++++++++ ...-7638-invalidate-transition-error.test.tsx | 129 ++++++++++----- packages/react-router/tests/loaders.test.tsx | 15 +- .../issue-3293-on-enter-after-loader.test.ts | 108 +++++++++++++ ...4078-loader-notfound-root-boundary.test.ts | 111 +++++++++++++ ...arent-context-search-normalization.test.ts | 102 ++++++++++++ ...ue-5106-hydrated-notfound-boundary.test.ts | 151 ++++++++++++++++++ 11 files changed, 840 insertions(+), 47 deletions(-) create mode 100644 packages/react-router/tests/issue-2905-root-beforeload-pending.test.tsx create mode 100644 packages/react-router/tests/issue-5778-router-provider-context-preload.test.tsx create mode 100644 packages/router-core/tests/issue-3293-on-enter-after-loader.test.ts create mode 100644 packages/router-core/tests/issue-4078-loader-notfound-root-boundary.test.ts create mode 100644 packages/router-core/tests/issue-4696-parent-context-search-normalization.test.ts create mode 100644 packages/router-core/tests/issue-5106-hydrated-notfound-boundary.test.ts diff --git a/e2e/react-start/basic/src/routes/posts.tsx b/e2e/react-start/basic/src/routes/posts.tsx index 0f69c18341..22fb7e60a8 100644 --- a/e2e/react-start/basic/src/routes/posts.tsx +++ b/e2e/react-start/basic/src/routes/posts.tsx @@ -1,3 +1,4 @@ +import { useState } from 'react' import { Link, Outlet, createFileRoute } from '@tanstack/react-router' import { fetchPosts } from '~/utils/posts' @@ -16,9 +17,16 @@ export const Route = createFileRoute('/posts')({ function PostsComponent() { const posts = Route.useLoaderData() + const [hydrationCount, setHydrationCount] = useState(0) return (
+
    {[...posts, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map( (post) => { diff --git a/e2e/react-start/basic/tests/not-found.spec.ts b/e2e/react-start/basic/tests/not-found.spec.ts index d6e91696e3..339f841b21 100644 --- a/e2e/react-start/basic/tests/not-found.spec.ts +++ b/e2e/react-start/basic/tests/not-found.spec.ts @@ -12,6 +12,24 @@ test.use({ ], }) test.describe('not-found', () => { + test('direct nested not-found hydrates its already-rendered parent route', async ({ + page, + }) => { + test.skip(isSpaMode, 'issue #5106 requires a server-rendered direct visit') + + const response = await page.goto('/posts/i-do-not-exist') + await page.waitForLoadState('networkidle') + + const serverHtml = await response?.text() + expect(serverHtml).toContain('posts-parent-hydration-counter') + expect(serverHtml).toContain('Post not found') + await expect(page.getByText('Post not found')).toBeInViewport() + const counter = page.getByTestId('posts-parent-hydration-counter') + await expect(counter).toHaveText('Parent hydration count: 0') + await counter.click() + await expect(counter).toHaveText('Parent hydration count: 1') + }) + test(`global not found`, async ({ page }) => { const response = await page.goto(`/this-page-does-not-exist/foo/bar`) diff --git a/packages/react-router/tests/errorComponent.test.tsx b/packages/react-router/tests/errorComponent.test.tsx index 0779c2e8c4..296c0caf34 100644 --- a/packages/react-router/tests/errorComponent.test.tsx +++ b/packages/react-router/tests/errorComponent.test.tsx @@ -3,6 +3,7 @@ import { act, cleanup, fireEvent, render, screen } from '@testing-library/react' import ReactDOMServer from 'react-dom/server' import { + HeadContent, Link, Outlet, RouterProvider, @@ -14,6 +15,11 @@ import { createRouter, notFound, } from '../src' +import { + RouterServer, + createRequestHandler, + renderRouterToString, +} from '../src/ssr/server' import type { ErrorComponentProps, RouterHistory } from '../src' function MyErrorComponent(props: ErrorComponentProps) { @@ -344,6 +350,73 @@ test('SSR errorComponent receives primitive errors thrown from beforeLoad', asyn expect(html).toContain('primitive error thrown') }) +// https://github.com/TanStack/router/issues/4684 +test('#4684: SSR renders head content when beforeLoad throws', async () => { + const rootRoute = createRootRoute({ + head: () => ({ + links: [{ rel: 'stylesheet', href: '/global.css' }], + }), + shellComponent: function RootDocument({ children }) { + return ( + + + + + {children} + + ) + }, + component: Outlet, + }) + const failingRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/fail', + beforeLoad: () => { + throw new Error('beforeLoad failed') + }, + head: ({ match }) => ({ + meta: [{ title: match.error ? 'Error title' : 'Success title' }], + }), + component: function FailingRoute() { + return
    Route content
    + }, + errorComponent: ({ error }) =>
    Error UI: {error.message}
    , + }) + + const handler = createRequestHandler({ + request: new Request('http://localhost/fail'), + createRouter: () => + createRouter({ + routeTree: rootRoute.addChildren([failingRoute]), + isServer: true, + }), + }) + + const response = await handler(({ router, responseHeaders }) => + renderRouterToString({ + router, + responseHeaders, + children: , + }), + ) + + expect(response.status).toBe(500) + const html = await response.text() + const serverDocument = new DOMParser().parseFromString(html, 'text/html') + + expect(serverDocument.body.textContent).toContain( + 'Error UI: beforeLoad failed', + ) + expect(serverDocument.head.querySelector('title')?.textContent).toBe( + 'Error title', + ) + expect( + serverDocument.head.querySelector( + 'link[rel="stylesheet"][href="/global.css"]', + ), + ).not.toBeNull() +}) + describe('notFoundComponent is rendered when an error is thrown in params.parse', () => { test('displays notFoundComponent when error is thrown in params.parse', async () => { const history = createMemoryHistory({ initialEntries: ['/'] }) diff --git a/packages/react-router/tests/issue-2905-root-beforeload-pending.test.tsx b/packages/react-router/tests/issue-2905-root-beforeload-pending.test.tsx new file mode 100644 index 0000000000..2821571c42 --- /dev/null +++ b/packages/react-router/tests/issue-2905-root-beforeload-pending.test.tsx @@ -0,0 +1,59 @@ +import * as React from 'react' +import { act, cleanup, render, screen } from '@testing-library/react' +import { afterEach, expect, test, vi } from 'vitest' +import { + RouterProvider, + createMemoryHistory, + createRootRoute, + createRouter, +} from '../src' + +afterEach(() => { + cleanup() + vi.useRealTimers() +}) + +// https://github.com/TanStack/router/issues/2905 +test('#2905: a root pendingComponent renders while root beforeLoad is pending', async () => { + vi.useFakeTimers() + + const rootRoute = createRootRoute({ + beforeLoad: async () => { + await new Promise((resolve) => setTimeout(resolve, 3_000)) + }, + pendingComponent: () =>
    Root pending
    , + component: () =>
    Root
    , + }) + const router = createRouter({ + routeTree: rootRoute, + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + render() + await act(async () => { + await vi.advanceTimersByTimeAsync(0) + }) + + await act(async () => { + await vi.advanceTimersByTimeAsync(999) + }) + expect(screen.queryByTestId('root-pending')).not.toBeInTheDocument() + expect(screen.queryByTestId('root-content')).not.toBeInTheDocument() + + await act(async () => { + await vi.advanceTimersByTimeAsync(1) + }) + expect(screen.getByTestId('root-pending')).toBeInTheDocument() + expect(screen.queryByTestId('root-content')).not.toBeInTheDocument() + + await act(async () => { + await vi.advanceTimersByTimeAsync(1_999) + }) + expect(screen.getByTestId('root-pending')).toBeInTheDocument() + + await act(async () => { + await vi.advanceTimersByTimeAsync(1) + }) + expect(screen.queryByTestId('root-pending')).not.toBeInTheDocument() + expect(screen.getByTestId('root-content')).toBeInTheDocument() +}) diff --git a/packages/react-router/tests/issue-5778-router-provider-context-preload.test.tsx b/packages/react-router/tests/issue-5778-router-provider-context-preload.test.tsx new file mode 100644 index 0000000000..e179a1502e --- /dev/null +++ b/packages/react-router/tests/issue-5778-router-provider-context-preload.test.tsx @@ -0,0 +1,113 @@ +import * as React from 'react' +import { + cleanup, + fireEvent, + render, + screen, + waitFor, +} from '@testing-library/react' +import { afterEach, expect, test } from 'vitest' +import { + Link, + Outlet, + RouterProvider, + createMemoryHistory, + createRootRouteWithContext, + createRoute, + createRouter, +} from '../src' + +afterEach(cleanup) + +// https://github.com/TanStack/router/issues/5778 +test('#5778: intent preload sees a RouterProvider context update before the first navigation', async () => { + const seen: Array<{ + route: 'auth' | 'foo' + foo: string + cause: string + preload: boolean + }> = [] + const rootRoute = createRootRouteWithContext<{ foo: string }>()({ + component: () => ( + <> + + Foo + + + + ), + }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
    Home
    , + }) + const authRoute = createRoute({ + getParentRoute: () => rootRoute, + id: '_authenticated', + beforeLoad: ({ context, cause, preload }) => { + seen.push({ route: 'auth', foo: context.foo, cause, preload }) + }, + component: () => , + }) + const fooRoute = createRoute({ + getParentRoute: () => authRoute, + path: '/foo', + beforeLoad: ({ context, cause, preload }) => { + seen.push({ route: 'foo', foo: context.foo, cause, preload }) + }, + component: () =>
    Foo page
    , + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([ + indexRoute, + authRoute.addChildren([fooRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/'] }), + context: { foo: null! }, + defaultPreload: 'intent', + defaultPreloadDelay: 0, + }) + + function App() { + const [foo, setFoo] = React.useState('foo') + return ( + <> + {foo} + + + + ) + } + + render() + expect(await screen.findByText('Home')).toBeInTheDocument() + expect(screen.getByLabelText('Current context')).toHaveTextContent('foo') + + fireEvent.click(screen.getByRole('button', { name: 'Update context' })) + await waitFor(() => + expect(screen.getByLabelText('Current context')).toHaveTextContent('baz'), + ) + + expect(seen).toEqual([]) + const link = screen.getByRole('link', { name: 'Foo' }) + fireEvent.mouseEnter(link) + await waitFor(() => expect(seen).toHaveLength(2)) + expect(seen).toEqual([ + { route: 'auth', foo: 'baz', cause: 'preload', preload: true }, + { route: 'foo', foo: 'baz', cause: 'preload', preload: true }, + ]) + expect(screen.getByText('Home')).toBeInTheDocument() + expect(screen.queryByText('Foo page')).not.toBeInTheDocument() + expect(router.state.location.pathname).toBe('/') + + fireEvent.click(link) + expect(await screen.findByText('Foo page')).toBeInTheDocument() + expect(router.state.location.pathname).toBe('/foo') +}) diff --git a/packages/react-router/tests/issue-7638-invalidate-transition-error.test.tsx b/packages/react-router/tests/issue-7638-invalidate-transition-error.test.tsx index eb31703b0a..9f1c0eec3f 100644 --- a/packages/react-router/tests/issue-7638-invalidate-transition-error.test.tsx +++ b/packages/react-router/tests/issue-7638-invalidate-transition-error.test.tsx @@ -1,6 +1,7 @@ import * as React from 'react' import { afterEach, expect, test, vi } from 'vitest' import { + act, cleanup, fireEvent, render, @@ -10,6 +11,7 @@ import { import { Outlet, RouterProvider, + createControlledPromise, createMemoryHistory, createRootRoute, createRoute, @@ -19,7 +21,6 @@ import { import type { ErrorComponentProps } from '../src' afterEach(() => { - vi.restoreAllMocks() cleanup() }) @@ -30,11 +31,15 @@ afterEach(() => { // "Rendered more hooks than during the previous render." function setup({ failVia }: { failVia: 'render' | 'loader' }) { const rootRoute = createRootRoute({ component: () => }) + const parentAction = vi.fn() + const secondChildLoad = createControlledPromise() + let invalidation: Promise | undefined const testRoute = createRoute({ getParentRoute: () => rootRoute, path: '/test', component: function TestComponent() { + testRoute.useLoaderData() const router = useRouter() const [isPending, startTransition] = React.useTransition() return ( @@ -44,11 +49,15 @@ function setup({ failVia }: { failVia: 'render' | 'loader' }) { disabled={isPending} onClick={() => startTransition(() => { - router.invalidate() + invalidation = router.invalidate() + return invalidation }) } > - invalidate + {isPending ? 'pending' : 'invalidate'} + +
@@ -56,9 +65,14 @@ function setup({ failVia }: { failVia: 'render' | 'loader' }) { }, }) - const childLoader = vi.fn(() => { + let childLoaderCalls = 0 + const childLoader = vi.fn(async () => { + childLoaderCalls++ + if (childLoaderCalls === 2) { + await secondChildLoad + } if (failVia === 'loader') { - throw new Error('test error') + throw new Error('loader error') } return 'data' }) @@ -69,7 +83,7 @@ function setup({ failVia }: { failVia: 'render' | 'loader' }) { loader: childLoader, component: function ChildComponent() { if (failVia === 'render') { - throw new Error('test error') + throw new Error('render error') } return
child content
}, @@ -85,7 +99,14 @@ function setup({ failVia }: { failVia: 'render' | 'loader' }) { }, }) - return { router, childLoader, getErrorRenders: () => errorRenders } + return { + router, + childLoader, + parentAction, + secondChildLoad, + getErrorRenders: () => errorRenders, + getInvalidation: () => invalidation, + } } test.each(['render', 'loader'] as const)( @@ -94,37 +115,71 @@ test.each(['render', 'loader'] as const)( // Error boundaries log caught errors through console.error, and so does a // hooks-order crash. Capture instead of polluting the test output, then // inspect the captured calls for the crash signature. + const { + router, + childLoader, + parentAction, + secondChildLoad, + getErrorRenders, + getInvalidation, + } = setup({ failVia }) const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {}) - const { router, childLoader, getErrorRenders } = setup({ failVia }) - render() - - expect(await screen.findByTestId('error-ui')).toBeInTheDocument() - const initialErrorRenders = getErrorRenders() - const initialLoaderCalls = childLoader.mock.calls.length - - fireEvent.click(screen.getByTestId('invalidate')) - - // The invalidated reload must actually complete: the loader re-ran ... - await waitFor(() => { - expect(childLoader.mock.calls.length).toBeGreaterThan(initialLoaderCalls) - }) - - // ... the error UI is rendered again after the reload ... - await waitFor(() => { - expect(screen.getByTestId('error-ui')).toBeInTheDocument() - expect(getErrorRenders()).toBeGreaterThan(initialErrorRenders) - }) - - // React must not have torn the tree down with a hooks-order violation. - const hooksCrash = consoleError.mock.calls.find((call) => - call.some((arg) => - String(arg?.message ?? arg).includes('Rendered more hooks'), - ), - ) - expect(hooksCrash).toBeUndefined() - // The surrounding route (the issue's "frozen" parent) is still mounted - // and interactive. - expect(screen.getByTestId('invalidate')).toBeInTheDocument() + try { + render() + + expect(await screen.findByTestId('error-ui')).toHaveTextContent( + `error: ${failVia} error`, + ) + const initialErrorRenders = getErrorRenders() + expect(childLoader).toHaveBeenCalledTimes(1) + consoleError.mockClear() + + fireEvent.click(screen.getByTestId('invalidate')) + + await waitFor(() => { + expect(childLoader).toHaveBeenCalledTimes(2) + expect(screen.getByTestId('invalidate')).toHaveTextContent('pending') + expect(screen.getByTestId('invalidate')).toBeDisabled() + }) + expect(secondChildLoad.status).toBe('pending') + + const invalidation = getInvalidation() + if (!invalidation) { + throw new Error('invalidate action did not return its promise') + } + + await act(async () => { + secondChildLoad.resolve() + await invalidation + }) + + await waitFor(() => { + expect(screen.getByTestId('error-ui')).toHaveTextContent( + `error: ${failVia} error`, + ) + expect(getErrorRenders()).toBeGreaterThan(initialErrorRenders) + expect(screen.getByTestId('invalidate')).toHaveTextContent('invalidate') + expect(screen.getByTestId('invalidate')).toBeEnabled() + }) + + fireEvent.click(screen.getByTestId('parent-action')) + expect(parentAction).toHaveBeenCalledTimes(1) + + const hooksCrash = consoleError.mock.calls.find((call) => + call.some((arg) => + String(arg?.message ?? arg).includes('Rendered more hooks'), + ), + ) + expect(hooksCrash).toBeUndefined() + } finally { + if (secondChildLoad.status === 'pending') { + await act(async () => { + secondChildLoad.resolve() + await getInvalidation()?.catch(() => undefined) + }) + } + consoleError.mockRestore() + } }, ) diff --git a/packages/react-router/tests/loaders.test.tsx b/packages/react-router/tests/loaders.test.tsx index d9b5968d72..869aeec76c 100644 --- a/packages/react-router/tests/loaders.test.tsx +++ b/packages/react-router/tests/loaders.test.tsx @@ -912,30 +912,25 @@ test('reproducer for #6388 - rapid navigation between parameterized routes shoul }) render() - await act(() => router.latestLoadPromise) - - const pendingComponent = screen.findByTestId('pending-component') expect(await screen.findByTestId('home-page')).toBeInTheDocument() - const param1Link = await screen.findByTestId('link-to-param-1') fireEvent.click(param1Link) - expect(await pendingComponent).toBeInTheDocument() + expect(await screen.findByTestId('pending-component')).toBeInTheDocument() const param2Link = await screen.findByTestId('link-to-param-2') fireEvent.click(param2Link) - expect(await pendingComponent).toBeInTheDocument() + expect(await screen.findByTestId('pending-component')).toBeInTheDocument() fireEvent.click(param1Link) - expect(await pendingComponent).toBeInTheDocument() + expect(await screen.findByTestId('pending-component')).toBeInTheDocument() - await act(() => router.latestLoadPromise) + const paramPage = await screen.findByTestId('param-page') expect(onAbortMock).toHaveBeenCalled() expect(errorComponentRenderCount).not.toHaveBeenCalled() expect(screen.queryByTestId('error-component')).not.toBeInTheDocument() - expect(await pendingComponent).not.toBeInTheDocument() + expect(screen.queryByTestId('pending-component')).not.toBeInTheDocument() - const paramPage = await screen.findByTestId('param-page') expect(paramPage).toBeInTheDocument() expect(paramPage).toHaveTextContent('Param Component 1 Done') expect(loaderCompleteMock).toHaveBeenCalled() diff --git a/packages/router-core/tests/issue-3293-on-enter-after-loader.test.ts b/packages/router-core/tests/issue-3293-on-enter-after-loader.test.ts new file mode 100644 index 0000000000..a1be910314 --- /dev/null +++ b/packages/router-core/tests/issue-3293-on-enter-after-loader.test.ts @@ -0,0 +1,108 @@ +import { expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute, createControlledPromise } from '../src' +import { createTestRouter } from './routerTestUtils' + +// Existing-behavior controls for https://github.com/TanStack/router/issues/3293 +test('#3293 existing behavior: direct load runs onEnter after beforeLoad and loader', async () => { + const beforeLoadGate = createControlledPromise<{ ready: true }>() + const loaderGate = createControlledPromise<{ someData: 42 }>() + const beforeLoad = vi.fn(() => beforeLoadGate) + const loader = vi.fn(() => loaderGate) + const onEnter = vi.fn() + + const rootRoute = new BaseRootRoute({}) + const aboutRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/about', + beforeLoad, + loader, + onEnter, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([aboutRoute]), + history: createMemoryHistory({ initialEntries: ['/about'] }), + }) + + const load = router.load() + await vi.waitFor(() => expect(beforeLoad).toHaveBeenCalledTimes(1)) + expect(loader).not.toHaveBeenCalled() + expect(onEnter).not.toHaveBeenCalled() + + beforeLoadGate.resolve({ ready: true }) + await vi.waitFor(() => expect(loader).toHaveBeenCalledTimes(1)) + expect(loader).toHaveBeenCalledWith( + expect.objectContaining({ + context: expect.objectContaining({ ready: true }), + }), + ) + expect(onEnter).not.toHaveBeenCalled() + + loaderGate.resolve({ someData: 42 }) + await load + + expect(onEnter).toHaveBeenCalledTimes(1) + expect(onEnter).toHaveBeenCalledWith( + expect.objectContaining({ + status: 'success', + context: expect.objectContaining({ ready: true }), + loaderData: { someData: 42 }, + }), + ) +}) + +test('#3293 existing behavior: uncached SPA navigation runs onEnter after beforeLoad and loader', async () => { + const beforeLoadGate = createControlledPromise<{ ready: true }>() + const loaderGate = createControlledPromise<{ someData: 42 }>() + const beforeLoad = vi.fn(() => beforeLoadGate) + const loader = vi.fn(() => loaderGate) + const onEnter = vi.fn() + + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const aboutRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/about', + beforeLoad, + loader, + onEnter, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, aboutRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + expect(beforeLoad).not.toHaveBeenCalled() + expect(loader).not.toHaveBeenCalled() + expect(onEnter).not.toHaveBeenCalled() + + const navigation = router.navigate({ to: '/about' }) + await vi.waitFor(() => expect(beforeLoad).toHaveBeenCalledTimes(1)) + expect(loader).not.toHaveBeenCalled() + expect(onEnter).not.toHaveBeenCalled() + + beforeLoadGate.resolve({ ready: true }) + await vi.waitFor(() => expect(loader).toHaveBeenCalledTimes(1)) + expect(loader).toHaveBeenCalledWith( + expect.objectContaining({ + context: expect.objectContaining({ ready: true }), + }), + ) + expect(onEnter).not.toHaveBeenCalled() + + loaderGate.resolve({ someData: 42 }) + await navigation + + expect(onEnter).toHaveBeenCalledTimes(1) + expect(onEnter).toHaveBeenCalledWith( + expect.objectContaining({ + status: 'success', + context: expect.objectContaining({ ready: true }), + loaderData: { someData: 42 }, + }), + ) +}) diff --git a/packages/router-core/tests/issue-4078-loader-notfound-root-boundary.test.ts b/packages/router-core/tests/issue-4078-loader-notfound-root-boundary.test.ts new file mode 100644 index 0000000000..45085a8e1f --- /dev/null +++ b/packages/router-core/tests/issue-4078-loader-notfound-root-boundary.test.ts @@ -0,0 +1,111 @@ +import { describe, expect, it } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { + BaseRootRoute, + BaseRoute, + createControlledPromise, + notFound, + rootRouteId, +} from '../src' +import { createTestRouter } from './routerTestUtils' + +// Existing Core attribution coverage related to: +// https://github.com/TanStack/router/issues/4078 +// https://github.com/TanStack/router/issues/2255 +// Throwing an untargeted notFound() in a child loader used to always render the +// defaultNotFoundComponent, even when __root__ defined a notFoundComponent +// (#4078). #2255 is the same asymmetry: a path-mismatch notFound renders the +// root component + notFoundComponent, but a loader-thrown notFound did not. +// +// At Router Core's boundary, root attribution is represented by the root match +// with globalNotFound. These assertions do not distinguish the +// configured root notFoundComponent from the router default; that reported +// rendering behavior requires framework-level coverage. +describe('#4078 / #2255 existing Core root-boundary attribution', () => { + const setup = (initialEntries: Array) => { + const loaderStarted = createControlledPromise() + const loaderResponse = createControlledPromise() + const rootRoute = new BaseRootRoute({ + component: () => 'Root', + notFoundComponent: () => 'Root not found', + }) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const aboutRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/about', + loader: async () => { + loaderStarted.resolve() + await loaderResponse + throw notFound() + }, + component: () => 'About', + }) + + return { + router: createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, aboutRoute]), + history: createMemoryHistory({ initialEntries }), + }), + loaderStarted, + loaderResponse, + } + } + + const getRootBoundaryProjection = ( + router: ReturnType['router'], + ) => { + const rootMatch = router.state.matches.find( + (match) => match.routeId === rootRouteId, + ) + + return { + routeId: rootMatch?.routeId, + globalNotFound: rootMatch?.globalNotFound, + } + } + + it('an async loader notFound navigation selects the root boundary', async () => { + const { router, loaderStarted, loaderResponse } = setup(['/']) + await router.load() + + const navigation = router.navigate({ to: '/about' }) + await loaderStarted + try { + expect(loaderResponse.status).toBe('pending') + } finally { + loaderResponse.resolve() + } + + await navigation + + expect(router.state.location.pathname).toBe('/about') + expect(getRootBoundaryProjection(router)).toEqual({ + routeId: rootRouteId, + globalNotFound: true, + }) + }) + + it('matches the root-boundary projection of an unmatched URL (#2255 parity)', async () => { + const unmatched = setup(['/missing']) + await unmatched.router.load() + const unmatchedProjection = getRootBoundaryProjection(unmatched.router) + expect(unmatchedProjection).toEqual({ + routeId: rootRouteId, + globalNotFound: true, + }) + + const loaderNotFound = setup(['/']) + await loaderNotFound.router.load() + const navigation = loaderNotFound.router.navigate({ to: '/about' }) + await loaderNotFound.loaderStarted + loaderNotFound.loaderResponse.resolve() + await navigation + + expect(getRootBoundaryProjection(loaderNotFound.router)).toEqual( + unmatchedProjection, + ) + }) +}) diff --git a/packages/router-core/tests/issue-4696-parent-context-search-normalization.test.ts b/packages/router-core/tests/issue-4696-parent-context-search-normalization.test.ts new file mode 100644 index 0000000000..20ef368318 --- /dev/null +++ b/packages/router-core/tests/issue-4696-parent-context-search-normalization.test.ts @@ -0,0 +1,102 @@ +import { expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute } from '../src' +import { createTestRouter } from './routerTestUtils' + +// Existing-behavior coverage for https://github.com/TanStack/router/issues/4696 +test('#4696 existing behavior: normalized child search keeps reused parent context', async () => { + const rootLoader = vi.fn( + ({ context }: { context: Record }) => context, + ) + const dashboardBeforeLoad = vi.fn( + ({ context }: { context: Record }) => { + if (!context.isAuthenticated) { + throw new Error('Authentication context was lost') + } + }, + ) + const rootRoute = new BaseRootRoute({ + beforeLoad: async () => { + await Promise.resolve() + return { + initialData: { + user: { email: 'mr.user@gmail.com', role: 'user' }, + }, + initializationError: undefined, + isAuthenticated: true, + isAdmin: false, + } + }, + loader: rootLoader, + }) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const dashboardRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/dashboard', + validateSearch: () => ({ page: 0 }), + beforeLoad: dashboardBeforeLoad, + }) + const history = createMemoryHistory({ initialEntries: ['/'] }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, dashboardRoute]), + history, + }) + + await router.load() + expect(rootLoader).toHaveBeenCalledTimes(1) + expect(rootLoader.mock.calls[0]?.[0].context).toMatchObject({ + isAuthenticated: true, + }) + expect(router.state.location.pathname).toBe('/') + router.update({ + history: createMemoryHistory({ + initialEntries: ['/dashboard?page=0'], + }), + }) + await router.load() + expect(router.state.location.pathname).toBe('/dashboard') + expect(rootLoader).toHaveBeenCalledTimes(1) + expect(dashboardBeforeLoad).toHaveBeenCalledTimes(1) + expect(dashboardBeforeLoad).toHaveBeenLastCalledWith( + expect.objectContaining({ + context: expect.objectContaining({ isAuthenticated: true }), + }), + ) + expect( + router.state.matches.find((match) => match.routeId === rootRoute.id) + ?.loaderData, + ).toMatchObject({ isAuthenticated: true }) + + dashboardBeforeLoad.mockClear() + router.update({ + history: createMemoryHistory({ initialEntries: ['/dashboard'] }), + }) + await router.load() + + expect(dashboardBeforeLoad).toHaveBeenCalledTimes(1) + expect(dashboardBeforeLoad).toHaveBeenLastCalledWith( + expect.objectContaining({ + context: expect.objectContaining({ isAuthenticated: true }), + }), + ) + expect(rootLoader).toHaveBeenCalledTimes(1) + expect(router.state.location.pathname).toBe('/dashboard') + const rootMatch = router.state.matches.find( + (match) => match.routeId === rootRoute.id, + ) + const dashboardMatch = router.state.matches.find( + (match) => match.routeId === dashboardRoute.id, + ) + expect(rootMatch?.loaderData).toMatchObject({ + isAuthenticated: true, + isAdmin: false, + }) + expect(dashboardMatch).toMatchObject({ + status: 'success', + search: { page: 0 }, + searchError: undefined, + }) +}) diff --git a/packages/router-core/tests/issue-5106-hydrated-notfound-boundary.test.ts b/packages/router-core/tests/issue-5106-hydrated-notfound-boundary.test.ts new file mode 100644 index 0000000000..8a1570a3f4 --- /dev/null +++ b/packages/router-core/tests/issue-5106-hydrated-notfound-boundary.test.ts @@ -0,0 +1,151 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute, isNotFound } from '../src' +import { hydrate } from '../src/ssr/client' +import { createTestRouter } from './routerTestUtils' +import type { TsrSsrGlobal } from '../src/ssr/types' +import type { Manifest } from '../src/manifest' + +const testManifest: Manifest = { routes: {} } + +// Supplemental Core state coverage for +// https://github.com/TanStack/router/issues/5106. The reported rendered React +// hydration symptom is covered by the React Start E2E test. +describe('hydrated child-owned notFound boundary coverage', () => { + let mockWindow: { $_TSR?: TsrSsrGlobal } + + beforeEach(() => { + mockWindow = {} + ;(global as any).window = mockWindow + }) + + afterEach(() => { + delete (global as any).window + vi.restoreAllMocks() + }) + + it('#5106 existing behavior: adopts a child-owned boundary without invoking its loaders during hydration', async () => { + const postsLoader = vi.fn(() => 'posts-data') + const postLoader = vi.fn(() => 'post-data') + const history = createMemoryHistory({ + initialEntries: ['/posts/i-do-not-exist'], + }) + + const rootRoute = new BaseRootRoute({}) + const postsRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/posts', + loader: postsLoader, + component: () => 'Posts', + }) + const postRoute = new BaseRoute({ + getParentRoute: () => postsRoute, + path: '/$postId', + loader: postLoader, + component: () => 'Post', + notFoundComponent: () => 'Post not found', + }) + const safeLoader = vi.fn(() => 'safe-data') + const safeRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/safe', + loader: safeLoader, + }) + + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + postsRoute.addChildren([postRoute]), + safeRoute, + ]), + history, + isServer: false, + }) + + const matches = router.matchRoutes(router.stores.location.get()) + expect(matches.map((match) => match.routeId)).toEqual([ + rootRoute.id, + postsRoute.id, + postRoute.id, + ]) + + mockWindow.$_TSR = { + router: { + manifest: testManifest, + dehydratedData: {}, + // This synthetic payload models a child-owned boundary, so its + // terminal prefix includes the child match. + matches: [ + { + i: matches[0]!.id, + s: 'success' as const, + ssr: true, + u: Date.now(), + }, + { + i: matches[1]!.id, + s: 'success' as const, + l: 'posts-data', + ssr: true, + u: Date.now(), + }, + { + i: matches[2]!.id, + s: 'notFound' as const, + e: { isNotFound: true }, + ssr: true, + u: Date.now(), + }, + ], + }, + h: vi.fn(), + e: vi.fn(), + c: vi.fn(), + p: vi.fn(), + buffer: [], + initialized: false, + } + + await hydrate(router) + + const stateMatches = router.state.matches + expect(router.state.location.pathname).toBe('/posts/i-do-not-exist') + expect(router.state.isLoading).toBe(false) + expect(stateMatches.map((match) => match.routeId)).toEqual([ + rootRoute.id, + postsRoute.id, + postRoute.id, + ]) + + // The parent keeps the data and status supplied by the payload. + expect(stateMatches[1]!.status).toBe('success') + expect(stateMatches[1]!.loaderData).toBe('posts-data') + + // The child is adopted as the notFound boundary. + expect(stateMatches[2]!.status).toBe('notFound') + expect(isNotFound(stateMatches[2]!.error)).toBe(true) + + // Hydration does not invoke either route loader represented by the + // payload. + expect(postsLoader).not.toHaveBeenCalled() + expect(postLoader).not.toHaveBeenCalled() + expect(safeLoader).not.toHaveBeenCalled() + + // Supplemental public liveness coverage: a later client navigation still + // completes. This does not exercise React's rendered hydration boundary. + await router.navigate({ to: '/safe' }) + expect(router.state.location.pathname).toBe('/safe') + expect(router.state.isLoading).toBe(false) + expect(router.state.matches.map((match) => match.routeId)).toEqual([ + rootRoute.id, + safeRoute.id, + ]) + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: safeRoute.id, + status: 'success', + loaderData: 'safe-data', + }) + expect(safeLoader).toHaveBeenCalledTimes(1) + expect(postsLoader).not.toHaveBeenCalled() + expect(postLoader).not.toHaveBeenCalled() + }) +}) From d3e1cf4e2f1be7dc980afde8575aa40c339f9dc0 Mon Sep 17 00:00:00 2001 From: maurer2 <6861911+maurer2@users.noreply.github.com> Date: Fri, 17 Jul 2026 16:19:58 +0200 Subject: [PATCH 10/51] Add direction-aware view transitions to view transitions example repo (#5791) * Add direction aware view transitions * Restore comment * Linter fix --------- Co-authored-by: Birk Skyum <74932975+birkskyum@users.noreply.github.com> --- .../src/routes/posts.route.tsx | 34 ++++++++++++++++-- .../react/view-transitions/src/styles.css | 36 +++++++++++++++++++ 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/examples/react/view-transitions/src/routes/posts.route.tsx b/examples/react/view-transitions/src/routes/posts.route.tsx index c63dc78ff2..4612ed3d5d 100644 --- a/examples/react/view-transitions/src/routes/posts.route.tsx +++ b/examples/react/view-transitions/src/routes/posts.route.tsx @@ -1,5 +1,10 @@ import * as React from 'react' -import { Link, Outlet, createFileRoute } from '@tanstack/react-router' +import { + Link, + Outlet, + createFileRoute, + useRouter, +} from '@tanstack/react-router' import { fetchPosts } from '../posts' export const Route = createFileRoute('/posts')({ @@ -9,6 +14,7 @@ export const Route = createFileRoute('/posts')({ function PostsLayoutComponent() { const posts = Route.useLoaderData() + const router = useRouter() return (
@@ -24,8 +30,30 @@ function PostsLayoutComponent() { }} className="block py-1 text-blue-600 hover:opacity-75" activeProps={{ className: 'font-bold underline' }} - // see styles.css for 'warp' transition - viewTransition={{ types: ['warp'] }} + // see styles.css for 'warp' and 'warp-backwards' transition + viewTransition={{ + types: ({ fromLocation, toLocation }) => { + const fromRoute = router + .matchRoutes(fromLocation?.pathname ?? '/') + .find((entry) => entry.routeId === '/posts/$postId') + const toRoute = router + .matchRoutes(toLocation?.pathname ?? '/') + .find((entry) => entry.routeId === '/posts/$postId') + + const fromIndex = Number(fromRoute?.params.postId) + const toIndex = Number(toRoute?.params.postId) + + if ( + Number.isNaN(fromIndex) || + Number.isNaN(toIndex) || + fromIndex === toIndex + ) { + return false // no transition + } + + return fromIndex > toIndex ? ['warp-backwards'] : ['warp'] + }, + }} >
{post.title.substring(0, 20)}
diff --git a/examples/react/view-transitions/src/styles.css b/examples/react/view-transitions/src/styles.css index b51e6e1244..502642b383 100644 --- a/examples/react/view-transitions/src/styles.css +++ b/examples/react/view-transitions/src/styles.css @@ -87,6 +87,16 @@ html:active-view-transition-type(warp) { } } +html:active-view-transition-type(warp-backwards) { + &::view-transition-old(post) { + animation: 400ms ease-out both warp-out-backwards; + } + + &::view-transition-new(post) { + animation: 400ms ease-out both warp-in-backwards; + } +} + @keyframes warp-out { from { opacity: 1; @@ -112,3 +122,29 @@ html:active-view-transition-type(warp) { transform: scale(1) rotate(0deg); } } + +@keyframes warp-in-backwards { + from { + opacity: 0; + filter: blur(15px) brightness(1.8); + transform: scale(0.9) rotate(45deg); + } + to { + opacity: 1; + filter: blur(0) brightness(1); + transform: scale(1) rotate(0deg); + } +} + +@keyframes warp-out-backwards { + from { + opacity: 1; + filter: blur(0) brightness(1); + transform: scale(1) rotate(0deg); + } + to { + opacity: 0; + filter: blur(15px) brightness(1.8); + transform: scale(1.1) rotate(-90deg); + } +} From 00d00b3155e6e8bdbdae806ff12de129c4915d86 Mon Sep 17 00:00:00 2001 From: Mohamed Yahye El Joud Date: Fri, 17 Jul 2026 18:47:19 +0000 Subject: [PATCH 11/51] docs: Update environment variable validation to use z.url() (#5570) --- docs/start/framework/react/guide/environment-variables.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/start/framework/react/guide/environment-variables.md b/docs/start/framework/react/guide/environment-variables.md index 8953c0a099..f1f6851602 100644 --- a/docs/start/framework/react/guide/environment-variables.md +++ b/docs/start/framework/react/guide/environment-variables.md @@ -394,14 +394,14 @@ Use Zod for runtime validation of environment variables: import { z } from 'zod' const envSchema = z.object({ - DATABASE_URL: z.string().url(), + DATABASE_URL: z.url(), JWT_SECRET: z.string().min(32), NODE_ENV: z.enum(['development', 'production', 'test']), }) const clientEnvSchema = z.object({ VITE_APP_NAME: z.string(), - VITE_API_URL: z.string().url(), + VITE_API_URL: z.url(), VITE_AUTH0_DOMAIN: z.string(), VITE_AUTH0_CLIENT_ID: z.string(), }) From 6184661e56fa8aaf0c2cb08883f53624a25ef227 Mon Sep 17 00:00:00 2001 From: Sajad Torkamani Date: Fri, 17 Jul 2026 20:40:35 +0100 Subject: [PATCH 12/51] docs: small improvements to wording (#7840) --- docs/router/decisions-on-dx.md | 4 ++-- docs/router/installation/manual.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/router/decisions-on-dx.md b/docs/router/decisions-on-dx.md index 26c366a9da..a06ec4b331 100644 --- a/docs/router/decisions-on-dx.md +++ b/docs/router/decisions-on-dx.md @@ -39,7 +39,7 @@ But to achieve this, we had to make some decisions that deviate from the norms i ## Why is the Router's configuration done this way? -When you want to leverage the TypeScript's inference features to its fullest, you'll quickly realize that _Generics_ are your best friend. And so, TanStack Router uses Generics everywhere to ensure that the types of your routes are inferred as much as possible. +When you want to leverage TypeScript's inference features to its fullest, you'll quickly realize that _Generics_ are your best friend. And so, TanStack Router uses Generics everywhere to ensure that the types of your routes are inferred as much as possible. This means that you have to define your routes in a way that allows TypeScript to infer the types of your routes as much as possible. @@ -219,7 +219,7 @@ TanStack Router's file-based routing is designed to solve all of these issues. I The file-based routing approach is powered by the TanStack Router Bundler Plugin. It performs 3 essential tasks that solve the pain points in route configuration when using code-based routing: 1. **Route configuration boilerplate**: It generates the boilerplate for your route configurations. -2. **Route tree stitching**: It stitches together your route configurations into a single cohesive route-tree. Also in the background, it correctly updates the route configurations to define the `getParentRoute` function match the routes with their parent routes. +2. **Route tree stitching**: It stitches together your route configurations into a single cohesive route-tree. Also in the background, it correctly updates the route configurations to define the `getParentRoute` function and so matches the routes with their parent routes. 3. **Code-splitting**: It automatically code-splits your route content components and updates the route configurations with the correct component. Additionally, at runtime, it ensures that the correct component is loaded when the route is visited. Let's take a look at how the route configuration for the previous example would look like with file-based routing. diff --git a/docs/router/installation/manual.md b/docs/router/installation/manual.md index 76d1f1fe6a..ab89060ef1 100644 --- a/docs/router/installation/manual.md +++ b/docs/router/installation/manual.md @@ -232,7 +232,7 @@ render(() => , rootElement) Regardless of whether you are using the `@tanstack/router-plugin` package and running the `npm run dev`/`npm run build` scripts, or manually running the `tsr watch`/`tsr generate` commands from your package scripts, the route tree file will be generated at `src/routeTree.gen.ts`. -If you are working with this pattern you should change the `id` of the root `
` on your `index.html` file to `
` +If you are working with this pattern, you should change the `id` of the root `
` on your `index.html` file to `
` ## Using Code-Based Route Configuration From e56a67742da9021b009b8db0cdc8bfe99878c25b Mon Sep 17 00:00:00 2001 From: Thijs Koerselman Date: Fri, 17 Jul 2026 23:01:54 +0200 Subject: [PATCH 13/51] fix(router-generator): make buildRouteTree route ordering deterministic (#7751) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Manuel Schiller <6340397+schiller-manuel@users.noreply.github.com> --- .changeset/deterministic-route-tree-sort.md | 5 + packages/router-generator/src/generator.ts | 18 +- packages/router-generator/src/utils.ts | 24 ++ .../routeTree.snapshot.ts | 26 +- .../custom-scaffolding/routeTree.snapshot.ts | 24 +- .../custom-tokens/routeTree.snapshot.ts | 62 ++-- .../dot-escaped/routeTree.snapshot.ts | 42 +-- .../routeTree.snapshot.ts | 26 +- .../routeTree.snapshot.ts | 144 ++++---- .../export-variations/routeTree.snapshot.ts | 26 +- .../file-modification/routeTree.snapshot.ts | 98 ++--- .../flat-route-group/routeTree.snapshot.ts | 26 +- .../generator/flat/routeTree.snapshot.ts | 150 ++++---- .../invalid-param-names/routeTree.snapshot.ts | 36 +- .../nested-layouts/routeTree.snapshot.ts | 344 +++++++++--------- .../routeTree.snapshot.ts | 94 ++--- .../generator/nested/routeTree.snapshot.ts | 170 ++++----- .../numbers-in-path/routeTree.snapshot.ts | 62 ++-- .../routeTree.snapshot.ts | 254 ++++++------- .../prefix-suffix/routeTree.snapshot.ts | 66 ++-- .../regex-tokens-inline/routeTree.snapshot.ts | 26 +- .../regex-tokens-json/routeTree.snapshot.ts | 26 +- .../routeTree.snapshot.ts | 26 +- .../routeTree.snapshot.ts | 26 +- .../route-groups/routeTree.snapshot.ts | 116 +++--- .../routeFileIgnore/routeTree.snapshot.ts | 26 +- .../routeFilePrefix/routeTree.snapshot.ts | 26 +- .../single-level/routeTree.snapshot.ts | 26 +- .../types-disabled/routeTree.snapshot.js | 20 +- .../routeTree.snapshot.ts | 132 +++---- .../routeTree.snapshot.ts | 132 +++---- .../routeTree.snapshot.ts | 132 +++---- .../routeTree.snapshot.ts | 26 +- .../routeTree.snapshot.ts | 36 +- .../routeTree.snapshot.ts | 50 +-- .../routeTree.snapshot.ts | 266 +++++++------- .../routeTree.snapshot.ts | 36 +- .../routeTree.snapshot.ts | 26 +- .../routeTree.snapshot.ts | 36 +- .../routeTree.snapshot.ts | 26 +- .../generator/virtual/routeTree.snapshot.ts | 132 +++---- packages/router-generator/tests/utils.test.ts | 84 +++++ 42 files changed, 1617 insertions(+), 1512 deletions(-) create mode 100644 .changeset/deterministic-route-tree-sort.md diff --git a/.changeset/deterministic-route-tree-sort.md b/.changeset/deterministic-route-tree-sort.md new file mode 100644 index 0000000000..ab95de9593 --- /dev/null +++ b/.changeset/deterministic-route-tree-sort.md @@ -0,0 +1,5 @@ +--- +'@tanstack/router-generator': patch +--- + +Make `buildRouteTree` route ordering deterministic. Its final sort tiebreaker compared whole route-node objects (`(d) => d`), which coerce to `"[object Object]"` and so never order — an inconsistent comparator that left routes tying on segment-count/index-token in an engine- and input-dependent order, producing non-deterministic `routeTree.gen.ts` diffs across machines. The tiebreaker now compares `routePath`, mirroring the pre-sort and giving a stable total order. diff --git a/packages/router-generator/src/generator.ts b/packages/router-generator/src/generator.ts index 4203f2b44f..e8cd170e40 100644 --- a/packages/router-generator/src/generator.ts +++ b/packages/router-generator/src/generator.ts @@ -38,6 +38,7 @@ import { removeLayoutSegmentsAndUnderscoresWithEscape, removeTrailingSlash, replaceBackslash, + sortRouteNodes, trimPathLeft, } from './utils' import { fillTemplate, getTargetTemplate } from './template' @@ -616,19 +617,10 @@ export class Generator { ? this.indexTokenSegmentRegex : createTokenRegex(config.indexToken, { type: 'segment' }) - const sortedRouteNodes = multiSortBy(acc.routeNodes, [ - (d) => (d.routePath?.includes(`/${rootPathId}`) ? -1 : 1), - (d) => - d.routePath === undefined - ? undefined - : countSlashSeparatedParts(d.routePath), - (d) => { - const segments = d.routePath?.split('/').filter(Boolean) ?? [] - const last = segments[segments.length - 1] ?? '' - return indexTokenSegmentRegex.test(last) ? -1 : 1 - }, - (d) => d, - ]) + const sortedRouteNodes = sortRouteNodes( + acc.routeNodes, + indexTokenSegmentRegex, + ) const routeImports: Array = [] const virtualRouteNodes: Array = [] diff --git a/packages/router-generator/src/utils.ts b/packages/router-generator/src/utils.ts index 2354c5ed1c..f0a7986b1c 100644 --- a/packages/router-generator/src/utils.ts +++ b/packages/router-generator/src/utils.ts @@ -123,6 +123,30 @@ export function multiSortBy( return result } +/** + * Sorts route nodes by root, path depth, index status, and finally `routePath`. + * The `routePath` comparison keeps the output consistent when the earlier + * values are the same. + */ +export function sortRouteNodes( + routeNodes: Array, + indexTokenSegmentRegex: RegExp, +): Array { + return multiSortBy(routeNodes, [ + (d) => (d.routePath?.includes(`/${rootPathId}`) ? -1 : 1), + (d) => + d.routePath === undefined + ? undefined + : countSlashSeparatedParts(d.routePath), + (d) => { + const segments = d.routePath?.split('/').filter(Boolean) ?? [] + const last = segments[segments.length - 1] ?? '' + return indexTokenSegmentRegex.test(last) ? -1 : 1 + }, + (d) => d.routePath, + ]) +} + export function cleanPath(path: string) { // remove double slashes return path.replace(/\/{2,}/g, '/') diff --git a/packages/router-generator/tests/generator/add-extensions-custom/routeTree.snapshot.ts b/packages/router-generator/tests/generator/add-extensions-custom/routeTree.snapshot.ts index 7859b1f9ff..2e6aa9d355 100644 --- a/packages/router-generator/tests/generator/add-extensions-custom/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/add-extensions-custom/routeTree.snapshot.ts @@ -9,19 +9,19 @@ // 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.js' -import { Route as PostsRouteImport } from './routes/posts.js' import { Route as IndexRouteImport } from './routes/index.js' +import { Route as PostsRouteImport } from './routes/posts.js' -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/packages/router-generator/tests/generator/custom-scaffolding/routeTree.snapshot.ts b/packages/router-generator/tests/generator/custom-scaffolding/routeTree.snapshot.ts index e80d1e7e73..8f0ed564f4 100644 --- a/packages/router-generator/tests/generator/custom-scaffolding/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/custom-scaffolding/routeTree.snapshot.ts @@ -16,16 +16,16 @@ import { Route as ApiBarRouteImport } from './routes/api/bar' const FooLazyRouteImport = createFileRoute('/foo')() -const FooLazyRoute = FooLazyRouteImport.update({ - id: '/foo', - path: '/foo', - getParentRoute: () => rootRouteImport, -} as any).lazy(() => import('./routes/foo.lazy').then((d) => d.Route)) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const FooLazyRoute = FooLazyRouteImport.update({ + id: '/foo', + path: '/foo', + getParentRoute: () => rootRouteImport, +} as any).lazy(() => import('./routes/foo.lazy').then((d) => d.Route)) const ApiBarRoute = ApiBarRouteImport.update({ id: '/api/bar', path: '/api/bar', @@ -64,13 +64,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/foo': { - id: '/foo' - path: '/foo' - fullPath: '/foo' - preLoaderRoute: typeof FooLazyRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -78,6 +71,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/foo': { + id: '/foo' + path: '/foo' + fullPath: '/foo' + preLoaderRoute: typeof FooLazyRouteImport + parentRoute: typeof rootRouteImport + } '/api/bar': { id: '/api/bar' path: '/api/bar' diff --git a/packages/router-generator/tests/generator/custom-tokens/routeTree.snapshot.ts b/packages/router-generator/tests/generator/custom-tokens/routeTree.snapshot.ts index f49d059f20..fc136dc277 100644 --- a/packages/router-generator/tests/generator/custom-tokens/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/custom-tokens/routeTree.snapshot.ts @@ -9,18 +9,18 @@ // 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 PostsR0ut3RouteImport } from './routes/posts/_r0ut3_' -import { Route as BlogR0ut3RouteImport } from './routes/blog/_r0ut3_' import { Route as R1nd3xRouteImport } from './routes/_1nd3x' -import { Route as Posts1nd3xRouteImport } from './routes/posts/_1nd3x' +import { Route as BlogR0ut3RouteImport } from './routes/blog/_r0ut3_' +import { Route as PostsR0ut3RouteImport } from './routes/posts/_r0ut3_' import { Route as Blog1nd3xRouteImport } from './routes/blog/_1nd3x' import { Route as BlogSlugRouteImport } from './routes/blog/$slug' +import { Route as Posts1nd3xRouteImport } from './routes/posts/_1nd3x' import { Route as PostsPostId1nd3xRouteImport } from './routes/posts/$postId/_1nd3x' import { Route as PostsPostIdDeepRouteImport } from './routes/posts/$postId/deep' -const PostsR0ut3Route = PostsR0ut3RouteImport.update({ - id: '/posts', - path: '/posts', +const R1nd3xRoute = R1nd3xRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const BlogR0ut3Route = BlogR0ut3RouteImport.update({ @@ -28,16 +28,11 @@ const BlogR0ut3Route = BlogR0ut3RouteImport.update({ path: '/blog', getParentRoute: () => rootRouteImport, } as any) -const R1nd3xRoute = R1nd3xRouteImport.update({ - id: '/', - path: '/', +const PostsR0ut3Route = PostsR0ut3RouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const Posts1nd3xRoute = Posts1nd3xRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => PostsR0ut3Route, -} as any) const Blog1nd3xRoute = Blog1nd3xRouteImport.update({ id: '/', path: '/', @@ -48,6 +43,11 @@ const BlogSlugRoute = BlogSlugRouteImport.update({ path: '/$slug', getParentRoute: () => BlogR0ut3Route, } as any) +const Posts1nd3xRoute = Posts1nd3xRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => PostsR0ut3Route, +} as any) const PostsPostId1nd3xRoute = PostsPostId1nd3xRouteImport.update({ id: '/$postId/', path: '/$postId/', @@ -127,11 +127,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsR0ut3RouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof R1nd3xRouteImport parentRoute: typeof rootRouteImport } '/blog': { @@ -141,20 +141,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof BlogR0ut3RouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof R1nd3xRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsR0ut3RouteImport parentRoute: typeof rootRouteImport } - '/posts/': { - id: '/posts/' - path: '/' - fullPath: '/posts/' - preLoaderRoute: typeof Posts1nd3xRouteImport - parentRoute: typeof PostsR0ut3Route - } '/blog/': { id: '/blog/' path: '/' @@ -169,6 +162,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof BlogSlugRouteImport parentRoute: typeof BlogR0ut3Route } + '/posts/': { + id: '/posts/' + path: '/' + fullPath: '/posts/' + preLoaderRoute: typeof Posts1nd3xRouteImport + parentRoute: typeof PostsR0ut3Route + } '/posts/$postId/': { id: '/posts/$postId/' path: '/$postId' diff --git a/packages/router-generator/tests/generator/dot-escaped/routeTree.snapshot.ts b/packages/router-generator/tests/generator/dot-escaped/routeTree.snapshot.ts index 2ccc3a0a38..815dba6590 100644 --- a/packages/router-generator/tests/generator/dot-escaped/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/dot-escaped/routeTree.snapshot.ts @@ -9,25 +9,20 @@ // 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 ScriptDotjsRouteImport } from './routes/script[.]js' import { Route as NestedDotjsRouteImport } from './routes/nested[.]js' -import { Route as NestedDotjsScriptDotjsRouteImport } from './routes/nested[.]js.script[.]js' +import { Route as ScriptDotjsRouteImport } from './routes/script[.]js' import { Route as NestedDotjsDoubleDotextDotjsRouteImport } from './routes/nested[.]js.double[.]ext[.]js' +import { Route as NestedDotjsScriptDotjsRouteImport } from './routes/nested[.]js.script[.]js' -const ScriptDotjsRoute = ScriptDotjsRouteImport.update({ - id: '/script.js', - path: '/script.js', - getParentRoute: () => rootRouteImport, -} as any) const NestedDotjsRoute = NestedDotjsRouteImport.update({ id: '/nested.js', path: '/nested.js', getParentRoute: () => rootRouteImport, } as any) -const NestedDotjsScriptDotjsRoute = NestedDotjsScriptDotjsRouteImport.update({ +const ScriptDotjsRoute = ScriptDotjsRouteImport.update({ id: '/script.js', path: '/script.js', - getParentRoute: () => NestedDotjsRoute, + getParentRoute: () => rootRouteImport, } as any) const NestedDotjsDoubleDotextDotjsRoute = NestedDotjsDoubleDotextDotjsRouteImport.update({ @@ -35,6 +30,11 @@ const NestedDotjsDoubleDotextDotjsRoute = path: '/double.ext.js', getParentRoute: () => NestedDotjsRoute, } as any) +const NestedDotjsScriptDotjsRoute = NestedDotjsScriptDotjsRouteImport.update({ + id: '/script.js', + path: '/script.js', + getParentRoute: () => NestedDotjsRoute, +} as any) export interface FileRoutesByFullPath { '/nested.js': typeof NestedDotjsRouteWithChildren @@ -83,13 +83,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/script.js': { - id: '/script.js' - path: '/script.js' - fullPath: '/script.js' - preLoaderRoute: typeof ScriptDotjsRouteImport - parentRoute: typeof rootRouteImport - } '/nested.js': { id: '/nested.js' path: '/nested.js' @@ -97,12 +90,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof NestedDotjsRouteImport parentRoute: typeof rootRouteImport } - '/nested.js/script.js': { - id: '/nested.js/script.js' + '/script.js': { + id: '/script.js' path: '/script.js' - fullPath: '/nested.js/script.js' - preLoaderRoute: typeof NestedDotjsScriptDotjsRouteImport - parentRoute: typeof NestedDotjsRoute + fullPath: '/script.js' + preLoaderRoute: typeof ScriptDotjsRouteImport + parentRoute: typeof rootRouteImport } '/nested.js/double.ext.js': { id: '/nested.js/double.ext.js' @@ -111,6 +104,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof NestedDotjsDoubleDotextDotjsRouteImport parentRoute: typeof NestedDotjsRoute } + '/nested.js/script.js': { + id: '/nested.js/script.js' + path: '/script.js' + fullPath: '/nested.js/script.js' + preLoaderRoute: typeof NestedDotjsScriptDotjsRouteImport + parentRoute: typeof NestedDotjsRoute + } } } diff --git a/packages/router-generator/tests/generator/escaped-custom-tokens/routeTree.snapshot.ts b/packages/router-generator/tests/generator/escaped-custom-tokens/routeTree.snapshot.ts index e8b7404df9..e7902c671c 100644 --- a/packages/router-generator/tests/generator/escaped-custom-tokens/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/escaped-custom-tokens/routeTree.snapshot.ts @@ -10,8 +10,8 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as Char91_1nd3xChar93RouteImport } from './routes/[_1nd3x]' -import { Route as BlogR0ut3RouteImport } from './routes/blog._r0ut3_' import { Route as R1nd3xRouteImport } from './routes/_1nd3x' +import { Route as BlogR0ut3RouteImport } from './routes/blog._r0ut3_' import { Route as NestedChar91_1nd3xChar93RouteImport } from './routes/nested.[_1nd3x]' import { Route as PostsChar91_r0ut3_Char93RouteImport } from './routes/posts.[_r0ut3_]' @@ -20,16 +20,16 @@ const Char91_1nd3xChar93Route = Char91_1nd3xChar93RouteImport.update({ path: '/_1nd3x', getParentRoute: () => rootRouteImport, } as any) -const BlogR0ut3Route = BlogR0ut3RouteImport.update({ - id: '/blog', - path: '/blog', - getParentRoute: () => rootRouteImport, -} as any) const R1nd3xRoute = R1nd3xRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const BlogR0ut3Route = BlogR0ut3RouteImport.update({ + id: '/blog', + path: '/blog', + getParentRoute: () => rootRouteImport, +} as any) const NestedChar91_1nd3xChar93Route = NestedChar91_1nd3xChar93RouteImport.update({ id: '/nested/_1nd3x', @@ -96,13 +96,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof Char91_1nd3xChar93RouteImport parentRoute: typeof rootRouteImport } - '/blog': { - id: '/blog' - path: '/blog' - fullPath: '/blog' - preLoaderRoute: typeof BlogR0ut3RouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -110,6 +103,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof R1nd3xRouteImport parentRoute: typeof rootRouteImport } + '/blog': { + id: '/blog' + path: '/blog' + fullPath: '/blog' + preLoaderRoute: typeof BlogR0ut3RouteImport + parentRoute: typeof rootRouteImport + } '/nested/_1nd3x': { id: '/nested/_1nd3x' path: '/nested/_1nd3x' diff --git a/packages/router-generator/tests/generator/escaped-special-strings/routeTree.snapshot.ts b/packages/router-generator/tests/generator/escaped-special-strings/routeTree.snapshot.ts index ea71d9c21b..2600d0296d 100644 --- a/packages/router-generator/tests/generator/escaped-special-strings/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/escaped-special-strings/routeTree.snapshot.ts @@ -10,15 +10,15 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as Char91indexChar93RouteImport } from './routes/[index]' -import { Route as Char91routeChar93RouteImport } from './routes/[route]' -import { Route as Char91lazyChar93RouteImport } from './routes/[lazy]' -import { Route as Foo_barRouteImport } from './routes/foo[_]bar' -import { Route as BlogRouteImport } from './routes/blog[_]' -import { Route as Api_v2_usersRouteImport } from './routes/api[_]v2[_]users' -import { Route as Prefix_middle_suffixRouteImport } from './routes/[_]prefix[_]middle[_]suffix' -import { Route as LayoutRouteImport } from './routes/[_]layout' -import { Route as AuthRouteRouteImport } from './routes/[_]auth.route' import { Route as IndexRouteImport } from './routes/index' +import { Route as AuthRouteRouteImport } from './routes/[_]auth.route' +import { Route as LayoutRouteImport } from './routes/[_]layout' +import { Route as Prefix_middle_suffixRouteImport } from './routes/[_]prefix[_]middle[_]suffix' +import { Route as Api_v2_usersRouteImport } from './routes/api[_]v2[_]users' +import { Route as BlogRouteImport } from './routes/blog[_]' +import { Route as Foo_barRouteImport } from './routes/foo[_]bar' +import { Route as Char91lazyChar93RouteImport } from './routes/[lazy]' +import { Route as Char91routeChar93RouteImport } from './routes/[route]' import { Route as NestedChar91indexChar93RouteImport } from './routes/nested.[index]' const Char91indexChar93Route = Char91indexChar93RouteImport.update({ @@ -26,24 +26,24 @@ const Char91indexChar93Route = Char91indexChar93RouteImport.update({ path: '/index', getParentRoute: () => rootRouteImport, } as any) -const Char91routeChar93Route = Char91routeChar93RouteImport.update({ - id: '/route', - path: '/route', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const Char91lazyChar93Route = Char91lazyChar93RouteImport.update({ - id: '/lazy', - path: '/lazy', +const AuthRouteRoute = AuthRouteRouteImport.update({ + id: '/_auth', + path: '/_auth', getParentRoute: () => rootRouteImport, } as any) -const Foo_barRoute = Foo_barRouteImport.update({ - id: '/foo_bar', - path: '/foo_bar', +const LayoutRoute = LayoutRouteImport.update({ + id: '/_layout', + path: '/_layout', getParentRoute: () => rootRouteImport, } as any) -const BlogRoute = BlogRouteImport.update({ - id: '/blog_', - path: '/blog_', +const Prefix_middle_suffixRoute = Prefix_middle_suffixRouteImport.update({ + id: '/_prefix_middle_suffix', + path: '/_prefix_middle_suffix', getParentRoute: () => rootRouteImport, } as any) const Api_v2_usersRoute = Api_v2_usersRouteImport.update({ @@ -51,24 +51,24 @@ const Api_v2_usersRoute = Api_v2_usersRouteImport.update({ path: '/api_v2_users', getParentRoute: () => rootRouteImport, } as any) -const Prefix_middle_suffixRoute = Prefix_middle_suffixRouteImport.update({ - id: '/_prefix_middle_suffix', - path: '/_prefix_middle_suffix', +const BlogRoute = BlogRouteImport.update({ + id: '/blog_', + path: '/blog_', getParentRoute: () => rootRouteImport, } as any) -const LayoutRoute = LayoutRouteImport.update({ - id: '/_layout', - path: '/_layout', +const Foo_barRoute = Foo_barRouteImport.update({ + id: '/foo_bar', + path: '/foo_bar', getParentRoute: () => rootRouteImport, } as any) -const AuthRouteRoute = AuthRouteRouteImport.update({ - id: '/_auth', - path: '/_auth', +const Char91lazyChar93Route = Char91lazyChar93RouteImport.update({ + id: '/lazy', + path: '/lazy', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const Char91routeChar93Route = Char91routeChar93RouteImport.update({ + id: '/route', + path: '/route', getParentRoute: () => rootRouteImport, } as any) const NestedChar91indexChar93Route = NestedChar91indexChar93RouteImport.update({ @@ -182,32 +182,32 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof Char91indexChar93RouteImport parentRoute: typeof rootRouteImport } - '/route': { - id: '/route' - path: '/route' - fullPath: '/route' - preLoaderRoute: typeof Char91routeChar93RouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/lazy': { - id: '/lazy' - path: '/lazy' - fullPath: '/lazy' - preLoaderRoute: typeof Char91lazyChar93RouteImport + '/_auth': { + id: '/_auth' + path: '/_auth' + fullPath: '/_auth' + preLoaderRoute: typeof AuthRouteRouteImport parentRoute: typeof rootRouteImport } - '/foo_bar': { - id: '/foo_bar' - path: '/foo_bar' - fullPath: '/foo_bar' - preLoaderRoute: typeof Foo_barRouteImport + '/_layout': { + id: '/_layout' + path: '/_layout' + fullPath: '/_layout' + preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } - '/blog_': { - id: '/blog_' - path: '/blog_' - fullPath: '/blog_' - preLoaderRoute: typeof BlogRouteImport + '/_prefix_middle_suffix': { + id: '/_prefix_middle_suffix' + path: '/_prefix_middle_suffix' + fullPath: '/_prefix_middle_suffix' + preLoaderRoute: typeof Prefix_middle_suffixRouteImport parentRoute: typeof rootRouteImport } '/api_v2_users': { @@ -217,32 +217,32 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof Api_v2_usersRouteImport parentRoute: typeof rootRouteImport } - '/_prefix_middle_suffix': { - id: '/_prefix_middle_suffix' - path: '/_prefix_middle_suffix' - fullPath: '/_prefix_middle_suffix' - preLoaderRoute: typeof Prefix_middle_suffixRouteImport + '/blog_': { + id: '/blog_' + path: '/blog_' + fullPath: '/blog_' + preLoaderRoute: typeof BlogRouteImport parentRoute: typeof rootRouteImport } - '/_layout': { - id: '/_layout' - path: '/_layout' - fullPath: '/_layout' - preLoaderRoute: typeof LayoutRouteImport + '/foo_bar': { + id: '/foo_bar' + path: '/foo_bar' + fullPath: '/foo_bar' + preLoaderRoute: typeof Foo_barRouteImport parentRoute: typeof rootRouteImport } - '/_auth': { - id: '/_auth' - path: '/_auth' - fullPath: '/_auth' - preLoaderRoute: typeof AuthRouteRouteImport + '/lazy': { + id: '/lazy' + path: '/lazy' + fullPath: '/lazy' + preLoaderRoute: typeof Char91lazyChar93RouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/route': { + id: '/route' + path: '/route' + fullPath: '/route' + preLoaderRoute: typeof Char91routeChar93RouteImport parentRoute: typeof rootRouteImport } '/nested/index': { diff --git a/packages/router-generator/tests/generator/export-variations/routeTree.snapshot.ts b/packages/router-generator/tests/generator/export-variations/routeTree.snapshot.ts index 6362489435..bb251cc516 100644 --- a/packages/router-generator/tests/generator/export-variations/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/export-variations/routeTree.snapshot.ts @@ -9,20 +9,20 @@ // 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 ExportWithAsRouteImport } from './routes/export-with-as' import { Route as ExportSeparateFromDeclarationRouteImport } from './routes/export-separate-from-declaration' +import { Route as ExportWithAsRouteImport } from './routes/export-with-as' -const ExportWithAsRoute = ExportWithAsRouteImport.update({ - id: '/export-with-as', - path: '/export-with-as', - getParentRoute: () => rootRouteImport, -} as any) const ExportSeparateFromDeclarationRoute = ExportSeparateFromDeclarationRouteImport.update({ id: '/export-separate-from-declaration', path: '/export-separate-from-declaration', getParentRoute: () => rootRouteImport, } as any) +const ExportWithAsRoute = ExportWithAsRouteImport.update({ + id: '/export-with-as', + path: '/export-with-as', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/export-separate-from-declaration': typeof ExportSeparateFromDeclarationRoute @@ -52,13 +52,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/export-with-as': { - id: '/export-with-as' - path: '/export-with-as' - fullPath: '/export-with-as' - preLoaderRoute: typeof ExportWithAsRouteImport - parentRoute: typeof rootRouteImport - } '/export-separate-from-declaration': { id: '/export-separate-from-declaration' path: '/export-separate-from-declaration' @@ -66,6 +59,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ExportSeparateFromDeclarationRouteImport parentRoute: typeof rootRouteImport } + '/export-with-as': { + id: '/export-with-as' + path: '/export-with-as' + fullPath: '/export-with-as' + preLoaderRoute: typeof ExportWithAsRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/packages/router-generator/tests/generator/file-modification/routeTree.snapshot.ts b/packages/router-generator/tests/generator/file-modification/routeTree.snapshot.ts index ddcc5ea6d6..7ec8640782 100644 --- a/packages/router-generator/tests/generator/file-modification/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/file-modification/routeTree.snapshot.ts @@ -11,12 +11,12 @@ import { createFileRoute } from '@tanstack/react-router' import { Route as rootRouteImport } from './routes/__root' -import { Route as testTemplateLiteralRouteImport } from './routes/(test)/template-literal' -import { Route as testInitiallyLazyRouteImport } from './routes/(test)/initiallyLazy' -import { Route as testInitiallyEmptyRouteImport } from './routes/(test)/initiallyEmpty' -import { Route as testFooRouteImport } from './routes/(test)/foo' -import { Route as testDuplicateImportRouteImport } from './routes/(test)/duplicate-import' import { Route as testDoubleQuotesRouteImport } from './routes/(test)/double-quotes' +import { Route as testDuplicateImportRouteImport } from './routes/(test)/duplicate-import' +import { Route as testFooRouteImport } from './routes/(test)/foo' +import { Route as testInitiallyEmptyRouteImport } from './routes/(test)/initiallyEmpty' +import { Route as testInitiallyLazyRouteImport } from './routes/(test)/initiallyLazy' +import { Route as testTemplateLiteralRouteImport } from './routes/(test)/template-literal' import { Route as testFooBarRouteImport } from './routes/(test)/foo.bar' const testBarLazyRouteImport = createFileRoute('/(test)/bar')() @@ -28,14 +28,19 @@ const testBarLazyRoute = testBarLazyRouteImport getParentRoute: () => rootRouteImport, } as any) .lazy(() => import('./routes/(test)/bar.lazy').then((d) => d.Route)) -const testTemplateLiteralRoute = testTemplateLiteralRouteImport.update({ - id: '/(test)/template-literal', - path: '/template-literal', +const testDoubleQuotesRoute = testDoubleQuotesRouteImport.update({ + id: '/(test)/double-quotes', + path: '/double-quotes', getParentRoute: () => rootRouteImport, } as any) -const testInitiallyLazyRoute = testInitiallyLazyRouteImport.update({ - id: '/(test)/initiallyLazy', - path: '/initiallyLazy', +const testDuplicateImportRoute = testDuplicateImportRouteImport.update({ + id: '/(test)/duplicate-import', + path: '/duplicate-import', + getParentRoute: () => rootRouteImport, +} as any) +const testFooRoute = testFooRouteImport.update({ + id: '/(test)/foo', + path: '/foo', getParentRoute: () => rootRouteImport, } as any) const testInitiallyEmptyRoute = testInitiallyEmptyRouteImport @@ -47,19 +52,14 @@ const testInitiallyEmptyRoute = testInitiallyEmptyRouteImport .lazy(() => import('./routes/(test)/initiallyEmpty.lazy').then((d) => d.Route), ) -const testFooRoute = testFooRouteImport.update({ - id: '/(test)/foo', - path: '/foo', - getParentRoute: () => rootRouteImport, -} as any) -const testDuplicateImportRoute = testDuplicateImportRouteImport.update({ - id: '/(test)/duplicate-import', - path: '/duplicate-import', +const testInitiallyLazyRoute = testInitiallyLazyRouteImport.update({ + id: '/(test)/initiallyLazy', + path: '/initiallyLazy', getParentRoute: () => rootRouteImport, } as any) -const testDoubleQuotesRoute = testDoubleQuotesRouteImport.update({ - id: '/(test)/double-quotes', - path: '/double-quotes', +const testTemplateLiteralRoute = testTemplateLiteralRouteImport.update({ + id: '/(test)/template-literal', + path: '/template-literal', getParentRoute: () => rootRouteImport, } as any) const testFooBarRoute = testFooBarRouteImport.update({ @@ -151,25 +151,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof testBarLazyRouteImport parentRoute: typeof rootRouteImport } - '/(test)/template-literal': { - id: '/(test)/template-literal' - path: '/template-literal' - fullPath: '/template-literal' - preLoaderRoute: typeof testTemplateLiteralRouteImport - parentRoute: typeof rootRouteImport - } - '/(test)/initiallyLazy': { - id: '/(test)/initiallyLazy' - path: '/initiallyLazy' - fullPath: '/initiallyLazy' - preLoaderRoute: typeof testInitiallyLazyRouteImport + '/(test)/double-quotes': { + id: '/(test)/double-quotes' + path: '/double-quotes' + fullPath: '/double-quotes' + preLoaderRoute: typeof testDoubleQuotesRouteImport parentRoute: typeof rootRouteImport } - '/(test)/initiallyEmpty': { - id: '/(test)/initiallyEmpty' - path: '/initiallyEmpty' - fullPath: '/initiallyEmpty' - preLoaderRoute: typeof testInitiallyEmptyRouteImport + '/(test)/duplicate-import': { + id: '/(test)/duplicate-import' + path: '/duplicate-import' + fullPath: '/duplicate-import' + preLoaderRoute: typeof testDuplicateImportRouteImport parentRoute: typeof rootRouteImport } '/(test)/foo': { @@ -179,18 +172,25 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof testFooRouteImport parentRoute: typeof rootRouteImport } - '/(test)/duplicate-import': { - id: '/(test)/duplicate-import' - path: '/duplicate-import' - fullPath: '/duplicate-import' - preLoaderRoute: typeof testDuplicateImportRouteImport + '/(test)/initiallyEmpty': { + id: '/(test)/initiallyEmpty' + path: '/initiallyEmpty' + fullPath: '/initiallyEmpty' + preLoaderRoute: typeof testInitiallyEmptyRouteImport parentRoute: typeof rootRouteImport } - '/(test)/double-quotes': { - id: '/(test)/double-quotes' - path: '/double-quotes' - fullPath: '/double-quotes' - preLoaderRoute: typeof testDoubleQuotesRouteImport + '/(test)/initiallyLazy': { + id: '/(test)/initiallyLazy' + path: '/initiallyLazy' + fullPath: '/initiallyLazy' + preLoaderRoute: typeof testInitiallyLazyRouteImport + parentRoute: typeof rootRouteImport + } + '/(test)/template-literal': { + id: '/(test)/template-literal' + path: '/template-literal' + fullPath: '/template-literal' + preLoaderRoute: typeof testTemplateLiteralRouteImport parentRoute: typeof rootRouteImport } '/(test)/foo/bar': { diff --git a/packages/router-generator/tests/generator/flat-route-group/routeTree.snapshot.ts b/packages/router-generator/tests/generator/flat-route-group/routeTree.snapshot.ts index 7f7990e787..ca3cf2f2b7 100644 --- a/packages/router-generator/tests/generator/flat-route-group/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/flat-route-group/routeTree.snapshot.ts @@ -10,8 +10,8 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as AppRouteImport } from './routes/app' -import { Route as AppcomprasComprasOrdenesRouteImport } from './routes/app.(compras)/compras_.ordenes' import { Route as AppcomprasCompras_masRouteImport } from './routes/app.(compras)/compras_._mas' +import { Route as AppcomprasComprasOrdenesRouteImport } from './routes/app.(compras)/compras_.ordenes' import { Route as AppcomprasCompras_masDivisionesRouteImport } from './routes/app.(compras)/compras_._mas.divisiones' const AppRoute = AppRouteImport.update({ @@ -19,17 +19,17 @@ const AppRoute = AppRouteImport.update({ path: '/app', getParentRoute: () => rootRouteImport, } as any) +const AppcomprasCompras_masRoute = AppcomprasCompras_masRouteImport.update({ + id: '/(compras)/compras_/_mas', + path: '/compras', + getParentRoute: () => AppRoute, +} as any) const AppcomprasComprasOrdenesRoute = AppcomprasComprasOrdenesRouteImport.update({ id: '/(compras)/compras_/ordenes', path: '/compras/ordenes', getParentRoute: () => AppRoute, } as any) -const AppcomprasCompras_masRoute = AppcomprasCompras_masRouteImport.update({ - id: '/(compras)/compras_/_mas', - path: '/compras', - getParentRoute: () => AppRoute, -} as any) const AppcomprasCompras_masDivisionesRoute = AppcomprasCompras_masDivisionesRouteImport.update({ id: '/divisiones', @@ -90,13 +90,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AppRouteImport parentRoute: typeof rootRouteImport } - '/app/(compras)/compras_/ordenes': { - id: '/app/(compras)/compras_/ordenes' - path: '/compras/ordenes' - fullPath: '/app/compras/ordenes' - preLoaderRoute: typeof AppcomprasComprasOrdenesRouteImport - parentRoute: typeof AppRoute - } '/app/(compras)/compras_/_mas': { id: '/app/(compras)/compras_/_mas' path: '/compras' @@ -104,6 +97,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AppcomprasCompras_masRouteImport parentRoute: typeof AppRoute } + '/app/(compras)/compras_/ordenes': { + id: '/app/(compras)/compras_/ordenes' + path: '/compras/ordenes' + fullPath: '/app/compras/ordenes' + preLoaderRoute: typeof AppcomprasComprasOrdenesRouteImport + parentRoute: typeof AppRoute + } '/app/(compras)/compras_/_mas/divisiones': { id: '/app/(compras)/compras_/_mas/divisiones' path: '/divisiones' diff --git a/packages/router-generator/tests/generator/flat/routeTree.snapshot.ts b/packages/router-generator/tests/generator/flat/routeTree.snapshot.ts index b1bf6587fc..923cfc0530 100644 --- a/packages/router-generator/tests/generator/flat/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/flat/routeTree.snapshot.ts @@ -9,23 +9,23 @@ // 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 PostsRouteRouteImport } from './routes/posts.route' -import { Route as BlogRouteRouteImport } from './routes/blog.route' import { Route as IndexRouteImport } from './routes/index' -import { Route as PostsIndexRouteImport } from './routes/posts.index' +import { Route as BlogRouteRouteImport } from './routes/blog.route' +import { Route as PostsRouteRouteImport } from './routes/posts.route' import { Route as BlogIndexRouteImport } from './routes/blog.index' -import { Route as BlogStatsRouteImport } from './routes/blog_.stats' import { Route as BlogBlogIdRouteRouteImport } from './routes/blog_.$blogId.route' -import { Route as PostsPostIdIndexRouteImport } from './routes/posts.$postId.index' +import { Route as BlogStatsRouteImport } from './routes/blog_.stats' +import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as BlogSlugIndexRouteImport } from './routes/blog.$slug.index' -import { Route as PostsPostIdDeepRouteImport } from './routes/posts.$postId.deep' -import { Route as BlogBlogIdEditRouteImport } from './routes/blog_.$blogId_.edit' import { Route as BlogBlogIdSlugRouteRouteImport } from './routes/blog_.$blogId.$slug.route' +import { Route as BlogBlogIdEditRouteImport } from './routes/blog_.$blogId_.edit' +import { Route as PostsPostIdIndexRouteImport } from './routes/posts.$postId.index' +import { Route as PostsPostIdDeepRouteImport } from './routes/posts.$postId.deep' import { Route as BlogBlogIdSlugBarRouteImport } from './routes/blog_.$blogId.$slug_.bar' -const PostsRouteRoute = PostsRouteRouteImport.update({ - id: '/posts', - path: '/posts', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const BlogRouteRoute = BlogRouteRouteImport.update({ @@ -33,34 +33,29 @@ const BlogRouteRoute = BlogRouteRouteImport.update({ path: '/blog', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const PostsRouteRoute = PostsRouteRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const PostsIndexRoute = PostsIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => PostsRouteRoute, -} as any) const BlogIndexRoute = BlogIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => BlogRouteRoute, } as any) -const BlogStatsRoute = BlogStatsRouteImport.update({ - id: '/blog_/stats', - path: '/blog/stats', - getParentRoute: () => rootRouteImport, -} as any) const BlogBlogIdRouteRoute = BlogBlogIdRouteRouteImport.update({ id: '/blog_/$blogId', path: '/blog/$blogId', getParentRoute: () => rootRouteImport, } as any) -const PostsPostIdIndexRoute = PostsPostIdIndexRouteImport.update({ - id: '/$postId/', - path: '/$postId/', +const BlogStatsRoute = BlogStatsRouteImport.update({ + id: '/blog_/stats', + path: '/blog/stats', + getParentRoute: () => rootRouteImport, +} as any) +const PostsIndexRoute = PostsIndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => PostsRouteRoute, } as any) const BlogSlugIndexRoute = BlogSlugIndexRouteImport.update({ @@ -68,20 +63,25 @@ const BlogSlugIndexRoute = BlogSlugIndexRouteImport.update({ path: '/$slug/', getParentRoute: () => BlogRouteRoute, } as any) -const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ - id: '/$postId/deep', - path: '/$postId/deep', - getParentRoute: () => PostsRouteRoute, +const BlogBlogIdSlugRouteRoute = BlogBlogIdSlugRouteRouteImport.update({ + id: '/$slug', + path: '/$slug', + getParentRoute: () => BlogBlogIdRouteRoute, } as any) const BlogBlogIdEditRoute = BlogBlogIdEditRouteImport.update({ id: '/blog_/$blogId_/edit', path: '/blog/$blogId/edit', getParentRoute: () => rootRouteImport, } as any) -const BlogBlogIdSlugRouteRoute = BlogBlogIdSlugRouteRouteImport.update({ - id: '/$slug', - path: '/$slug', - getParentRoute: () => BlogBlogIdRouteRoute, +const PostsPostIdIndexRoute = PostsPostIdIndexRouteImport.update({ + id: '/$postId/', + path: '/$postId/', + getParentRoute: () => PostsRouteRoute, +} as any) +const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ + id: '/$postId/deep', + path: '/$postId/deep', + getParentRoute: () => PostsRouteRoute, } as any) const BlogBlogIdSlugBarRoute = BlogBlogIdSlugBarRouteImport.update({ id: '/$slug_/bar', @@ -190,11 +190,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/blog': { @@ -204,20 +204,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof BlogRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteRouteImport parentRoute: typeof rootRouteImport } - '/posts/': { - id: '/posts/' - path: '/' - fullPath: '/posts/' - preLoaderRoute: typeof PostsIndexRouteImport - parentRoute: typeof PostsRouteRoute - } '/blog/': { id: '/blog/' path: '/' @@ -225,13 +218,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof BlogIndexRouteImport parentRoute: typeof BlogRouteRoute } - '/blog_/stats': { - id: '/blog_/stats' - path: '/blog/stats' - fullPath: '/blog/stats' - preLoaderRoute: typeof BlogStatsRouteImport - parentRoute: typeof rootRouteImport - } '/blog_/$blogId': { id: '/blog_/$blogId' path: '/blog/$blogId' @@ -239,11 +225,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof BlogBlogIdRouteRouteImport parentRoute: typeof rootRouteImport } - '/posts/$postId/': { - id: '/posts/$postId/' - path: '/$postId' - fullPath: '/posts/$postId/' - preLoaderRoute: typeof PostsPostIdIndexRouteImport + '/blog_/stats': { + id: '/blog_/stats' + path: '/blog/stats' + fullPath: '/blog/stats' + preLoaderRoute: typeof BlogStatsRouteImport + parentRoute: typeof rootRouteImport + } + '/posts/': { + id: '/posts/' + path: '/' + fullPath: '/posts/' + preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRouteRoute } '/blog/$slug/': { @@ -253,12 +246,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof BlogSlugIndexRouteImport parentRoute: typeof BlogRouteRoute } - '/posts/$postId/deep': { - id: '/posts/$postId/deep' - path: '/$postId/deep' - fullPath: '/posts/$postId/deep' - preLoaderRoute: typeof PostsPostIdDeepRouteImport - parentRoute: typeof PostsRouteRoute + '/blog_/$blogId/$slug': { + id: '/blog_/$blogId/$slug' + path: '/$slug' + fullPath: '/blog/$blogId/$slug' + preLoaderRoute: typeof BlogBlogIdSlugRouteRouteImport + parentRoute: typeof BlogBlogIdRouteRoute } '/blog_/$blogId_/edit': { id: '/blog_/$blogId_/edit' @@ -267,12 +260,19 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof BlogBlogIdEditRouteImport parentRoute: typeof rootRouteImport } - '/blog_/$blogId/$slug': { - id: '/blog_/$blogId/$slug' - path: '/$slug' - fullPath: '/blog/$blogId/$slug' - preLoaderRoute: typeof BlogBlogIdSlugRouteRouteImport - parentRoute: typeof BlogBlogIdRouteRoute + '/posts/$postId/': { + id: '/posts/$postId/' + path: '/$postId' + fullPath: '/posts/$postId/' + preLoaderRoute: typeof PostsPostIdIndexRouteImport + parentRoute: typeof PostsRouteRoute + } + '/posts/$postId/deep': { + id: '/posts/$postId/deep' + path: '/$postId/deep' + fullPath: '/posts/$postId/deep' + preLoaderRoute: typeof PostsPostIdDeepRouteImport + parentRoute: typeof PostsRouteRoute } '/blog_/$blogId/$slug_/bar': { id: '/blog_/$blogId/$slug_/bar' diff --git a/packages/router-generator/tests/generator/invalid-param-names/routeTree.snapshot.ts b/packages/router-generator/tests/generator/invalid-param-names/routeTree.snapshot.ts index 4728dd439e..9048976e40 100644 --- a/packages/router-generator/tests/generator/invalid-param-names/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/invalid-param-names/routeTree.snapshot.ts @@ -9,13 +9,13 @@ // 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 ValidParamRouteImport } from './routes/$validParam' -import { Route as UserNameRouteImport } from './routes/$user-name' import { Route as R123RouteImport } from './routes/$123' +import { Route as UserNameRouteImport } from './routes/$user-name' +import { Route as ValidParamRouteImport } from './routes/$validParam' -const ValidParamRoute = ValidParamRouteImport.update({ - id: '/$validParam', - path: '/$validParam', +const R123Route = R123RouteImport.update({ + id: '/$123', + path: '/$123', getParentRoute: () => rootRouteImport, } as any) const UserNameRoute = UserNameRouteImport.update({ @@ -23,9 +23,9 @@ const UserNameRoute = UserNameRouteImport.update({ path: '/$user-name', getParentRoute: () => rootRouteImport, } as any) -const R123Route = R123RouteImport.update({ - id: '/$123', - path: '/$123', +const ValidParamRoute = ValidParamRouteImport.update({ + id: '/$validParam', + path: '/$validParam', getParentRoute: () => rootRouteImport, } as any) @@ -61,11 +61,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/$validParam': { - id: '/$validParam' - path: '/$validParam' - fullPath: '/$validParam' - preLoaderRoute: typeof ValidParamRouteImport + '/$123': { + id: '/$123' + path: '/$123' + fullPath: '/$123' + preLoaderRoute: typeof R123RouteImport parentRoute: typeof rootRouteImport } '/$user-name': { @@ -75,11 +75,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof UserNameRouteImport parentRoute: typeof rootRouteImport } - '/$123': { - id: '/$123' - path: '/$123' - fullPath: '/$123' - preLoaderRoute: typeof R123RouteImport + '/$validParam': { + id: '/$validParam' + path: '/$validParam' + fullPath: '/$validParam' + preLoaderRoute: typeof ValidParamRouteImport parentRoute: typeof rootRouteImport } } diff --git a/packages/router-generator/tests/generator/nested-layouts/routeTree.snapshot.ts b/packages/router-generator/tests/generator/nested-layouts/routeTree.snapshot.ts index d99745d928..7fb6ebb0d0 100644 --- a/packages/router-generator/tests/generator/nested-layouts/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/nested-layouts/routeTree.snapshot.ts @@ -9,141 +9,141 @@ // 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 LayoutA2RouteImport } from './routes/_layout-a2' +import { Route as IndexRouteImport } from './routes/index' import { Route as LayoutA1RouteImport } from './routes/_layout-a1' +import { Route as LayoutA2RouteImport } from './routes/_layout-a2' import { Route as JestedRouteRouteImport } from './routes/jested/route' -import { Route as IndexRouteImport } from './routes/index' -import { Route as NestedLayoutB2RouteImport } from './routes/nested/_layout-b2' -import { Route as NestedLayoutB1RouteImport } from './routes/nested/_layout-b1' -import { Route as JestedLayoutB4RouteImport } from './routes/jested/_layout-b4' -import { Route as JestedLayoutB3RouteImport } from './routes/jested/_layout-b3' -import { Route as FooBarRouteImport } from './routes/foo/bar' -import { Route as LayoutA2BarRouteImport } from './routes/_layout-a2/bar' -import { Route as LayoutA1FooRouteImport } from './routes/_layout-a1/foo' import { Route as folderInFolderRouteImport } from './routes/(folder)/in-folder' +import { Route as LayoutA1FooRouteImport } from './routes/_layout-a1/foo' +import { Route as LayoutA2BarRouteImport } from './routes/_layout-a2/bar' import { Route as FooLayoutB5RouteRouteImport } from './routes/foo/_layout-b5/route' -import { Route as NestedLayoutB1IndexRouteImport } from './routes/nested/_layout-b1/index' -import { Route as JestedLayoutB3IndexRouteImport } from './routes/jested/_layout-b3/index' +import { Route as FooBarRouteImport } from './routes/foo/bar' +import { Route as JestedLayoutB3RouteImport } from './routes/jested/_layout-b3' +import { Route as JestedLayoutB4RouteImport } from './routes/jested/_layout-b4' +import { Route as NestedLayoutB1RouteImport } from './routes/nested/_layout-b1' +import { Route as NestedLayoutB2RouteImport } from './routes/nested/_layout-b2' import { Route as FooLayoutB5IndexRouteImport } from './routes/foo/_layout-b5/index' -import { Route as NestedLayoutB2FooRouteImport } from './routes/nested/_layout-b2/foo' -import { Route as NestedLayoutB1LayoutC1RouteImport } from './routes/nested/_layout-b1/_layout-c1' -import { Route as JestedLayoutB4FooRouteImport } from './routes/jested/_layout-b4/foo' -import { Route as JestedLayoutB3LayoutC2RouteImport } from './routes/jested/_layout-b3/_layout-c2' import { Route as FooLayoutB5IdRouteImport } from './routes/foo/_layout-b5/$id' -import { Route as NestedLayoutB1LayoutC1BarRouteImport } from './routes/nested/_layout-b1/_layout-c1/bar' +import { Route as JestedLayoutB3IndexRouteImport } from './routes/jested/_layout-b3/index' +import { Route as JestedLayoutB3LayoutC2RouteImport } from './routes/jested/_layout-b3/_layout-c2' +import { Route as JestedLayoutB4FooRouteImport } from './routes/jested/_layout-b4/foo' +import { Route as NestedLayoutB1IndexRouteImport } from './routes/nested/_layout-b1/index' +import { Route as NestedLayoutB1LayoutC1RouteImport } from './routes/nested/_layout-b1/_layout-c1' +import { Route as NestedLayoutB2FooRouteImport } from './routes/nested/_layout-b2/foo' import { Route as JestedLayoutB3LayoutC2BarRouteImport } from './routes/jested/_layout-b3/_layout-c2/bar' +import { Route as NestedLayoutB1LayoutC1BarRouteImport } from './routes/nested/_layout-b1/_layout-c1/bar' -const LayoutA2Route = LayoutA2RouteImport.update({ - id: '/_layout-a2', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const LayoutA1Route = LayoutA1RouteImport.update({ id: '/_layout-a1', getParentRoute: () => rootRouteImport, } as any) +const LayoutA2Route = LayoutA2RouteImport.update({ + id: '/_layout-a2', + getParentRoute: () => rootRouteImport, +} as any) const JestedRouteRoute = JestedRouteRouteImport.update({ id: '/jested', path: '/jested', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) -const NestedLayoutB2Route = NestedLayoutB2RouteImport.update({ - id: '/nested/_layout-b2', - path: '/nested', +const folderInFolderRoute = folderInFolderRouteImport.update({ + id: '/(folder)/in-folder', + path: '/in-folder', getParentRoute: () => rootRouteImport, } as any) -const NestedLayoutB1Route = NestedLayoutB1RouteImport.update({ - id: '/nested/_layout-b1', - path: '/nested', - getParentRoute: () => rootRouteImport, +const LayoutA1FooRoute = LayoutA1FooRouteImport.update({ + id: '/foo', + path: '/foo', + getParentRoute: () => LayoutA1Route, } as any) -const JestedLayoutB4Route = JestedLayoutB4RouteImport.update({ - id: '/_layout-b4', - getParentRoute: () => JestedRouteRoute, +const LayoutA2BarRoute = LayoutA2BarRouteImport.update({ + id: '/bar', + path: '/bar', + getParentRoute: () => LayoutA2Route, } as any) -const JestedLayoutB3Route = JestedLayoutB3RouteImport.update({ - id: '/_layout-b3', - getParentRoute: () => JestedRouteRoute, +const FooLayoutB5RouteRoute = FooLayoutB5RouteRouteImport.update({ + id: '/foo/_layout-b5', + path: '/foo', + getParentRoute: () => rootRouteImport, } as any) const FooBarRoute = FooBarRouteImport.update({ id: '/foo/bar', path: '/foo/bar', getParentRoute: () => rootRouteImport, } as any) -const LayoutA2BarRoute = LayoutA2BarRouteImport.update({ - id: '/bar', - path: '/bar', - getParentRoute: () => LayoutA2Route, +const JestedLayoutB3Route = JestedLayoutB3RouteImport.update({ + id: '/_layout-b3', + getParentRoute: () => JestedRouteRoute, } as any) -const LayoutA1FooRoute = LayoutA1FooRouteImport.update({ - id: '/foo', - path: '/foo', - getParentRoute: () => LayoutA1Route, +const JestedLayoutB4Route = JestedLayoutB4RouteImport.update({ + id: '/_layout-b4', + getParentRoute: () => JestedRouteRoute, } as any) -const folderInFolderRoute = folderInFolderRouteImport.update({ - id: '/(folder)/in-folder', - path: '/in-folder', +const NestedLayoutB1Route = NestedLayoutB1RouteImport.update({ + id: '/nested/_layout-b1', + path: '/nested', getParentRoute: () => rootRouteImport, } as any) -const FooLayoutB5RouteRoute = FooLayoutB5RouteRouteImport.update({ - id: '/foo/_layout-b5', - path: '/foo', +const NestedLayoutB2Route = NestedLayoutB2RouteImport.update({ + id: '/nested/_layout-b2', + path: '/nested', getParentRoute: () => rootRouteImport, } as any) -const NestedLayoutB1IndexRoute = NestedLayoutB1IndexRouteImport.update({ +const FooLayoutB5IndexRoute = FooLayoutB5IndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => NestedLayoutB1Route, + getParentRoute: () => FooLayoutB5RouteRoute, +} as any) +const FooLayoutB5IdRoute = FooLayoutB5IdRouteImport.update({ + id: '/$id', + path: '/$id', + getParentRoute: () => FooLayoutB5RouteRoute, } as any) const JestedLayoutB3IndexRoute = JestedLayoutB3IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => JestedLayoutB3Route, } as any) -const FooLayoutB5IndexRoute = FooLayoutB5IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => FooLayoutB5RouteRoute, +const JestedLayoutB3LayoutC2Route = JestedLayoutB3LayoutC2RouteImport.update({ + id: '/_layout-c2', + getParentRoute: () => JestedLayoutB3Route, } as any) -const NestedLayoutB2FooRoute = NestedLayoutB2FooRouteImport.update({ +const JestedLayoutB4FooRoute = JestedLayoutB4FooRouteImport.update({ id: '/foo', path: '/foo', - getParentRoute: () => NestedLayoutB2Route, + getParentRoute: () => JestedLayoutB4Route, +} as any) +const NestedLayoutB1IndexRoute = NestedLayoutB1IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => NestedLayoutB1Route, } as any) const NestedLayoutB1LayoutC1Route = NestedLayoutB1LayoutC1RouteImport.update({ id: '/_layout-c1', getParentRoute: () => NestedLayoutB1Route, } as any) -const JestedLayoutB4FooRoute = JestedLayoutB4FooRouteImport.update({ +const NestedLayoutB2FooRoute = NestedLayoutB2FooRouteImport.update({ id: '/foo', path: '/foo', - getParentRoute: () => JestedLayoutB4Route, -} as any) -const JestedLayoutB3LayoutC2Route = JestedLayoutB3LayoutC2RouteImport.update({ - id: '/_layout-c2', - getParentRoute: () => JestedLayoutB3Route, -} as any) -const FooLayoutB5IdRoute = FooLayoutB5IdRouteImport.update({ - id: '/$id', - path: '/$id', - getParentRoute: () => FooLayoutB5RouteRoute, + getParentRoute: () => NestedLayoutB2Route, } as any) -const NestedLayoutB1LayoutC1BarRoute = - NestedLayoutB1LayoutC1BarRouteImport.update({ - id: '/bar', - path: '/bar', - getParentRoute: () => NestedLayoutB1LayoutC1Route, - } as any) const JestedLayoutB3LayoutC2BarRoute = JestedLayoutB3LayoutC2BarRouteImport.update({ id: '/bar', path: '/bar', getParentRoute: () => JestedLayoutB3LayoutC2Route, } as any) +const NestedLayoutB1LayoutC1BarRoute = + NestedLayoutB1LayoutC1BarRouteImport.update({ + id: '/bar', + path: '/bar', + getParentRoute: () => NestedLayoutB1LayoutC1Route, + } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -275,11 +275,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/_layout-a2': { - id: '/_layout-a2' - path: '' + '/': { + id: '/' + path: '/' fullPath: '/' - preLoaderRoute: typeof LayoutA2RouteImport + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/_layout-a1': { @@ -289,6 +289,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LayoutA1RouteImport parentRoute: typeof rootRouteImport } + '/_layout-a2': { + id: '/_layout-a2' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutA2RouteImport + parentRoute: typeof rootRouteImport + } '/jested': { id: '/jested' path: '/jested' @@ -296,40 +303,33 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof JestedRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport - } - '/nested/_layout-b2': { - id: '/nested/_layout-b2' - path: '/nested' - fullPath: '/nested' - preLoaderRoute: typeof NestedLayoutB2RouteImport + '/(folder)/in-folder': { + id: '/(folder)/in-folder' + path: '/in-folder' + fullPath: '/in-folder' + preLoaderRoute: typeof folderInFolderRouteImport parentRoute: typeof rootRouteImport } - '/nested/_layout-b1': { - id: '/nested/_layout-b1' - path: '/nested' - fullPath: '/nested' - preLoaderRoute: typeof NestedLayoutB1RouteImport - parentRoute: typeof rootRouteImport + '/_layout-a1/foo': { + id: '/_layout-a1/foo' + path: '/foo' + fullPath: '/foo' + preLoaderRoute: typeof LayoutA1FooRouteImport + parentRoute: typeof LayoutA1Route } - '/jested/_layout-b4': { - id: '/jested/_layout-b4' - path: '' - fullPath: '/jested' - preLoaderRoute: typeof JestedLayoutB4RouteImport - parentRoute: typeof JestedRouteRoute + '/_layout-a2/bar': { + id: '/_layout-a2/bar' + path: '/bar' + fullPath: '/bar' + preLoaderRoute: typeof LayoutA2BarRouteImport + parentRoute: typeof LayoutA2Route } - '/jested/_layout-b3': { - id: '/jested/_layout-b3' - path: '' - fullPath: '/jested' - preLoaderRoute: typeof JestedLayoutB3RouteImport - parentRoute: typeof JestedRouteRoute + '/foo/_layout-b5': { + id: '/foo/_layout-b5' + path: '/foo' + fullPath: '/foo' + preLoaderRoute: typeof FooLayoutB5RouteRouteImport + parentRoute: typeof rootRouteImport } '/foo/bar': { id: '/foo/bar' @@ -338,40 +338,47 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof FooBarRouteImport parentRoute: typeof rootRouteImport } - '/_layout-a2/bar': { - id: '/_layout-a2/bar' - path: '/bar' - fullPath: '/bar' - preLoaderRoute: typeof LayoutA2BarRouteImport - parentRoute: typeof LayoutA2Route + '/jested/_layout-b3': { + id: '/jested/_layout-b3' + path: '' + fullPath: '/jested' + preLoaderRoute: typeof JestedLayoutB3RouteImport + parentRoute: typeof JestedRouteRoute } - '/_layout-a1/foo': { - id: '/_layout-a1/foo' - path: '/foo' - fullPath: '/foo' - preLoaderRoute: typeof LayoutA1FooRouteImport - parentRoute: typeof LayoutA1Route + '/jested/_layout-b4': { + id: '/jested/_layout-b4' + path: '' + fullPath: '/jested' + preLoaderRoute: typeof JestedLayoutB4RouteImport + parentRoute: typeof JestedRouteRoute } - '/(folder)/in-folder': { - id: '/(folder)/in-folder' - path: '/in-folder' - fullPath: '/in-folder' - preLoaderRoute: typeof folderInFolderRouteImport + '/nested/_layout-b1': { + id: '/nested/_layout-b1' + path: '/nested' + fullPath: '/nested' + preLoaderRoute: typeof NestedLayoutB1RouteImport parentRoute: typeof rootRouteImport } - '/foo/_layout-b5': { - id: '/foo/_layout-b5' - path: '/foo' - fullPath: '/foo' - preLoaderRoute: typeof FooLayoutB5RouteRouteImport + '/nested/_layout-b2': { + id: '/nested/_layout-b2' + path: '/nested' + fullPath: '/nested' + preLoaderRoute: typeof NestedLayoutB2RouteImport parentRoute: typeof rootRouteImport } - '/nested/_layout-b1/': { - id: '/nested/_layout-b1/' + '/foo/_layout-b5/': { + id: '/foo/_layout-b5/' path: '/' - fullPath: '/nested/' - preLoaderRoute: typeof NestedLayoutB1IndexRouteImport - parentRoute: typeof NestedLayoutB1Route + fullPath: '/foo/' + preLoaderRoute: typeof FooLayoutB5IndexRouteImport + parentRoute: typeof FooLayoutB5RouteRoute + } + '/foo/_layout-b5/$id': { + id: '/foo/_layout-b5/$id' + path: '/$id' + fullPath: '/foo/$id' + preLoaderRoute: typeof FooLayoutB5IdRouteImport + parentRoute: typeof FooLayoutB5RouteRoute } '/jested/_layout-b3/': { id: '/jested/_layout-b3/' @@ -380,19 +387,26 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof JestedLayoutB3IndexRouteImport parentRoute: typeof JestedLayoutB3Route } - '/foo/_layout-b5/': { - id: '/foo/_layout-b5/' - path: '/' - fullPath: '/foo/' - preLoaderRoute: typeof FooLayoutB5IndexRouteImport - parentRoute: typeof FooLayoutB5RouteRoute + '/jested/_layout-b3/_layout-c2': { + id: '/jested/_layout-b3/_layout-c2' + path: '' + fullPath: '/jested' + preLoaderRoute: typeof JestedLayoutB3LayoutC2RouteImport + parentRoute: typeof JestedLayoutB3Route } - '/nested/_layout-b2/foo': { - id: '/nested/_layout-b2/foo' + '/jested/_layout-b4/foo': { + id: '/jested/_layout-b4/foo' path: '/foo' - fullPath: '/nested/foo' - preLoaderRoute: typeof NestedLayoutB2FooRouteImport - parentRoute: typeof NestedLayoutB2Route + fullPath: '/jested/foo' + preLoaderRoute: typeof JestedLayoutB4FooRouteImport + parentRoute: typeof JestedLayoutB4Route + } + '/nested/_layout-b1/': { + id: '/nested/_layout-b1/' + path: '/' + fullPath: '/nested/' + preLoaderRoute: typeof NestedLayoutB1IndexRouteImport + parentRoute: typeof NestedLayoutB1Route } '/nested/_layout-b1/_layout-c1': { id: '/nested/_layout-b1/_layout-c1' @@ -401,26 +415,19 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof NestedLayoutB1LayoutC1RouteImport parentRoute: typeof NestedLayoutB1Route } - '/jested/_layout-b4/foo': { - id: '/jested/_layout-b4/foo' + '/nested/_layout-b2/foo': { + id: '/nested/_layout-b2/foo' path: '/foo' - fullPath: '/jested/foo' - preLoaderRoute: typeof JestedLayoutB4FooRouteImport - parentRoute: typeof JestedLayoutB4Route - } - '/jested/_layout-b3/_layout-c2': { - id: '/jested/_layout-b3/_layout-c2' - path: '' - fullPath: '/jested' - preLoaderRoute: typeof JestedLayoutB3LayoutC2RouteImport - parentRoute: typeof JestedLayoutB3Route + fullPath: '/nested/foo' + preLoaderRoute: typeof NestedLayoutB2FooRouteImport + parentRoute: typeof NestedLayoutB2Route } - '/foo/_layout-b5/$id': { - id: '/foo/_layout-b5/$id' - path: '/$id' - fullPath: '/foo/$id' - preLoaderRoute: typeof FooLayoutB5IdRouteImport - parentRoute: typeof FooLayoutB5RouteRoute + '/jested/_layout-b3/_layout-c2/bar': { + id: '/jested/_layout-b3/_layout-c2/bar' + path: '/bar' + fullPath: '/jested/bar' + preLoaderRoute: typeof JestedLayoutB3LayoutC2BarRouteImport + parentRoute: typeof JestedLayoutB3LayoutC2Route } '/nested/_layout-b1/_layout-c1/bar': { id: '/nested/_layout-b1/_layout-c1/bar' @@ -429,13 +436,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof NestedLayoutB1LayoutC1BarRouteImport parentRoute: typeof NestedLayoutB1LayoutC1Route } - '/jested/_layout-b3/_layout-c2/bar': { - id: '/jested/_layout-b3/_layout-c2/bar' - path: '/bar' - fullPath: '/jested/bar' - preLoaderRoute: typeof JestedLayoutB3LayoutC2BarRouteImport - parentRoute: typeof JestedLayoutB3LayoutC2Route - } } } diff --git a/packages/router-generator/tests/generator/nested-route-groups-with-layouts-before-physical/routeTree.snapshot.ts b/packages/router-generator/tests/generator/nested-route-groups-with-layouts-before-physical/routeTree.snapshot.ts index 6c5c9950b2..8f2741b206 100644 --- a/packages/router-generator/tests/generator/nested-route-groups-with-layouts-before-physical/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/nested-route-groups-with-layouts-before-physical/routeTree.snapshot.ts @@ -9,45 +9,45 @@ // 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 groupCLayoutCRouteImport } from './routes/(group-c)/_layout-c' -import { Route as groupBLayoutBRouteImport } from './routes/(group-b)/_layout-b' import { Route as groupALayoutARouteImport } from './routes/(group-a)/_layout-a' -import { Route as groupCLayoutCIndexRouteImport } from './routes/(group-c)/_layout-c/index' -import { Route as groupBLayoutBDashboardRouteImport } from './routes/(group-b)/_layout-b/dashboard' -import { Route as groupALayoutASignupRouteImport } from './routes/(group-a)/_layout-a/signup' +import { Route as groupBLayoutBRouteImport } from './routes/(group-b)/_layout-b' +import { Route as groupCLayoutCRouteImport } from './routes/(group-c)/_layout-c' import { Route as groupALayoutALoginRouteImport } from './routes/(group-a)/_layout-a/login' +import { Route as groupALayoutASignupRouteImport } from './routes/(group-a)/_layout-a/signup' +import { Route as groupBLayoutBDashboardRouteImport } from './routes/(group-b)/_layout-b/dashboard' +import { Route as groupCLayoutCIndexRouteImport } from './routes/(group-c)/_layout-c/index' -const groupCLayoutCRoute = groupCLayoutCRouteImport.update({ - id: '/(group-c)/_layout-c', +const groupALayoutARoute = groupALayoutARouteImport.update({ + id: '/(group-a)/_layout-a', getParentRoute: () => rootRouteImport, } as any) const groupBLayoutBRoute = groupBLayoutBRouteImport.update({ id: '/(group-b)/_layout-b', getParentRoute: () => rootRouteImport, } as any) -const groupALayoutARoute = groupALayoutARouteImport.update({ - id: '/(group-a)/_layout-a', +const groupCLayoutCRoute = groupCLayoutCRouteImport.update({ + id: '/(group-c)/_layout-c', getParentRoute: () => rootRouteImport, } as any) -const groupCLayoutCIndexRoute = groupCLayoutCIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => groupCLayoutCRoute, -} as any) -const groupBLayoutBDashboardRoute = groupBLayoutBDashboardRouteImport.update({ - id: '/dashboard', - path: '/dashboard', - getParentRoute: () => groupBLayoutBRoute, +const groupALayoutALoginRoute = groupALayoutALoginRouteImport.update({ + id: '/login', + path: '/login', + getParentRoute: () => groupALayoutARoute, } as any) const groupALayoutASignupRoute = groupALayoutASignupRouteImport.update({ id: '/signup', path: '/signup', getParentRoute: () => groupALayoutARoute, } as any) -const groupALayoutALoginRoute = groupALayoutALoginRouteImport.update({ - id: '/login', - path: '/login', - getParentRoute: () => groupALayoutARoute, +const groupBLayoutBDashboardRoute = groupBLayoutBDashboardRouteImport.update({ + id: '/dashboard', + path: '/dashboard', + getParentRoute: () => groupBLayoutBRoute, +} as any) +const groupCLayoutCIndexRoute = groupCLayoutCIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => groupCLayoutCRoute, } as any) export interface FileRoutesByFullPath { @@ -96,11 +96,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/(group-c)/_layout-c': { - id: '/(group-c)/_layout-c' + '/(group-a)/_layout-a': { + id: '/(group-a)/_layout-a' path: '' fullPath: '' - preLoaderRoute: typeof groupCLayoutCRouteImport + preLoaderRoute: typeof groupALayoutARouteImport parentRoute: typeof rootRouteImport } '/(group-b)/_layout-b': { @@ -110,26 +110,19 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof groupBLayoutBRouteImport parentRoute: typeof rootRouteImport } - '/(group-a)/_layout-a': { - id: '/(group-a)/_layout-a' + '/(group-c)/_layout-c': { + id: '/(group-c)/_layout-c' path: '' fullPath: '' - preLoaderRoute: typeof groupALayoutARouteImport + preLoaderRoute: typeof groupCLayoutCRouteImport parentRoute: typeof rootRouteImport } - '/(group-c)/_layout-c/': { - id: '/(group-c)/_layout-c/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof groupCLayoutCIndexRouteImport - parentRoute: typeof groupCLayoutCRoute - } - '/(group-b)/_layout-b/dashboard': { - id: '/(group-b)/_layout-b/dashboard' - path: '/dashboard' - fullPath: '/dashboard' - preLoaderRoute: typeof groupBLayoutBDashboardRouteImport - parentRoute: typeof groupBLayoutBRoute + '/(group-a)/_layout-a/login': { + id: '/(group-a)/_layout-a/login' + path: '/login' + fullPath: '/login' + preLoaderRoute: typeof groupALayoutALoginRouteImport + parentRoute: typeof groupALayoutARoute } '/(group-a)/_layout-a/signup': { id: '/(group-a)/_layout-a/signup' @@ -138,12 +131,19 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof groupALayoutASignupRouteImport parentRoute: typeof groupALayoutARoute } - '/(group-a)/_layout-a/login': { - id: '/(group-a)/_layout-a/login' - path: '/login' - fullPath: '/login' - preLoaderRoute: typeof groupALayoutALoginRouteImport - parentRoute: typeof groupALayoutARoute + '/(group-b)/_layout-b/dashboard': { + id: '/(group-b)/_layout-b/dashboard' + path: '/dashboard' + fullPath: '/dashboard' + preLoaderRoute: typeof groupBLayoutBDashboardRouteImport + parentRoute: typeof groupBLayoutBRoute + } + '/(group-c)/_layout-c/': { + id: '/(group-c)/_layout-c/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof groupCLayoutCIndexRouteImport + parentRoute: typeof groupCLayoutCRoute } } } diff --git a/packages/router-generator/tests/generator/nested/routeTree.snapshot.ts b/packages/router-generator/tests/generator/nested/routeTree.snapshot.ts index 9a87dd6953..685b1b29ca 100644 --- a/packages/router-generator/tests/generator/nested/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/nested/routeTree.snapshot.ts @@ -9,29 +9,29 @@ // 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 IndexRouteImport } from './routes/index' import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' -import { Route as PostsRouteRouteImport } from './routes/posts/route' import { Route as BlogRouteRouteImport } from './routes/blog/route' -import { Route as IndexRouteImport } from './routes/index' -import { Route as PostsIndexRouteImport } from './routes/posts/index' +import { Route as PostsRouteRouteImport } from './routes/posts/route' +import { Route as PathlessLayoutSettingsRouteImport } from './routes/_pathlessLayout/settings' import { Route as BlogIndexRouteImport } from './routes/blog/index' -import { Route as BlogStatsRouteImport } from './routes/blog_/stats' import { Route as BlogSlugRouteImport } from './routes/blog/$slug' -import { Route as PathlessLayoutSettingsRouteImport } from './routes/_pathlessLayout/settings' import { Route as BlogBlogIdRouteRouteImport } from './routes/blog_/$blogId/route' +import { Route as BlogStatsRouteImport } from './routes/blog_/stats' +import { Route as PostsIndexRouteImport } from './routes/posts/index' +import { Route as BlogBlogIdSlugRouteRouteImport } from './routes/blog_/$blogId/$slug/route' +import { Route as BlogBlogIdEditRouteImport } from './routes/blog_/$blogId_/edit' import { Route as PostsPostIdIndexRouteImport } from './routes/posts/$postId/index' import { Route as PostsPostIdDeepRouteImport } from './routes/posts/$postId/deep' -import { Route as BlogBlogIdEditRouteImport } from './routes/blog_/$blogId_/edit' -import { Route as BlogBlogIdSlugRouteRouteImport } from './routes/blog_/$blogId/$slug/route' import { Route as BlogBlogIdSlugBarRouteImport } from './routes/blog_/$blogId/$slug_/bar' -const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ - id: '/_pathlessLayout', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const PostsRouteRoute = PostsRouteRouteImport.update({ - id: '/posts', - path: '/posts', +const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ + id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, } as any) const BlogRouteRoute = BlogRouteRouteImport.update({ @@ -39,41 +39,51 @@ const BlogRouteRoute = BlogRouteRouteImport.update({ path: '/blog', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const PostsRouteRoute = PostsRouteRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const PostsIndexRoute = PostsIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => PostsRouteRoute, +const PathlessLayoutSettingsRoute = PathlessLayoutSettingsRouteImport.update({ + id: '/settings', + path: '/settings', + getParentRoute: () => PathlessLayoutRoute, } as any) const BlogIndexRoute = BlogIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => BlogRouteRoute, } as any) -const BlogStatsRoute = BlogStatsRouteImport.update({ - id: '/blog_/stats', - path: '/blog/stats', - getParentRoute: () => rootRouteImport, -} as any) const BlogSlugRoute = BlogSlugRouteImport.update({ id: '/$slug', path: '/$slug', getParentRoute: () => BlogRouteRoute, } as any) -const PathlessLayoutSettingsRoute = PathlessLayoutSettingsRouteImport.update({ - id: '/settings', - path: '/settings', - getParentRoute: () => PathlessLayoutRoute, -} as any) const BlogBlogIdRouteRoute = BlogBlogIdRouteRouteImport.update({ id: '/blog_/$blogId', path: '/blog/$blogId', getParentRoute: () => rootRouteImport, } as any) +const BlogStatsRoute = BlogStatsRouteImport.update({ + id: '/blog_/stats', + path: '/blog/stats', + getParentRoute: () => rootRouteImport, +} as any) +const PostsIndexRoute = PostsIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => PostsRouteRoute, +} as any) +const BlogBlogIdSlugRouteRoute = BlogBlogIdSlugRouteRouteImport.update({ + id: '/$slug', + path: '/$slug', + getParentRoute: () => BlogBlogIdRouteRoute, +} as any) +const BlogBlogIdEditRoute = BlogBlogIdEditRouteImport.update({ + id: '/blog_/$blogId_/edit', + path: '/blog/$blogId/edit', + getParentRoute: () => rootRouteImport, +} as any) const PostsPostIdIndexRoute = PostsPostIdIndexRouteImport.update({ id: '/$postId/', path: '/$postId/', @@ -84,16 +94,6 @@ const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ path: '/$postId/deep', getParentRoute: () => PostsRouteRoute, } as any) -const BlogBlogIdEditRoute = BlogBlogIdEditRouteImport.update({ - id: '/blog_/$blogId_/edit', - path: '/blog/$blogId/edit', - getParentRoute: () => rootRouteImport, -} as any) -const BlogBlogIdSlugRouteRoute = BlogBlogIdSlugRouteRouteImport.update({ - id: '/$slug', - path: '/$slug', - getParentRoute: () => BlogBlogIdRouteRoute, -} as any) const BlogBlogIdSlugBarRoute = BlogBlogIdSlugBarRouteImport.update({ id: '/$slug_/bar', path: '/$slug/bar', @@ -210,6 +210,13 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } '/_pathlessLayout': { id: '/_pathlessLayout' path: '' @@ -217,13 +224,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PathlessLayoutRouteImport parentRoute: typeof rootRouteImport } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteRouteImport - parentRoute: typeof rootRouteImport - } '/blog': { id: '/blog' path: '/blog' @@ -231,19 +231,19 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof BlogRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteRouteImport parentRoute: typeof rootRouteImport } - '/posts/': { - id: '/posts/' - path: '/' - fullPath: '/posts/' - preLoaderRoute: typeof PostsIndexRouteImport - parentRoute: typeof PostsRouteRoute + '/_pathlessLayout/settings': { + id: '/_pathlessLayout/settings' + path: '/settings' + fullPath: '/settings' + preLoaderRoute: typeof PathlessLayoutSettingsRouteImport + parentRoute: typeof PathlessLayoutRoute } '/blog/': { id: '/blog/' @@ -252,13 +252,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof BlogIndexRouteImport parentRoute: typeof BlogRouteRoute } - '/blog_/stats': { - id: '/blog_/stats' - path: '/blog/stats' - fullPath: '/blog/stats' - preLoaderRoute: typeof BlogStatsRouteImport - parentRoute: typeof rootRouteImport - } '/blog/$slug': { id: '/blog/$slug' path: '/$slug' @@ -266,13 +259,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof BlogSlugRouteImport parentRoute: typeof BlogRouteRoute } - '/_pathlessLayout/settings': { - id: '/_pathlessLayout/settings' - path: '/settings' - fullPath: '/settings' - preLoaderRoute: typeof PathlessLayoutSettingsRouteImport - parentRoute: typeof PathlessLayoutRoute - } '/blog_/$blogId': { id: '/blog_/$blogId' path: '/blog/$blogId' @@ -280,6 +266,34 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof BlogBlogIdRouteRouteImport parentRoute: typeof rootRouteImport } + '/blog_/stats': { + id: '/blog_/stats' + path: '/blog/stats' + fullPath: '/blog/stats' + preLoaderRoute: typeof BlogStatsRouteImport + parentRoute: typeof rootRouteImport + } + '/posts/': { + id: '/posts/' + path: '/' + fullPath: '/posts/' + preLoaderRoute: typeof PostsIndexRouteImport + parentRoute: typeof PostsRouteRoute + } + '/blog_/$blogId/$slug': { + id: '/blog_/$blogId/$slug' + path: '/$slug' + fullPath: '/blog/$blogId/$slug' + preLoaderRoute: typeof BlogBlogIdSlugRouteRouteImport + parentRoute: typeof BlogBlogIdRouteRoute + } + '/blog_/$blogId_/edit': { + id: '/blog_/$blogId_/edit' + path: '/blog/$blogId/edit' + fullPath: '/blog/$blogId/edit' + preLoaderRoute: typeof BlogBlogIdEditRouteImport + parentRoute: typeof rootRouteImport + } '/posts/$postId/': { id: '/posts/$postId/' path: '/$postId' @@ -294,20 +308,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsPostIdDeepRouteImport parentRoute: typeof PostsRouteRoute } - '/blog_/$blogId_/edit': { - id: '/blog_/$blogId_/edit' - path: '/blog/$blogId/edit' - fullPath: '/blog/$blogId/edit' - preLoaderRoute: typeof BlogBlogIdEditRouteImport - parentRoute: typeof rootRouteImport - } - '/blog_/$blogId/$slug': { - id: '/blog_/$blogId/$slug' - path: '/$slug' - fullPath: '/blog/$blogId/$slug' - preLoaderRoute: typeof BlogBlogIdSlugRouteRouteImport - parentRoute: typeof BlogBlogIdRouteRoute - } '/blog_/$blogId/$slug_/bar': { id: '/blog_/$blogId/$slug_/bar' path: '/$slug/bar' diff --git a/packages/router-generator/tests/generator/numbers-in-path/routeTree.snapshot.ts b/packages/router-generator/tests/generator/numbers-in-path/routeTree.snapshot.ts index 76af935401..826e1b8b4f 100644 --- a/packages/router-generator/tests/generator/numbers-in-path/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/numbers-in-path/routeTree.snapshot.ts @@ -9,15 +9,15 @@ // 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 AboutRouteImport } from './routes/about' -import { Route as R03RouteImport } from './routes/03' import { Route as IndexRouteImport } from './routes/index' -import { Route as R02IndexRouteImport } from './routes/02.index' +import { Route as R03RouteImport } from './routes/03' +import { Route as AboutRouteImport } from './routes/about' import { Route as R01ExampleIndexRouteImport } from './routes/01-example/index' +import { Route as R02IndexRouteImport } from './routes/02.index' -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const R03Route = R03RouteImport.update({ @@ -25,14 +25,9 @@ const R03Route = R03RouteImport.update({ path: '/03', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) -const R02IndexRoute = R02IndexRouteImport.update({ - id: '/02/', - path: '/02/', +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', getParentRoute: () => rootRouteImport, } as any) const R01ExampleIndexRoute = R01ExampleIndexRouteImport.update({ @@ -40,6 +35,11 @@ const R01ExampleIndexRoute = R01ExampleIndexRouteImport.update({ path: '/01-example/', getParentRoute: () => rootRouteImport, } as any) +const R02IndexRoute = R02IndexRouteImport.update({ + id: '/02/', + path: '/02/', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -81,11 +81,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/03': { @@ -95,18 +95,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof R03RouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport - } - '/02/': { - id: '/02/' - path: '/02' - fullPath: '/02/' - preLoaderRoute: typeof R02IndexRouteImport + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport parentRoute: typeof rootRouteImport } '/01-example/': { @@ -116,6 +109,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof R01ExampleIndexRouteImport parentRoute: typeof rootRouteImport } + '/02/': { + id: '/02/' + path: '/02' + fullPath: '/02/' + preLoaderRoute: typeof R02IndexRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routeTree.snapshot.ts b/packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routeTree.snapshot.ts index 53a35bf149..7c7789143a 100644 --- a/packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routeTree.snapshot.ts @@ -10,23 +10,23 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as Char91__rootIndexChar93IndexRouteImport } from './routes/[__root-index]/index' -import { Route as LayoutRouteImport } from './routes/_layout' -import { Route as FooRouteImport } from './routes/[_]foo' -import { Route as Char91__literalChar93RouteImport } from './routes/[__literal]' import { Route as _layoutRouteImport } from './routes/__layout' -import { Route as RootIndexIndexRouteImport } from './routes/[_]root-index/index' -import { Route as LayoutNestedRouteImport } from './routes/_layout/_nested' -import { Route as LayoutBarRouteImport } from './routes/_layout/[_]bar' -import { Route as Layout_nested2RouteImport } from './routes/_layout/__nested2' -import { Route as LayoutChar91__doubleChar93RouteImport } from './routes/_layout/[__double]' +import { Route as Char91__literalChar93RouteImport } from './routes/[__literal]' +import { Route as FooRouteImport } from './routes/[_]foo' +import { Route as LayoutRouteImport } from './routes/_layout' import { Route as _layoutQuxRouteImport } from './routes/__layout/[_]qux' -import { Route as LayoutNestedTrailingIndexRouteImport } from './routes/_layout/nested-trailing[_]/index' -import { Route as LayoutNestedIndexIndexRouteImport } from './routes/_layout/[_]nested-index/index' +import { Route as LayoutChar91__doubleChar93RouteImport } from './routes/_layout/[__double]' +import { Route as Layout_nested2RouteImport } from './routes/_layout/__nested2' +import { Route as LayoutBarRouteImport } from './routes/_layout/[_]bar' +import { Route as LayoutNestedRouteImport } from './routes/_layout/_nested' +import { Route as RootIndexIndexRouteImport } from './routes/[_]root-index/index' import { Route as LayoutChar91__doubleIndexChar93IndexRouteImport } from './routes/_layout/[__double-index]/index' -import { Route as LayoutNestedBazRouteImport } from './routes/_layout/_nested/[_]baz' -import { Route as LayoutNestedIndexIdRouteImport } from './routes/_layout/[_]nested-index/$id' -import { Route as Layout_nested2DeepRouteImport } from './routes/_layout/__nested2/[_]deep' import { Route as LayoutChar91__doubleIndexChar93IdRouteImport } from './routes/_layout/[__double-index]/$id' +import { Route as Layout_nested2DeepRouteImport } from './routes/_layout/__nested2/[_]deep' +import { Route as LayoutNestedIndexIndexRouteImport } from './routes/_layout/[_]nested-index/index' +import { Route as LayoutNestedIndexIdRouteImport } from './routes/_layout/[_]nested-index/$id' +import { Route as LayoutNestedBazRouteImport } from './routes/_layout/_nested/[_]baz' +import { Route as LayoutNestedTrailingIndexRouteImport } from './routes/_layout/nested-trailing[_]/index' import { Route as LayoutNestedDeepIndexIndexRouteImport } from './routes/_layout/_nested/[_]deep-index/index' const Char91__rootIndexChar93IndexRoute = @@ -35,13 +35,8 @@ const Char91__rootIndexChar93IndexRoute = path: '/__root-index/', getParentRoute: () => rootRouteImport, } as any) -const LayoutRoute = LayoutRouteImport.update({ - id: '/_layout', - getParentRoute: () => rootRouteImport, -} as any) -const FooRoute = FooRouteImport.update({ - id: '/_foo', - path: '/_foo', +const _layoutRoute = _layoutRouteImport.update({ + id: '/__layout', getParentRoute: () => rootRouteImport, } as any) const Char91__literalChar93Route = Char91__literalChar93RouteImport.update({ @@ -49,17 +44,28 @@ const Char91__literalChar93Route = Char91__literalChar93RouteImport.update({ path: '/__literal', getParentRoute: () => rootRouteImport, } as any) -const _layoutRoute = _layoutRouteImport.update({ - id: '/__layout', +const FooRoute = FooRouteImport.update({ + id: '/_foo', + path: '/_foo', getParentRoute: () => rootRouteImport, } as any) -const RootIndexIndexRoute = RootIndexIndexRouteImport.update({ - id: '/_root-index/', - path: '/_root-index/', +const LayoutRoute = LayoutRouteImport.update({ + id: '/_layout', getParentRoute: () => rootRouteImport, } as any) -const LayoutNestedRoute = LayoutNestedRouteImport.update({ - id: '/_nested', +const _layoutQuxRoute = _layoutQuxRouteImport.update({ + id: '/_qux', + path: '/_qux', + getParentRoute: () => _layoutRoute, +} as any) +const LayoutChar91__doubleChar93Route = + LayoutChar91__doubleChar93RouteImport.update({ + id: '/__double', + path: '/__double', + getParentRoute: () => LayoutRoute, + } as any) +const Layout_nested2Route = Layout_nested2RouteImport.update({ + id: '/__nested2', getParentRoute: () => LayoutRoute, } as any) const LayoutBarRoute = LayoutBarRouteImport.update({ @@ -67,57 +73,51 @@ const LayoutBarRoute = LayoutBarRouteImport.update({ path: '/_bar', getParentRoute: () => LayoutRoute, } as any) -const Layout_nested2Route = Layout_nested2RouteImport.update({ - id: '/__nested2', +const LayoutNestedRoute = LayoutNestedRouteImport.update({ + id: '/_nested', getParentRoute: () => LayoutRoute, } as any) -const LayoutChar91__doubleChar93Route = - LayoutChar91__doubleChar93RouteImport.update({ - id: '/__double', - path: '/__double', +const RootIndexIndexRoute = RootIndexIndexRouteImport.update({ + id: '/_root-index/', + path: '/_root-index/', + getParentRoute: () => rootRouteImport, +} as any) +const LayoutChar91__doubleIndexChar93IndexRoute = + LayoutChar91__doubleIndexChar93IndexRouteImport.update({ + id: '/__double-index/', + path: '/__double-index/', getParentRoute: () => LayoutRoute, } as any) -const _layoutQuxRoute = _layoutQuxRouteImport.update({ - id: '/_qux', - path: '/_qux', - getParentRoute: () => _layoutRoute, -} as any) -const LayoutNestedTrailingIndexRoute = - LayoutNestedTrailingIndexRouteImport.update({ - id: '/nested-trailing_/', - path: '/nested-trailing_/', +const LayoutChar91__doubleIndexChar93IdRoute = + LayoutChar91__doubleIndexChar93IdRouteImport.update({ + id: '/__double-index/$id', + path: '/__double-index/$id', getParentRoute: () => LayoutRoute, } as any) +const Layout_nested2DeepRoute = Layout_nested2DeepRouteImport.update({ + id: '/_deep', + path: '/_deep', + getParentRoute: () => Layout_nested2Route, +} as any) const LayoutNestedIndexIndexRoute = LayoutNestedIndexIndexRouteImport.update({ id: '/_nested-index/', path: '/_nested-index/', getParentRoute: () => LayoutRoute, } as any) -const LayoutChar91__doubleIndexChar93IndexRoute = - LayoutChar91__doubleIndexChar93IndexRouteImport.update({ - id: '/__double-index/', - path: '/__double-index/', - getParentRoute: () => LayoutRoute, - } as any) -const LayoutNestedBazRoute = LayoutNestedBazRouteImport.update({ - id: '/_baz', - path: '/_baz', - getParentRoute: () => LayoutNestedRoute, -} as any) const LayoutNestedIndexIdRoute = LayoutNestedIndexIdRouteImport.update({ id: '/_nested-index/$id', path: '/_nested-index/$id', getParentRoute: () => LayoutRoute, } as any) -const Layout_nested2DeepRoute = Layout_nested2DeepRouteImport.update({ - id: '/_deep', - path: '/_deep', - getParentRoute: () => Layout_nested2Route, +const LayoutNestedBazRoute = LayoutNestedBazRouteImport.update({ + id: '/_baz', + path: '/_baz', + getParentRoute: () => LayoutNestedRoute, } as any) -const LayoutChar91__doubleIndexChar93IdRoute = - LayoutChar91__doubleIndexChar93IdRouteImport.update({ - id: '/__double-index/$id', - path: '/__double-index/$id', +const LayoutNestedTrailingIndexRoute = + LayoutNestedTrailingIndexRouteImport.update({ + id: '/nested-trailing_/', + path: '/nested-trailing_/', getParentRoute: () => LayoutRoute, } as any) const LayoutNestedDeepIndexIndexRoute = @@ -263,18 +263,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof Char91__rootIndexChar93IndexRouteImport parentRoute: typeof rootRouteImport } - '/_layout': { - id: '/_layout' + '/__layout': { + id: '/__layout' path: '' fullPath: '/' - preLoaderRoute: typeof LayoutRouteImport - parentRoute: typeof rootRouteImport - } - '/_foo': { - id: '/_foo' - path: '/_foo' - fullPath: '/_foo' - preLoaderRoute: typeof FooRouteImport + preLoaderRoute: typeof _layoutRouteImport parentRoute: typeof rootRouteImport } '/__literal': { @@ -284,25 +277,39 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof Char91__literalChar93RouteImport parentRoute: typeof rootRouteImport } - '/__layout': { - id: '/__layout' + '/_foo': { + id: '/_foo' + path: '/_foo' + fullPath: '/_foo' + preLoaderRoute: typeof FooRouteImport + parentRoute: typeof rootRouteImport + } + '/_layout': { + id: '/_layout' path: '' fullPath: '/' - preLoaderRoute: typeof _layoutRouteImport + preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } - '/_root-index/': { - id: '/_root-index/' - path: '/_root-index' - fullPath: '/_root-index/' - preLoaderRoute: typeof RootIndexIndexRouteImport - parentRoute: typeof rootRouteImport + '/__layout/_qux': { + id: '/__layout/_qux' + path: '/_qux' + fullPath: '/_qux' + preLoaderRoute: typeof _layoutQuxRouteImport + parentRoute: typeof _layoutRoute } - '/_layout/_nested': { - id: '/_layout/_nested' + '/_layout/__double': { + id: '/_layout/__double' + path: '/__double' + fullPath: '/__double' + preLoaderRoute: typeof LayoutChar91__doubleChar93RouteImport + parentRoute: typeof LayoutRoute + } + '/_layout/__nested2': { + id: '/_layout/__nested2' path: '' fullPath: '/' - preLoaderRoute: typeof LayoutNestedRouteImport + preLoaderRoute: typeof Layout_nested2RouteImport parentRoute: typeof LayoutRoute } '/_layout/_bar': { @@ -312,34 +319,41 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LayoutBarRouteImport parentRoute: typeof LayoutRoute } - '/_layout/__nested2': { - id: '/_layout/__nested2' + '/_layout/_nested': { + id: '/_layout/_nested' path: '' fullPath: '/' - preLoaderRoute: typeof Layout_nested2RouteImport + preLoaderRoute: typeof LayoutNestedRouteImport parentRoute: typeof LayoutRoute } - '/_layout/__double': { - id: '/_layout/__double' - path: '/__double' - fullPath: '/__double' - preLoaderRoute: typeof LayoutChar91__doubleChar93RouteImport - parentRoute: typeof LayoutRoute + '/_root-index/': { + id: '/_root-index/' + path: '/_root-index' + fullPath: '/_root-index/' + preLoaderRoute: typeof RootIndexIndexRouteImport + parentRoute: typeof rootRouteImport } - '/__layout/_qux': { - id: '/__layout/_qux' - path: '/_qux' - fullPath: '/_qux' - preLoaderRoute: typeof _layoutQuxRouteImport - parentRoute: typeof _layoutRoute + '/_layout/__double-index/': { + id: '/_layout/__double-index/' + path: '/__double-index' + fullPath: '/__double-index/' + preLoaderRoute: typeof LayoutChar91__doubleIndexChar93IndexRouteImport + parentRoute: typeof LayoutRoute } - '/_layout/nested-trailing_/': { - id: '/_layout/nested-trailing_/' - path: '/nested-trailing_' - fullPath: '/nested-trailing_/' - preLoaderRoute: typeof LayoutNestedTrailingIndexRouteImport + '/_layout/__double-index/$id': { + id: '/_layout/__double-index/$id' + path: '/__double-index/$id' + fullPath: '/__double-index/$id' + preLoaderRoute: typeof LayoutChar91__doubleIndexChar93IdRouteImport parentRoute: typeof LayoutRoute } + '/_layout/__nested2/_deep': { + id: '/_layout/__nested2/_deep' + path: '/_deep' + fullPath: '/_deep' + preLoaderRoute: typeof Layout_nested2DeepRouteImport + parentRoute: typeof Layout_nested2Route + } '/_layout/_nested-index/': { id: '/_layout/_nested-index/' path: '/_nested-index' @@ -347,11 +361,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LayoutNestedIndexIndexRouteImport parentRoute: typeof LayoutRoute } - '/_layout/__double-index/': { - id: '/_layout/__double-index/' - path: '/__double-index' - fullPath: '/__double-index/' - preLoaderRoute: typeof LayoutChar91__doubleIndexChar93IndexRouteImport + '/_layout/_nested-index/$id': { + id: '/_layout/_nested-index/$id' + path: '/_nested-index/$id' + fullPath: '/_nested-index/$id' + preLoaderRoute: typeof LayoutNestedIndexIdRouteImport parentRoute: typeof LayoutRoute } '/_layout/_nested/_baz': { @@ -361,25 +375,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LayoutNestedBazRouteImport parentRoute: typeof LayoutNestedRoute } - '/_layout/_nested-index/$id': { - id: '/_layout/_nested-index/$id' - path: '/_nested-index/$id' - fullPath: '/_nested-index/$id' - preLoaderRoute: typeof LayoutNestedIndexIdRouteImport - parentRoute: typeof LayoutRoute - } - '/_layout/__nested2/_deep': { - id: '/_layout/__nested2/_deep' - path: '/_deep' - fullPath: '/_deep' - preLoaderRoute: typeof Layout_nested2DeepRouteImport - parentRoute: typeof Layout_nested2Route - } - '/_layout/__double-index/$id': { - id: '/_layout/__double-index/$id' - path: '/__double-index/$id' - fullPath: '/__double-index/$id' - preLoaderRoute: typeof LayoutChar91__doubleIndexChar93IdRouteImport + '/_layout/nested-trailing_/': { + id: '/_layout/nested-trailing_/' + path: '/nested-trailing_' + fullPath: '/nested-trailing_/' + preLoaderRoute: typeof LayoutNestedTrailingIndexRouteImport parentRoute: typeof LayoutRoute } '/_layout/_nested/_deep-index/': { diff --git a/packages/router-generator/tests/generator/prefix-suffix/routeTree.snapshot.ts b/packages/router-generator/tests/generator/prefix-suffix/routeTree.snapshot.ts index 5f7f854d9f..2ef38f491d 100644 --- a/packages/router-generator/tests/generator/prefix-suffix/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/prefix-suffix/routeTree.snapshot.ts @@ -9,15 +9,20 @@ // 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 WildcardChar123Char125suffixRouteImport } from './routes/wildcard/{$}suffix' -import { Route as WildcardChar123Char125DotsuffixRouteImport } from './routes/wildcard/{$}[.]suffix' -import { Route as WildcardPrefixChar123Char125RouteImport } from './routes/wildcard/prefix{$}' import { Route as WildcardSplatRouteImport } from './routes/wildcard/$' +import { Route as WildcardPrefixChar123Char125RouteImport } from './routes/wildcard/prefix{$}' +import { Route as WildcardChar123Char125DotsuffixRouteImport } from './routes/wildcard/{$}[.]suffix' +import { Route as WildcardChar123Char125suffixRouteImport } from './routes/wildcard/{$}suffix' -const WildcardChar123Char125suffixRoute = - WildcardChar123Char125suffixRouteImport.update({ - id: '/wildcard/{$}suffix', - path: '/wildcard/{$}suffix', +const WildcardSplatRoute = WildcardSplatRouteImport.update({ + id: '/wildcard/$', + path: '/wildcard/$', + getParentRoute: () => rootRouteImport, +} as any) +const WildcardPrefixChar123Char125Route = + WildcardPrefixChar123Char125RouteImport.update({ + id: '/wildcard/prefix{$}', + path: '/wildcard/prefix{$}', getParentRoute: () => rootRouteImport, } as any) const WildcardChar123Char125DotsuffixRoute = @@ -26,17 +31,12 @@ const WildcardChar123Char125DotsuffixRoute = path: '/wildcard/{$}.suffix', getParentRoute: () => rootRouteImport, } as any) -const WildcardPrefixChar123Char125Route = - WildcardPrefixChar123Char125RouteImport.update({ - id: '/wildcard/prefix{$}', - path: '/wildcard/prefix{$}', +const WildcardChar123Char125suffixRoute = + WildcardChar123Char125suffixRouteImport.update({ + id: '/wildcard/{$}suffix', + path: '/wildcard/{$}suffix', getParentRoute: () => rootRouteImport, } as any) -const WildcardSplatRoute = WildcardSplatRouteImport.update({ - id: '/wildcard/$', - path: '/wildcard/$', - getParentRoute: () => rootRouteImport, -} as any) export interface FileRoutesByFullPath { '/wildcard/$': typeof WildcardSplatRoute @@ -87,18 +87,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/wildcard/{$}suffix': { - id: '/wildcard/{$}suffix' - path: '/wildcard/{$}suffix' - fullPath: '/wildcard/{$}suffix' - preLoaderRoute: typeof WildcardChar123Char125suffixRouteImport - parentRoute: typeof rootRouteImport - } - '/wildcard/{$}.suffix': { - id: '/wildcard/{$}.suffix' - path: '/wildcard/{$}.suffix' - fullPath: '/wildcard/{$}.suffix' - preLoaderRoute: typeof WildcardChar123Char125DotsuffixRouteImport + '/wildcard/$': { + id: '/wildcard/$' + path: '/wildcard/$' + fullPath: '/wildcard/$' + preLoaderRoute: typeof WildcardSplatRouteImport parentRoute: typeof rootRouteImport } '/wildcard/prefix{$}': { @@ -108,11 +101,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof WildcardPrefixChar123Char125RouteImport parentRoute: typeof rootRouteImport } - '/wildcard/$': { - id: '/wildcard/$' - path: '/wildcard/$' - fullPath: '/wildcard/$' - preLoaderRoute: typeof WildcardSplatRouteImport + '/wildcard/{$}.suffix': { + id: '/wildcard/{$}.suffix' + path: '/wildcard/{$}.suffix' + fullPath: '/wildcard/{$}.suffix' + preLoaderRoute: typeof WildcardChar123Char125DotsuffixRouteImport + parentRoute: typeof rootRouteImport + } + '/wildcard/{$}suffix': { + id: '/wildcard/{$}suffix' + path: '/wildcard/{$}suffix' + fullPath: '/wildcard/{$}suffix' + preLoaderRoute: typeof WildcardChar123Char125suffixRouteImport parentRoute: typeof rootRouteImport } } diff --git a/packages/router-generator/tests/generator/regex-tokens-inline/routeTree.snapshot.ts b/packages/router-generator/tests/generator/regex-tokens-inline/routeTree.snapshot.ts index 3909bbe9f4..669cebaf59 100644 --- a/packages/router-generator/tests/generator/regex-tokens-inline/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/regex-tokens-inline/routeTree.snapshot.ts @@ -9,21 +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 DashboardMainLayoutRouteImport } from './routes/dashboard.main-layout' import { Route as IndexPageRouteImport } from './routes/index-page' +import { Route as DashboardMainLayoutRouteImport } from './routes/dashboard.main-layout' import { Route as DashboardHomePageRouteImport } from './routes/dashboard.home-page' import { Route as DashboardSettingsRouteImport } from './routes/dashboard.settings' -const DashboardMainLayoutRoute = DashboardMainLayoutRouteImport.update({ - id: '/dashboard', - path: '/dashboard', - getParentRoute: () => rootRouteImport, -} as any) const IndexPageRoute = IndexPageRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const DashboardMainLayoutRoute = DashboardMainLayoutRouteImport.update({ + id: '/dashboard', + path: '/dashboard', + getParentRoute: () => rootRouteImport, +} as any) const DashboardHomePageRoute = DashboardHomePageRouteImport.update({ id: '/', path: '/', @@ -68,13 +68,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/dashboard': { - id: '/dashboard' - path: '/dashboard' - fullPath: '/dashboard' - preLoaderRoute: typeof DashboardMainLayoutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -82,6 +75,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexPageRouteImport parentRoute: typeof rootRouteImport } + '/dashboard': { + id: '/dashboard' + path: '/dashboard' + fullPath: '/dashboard' + preLoaderRoute: typeof DashboardMainLayoutRouteImport + parentRoute: typeof rootRouteImport + } '/dashboard/': { id: '/dashboard/' path: '/' diff --git a/packages/router-generator/tests/generator/regex-tokens-json/routeTree.snapshot.ts b/packages/router-generator/tests/generator/regex-tokens-json/routeTree.snapshot.ts index 3909bbe9f4..669cebaf59 100644 --- a/packages/router-generator/tests/generator/regex-tokens-json/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/regex-tokens-json/routeTree.snapshot.ts @@ -9,21 +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 DashboardMainLayoutRouteImport } from './routes/dashboard.main-layout' import { Route as IndexPageRouteImport } from './routes/index-page' +import { Route as DashboardMainLayoutRouteImport } from './routes/dashboard.main-layout' import { Route as DashboardHomePageRouteImport } from './routes/dashboard.home-page' import { Route as DashboardSettingsRouteImport } from './routes/dashboard.settings' -const DashboardMainLayoutRoute = DashboardMainLayoutRouteImport.update({ - id: '/dashboard', - path: '/dashboard', - getParentRoute: () => rootRouteImport, -} as any) const IndexPageRoute = IndexPageRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const DashboardMainLayoutRoute = DashboardMainLayoutRouteImport.update({ + id: '/dashboard', + path: '/dashboard', + getParentRoute: () => rootRouteImport, +} as any) const DashboardHomePageRoute = DashboardHomePageRouteImport.update({ id: '/', path: '/', @@ -68,13 +68,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/dashboard': { - id: '/dashboard' - path: '/dashboard' - fullPath: '/dashboard' - preLoaderRoute: typeof DashboardMainLayoutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -82,6 +75,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexPageRouteImport parentRoute: typeof rootRouteImport } + '/dashboard': { + id: '/dashboard' + path: '/dashboard' + fullPath: '/dashboard' + preLoaderRoute: typeof DashboardMainLayoutRouteImport + parentRoute: typeof rootRouteImport + } '/dashboard/': { id: '/dashboard/' path: '/' diff --git a/packages/router-generator/tests/generator/regex-tokens-plus-json/routeTree.snapshot.ts b/packages/router-generator/tests/generator/regex-tokens-plus-json/routeTree.snapshot.ts index b9b7117716..74c38f90ad 100644 --- a/packages/router-generator/tests/generator/regex-tokens-plus-json/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/regex-tokens-plus-json/routeTree.snapshot.ts @@ -9,21 +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 DashboardChar43layoutRouteImport } from './routes/dashboard/+layout' import { Route as Char43pageRouteImport } from './routes/+page' +import { Route as DashboardChar43layoutRouteImport } from './routes/dashboard/+layout' import { Route as DashboardChar43pageRouteImport } from './routes/dashboard/+page' import { Route as DashboardSettingsRouteImport } from './routes/dashboard/settings' -const DashboardChar43layoutRoute = DashboardChar43layoutRouteImport.update({ - id: '/dashboard', - path: '/dashboard', - getParentRoute: () => rootRouteImport, -} as any) const Char43pageRoute = Char43pageRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const DashboardChar43layoutRoute = DashboardChar43layoutRouteImport.update({ + id: '/dashboard', + path: '/dashboard', + getParentRoute: () => rootRouteImport, +} as any) const DashboardChar43pageRoute = DashboardChar43pageRouteImport.update({ id: '/', path: '/', @@ -68,13 +68,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/dashboard': { - id: '/dashboard' - path: '/dashboard' - fullPath: '/dashboard' - preLoaderRoute: typeof DashboardChar43layoutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -82,6 +75,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof Char43pageRouteImport parentRoute: typeof rootRouteImport } + '/dashboard': { + id: '/dashboard' + path: '/dashboard' + fullPath: '/dashboard' + preLoaderRoute: typeof DashboardChar43layoutRouteImport + parentRoute: typeof rootRouteImport + } '/dashboard/': { id: '/dashboard/' path: '/' diff --git a/packages/router-generator/tests/generator/root-export-specifier/routeTree.snapshot.ts b/packages/router-generator/tests/generator/root-export-specifier/routeTree.snapshot.ts index 59499d9fbf..12bc916ae4 100644 --- a/packages/router-generator/tests/generator/root-export-specifier/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/root-export-specifier/routeTree.snapshot.ts @@ -9,19 +9,19 @@ // 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 AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' +import { Route as AboutRouteImport } from './routes/about' -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/packages/router-generator/tests/generator/route-groups/routeTree.snapshot.ts b/packages/router-generator/tests/generator/route-groups/routeTree.snapshot.ts index f2e84bbbb7..e76c675d2c 100644 --- a/packages/router-generator/tests/generator/route-groups/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/route-groups/routeTree.snapshot.ts @@ -12,13 +12,13 @@ import { createFileRoute } from '@tanstack/react-router' import { Route as rootRouteImport } from './routes/__root' import { Route as barBarRouteImport } from './routes/(bar)/_bar' -import { Route as fooAsdfLayoutRouteImport } from './routes/(foo)/asdf/_layout' import { Route as barBarHelloRouteImport } from './routes/(bar)/_bar.hello' -import { Route as fooAsdfLayoutFooRouteImport } from './routes/(foo)/asdf/_layout.foo' -import { Route as fooAsdfbarIdRouteImport } from './routes/(foo)/asdf/(bar)/$id' +import { Route as fooAsdfLayoutRouteImport } from './routes/(foo)/asdf/_layout' import { Route as fooAsdfanotherGroupLayoutRouteImport } from './routes/(foo)/asdf/(another-group)/_layout' -import { Route as fooAsdfbarLayoutAboutRouteImport } from './routes/(foo)/asdf/(bar)/_layout.about' +import { Route as fooAsdfbarIdRouteImport } from './routes/(foo)/asdf/(bar)/$id' +import { Route as fooAsdfLayoutFooRouteImport } from './routes/(foo)/asdf/_layout.foo' import { Route as fooAsdfanotherGroupLayoutBazRouteImport } from './routes/(foo)/asdf/(another-group)/_layout.baz' +import { Route as fooAsdfbarLayoutAboutRouteImport } from './routes/(foo)/asdf/(bar)/_layout.about' const fooAsdfbarLayoutXyzLazyRouteImport = createFileRoute( '/(foo)/asdf/(bar)/_layout/xyz', @@ -28,24 +28,14 @@ const barBarRoute = barBarRouteImport.update({ id: '/(bar)/_bar', getParentRoute: () => rootRouteImport, } as any) -const fooAsdfLayoutRoute = fooAsdfLayoutRouteImport.update({ - id: '/(foo)/asdf/_layout', - path: '/asdf', - getParentRoute: () => rootRouteImport, -} as any) const barBarHelloRoute = barBarHelloRouteImport.update({ id: '/hello', path: '/hello', getParentRoute: () => barBarRoute, } as any) -const fooAsdfLayoutFooRoute = fooAsdfLayoutFooRouteImport.update({ - id: '/foo', - path: '/foo', - getParentRoute: () => fooAsdfLayoutRoute, -} as any) -const fooAsdfbarIdRoute = fooAsdfbarIdRouteImport.update({ - id: '/(foo)/asdf/(bar)/$id', - path: '/asdf/$id', +const fooAsdfLayoutRoute = fooAsdfLayoutRouteImport.update({ + id: '/(foo)/asdf/_layout', + path: '/asdf', getParentRoute: () => rootRouteImport, } as any) const fooAsdfanotherGroupLayoutRoute = @@ -54,6 +44,27 @@ const fooAsdfanotherGroupLayoutRoute = path: '/asdf', getParentRoute: () => rootRouteImport, } as any) +const fooAsdfbarIdRoute = fooAsdfbarIdRouteImport.update({ + id: '/(foo)/asdf/(bar)/$id', + path: '/asdf/$id', + getParentRoute: () => rootRouteImport, +} as any) +const fooAsdfLayoutFooRoute = fooAsdfLayoutFooRouteImport.update({ + id: '/foo', + path: '/foo', + getParentRoute: () => fooAsdfLayoutRoute, +} as any) +const fooAsdfanotherGroupLayoutBazRoute = + fooAsdfanotherGroupLayoutBazRouteImport.update({ + id: '/baz', + path: '/baz', + getParentRoute: () => fooAsdfanotherGroupLayoutRoute, + } as any) +const fooAsdfbarLayoutAboutRoute = fooAsdfbarLayoutAboutRouteImport.update({ + id: '/(foo)/asdf/(bar)/_layout/about', + path: '/asdf/about', + getParentRoute: () => rootRouteImport, +} as any) const fooAsdfbarLayoutXyzLazyRoute = fooAsdfbarLayoutXyzLazyRouteImport .update({ id: '/(foo)/asdf/(bar)/_layout/xyz', @@ -63,17 +74,6 @@ const fooAsdfbarLayoutXyzLazyRoute = fooAsdfbarLayoutXyzLazyRouteImport .lazy(() => import('./routes/(foo)/asdf/(bar)/_layout.xyz.lazy').then((d) => d.Route), ) -const fooAsdfbarLayoutAboutRoute = fooAsdfbarLayoutAboutRouteImport.update({ - id: '/(foo)/asdf/(bar)/_layout/about', - path: '/asdf/about', - getParentRoute: () => rootRouteImport, -} as any) -const fooAsdfanotherGroupLayoutBazRoute = - fooAsdfanotherGroupLayoutBazRouteImport.update({ - id: '/baz', - path: '/baz', - getParentRoute: () => fooAsdfanotherGroupLayoutRoute, - } as any) export interface FileRoutesByFullPath { '/hello': typeof barBarHelloRoute @@ -155,13 +155,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof barBarRouteImport parentRoute: typeof rootRouteImport } - '/(foo)/asdf/_layout': { - id: '/(foo)/asdf/_layout' - path: '/asdf' - fullPath: '/asdf' - preLoaderRoute: typeof fooAsdfLayoutRouteImport - parentRoute: typeof rootRouteImport - } '/(bar)/_bar/hello': { id: '/(bar)/_bar/hello' path: '/hello' @@ -169,18 +162,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof barBarHelloRouteImport parentRoute: typeof barBarRoute } - '/(foo)/asdf/_layout/foo': { - id: '/(foo)/asdf/_layout/foo' - path: '/foo' - fullPath: '/asdf/foo' - preLoaderRoute: typeof fooAsdfLayoutFooRouteImport - parentRoute: typeof fooAsdfLayoutRoute - } - '/(foo)/asdf/(bar)/$id': { - id: '/(foo)/asdf/(bar)/$id' - path: '/asdf/$id' - fullPath: '/asdf/$id' - preLoaderRoute: typeof fooAsdfbarIdRouteImport + '/(foo)/asdf/_layout': { + id: '/(foo)/asdf/_layout' + path: '/asdf' + fullPath: '/asdf' + preLoaderRoute: typeof fooAsdfLayoutRouteImport parentRoute: typeof rootRouteImport } '/(foo)/asdf/(another-group)/_layout': { @@ -190,19 +176,19 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof fooAsdfanotherGroupLayoutRouteImport parentRoute: typeof rootRouteImport } - '/(foo)/asdf/(bar)/_layout/xyz': { - id: '/(foo)/asdf/(bar)/_layout/xyz' - path: '/asdf/xyz' - fullPath: '/asdf/xyz' - preLoaderRoute: typeof fooAsdfbarLayoutXyzLazyRouteImport + '/(foo)/asdf/(bar)/$id': { + id: '/(foo)/asdf/(bar)/$id' + path: '/asdf/$id' + fullPath: '/asdf/$id' + preLoaderRoute: typeof fooAsdfbarIdRouteImport parentRoute: typeof rootRouteImport } - '/(foo)/asdf/(bar)/_layout/about': { - id: '/(foo)/asdf/(bar)/_layout/about' - path: '/asdf/about' - fullPath: '/asdf/about' - preLoaderRoute: typeof fooAsdfbarLayoutAboutRouteImport - parentRoute: typeof rootRouteImport + '/(foo)/asdf/_layout/foo': { + id: '/(foo)/asdf/_layout/foo' + path: '/foo' + fullPath: '/asdf/foo' + preLoaderRoute: typeof fooAsdfLayoutFooRouteImport + parentRoute: typeof fooAsdfLayoutRoute } '/(foo)/asdf/(another-group)/_layout/baz': { id: '/(foo)/asdf/(another-group)/_layout/baz' @@ -211,6 +197,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof fooAsdfanotherGroupLayoutBazRouteImport parentRoute: typeof fooAsdfanotherGroupLayoutRoute } + '/(foo)/asdf/(bar)/_layout/about': { + id: '/(foo)/asdf/(bar)/_layout/about' + path: '/asdf/about' + fullPath: '/asdf/about' + preLoaderRoute: typeof fooAsdfbarLayoutAboutRouteImport + parentRoute: typeof rootRouteImport + } + '/(foo)/asdf/(bar)/_layout/xyz': { + id: '/(foo)/asdf/(bar)/_layout/xyz' + path: '/asdf/xyz' + fullPath: '/asdf/xyz' + preLoaderRoute: typeof fooAsdfbarLayoutXyzLazyRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/packages/router-generator/tests/generator/routeFileIgnore/routeTree.snapshot.ts b/packages/router-generator/tests/generator/routeFileIgnore/routeTree.snapshot.ts index 33a4093b9e..ceb6811cf7 100644 --- a/packages/router-generator/tests/generator/routeFileIgnore/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/routeFileIgnore/routeTree.snapshot.ts @@ -9,19 +9,19 @@ // 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 BlogRouteRouteImport } from './routes/blog.route' import { Route as IndexRouteImport } from './routes/index' +import { Route as BlogRouteRouteImport } from './routes/blog.route' -const BlogRouteRoute = BlogRouteRouteImport.update({ - id: '/blog', - path: '/blog', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const BlogRouteRoute = BlogRouteRouteImport.update({ + id: '/blog', + path: '/blog', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/blog': { - id: '/blog' - path: '/blog' - fullPath: '/blog' - preLoaderRoute: typeof BlogRouteRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/blog': { + id: '/blog' + path: '/blog' + fullPath: '/blog' + preLoaderRoute: typeof BlogRouteRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/packages/router-generator/tests/generator/routeFilePrefix/routeTree.snapshot.ts b/packages/router-generator/tests/generator/routeFilePrefix/routeTree.snapshot.ts index 46ee215c03..9805633c47 100644 --- a/packages/router-generator/tests/generator/routeFilePrefix/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/routeFilePrefix/routeTree.snapshot.ts @@ -9,19 +9,19 @@ // 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/r&__root' -import { Route as BlogRouteRouteImport } from './routes/r&blog.route' import { Route as IndexRouteImport } from './routes/r&index' +import { Route as BlogRouteRouteImport } from './routes/r&blog.route' -const BlogRouteRoute = BlogRouteRouteImport.update({ - id: '/blog', - path: '/blog', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const BlogRouteRoute = BlogRouteRouteImport.update({ + id: '/blog', + path: '/blog', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/blog': { - id: '/blog' - path: '/blog' - fullPath: '/blog' - preLoaderRoute: typeof BlogRouteRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/blog': { + id: '/blog' + path: '/blog' + fullPath: '/blog' + preLoaderRoute: typeof BlogRouteRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/packages/router-generator/tests/generator/single-level/routeTree.snapshot.ts b/packages/router-generator/tests/generator/single-level/routeTree.snapshot.ts index b60f0b934f..12b48f052a 100644 --- a/packages/router-generator/tests/generator/single-level/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/single-level/routeTree.snapshot.ts @@ -9,19 +9,19 @@ // 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 PostsRouteImport } from './routes/posts' import { Route as IndexRouteImport } from './routes/index' +import { Route as PostsRouteImport } from './routes/posts' -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/packages/router-generator/tests/generator/types-disabled/routeTree.snapshot.js b/packages/router-generator/tests/generator/types-disabled/routeTree.snapshot.js index 9c410eea0f..4dc8f12ad7 100644 --- a/packages/router-generator/tests/generator/types-disabled/routeTree.snapshot.js +++ b/packages/router-generator/tests/generator/types-disabled/routeTree.snapshot.js @@ -9,24 +9,19 @@ // 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 PostsRouteImport } from './routes/posts' import { Route as IndexRouteImport } from './routes/index' -import { Route as UsersUserIdRouteImport } from './routes/users.$userId' +import { Route as PostsRouteImport } from './routes/posts' import { Route as PostsPostIdRouteImport } from './routes/posts/$postId' +import { Route as UsersUserIdRouteImport } from './routes/users.$userId' -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', - getParentRoute: () => rootRouteImport, -}) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, }) -const UsersUserIdRoute = UsersUserIdRouteImport.update({ - id: '/users/$userId', - path: '/users/$userId', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, }) const PostsPostIdRoute = PostsPostIdRouteImport.update({ @@ -34,6 +29,11 @@ const PostsPostIdRoute = PostsPostIdRouteImport.update({ path: '/$postId', getParentRoute: () => PostsRoute, }) +const UsersUserIdRoute = UsersUserIdRouteImport.update({ + id: '/users/$userId', + path: '/users/$userId', + getParentRoute: () => rootRouteImport, +}) const PostsRouteChildren = { PostsPostIdRoute: PostsPostIdRoute, diff --git a/packages/router-generator/tests/generator/virtual-config-file-default-export/routeTree.snapshot.ts b/packages/router-generator/tests/generator/virtual-config-file-default-export/routeTree.snapshot.ts index 3d3c2de9d5..6b16924d5b 100644 --- a/packages/router-generator/tests/generator/virtual-config-file-default-export/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/virtual-config-file-default-export/routeTree.snapshot.ts @@ -9,51 +9,61 @@ // 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 layoutRouteImport } from './routes/layout' import { Route as indexRouteImport } from './routes/index' -import { Route as dbDashboardRouteImport } from './routes/db/dashboard' +import { Route as layoutRouteImport } from './routes/layout' import { Route as pagesRouteImport } from './routes/pages' -import { Route as HelloIndexRouteImport } from './routes/subtree/index' -import { Route as dbDashboardInvoicesRouteImport } from './routes/db/dashboard-invoices' +import { Route as dbDashboardRouteImport } from './routes/db/dashboard' import { Route as dbDashboardIndexRouteImport } from './routes/db/dashboard-index' +import { Route as dbDashboardInvoicesRouteImport } from './routes/db/dashboard-invoices' +import { Route as HelloIndexRouteImport } from './routes/subtree/index' +import { Route as dbInvoicesIndexRouteImport } from './routes/db/invoices-index' +import { Route as dbInvoiceDetailRouteImport } from './routes/db/invoice-detail' import { Route as HelloFooIndexRouteImport } from './routes/subtree/foo/index' import { Route as HelloFooIdRouteImport } from './routes/subtree/foo/$id' -import { Route as dbInvoiceDetailRouteImport } from './routes/db/invoice-detail' -import { Route as dbInvoicesIndexRouteImport } from './routes/db/invoices-index' -const layoutRoute = layoutRouteImport.update({ - id: '/_layout', - getParentRoute: () => rootRouteImport, -} as any) const indexRoute = indexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) -const dbDashboardRoute = dbDashboardRouteImport.update({ - id: '/dashboard', - path: '/dashboard', - getParentRoute: () => layoutRoute, +const layoutRoute = layoutRouteImport.update({ + id: '/_layout', + getParentRoute: () => rootRouteImport, } as any) const pagesRoute = pagesRouteImport.update({ id: '/$lang/', path: '/$lang/', getParentRoute: () => rootRouteImport, } as any) -const HelloIndexRoute = HelloIndexRouteImport.update({ - id: '/hello/', - path: '/hello/', +const dbDashboardRoute = dbDashboardRouteImport.update({ + id: '/dashboard', + path: '/dashboard', getParentRoute: () => layoutRoute, } as any) +const dbDashboardIndexRoute = dbDashboardIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => dbDashboardRoute, +} as any) const dbDashboardInvoicesRoute = dbDashboardInvoicesRouteImport.update({ id: '/invoices', path: '/invoices', getParentRoute: () => dbDashboardRoute, } as any) -const dbDashboardIndexRoute = dbDashboardIndexRouteImport.update({ +const HelloIndexRoute = HelloIndexRouteImport.update({ + id: '/hello/', + path: '/hello/', + getParentRoute: () => layoutRoute, +} as any) +const dbInvoicesIndexRoute = dbInvoicesIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => dbDashboardRoute, + getParentRoute: () => dbDashboardInvoicesRoute, +} as any) +const dbInvoiceDetailRoute = dbInvoiceDetailRouteImport.update({ + id: '/$id', + path: '/$id', + getParentRoute: () => dbDashboardInvoicesRoute, } as any) const HelloFooIndexRoute = HelloFooIndexRouteImport.update({ id: '/hello/foo/', @@ -65,16 +75,6 @@ const HelloFooIdRoute = HelloFooIdRouteImport.update({ path: '/hello/foo/$id', getParentRoute: () => layoutRoute, } as any) -const dbInvoiceDetailRoute = dbInvoiceDetailRouteImport.update({ - id: '/$id', - path: '/$id', - getParentRoute: () => dbDashboardInvoicesRoute, -} as any) -const dbInvoicesIndexRoute = dbInvoicesIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => dbDashboardInvoicesRoute, -} as any) export interface FileRoutesByFullPath { '/': typeof indexRoute @@ -158,13 +158,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/_layout': { - id: '/_layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof layoutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -172,12 +165,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof indexRouteImport parentRoute: typeof rootRouteImport } - '/_layout/dashboard': { - id: '/_layout/dashboard' - path: '/dashboard' - fullPath: '/dashboard' - preLoaderRoute: typeof dbDashboardRouteImport - parentRoute: typeof layoutRoute + '/_layout': { + id: '/_layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof layoutRouteImport + parentRoute: typeof rootRouteImport } '/$lang/': { id: '/$lang/' @@ -186,13 +179,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof pagesRouteImport parentRoute: typeof rootRouteImport } - '/_layout/hello/': { - id: '/_layout/hello/' - path: '/hello' - fullPath: '/hello/' - preLoaderRoute: typeof HelloIndexRouteImport + '/_layout/dashboard': { + id: '/_layout/dashboard' + path: '/dashboard' + fullPath: '/dashboard' + preLoaderRoute: typeof dbDashboardRouteImport parentRoute: typeof layoutRoute } + '/_layout/dashboard/': { + id: '/_layout/dashboard/' + path: '/' + fullPath: '/dashboard/' + preLoaderRoute: typeof dbDashboardIndexRouteImport + parentRoute: typeof dbDashboardRoute + } '/_layout/dashboard/invoices': { id: '/_layout/dashboard/invoices' path: '/invoices' @@ -200,12 +200,26 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof dbDashboardInvoicesRouteImport parentRoute: typeof dbDashboardRoute } - '/_layout/dashboard/': { - id: '/_layout/dashboard/' + '/_layout/hello/': { + id: '/_layout/hello/' + path: '/hello' + fullPath: '/hello/' + preLoaderRoute: typeof HelloIndexRouteImport + parentRoute: typeof layoutRoute + } + '/_layout/dashboard/invoices/': { + id: '/_layout/dashboard/invoices/' path: '/' - fullPath: '/dashboard/' - preLoaderRoute: typeof dbDashboardIndexRouteImport - parentRoute: typeof dbDashboardRoute + fullPath: '/dashboard/invoices/' + preLoaderRoute: typeof dbInvoicesIndexRouteImport + parentRoute: typeof dbDashboardInvoicesRoute + } + '/_layout/dashboard/invoices/$id': { + id: '/_layout/dashboard/invoices/$id' + path: '/$id' + fullPath: '/dashboard/invoices/$id' + preLoaderRoute: typeof dbInvoiceDetailRouteImport + parentRoute: typeof dbDashboardInvoicesRoute } '/_layout/hello/foo/': { id: '/_layout/hello/foo/' @@ -221,20 +235,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof HelloFooIdRouteImport parentRoute: typeof layoutRoute } - '/_layout/dashboard/invoices/$id': { - id: '/_layout/dashboard/invoices/$id' - path: '/$id' - fullPath: '/dashboard/invoices/$id' - preLoaderRoute: typeof dbInvoiceDetailRouteImport - parentRoute: typeof dbDashboardInvoicesRoute - } - '/_layout/dashboard/invoices/': { - id: '/_layout/dashboard/invoices/' - path: '/' - fullPath: '/dashboard/invoices/' - preLoaderRoute: typeof dbInvoicesIndexRouteImport - parentRoute: typeof dbDashboardInvoicesRoute - } } } diff --git a/packages/router-generator/tests/generator/virtual-config-file-named-export/routeTree.snapshot.ts b/packages/router-generator/tests/generator/virtual-config-file-named-export/routeTree.snapshot.ts index 3d3c2de9d5..6b16924d5b 100644 --- a/packages/router-generator/tests/generator/virtual-config-file-named-export/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/virtual-config-file-named-export/routeTree.snapshot.ts @@ -9,51 +9,61 @@ // 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 layoutRouteImport } from './routes/layout' import { Route as indexRouteImport } from './routes/index' -import { Route as dbDashboardRouteImport } from './routes/db/dashboard' +import { Route as layoutRouteImport } from './routes/layout' import { Route as pagesRouteImport } from './routes/pages' -import { Route as HelloIndexRouteImport } from './routes/subtree/index' -import { Route as dbDashboardInvoicesRouteImport } from './routes/db/dashboard-invoices' +import { Route as dbDashboardRouteImport } from './routes/db/dashboard' import { Route as dbDashboardIndexRouteImport } from './routes/db/dashboard-index' +import { Route as dbDashboardInvoicesRouteImport } from './routes/db/dashboard-invoices' +import { Route as HelloIndexRouteImport } from './routes/subtree/index' +import { Route as dbInvoicesIndexRouteImport } from './routes/db/invoices-index' +import { Route as dbInvoiceDetailRouteImport } from './routes/db/invoice-detail' import { Route as HelloFooIndexRouteImport } from './routes/subtree/foo/index' import { Route as HelloFooIdRouteImport } from './routes/subtree/foo/$id' -import { Route as dbInvoiceDetailRouteImport } from './routes/db/invoice-detail' -import { Route as dbInvoicesIndexRouteImport } from './routes/db/invoices-index' -const layoutRoute = layoutRouteImport.update({ - id: '/_layout', - getParentRoute: () => rootRouteImport, -} as any) const indexRoute = indexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) -const dbDashboardRoute = dbDashboardRouteImport.update({ - id: '/dashboard', - path: '/dashboard', - getParentRoute: () => layoutRoute, +const layoutRoute = layoutRouteImport.update({ + id: '/_layout', + getParentRoute: () => rootRouteImport, } as any) const pagesRoute = pagesRouteImport.update({ id: '/$lang/', path: '/$lang/', getParentRoute: () => rootRouteImport, } as any) -const HelloIndexRoute = HelloIndexRouteImport.update({ - id: '/hello/', - path: '/hello/', +const dbDashboardRoute = dbDashboardRouteImport.update({ + id: '/dashboard', + path: '/dashboard', getParentRoute: () => layoutRoute, } as any) +const dbDashboardIndexRoute = dbDashboardIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => dbDashboardRoute, +} as any) const dbDashboardInvoicesRoute = dbDashboardInvoicesRouteImport.update({ id: '/invoices', path: '/invoices', getParentRoute: () => dbDashboardRoute, } as any) -const dbDashboardIndexRoute = dbDashboardIndexRouteImport.update({ +const HelloIndexRoute = HelloIndexRouteImport.update({ + id: '/hello/', + path: '/hello/', + getParentRoute: () => layoutRoute, +} as any) +const dbInvoicesIndexRoute = dbInvoicesIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => dbDashboardRoute, + getParentRoute: () => dbDashboardInvoicesRoute, +} as any) +const dbInvoiceDetailRoute = dbInvoiceDetailRouteImport.update({ + id: '/$id', + path: '/$id', + getParentRoute: () => dbDashboardInvoicesRoute, } as any) const HelloFooIndexRoute = HelloFooIndexRouteImport.update({ id: '/hello/foo/', @@ -65,16 +75,6 @@ const HelloFooIdRoute = HelloFooIdRouteImport.update({ path: '/hello/foo/$id', getParentRoute: () => layoutRoute, } as any) -const dbInvoiceDetailRoute = dbInvoiceDetailRouteImport.update({ - id: '/$id', - path: '/$id', - getParentRoute: () => dbDashboardInvoicesRoute, -} as any) -const dbInvoicesIndexRoute = dbInvoicesIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => dbDashboardInvoicesRoute, -} as any) export interface FileRoutesByFullPath { '/': typeof indexRoute @@ -158,13 +158,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/_layout': { - id: '/_layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof layoutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -172,12 +165,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof indexRouteImport parentRoute: typeof rootRouteImport } - '/_layout/dashboard': { - id: '/_layout/dashboard' - path: '/dashboard' - fullPath: '/dashboard' - preLoaderRoute: typeof dbDashboardRouteImport - parentRoute: typeof layoutRoute + '/_layout': { + id: '/_layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof layoutRouteImport + parentRoute: typeof rootRouteImport } '/$lang/': { id: '/$lang/' @@ -186,13 +179,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof pagesRouteImport parentRoute: typeof rootRouteImport } - '/_layout/hello/': { - id: '/_layout/hello/' - path: '/hello' - fullPath: '/hello/' - preLoaderRoute: typeof HelloIndexRouteImport + '/_layout/dashboard': { + id: '/_layout/dashboard' + path: '/dashboard' + fullPath: '/dashboard' + preLoaderRoute: typeof dbDashboardRouteImport parentRoute: typeof layoutRoute } + '/_layout/dashboard/': { + id: '/_layout/dashboard/' + path: '/' + fullPath: '/dashboard/' + preLoaderRoute: typeof dbDashboardIndexRouteImport + parentRoute: typeof dbDashboardRoute + } '/_layout/dashboard/invoices': { id: '/_layout/dashboard/invoices' path: '/invoices' @@ -200,12 +200,26 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof dbDashboardInvoicesRouteImport parentRoute: typeof dbDashboardRoute } - '/_layout/dashboard/': { - id: '/_layout/dashboard/' + '/_layout/hello/': { + id: '/_layout/hello/' + path: '/hello' + fullPath: '/hello/' + preLoaderRoute: typeof HelloIndexRouteImport + parentRoute: typeof layoutRoute + } + '/_layout/dashboard/invoices/': { + id: '/_layout/dashboard/invoices/' path: '/' - fullPath: '/dashboard/' - preLoaderRoute: typeof dbDashboardIndexRouteImport - parentRoute: typeof dbDashboardRoute + fullPath: '/dashboard/invoices/' + preLoaderRoute: typeof dbInvoicesIndexRouteImport + parentRoute: typeof dbDashboardInvoicesRoute + } + '/_layout/dashboard/invoices/$id': { + id: '/_layout/dashboard/invoices/$id' + path: '/$id' + fullPath: '/dashboard/invoices/$id' + preLoaderRoute: typeof dbInvoiceDetailRouteImport + parentRoute: typeof dbDashboardInvoicesRoute } '/_layout/hello/foo/': { id: '/_layout/hello/foo/' @@ -221,20 +235,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof HelloFooIdRouteImport parentRoute: typeof layoutRoute } - '/_layout/dashboard/invoices/$id': { - id: '/_layout/dashboard/invoices/$id' - path: '/$id' - fullPath: '/dashboard/invoices/$id' - preLoaderRoute: typeof dbInvoiceDetailRouteImport - parentRoute: typeof dbDashboardInvoicesRoute - } - '/_layout/dashboard/invoices/': { - id: '/_layout/dashboard/invoices/' - path: '/' - fullPath: '/dashboard/invoices/' - preLoaderRoute: typeof dbInvoicesIndexRouteImport - parentRoute: typeof dbDashboardInvoicesRoute - } } } diff --git a/packages/router-generator/tests/generator/virtual-config-file-tsconfig-paths/routeTree.snapshot.ts b/packages/router-generator/tests/generator/virtual-config-file-tsconfig-paths/routeTree.snapshot.ts index 3d3c2de9d5..6b16924d5b 100644 --- a/packages/router-generator/tests/generator/virtual-config-file-tsconfig-paths/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/virtual-config-file-tsconfig-paths/routeTree.snapshot.ts @@ -9,51 +9,61 @@ // 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 layoutRouteImport } from './routes/layout' import { Route as indexRouteImport } from './routes/index' -import { Route as dbDashboardRouteImport } from './routes/db/dashboard' +import { Route as layoutRouteImport } from './routes/layout' import { Route as pagesRouteImport } from './routes/pages' -import { Route as HelloIndexRouteImport } from './routes/subtree/index' -import { Route as dbDashboardInvoicesRouteImport } from './routes/db/dashboard-invoices' +import { Route as dbDashboardRouteImport } from './routes/db/dashboard' import { Route as dbDashboardIndexRouteImport } from './routes/db/dashboard-index' +import { Route as dbDashboardInvoicesRouteImport } from './routes/db/dashboard-invoices' +import { Route as HelloIndexRouteImport } from './routes/subtree/index' +import { Route as dbInvoicesIndexRouteImport } from './routes/db/invoices-index' +import { Route as dbInvoiceDetailRouteImport } from './routes/db/invoice-detail' import { Route as HelloFooIndexRouteImport } from './routes/subtree/foo/index' import { Route as HelloFooIdRouteImport } from './routes/subtree/foo/$id' -import { Route as dbInvoiceDetailRouteImport } from './routes/db/invoice-detail' -import { Route as dbInvoicesIndexRouteImport } from './routes/db/invoices-index' -const layoutRoute = layoutRouteImport.update({ - id: '/_layout', - getParentRoute: () => rootRouteImport, -} as any) const indexRoute = indexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) -const dbDashboardRoute = dbDashboardRouteImport.update({ - id: '/dashboard', - path: '/dashboard', - getParentRoute: () => layoutRoute, +const layoutRoute = layoutRouteImport.update({ + id: '/_layout', + getParentRoute: () => rootRouteImport, } as any) const pagesRoute = pagesRouteImport.update({ id: '/$lang/', path: '/$lang/', getParentRoute: () => rootRouteImport, } as any) -const HelloIndexRoute = HelloIndexRouteImport.update({ - id: '/hello/', - path: '/hello/', +const dbDashboardRoute = dbDashboardRouteImport.update({ + id: '/dashboard', + path: '/dashboard', getParentRoute: () => layoutRoute, } as any) +const dbDashboardIndexRoute = dbDashboardIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => dbDashboardRoute, +} as any) const dbDashboardInvoicesRoute = dbDashboardInvoicesRouteImport.update({ id: '/invoices', path: '/invoices', getParentRoute: () => dbDashboardRoute, } as any) -const dbDashboardIndexRoute = dbDashboardIndexRouteImport.update({ +const HelloIndexRoute = HelloIndexRouteImport.update({ + id: '/hello/', + path: '/hello/', + getParentRoute: () => layoutRoute, +} as any) +const dbInvoicesIndexRoute = dbInvoicesIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => dbDashboardRoute, + getParentRoute: () => dbDashboardInvoicesRoute, +} as any) +const dbInvoiceDetailRoute = dbInvoiceDetailRouteImport.update({ + id: '/$id', + path: '/$id', + getParentRoute: () => dbDashboardInvoicesRoute, } as any) const HelloFooIndexRoute = HelloFooIndexRouteImport.update({ id: '/hello/foo/', @@ -65,16 +75,6 @@ const HelloFooIdRoute = HelloFooIdRouteImport.update({ path: '/hello/foo/$id', getParentRoute: () => layoutRoute, } as any) -const dbInvoiceDetailRoute = dbInvoiceDetailRouteImport.update({ - id: '/$id', - path: '/$id', - getParentRoute: () => dbDashboardInvoicesRoute, -} as any) -const dbInvoicesIndexRoute = dbInvoicesIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => dbDashboardInvoicesRoute, -} as any) export interface FileRoutesByFullPath { '/': typeof indexRoute @@ -158,13 +158,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/_layout': { - id: '/_layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof layoutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -172,12 +165,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof indexRouteImport parentRoute: typeof rootRouteImport } - '/_layout/dashboard': { - id: '/_layout/dashboard' - path: '/dashboard' - fullPath: '/dashboard' - preLoaderRoute: typeof dbDashboardRouteImport - parentRoute: typeof layoutRoute + '/_layout': { + id: '/_layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof layoutRouteImport + parentRoute: typeof rootRouteImport } '/$lang/': { id: '/$lang/' @@ -186,13 +179,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof pagesRouteImport parentRoute: typeof rootRouteImport } - '/_layout/hello/': { - id: '/_layout/hello/' - path: '/hello' - fullPath: '/hello/' - preLoaderRoute: typeof HelloIndexRouteImport + '/_layout/dashboard': { + id: '/_layout/dashboard' + path: '/dashboard' + fullPath: '/dashboard' + preLoaderRoute: typeof dbDashboardRouteImport parentRoute: typeof layoutRoute } + '/_layout/dashboard/': { + id: '/_layout/dashboard/' + path: '/' + fullPath: '/dashboard/' + preLoaderRoute: typeof dbDashboardIndexRouteImport + parentRoute: typeof dbDashboardRoute + } '/_layout/dashboard/invoices': { id: '/_layout/dashboard/invoices' path: '/invoices' @@ -200,12 +200,26 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof dbDashboardInvoicesRouteImport parentRoute: typeof dbDashboardRoute } - '/_layout/dashboard/': { - id: '/_layout/dashboard/' + '/_layout/hello/': { + id: '/_layout/hello/' + path: '/hello' + fullPath: '/hello/' + preLoaderRoute: typeof HelloIndexRouteImport + parentRoute: typeof layoutRoute + } + '/_layout/dashboard/invoices/': { + id: '/_layout/dashboard/invoices/' path: '/' - fullPath: '/dashboard/' - preLoaderRoute: typeof dbDashboardIndexRouteImport - parentRoute: typeof dbDashboardRoute + fullPath: '/dashboard/invoices/' + preLoaderRoute: typeof dbInvoicesIndexRouteImport + parentRoute: typeof dbDashboardInvoicesRoute + } + '/_layout/dashboard/invoices/$id': { + id: '/_layout/dashboard/invoices/$id' + path: '/$id' + fullPath: '/dashboard/invoices/$id' + preLoaderRoute: typeof dbInvoiceDetailRouteImport + parentRoute: typeof dbDashboardInvoicesRoute } '/_layout/hello/foo/': { id: '/_layout/hello/foo/' @@ -221,20 +235,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof HelloFooIdRouteImport parentRoute: typeof layoutRoute } - '/_layout/dashboard/invoices/$id': { - id: '/_layout/dashboard/invoices/$id' - path: '/$id' - fullPath: '/dashboard/invoices/$id' - preLoaderRoute: typeof dbInvoiceDetailRouteImport - parentRoute: typeof dbDashboardInvoicesRoute - } - '/_layout/dashboard/invoices/': { - id: '/_layout/dashboard/invoices/' - path: '/' - fullPath: '/dashboard/invoices/' - preLoaderRoute: typeof dbInvoicesIndexRouteImport - parentRoute: typeof dbDashboardInvoicesRoute - } } } diff --git a/packages/router-generator/tests/generator/virtual-inside-nested/routeTree.snapshot.ts b/packages/router-generator/tests/generator/virtual-inside-nested/routeTree.snapshot.ts index 9f60b82e06..37de7dc290 100644 --- a/packages/router-generator/tests/generator/virtual-inside-nested/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/virtual-inside-nested/routeTree.snapshot.ts @@ -11,8 +11,8 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' import { Route as FooBarRouteImport } from './routes/foo/bar' -import { Route as FooBarDetailsRouteImport } from './routes/foo/bar/details' import { Route as FooBarHomeRouteImport } from './routes/foo/bar/home' +import { Route as FooBarDetailsRouteImport } from './routes/foo/bar/details' const IndexRoute = IndexRouteImport.update({ id: '/', @@ -24,16 +24,16 @@ const FooBarRoute = FooBarRouteImport.update({ path: '/foo/bar', getParentRoute: () => rootRouteImport, } as any) -const FooBarDetailsRoute = FooBarDetailsRouteImport.update({ - id: '/$id', - path: '/$id', - getParentRoute: () => FooBarRoute, -} as any) const FooBarHomeRoute = FooBarHomeRouteImport.update({ id: '/', path: '/', getParentRoute: () => FooBarRoute, } as any) +const FooBarDetailsRoute = FooBarDetailsRouteImport.update({ + id: '/$id', + path: '/$id', + getParentRoute: () => FooBarRoute, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -82,13 +82,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof FooBarRouteImport parentRoute: typeof rootRouteImport } - '/foo/bar/$id': { - id: '/foo/bar/$id' - path: '/$id' - fullPath: '/foo/bar/$id' - preLoaderRoute: typeof FooBarDetailsRouteImport - parentRoute: typeof FooBarRoute - } '/foo/bar/': { id: '/foo/bar/' path: '/' @@ -96,6 +89,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof FooBarHomeRouteImport parentRoute: typeof FooBarRoute } + '/foo/bar/$id': { + id: '/foo/bar/$id' + path: '/$id' + fullPath: '/foo/bar/$id' + preLoaderRoute: typeof FooBarDetailsRouteImport + parentRoute: typeof FooBarRoute + } } } diff --git a/packages/router-generator/tests/generator/virtual-inside-with-escaped-underscore/routeTree.snapshot.ts b/packages/router-generator/tests/generator/virtual-inside-with-escaped-underscore/routeTree.snapshot.ts index 664cea729c..340a1f0f5e 100644 --- a/packages/router-generator/tests/generator/virtual-inside-with-escaped-underscore/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/virtual-inside-with-escaped-underscore/routeTree.snapshot.ts @@ -10,18 +10,18 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' -import { Route as NestedCallbackRouteImport } from './routes/nested/callback' -import { Route as NestedAuthRouteImport } from './routes/nested/auth' import { Route as NestedHomeRouteImport } from './routes/nested/home' +import { Route as NestedAuthRouteImport } from './routes/nested/auth' +import { Route as NestedCallbackRouteImport } from './routes/nested/callback' const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) -const NestedCallbackRoute = NestedCallbackRouteImport.update({ - id: '/nested/_callback', - path: '/nested/_callback', +const NestedHomeRoute = NestedHomeRouteImport.update({ + id: '/nested/', + path: '/nested/', getParentRoute: () => rootRouteImport, } as any) const NestedAuthRoute = NestedAuthRouteImport.update({ @@ -29,9 +29,9 @@ const NestedAuthRoute = NestedAuthRouteImport.update({ path: '/nested/_auth', getParentRoute: () => rootRouteImport, } as any) -const NestedHomeRoute = NestedHomeRouteImport.update({ - id: '/nested/', - path: '/nested/', +const NestedCallbackRoute = NestedCallbackRouteImport.update({ + id: '/nested/_callback', + path: '/nested/_callback', getParentRoute: () => rootRouteImport, } as any) @@ -78,11 +78,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/nested/_callback': { - id: '/nested/_callback' - path: '/nested/_callback' - fullPath: '/nested/_callback' - preLoaderRoute: typeof NestedCallbackRouteImport + '/nested/': { + id: '/nested/' + path: '/nested' + fullPath: '/nested/' + preLoaderRoute: typeof NestedHomeRouteImport parentRoute: typeof rootRouteImport } '/nested/_auth': { @@ -92,11 +92,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof NestedAuthRouteImport parentRoute: typeof rootRouteImport } - '/nested/': { - id: '/nested/' - path: '/nested' - fullPath: '/nested/' - preLoaderRoute: typeof NestedHomeRouteImport + '/nested/_callback': { + id: '/nested/_callback' + path: '/nested/_callback' + fullPath: '/nested/_callback' + preLoaderRoute: typeof NestedCallbackRouteImport parentRoute: typeof rootRouteImport } } diff --git a/packages/router-generator/tests/generator/virtual-nested-layouts-with-virtual-route/routeTree.snapshot.ts b/packages/router-generator/tests/generator/virtual-nested-layouts-with-virtual-route/routeTree.snapshot.ts index e991cd3964..7268f402ae 100644 --- a/packages/router-generator/tests/generator/virtual-nested-layouts-with-virtual-route/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/virtual-nested-layouts-with-virtual-route/routeTree.snapshot.ts @@ -9,35 +9,35 @@ // 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 layoutFirstLayoutRouteImport } from './routes/layout/first-layout' import { Route as homeRouteImport } from './routes/home' +import { Route as layoutFirstLayoutRouteImport } from './routes/layout/first-layout' import { Route as layoutSecondLayoutRouteImport } from './routes/layout/second-layout' -import { Route as bRouteImport } from './routes/b' import { Route as aRouteImport } from './routes/a' +import { Route as bRouteImport } from './routes/b' -const layoutFirstLayoutRoute = layoutFirstLayoutRouteImport.update({ - id: '/_first', - getParentRoute: () => rootRouteImport, -} as any) const homeRoute = homeRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const layoutFirstLayoutRoute = layoutFirstLayoutRouteImport.update({ + id: '/_first', + getParentRoute: () => rootRouteImport, +} as any) const layoutSecondLayoutRoute = layoutSecondLayoutRouteImport.update({ id: '/_second-layout', getParentRoute: () => layoutFirstLayoutRoute, } as any) -const bRoute = bRouteImport.update({ - id: '/route-without-file/layout-b', - path: '/route-without-file/layout-b', - getParentRoute: () => layoutSecondLayoutRoute, -} as any) const aRoute = aRouteImport.update({ id: '/route-without-file/layout-a', path: '/route-without-file/layout-a', getParentRoute: () => layoutSecondLayoutRoute, } as any) +const bRoute = bRouteImport.update({ + id: '/route-without-file/layout-b', + path: '/route-without-file/layout-b', + getParentRoute: () => layoutSecondLayoutRoute, +} as any) export interface FileRoutesByFullPath { '/': typeof homeRoute @@ -81,13 +81,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/_first': { - id: '/_first' - path: '' - fullPath: '/' - preLoaderRoute: typeof layoutFirstLayoutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -95,6 +88,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof homeRouteImport parentRoute: typeof rootRouteImport } + '/_first': { + id: '/_first' + path: '' + fullPath: '/' + preLoaderRoute: typeof layoutFirstLayoutRouteImport + parentRoute: typeof rootRouteImport + } '/_first/_second-layout': { id: '/_first/_second-layout' path: '' @@ -102,13 +102,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof layoutSecondLayoutRouteImport parentRoute: typeof layoutFirstLayoutRoute } - '/_first/_second-layout/route-without-file/layout-b': { - id: '/_first/_second-layout/route-without-file/layout-b' - path: '/route-without-file/layout-b' - fullPath: '/route-without-file/layout-b' - preLoaderRoute: typeof bRouteImport - parentRoute: typeof layoutSecondLayoutRoute - } '/_first/_second-layout/route-without-file/layout-a': { id: '/_first/_second-layout/route-without-file/layout-a' path: '/route-without-file/layout-a' @@ -116,6 +109,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof aRouteImport parentRoute: typeof layoutSecondLayoutRoute } + '/_first/_second-layout/route-without-file/layout-b': { + id: '/_first/_second-layout/route-without-file/layout-b' + path: '/route-without-file/layout-b' + fullPath: '/route-without-file/layout-b' + preLoaderRoute: typeof bRouteImport + parentRoute: typeof layoutSecondLayoutRoute + } } } diff --git a/packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routeTree.snapshot.ts b/packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routeTree.snapshot.ts index 70d06b2004..ec20742d1e 100644 --- a/packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routeTree.snapshot.ts @@ -9,34 +9,30 @@ // 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 layoutRouteImport } from './routes/layout' -import { Route as fooRouteImport } from './routes/foo' -import { Route as escapedRouteImport } from './routes/escaped' import { Route as doubleRouteImport } from './routes/double' -import { Route as rootIdRouteImport } from './routes/root-id' -import { Route as nestedLayoutRouteImport } from './routes/nested-layout' -import { Route as innerLayoutRouteImport } from './routes/inner-layout' -import { Route as escapedBarRouteImport } from './routes/escaped-bar' -import { Route as barRouteImport } from './routes/bar' +import { Route as escapedRouteImport } from './routes/escaped' +import { Route as fooRouteImport } from './routes/foo' +import { Route as layoutRouteImport } from './routes/layout' import { Route as doubleBarRouteImport } from './routes/double-bar' +import { Route as barRouteImport } from './routes/bar' +import { Route as escapedBarRouteImport } from './routes/escaped-bar' +import { Route as innerLayoutRouteImport } from './routes/inner-layout' +import { Route as nestedLayoutRouteImport } from './routes/nested-layout' import { Route as rootIndexRouteImport } from './routes/root-index' -import { Route as bazRouteImport } from './routes/baz' -import { Route as nestedIdRouteImport } from './routes/nested-id' -import { Route as escapedIdRouteImport } from './routes/escaped-id' +import { Route as rootIdRouteImport } from './routes/root-id' +import { Route as doubleIndexRouteImport } from './routes/double-index' import { Route as doubleIdRouteImport } from './routes/double-id' -import { Route as trailIndexRouteImport } from './routes/trail-index' -import { Route as nestedIndexRouteImport } from './routes/nested-index' import { Route as escapedIndexRouteImport } from './routes/escaped-index' -import { Route as doubleIndexRouteImport } from './routes/double-index' +import { Route as escapedIdRouteImport } from './routes/escaped-id' +import { Route as nestedIndexRouteImport } from './routes/nested-index' +import { Route as nestedIdRouteImport } from './routes/nested-id' +import { Route as bazRouteImport } from './routes/baz' +import { Route as trailIndexRouteImport } from './routes/trail-index' import { Route as deepIndexRouteImport } from './routes/deep-index' -const layoutRoute = layoutRouteImport.update({ - id: '/_layout', - getParentRoute: () => rootRouteImport, -} as any) -const fooRoute = fooRouteImport.update({ - id: '/_foo', - path: '/_foo', +const doubleRoute = doubleRouteImport.update({ + id: '/__double', + path: '/__double', getParentRoute: () => rootRouteImport, } as any) const escapedRoute = escapedRouteImport.update({ @@ -44,22 +40,23 @@ const escapedRoute = escapedRouteImport.update({ path: '/_escaped', getParentRoute: () => rootRouteImport, } as any) -const doubleRoute = doubleRouteImport.update({ - id: '/__double', - path: '/__double', +const fooRoute = fooRouteImport.update({ + id: '/_foo', + path: '/_foo', getParentRoute: () => rootRouteImport, } as any) -const rootIdRoute = rootIdRouteImport.update({ - id: '/_root-index/$id', - path: '/_root-index/$id', +const layoutRoute = layoutRouteImport.update({ + id: '/_layout', getParentRoute: () => rootRouteImport, } as any) -const nestedLayoutRoute = nestedLayoutRouteImport.update({ - id: '/_nested', +const doubleBarRoute = doubleBarRouteImport.update({ + id: '/__double-bar', + path: '/__double-bar', getParentRoute: () => layoutRoute, } as any) -const innerLayoutRoute = innerLayoutRouteImport.update({ - id: '/_inner', +const barRoute = barRouteImport.update({ + id: '/_bar', + path: '/_bar', getParentRoute: () => layoutRoute, } as any) const escapedBarRoute = escapedBarRouteImport.update({ @@ -67,14 +64,12 @@ const escapedBarRoute = escapedBarRouteImport.update({ path: '/_escaped-bar', getParentRoute: () => layoutRoute, } as any) -const barRoute = barRouteImport.update({ - id: '/_bar', - path: '/_bar', +const innerLayoutRoute = innerLayoutRouteImport.update({ + id: '/_inner', getParentRoute: () => layoutRoute, } as any) -const doubleBarRoute = doubleBarRouteImport.update({ - id: '/__double-bar', - path: '/__double-bar', +const nestedLayoutRoute = nestedLayoutRouteImport.update({ + id: '/_nested', getParentRoute: () => layoutRoute, } as any) const rootIndexRoute = rootIndexRouteImport.update({ @@ -82,19 +77,14 @@ const rootIndexRoute = rootIndexRouteImport.update({ path: '/_root-index/', getParentRoute: () => rootRouteImport, } as any) -const bazRoute = bazRouteImport.update({ - id: '/_baz', - path: '/_baz', - getParentRoute: () => nestedLayoutRoute, -} as any) -const nestedIdRoute = nestedIdRouteImport.update({ - id: '/_nested-index/$id', - path: '/_nested-index/$id', - getParentRoute: () => layoutRoute, +const rootIdRoute = rootIdRouteImport.update({ + id: '/_root-index/$id', + path: '/_root-index/$id', + getParentRoute: () => rootRouteImport, } as any) -const escapedIdRoute = escapedIdRouteImport.update({ - id: '/_escaped-index/$id', - path: '/_escaped-index/$id', +const doubleIndexRoute = doubleIndexRouteImport.update({ + id: '/__double-index/', + path: '/__double-index/', getParentRoute: () => layoutRoute, } as any) const doubleIdRoute = doubleIdRouteImport.update({ @@ -102,9 +92,14 @@ const doubleIdRoute = doubleIdRouteImport.update({ path: '/__double-index/$id', getParentRoute: () => layoutRoute, } as any) -const trailIndexRoute = trailIndexRouteImport.update({ - id: '/trail_/', - path: '/trail_/', +const escapedIndexRoute = escapedIndexRouteImport.update({ + id: '/_escaped-index/', + path: '/_escaped-index/', + getParentRoute: () => layoutRoute, +} as any) +const escapedIdRoute = escapedIdRouteImport.update({ + id: '/_escaped-index/$id', + path: '/_escaped-index/$id', getParentRoute: () => layoutRoute, } as any) const nestedIndexRoute = nestedIndexRouteImport.update({ @@ -112,14 +107,19 @@ const nestedIndexRoute = nestedIndexRouteImport.update({ path: '/_nested-index/', getParentRoute: () => layoutRoute, } as any) -const escapedIndexRoute = escapedIndexRouteImport.update({ - id: '/_escaped-index/', - path: '/_escaped-index/', +const nestedIdRoute = nestedIdRouteImport.update({ + id: '/_nested-index/$id', + path: '/_nested-index/$id', getParentRoute: () => layoutRoute, } as any) -const doubleIndexRoute = doubleIndexRouteImport.update({ - id: '/__double-index/', - path: '/__double-index/', +const bazRoute = bazRouteImport.update({ + id: '/_baz', + path: '/_baz', + getParentRoute: () => nestedLayoutRoute, +} as any) +const trailIndexRoute = trailIndexRouteImport.update({ + id: '/trail_/', + path: '/trail_/', getParentRoute: () => layoutRoute, } as any) const deepIndexRoute = deepIndexRouteImport.update({ @@ -267,18 +267,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/_layout': { - id: '/_layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof layoutRouteImport - parentRoute: typeof rootRouteImport - } - '/_foo': { - id: '/_foo' - path: '/_foo' - fullPath: '/_foo' - preLoaderRoute: typeof fooRouteImport + '/__double': { + id: '/__double' + path: '/__double' + fullPath: '/__double' + preLoaderRoute: typeof doubleRouteImport parentRoute: typeof rootRouteImport } '/_escaped': { @@ -288,32 +281,32 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof escapedRouteImport parentRoute: typeof rootRouteImport } - '/__double': { - id: '/__double' - path: '/__double' - fullPath: '/__double' - preLoaderRoute: typeof doubleRouteImport - parentRoute: typeof rootRouteImport - } - '/_root-index/$id': { - id: '/_root-index/$id' - path: '/_root-index/$id' - fullPath: '/_root-index/$id' - preLoaderRoute: typeof rootIdRouteImport + '/_foo': { + id: '/_foo' + path: '/_foo' + fullPath: '/_foo' + preLoaderRoute: typeof fooRouteImport parentRoute: typeof rootRouteImport } - '/_layout/_nested': { - id: '/_layout/_nested' + '/_layout': { + id: '/_layout' path: '' fullPath: '/' - preLoaderRoute: typeof nestedLayoutRouteImport + preLoaderRoute: typeof layoutRouteImport + parentRoute: typeof rootRouteImport + } + '/_layout/__double-bar': { + id: '/_layout/__double-bar' + path: '/__double-bar' + fullPath: '/__double-bar' + preLoaderRoute: typeof doubleBarRouteImport parentRoute: typeof layoutRoute } - '/_layout/_inner': { - id: '/_layout/_inner' - path: '' - fullPath: '/' - preLoaderRoute: typeof innerLayoutRouteImport + '/_layout/_bar': { + id: '/_layout/_bar' + path: '/_bar' + fullPath: '/_bar' + preLoaderRoute: typeof barRouteImport parentRoute: typeof layoutRoute } '/_layout/_escaped-bar': { @@ -323,18 +316,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof escapedBarRouteImport parentRoute: typeof layoutRoute } - '/_layout/_bar': { - id: '/_layout/_bar' - path: '/_bar' - fullPath: '/_bar' - preLoaderRoute: typeof barRouteImport + '/_layout/_inner': { + id: '/_layout/_inner' + path: '' + fullPath: '/' + preLoaderRoute: typeof innerLayoutRouteImport parentRoute: typeof layoutRoute } - '/_layout/__double-bar': { - id: '/_layout/__double-bar' - path: '/__double-bar' - fullPath: '/__double-bar' - preLoaderRoute: typeof doubleBarRouteImport + '/_layout/_nested': { + id: '/_layout/_nested' + path: '' + fullPath: '/' + preLoaderRoute: typeof nestedLayoutRouteImport parentRoute: typeof layoutRoute } '/_root-index/': { @@ -344,25 +337,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof rootIndexRouteImport parentRoute: typeof rootRouteImport } - '/_layout/_nested/_baz': { - id: '/_layout/_nested/_baz' - path: '/_baz' - fullPath: '/_baz' - preLoaderRoute: typeof bazRouteImport - parentRoute: typeof nestedLayoutRoute - } - '/_layout/_nested-index/$id': { - id: '/_layout/_nested-index/$id' - path: '/_nested-index/$id' - fullPath: '/_nested-index/$id' - preLoaderRoute: typeof nestedIdRouteImport - parentRoute: typeof layoutRoute + '/_root-index/$id': { + id: '/_root-index/$id' + path: '/_root-index/$id' + fullPath: '/_root-index/$id' + preLoaderRoute: typeof rootIdRouteImport + parentRoute: typeof rootRouteImport } - '/_layout/_escaped-index/$id': { - id: '/_layout/_escaped-index/$id' - path: '/_escaped-index/$id' - fullPath: '/_escaped-index/$id' - preLoaderRoute: typeof escapedIdRouteImport + '/_layout/__double-index/': { + id: '/_layout/__double-index/' + path: '/__double-index' + fullPath: '/__double-index/' + preLoaderRoute: typeof doubleIndexRouteImport parentRoute: typeof layoutRoute } '/_layout/__double-index/$id': { @@ -372,11 +358,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof doubleIdRouteImport parentRoute: typeof layoutRoute } - '/_layout/trail_/': { - id: '/_layout/trail_/' - path: '/trail_' - fullPath: '/trail_/' - preLoaderRoute: typeof trailIndexRouteImport + '/_layout/_escaped-index/': { + id: '/_layout/_escaped-index/' + path: '/_escaped-index' + fullPath: '/_escaped-index/' + preLoaderRoute: typeof escapedIndexRouteImport + parentRoute: typeof layoutRoute + } + '/_layout/_escaped-index/$id': { + id: '/_layout/_escaped-index/$id' + path: '/_escaped-index/$id' + fullPath: '/_escaped-index/$id' + preLoaderRoute: typeof escapedIdRouteImport parentRoute: typeof layoutRoute } '/_layout/_nested-index/': { @@ -386,18 +379,25 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof nestedIndexRouteImport parentRoute: typeof layoutRoute } - '/_layout/_escaped-index/': { - id: '/_layout/_escaped-index/' - path: '/_escaped-index' - fullPath: '/_escaped-index/' - preLoaderRoute: typeof escapedIndexRouteImport + '/_layout/_nested-index/$id': { + id: '/_layout/_nested-index/$id' + path: '/_nested-index/$id' + fullPath: '/_nested-index/$id' + preLoaderRoute: typeof nestedIdRouteImport parentRoute: typeof layoutRoute } - '/_layout/__double-index/': { - id: '/_layout/__double-index/' - path: '/__double-index' - fullPath: '/__double-index/' - preLoaderRoute: typeof doubleIndexRouteImport + '/_layout/_nested/_baz': { + id: '/_layout/_nested/_baz' + path: '/_baz' + fullPath: '/_baz' + preLoaderRoute: typeof bazRouteImport + parentRoute: typeof nestedLayoutRoute + } + '/_layout/trail_/': { + id: '/_layout/trail_/' + path: '/trail_' + fullPath: '/trail_/' + preLoaderRoute: typeof trailIndexRouteImport parentRoute: typeof layoutRoute } '/_layout/_inner/_deep-index/': { diff --git a/packages/router-generator/tests/generator/virtual-physical-empty-path-merge/routeTree.snapshot.ts b/packages/router-generator/tests/generator/virtual-physical-empty-path-merge/routeTree.snapshot.ts index 6de1ed6975..eeb3c71359 100644 --- a/packages/router-generator/tests/generator/virtual-physical-empty-path-merge/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/virtual-physical-empty-path-merge/routeTree.snapshot.ts @@ -9,13 +9,13 @@ // 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 ContactRouteImport } from './routes/merged/contact' -import { Route as aboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/merged/index' +import { Route as aboutRouteImport } from './routes/about' +import { Route as ContactRouteImport } from './routes/merged/contact' -const ContactRoute = ContactRouteImport.update({ - id: '/contact', - path: '/contact', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const aboutRoute = aboutRouteImport.update({ @@ -23,9 +23,9 @@ const aboutRoute = aboutRouteImport.update({ path: '/about', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const ContactRoute = ContactRouteImport.update({ + id: '/contact', + path: '/contact', getParentRoute: () => rootRouteImport, } as any) @@ -61,11 +61,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/contact': { - id: '/contact' - path: '/contact' - fullPath: '/contact' - preLoaderRoute: typeof ContactRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/about': { @@ -75,11 +75,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof aboutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/contact': { + id: '/contact' + path: '/contact' + fullPath: '/contact' + preLoaderRoute: typeof ContactRouteImport parentRoute: typeof rootRouteImport } } diff --git a/packages/router-generator/tests/generator/virtual-physical-layout-and-index/routeTree.snapshot.ts b/packages/router-generator/tests/generator/virtual-physical-layout-and-index/routeTree.snapshot.ts index bade75773c..e8cddcbc9c 100644 --- a/packages/router-generator/tests/generator/virtual-physical-layout-and-index/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/virtual-physical-layout-and-index/routeTree.snapshot.ts @@ -9,20 +9,20 @@ // 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 FeatureRouteRouteImport } from './routes/feature/route' import { Route as indexRouteImport } from './routes/index' +import { Route as FeatureRouteRouteImport } from './routes/feature/route' import { Route as FeatureIndexRouteImport } from './routes/feature/index' -const FeatureRouteRoute = FeatureRouteRouteImport.update({ - id: '/feature', - path: '/feature', - getParentRoute: () => rootRouteImport, -} as any) const indexRoute = indexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const FeatureRouteRoute = FeatureRouteRouteImport.update({ + id: '/feature', + path: '/feature', + getParentRoute: () => rootRouteImport, +} as any) const FeatureIndexRoute = FeatureIndexRouteImport.update({ id: '/', path: '/', @@ -59,13 +59,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/feature': { - id: '/feature' - path: '/feature' - fullPath: '/feature' - preLoaderRoute: typeof FeatureRouteRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -73,6 +66,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof indexRouteImport parentRoute: typeof rootRouteImport } + '/feature': { + id: '/feature' + path: '/feature' + fullPath: '/feature' + preLoaderRoute: typeof FeatureRouteRouteImport + parentRoute: typeof rootRouteImport + } '/feature/': { id: '/feature/' path: '/' diff --git a/packages/router-generator/tests/generator/virtual-physical-no-prefix/routeTree.snapshot.ts b/packages/router-generator/tests/generator/virtual-physical-no-prefix/routeTree.snapshot.ts index 6de1ed6975..eeb3c71359 100644 --- a/packages/router-generator/tests/generator/virtual-physical-no-prefix/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/virtual-physical-no-prefix/routeTree.snapshot.ts @@ -9,13 +9,13 @@ // 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 ContactRouteImport } from './routes/merged/contact' -import { Route as aboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/merged/index' +import { Route as aboutRouteImport } from './routes/about' +import { Route as ContactRouteImport } from './routes/merged/contact' -const ContactRoute = ContactRouteImport.update({ - id: '/contact', - path: '/contact', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const aboutRoute = aboutRouteImport.update({ @@ -23,9 +23,9 @@ const aboutRoute = aboutRouteImport.update({ path: '/about', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const ContactRoute = ContactRouteImport.update({ + id: '/contact', + path: '/contact', getParentRoute: () => rootRouteImport, } as any) @@ -61,11 +61,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/contact': { - id: '/contact' - path: '/contact' - fullPath: '/contact' - preLoaderRoute: typeof ContactRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/about': { @@ -75,11 +75,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof aboutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/contact': { + id: '/contact' + path: '/contact' + fullPath: '/contact' + preLoaderRoute: typeof ContactRouteImport parentRoute: typeof rootRouteImport } } diff --git a/packages/router-generator/tests/generator/virtual-with-escaped-underscore/routeTree.snapshot.ts b/packages/router-generator/tests/generator/virtual-with-escaped-underscore/routeTree.snapshot.ts index 8eed55a7fe..18d7439610 100644 --- a/packages/router-generator/tests/generator/virtual-with-escaped-underscore/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/virtual-with-escaped-underscore/routeTree.snapshot.ts @@ -11,8 +11,8 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as indexRouteImport } from './routes/index' import { Route as ApiIndexRouteImport } from './routes/physical-routes/index' -import { Route as ApiChar91_Char93helloRouteImport } from './routes/physical-routes/[_]hello' import { Route as ApiChar91_Char93authDotrouteRouteImport } from './routes/physical-routes/[_]auth.route' +import { Route as ApiChar91_Char93helloRouteImport } from './routes/physical-routes/[_]hello' const indexRoute = indexRouteImport.update({ id: '/', @@ -24,17 +24,17 @@ const ApiIndexRoute = ApiIndexRouteImport.update({ path: '/api/', getParentRoute: () => rootRouteImport, } as any) -const ApiChar91_Char93helloRoute = ApiChar91_Char93helloRouteImport.update({ - id: '/api/_hello', - path: '/api/_hello', - getParentRoute: () => rootRouteImport, -} as any) const ApiChar91_Char93authDotrouteRoute = ApiChar91_Char93authDotrouteRouteImport.update({ id: '/api/_auth', path: '/api/_auth', getParentRoute: () => rootRouteImport, } as any) +const ApiChar91_Char93helloRoute = ApiChar91_Char93helloRouteImport.update({ + id: '/api/_hello', + path: '/api/_hello', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof indexRoute @@ -86,13 +86,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ApiIndexRouteImport parentRoute: typeof rootRouteImport } - '/api/_hello': { - id: '/api/_hello' - path: '/api/_hello' - fullPath: '/api/_hello' - preLoaderRoute: typeof ApiChar91_Char93helloRouteImport - parentRoute: typeof rootRouteImport - } '/api/_auth': { id: '/api/_auth' path: '/api/_auth' @@ -100,6 +93,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ApiChar91_Char93authDotrouteRouteImport parentRoute: typeof rootRouteImport } + '/api/_hello': { + id: '/api/_hello' + path: '/api/_hello' + fullPath: '/api/_hello' + preLoaderRoute: typeof ApiChar91_Char93helloRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/packages/router-generator/tests/generator/virtual/routeTree.snapshot.ts b/packages/router-generator/tests/generator/virtual/routeTree.snapshot.ts index 3d3c2de9d5..6b16924d5b 100644 --- a/packages/router-generator/tests/generator/virtual/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/virtual/routeTree.snapshot.ts @@ -9,51 +9,61 @@ // 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 layoutRouteImport } from './routes/layout' import { Route as indexRouteImport } from './routes/index' -import { Route as dbDashboardRouteImport } from './routes/db/dashboard' +import { Route as layoutRouteImport } from './routes/layout' import { Route as pagesRouteImport } from './routes/pages' -import { Route as HelloIndexRouteImport } from './routes/subtree/index' -import { Route as dbDashboardInvoicesRouteImport } from './routes/db/dashboard-invoices' +import { Route as dbDashboardRouteImport } from './routes/db/dashboard' import { Route as dbDashboardIndexRouteImport } from './routes/db/dashboard-index' +import { Route as dbDashboardInvoicesRouteImport } from './routes/db/dashboard-invoices' +import { Route as HelloIndexRouteImport } from './routes/subtree/index' +import { Route as dbInvoicesIndexRouteImport } from './routes/db/invoices-index' +import { Route as dbInvoiceDetailRouteImport } from './routes/db/invoice-detail' import { Route as HelloFooIndexRouteImport } from './routes/subtree/foo/index' import { Route as HelloFooIdRouteImport } from './routes/subtree/foo/$id' -import { Route as dbInvoiceDetailRouteImport } from './routes/db/invoice-detail' -import { Route as dbInvoicesIndexRouteImport } from './routes/db/invoices-index' -const layoutRoute = layoutRouteImport.update({ - id: '/_layout', - getParentRoute: () => rootRouteImport, -} as any) const indexRoute = indexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) -const dbDashboardRoute = dbDashboardRouteImport.update({ - id: '/dashboard', - path: '/dashboard', - getParentRoute: () => layoutRoute, +const layoutRoute = layoutRouteImport.update({ + id: '/_layout', + getParentRoute: () => rootRouteImport, } as any) const pagesRoute = pagesRouteImport.update({ id: '/$lang/', path: '/$lang/', getParentRoute: () => rootRouteImport, } as any) -const HelloIndexRoute = HelloIndexRouteImport.update({ - id: '/hello/', - path: '/hello/', +const dbDashboardRoute = dbDashboardRouteImport.update({ + id: '/dashboard', + path: '/dashboard', getParentRoute: () => layoutRoute, } as any) +const dbDashboardIndexRoute = dbDashboardIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => dbDashboardRoute, +} as any) const dbDashboardInvoicesRoute = dbDashboardInvoicesRouteImport.update({ id: '/invoices', path: '/invoices', getParentRoute: () => dbDashboardRoute, } as any) -const dbDashboardIndexRoute = dbDashboardIndexRouteImport.update({ +const HelloIndexRoute = HelloIndexRouteImport.update({ + id: '/hello/', + path: '/hello/', + getParentRoute: () => layoutRoute, +} as any) +const dbInvoicesIndexRoute = dbInvoicesIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => dbDashboardRoute, + getParentRoute: () => dbDashboardInvoicesRoute, +} as any) +const dbInvoiceDetailRoute = dbInvoiceDetailRouteImport.update({ + id: '/$id', + path: '/$id', + getParentRoute: () => dbDashboardInvoicesRoute, } as any) const HelloFooIndexRoute = HelloFooIndexRouteImport.update({ id: '/hello/foo/', @@ -65,16 +75,6 @@ const HelloFooIdRoute = HelloFooIdRouteImport.update({ path: '/hello/foo/$id', getParentRoute: () => layoutRoute, } as any) -const dbInvoiceDetailRoute = dbInvoiceDetailRouteImport.update({ - id: '/$id', - path: '/$id', - getParentRoute: () => dbDashboardInvoicesRoute, -} as any) -const dbInvoicesIndexRoute = dbInvoicesIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => dbDashboardInvoicesRoute, -} as any) export interface FileRoutesByFullPath { '/': typeof indexRoute @@ -158,13 +158,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/_layout': { - id: '/_layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof layoutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -172,12 +165,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof indexRouteImport parentRoute: typeof rootRouteImport } - '/_layout/dashboard': { - id: '/_layout/dashboard' - path: '/dashboard' - fullPath: '/dashboard' - preLoaderRoute: typeof dbDashboardRouteImport - parentRoute: typeof layoutRoute + '/_layout': { + id: '/_layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof layoutRouteImport + parentRoute: typeof rootRouteImport } '/$lang/': { id: '/$lang/' @@ -186,13 +179,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof pagesRouteImport parentRoute: typeof rootRouteImport } - '/_layout/hello/': { - id: '/_layout/hello/' - path: '/hello' - fullPath: '/hello/' - preLoaderRoute: typeof HelloIndexRouteImport + '/_layout/dashboard': { + id: '/_layout/dashboard' + path: '/dashboard' + fullPath: '/dashboard' + preLoaderRoute: typeof dbDashboardRouteImport parentRoute: typeof layoutRoute } + '/_layout/dashboard/': { + id: '/_layout/dashboard/' + path: '/' + fullPath: '/dashboard/' + preLoaderRoute: typeof dbDashboardIndexRouteImport + parentRoute: typeof dbDashboardRoute + } '/_layout/dashboard/invoices': { id: '/_layout/dashboard/invoices' path: '/invoices' @@ -200,12 +200,26 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof dbDashboardInvoicesRouteImport parentRoute: typeof dbDashboardRoute } - '/_layout/dashboard/': { - id: '/_layout/dashboard/' + '/_layout/hello/': { + id: '/_layout/hello/' + path: '/hello' + fullPath: '/hello/' + preLoaderRoute: typeof HelloIndexRouteImport + parentRoute: typeof layoutRoute + } + '/_layout/dashboard/invoices/': { + id: '/_layout/dashboard/invoices/' path: '/' - fullPath: '/dashboard/' - preLoaderRoute: typeof dbDashboardIndexRouteImport - parentRoute: typeof dbDashboardRoute + fullPath: '/dashboard/invoices/' + preLoaderRoute: typeof dbInvoicesIndexRouteImport + parentRoute: typeof dbDashboardInvoicesRoute + } + '/_layout/dashboard/invoices/$id': { + id: '/_layout/dashboard/invoices/$id' + path: '/$id' + fullPath: '/dashboard/invoices/$id' + preLoaderRoute: typeof dbInvoiceDetailRouteImport + parentRoute: typeof dbDashboardInvoicesRoute } '/_layout/hello/foo/': { id: '/_layout/hello/foo/' @@ -221,20 +235,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof HelloFooIdRouteImport parentRoute: typeof layoutRoute } - '/_layout/dashboard/invoices/$id': { - id: '/_layout/dashboard/invoices/$id' - path: '/$id' - fullPath: '/dashboard/invoices/$id' - preLoaderRoute: typeof dbInvoiceDetailRouteImport - parentRoute: typeof dbDashboardInvoicesRoute - } - '/_layout/dashboard/invoices/': { - id: '/_layout/dashboard/invoices/' - path: '/' - fullPath: '/dashboard/invoices/' - preLoaderRoute: typeof dbInvoicesIndexRouteImport - parentRoute: typeof dbDashboardInvoicesRoute - } } } diff --git a/packages/router-generator/tests/utils.test.ts b/packages/router-generator/tests/utils.test.ts index 0cdce3cfae..1d04d6b90d 100644 --- a/packages/router-generator/tests/utils.test.ts +++ b/packages/router-generator/tests/utils.test.ts @@ -6,6 +6,7 @@ import { createRoutePathSegmentMetadata, createRouteNodesByFullPath, createRouteNodesByTo, + createTokenRegex, determineInitialRoutePath, hasEscapedLeadingUnderscore, hasEscapedTrailingUnderscore, @@ -21,6 +22,7 @@ import { removeUnderscores, removeUnderscoresWithEscape, routePathToVariable, + sortRouteNodes, } from '../src/utils' import type { ImportDeclaration, RouteNode } from '../src/types' @@ -965,3 +967,85 @@ describe('RoutePrefixMap', () => { }) }) }) + +describe('sortRouteNodes', () => { + const indexTokenSegmentRegex = createTokenRegex('index', { type: 'segment' }) + + const node = (routePath: string): RouteNode => + ({ routePath }) as unknown as RouteNode + + const sortPaths = (routePaths: Array): Array => + sortRouteNodes(routePaths.map(node), indexTokenSegmentRegex).map( + (n) => n.routePath, + ) + + /** Every distinct ordering of `arr` (Heap-free recursive permutation). */ + function permutations(arr: Array): Array> { + if (arr.length <= 1) { + return [arr] + } + const result: Array> = [] + arr.forEach((item, i) => { + const rest = [...arr.slice(0, i), ...arr.slice(i + 1)] + for (const perm of permutations(rest)) { + result.push([item, ...perm]) + } + }) + return result + } + + it('orders tied siblings by routePath', () => { + // Same segment count, both non-index: they tie on every key before routePath. + const expected = ['/account/beta-features', '/account/busy-guard'] + expect( + sortPaths(['/account/busy-guard', '/account/beta-features']), + ).toEqual(expected) + expect( + sortPaths(['/account/beta-features', '/account/busy-guard']), + ).toEqual(expected) + }) + + it('applies root, segment-count and index precedence before the routePath tiebreaker', () => { + const sorted = sortPaths([ + '/posts', + '/account/busy-guard', + '/index', + '/__root', + '/account/beta-features', + '/about', + '/posts/index', + ]) + + expect(sorted).toEqual([ + '/__root', // root always first + '/index', // depth 1, index segment before non-index + '/about', // depth 1, non-index, routePath tiebreak + '/posts', // depth 1, non-index, routePath tiebreak + '/posts/index', // depth 2, index segment before non-index + '/account/beta-features', // depth 2, non-index, routePath tiebreak + '/account/busy-guard', // depth 2, non-index, routePath tiebreak + ]) + }) + + it('is deterministic for every input permutation (stable sort)', () => { + // Includes several sets that tie on all keys before routePath (the two + // `/account/*` leaves, `/about` vs `/posts`) — exactly the case that the + // former whole-object tiebreaker left in an engine- and input-order- + // dependent order. `sortRouteNodes` must map every permutation to the + // same output. + const routePaths = [ + '/__root', + '/index', + '/about', + '/posts', + '/account/beta-features', + '/account/busy-guard', + ] + + const reference = sortPaths(routePaths) + + for (const permutation of permutations(routePaths)) { + expect(sortPaths(permutation)).toEqual(reference) + } + }) +}) From 604996d14c991a44e38849778c6dc75e0a86665d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 18 Jul 2026 00:25:19 +0200 Subject: [PATCH 14/51] ci: Version Packages (#7841) --- .changeset/deterministic-route-tree-sort.md | 5 ----- examples/react/authenticated-routes-firebase/package.json | 2 +- examples/react/authenticated-routes/package.json | 2 +- examples/react/basic-file-based/package.json | 2 +- examples/react/basic-react-query-file-based/package.json | 2 +- examples/react/basic-ssr-file-based/package.json | 2 +- .../react/basic-ssr-streaming-file-based/package.json | 2 +- examples/react/basic-virtual-file-based/package.json | 2 +- .../react/basic-virtual-inside-file-based/package.json | 2 +- examples/react/i18n-paraglide/package.json | 2 +- examples/react/kitchen-sink-file-based/package.json | 2 +- .../kitchen-sink-react-query-file-based/package.json | 2 +- examples/react/large-file-based/package.json | 2 +- examples/react/quickstart-esbuild-file-based/package.json | 2 +- examples/react/quickstart-file-based/package.json | 2 +- examples/react/quickstart-rspack-file-based/package.json | 2 +- examples/react/quickstart-webpack-file-based/package.json | 2 +- examples/react/router-monorepo-react-query/package.json | 2 +- .../packages/router/package.json | 2 +- examples/react/router-monorepo-simple-lazy/package.json | 2 +- .../packages/router/package.json | 2 +- examples/react/router-monorepo-simple/package.json | 2 +- .../router-monorepo-simple/packages/router/package.json | 2 +- examples/react/search-validator-adapters/package.json | 2 +- examples/react/start-bare/package.json | 2 +- examples/react/start-basic-auth/package.json | 2 +- examples/react/start-basic-authjs/package.json | 2 +- examples/react/start-basic-cloudflare/package.json | 2 +- examples/react/start-basic-react-query/package.json | 2 +- examples/react/start-basic-static/package.json | 2 +- examples/react/start-basic/package.json | 2 +- examples/react/start-bun/package.json | 4 ++-- examples/react/start-clerk-basic/package.json | 2 +- examples/react/start-convex-trellaux/package.json | 2 +- examples/react/start-counter/package.json | 2 +- examples/react/start-i18n-paraglide/package.json | 2 +- examples/react/start-large/package.json | 2 +- examples/react/start-material-ui/package.json | 2 +- examples/react/start-rscs/package.json | 2 +- .../package.json | 2 +- examples/react/start-supabase-basic/package.json | 2 +- examples/react/start-tailwind-v4/package.json | 2 +- examples/react/start-trellaux/package.json | 2 +- examples/react/start-workos/package.json | 2 +- examples/react/view-transitions/package.json | 2 +- examples/react/with-trpc-react-query/package.json | 2 +- examples/react/with-trpc/package.json | 2 +- examples/solid/authenticated-routes-firebase/package.json | 2 +- examples/solid/authenticated-routes/package.json | 2 +- examples/solid/basic-file-based/package.json | 2 +- examples/solid/basic-solid-query-file-based/package.json | 2 +- examples/solid/basic-solid-query/package.json | 2 +- examples/solid/basic-ssr-file-based/package.json | 2 +- .../solid/basic-ssr-streaming-file-based/package.json | 2 +- examples/solid/basic-virtual-file-based/package.json | 2 +- .../solid/basic-virtual-inside-file-based/package.json | 2 +- examples/solid/i18n-paraglide/package.json | 2 +- examples/solid/kitchen-sink-file-based/package.json | 2 +- .../kitchen-sink-solid-query-file-based/package.json | 2 +- examples/solid/large-file-based/package.json | 2 +- examples/solid/quickstart-esbuild-file-based/package.json | 2 +- examples/solid/quickstart-file-based/package.json | 2 +- examples/solid/quickstart-rspack-file-based/package.json | 2 +- examples/solid/quickstart-webpack-file-based/package.json | 2 +- examples/solid/router-monorepo-simple-lazy/package.json | 2 +- .../packages/router/package.json | 2 +- examples/solid/router-monorepo-simple/package.json | 2 +- .../router-monorepo-simple/packages/router/package.json | 2 +- examples/solid/router-monorepo-solid-query/package.json | 2 +- .../packages/router/package.json | 2 +- examples/solid/search-validator-adapters/package.json | 2 +- examples/solid/start-basic-auth/package.json | 2 +- examples/solid/start-basic-authjs/package.json | 2 +- examples/solid/start-basic-cloudflare/package.json | 2 +- examples/solid/start-basic-netlify/package.json | 2 +- examples/solid/start-basic-nitro/package.json | 2 +- examples/solid/start-basic-solid-query/package.json | 2 +- examples/solid/start-basic-static/package.json | 2 +- examples/solid/start-basic/package.json | 2 +- examples/solid/start-bun/package.json | 4 ++-- examples/solid/start-convex-better-auth/package.json | 2 +- examples/solid/start-counter/package.json | 2 +- examples/solid/start-i18n-paraglide/package.json | 2 +- examples/solid/start-large/package.json | 2 +- .../package.json | 2 +- examples/solid/start-supabase-basic/package.json | 2 +- examples/solid/start-tailwind-v4/package.json | 2 +- examples/solid/view-transitions/package.json | 2 +- examples/solid/with-trpc/package.json | 2 +- examples/vue/basic-file-based-jsx/package.json | 2 +- examples/vue/basic-file-based-sfc/package.json | 2 +- packages/react-start-rsc/CHANGELOG.md | 7 +++++++ packages/react-start-rsc/package.json | 2 +- packages/react-start/CHANGELOG.md | 8 ++++++++ packages/react-start/package.json | 2 +- packages/router-cli/CHANGELOG.md | 7 +++++++ packages/router-cli/package.json | 2 +- packages/router-generator/CHANGELOG.md | 6 ++++++ packages/router-generator/package.json | 2 +- packages/router-plugin/CHANGELOG.md | 7 +++++++ packages/router-plugin/package.json | 2 +- packages/router-vite-plugin/CHANGELOG.md | 7 +++++++ packages/router-vite-plugin/package.json | 2 +- packages/solid-start/CHANGELOG.md | 7 +++++++ packages/solid-start/package.json | 2 +- packages/start-plugin-core/CHANGELOG.md | 8 ++++++++ packages/start-plugin-core/package.json | 2 +- packages/vue-start/CHANGELOG.md | 7 +++++++ packages/vue-start/package.json | 2 +- 109 files changed, 165 insertions(+), 106 deletions(-) delete mode 100644 .changeset/deterministic-route-tree-sort.md diff --git a/.changeset/deterministic-route-tree-sort.md b/.changeset/deterministic-route-tree-sort.md deleted file mode 100644 index ab95de9593..0000000000 --- a/.changeset/deterministic-route-tree-sort.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@tanstack/router-generator': patch ---- - -Make `buildRouteTree` route ordering deterministic. Its final sort tiebreaker compared whole route-node objects (`(d) => d`), which coerce to `"[object Object]"` and so never order — an inconsistent comparator that left routes tying on segment-count/index-token in an engine- and input-dependent order, producing non-deterministic `routeTree.gen.ts` diffs across machines. The tiebreaker now compares `routePath`, mirroring the pre-sort and giving a stable total order. diff --git a/examples/react/authenticated-routes-firebase/package.json b/examples/react/authenticated-routes-firebase/package.json index ed0c97607c..05af0b2ac6 100644 --- a/examples/react/authenticated-routes-firebase/package.json +++ b/examples/react/authenticated-routes-firebase/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "firebase": "^11.4.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/authenticated-routes/package.json b/examples/react/authenticated-routes/package.json index b47b6a38a2..c2a85fc56f 100644 --- a/examples/react/authenticated-routes/package.json +++ b/examples/react/authenticated-routes/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-file-based/package.json b/examples/react/basic-file-based/package.json index 7aecf29cce..6379947a35 100644 --- a/examples/react/basic-file-based/package.json +++ b/examples/react/basic-file-based/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-react-query-file-based/package.json b/examples/react/basic-react-query-file-based/package.json index e0fa7c1a36..a073859954 100644 --- a/examples/react/basic-react-query-file-based/package.json +++ b/examples/react/basic-react-query-file-based/package.json @@ -14,7 +14,7 @@ "@tanstack/react-query-devtools": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-ssr-file-based/package.json b/examples/react/basic-ssr-file-based/package.json index c7f7da2e0c..24081f0753 100644 --- a/examples/react/basic-ssr-file-based/package.json +++ b/examples/react/basic-ssr-file-based/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "compression": "^1.8.0", "express": "^5.2.1", "get-port": "^7.1.0", diff --git a/examples/react/basic-ssr-streaming-file-based/package.json b/examples/react/basic-ssr-streaming-file-based/package.json index f022ebb718..fdd7ccc361 100644 --- a/examples/react/basic-ssr-streaming-file-based/package.json +++ b/examples/react/basic-ssr-streaming-file-based/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "compression": "^1.8.0", "express": "^5.2.1", "get-port": "^7.1.0", diff --git a/examples/react/basic-virtual-file-based/package.json b/examples/react/basic-virtual-file-based/package.json index 7cf590ef31..d1e54fd89b 100644 --- a/examples/react/basic-virtual-file-based/package.json +++ b/examples/react/basic-virtual-file-based/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "@tanstack/virtual-file-routes": "^1.162.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/basic-virtual-inside-file-based/package.json b/examples/react/basic-virtual-inside-file-based/package.json index 255facb7ff..a8c46f07ed 100644 --- a/examples/react/basic-virtual-inside-file-based/package.json +++ b/examples/react/basic-virtual-inside-file-based/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "@tanstack/virtual-file-routes": "^1.162.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/i18n-paraglide/package.json b/examples/react/i18n-paraglide/package.json index 55079cfebb..3e61003b5a 100644 --- a/examples/react/i18n-paraglide/package.json +++ b/examples/react/i18n-paraglide/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "react": "^19.1.1", "react-dom": "^19.1.1", "tailwindcss": "^4.2.2" diff --git a/examples/react/kitchen-sink-file-based/package.json b/examples/react/kitchen-sink-file-based/package.json index 989367745e..afa3fc1817 100644 --- a/examples/react/kitchen-sink-file-based/package.json +++ b/examples/react/kitchen-sink-file-based/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "immer": "^10.1.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/kitchen-sink-react-query-file-based/package.json b/examples/react/kitchen-sink-react-query-file-based/package.json index 378ad424c4..47f1f26d7b 100644 --- a/examples/react/kitchen-sink-react-query-file-based/package.json +++ b/examples/react/kitchen-sink-react-query-file-based/package.json @@ -14,7 +14,7 @@ "@tanstack/react-query-devtools": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "immer": "^10.1.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/large-file-based/package.json b/examples/react/large-file-based/package.json index e57ed59fbc..6fcdce4181 100644 --- a/examples/react/large-file-based/package.json +++ b/examples/react/large-file-based/package.json @@ -15,7 +15,7 @@ "@tanstack/react-query": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/quickstart-esbuild-file-based/package.json b/examples/react/quickstart-esbuild-file-based/package.json index 57203d5eec..952a0bbbda 100644 --- a/examples/react/quickstart-esbuild-file-based/package.json +++ b/examples/react/quickstart-esbuild-file-based/package.json @@ -11,7 +11,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/quickstart-file-based/package.json b/examples/react/quickstart-file-based/package.json index f89da750cc..8dc03b2a2a 100644 --- a/examples/react/quickstart-file-based/package.json +++ b/examples/react/quickstart-file-based/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/quickstart-rspack-file-based/package.json b/examples/react/quickstart-rspack-file-based/package.json index 5ae8e4e737..051fcf5c2d 100644 --- a/examples/react/quickstart-rspack-file-based/package.json +++ b/examples/react/quickstart-rspack-file-based/package.json @@ -19,7 +19,7 @@ "devDependencies": { "@rsbuild/core": "^2.0.11", "@rsbuild/plugin-react": "^2.0.0", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "typescript": "^6.0.2" diff --git a/examples/react/quickstart-webpack-file-based/package.json b/examples/react/quickstart-webpack-file-based/package.json index d5a729b1ff..44c48a77d5 100644 --- a/examples/react/quickstart-webpack-file-based/package.json +++ b/examples/react/quickstart-webpack-file-based/package.json @@ -14,7 +14,7 @@ }, "devDependencies": { "@swc/core": "^1.15.33", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "html-webpack-plugin": "^5.6.3", diff --git a/examples/react/router-monorepo-react-query/package.json b/examples/react/router-monorepo-react-query/package.json index b5cc722a6b..31eb791976 100644 --- a/examples/react/router-monorepo-react-query/package.json +++ b/examples/react/router-monorepo-react-query/package.json @@ -14,7 +14,7 @@ "@tanstack/react-query-devtools": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/router-monorepo-react-query/packages/router/package.json b/examples/react/router-monorepo-react-query/packages/router/package.json index 56d50596df..8ee779efd7 100644 --- a/examples/react/router-monorepo-react-query/packages/router/package.json +++ b/examples/react/router-monorepo-react-query/packages/router/package.json @@ -12,7 +12,7 @@ "@tanstack/history": "^1.162.0", "@tanstack/react-query": "^5.90.0", "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/router-monorepo-simple-lazy/package.json b/examples/react/router-monorepo-simple-lazy/package.json index e259aba770..d407da4570 100644 --- a/examples/react/router-monorepo-simple-lazy/package.json +++ b/examples/react/router-monorepo-simple-lazy/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/router-monorepo-simple-lazy/packages/router/package.json b/examples/react/router-monorepo-simple-lazy/packages/router/package.json index c9ff6bb101..f1c276851e 100644 --- a/examples/react/router-monorepo-simple-lazy/packages/router/package.json +++ b/examples/react/router-monorepo-simple-lazy/packages/router/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/history": "^1.162.0", "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/router-monorepo-simple/package.json b/examples/react/router-monorepo-simple/package.json index e126976126..1eabbf609b 100644 --- a/examples/react/router-monorepo-simple/package.json +++ b/examples/react/router-monorepo-simple/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/router-monorepo-simple/packages/router/package.json b/examples/react/router-monorepo-simple/packages/router/package.json index 0eae7a368b..d98d54505f 100644 --- a/examples/react/router-monorepo-simple/packages/router/package.json +++ b/examples/react/router-monorepo-simple/packages/router/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/history": "^1.162.0", "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/search-validator-adapters/package.json b/examples/react/search-validator-adapters/package.json index cfdeba55d5..fe2e326b30 100644 --- a/examples/react/search-validator-adapters/package.json +++ b/examples/react/search-validator-adapters/package.json @@ -15,7 +15,7 @@ "@tanstack/react-query": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "@tanstack/valibot-adapter": "^1.167.0", "@tanstack/zod-adapter": "^1.167.0", "arktype": "^2.1.7", diff --git a/examples/react/start-bare/package.json b/examples/react/start-bare/package.json index d0ff2ea336..5537ddbdaf 100644 --- a/examples/react/start-bare/package.json +++ b/examples/react/start-bare/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.28", + "@tanstack/react-start": "^1.168.29", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-basic-auth/package.json b/examples/react/start-basic-auth/package.json index 4533b4bee1..8e1ae0b1e2 100644 --- a/examples/react/start-basic-auth/package.json +++ b/examples/react/start-basic-auth/package.json @@ -16,7 +16,7 @@ "@prisma/client": "^7.0.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.28", + "@tanstack/react-start": "^1.168.29", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-basic-authjs/package.json b/examples/react/start-basic-authjs/package.json index ba1628938d..80e77e2552 100644 --- a/examples/react/start-basic-authjs/package.json +++ b/examples/react/start-basic-authjs/package.json @@ -13,7 +13,7 @@ "@auth/core": "^0.41.1", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.28", + "@tanstack/react-start": "^1.168.29", "react": "^19.0.0", "react-dom": "^19.0.0", "start-authjs": "^1.0.0", diff --git a/examples/react/start-basic-cloudflare/package.json b/examples/react/start-basic-cloudflare/package.json index 78a01c103a..5932da8fd2 100644 --- a/examples/react/start-basic-cloudflare/package.json +++ b/examples/react/start-basic-cloudflare/package.json @@ -14,7 +14,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.28", + "@tanstack/react-start": "^1.168.29", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/examples/react/start-basic-react-query/package.json b/examples/react/start-basic-react-query/package.json index a39283577f..72ec675565 100644 --- a/examples/react/start-basic-react-query/package.json +++ b/examples/react/start-basic-react-query/package.json @@ -15,7 +15,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.28", + "@tanstack/react-start": "^1.168.29", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-basic-static/package.json b/examples/react/start-basic-static/package.json index c167f5ce79..1571c0afa7 100644 --- a/examples/react/start-basic-static/package.json +++ b/examples/react/start-basic-static/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.28", + "@tanstack/react-start": "^1.168.29", "@tanstack/start-static-server-functions": "^1.167.19", "@vitejs/plugin-react": "^6.0.1", "react": "^19.0.0", diff --git a/examples/react/start-basic/package.json b/examples/react/start-basic/package.json index 9fd4e517a6..8b30c17108 100644 --- a/examples/react/start-basic/package.json +++ b/examples/react/start-basic/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.28", + "@tanstack/react-start": "^1.168.29", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.6.0", diff --git a/examples/react/start-bun/package.json b/examples/react/start-bun/package.json index c0571fea19..5bef3951a7 100644 --- a/examples/react/start-bun/package.json +++ b/examples/react/start-bun/package.json @@ -18,8 +18,8 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.28", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/react-start": "^1.168.29", + "@tanstack/router-plugin": "^1.168.21", "react": "^19.1.1", "react-dom": "^19.1.1", "tailwindcss": "^4.2.2" diff --git a/examples/react/start-clerk-basic/package.json b/examples/react/start-clerk-basic/package.json index 7aeffb6a9b..1045c7ac85 100644 --- a/examples/react/start-clerk-basic/package.json +++ b/examples/react/start-clerk-basic/package.json @@ -13,7 +13,7 @@ "@clerk/tanstack-react-start": "^0.27.14", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.28", + "@tanstack/react-start": "^1.168.29", "@vitejs/plugin-react": "^6.0.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/start-convex-trellaux/package.json b/examples/react/start-convex-trellaux/package.json index 356a7cab2d..b82d4be2b2 100644 --- a/examples/react/start-convex-trellaux/package.json +++ b/examples/react/start-convex-trellaux/package.json @@ -18,7 +18,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.28", + "@tanstack/react-start": "^1.168.29", "concurrently": "^8.2.2", "convex": "^1.19.0", "ky": "^1.7.4", diff --git a/examples/react/start-counter/package.json b/examples/react/start-counter/package.json index 03a572f79c..b6479019e1 100644 --- a/examples/react/start-counter/package.json +++ b/examples/react/start-counter/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.28", + "@tanstack/react-start": "^1.168.29", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/examples/react/start-i18n-paraglide/package.json b/examples/react/start-i18n-paraglide/package.json index b4a62068a3..e409443183 100644 --- a/examples/react/start-i18n-paraglide/package.json +++ b/examples/react/start-i18n-paraglide/package.json @@ -12,7 +12,7 @@ "@tanstack/react-devtools": "^0.7.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.28", + "@tanstack/react-start": "^1.168.29", "react": "^19.1.1", "react-dom": "^19.1.1" }, diff --git a/examples/react/start-large/package.json b/examples/react/start-large/package.json index f4822d3eba..ddc1f58041 100644 --- a/examples/react/start-large/package.json +++ b/examples/react/start-large/package.json @@ -15,7 +15,7 @@ "@tanstack/react-query": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.28", + "@tanstack/react-start": "^1.168.29", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-material-ui/package.json b/examples/react/start-material-ui/package.json index 0efed05e33..dc3d878efc 100644 --- a/examples/react/start-material-ui/package.json +++ b/examples/react/start-material-ui/package.json @@ -17,7 +17,7 @@ "@mui/material": "6.4.7", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.28", + "@tanstack/react-start": "^1.168.29", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-rscs/package.json b/examples/react/start-rscs/package.json index 8d39c00a79..6ff81f3163 100644 --- a/examples/react/start-rscs/package.json +++ b/examples/react/start-rscs/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.28", + "@tanstack/react-start": "^1.168.29", "dexie": "^4.0.10", "react": "^19.2.0", "react-dom": "^19.2.0", diff --git a/examples/react/start-streaming-data-from-server-functions/package.json b/examples/react/start-streaming-data-from-server-functions/package.json index 82ddd0726e..be2550a460 100644 --- a/examples/react/start-streaming-data-from-server-functions/package.json +++ b/examples/react/start-streaming-data-from-server-functions/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.28", + "@tanstack/react-start": "^1.168.29", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-supabase-basic/package.json b/examples/react/start-supabase-basic/package.json index c28c4c0972..0b5c361c1f 100644 --- a/examples/react/start-supabase-basic/package.json +++ b/examples/react/start-supabase-basic/package.json @@ -17,7 +17,7 @@ "@supabase/supabase-js": "^2.48.1", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.28", + "@tanstack/react-start": "^1.168.29", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/start-tailwind-v4/package.json b/examples/react/start-tailwind-v4/package.json index 840669580e..0a2798d16a 100644 --- a/examples/react/start-tailwind-v4/package.json +++ b/examples/react/start-tailwind-v4/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.28", + "@tanstack/react-start": "^1.168.29", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.6.0", diff --git a/examples/react/start-trellaux/package.json b/examples/react/start-trellaux/package.json index 58d3386253..6fb031bd69 100644 --- a/examples/react/start-trellaux/package.json +++ b/examples/react/start-trellaux/package.json @@ -15,7 +15,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.28", + "@tanstack/react-start": "^1.168.29", "ky": "^1.7.4", "msw": "^2.7.0", "react": "^19.0.0", diff --git a/examples/react/start-workos/package.json b/examples/react/start-workos/package.json index 3639294bac..276f175b2c 100644 --- a/examples/react/start-workos/package.json +++ b/examples/react/start-workos/package.json @@ -16,7 +16,7 @@ "@radix-ui/themes": "^3.3.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.28", + "@tanstack/react-start": "^1.168.29", "@workos/authkit-tanstack-react-start": "^0.5.0", "react": "^19.0.0", "react-dom": "^19.0.0" diff --git a/examples/react/view-transitions/package.json b/examples/react/view-transitions/package.json index 4170f718e9..9e3e3f97aa 100644 --- a/examples/react/view-transitions/package.json +++ b/examples/react/view-transitions/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/with-trpc-react-query/package.json b/examples/react/with-trpc-react-query/package.json index 8831056e3d..3185e82b73 100644 --- a/examples/react/with-trpc-react-query/package.json +++ b/examples/react/with-trpc-react-query/package.json @@ -15,7 +15,7 @@ "@tanstack/react-query-devtools": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "@trpc/client": "^11.4.3", "@trpc/server": "^11.4.3", "@trpc/tanstack-react-query": "^11.4.3", diff --git a/examples/react/with-trpc/package.json b/examples/react/with-trpc/package.json index 6ec641420c..9599f90f23 100644 --- a/examples/react/with-trpc/package.json +++ b/examples/react/with-trpc/package.json @@ -13,7 +13,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "@trpc/client": "^11.4.3", "@trpc/server": "^11.4.3", "express": "^5.2.1", diff --git a/examples/solid/authenticated-routes-firebase/package.json b/examples/solid/authenticated-routes-firebase/package.json index 0ed139ce36..7615d7c166 100644 --- a/examples/solid/authenticated-routes-firebase/package.json +++ b/examples/solid/authenticated-routes-firebase/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "firebase": "^11.4.0", diff --git a/examples/solid/authenticated-routes/package.json b/examples/solid/authenticated-routes/package.json index 979f85c8a9..2ad927da08 100644 --- a/examples/solid/authenticated-routes/package.json +++ b/examples/solid/authenticated-routes/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "redaxios": "^0.5.1", diff --git a/examples/solid/basic-file-based/package.json b/examples/solid/basic-file-based/package.json index b032501a85..7e8f2261cf 100644 --- a/examples/solid/basic-file-based/package.json +++ b/examples/solid/basic-file-based/package.json @@ -18,7 +18,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "typescript": "^6.0.2", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/examples/solid/basic-solid-query-file-based/package.json b/examples/solid/basic-solid-query-file-based/package.json index e70255d71e..4eab4d995b 100644 --- a/examples/solid/basic-solid-query-file-based/package.json +++ b/examples/solid/basic-solid-query-file-based/package.json @@ -21,7 +21,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "typescript": "^6.0.2", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/examples/solid/basic-solid-query/package.json b/examples/solid/basic-solid-query/package.json index a3aaeb30da..cbcc4478e2 100644 --- a/examples/solid/basic-solid-query/package.json +++ b/examples/solid/basic-solid-query/package.json @@ -19,7 +19,7 @@ "tailwindcss": "^4.2.2" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "typescript": "^6.0.2", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/examples/solid/basic-ssr-file-based/package.json b/examples/solid/basic-ssr-file-based/package.json index 6a298c782e..f30d51ce37 100644 --- a/examples/solid/basic-ssr-file-based/package.json +++ b/examples/solid/basic-ssr-file-based/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@tanstack/solid-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "compression": "^1.8.0", "express": "^5.2.1", "get-port": "^7.1.0", diff --git a/examples/solid/basic-ssr-streaming-file-based/package.json b/examples/solid/basic-ssr-streaming-file-based/package.json index 7e8e90bb09..c43737c46e 100644 --- a/examples/solid/basic-ssr-streaming-file-based/package.json +++ b/examples/solid/basic-ssr-streaming-file-based/package.json @@ -24,7 +24,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "@types/express": "^5.0.6", "typescript": "^6.0.2", "vite": "^8.0.14", diff --git a/examples/solid/basic-virtual-file-based/package.json b/examples/solid/basic-virtual-file-based/package.json index 461b72f958..8eb166bbd5 100644 --- a/examples/solid/basic-virtual-file-based/package.json +++ b/examples/solid/basic-virtual-file-based/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@tanstack/virtual-file-routes": "^1.162.0", diff --git a/examples/solid/basic-virtual-inside-file-based/package.json b/examples/solid/basic-virtual-inside-file-based/package.json index 449c620d69..42d3db9f94 100644 --- a/examples/solid/basic-virtual-inside-file-based/package.json +++ b/examples/solid/basic-virtual-inside-file-based/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@tanstack/virtual-file-routes": "^1.162.0", diff --git a/examples/solid/i18n-paraglide/package.json b/examples/solid/i18n-paraglide/package.json index a6c4136a40..15ce1b940a 100644 --- a/examples/solid/i18n-paraglide/package.json +++ b/examples/solid/i18n-paraglide/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/solid-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" }, diff --git a/examples/solid/kitchen-sink-file-based/package.json b/examples/solid/kitchen-sink-file-based/package.json index d38afe3bb5..6ec8942e0b 100644 --- a/examples/solid/kitchen-sink-file-based/package.json +++ b/examples/solid/kitchen-sink-file-based/package.json @@ -19,7 +19,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "typescript": "^6.0.2", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/examples/solid/kitchen-sink-solid-query-file-based/package.json b/examples/solid/kitchen-sink-solid-query-file-based/package.json index fe71bf13e9..99a06020a7 100644 --- a/examples/solid/kitchen-sink-solid-query-file-based/package.json +++ b/examples/solid/kitchen-sink-solid-query-file-based/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-query-devtools": "^5.90.0", "@tanstack/solid-router": "^1.170.18", diff --git a/examples/solid/large-file-based/package.json b/examples/solid/large-file-based/package.json index bfdff9eaba..e615b4540b 100644 --- a/examples/solid/large-file-based/package.json +++ b/examples/solid/large-file-based/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", diff --git a/examples/solid/quickstart-esbuild-file-based/package.json b/examples/solid/quickstart-esbuild-file-based/package.json index 71bac328eb..d45a6fa4d3 100644 --- a/examples/solid/quickstart-esbuild-file-based/package.json +++ b/examples/solid/quickstart-esbuild-file-based/package.json @@ -9,7 +9,7 @@ "start": "dev" }, "dependencies": { - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "redaxios": "^0.5.1", diff --git a/examples/solid/quickstart-file-based/package.json b/examples/solid/quickstart-file-based/package.json index 619c6d00f7..cad050a205 100644 --- a/examples/solid/quickstart-file-based/package.json +++ b/examples/solid/quickstart-file-based/package.json @@ -18,7 +18,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "typescript": "^6.0.2", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/examples/solid/quickstart-rspack-file-based/package.json b/examples/solid/quickstart-rspack-file-based/package.json index 5b80ce7428..b72881e976 100644 --- a/examples/solid/quickstart-rspack-file-based/package.json +++ b/examples/solid/quickstart-rspack-file-based/package.json @@ -19,7 +19,7 @@ "@rsbuild/core": "^2.0.11", "@rsbuild/plugin-babel": "^1.1.2", "@rsbuild/plugin-solid": "^1.1.1", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "typescript": "^6.0.2" } } diff --git a/examples/solid/quickstart-webpack-file-based/package.json b/examples/solid/quickstart-webpack-file-based/package.json index e8b66516d7..3b3f5c4368 100644 --- a/examples/solid/quickstart-webpack-file-based/package.json +++ b/examples/solid/quickstart-webpack-file-based/package.json @@ -15,7 +15,7 @@ "devDependencies": { "@babel/core": "^7.28.5", "@babel/preset-typescript": "^7.27.1", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "babel-loader": "^10.0.0", "babel-preset-solid": "^1.9.10", "css-loader": "^7.1.2", diff --git a/examples/solid/router-monorepo-simple-lazy/package.json b/examples/solid/router-monorepo-simple-lazy/package.json index ccbe70b582..ab35cc40e3 100644 --- a/examples/solid/router-monorepo-simple-lazy/package.json +++ b/examples/solid/router-monorepo-simple-lazy/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "solid-js": "^1.9.10", "redaxios": "^0.5.1" }, diff --git a/examples/solid/router-monorepo-simple-lazy/packages/router/package.json b/examples/solid/router-monorepo-simple-lazy/packages/router/package.json index d6f18e536d..da34365caf 100644 --- a/examples/solid/router-monorepo-simple-lazy/packages/router/package.json +++ b/examples/solid/router-monorepo-simple-lazy/packages/router/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/history": "^1.162.0", "@tanstack/solid-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "redaxios": "^0.5.1", "zod": "^4.4.3", "solid-js": "^1.9.10" diff --git a/examples/solid/router-monorepo-simple/package.json b/examples/solid/router-monorepo-simple/package.json index 52a1d9386a..5be4790c6d 100644 --- a/examples/solid/router-monorepo-simple/package.json +++ b/examples/solid/router-monorepo-simple/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "solid-js": "^1.9.10", "redaxios": "^0.5.1" }, diff --git a/examples/solid/router-monorepo-simple/packages/router/package.json b/examples/solid/router-monorepo-simple/packages/router/package.json index b1ac76acab..78778c90f3 100644 --- a/examples/solid/router-monorepo-simple/packages/router/package.json +++ b/examples/solid/router-monorepo-simple/packages/router/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/history": "^1.162.0", "@tanstack/solid-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "redaxios": "^0.5.1", "zod": "^4.4.3", "solid-js": "^1.9.10" diff --git a/examples/solid/router-monorepo-solid-query/package.json b/examples/solid/router-monorepo-solid-query/package.json index 9acd494517..b983702cf3 100644 --- a/examples/solid/router-monorepo-solid-query/package.json +++ b/examples/solid/router-monorepo-solid-query/package.json @@ -14,7 +14,7 @@ "@tanstack/solid-query-devtools": "^5.90.0", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "solid-js": "^1.9.10", "redaxios": "^0.5.1" }, diff --git a/examples/solid/router-monorepo-solid-query/packages/router/package.json b/examples/solid/router-monorepo-solid-query/packages/router/package.json index d96aa3ab09..fca1bdbca0 100644 --- a/examples/solid/router-monorepo-solid-query/packages/router/package.json +++ b/examples/solid/router-monorepo-solid-query/packages/router/package.json @@ -11,7 +11,7 @@ "@tanstack/history": "^1.162.0", "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "@router-solid-mono-solid-query/post-query": "workspace:*", "redaxios": "^0.5.1", "zod": "^4.4.3", diff --git a/examples/solid/search-validator-adapters/package.json b/examples/solid/search-validator-adapters/package.json index 17e74dcc7a..f88570f556 100644 --- a/examples/solid/search-validator-adapters/package.json +++ b/examples/solid/search-validator-adapters/package.json @@ -15,7 +15,7 @@ "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "@tanstack/valibot-adapter": "^1.167.0", "@tanstack/zod-adapter": "^1.167.0", "arktype": "^2.1.7", diff --git a/examples/solid/start-basic-auth/package.json b/examples/solid/start-basic-auth/package.json index 108a260b20..6cc52ae3cb 100644 --- a/examples/solid/start-basic-auth/package.json +++ b/examples/solid/start-basic-auth/package.json @@ -16,7 +16,7 @@ "@prisma/client": "^7.0.0", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.28", + "@tanstack/solid-start": "^1.168.29", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-authjs/package.json b/examples/solid/start-basic-authjs/package.json index f6fde8367f..89b9e7fc18 100644 --- a/examples/solid/start-basic-authjs/package.json +++ b/examples/solid/start-basic-authjs/package.json @@ -13,7 +13,7 @@ "@auth/core": "^0.41.1", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.28", + "@tanstack/solid-start": "^1.168.29", "solid-js": "^1.9.10", "start-authjs": "^1.0.0", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-cloudflare/package.json b/examples/solid/start-basic-cloudflare/package.json index 936fb0d20d..f219fbc2b2 100644 --- a/examples/solid/start-basic-cloudflare/package.json +++ b/examples/solid/start-basic-cloudflare/package.json @@ -14,7 +14,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.28", + "@tanstack/solid-start": "^1.168.29", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-netlify/package.json b/examples/solid/start-basic-netlify/package.json index c727191374..252a0055eb 100644 --- a/examples/solid/start-basic-netlify/package.json +++ b/examples/solid/start-basic-netlify/package.json @@ -11,7 +11,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.28", + "@tanstack/solid-start": "^1.168.29", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-nitro/package.json b/examples/solid/start-basic-nitro/package.json index a0fad2103f..0d8cdedcd1 100644 --- a/examples/solid/start-basic-nitro/package.json +++ b/examples/solid/start-basic-nitro/package.json @@ -11,7 +11,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.28", + "@tanstack/solid-start": "^1.168.29", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-solid-query/package.json b/examples/solid/start-basic-solid-query/package.json index 5fe6a67c26..9e1ebf8d1c 100644 --- a/examples/solid/start-basic-solid-query/package.json +++ b/examples/solid/start-basic-solid-query/package.json @@ -15,7 +15,7 @@ "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@tanstack/solid-router-ssr-query": "^1.167.1", - "@tanstack/solid-start": "^1.168.28", + "@tanstack/solid-start": "^1.168.29", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-static/package.json b/examples/solid/start-basic-static/package.json index 636e3cc8db..388b76bc71 100644 --- a/examples/solid/start-basic-static/package.json +++ b/examples/solid/start-basic-static/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.28", + "@tanstack/solid-start": "^1.168.29", "@tanstack/start-static-server-functions": "^1.167.19", "redaxios": "^0.5.1", "solid-js": "^1.9.10", diff --git a/examples/solid/start-basic/package.json b/examples/solid/start-basic/package.json index 2c7d7936d5..448c01f250 100644 --- a/examples/solid/start-basic/package.json +++ b/examples/solid/start-basic/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.28", + "@tanstack/solid-start": "^1.168.29", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-bun/package.json b/examples/solid/start-bun/package.json index 7ae945fa42..79e7d1c1ca 100644 --- a/examples/solid/start-bun/package.json +++ b/examples/solid/start-bun/package.json @@ -18,8 +18,8 @@ "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@tanstack/solid-router-ssr-query": "^1.167.1", - "@tanstack/solid-start": "^1.168.28", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/solid-start": "^1.168.29", + "@tanstack/router-plugin": "^1.168.21", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" }, diff --git a/examples/solid/start-convex-better-auth/package.json b/examples/solid/start-convex-better-auth/package.json index 7924c5e68d..2357d0d0f2 100644 --- a/examples/solid/start-convex-better-auth/package.json +++ b/examples/solid/start-convex-better-auth/package.json @@ -15,7 +15,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.28", + "@tanstack/solid-start": "^1.168.29", "better-auth": "^1.6.19", "clsx": "^2.1.1", "convex": "^1.28.2", diff --git a/examples/solid/start-counter/package.json b/examples/solid/start-counter/package.json index 0f5ea8e496..665573c9e7 100644 --- a/examples/solid/start-counter/package.json +++ b/examples/solid/start-counter/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.28", + "@tanstack/solid-start": "^1.168.29", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", diff --git a/examples/solid/start-i18n-paraglide/package.json b/examples/solid/start-i18n-paraglide/package.json index d81127d045..8094830240 100644 --- a/examples/solid/start-i18n-paraglide/package.json +++ b/examples/solid/start-i18n-paraglide/package.json @@ -12,7 +12,7 @@ "@tanstack/solid-devtools": "^0.7.0", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.28", + "@tanstack/solid-start": "^1.168.29", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-large/package.json b/examples/solid/start-large/package.json index 0e941745d5..0ff6cabe16 100644 --- a/examples/solid/start-large/package.json +++ b/examples/solid/start-large/package.json @@ -15,7 +15,7 @@ "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.28", + "@tanstack/solid-start": "^1.168.29", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", diff --git a/examples/solid/start-streaming-data-from-server-functions/package.json b/examples/solid/start-streaming-data-from-server-functions/package.json index 6ca3946475..9125df39ef 100644 --- a/examples/solid/start-streaming-data-from-server-functions/package.json +++ b/examples/solid/start-streaming-data-from-server-functions/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.28", + "@tanstack/solid-start": "^1.168.29", "solid-js": "^1.9.10", "zod": "^4.4.3" }, diff --git a/examples/solid/start-supabase-basic/package.json b/examples/solid/start-supabase-basic/package.json index a003a3eb47..b12a4f4afe 100644 --- a/examples/solid/start-supabase-basic/package.json +++ b/examples/solid/start-supabase-basic/package.json @@ -16,7 +16,7 @@ "@supabase/supabase-js": "^2.48.1", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.28", + "@tanstack/solid-start": "^1.168.29", "solid-js": "^1.9.9", "redaxios": "^0.5.1" }, diff --git a/examples/solid/start-tailwind-v4/package.json b/examples/solid/start-tailwind-v4/package.json index 57adb50957..44c8780b3a 100644 --- a/examples/solid/start-tailwind-v4/package.json +++ b/examples/solid/start-tailwind-v4/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.28", + "@tanstack/solid-start": "^1.168.29", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", "zod": "^4.4.3" diff --git a/examples/solid/view-transitions/package.json b/examples/solid/view-transitions/package.json index cf4631b96f..b0d724bb33 100644 --- a/examples/solid/view-transitions/package.json +++ b/examples/solid/view-transitions/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "redaxios": "^0.5.1", diff --git a/examples/solid/with-trpc/package.json b/examples/solid/with-trpc/package.json index 8b68448842..7778d9c25e 100644 --- a/examples/solid/with-trpc/package.json +++ b/examples/solid/with-trpc/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@trpc/client": "^11.4.3", diff --git a/examples/vue/basic-file-based-jsx/package.json b/examples/vue/basic-file-based-jsx/package.json index fdeefd2f88..4be4d124d6 100644 --- a/examples/vue/basic-file-based-jsx/package.json +++ b/examples/vue/basic-file-based-jsx/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "@tanstack/vue-router": "^1.170.17", "@tanstack/vue-router-devtools": "^1.167.0", "redaxios": "^0.5.1", diff --git a/examples/vue/basic-file-based-sfc/package.json b/examples/vue/basic-file-based-sfc/package.json index bef5799572..4d2ff736c2 100644 --- a/examples/vue/basic-file-based-sfc/package.json +++ b/examples/vue/basic-file-based-sfc/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.20", + "@tanstack/router-plugin": "^1.168.21", "@tanstack/vue-router": "^1.170.17", "@tanstack/vue-router-devtools": "^1.167.0", "redaxios": "^0.5.1", diff --git a/packages/react-start-rsc/CHANGELOG.md b/packages/react-start-rsc/CHANGELOG.md index 48185a4c10..450f8a2b76 100644 --- a/packages/react-start-rsc/CHANGELOG.md +++ b/packages/react-start-rsc/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/react-start-rsc +## 0.1.28 + +### Patch Changes + +- Updated dependencies []: + - @tanstack/start-plugin-core@1.171.21 + ## 0.1.27 ### Patch Changes diff --git a/packages/react-start-rsc/package.json b/packages/react-start-rsc/package.json index 6e57053107..bb4d53bfcd 100644 --- a/packages/react-start-rsc/package.json +++ b/packages/react-start-rsc/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start-rsc", - "version": "0.1.27", + "version": "0.1.28", "description": "React Server Components support for TanStack Start", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-start/CHANGELOG.md b/packages/react-start/CHANGELOG.md index 5b38d1acf0..043d1f5ce2 100644 --- a/packages/react-start/CHANGELOG.md +++ b/packages/react-start/CHANGELOG.md @@ -1,5 +1,13 @@ # @tanstack/react-start +## 1.168.29 + +### Patch Changes + +- Updated dependencies []: + - @tanstack/start-plugin-core@1.171.21 + - @tanstack/react-start-rsc@0.1.28 + ## 1.168.28 ### Patch Changes diff --git a/packages/react-start/package.json b/packages/react-start/package.json index e1c915b749..937fd3ae93 100644 --- a/packages/react-start/package.json +++ b/packages/react-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start", - "version": "1.168.28", + "version": "1.168.29", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-cli/CHANGELOG.md b/packages/router-cli/CHANGELOG.md index d0933353be..9bb43d7288 100644 --- a/packages/router-cli/CHANGELOG.md +++ b/packages/router-cli/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/router-cli +## 1.167.20 + +### Patch Changes + +- Updated dependencies [[`e56a677`](https://github.com/TanStack/router/commit/e56a67742da9021b009b8db0cdc8bfe99878c25b)]: + - @tanstack/router-generator@1.167.20 + ## 1.167.19 ### Patch Changes diff --git a/packages/router-cli/package.json b/packages/router-cli/package.json index 7bff76b1b8..b809b4a04d 100644 --- a/packages/router-cli/package.json +++ b/packages/router-cli/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-cli", - "version": "1.167.19", + "version": "1.167.20", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-generator/CHANGELOG.md b/packages/router-generator/CHANGELOG.md index 005d25c061..bd8e6dc45a 100644 --- a/packages/router-generator/CHANGELOG.md +++ b/packages/router-generator/CHANGELOG.md @@ -1,5 +1,11 @@ # @tanstack/router-generator +## 1.167.20 + +### Patch Changes + +- [#7751](https://github.com/TanStack/router/pull/7751) [`e56a677`](https://github.com/TanStack/router/commit/e56a67742da9021b009b8db0cdc8bfe99878c25b) - Make `buildRouteTree` route ordering deterministic. Its final sort tiebreaker compared whole route-node objects (`(d) => d`), which coerce to `"[object Object]"` and so never order — an inconsistent comparator that left routes tying on segment-count/index-token in an engine- and input-dependent order, producing non-deterministic `routeTree.gen.ts` diffs across machines. The tiebreaker now compares `routePath`, mirroring the pre-sort and giving a stable total order. + ## 1.167.19 ### Patch Changes diff --git a/packages/router-generator/package.json b/packages/router-generator/package.json index 3f2c64a545..5489ce4e42 100644 --- a/packages/router-generator/package.json +++ b/packages/router-generator/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-generator", - "version": "1.167.19", + "version": "1.167.20", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-plugin/CHANGELOG.md b/packages/router-plugin/CHANGELOG.md index e951de2462..4edbb54c21 100644 --- a/packages/router-plugin/CHANGELOG.md +++ b/packages/router-plugin/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/router-plugin +## 1.168.21 + +### Patch Changes + +- Updated dependencies [[`e56a677`](https://github.com/TanStack/router/commit/e56a67742da9021b009b8db0cdc8bfe99878c25b)]: + - @tanstack/router-generator@1.167.20 + ## 1.168.20 ### Patch Changes diff --git a/packages/router-plugin/package.json b/packages/router-plugin/package.json index b9a1a3fc84..196838236e 100644 --- a/packages/router-plugin/package.json +++ b/packages/router-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-plugin", - "version": "1.168.20", + "version": "1.168.21", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-vite-plugin/CHANGELOG.md b/packages/router-vite-plugin/CHANGELOG.md index accae2e5f1..83792ed1c1 100644 --- a/packages/router-vite-plugin/CHANGELOG.md +++ b/packages/router-vite-plugin/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/router-vite-plugin +## 1.167.21 + +### Patch Changes + +- Updated dependencies []: + - @tanstack/router-plugin@1.168.21 + ## 1.167.20 ### Patch Changes diff --git a/packages/router-vite-plugin/package.json b/packages/router-vite-plugin/package.json index 684a7a7f9f..b3bf3012a8 100644 --- a/packages/router-vite-plugin/package.json +++ b/packages/router-vite-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-vite-plugin", - "version": "1.167.20", + "version": "1.167.21", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/solid-start/CHANGELOG.md b/packages/solid-start/CHANGELOG.md index 22b034ef6b..9ba9f1efb5 100644 --- a/packages/solid-start/CHANGELOG.md +++ b/packages/solid-start/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/solid-start +## 1.168.29 + +### Patch Changes + +- Updated dependencies []: + - @tanstack/start-plugin-core@1.171.21 + ## 1.168.28 ### Patch Changes diff --git a/packages/solid-start/package.json b/packages/solid-start/package.json index cf7a33210f..ebb2a12f6f 100644 --- a/packages/solid-start/package.json +++ b/packages/solid-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-start", - "version": "1.168.28", + "version": "1.168.29", "description": "Modern and scalable routing for Solid applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-plugin-core/CHANGELOG.md b/packages/start-plugin-core/CHANGELOG.md index 03108f0528..9b17fb628a 100644 --- a/packages/start-plugin-core/CHANGELOG.md +++ b/packages/start-plugin-core/CHANGELOG.md @@ -1,5 +1,13 @@ # @tanstack/start-plugin-core +## 1.171.21 + +### Patch Changes + +- Updated dependencies [[`e56a677`](https://github.com/TanStack/router/commit/e56a67742da9021b009b8db0cdc8bfe99878c25b)]: + - @tanstack/router-generator@1.167.20 + - @tanstack/router-plugin@1.168.21 + ## 1.171.20 ### Patch Changes diff --git a/packages/start-plugin-core/package.json b/packages/start-plugin-core/package.json index 5cbae2851f..2934a49a82 100644 --- a/packages/start-plugin-core/package.json +++ b/packages/start-plugin-core/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-plugin-core", - "version": "1.171.20", + "version": "1.171.21", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/vue-start/CHANGELOG.md b/packages/vue-start/CHANGELOG.md index 9212ab4035..d552fb31b0 100644 --- a/packages/vue-start/CHANGELOG.md +++ b/packages/vue-start/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/vue-start +## 1.168.28 + +### Patch Changes + +- Updated dependencies []: + - @tanstack/start-plugin-core@1.171.21 + ## 1.168.27 ### Patch Changes diff --git a/packages/vue-start/package.json b/packages/vue-start/package.json index 92c9929437..4be3186cb2 100644 --- a/packages/vue-start/package.json +++ b/packages/vue-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/vue-start", - "version": "1.168.27", + "version": "1.168.28", "description": "Modern and scalable routing for Vue applications", "author": "Tanner Linsley", "license": "MIT", From 85ef32ad7b702234b6ad52e7e06ce82483939677 Mon Sep 17 00:00:00 2001 From: Sarah Gerrard Date: Fri, 17 Jul 2026 15:48:35 -0700 Subject: [PATCH 15/51] docs: refresh contribution guidelines (#7839) --- CONTRIBUTING.md | 121 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 91 insertions(+), 30 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c28d01241b..e9e793926f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,30 +1,85 @@ -# Contributing - -- Clone the repo - - `gh repo clone TanStack/router` -- Ensure `node` is installed - - https://nodejs.org/en/ -- Ensure `pnpm` is installed - - https://pnpm.io/installation - - Why? We use `pnpm` to manage workspace dependencies. It's easily the best monorepo/workspace experience available as of when this was written. -- Install dependencies - - `pnpm install` - - This installs dependencies for all of the packages in the monorepo, even examples! - - Dependencies inside of the packages and examples are automatically linked together as local/dynamic dependencies. -- Install test dependencies - - `pnpm exec playwright install` (required for e2e tests) -- Run the build or dev watcher - - `pnpm build:all` (build all packages) or - - `pnpm build` (cached build with [nx affected](https://nx.dev/nx-api/nx/documents/affected)) or - - `pnpm dev` -- Navigate to an example - - `cd examples/react/basic` -- Run the example - - `pnpm dev` -- Make changes to the code - - If you ran `pnpm dev` the dev watcher will automatically rebuild the code that has changed -- Editing the docs locally and previewing the changes - - The documentations for all the TanStack projects are hosted on [tanstack.com](https://tanstack.com), which is a TanStack Start application (https://github.com/TanStack/tanstack.com). You need to run this app locally to preview your changes in the `TanStack/router` docs. +# Contributing to TanStack Router + +TanStack Router is a community project. Contributions to both TanStack Router and TanStack Start are welcome. + +## Setup + +1. Clone the repository: + + ```sh + gh repo clone TanStack/router + ``` + +2. Install [Node.js](https://nodejs.org/en/) and [pnpm](https://pnpm.io/installation). This repository requires pnpm `11.x.x` or newer. +3. Install dependencies: + + ```sh + pnpm install + ``` + + This installs dependencies for the entire monorepo, including examples. Workspace dependencies are linked automatically. + +4. Install the Playwright browsers if you will run end-to-end tests: + + ```sh + pnpm exec playwright install + ``` + +5. Create a branch for your changes: + + ```sh + git checkout -b my-new-branch + ``` + +## Before you start + +PRs that require API changes must first be proposed as an issue and receive maintainer sign-off before implementation begins. + +## Development + +- `pnpm build:all` builds all packages. +- `pnpm build` runs the cached Nx build for affected packages. +- `pnpm dev` builds the packages and starts the development watcher. + +To run an example application, move into its directory and start its development server. For example: + +```sh +cd examples/react/basic +pnpm dev +``` + +## Testing and linting + +Run the checks relevant to your changes while developing: + +- `pnpm test:eslint` checks linting for affected packages. +- `pnpm test:types` checks types for affected packages. +- `pnpm test:unit` runs unit tests for affected packages. +- `pnpm test:build` checks builds for affected packages. +- `pnpm test:e2e` runs end-to-end tests and requires the Playwright browsers. + +Before opening a pull request, run at least: + +```sh +pnpm test:eslint +pnpm test:types +pnpm test:unit +``` + +To run the full CI test suite, use `pnpm test:ci`. For faster feedback, use an Nx target for a specific package or pass a test file or test name after `--`. For example: + +```sh +pnpm nx run @tanstack/react-router:test:unit -- tests/link.test.tsx +pnpm nx run @tanstack/react-router:test:unit -- tests/link.test.tsx -t "preloading" +``` + +Add appropriate unit tests for isolated behavior and end-to-end tests for browser or application workflows. Test relevant example applications when the change affects them. + +## Documentation + +Update the relevant documentation for user-facing changes. Documentation lives in the `docs/` directory, with separate sections for Router and Start. Use relative links within the `docs/` directory, such as `./guide/data-loading`. + +The documentation sites are hosted on [tanstack.com](https://tanstack.com), which is a [TanStack Start application](https://github.com/TanStack/tanstack.com). To preview documentation changes locally, clone both repositories as siblings: > [!NOTE] > The website fetches the doc pages from GitHub in production, and searches for them at `../router/docs` in development. Your local clone of `TanStack/router` needs to be in the same directory as the local clone of `TanStack/tanstack.com`. @@ -53,7 +108,7 @@ git clone git@github.com:TanStack/tanstack.com.git --depth=1 --single-branch --b > [!NOTE] > Your `tanstack` directory should look like this: > -> ``` +> ```text > tanstack/ > | > +-- router/ (<-- this directory cannot be called anything else!) @@ -75,6 +130,12 @@ pnpm dev > [!WARNING] > You will need to update the `docs/(router or start)config.json` file (in `TanStack/router`) if you add a new documentation page! -You can see the whole process in the screen capture below: +## AI-assisted contributions + +AI tools can be useful for exploring the codebase, drafting changes, and writing tests. They do not replace the contributor's responsibility for the result. + +1. **Keep the contribution in your own voice**. Review and rewrite generated code comments, issue reports, and pull request descriptions so they reflect what you actually mean. +2. **Keep ownership of technical decisions**. Understand every change, verify its behavior with the appropriate tests and documentation, and be ready to explain it during review. +3. **Do not paste LLM output verbatim into the codebase or a contribution**. Treat generated text and code as a draft: check its claims, adapt it to the repository, and remove anything you cannot justify. Issues or pull requests that appear to contain unreviewed LLM output may be closed without further review. -https://github.com/fulopkovacs/form/assets/43729152/9d35a3c3-8153-4e74-9cb2-af275f7a269b +AI-assisted contributions follow the same standards for correctness, testing, documentation, and review as every other contribution. From 78dd1a645dc1a9e9f4f649d9aff12005044d4fcc Mon Sep 17 00:00:00 2001 From: Manuel Schiller <6340397+schiller-manuel@users.noreply.github.com> Date: Sat, 18 Jul 2026 01:17:42 +0200 Subject: [PATCH 16/51] fix(router-generator): preserve dots in virtual routes (#7845) --- .changeset/tidy-virtual-route-dots.md | 6 ++ .../src/filesystem/virtual/getRouteNodes.ts | 14 ++- packages/router-generator/src/utils.ts | 26 +++-- .../router-generator/tests/generator.test.ts | 20 ++++ .../routeTree.snapshot.ts | 77 +++++++++++++ .../routes/direct.tsx | 5 + .../routes/physical-routes/index.tsx | 5 + .../routes/root.tsx | 3 + .../routeTree.snapshot.ts | 101 ++++++++++++++++++ .../routes/index.route.tsx | 5 + .../routes/pathless.layout.tsx | 5 + .../routes/root.route.tsx | 3 + .../routes/subpath.route.tsx | 5 + packages/router-generator/tests/utils.test.ts | 21 ++++ 14 files changed, 286 insertions(+), 10 deletions(-) create mode 100644 .changeset/tidy-virtual-route-dots.md create mode 100644 packages/router-generator/tests/generator/virtual-explicit-dotted-paths/routeTree.snapshot.ts create mode 100644 packages/router-generator/tests/generator/virtual-explicit-dotted-paths/routes/direct.tsx create mode 100644 packages/router-generator/tests/generator/virtual-explicit-dotted-paths/routes/physical-routes/index.tsx create mode 100644 packages/router-generator/tests/generator/virtual-explicit-dotted-paths/routes/root.tsx create mode 100644 packages/router-generator/tests/generator/virtual-pathless-layout-dotted-filename/routeTree.snapshot.ts create mode 100644 packages/router-generator/tests/generator/virtual-pathless-layout-dotted-filename/routes/index.route.tsx create mode 100644 packages/router-generator/tests/generator/virtual-pathless-layout-dotted-filename/routes/pathless.layout.tsx create mode 100644 packages/router-generator/tests/generator/virtual-pathless-layout-dotted-filename/routes/root.route.tsx create mode 100644 packages/router-generator/tests/generator/virtual-pathless-layout-dotted-filename/routes/subpath.route.tsx diff --git a/.changeset/tidy-virtual-route-dots.md b/.changeset/tidy-virtual-route-dots.md new file mode 100644 index 0000000000..72bbb5565b --- /dev/null +++ b/.changeset/tidy-virtual-route-dots.md @@ -0,0 +1,6 @@ +--- +'@tanstack/router-generator': patch +--- + +Preserve dots in explicit virtual route paths and pathless layout IDs instead +of treating them as flat-file route separators. diff --git a/packages/router-generator/src/filesystem/virtual/getRouteNodes.ts b/packages/router-generator/src/filesystem/virtual/getRouteNodes.ts index 509b1be321..5d3cd610be 100644 --- a/packages/router-generator/src/filesystem/virtual/getRouteNodes.ts +++ b/packages/router-generator/src/filesystem/virtual/getRouteNodes.ts @@ -3,7 +3,7 @@ import { cleanPath, createLiteralRoutePathSegmentMetadata, createRoutePathSegmentMetadata, - determineInitialRoutePath, + determineInitialRoutePathFromExplicitPath, joinRoutePathSegmentMetadata, removeExt, removeLeadingSlash, @@ -156,7 +156,9 @@ export async function getRouteNodesRecursive( routePath: routePathPrefix, originalRoutePath: originalRoutePathPrefix, } = node.pathPrefix - ? determineInitialRoutePath(removeLeadingSlash(node.pathPrefix)) + ? determineInitialRoutePathFromExplicitPath( + removeLeadingSlash(node.pathPrefix), + ) : { routePath: '', originalRoutePath: '' } const { routeNodes, physicalDirectories } = await getRouteNodesPhysical( { @@ -240,7 +242,9 @@ export async function getRouteNodesRecursive( const { routePath: escapedSegment, originalRoutePath: originalSegment, - } = determineInitialRoutePath(removeLeadingSlash(lastSegment)) + } = determineInitialRoutePathFromExplicitPath( + removeLeadingSlash(lastSegment), + ) const routePath = `${parentRoutePath}${escapedSegment}` const originalRoutePath = `${parentOriginalRoutePath}${originalSegment}` const routePathSegmentMetadata = @@ -305,7 +309,9 @@ export async function getRouteNodesRecursive( const { routePath: escapedSegment, originalRoutePath: originalSegment, - } = determineInitialRoutePath(removeLeadingSlash(lastSegment)) + } = determineInitialRoutePathFromExplicitPath( + removeLeadingSlash(lastSegment), + ) const routePath = `${parentRoutePath}${escapedSegment}` // Store the original path with brackets for escape detection const originalRoutePath = `${parentOriginalRoutePath}${originalSegment}` diff --git a/packages/router-generator/src/utils.ts b/packages/router-generator/src/utils.ts index f0a7986b1c..747980aa7b 100644 --- a/packages/router-generator/src/utils.ts +++ b/packages/router-generator/src/utils.ts @@ -186,13 +186,12 @@ const DISALLOWED_ESCAPE_CHARS = new Set([ '%', ]) -export function determineInitialRoutePath(routePath: string) { +function determineInitialRoutePathFromParts( + routePath: string, + parts: Array, +) { const originalRoutePath = - cleanPath( - `/${(cleanPath(routePath) || '').split(SPLIT_REGEX).join('/')}`, - ) || '' - - const parts = routePath.split(SPLIT_REGEX) + cleanPath(`/${cleanPath(parts.join('/')) || ''}`) || '' // Escape any characters that in square brackets // we keep the original path untouched @@ -231,6 +230,21 @@ export function determineInitialRoutePath(routePath: string) { } } +export function determineInitialRoutePath(routePath: string) { + return determineInitialRoutePathFromParts( + routePath, + routePath.split(SPLIT_REGEX), + ) +} + +/** + * Resolves bracket escapes in an explicit route path or ID without applying + * the dot-delimited flat-file route convention. + */ +export function determineInitialRoutePathFromExplicitPath(routePath: string) { + return determineInitialRoutePathFromParts(routePath, [routePath]) +} + /** * Checks if a segment is fully escaped (entirely wrapped in brackets with no nested brackets). * E.g., "[index]" -> true, "[_layout]" -> true, "foo[.]bar" -> false, "index" -> false diff --git a/packages/router-generator/tests/generator.test.ts b/packages/router-generator/tests/generator.test.ts index 79f7d505b2..3e0831fe91 100644 --- a/packages/router-generator/tests/generator.test.ts +++ b/packages/router-generator/tests/generator.test.ts @@ -166,6 +166,26 @@ function rewriteConfigByFolderName(folderName: string, config: Config) { config.virtualRouteConfig = virtualRouteConfig } break + case 'virtual-pathless-layout-dotted-filename': + { + const virtualRouteConfig = rootRoute('root.route.tsx', [ + index('index.route.tsx'), + layout('pathless.layout.tsx', [ + route('subpath', 'subpath.route.tsx'), + ]), + ]) + config.virtualRouteConfig = virtualRouteConfig + } + break + case 'virtual-explicit-dotted-paths': + { + const virtualRouteConfig = rootRoute('root.tsx', [ + route('direct.path', 'direct.tsx'), + physical('/mounted.path', 'physical-routes'), + ]) + config.virtualRouteConfig = virtualRouteConfig + } + break case 'virtual-root-sibling-routes': { // Test case for issue #5431: Virtual routes that are siblings at the root level diff --git a/packages/router-generator/tests/generator/virtual-explicit-dotted-paths/routeTree.snapshot.ts b/packages/router-generator/tests/generator/virtual-explicit-dotted-paths/routeTree.snapshot.ts new file mode 100644 index 0000000000..6a5263d55b --- /dev/null +++ b/packages/router-generator/tests/generator/virtual-explicit-dotted-paths/routeTree.snapshot.ts @@ -0,0 +1,77 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// 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 directRouteImport } from './routes/direct' +import { Route as MountedDotpathIndexRouteImport } from './routes/physical-routes/index' + +const directRoute = directRouteImport.update({ + id: '/direct.path', + path: '/direct.path', + getParentRoute: () => rootRouteImport, +} as any) +const MountedDotpathIndexRoute = MountedDotpathIndexRouteImport.update({ + id: '/mounted.path/', + path: '/mounted.path/', + getParentRoute: () => rootRouteImport, +} as any) + +export interface FileRoutesByFullPath { + '/direct.path': typeof directRoute + '/mounted.path/': typeof MountedDotpathIndexRoute +} +export interface FileRoutesByTo { + '/direct.path': typeof directRoute + '/mounted.path': typeof MountedDotpathIndexRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/direct.path': typeof directRoute + '/mounted.path/': typeof MountedDotpathIndexRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/direct.path' | '/mounted.path/' + fileRoutesByTo: FileRoutesByTo + to: '/direct.path' | '/mounted.path' + id: '__root__' | '/direct.path' | '/mounted.path/' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + directRoute: typeof directRoute + MountedDotpathIndexRoute: typeof MountedDotpathIndexRoute +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/direct.path': { + id: '/direct.path' + path: '/direct.path' + fullPath: '/direct.path' + preLoaderRoute: typeof directRouteImport + parentRoute: typeof rootRouteImport + } + '/mounted.path/': { + id: '/mounted.path/' + path: '/mounted.path' + fullPath: '/mounted.path/' + preLoaderRoute: typeof MountedDotpathIndexRouteImport + parentRoute: typeof rootRouteImport + } + } +} + +const rootRouteChildren: RootRouteChildren = { + directRoute: directRoute, + MountedDotpathIndexRoute: MountedDotpathIndexRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() diff --git a/packages/router-generator/tests/generator/virtual-explicit-dotted-paths/routes/direct.tsx b/packages/router-generator/tests/generator/virtual-explicit-dotted-paths/routes/direct.tsx new file mode 100644 index 0000000000..e56ad97d40 --- /dev/null +++ b/packages/router-generator/tests/generator/virtual-explicit-dotted-paths/routes/direct.tsx @@ -0,0 +1,5 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/direct.path')({ + component: () => 'Direct', +}) diff --git a/packages/router-generator/tests/generator/virtual-explicit-dotted-paths/routes/physical-routes/index.tsx b/packages/router-generator/tests/generator/virtual-explicit-dotted-paths/routes/physical-routes/index.tsx new file mode 100644 index 0000000000..2e768e44ac --- /dev/null +++ b/packages/router-generator/tests/generator/virtual-explicit-dotted-paths/routes/physical-routes/index.tsx @@ -0,0 +1,5 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/mounted.path/')({ + component: () => 'Mounted index', +}) diff --git a/packages/router-generator/tests/generator/virtual-explicit-dotted-paths/routes/root.tsx b/packages/router-generator/tests/generator/virtual-explicit-dotted-paths/routes/root.tsx new file mode 100644 index 0000000000..9c657c7d5b --- /dev/null +++ b/packages/router-generator/tests/generator/virtual-explicit-dotted-paths/routes/root.tsx @@ -0,0 +1,3 @@ +import { createRootRoute } from '@tanstack/react-router' + +export const Route = createRootRoute() diff --git a/packages/router-generator/tests/generator/virtual-pathless-layout-dotted-filename/routeTree.snapshot.ts b/packages/router-generator/tests/generator/virtual-pathless-layout-dotted-filename/routeTree.snapshot.ts new file mode 100644 index 0000000000..e9d9980378 --- /dev/null +++ b/packages/router-generator/tests/generator/virtual-pathless-layout-dotted-filename/routeTree.snapshot.ts @@ -0,0 +1,101 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// 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.route' +import { Route as indexDotrouteRouteImport } from './routes/index.route' +import { Route as pathlessDotlayoutRouteImport } from './routes/pathless.layout' +import { Route as subpathDotrouteRouteImport } from './routes/subpath.route' + +const indexDotrouteRoute = indexDotrouteRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) +const pathlessDotlayoutRoute = pathlessDotlayoutRouteImport.update({ + id: '/_pathless.layout', + getParentRoute: () => rootRouteImport, +} as any) +const subpathDotrouteRoute = subpathDotrouteRouteImport.update({ + id: '/subpath', + path: '/subpath', + getParentRoute: () => pathlessDotlayoutRoute, +} as any) + +export interface FileRoutesByFullPath { + '/': typeof indexDotrouteRoute + '/subpath': typeof subpathDotrouteRoute +} +export interface FileRoutesByTo { + '/': typeof indexDotrouteRoute + '/subpath': typeof subpathDotrouteRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof indexDotrouteRoute + '/_pathless.layout': typeof pathlessDotlayoutRouteWithChildren + '/_pathless.layout/subpath': typeof subpathDotrouteRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' | '/subpath' + fileRoutesByTo: FileRoutesByTo + to: '/' | '/subpath' + id: '__root__' | '/' | '/_pathless.layout' | '/_pathless.layout/subpath' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + indexDotrouteRoute: typeof indexDotrouteRoute + pathlessDotlayoutRoute: typeof pathlessDotlayoutRouteWithChildren +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof indexDotrouteRouteImport + parentRoute: typeof rootRouteImport + } + '/_pathless.layout': { + id: '/_pathless.layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof pathlessDotlayoutRouteImport + parentRoute: typeof rootRouteImport + } + '/_pathless.layout/subpath': { + id: '/_pathless.layout/subpath' + path: '/subpath' + fullPath: '/subpath' + preLoaderRoute: typeof subpathDotrouteRouteImport + parentRoute: typeof pathlessDotlayoutRoute + } + } +} + +interface pathlessDotlayoutRouteChildren { + subpathDotrouteRoute: typeof subpathDotrouteRoute +} + +const pathlessDotlayoutRouteChildren: pathlessDotlayoutRouteChildren = { + subpathDotrouteRoute: subpathDotrouteRoute, +} + +const pathlessDotlayoutRouteWithChildren = + pathlessDotlayoutRoute._addFileChildren(pathlessDotlayoutRouteChildren) + +const rootRouteChildren: RootRouteChildren = { + indexDotrouteRoute: indexDotrouteRoute, + pathlessDotlayoutRoute: pathlessDotlayoutRouteWithChildren, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() diff --git a/packages/router-generator/tests/generator/virtual-pathless-layout-dotted-filename/routes/index.route.tsx b/packages/router-generator/tests/generator/virtual-pathless-layout-dotted-filename/routes/index.route.tsx new file mode 100644 index 0000000000..379428d7b2 --- /dev/null +++ b/packages/router-generator/tests/generator/virtual-pathless-layout-dotted-filename/routes/index.route.tsx @@ -0,0 +1,5 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/')({ + component: () => 'Index', +}) diff --git a/packages/router-generator/tests/generator/virtual-pathless-layout-dotted-filename/routes/pathless.layout.tsx b/packages/router-generator/tests/generator/virtual-pathless-layout-dotted-filename/routes/pathless.layout.tsx new file mode 100644 index 0000000000..03c79de0aa --- /dev/null +++ b/packages/router-generator/tests/generator/virtual-pathless-layout-dotted-filename/routes/pathless.layout.tsx @@ -0,0 +1,5 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/_pathless.layout')({ + component: () => 'Pathless Layout', +}) diff --git a/packages/router-generator/tests/generator/virtual-pathless-layout-dotted-filename/routes/root.route.tsx b/packages/router-generator/tests/generator/virtual-pathless-layout-dotted-filename/routes/root.route.tsx new file mode 100644 index 0000000000..9c657c7d5b --- /dev/null +++ b/packages/router-generator/tests/generator/virtual-pathless-layout-dotted-filename/routes/root.route.tsx @@ -0,0 +1,3 @@ +import { createRootRoute } from '@tanstack/react-router' + +export const Route = createRootRoute() diff --git a/packages/router-generator/tests/generator/virtual-pathless-layout-dotted-filename/routes/subpath.route.tsx b/packages/router-generator/tests/generator/virtual-pathless-layout-dotted-filename/routes/subpath.route.tsx new file mode 100644 index 0000000000..6c28fa210f --- /dev/null +++ b/packages/router-generator/tests/generator/virtual-pathless-layout-dotted-filename/routes/subpath.route.tsx @@ -0,0 +1,5 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/_pathless.layout/subpath')({ + component: () => 'Subpath', +}) diff --git a/packages/router-generator/tests/utils.test.ts b/packages/router-generator/tests/utils.test.ts index 1d04d6b90d..504dacc0e6 100644 --- a/packages/router-generator/tests/utils.test.ts +++ b/packages/router-generator/tests/utils.test.ts @@ -8,6 +8,7 @@ import { createRouteNodesByTo, createTokenRegex, determineInitialRoutePath, + determineInitialRoutePathFromExplicitPath, hasEscapedLeadingUnderscore, hasEscapedTrailingUnderscore, inferFullPath, @@ -308,6 +309,26 @@ describe('determineInitialRoutePath', () => { }) }) +describe('determineInitialRoutePathFromExplicitPath', () => { + it('preserves dots as literal route path characters', () => { + expect( + determineInitialRoutePathFromExplicitPath('pathless.layout'), + ).toStrictEqual({ + routePath: '/pathless.layout', + originalRoutePath: '/pathless.layout', + }) + }) + + it('resolves bracket escapes without applying file-route separators', () => { + expect( + determineInitialRoutePathFromExplicitPath('pathless[.]layout'), + ).toStrictEqual({ + routePath: '/pathless.layout', + originalRoutePath: '/pathless[.]layout', + }) + }) +}) + describe('multiSortBy', () => { it('sorts by single accessor', () => { const arr = [{ v: 3 }, { v: 1 }, { v: 2 }] From 62a191baa068e9a2d27815cc82fb2a16690fedea Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 18 Jul 2026 02:25:47 +0200 Subject: [PATCH 17/51] ci: Version Packages (#7846) --- .changeset/tidy-virtual-route-dots.md | 6 ------ examples/react/authenticated-routes-firebase/package.json | 2 +- examples/react/authenticated-routes/package.json | 2 +- examples/react/basic-file-based/package.json | 2 +- examples/react/basic-react-query-file-based/package.json | 2 +- examples/react/basic-ssr-file-based/package.json | 2 +- .../react/basic-ssr-streaming-file-based/package.json | 2 +- examples/react/basic-virtual-file-based/package.json | 2 +- .../react/basic-virtual-inside-file-based/package.json | 2 +- examples/react/i18n-paraglide/package.json | 2 +- examples/react/kitchen-sink-file-based/package.json | 2 +- .../kitchen-sink-react-query-file-based/package.json | 2 +- examples/react/large-file-based/package.json | 2 +- examples/react/quickstart-esbuild-file-based/package.json | 2 +- examples/react/quickstart-file-based/package.json | 2 +- examples/react/quickstart-rspack-file-based/package.json | 2 +- examples/react/quickstart-webpack-file-based/package.json | 2 +- examples/react/router-monorepo-react-query/package.json | 2 +- .../packages/router/package.json | 2 +- examples/react/router-monorepo-simple-lazy/package.json | 2 +- .../packages/router/package.json | 2 +- examples/react/router-monorepo-simple/package.json | 2 +- .../router-monorepo-simple/packages/router/package.json | 2 +- examples/react/search-validator-adapters/package.json | 2 +- examples/react/start-bare/package.json | 2 +- examples/react/start-basic-auth/package.json | 2 +- examples/react/start-basic-authjs/package.json | 2 +- examples/react/start-basic-cloudflare/package.json | 2 +- examples/react/start-basic-react-query/package.json | 2 +- examples/react/start-basic-static/package.json | 2 +- examples/react/start-basic/package.json | 2 +- examples/react/start-bun/package.json | 4 ++-- examples/react/start-clerk-basic/package.json | 2 +- examples/react/start-convex-trellaux/package.json | 2 +- examples/react/start-counter/package.json | 2 +- examples/react/start-i18n-paraglide/package.json | 2 +- examples/react/start-large/package.json | 2 +- examples/react/start-material-ui/package.json | 2 +- examples/react/start-rscs/package.json | 2 +- .../package.json | 2 +- examples/react/start-supabase-basic/package.json | 2 +- examples/react/start-tailwind-v4/package.json | 2 +- examples/react/start-trellaux/package.json | 2 +- examples/react/start-workos/package.json | 2 +- examples/react/view-transitions/package.json | 2 +- examples/react/with-trpc-react-query/package.json | 2 +- examples/react/with-trpc/package.json | 2 +- examples/solid/authenticated-routes-firebase/package.json | 2 +- examples/solid/authenticated-routes/package.json | 2 +- examples/solid/basic-file-based/package.json | 2 +- examples/solid/basic-solid-query-file-based/package.json | 2 +- examples/solid/basic-solid-query/package.json | 2 +- examples/solid/basic-ssr-file-based/package.json | 2 +- .../solid/basic-ssr-streaming-file-based/package.json | 2 +- examples/solid/basic-virtual-file-based/package.json | 2 +- .../solid/basic-virtual-inside-file-based/package.json | 2 +- examples/solid/i18n-paraglide/package.json | 2 +- examples/solid/kitchen-sink-file-based/package.json | 2 +- .../kitchen-sink-solid-query-file-based/package.json | 2 +- examples/solid/large-file-based/package.json | 2 +- examples/solid/quickstart-esbuild-file-based/package.json | 2 +- examples/solid/quickstart-file-based/package.json | 2 +- examples/solid/quickstart-rspack-file-based/package.json | 2 +- examples/solid/quickstart-webpack-file-based/package.json | 2 +- examples/solid/router-monorepo-simple-lazy/package.json | 2 +- .../packages/router/package.json | 2 +- examples/solid/router-monorepo-simple/package.json | 2 +- .../router-monorepo-simple/packages/router/package.json | 2 +- examples/solid/router-monorepo-solid-query/package.json | 2 +- .../packages/router/package.json | 2 +- examples/solid/search-validator-adapters/package.json | 2 +- examples/solid/start-basic-auth/package.json | 2 +- examples/solid/start-basic-authjs/package.json | 2 +- examples/solid/start-basic-cloudflare/package.json | 2 +- examples/solid/start-basic-netlify/package.json | 2 +- examples/solid/start-basic-nitro/package.json | 2 +- examples/solid/start-basic-solid-query/package.json | 2 +- examples/solid/start-basic-static/package.json | 2 +- examples/solid/start-basic/package.json | 2 +- examples/solid/start-bun/package.json | 4 ++-- examples/solid/start-convex-better-auth/package.json | 2 +- examples/solid/start-counter/package.json | 2 +- examples/solid/start-i18n-paraglide/package.json | 2 +- examples/solid/start-large/package.json | 2 +- .../package.json | 2 +- examples/solid/start-supabase-basic/package.json | 2 +- examples/solid/start-tailwind-v4/package.json | 2 +- examples/solid/view-transitions/package.json | 2 +- examples/solid/with-trpc/package.json | 2 +- examples/vue/basic-file-based-jsx/package.json | 2 +- examples/vue/basic-file-based-sfc/package.json | 2 +- packages/react-start-rsc/CHANGELOG.md | 7 +++++++ packages/react-start-rsc/package.json | 2 +- packages/react-start/CHANGELOG.md | 8 ++++++++ packages/react-start/package.json | 2 +- packages/router-cli/CHANGELOG.md | 7 +++++++ packages/router-cli/package.json | 2 +- packages/router-generator/CHANGELOG.md | 7 +++++++ packages/router-generator/package.json | 2 +- packages/router-plugin/CHANGELOG.md | 7 +++++++ packages/router-plugin/package.json | 2 +- packages/router-vite-plugin/CHANGELOG.md | 7 +++++++ packages/router-vite-plugin/package.json | 2 +- packages/solid-start/CHANGELOG.md | 7 +++++++ packages/solid-start/package.json | 2 +- packages/start-plugin-core/CHANGELOG.md | 8 ++++++++ packages/start-plugin-core/package.json | 2 +- packages/vue-start/CHANGELOG.md | 7 +++++++ packages/vue-start/package.json | 2 +- 109 files changed, 166 insertions(+), 107 deletions(-) delete mode 100644 .changeset/tidy-virtual-route-dots.md diff --git a/.changeset/tidy-virtual-route-dots.md b/.changeset/tidy-virtual-route-dots.md deleted file mode 100644 index 72bbb5565b..0000000000 --- a/.changeset/tidy-virtual-route-dots.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@tanstack/router-generator': patch ---- - -Preserve dots in explicit virtual route paths and pathless layout IDs instead -of treating them as flat-file route separators. diff --git a/examples/react/authenticated-routes-firebase/package.json b/examples/react/authenticated-routes-firebase/package.json index 05af0b2ac6..c11599bf0b 100644 --- a/examples/react/authenticated-routes-firebase/package.json +++ b/examples/react/authenticated-routes-firebase/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "firebase": "^11.4.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/authenticated-routes/package.json b/examples/react/authenticated-routes/package.json index c2a85fc56f..89b8f7842c 100644 --- a/examples/react/authenticated-routes/package.json +++ b/examples/react/authenticated-routes/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-file-based/package.json b/examples/react/basic-file-based/package.json index 6379947a35..68b451b310 100644 --- a/examples/react/basic-file-based/package.json +++ b/examples/react/basic-file-based/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-react-query-file-based/package.json b/examples/react/basic-react-query-file-based/package.json index a073859954..c85ea7b083 100644 --- a/examples/react/basic-react-query-file-based/package.json +++ b/examples/react/basic-react-query-file-based/package.json @@ -14,7 +14,7 @@ "@tanstack/react-query-devtools": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-ssr-file-based/package.json b/examples/react/basic-ssr-file-based/package.json index 24081f0753..20bdd7f437 100644 --- a/examples/react/basic-ssr-file-based/package.json +++ b/examples/react/basic-ssr-file-based/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "compression": "^1.8.0", "express": "^5.2.1", "get-port": "^7.1.0", diff --git a/examples/react/basic-ssr-streaming-file-based/package.json b/examples/react/basic-ssr-streaming-file-based/package.json index fdd7ccc361..de2cfab761 100644 --- a/examples/react/basic-ssr-streaming-file-based/package.json +++ b/examples/react/basic-ssr-streaming-file-based/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "compression": "^1.8.0", "express": "^5.2.1", "get-port": "^7.1.0", diff --git a/examples/react/basic-virtual-file-based/package.json b/examples/react/basic-virtual-file-based/package.json index d1e54fd89b..b2009e135a 100644 --- a/examples/react/basic-virtual-file-based/package.json +++ b/examples/react/basic-virtual-file-based/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "@tanstack/virtual-file-routes": "^1.162.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/basic-virtual-inside-file-based/package.json b/examples/react/basic-virtual-inside-file-based/package.json index a8c46f07ed..591551fd44 100644 --- a/examples/react/basic-virtual-inside-file-based/package.json +++ b/examples/react/basic-virtual-inside-file-based/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "@tanstack/virtual-file-routes": "^1.162.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/i18n-paraglide/package.json b/examples/react/i18n-paraglide/package.json index 3e61003b5a..db9634854c 100644 --- a/examples/react/i18n-paraglide/package.json +++ b/examples/react/i18n-paraglide/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "react": "^19.1.1", "react-dom": "^19.1.1", "tailwindcss": "^4.2.2" diff --git a/examples/react/kitchen-sink-file-based/package.json b/examples/react/kitchen-sink-file-based/package.json index afa3fc1817..f256824590 100644 --- a/examples/react/kitchen-sink-file-based/package.json +++ b/examples/react/kitchen-sink-file-based/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "immer": "^10.1.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/kitchen-sink-react-query-file-based/package.json b/examples/react/kitchen-sink-react-query-file-based/package.json index 47f1f26d7b..62bec46301 100644 --- a/examples/react/kitchen-sink-react-query-file-based/package.json +++ b/examples/react/kitchen-sink-react-query-file-based/package.json @@ -14,7 +14,7 @@ "@tanstack/react-query-devtools": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "immer": "^10.1.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/large-file-based/package.json b/examples/react/large-file-based/package.json index 6fcdce4181..07c6eb78e3 100644 --- a/examples/react/large-file-based/package.json +++ b/examples/react/large-file-based/package.json @@ -15,7 +15,7 @@ "@tanstack/react-query": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/quickstart-esbuild-file-based/package.json b/examples/react/quickstart-esbuild-file-based/package.json index 952a0bbbda..7e9f68e0a4 100644 --- a/examples/react/quickstart-esbuild-file-based/package.json +++ b/examples/react/quickstart-esbuild-file-based/package.json @@ -11,7 +11,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/quickstart-file-based/package.json b/examples/react/quickstart-file-based/package.json index 8dc03b2a2a..ee81b51092 100644 --- a/examples/react/quickstart-file-based/package.json +++ b/examples/react/quickstart-file-based/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/quickstart-rspack-file-based/package.json b/examples/react/quickstart-rspack-file-based/package.json index 051fcf5c2d..8b3dc38116 100644 --- a/examples/react/quickstart-rspack-file-based/package.json +++ b/examples/react/quickstart-rspack-file-based/package.json @@ -19,7 +19,7 @@ "devDependencies": { "@rsbuild/core": "^2.0.11", "@rsbuild/plugin-react": "^2.0.0", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "typescript": "^6.0.2" diff --git a/examples/react/quickstart-webpack-file-based/package.json b/examples/react/quickstart-webpack-file-based/package.json index 44c48a77d5..c352ab4a88 100644 --- a/examples/react/quickstart-webpack-file-based/package.json +++ b/examples/react/quickstart-webpack-file-based/package.json @@ -14,7 +14,7 @@ }, "devDependencies": { "@swc/core": "^1.15.33", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "html-webpack-plugin": "^5.6.3", diff --git a/examples/react/router-monorepo-react-query/package.json b/examples/react/router-monorepo-react-query/package.json index 31eb791976..913a60975b 100644 --- a/examples/react/router-monorepo-react-query/package.json +++ b/examples/react/router-monorepo-react-query/package.json @@ -14,7 +14,7 @@ "@tanstack/react-query-devtools": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/router-monorepo-react-query/packages/router/package.json b/examples/react/router-monorepo-react-query/packages/router/package.json index 8ee779efd7..af2fb7288c 100644 --- a/examples/react/router-monorepo-react-query/packages/router/package.json +++ b/examples/react/router-monorepo-react-query/packages/router/package.json @@ -12,7 +12,7 @@ "@tanstack/history": "^1.162.0", "@tanstack/react-query": "^5.90.0", "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/router-monorepo-simple-lazy/package.json b/examples/react/router-monorepo-simple-lazy/package.json index d407da4570..a52d7b172e 100644 --- a/examples/react/router-monorepo-simple-lazy/package.json +++ b/examples/react/router-monorepo-simple-lazy/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/router-monorepo-simple-lazy/packages/router/package.json b/examples/react/router-monorepo-simple-lazy/packages/router/package.json index f1c276851e..544a7d8ce6 100644 --- a/examples/react/router-monorepo-simple-lazy/packages/router/package.json +++ b/examples/react/router-monorepo-simple-lazy/packages/router/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/history": "^1.162.0", "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/router-monorepo-simple/package.json b/examples/react/router-monorepo-simple/package.json index 1eabbf609b..7192cfa977 100644 --- a/examples/react/router-monorepo-simple/package.json +++ b/examples/react/router-monorepo-simple/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/router-monorepo-simple/packages/router/package.json b/examples/react/router-monorepo-simple/packages/router/package.json index d98d54505f..b762f00016 100644 --- a/examples/react/router-monorepo-simple/packages/router/package.json +++ b/examples/react/router-monorepo-simple/packages/router/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/history": "^1.162.0", "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/search-validator-adapters/package.json b/examples/react/search-validator-adapters/package.json index fe2e326b30..057205072d 100644 --- a/examples/react/search-validator-adapters/package.json +++ b/examples/react/search-validator-adapters/package.json @@ -15,7 +15,7 @@ "@tanstack/react-query": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "@tanstack/valibot-adapter": "^1.167.0", "@tanstack/zod-adapter": "^1.167.0", "arktype": "^2.1.7", diff --git a/examples/react/start-bare/package.json b/examples/react/start-bare/package.json index 5537ddbdaf..6319e85aaf 100644 --- a/examples/react/start-bare/package.json +++ b/examples/react/start-bare/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.29", + "@tanstack/react-start": "^1.168.30", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-basic-auth/package.json b/examples/react/start-basic-auth/package.json index 8e1ae0b1e2..825ebfe353 100644 --- a/examples/react/start-basic-auth/package.json +++ b/examples/react/start-basic-auth/package.json @@ -16,7 +16,7 @@ "@prisma/client": "^7.0.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.29", + "@tanstack/react-start": "^1.168.30", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-basic-authjs/package.json b/examples/react/start-basic-authjs/package.json index 80e77e2552..4896acf5df 100644 --- a/examples/react/start-basic-authjs/package.json +++ b/examples/react/start-basic-authjs/package.json @@ -13,7 +13,7 @@ "@auth/core": "^0.41.1", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.29", + "@tanstack/react-start": "^1.168.30", "react": "^19.0.0", "react-dom": "^19.0.0", "start-authjs": "^1.0.0", diff --git a/examples/react/start-basic-cloudflare/package.json b/examples/react/start-basic-cloudflare/package.json index 5932da8fd2..f60aed7be3 100644 --- a/examples/react/start-basic-cloudflare/package.json +++ b/examples/react/start-basic-cloudflare/package.json @@ -14,7 +14,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.29", + "@tanstack/react-start": "^1.168.30", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/examples/react/start-basic-react-query/package.json b/examples/react/start-basic-react-query/package.json index 72ec675565..80289dc857 100644 --- a/examples/react/start-basic-react-query/package.json +++ b/examples/react/start-basic-react-query/package.json @@ -15,7 +15,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.29", + "@tanstack/react-start": "^1.168.30", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-basic-static/package.json b/examples/react/start-basic-static/package.json index 1571c0afa7..030343a1b5 100644 --- a/examples/react/start-basic-static/package.json +++ b/examples/react/start-basic-static/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.29", + "@tanstack/react-start": "^1.168.30", "@tanstack/start-static-server-functions": "^1.167.19", "@vitejs/plugin-react": "^6.0.1", "react": "^19.0.0", diff --git a/examples/react/start-basic/package.json b/examples/react/start-basic/package.json index 8b30c17108..5b138167d8 100644 --- a/examples/react/start-basic/package.json +++ b/examples/react/start-basic/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.29", + "@tanstack/react-start": "^1.168.30", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.6.0", diff --git a/examples/react/start-bun/package.json b/examples/react/start-bun/package.json index 5bef3951a7..ce2b644227 100644 --- a/examples/react/start-bun/package.json +++ b/examples/react/start-bun/package.json @@ -18,8 +18,8 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.29", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/react-start": "^1.168.30", + "@tanstack/router-plugin": "^1.168.22", "react": "^19.1.1", "react-dom": "^19.1.1", "tailwindcss": "^4.2.2" diff --git a/examples/react/start-clerk-basic/package.json b/examples/react/start-clerk-basic/package.json index 1045c7ac85..0baf4d2ee5 100644 --- a/examples/react/start-clerk-basic/package.json +++ b/examples/react/start-clerk-basic/package.json @@ -13,7 +13,7 @@ "@clerk/tanstack-react-start": "^0.27.14", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.29", + "@tanstack/react-start": "^1.168.30", "@vitejs/plugin-react": "^6.0.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/start-convex-trellaux/package.json b/examples/react/start-convex-trellaux/package.json index b82d4be2b2..27fab4e79b 100644 --- a/examples/react/start-convex-trellaux/package.json +++ b/examples/react/start-convex-trellaux/package.json @@ -18,7 +18,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.29", + "@tanstack/react-start": "^1.168.30", "concurrently": "^8.2.2", "convex": "^1.19.0", "ky": "^1.7.4", diff --git a/examples/react/start-counter/package.json b/examples/react/start-counter/package.json index b6479019e1..3298c9d5fc 100644 --- a/examples/react/start-counter/package.json +++ b/examples/react/start-counter/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.29", + "@tanstack/react-start": "^1.168.30", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/examples/react/start-i18n-paraglide/package.json b/examples/react/start-i18n-paraglide/package.json index e409443183..12aadecf9a 100644 --- a/examples/react/start-i18n-paraglide/package.json +++ b/examples/react/start-i18n-paraglide/package.json @@ -12,7 +12,7 @@ "@tanstack/react-devtools": "^0.7.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.29", + "@tanstack/react-start": "^1.168.30", "react": "^19.1.1", "react-dom": "^19.1.1" }, diff --git a/examples/react/start-large/package.json b/examples/react/start-large/package.json index ddc1f58041..2a29d06c7e 100644 --- a/examples/react/start-large/package.json +++ b/examples/react/start-large/package.json @@ -15,7 +15,7 @@ "@tanstack/react-query": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.29", + "@tanstack/react-start": "^1.168.30", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-material-ui/package.json b/examples/react/start-material-ui/package.json index dc3d878efc..58ccf1fe80 100644 --- a/examples/react/start-material-ui/package.json +++ b/examples/react/start-material-ui/package.json @@ -17,7 +17,7 @@ "@mui/material": "6.4.7", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.29", + "@tanstack/react-start": "^1.168.30", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-rscs/package.json b/examples/react/start-rscs/package.json index 6ff81f3163..1ed16c922d 100644 --- a/examples/react/start-rscs/package.json +++ b/examples/react/start-rscs/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.29", + "@tanstack/react-start": "^1.168.30", "dexie": "^4.0.10", "react": "^19.2.0", "react-dom": "^19.2.0", diff --git a/examples/react/start-streaming-data-from-server-functions/package.json b/examples/react/start-streaming-data-from-server-functions/package.json index be2550a460..6835fb1160 100644 --- a/examples/react/start-streaming-data-from-server-functions/package.json +++ b/examples/react/start-streaming-data-from-server-functions/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.29", + "@tanstack/react-start": "^1.168.30", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-supabase-basic/package.json b/examples/react/start-supabase-basic/package.json index 0b5c361c1f..8dbc602e01 100644 --- a/examples/react/start-supabase-basic/package.json +++ b/examples/react/start-supabase-basic/package.json @@ -17,7 +17,7 @@ "@supabase/supabase-js": "^2.48.1", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.29", + "@tanstack/react-start": "^1.168.30", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/start-tailwind-v4/package.json b/examples/react/start-tailwind-v4/package.json index 0a2798d16a..d09c184032 100644 --- a/examples/react/start-tailwind-v4/package.json +++ b/examples/react/start-tailwind-v4/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.29", + "@tanstack/react-start": "^1.168.30", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.6.0", diff --git a/examples/react/start-trellaux/package.json b/examples/react/start-trellaux/package.json index 6fb031bd69..58f4b488e2 100644 --- a/examples/react/start-trellaux/package.json +++ b/examples/react/start-trellaux/package.json @@ -15,7 +15,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.29", + "@tanstack/react-start": "^1.168.30", "ky": "^1.7.4", "msw": "^2.7.0", "react": "^19.0.0", diff --git a/examples/react/start-workos/package.json b/examples/react/start-workos/package.json index 276f175b2c..df8fbb92ef 100644 --- a/examples/react/start-workos/package.json +++ b/examples/react/start-workos/package.json @@ -16,7 +16,7 @@ "@radix-ui/themes": "^3.3.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.29", + "@tanstack/react-start": "^1.168.30", "@workos/authkit-tanstack-react-start": "^0.5.0", "react": "^19.0.0", "react-dom": "^19.0.0" diff --git a/examples/react/view-transitions/package.json b/examples/react/view-transitions/package.json index 9e3e3f97aa..5ff7d5b04c 100644 --- a/examples/react/view-transitions/package.json +++ b/examples/react/view-transitions/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/with-trpc-react-query/package.json b/examples/react/with-trpc-react-query/package.json index 3185e82b73..cae3ef999c 100644 --- a/examples/react/with-trpc-react-query/package.json +++ b/examples/react/with-trpc-react-query/package.json @@ -15,7 +15,7 @@ "@tanstack/react-query-devtools": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "@trpc/client": "^11.4.3", "@trpc/server": "^11.4.3", "@trpc/tanstack-react-query": "^11.4.3", diff --git a/examples/react/with-trpc/package.json b/examples/react/with-trpc/package.json index 9599f90f23..14f95fe1f3 100644 --- a/examples/react/with-trpc/package.json +++ b/examples/react/with-trpc/package.json @@ -13,7 +13,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "@trpc/client": "^11.4.3", "@trpc/server": "^11.4.3", "express": "^5.2.1", diff --git a/examples/solid/authenticated-routes-firebase/package.json b/examples/solid/authenticated-routes-firebase/package.json index 7615d7c166..d96020f657 100644 --- a/examples/solid/authenticated-routes-firebase/package.json +++ b/examples/solid/authenticated-routes-firebase/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "firebase": "^11.4.0", diff --git a/examples/solid/authenticated-routes/package.json b/examples/solid/authenticated-routes/package.json index 2ad927da08..d6991e2b8b 100644 --- a/examples/solid/authenticated-routes/package.json +++ b/examples/solid/authenticated-routes/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "redaxios": "^0.5.1", diff --git a/examples/solid/basic-file-based/package.json b/examples/solid/basic-file-based/package.json index 7e8f2261cf..d3aa4ab8c3 100644 --- a/examples/solid/basic-file-based/package.json +++ b/examples/solid/basic-file-based/package.json @@ -18,7 +18,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "typescript": "^6.0.2", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/examples/solid/basic-solid-query-file-based/package.json b/examples/solid/basic-solid-query-file-based/package.json index 4eab4d995b..c41942917b 100644 --- a/examples/solid/basic-solid-query-file-based/package.json +++ b/examples/solid/basic-solid-query-file-based/package.json @@ -21,7 +21,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "typescript": "^6.0.2", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/examples/solid/basic-solid-query/package.json b/examples/solid/basic-solid-query/package.json index cbcc4478e2..756747e412 100644 --- a/examples/solid/basic-solid-query/package.json +++ b/examples/solid/basic-solid-query/package.json @@ -19,7 +19,7 @@ "tailwindcss": "^4.2.2" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "typescript": "^6.0.2", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/examples/solid/basic-ssr-file-based/package.json b/examples/solid/basic-ssr-file-based/package.json index f30d51ce37..22058c1745 100644 --- a/examples/solid/basic-ssr-file-based/package.json +++ b/examples/solid/basic-ssr-file-based/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@tanstack/solid-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "compression": "^1.8.0", "express": "^5.2.1", "get-port": "^7.1.0", diff --git a/examples/solid/basic-ssr-streaming-file-based/package.json b/examples/solid/basic-ssr-streaming-file-based/package.json index c43737c46e..51d14f543e 100644 --- a/examples/solid/basic-ssr-streaming-file-based/package.json +++ b/examples/solid/basic-ssr-streaming-file-based/package.json @@ -24,7 +24,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "@types/express": "^5.0.6", "typescript": "^6.0.2", "vite": "^8.0.14", diff --git a/examples/solid/basic-virtual-file-based/package.json b/examples/solid/basic-virtual-file-based/package.json index 8eb166bbd5..dfcd178efe 100644 --- a/examples/solid/basic-virtual-file-based/package.json +++ b/examples/solid/basic-virtual-file-based/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@tanstack/virtual-file-routes": "^1.162.0", diff --git a/examples/solid/basic-virtual-inside-file-based/package.json b/examples/solid/basic-virtual-inside-file-based/package.json index 42d3db9f94..d590916c05 100644 --- a/examples/solid/basic-virtual-inside-file-based/package.json +++ b/examples/solid/basic-virtual-inside-file-based/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@tanstack/virtual-file-routes": "^1.162.0", diff --git a/examples/solid/i18n-paraglide/package.json b/examples/solid/i18n-paraglide/package.json index 15ce1b940a..bfec40a2ec 100644 --- a/examples/solid/i18n-paraglide/package.json +++ b/examples/solid/i18n-paraglide/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/solid-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" }, diff --git a/examples/solid/kitchen-sink-file-based/package.json b/examples/solid/kitchen-sink-file-based/package.json index 6ec8942e0b..6c57a27ab6 100644 --- a/examples/solid/kitchen-sink-file-based/package.json +++ b/examples/solid/kitchen-sink-file-based/package.json @@ -19,7 +19,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "typescript": "^6.0.2", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/examples/solid/kitchen-sink-solid-query-file-based/package.json b/examples/solid/kitchen-sink-solid-query-file-based/package.json index 99a06020a7..e10ead122b 100644 --- a/examples/solid/kitchen-sink-solid-query-file-based/package.json +++ b/examples/solid/kitchen-sink-solid-query-file-based/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-query-devtools": "^5.90.0", "@tanstack/solid-router": "^1.170.18", diff --git a/examples/solid/large-file-based/package.json b/examples/solid/large-file-based/package.json index e615b4540b..a1d240c37e 100644 --- a/examples/solid/large-file-based/package.json +++ b/examples/solid/large-file-based/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", diff --git a/examples/solid/quickstart-esbuild-file-based/package.json b/examples/solid/quickstart-esbuild-file-based/package.json index d45a6fa4d3..8849f4a443 100644 --- a/examples/solid/quickstart-esbuild-file-based/package.json +++ b/examples/solid/quickstart-esbuild-file-based/package.json @@ -9,7 +9,7 @@ "start": "dev" }, "dependencies": { - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "redaxios": "^0.5.1", diff --git a/examples/solid/quickstart-file-based/package.json b/examples/solid/quickstart-file-based/package.json index cad050a205..c2308fcf0c 100644 --- a/examples/solid/quickstart-file-based/package.json +++ b/examples/solid/quickstart-file-based/package.json @@ -18,7 +18,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "typescript": "^6.0.2", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/examples/solid/quickstart-rspack-file-based/package.json b/examples/solid/quickstart-rspack-file-based/package.json index b72881e976..0290e9c77d 100644 --- a/examples/solid/quickstart-rspack-file-based/package.json +++ b/examples/solid/quickstart-rspack-file-based/package.json @@ -19,7 +19,7 @@ "@rsbuild/core": "^2.0.11", "@rsbuild/plugin-babel": "^1.1.2", "@rsbuild/plugin-solid": "^1.1.1", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "typescript": "^6.0.2" } } diff --git a/examples/solid/quickstart-webpack-file-based/package.json b/examples/solid/quickstart-webpack-file-based/package.json index 3b3f5c4368..a519e80916 100644 --- a/examples/solid/quickstart-webpack-file-based/package.json +++ b/examples/solid/quickstart-webpack-file-based/package.json @@ -15,7 +15,7 @@ "devDependencies": { "@babel/core": "^7.28.5", "@babel/preset-typescript": "^7.27.1", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "babel-loader": "^10.0.0", "babel-preset-solid": "^1.9.10", "css-loader": "^7.1.2", diff --git a/examples/solid/router-monorepo-simple-lazy/package.json b/examples/solid/router-monorepo-simple-lazy/package.json index ab35cc40e3..130b94021e 100644 --- a/examples/solid/router-monorepo-simple-lazy/package.json +++ b/examples/solid/router-monorepo-simple-lazy/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "solid-js": "^1.9.10", "redaxios": "^0.5.1" }, diff --git a/examples/solid/router-monorepo-simple-lazy/packages/router/package.json b/examples/solid/router-monorepo-simple-lazy/packages/router/package.json index da34365caf..f102e265cc 100644 --- a/examples/solid/router-monorepo-simple-lazy/packages/router/package.json +++ b/examples/solid/router-monorepo-simple-lazy/packages/router/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/history": "^1.162.0", "@tanstack/solid-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "redaxios": "^0.5.1", "zod": "^4.4.3", "solid-js": "^1.9.10" diff --git a/examples/solid/router-monorepo-simple/package.json b/examples/solid/router-monorepo-simple/package.json index 5be4790c6d..494e9fb7f6 100644 --- a/examples/solid/router-monorepo-simple/package.json +++ b/examples/solid/router-monorepo-simple/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "solid-js": "^1.9.10", "redaxios": "^0.5.1" }, diff --git a/examples/solid/router-monorepo-simple/packages/router/package.json b/examples/solid/router-monorepo-simple/packages/router/package.json index 78778c90f3..afbb05b627 100644 --- a/examples/solid/router-monorepo-simple/packages/router/package.json +++ b/examples/solid/router-monorepo-simple/packages/router/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/history": "^1.162.0", "@tanstack/solid-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "redaxios": "^0.5.1", "zod": "^4.4.3", "solid-js": "^1.9.10" diff --git a/examples/solid/router-monorepo-solid-query/package.json b/examples/solid/router-monorepo-solid-query/package.json index b983702cf3..63ab851a79 100644 --- a/examples/solid/router-monorepo-solid-query/package.json +++ b/examples/solid/router-monorepo-solid-query/package.json @@ -14,7 +14,7 @@ "@tanstack/solid-query-devtools": "^5.90.0", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "solid-js": "^1.9.10", "redaxios": "^0.5.1" }, diff --git a/examples/solid/router-monorepo-solid-query/packages/router/package.json b/examples/solid/router-monorepo-solid-query/packages/router/package.json index fca1bdbca0..0b0212a2d9 100644 --- a/examples/solid/router-monorepo-solid-query/packages/router/package.json +++ b/examples/solid/router-monorepo-solid-query/packages/router/package.json @@ -11,7 +11,7 @@ "@tanstack/history": "^1.162.0", "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "@router-solid-mono-solid-query/post-query": "workspace:*", "redaxios": "^0.5.1", "zod": "^4.4.3", diff --git a/examples/solid/search-validator-adapters/package.json b/examples/solid/search-validator-adapters/package.json index f88570f556..3c2f179f5d 100644 --- a/examples/solid/search-validator-adapters/package.json +++ b/examples/solid/search-validator-adapters/package.json @@ -15,7 +15,7 @@ "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "@tanstack/valibot-adapter": "^1.167.0", "@tanstack/zod-adapter": "^1.167.0", "arktype": "^2.1.7", diff --git a/examples/solid/start-basic-auth/package.json b/examples/solid/start-basic-auth/package.json index 6cc52ae3cb..02811e26fe 100644 --- a/examples/solid/start-basic-auth/package.json +++ b/examples/solid/start-basic-auth/package.json @@ -16,7 +16,7 @@ "@prisma/client": "^7.0.0", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.29", + "@tanstack/solid-start": "^1.168.30", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-authjs/package.json b/examples/solid/start-basic-authjs/package.json index 89b9e7fc18..9b86c9b309 100644 --- a/examples/solid/start-basic-authjs/package.json +++ b/examples/solid/start-basic-authjs/package.json @@ -13,7 +13,7 @@ "@auth/core": "^0.41.1", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.29", + "@tanstack/solid-start": "^1.168.30", "solid-js": "^1.9.10", "start-authjs": "^1.0.0", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-cloudflare/package.json b/examples/solid/start-basic-cloudflare/package.json index f219fbc2b2..c3ce0b486b 100644 --- a/examples/solid/start-basic-cloudflare/package.json +++ b/examples/solid/start-basic-cloudflare/package.json @@ -14,7 +14,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.29", + "@tanstack/solid-start": "^1.168.30", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-netlify/package.json b/examples/solid/start-basic-netlify/package.json index 252a0055eb..8b56354537 100644 --- a/examples/solid/start-basic-netlify/package.json +++ b/examples/solid/start-basic-netlify/package.json @@ -11,7 +11,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.29", + "@tanstack/solid-start": "^1.168.30", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-nitro/package.json b/examples/solid/start-basic-nitro/package.json index 0d8cdedcd1..583ae94e22 100644 --- a/examples/solid/start-basic-nitro/package.json +++ b/examples/solid/start-basic-nitro/package.json @@ -11,7 +11,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.29", + "@tanstack/solid-start": "^1.168.30", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-solid-query/package.json b/examples/solid/start-basic-solid-query/package.json index 9e1ebf8d1c..85e0df2592 100644 --- a/examples/solid/start-basic-solid-query/package.json +++ b/examples/solid/start-basic-solid-query/package.json @@ -15,7 +15,7 @@ "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@tanstack/solid-router-ssr-query": "^1.167.1", - "@tanstack/solid-start": "^1.168.29", + "@tanstack/solid-start": "^1.168.30", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-static/package.json b/examples/solid/start-basic-static/package.json index 388b76bc71..f3375c011a 100644 --- a/examples/solid/start-basic-static/package.json +++ b/examples/solid/start-basic-static/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.29", + "@tanstack/solid-start": "^1.168.30", "@tanstack/start-static-server-functions": "^1.167.19", "redaxios": "^0.5.1", "solid-js": "^1.9.10", diff --git a/examples/solid/start-basic/package.json b/examples/solid/start-basic/package.json index 448c01f250..96af557304 100644 --- a/examples/solid/start-basic/package.json +++ b/examples/solid/start-basic/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.29", + "@tanstack/solid-start": "^1.168.30", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-bun/package.json b/examples/solid/start-bun/package.json index 79e7d1c1ca..bb3e3b6c27 100644 --- a/examples/solid/start-bun/package.json +++ b/examples/solid/start-bun/package.json @@ -18,8 +18,8 @@ "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@tanstack/solid-router-ssr-query": "^1.167.1", - "@tanstack/solid-start": "^1.168.29", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/solid-start": "^1.168.30", + "@tanstack/router-plugin": "^1.168.22", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" }, diff --git a/examples/solid/start-convex-better-auth/package.json b/examples/solid/start-convex-better-auth/package.json index 2357d0d0f2..a18eec8c08 100644 --- a/examples/solid/start-convex-better-auth/package.json +++ b/examples/solid/start-convex-better-auth/package.json @@ -15,7 +15,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.29", + "@tanstack/solid-start": "^1.168.30", "better-auth": "^1.6.19", "clsx": "^2.1.1", "convex": "^1.28.2", diff --git a/examples/solid/start-counter/package.json b/examples/solid/start-counter/package.json index 665573c9e7..f282200707 100644 --- a/examples/solid/start-counter/package.json +++ b/examples/solid/start-counter/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.29", + "@tanstack/solid-start": "^1.168.30", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", diff --git a/examples/solid/start-i18n-paraglide/package.json b/examples/solid/start-i18n-paraglide/package.json index 8094830240..5638fc74d1 100644 --- a/examples/solid/start-i18n-paraglide/package.json +++ b/examples/solid/start-i18n-paraglide/package.json @@ -12,7 +12,7 @@ "@tanstack/solid-devtools": "^0.7.0", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.29", + "@tanstack/solid-start": "^1.168.30", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-large/package.json b/examples/solid/start-large/package.json index 0ff6cabe16..c84530c98a 100644 --- a/examples/solid/start-large/package.json +++ b/examples/solid/start-large/package.json @@ -15,7 +15,7 @@ "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.29", + "@tanstack/solid-start": "^1.168.30", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", diff --git a/examples/solid/start-streaming-data-from-server-functions/package.json b/examples/solid/start-streaming-data-from-server-functions/package.json index 9125df39ef..b5fd12f148 100644 --- a/examples/solid/start-streaming-data-from-server-functions/package.json +++ b/examples/solid/start-streaming-data-from-server-functions/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.29", + "@tanstack/solid-start": "^1.168.30", "solid-js": "^1.9.10", "zod": "^4.4.3" }, diff --git a/examples/solid/start-supabase-basic/package.json b/examples/solid/start-supabase-basic/package.json index b12a4f4afe..5e74806b8d 100644 --- a/examples/solid/start-supabase-basic/package.json +++ b/examples/solid/start-supabase-basic/package.json @@ -16,7 +16,7 @@ "@supabase/supabase-js": "^2.48.1", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.29", + "@tanstack/solid-start": "^1.168.30", "solid-js": "^1.9.9", "redaxios": "^0.5.1" }, diff --git a/examples/solid/start-tailwind-v4/package.json b/examples/solid/start-tailwind-v4/package.json index 44c8780b3a..4f9ce052ae 100644 --- a/examples/solid/start-tailwind-v4/package.json +++ b/examples/solid/start-tailwind-v4/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.29", + "@tanstack/solid-start": "^1.168.30", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", "zod": "^4.4.3" diff --git a/examples/solid/view-transitions/package.json b/examples/solid/view-transitions/package.json index b0d724bb33..87fb50ffd3 100644 --- a/examples/solid/view-transitions/package.json +++ b/examples/solid/view-transitions/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "redaxios": "^0.5.1", diff --git a/examples/solid/with-trpc/package.json b/examples/solid/with-trpc/package.json index 7778d9c25e..3ea2028de1 100644 --- a/examples/solid/with-trpc/package.json +++ b/examples/solid/with-trpc/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@trpc/client": "^11.4.3", diff --git a/examples/vue/basic-file-based-jsx/package.json b/examples/vue/basic-file-based-jsx/package.json index 4be4d124d6..147ff341ee 100644 --- a/examples/vue/basic-file-based-jsx/package.json +++ b/examples/vue/basic-file-based-jsx/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "@tanstack/vue-router": "^1.170.17", "@tanstack/vue-router-devtools": "^1.167.0", "redaxios": "^0.5.1", diff --git a/examples/vue/basic-file-based-sfc/package.json b/examples/vue/basic-file-based-sfc/package.json index 4d2ff736c2..9f6eda684e 100644 --- a/examples/vue/basic-file-based-sfc/package.json +++ b/examples/vue/basic-file-based-sfc/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.21", + "@tanstack/router-plugin": "^1.168.22", "@tanstack/vue-router": "^1.170.17", "@tanstack/vue-router-devtools": "^1.167.0", "redaxios": "^0.5.1", diff --git a/packages/react-start-rsc/CHANGELOG.md b/packages/react-start-rsc/CHANGELOG.md index 450f8a2b76..fb3b987d54 100644 --- a/packages/react-start-rsc/CHANGELOG.md +++ b/packages/react-start-rsc/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/react-start-rsc +## 0.1.29 + +### Patch Changes + +- Updated dependencies []: + - @tanstack/start-plugin-core@1.171.22 + ## 0.1.28 ### Patch Changes diff --git a/packages/react-start-rsc/package.json b/packages/react-start-rsc/package.json index bb4d53bfcd..ba8a93ee54 100644 --- a/packages/react-start-rsc/package.json +++ b/packages/react-start-rsc/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start-rsc", - "version": "0.1.28", + "version": "0.1.29", "description": "React Server Components support for TanStack Start", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-start/CHANGELOG.md b/packages/react-start/CHANGELOG.md index 043d1f5ce2..d4f3f5d9a6 100644 --- a/packages/react-start/CHANGELOG.md +++ b/packages/react-start/CHANGELOG.md @@ -1,5 +1,13 @@ # @tanstack/react-start +## 1.168.30 + +### Patch Changes + +- Updated dependencies []: + - @tanstack/start-plugin-core@1.171.22 + - @tanstack/react-start-rsc@0.1.29 + ## 1.168.29 ### Patch Changes diff --git a/packages/react-start/package.json b/packages/react-start/package.json index 937fd3ae93..b0a07d10a7 100644 --- a/packages/react-start/package.json +++ b/packages/react-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start", - "version": "1.168.29", + "version": "1.168.30", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-cli/CHANGELOG.md b/packages/router-cli/CHANGELOG.md index 9bb43d7288..41092bfd37 100644 --- a/packages/router-cli/CHANGELOG.md +++ b/packages/router-cli/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/router-cli +## 1.167.21 + +### Patch Changes + +- Updated dependencies [[`78dd1a6`](https://github.com/TanStack/router/commit/78dd1a645dc1a9e9f4f649d9aff12005044d4fcc)]: + - @tanstack/router-generator@1.167.21 + ## 1.167.20 ### Patch Changes diff --git a/packages/router-cli/package.json b/packages/router-cli/package.json index b809b4a04d..887b042815 100644 --- a/packages/router-cli/package.json +++ b/packages/router-cli/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-cli", - "version": "1.167.20", + "version": "1.167.21", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-generator/CHANGELOG.md b/packages/router-generator/CHANGELOG.md index bd8e6dc45a..64189e0fe8 100644 --- a/packages/router-generator/CHANGELOG.md +++ b/packages/router-generator/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/router-generator +## 1.167.21 + +### Patch Changes + +- [#7845](https://github.com/TanStack/router/pull/7845) [`78dd1a6`](https://github.com/TanStack/router/commit/78dd1a645dc1a9e9f4f649d9aff12005044d4fcc) - Preserve dots in explicit virtual route paths and pathless layout IDs instead + of treating them as flat-file route separators. + ## 1.167.20 ### Patch Changes diff --git a/packages/router-generator/package.json b/packages/router-generator/package.json index 5489ce4e42..49cb783772 100644 --- a/packages/router-generator/package.json +++ b/packages/router-generator/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-generator", - "version": "1.167.20", + "version": "1.167.21", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-plugin/CHANGELOG.md b/packages/router-plugin/CHANGELOG.md index 4edbb54c21..3cbf45a5dc 100644 --- a/packages/router-plugin/CHANGELOG.md +++ b/packages/router-plugin/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/router-plugin +## 1.168.22 + +### Patch Changes + +- Updated dependencies [[`78dd1a6`](https://github.com/TanStack/router/commit/78dd1a645dc1a9e9f4f649d9aff12005044d4fcc)]: + - @tanstack/router-generator@1.167.21 + ## 1.168.21 ### Patch Changes diff --git a/packages/router-plugin/package.json b/packages/router-plugin/package.json index 196838236e..36a92be67b 100644 --- a/packages/router-plugin/package.json +++ b/packages/router-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-plugin", - "version": "1.168.21", + "version": "1.168.22", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-vite-plugin/CHANGELOG.md b/packages/router-vite-plugin/CHANGELOG.md index 83792ed1c1..79eb4fb014 100644 --- a/packages/router-vite-plugin/CHANGELOG.md +++ b/packages/router-vite-plugin/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/router-vite-plugin +## 1.167.22 + +### Patch Changes + +- Updated dependencies []: + - @tanstack/router-plugin@1.168.22 + ## 1.167.21 ### Patch Changes diff --git a/packages/router-vite-plugin/package.json b/packages/router-vite-plugin/package.json index b3bf3012a8..c6f3ef2b77 100644 --- a/packages/router-vite-plugin/package.json +++ b/packages/router-vite-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-vite-plugin", - "version": "1.167.21", + "version": "1.167.22", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/solid-start/CHANGELOG.md b/packages/solid-start/CHANGELOG.md index 9ba9f1efb5..ab571e1e9d 100644 --- a/packages/solid-start/CHANGELOG.md +++ b/packages/solid-start/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/solid-start +## 1.168.30 + +### Patch Changes + +- Updated dependencies []: + - @tanstack/start-plugin-core@1.171.22 + ## 1.168.29 ### Patch Changes diff --git a/packages/solid-start/package.json b/packages/solid-start/package.json index ebb2a12f6f..9aae38d246 100644 --- a/packages/solid-start/package.json +++ b/packages/solid-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-start", - "version": "1.168.29", + "version": "1.168.30", "description": "Modern and scalable routing for Solid applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-plugin-core/CHANGELOG.md b/packages/start-plugin-core/CHANGELOG.md index 9b17fb628a..0ba16a28ab 100644 --- a/packages/start-plugin-core/CHANGELOG.md +++ b/packages/start-plugin-core/CHANGELOG.md @@ -1,5 +1,13 @@ # @tanstack/start-plugin-core +## 1.171.22 + +### Patch Changes + +- Updated dependencies [[`78dd1a6`](https://github.com/TanStack/router/commit/78dd1a645dc1a9e9f4f649d9aff12005044d4fcc)]: + - @tanstack/router-generator@1.167.21 + - @tanstack/router-plugin@1.168.22 + ## 1.171.21 ### Patch Changes diff --git a/packages/start-plugin-core/package.json b/packages/start-plugin-core/package.json index 2934a49a82..64f0278ec7 100644 --- a/packages/start-plugin-core/package.json +++ b/packages/start-plugin-core/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-plugin-core", - "version": "1.171.21", + "version": "1.171.22", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/vue-start/CHANGELOG.md b/packages/vue-start/CHANGELOG.md index d552fb31b0..150151c95b 100644 --- a/packages/vue-start/CHANGELOG.md +++ b/packages/vue-start/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/vue-start +## 1.168.29 + +### Patch Changes + +- Updated dependencies []: + - @tanstack/start-plugin-core@1.171.22 + ## 1.168.28 ### Patch Changes diff --git a/packages/vue-start/package.json b/packages/vue-start/package.json index 4be3186cb2..3121d9a0b8 100644 --- a/packages/vue-start/package.json +++ b/packages/vue-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/vue-start", - "version": "1.168.28", + "version": "1.168.29", "description": "Modern and scalable routing for Vue applications", "author": "Tanner Linsley", "license": "MIT", From 31a634d84ecf393dfb95adcf713fd5f1a13ab347 Mon Sep 17 00:00:00 2001 From: Alex Kostyniuk Date: Sat, 18 Jul 2026 03:19:11 +0200 Subject: [PATCH 18/51] docs: update Next.js middleware description (#6155) * docs: update Next.js middleware description * Fix grammar in Next.js Edge Runtime description Correct grammatical error in comparison of Next.js limitations. * Revise Next.js proxy.ts runtime details Updated Next.js proxy.ts description to reflect runtime changes. * removed the not needed '+' --------- Co-authored-by: Sarah Gerrard --- docs/start/framework/react/comparison.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/start/framework/react/comparison.md b/docs/start/framework/react/comparison.md index e734263d9d..5c39e0f602 100644 --- a/docs/start/framework/react/comparison.md +++ b/docs/start/framework/react/comparison.md @@ -243,7 +243,7 @@ const authMiddleware = createMiddleware({ type: 'function' }) }) ``` -**Next.js** has a single middleware.ts file that runs on the Edge Runtime for all requests. It cannot access server-only resources like databases and has limitations compared to the Node.js runtime. +**Next.js** has a single proxy.ts file that runs on the Node.js runtime. Unlike the deprecated middleware.ts (which ran on Edge Runtime), proxy.ts has full access to server-side resources like databases. ### Deployment Flexibility From ffdd64e842acacbc9d368a4803a9e474e9f0c0ff Mon Sep 17 00:00:00 2001 From: Manuel Schiller <6340397+schiller-manuel@users.noreply.github.com> Date: Sat, 18 Jul 2026 20:08:42 +0200 Subject: [PATCH 19/51] fix(start): preserve resolved virtual module ids (#7844) --- .changeset/tidy-pandas-load.md | 5 ++ .../src/routes/issue-7725.tsx | 15 +++++ .../issue-7725-server-fn-factory.ts | 3 + .../import-protection/vite.config.ts | 55 +++++++++++++----- .../src/import-protection/constants.ts | 4 -- .../src/start-compiler/compiler.ts | 8 +-- .../src/start-compiler/utils.ts | 7 +++ .../vite/import-protection-plugin/plugin.ts | 12 ++-- .../start-plugin-core/src/vite/module-id.ts | 31 ++++++++++ .../src/vite/start-compiler-plugin/plugin.ts | 13 ++++- .../createServerFn/createServerFn.test.ts | 58 +++++++++++++++++++ .../tests/vite/module-id.test.ts | 56 ++++++++++++++++++ 12 files changed, 234 insertions(+), 33 deletions(-) create mode 100644 .changeset/tidy-pandas-load.md create mode 100644 e2e/react-start/import-protection/src/routes/issue-7725.tsx create mode 100644 e2e/react-start/import-protection/src/violations/issue-7725-server-fn-factory.ts create mode 100644 packages/start-plugin-core/src/vite/module-id.ts create mode 100644 packages/start-plugin-core/tests/vite/module-id.test.ts diff --git a/.changeset/tidy-pandas-load.md b/.changeset/tidy-pandas-load.md new file mode 100644 index 0000000000..d1332d5c42 --- /dev/null +++ b/.changeset/tidy-pandas-load.md @@ -0,0 +1,5 @@ +--- +'@tanstack/start-plugin-core': patch +--- + +Preserve exact Vite-resolved module IDs, including virtual prefixes and query variants, when the Start compiler recursively loads imports. Clean IDs only for file-oriented diagnostics and invalidation, and preserve existing query strings when adding compiler-owned lookup flags, so virtual modules such as import-protection mocks reach their plugin load hooks unchanged. diff --git a/e2e/react-start/import-protection/src/routes/issue-7725.tsx b/e2e/react-start/import-protection/src/routes/issue-7725.tsx new file mode 100644 index 0000000000..ef0152dd13 --- /dev/null +++ b/e2e/react-start/import-protection/src/routes/issue-7725.tsx @@ -0,0 +1,15 @@ +import { createFileRoute } from '@tanstack/react-router' +import { issue7725ServerFnFactory } from '../violations/issue-7725-server-fn-factory' + +const getIssue7725Data = issue7725ServerFnFactory.handler(async () => { + return 'issue 7725 server function' +}) + +export const Route = createFileRoute('/issue-7725')({ + loader: () => getIssue7725Data(), + component: Issue7725, +}) + +function Issue7725() { + return

Issue 7725

+} diff --git a/e2e/react-start/import-protection/src/violations/issue-7725-server-fn-factory.ts b/e2e/react-start/import-protection/src/violations/issue-7725-server-fn-factory.ts new file mode 100644 index 0000000000..45be765b1b --- /dev/null +++ b/e2e/react-start/import-protection/src/violations/issue-7725-server-fn-factory.ts @@ -0,0 +1,3 @@ +import { createServerFn } from '@tanstack/react-start' + +export const issue7725ServerFnFactory = createServerFn() diff --git a/e2e/react-start/import-protection/vite.config.ts b/e2e/react-start/import-protection/vite.config.ts index 3163ded433..6a17f3a7cd 100644 --- a/e2e/react-start/import-protection/vite.config.ts +++ b/e2e/react-start/import-protection/vite.config.ts @@ -8,20 +8,43 @@ const outDir = process.env.E2E_DIST_DIR ?? 'dist' const startModeConfig = getStartModeConfig() const bundledDev = process.env.E2E_VITE_BUNDLED_DEV === 'true' -export default defineConfig({ - resolve: { tsconfigPaths: true }, - experimental: bundledDev ? { bundledDev: true } : undefined, - build: { - outDir, - }, - server: { - port: 3000, - }, - plugins: [tanstackStart(startModeConfig), viteReact()], - // react-tweet's package.json exports resolve to `index.client.js` which - // matches the default **/*.client.* deny pattern. Bundling it via - // noExternal must NOT trigger a false-positive import-protection violation. - ssr: { - noExternal: ['react-tweet'], - }, +const ISSUE_7725_DENIED_SPECIFIER = '../violations/issue-7725-server-fn-factory' + +export default defineConfig(({ command }) => { + const importProtection = + command === 'build' + ? { + ...startModeConfig.importProtection, + client: { + // Reproduce build-only issue #7725: resolving this imported + // server-fn builder must load import protection's mock virtual + // module, including its leading null-byte ID. + specifiers: [ISSUE_7725_DENIED_SPECIFIER], + }, + } + : startModeConfig.importProtection + + return { + resolve: { tsconfigPaths: true }, + experimental: bundledDev ? { bundledDev: true } : undefined, + build: { + outDir, + }, + server: { + port: 3000, + }, + plugins: [ + tanstackStart({ + ...startModeConfig, + importProtection, + }), + viteReact(), + ], + // react-tweet's package.json exports resolve to `index.client.js` which + // matches the default **/*.client.* deny pattern. Bundling it via + // noExternal must NOT trigger a false-positive import-protection violation. + ssr: { + noExternal: ['react-tweet'], + }, + } }) diff --git a/packages/start-plugin-core/src/import-protection/constants.ts b/packages/start-plugin-core/src/import-protection/constants.ts index 54bb55fdab..4186f6af49 100644 --- a/packages/start-plugin-core/src/import-protection/constants.ts +++ b/packages/start-plugin-core/src/import-protection/constants.ts @@ -1,7 +1,3 @@ -import { SERVER_FN_LOOKUP } from '../constants' - -export const SERVER_FN_LOOKUP_QUERY = `?${SERVER_FN_LOOKUP}` - export const MOCK_MODULE_ID = 'tanstack-start-import-protection:mock' export const MOCK_BUILD_PREFIX = 'tanstack-start-import-protection:mock:build:' export const MOCK_EDGE_PREFIX = 'tanstack-start-import-protection:mock-edge:' diff --git a/packages/start-plugin-core/src/start-compiler/compiler.ts b/packages/start-plugin-core/src/start-compiler/compiler.ts index d6b36fe6e8..90fd5f564c 100644 --- a/packages/start-plugin-core/src/start-compiler/compiler.ts +++ b/packages/start-plugin-core/src/start-compiler/compiler.ts @@ -1537,7 +1537,7 @@ export class StartCompiler { // TODO improve cycle detection? should we throw here instead of returning 'None'? // prevent cycles - const vKey = `${cleanId(id)}:${ident}` + const vKey = `${id}:${ident}` if (visited.has(vKey)) { return 'None' } @@ -1634,7 +1634,7 @@ export class StartCompiler { resolution: ExportResolution, visited = new Set(), ): Promise { - const key = `${cleanId(resolution.moduleInfo.id)}:${resolution.localName}` + const key = `${resolution.moduleInfo.id}:${resolution.localName}` if (visited.has(key)) { return undefined } @@ -1709,7 +1709,7 @@ export class StartCompiler { // Match by resolved binding identity, not by export name alone. if ( target && - cleanId(resolved.moduleInfo.id) === cleanId(target.moduleInfo.id) && + resolved.moduleInfo.id === target.moduleInfo.id && resolved.localName === target.localName ) { return kind @@ -1764,7 +1764,7 @@ export class StartCompiler { // Import aliases can form cycles, e.g. A re-exports from B while B // re-exports from A. Track the exported binding before following it. - const vKey = `${cleanId(found.moduleInfo.id)}:${found.localName}` + const vKey = `${found.moduleInfo.id}:${found.localName}` if (visited.has(vKey)) { return 'None' } diff --git a/packages/start-plugin-core/src/start-compiler/utils.ts b/packages/start-plugin-core/src/start-compiler/utils.ts index 36900845fb..384a13113d 100644 --- a/packages/start-plugin-core/src/start-compiler/utils.ts +++ b/packages/start-plugin-core/src/start-compiler/utils.ts @@ -25,6 +25,13 @@ export function codeFrameError( return new Error(frame) } +/** + * Converts a bundler module ID to its physical-file identity for diagnostics, + * filesystem matching, and file-based invalidation. + * + * Do not use this for IDs passed to resolve/load hooks or as module cache keys: + * virtual prefixes and queries can be part of the module's semantic identity. + */ export function cleanId(id: string): string { // Remove null byte prefix used by Vite/Rollup for virtual modules if (id.startsWith('\0')) { diff --git a/packages/start-plugin-core/src/vite/import-protection-plugin/plugin.ts b/packages/start-plugin-core/src/vite/import-protection-plugin/plugin.ts index 55b98bec73..e319874fc6 100644 --- a/packages/start-plugin-core/src/vite/import-protection-plugin/plugin.ts +++ b/packages/start-plugin-core/src/vite/import-protection-plugin/plugin.ts @@ -3,7 +3,8 @@ import { normalizePath } from 'vite' import { dirname, relative } from 'pathe' import { escapeRegExp, resolveViteId } from '../../utils' -import { VITE_ENVIRONMENT_NAMES } from '../../constants' +import { SERVER_FN_LOOKUP, VITE_ENVIRONMENT_NAMES } from '../../constants' +import { hasIdQueryFlag } from '../module-id' import { ImportGraph, buildTrace, @@ -46,7 +47,6 @@ import { rewriteDeniedImports } from '../../import-protection/rewrite' import { ExtensionlessAbsoluteIdResolver } from '../../import-protection/extensionlessAbsoluteIdResolver' import { IMPORT_PROTECTION_DEBUG, - SERVER_FN_LOOKUP_QUERY, VITE_BROWSER_VIRTUAL_PREFIX, } from '../../import-protection/constants' import { @@ -1013,7 +1013,7 @@ export function importProtectionPlugin( if (keySet) { for (const k of keySet) { - if (k.includes(SERVER_FN_LOOKUP_QUERY)) continue + if (hasIdQueryFlag(k, SERVER_FN_LOOKUP)) continue const imports = env.postTransformImports.get(k) if (imports) { if (!merged) merged = new Set(imports) @@ -1051,7 +1051,7 @@ export function importProtectionPlugin( if (keySet) { for (const k of keySet) { - if (k.includes(SERVER_FN_LOOKUP_QUERY)) continue + if (hasIdQueryFlag(k, SERVER_FN_LOOKUP)) continue const imports = env.postTransformImports.get(k) if (imports) { anyVariantCached = true @@ -1650,7 +1650,7 @@ export function importProtectionPlugin( } const normalizedImporter = normalizeFilePath(importer) - const isDirectLookup = importer.includes(SERVER_FN_LOOKUP_QUERY) + const isDirectLookup = hasIdQueryFlag(importer, SERVER_FN_LOOKUP) const isBundledClientDev = config.command === 'serve' && config.bundledDev && @@ -2228,7 +2228,7 @@ export function importProtectionPlugin( const cacheKey = normalizePath(id) const envState = getEnv(envName) - const isServerFnLookup = id.includes(SERVER_FN_LOOKUP_QUERY) + const isServerFnLookup = hasIdQueryFlag(id, SERVER_FN_LOOKUP) // Propagate SERVER_FN_LOOKUP status before import-analysis if (isServerFnLookup) { diff --git a/packages/start-plugin-core/src/vite/module-id.ts b/packages/start-plugin-core/src/vite/module-id.ts new file mode 100644 index 0000000000..ef2ad98f10 --- /dev/null +++ b/packages/start-plugin-core/src/vite/module-id.ts @@ -0,0 +1,31 @@ +/** Checks for a query parameter without rewriting the opaque module ID. */ +export function hasIdQueryFlag(id: string, flag: string): boolean { + const queryIndex = id.indexOf('?') + if (queryIndex === -1) { + return false + } + + return new URLSearchParams(id.slice(queryIndex + 1)).has(flag) +} + +/** Appends an owned query flag without normalizing the existing query. */ +export function appendIdQueryFlag(id: string, flag: string): string { + if (!id.includes('?')) { + return `${id}?${flag}` + } + + const separator = id.endsWith('&') ? '' : '&' + return `${id}${separator}${flag}` +} + +/** Removes the owned query flag appended by {@link appendIdQueryFlag}. */ +export function removeIdQueryFlag(id: string, flag: string): string { + for (const separator of ['?', '&']) { + const suffix = `${separator}${flag}` + if (id.endsWith(suffix)) { + return id.slice(0, -suffix.length) + } + } + + return id +} diff --git a/packages/start-plugin-core/src/vite/start-compiler-plugin/plugin.ts b/packages/start-plugin-core/src/vite/start-compiler-plugin/plugin.ts index 6309b76eb2..1cc02f3b91 100644 --- a/packages/start-plugin-core/src/vite/start-compiler-plugin/plugin.ts +++ b/packages/start-plugin-core/src/vite/start-compiler-plugin/plugin.ts @@ -21,6 +21,7 @@ import { createHydrateCompilerPlugin, } from '../../hydrate-when-transform' import { resolveViteId } from '../../utils' +import { appendIdQueryFlag, removeIdQueryFlag } from '../module-id' import { createViteDevServerFnModuleSpecifierEncoder, decodeViteDevServerModuleSpecifier, @@ -351,7 +352,7 @@ export function startCompilerPlugin( { load: (options) => this.load(options), error: (message) => this.error(message), - devId: `${id}?${SERVER_FN_LOOKUP}`, + devId: appendIdQueryFlag(id, SERVER_FN_LOOKUP), }, ) if (code !== undefined) { @@ -364,7 +365,10 @@ export function startCompilerPlugin( if (r) { if (!r.external) { - return cleanId(r.id) + // Keep the resolved ID intact because it is passed back to + // Vite's load hook. Virtual-module prefixes and queries are + // part of that load identity, not compiler-only metadata. + return r.id } } @@ -496,7 +500,10 @@ export function startCompilerPlugin( }, handler(code, id) { const compiler = compilers.get(this.environment.name) - compiler?.ingestModule({ code, id: cleanId(id) }) + compiler?.ingestModule({ + code, + id: removeIdQueryFlag(id, SERVER_FN_LOOKUP), + }) }, }, }, diff --git a/packages/start-plugin-core/tests/createServerFn/createServerFn.test.ts b/packages/start-plugin-core/tests/createServerFn/createServerFn.test.ts index c4009657c4..230caf7173 100644 --- a/packages/start-plugin-core/tests/createServerFn/createServerFn.test.ts +++ b/packages/start-plugin-core/tests/createServerFn/createServerFn.test.ts @@ -457,6 +457,64 @@ describe('createServerFn compiles correctly', async () => { ) }) + test('keeps query-bearing virtual module identities distinct', async () => { + const publicFactoryId = '\0virtual:server-fn-factory?variant=public' + const implementationFactoryId = + '\0virtual:server-fn-factory?variant=implementation' + const virtualModules: Record = { + [publicFactoryId]: ` + import { createIssueServerFn } from 'virtual:server-fn-factory?variant=implementation' + export { createIssueServerFn } + `, + [implementationFactoryId]: ` + import { createServerFn } from '@tanstack/react-start' + export const createIssueServerFn = createServerFn + `, + } + const loadedIds: Array = [] + + const compiler = new StartCompiler({ + env: 'client', + ...getDefaultTestOptions('client'), + mode: 'build', + loadModule: async (id) => { + loadedIds.push(id) + const code = virtualModules[id] + if (code) { + compiler.ingestModule({ code, id }) + } + }, + lookupKinds: new Set(['ServerFn']), + lookupConfigurations: [ + { + libName: '@tanstack/react-start', + rootExport: 'createServerFn', + kind: 'Root', + }, + ], + getKnownServerFns: () => ({}), + resolveId: async (source) => { + if (source.startsWith('virtual:server-fn-factory?')) { + return `\0${source}` + } + + return null + }, + }) + + const result = await compiler.compile({ + id: '/test/src/test.ts', + code: ` + import { createIssueServerFn } from 'virtual:server-fn-factory?variant=public' + const issueServerFn = createIssueServerFn().handler(async () => 'ok') + `, + }) + + expect(result).not.toBeNull() + expect(result!.code).toContain('createClientRpc') + expect(loadedIds).toEqual([publicFactoryId, implementationFactoryId]) + }) + test('should resolve local named re-exports of createServerFn', async () => { const code = ` import { createFooServerFn } from './factory' diff --git a/packages/start-plugin-core/tests/vite/module-id.test.ts b/packages/start-plugin-core/tests/vite/module-id.test.ts new file mode 100644 index 0000000000..003926a606 --- /dev/null +++ b/packages/start-plugin-core/tests/vite/module-id.test.ts @@ -0,0 +1,56 @@ +import { describe, expect, test } from 'vitest' +import { + appendIdQueryFlag, + hasIdQueryFlag, + removeIdQueryFlag, +} from '../../src/vite/module-id' + +const flag = 'server-fn-module-lookup' + +describe('hasIdQueryFlag', () => { + test.each([ + ['/src/route.ts', false], + [`/src/route.ts?${flag}`, true], + [`/src/route.ts?mode=dev&${flag}`, true], + [`/src/route.ts?${flag}=enabled`, true], + [`/src/route.ts?mode=${flag}`, false], + [`/src/route.ts?${flag}-suffix`, false], + ])('checks the query parameter name in %s', (id, expected) => { + expect(hasIdQueryFlag(id, flag)).toBe(expected) + }) +}) + +describe('appendIdQueryFlag', () => { + test.each([ + ['/src/route.ts', `/src/route.ts?${flag}`], + ['/src/route.ts?mode=dev', `/src/route.ts?mode=dev&${flag}`], + ['/src/route.ts?', `/src/route.ts?&${flag}`], + ['/src/route.ts?mode=dev&', `/src/route.ts?mode=dev&${flag}`], + ])('appends the flag to %s', (id, expected) => { + expect(appendIdQueryFlag(id, flag)).toBe(expected) + }) +}) + +describe('removeIdQueryFlag', () => { + test.each([ + [`/src/route.ts?${flag}`, '/src/route.ts'], + [`/src/route.ts?mode=dev&${flag}`, '/src/route.ts?mode=dev'], + [`/src/route.ts?${flag}&mode=dev`, `/src/route.ts?${flag}&mode=dev`], + ['/src/route.ts?mode=dev', '/src/route.ts?mode=dev'], + ])('removes only an appended flag from %s', (id, expected) => { + expect(removeIdQueryFlag(id, flag)).toBe(expected) + }) +}) + +describe('module ID query flag round trip', () => { + test.each([ + '/src/route.ts', + '/src/route.ts?', + '/src/route.ts?variant=client', + '\0virtual:factory?variant=client&encoded=a%2Fb', + `/src/route.ts?${flag}`, + ])('preserves the original ID %s', (id) => { + const withFlag = appendIdQueryFlag(id, flag) + expect(removeIdQueryFlag(withFlag, flag)).toBe(id) + }) +}) From ecb1ef4817e3e58851407972d474ef8ec1245158 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 19 Jul 2026 09:11:05 +0200 Subject: [PATCH 20/51] ci: Version Packages (#7854) --- .changeset/tidy-pandas-load.md | 5 ----- examples/react/start-bare/package.json | 2 +- examples/react/start-basic-auth/package.json | 2 +- examples/react/start-basic-authjs/package.json | 2 +- examples/react/start-basic-cloudflare/package.json | 2 +- examples/react/start-basic-react-query/package.json | 2 +- examples/react/start-basic-static/package.json | 2 +- examples/react/start-basic/package.json | 2 +- examples/react/start-bun/package.json | 2 +- examples/react/start-clerk-basic/package.json | 2 +- examples/react/start-convex-trellaux/package.json | 2 +- examples/react/start-counter/package.json | 2 +- examples/react/start-i18n-paraglide/package.json | 2 +- examples/react/start-large/package.json | 2 +- examples/react/start-material-ui/package.json | 2 +- examples/react/start-rscs/package.json | 2 +- .../package.json | 2 +- examples/react/start-supabase-basic/package.json | 2 +- examples/react/start-tailwind-v4/package.json | 2 +- examples/react/start-trellaux/package.json | 2 +- examples/react/start-workos/package.json | 2 +- examples/solid/start-basic-auth/package.json | 2 +- examples/solid/start-basic-authjs/package.json | 2 +- examples/solid/start-basic-cloudflare/package.json | 2 +- examples/solid/start-basic-netlify/package.json | 2 +- examples/solid/start-basic-nitro/package.json | 2 +- examples/solid/start-basic-solid-query/package.json | 2 +- examples/solid/start-basic-static/package.json | 2 +- examples/solid/start-basic/package.json | 2 +- examples/solid/start-bun/package.json | 2 +- examples/solid/start-convex-better-auth/package.json | 2 +- examples/solid/start-counter/package.json | 2 +- examples/solid/start-i18n-paraglide/package.json | 2 +- examples/solid/start-large/package.json | 2 +- .../package.json | 2 +- examples/solid/start-supabase-basic/package.json | 2 +- examples/solid/start-tailwind-v4/package.json | 2 +- packages/react-start-rsc/CHANGELOG.md | 7 +++++++ packages/react-start-rsc/package.json | 2 +- packages/react-start/CHANGELOG.md | 8 ++++++++ packages/react-start/package.json | 2 +- packages/solid-start/CHANGELOG.md | 7 +++++++ packages/solid-start/package.json | 2 +- packages/start-plugin-core/CHANGELOG.md | 6 ++++++ packages/start-plugin-core/package.json | 2 +- packages/vue-start/CHANGELOG.md | 7 +++++++ packages/vue-start/package.json | 2 +- 47 files changed, 76 insertions(+), 46 deletions(-) delete mode 100644 .changeset/tidy-pandas-load.md diff --git a/.changeset/tidy-pandas-load.md b/.changeset/tidy-pandas-load.md deleted file mode 100644 index d1332d5c42..0000000000 --- a/.changeset/tidy-pandas-load.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@tanstack/start-plugin-core': patch ---- - -Preserve exact Vite-resolved module IDs, including virtual prefixes and query variants, when the Start compiler recursively loads imports. Clean IDs only for file-oriented diagnostics and invalidation, and preserve existing query strings when adding compiler-owned lookup flags, so virtual modules such as import-protection mocks reach their plugin load hooks unchanged. diff --git a/examples/react/start-bare/package.json b/examples/react/start-bare/package.json index 6319e85aaf..4b408ddc2f 100644 --- a/examples/react/start-bare/package.json +++ b/examples/react/start-bare/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.30", + "@tanstack/react-start": "^1.168.31", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-basic-auth/package.json b/examples/react/start-basic-auth/package.json index 825ebfe353..4de0348958 100644 --- a/examples/react/start-basic-auth/package.json +++ b/examples/react/start-basic-auth/package.json @@ -16,7 +16,7 @@ "@prisma/client": "^7.0.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.30", + "@tanstack/react-start": "^1.168.31", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-basic-authjs/package.json b/examples/react/start-basic-authjs/package.json index 4896acf5df..f278e98af9 100644 --- a/examples/react/start-basic-authjs/package.json +++ b/examples/react/start-basic-authjs/package.json @@ -13,7 +13,7 @@ "@auth/core": "^0.41.1", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.30", + "@tanstack/react-start": "^1.168.31", "react": "^19.0.0", "react-dom": "^19.0.0", "start-authjs": "^1.0.0", diff --git a/examples/react/start-basic-cloudflare/package.json b/examples/react/start-basic-cloudflare/package.json index f60aed7be3..df280a633f 100644 --- a/examples/react/start-basic-cloudflare/package.json +++ b/examples/react/start-basic-cloudflare/package.json @@ -14,7 +14,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.30", + "@tanstack/react-start": "^1.168.31", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/examples/react/start-basic-react-query/package.json b/examples/react/start-basic-react-query/package.json index 80289dc857..5da0dccc61 100644 --- a/examples/react/start-basic-react-query/package.json +++ b/examples/react/start-basic-react-query/package.json @@ -15,7 +15,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.30", + "@tanstack/react-start": "^1.168.31", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-basic-static/package.json b/examples/react/start-basic-static/package.json index 030343a1b5..cf48965e48 100644 --- a/examples/react/start-basic-static/package.json +++ b/examples/react/start-basic-static/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.30", + "@tanstack/react-start": "^1.168.31", "@tanstack/start-static-server-functions": "^1.167.19", "@vitejs/plugin-react": "^6.0.1", "react": "^19.0.0", diff --git a/examples/react/start-basic/package.json b/examples/react/start-basic/package.json index 5b138167d8..789d9139d5 100644 --- a/examples/react/start-basic/package.json +++ b/examples/react/start-basic/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.30", + "@tanstack/react-start": "^1.168.31", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.6.0", diff --git a/examples/react/start-bun/package.json b/examples/react/start-bun/package.json index ce2b644227..38ad3bce44 100644 --- a/examples/react/start-bun/package.json +++ b/examples/react/start-bun/package.json @@ -18,7 +18,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.30", + "@tanstack/react-start": "^1.168.31", "@tanstack/router-plugin": "^1.168.22", "react": "^19.1.1", "react-dom": "^19.1.1", diff --git a/examples/react/start-clerk-basic/package.json b/examples/react/start-clerk-basic/package.json index 0baf4d2ee5..405391cc61 100644 --- a/examples/react/start-clerk-basic/package.json +++ b/examples/react/start-clerk-basic/package.json @@ -13,7 +13,7 @@ "@clerk/tanstack-react-start": "^0.27.14", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.30", + "@tanstack/react-start": "^1.168.31", "@vitejs/plugin-react": "^6.0.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/start-convex-trellaux/package.json b/examples/react/start-convex-trellaux/package.json index 27fab4e79b..ec8a0c2fa2 100644 --- a/examples/react/start-convex-trellaux/package.json +++ b/examples/react/start-convex-trellaux/package.json @@ -18,7 +18,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.30", + "@tanstack/react-start": "^1.168.31", "concurrently": "^8.2.2", "convex": "^1.19.0", "ky": "^1.7.4", diff --git a/examples/react/start-counter/package.json b/examples/react/start-counter/package.json index 3298c9d5fc..1b47b9852b 100644 --- a/examples/react/start-counter/package.json +++ b/examples/react/start-counter/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.30", + "@tanstack/react-start": "^1.168.31", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/examples/react/start-i18n-paraglide/package.json b/examples/react/start-i18n-paraglide/package.json index 12aadecf9a..2b90afc1a9 100644 --- a/examples/react/start-i18n-paraglide/package.json +++ b/examples/react/start-i18n-paraglide/package.json @@ -12,7 +12,7 @@ "@tanstack/react-devtools": "^0.7.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.30", + "@tanstack/react-start": "^1.168.31", "react": "^19.1.1", "react-dom": "^19.1.1" }, diff --git a/examples/react/start-large/package.json b/examples/react/start-large/package.json index 2a29d06c7e..f373745dcc 100644 --- a/examples/react/start-large/package.json +++ b/examples/react/start-large/package.json @@ -15,7 +15,7 @@ "@tanstack/react-query": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.30", + "@tanstack/react-start": "^1.168.31", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-material-ui/package.json b/examples/react/start-material-ui/package.json index 58ccf1fe80..e528e0fb8f 100644 --- a/examples/react/start-material-ui/package.json +++ b/examples/react/start-material-ui/package.json @@ -17,7 +17,7 @@ "@mui/material": "6.4.7", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.30", + "@tanstack/react-start": "^1.168.31", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-rscs/package.json b/examples/react/start-rscs/package.json index 1ed16c922d..1b9a90d1f9 100644 --- a/examples/react/start-rscs/package.json +++ b/examples/react/start-rscs/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.30", + "@tanstack/react-start": "^1.168.31", "dexie": "^4.0.10", "react": "^19.2.0", "react-dom": "^19.2.0", diff --git a/examples/react/start-streaming-data-from-server-functions/package.json b/examples/react/start-streaming-data-from-server-functions/package.json index 6835fb1160..c00048fd10 100644 --- a/examples/react/start-streaming-data-from-server-functions/package.json +++ b/examples/react/start-streaming-data-from-server-functions/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.30", + "@tanstack/react-start": "^1.168.31", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-supabase-basic/package.json b/examples/react/start-supabase-basic/package.json index 8dbc602e01..c358363110 100644 --- a/examples/react/start-supabase-basic/package.json +++ b/examples/react/start-supabase-basic/package.json @@ -17,7 +17,7 @@ "@supabase/supabase-js": "^2.48.1", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.30", + "@tanstack/react-start": "^1.168.31", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/start-tailwind-v4/package.json b/examples/react/start-tailwind-v4/package.json index d09c184032..1d370b2c8c 100644 --- a/examples/react/start-tailwind-v4/package.json +++ b/examples/react/start-tailwind-v4/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.30", + "@tanstack/react-start": "^1.168.31", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.6.0", diff --git a/examples/react/start-trellaux/package.json b/examples/react/start-trellaux/package.json index 58f4b488e2..67a76ae325 100644 --- a/examples/react/start-trellaux/package.json +++ b/examples/react/start-trellaux/package.json @@ -15,7 +15,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.30", + "@tanstack/react-start": "^1.168.31", "ky": "^1.7.4", "msw": "^2.7.0", "react": "^19.0.0", diff --git a/examples/react/start-workos/package.json b/examples/react/start-workos/package.json index df8fbb92ef..ec99b81c50 100644 --- a/examples/react/start-workos/package.json +++ b/examples/react/start-workos/package.json @@ -16,7 +16,7 @@ "@radix-ui/themes": "^3.3.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.30", + "@tanstack/react-start": "^1.168.31", "@workos/authkit-tanstack-react-start": "^0.5.0", "react": "^19.0.0", "react-dom": "^19.0.0" diff --git a/examples/solid/start-basic-auth/package.json b/examples/solid/start-basic-auth/package.json index 02811e26fe..4153fa4418 100644 --- a/examples/solid/start-basic-auth/package.json +++ b/examples/solid/start-basic-auth/package.json @@ -16,7 +16,7 @@ "@prisma/client": "^7.0.0", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.30", + "@tanstack/solid-start": "^1.168.31", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-authjs/package.json b/examples/solid/start-basic-authjs/package.json index 9b86c9b309..e530b96fcb 100644 --- a/examples/solid/start-basic-authjs/package.json +++ b/examples/solid/start-basic-authjs/package.json @@ -13,7 +13,7 @@ "@auth/core": "^0.41.1", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.30", + "@tanstack/solid-start": "^1.168.31", "solid-js": "^1.9.10", "start-authjs": "^1.0.0", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-cloudflare/package.json b/examples/solid/start-basic-cloudflare/package.json index c3ce0b486b..37301e7006 100644 --- a/examples/solid/start-basic-cloudflare/package.json +++ b/examples/solid/start-basic-cloudflare/package.json @@ -14,7 +14,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.30", + "@tanstack/solid-start": "^1.168.31", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-netlify/package.json b/examples/solid/start-basic-netlify/package.json index 8b56354537..26843023e5 100644 --- a/examples/solid/start-basic-netlify/package.json +++ b/examples/solid/start-basic-netlify/package.json @@ -11,7 +11,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.30", + "@tanstack/solid-start": "^1.168.31", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-nitro/package.json b/examples/solid/start-basic-nitro/package.json index 583ae94e22..d852359c69 100644 --- a/examples/solid/start-basic-nitro/package.json +++ b/examples/solid/start-basic-nitro/package.json @@ -11,7 +11,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.30", + "@tanstack/solid-start": "^1.168.31", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-solid-query/package.json b/examples/solid/start-basic-solid-query/package.json index 85e0df2592..fdd069531a 100644 --- a/examples/solid/start-basic-solid-query/package.json +++ b/examples/solid/start-basic-solid-query/package.json @@ -15,7 +15,7 @@ "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@tanstack/solid-router-ssr-query": "^1.167.1", - "@tanstack/solid-start": "^1.168.30", + "@tanstack/solid-start": "^1.168.31", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-static/package.json b/examples/solid/start-basic-static/package.json index f3375c011a..a8bb95890f 100644 --- a/examples/solid/start-basic-static/package.json +++ b/examples/solid/start-basic-static/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.30", + "@tanstack/solid-start": "^1.168.31", "@tanstack/start-static-server-functions": "^1.167.19", "redaxios": "^0.5.1", "solid-js": "^1.9.10", diff --git a/examples/solid/start-basic/package.json b/examples/solid/start-basic/package.json index 96af557304..4970f83523 100644 --- a/examples/solid/start-basic/package.json +++ b/examples/solid/start-basic/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.30", + "@tanstack/solid-start": "^1.168.31", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-bun/package.json b/examples/solid/start-bun/package.json index bb3e3b6c27..84736bc627 100644 --- a/examples/solid/start-bun/package.json +++ b/examples/solid/start-bun/package.json @@ -18,7 +18,7 @@ "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@tanstack/solid-router-ssr-query": "^1.167.1", - "@tanstack/solid-start": "^1.168.30", + "@tanstack/solid-start": "^1.168.31", "@tanstack/router-plugin": "^1.168.22", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" diff --git a/examples/solid/start-convex-better-auth/package.json b/examples/solid/start-convex-better-auth/package.json index a18eec8c08..96ecf8b933 100644 --- a/examples/solid/start-convex-better-auth/package.json +++ b/examples/solid/start-convex-better-auth/package.json @@ -15,7 +15,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.30", + "@tanstack/solid-start": "^1.168.31", "better-auth": "^1.6.19", "clsx": "^2.1.1", "convex": "^1.28.2", diff --git a/examples/solid/start-counter/package.json b/examples/solid/start-counter/package.json index f282200707..375cdd1a0d 100644 --- a/examples/solid/start-counter/package.json +++ b/examples/solid/start-counter/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.30", + "@tanstack/solid-start": "^1.168.31", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", diff --git a/examples/solid/start-i18n-paraglide/package.json b/examples/solid/start-i18n-paraglide/package.json index 5638fc74d1..601677de98 100644 --- a/examples/solid/start-i18n-paraglide/package.json +++ b/examples/solid/start-i18n-paraglide/package.json @@ -12,7 +12,7 @@ "@tanstack/solid-devtools": "^0.7.0", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.30", + "@tanstack/solid-start": "^1.168.31", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-large/package.json b/examples/solid/start-large/package.json index c84530c98a..882312e4ac 100644 --- a/examples/solid/start-large/package.json +++ b/examples/solid/start-large/package.json @@ -15,7 +15,7 @@ "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.30", + "@tanstack/solid-start": "^1.168.31", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", diff --git a/examples/solid/start-streaming-data-from-server-functions/package.json b/examples/solid/start-streaming-data-from-server-functions/package.json index b5fd12f148..55245c5e12 100644 --- a/examples/solid/start-streaming-data-from-server-functions/package.json +++ b/examples/solid/start-streaming-data-from-server-functions/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.30", + "@tanstack/solid-start": "^1.168.31", "solid-js": "^1.9.10", "zod": "^4.4.3" }, diff --git a/examples/solid/start-supabase-basic/package.json b/examples/solid/start-supabase-basic/package.json index 5e74806b8d..6c54d45673 100644 --- a/examples/solid/start-supabase-basic/package.json +++ b/examples/solid/start-supabase-basic/package.json @@ -16,7 +16,7 @@ "@supabase/supabase-js": "^2.48.1", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.30", + "@tanstack/solid-start": "^1.168.31", "solid-js": "^1.9.9", "redaxios": "^0.5.1" }, diff --git a/examples/solid/start-tailwind-v4/package.json b/examples/solid/start-tailwind-v4/package.json index 4f9ce052ae..ce261b3ff7 100644 --- a/examples/solid/start-tailwind-v4/package.json +++ b/examples/solid/start-tailwind-v4/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.30", + "@tanstack/solid-start": "^1.168.31", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", "zod": "^4.4.3" diff --git a/packages/react-start-rsc/CHANGELOG.md b/packages/react-start-rsc/CHANGELOG.md index fb3b987d54..3178fb4940 100644 --- a/packages/react-start-rsc/CHANGELOG.md +++ b/packages/react-start-rsc/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/react-start-rsc +## 0.1.30 + +### Patch Changes + +- Updated dependencies [[`ffdd64e`](https://github.com/TanStack/router/commit/ffdd64e842acacbc9d368a4803a9e474e9f0c0ff)]: + - @tanstack/start-plugin-core@1.171.23 + ## 0.1.29 ### Patch Changes diff --git a/packages/react-start-rsc/package.json b/packages/react-start-rsc/package.json index ba8a93ee54..44a3f16c6b 100644 --- a/packages/react-start-rsc/package.json +++ b/packages/react-start-rsc/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start-rsc", - "version": "0.1.29", + "version": "0.1.30", "description": "React Server Components support for TanStack Start", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-start/CHANGELOG.md b/packages/react-start/CHANGELOG.md index d4f3f5d9a6..e63bb1209e 100644 --- a/packages/react-start/CHANGELOG.md +++ b/packages/react-start/CHANGELOG.md @@ -1,5 +1,13 @@ # @tanstack/react-start +## 1.168.31 + +### Patch Changes + +- Updated dependencies [[`ffdd64e`](https://github.com/TanStack/router/commit/ffdd64e842acacbc9d368a4803a9e474e9f0c0ff)]: + - @tanstack/start-plugin-core@1.171.23 + - @tanstack/react-start-rsc@0.1.30 + ## 1.168.30 ### Patch Changes diff --git a/packages/react-start/package.json b/packages/react-start/package.json index b0a07d10a7..24a7c5b58d 100644 --- a/packages/react-start/package.json +++ b/packages/react-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start", - "version": "1.168.30", + "version": "1.168.31", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/solid-start/CHANGELOG.md b/packages/solid-start/CHANGELOG.md index ab571e1e9d..218f86a421 100644 --- a/packages/solid-start/CHANGELOG.md +++ b/packages/solid-start/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/solid-start +## 1.168.31 + +### Patch Changes + +- Updated dependencies [[`ffdd64e`](https://github.com/TanStack/router/commit/ffdd64e842acacbc9d368a4803a9e474e9f0c0ff)]: + - @tanstack/start-plugin-core@1.171.23 + ## 1.168.30 ### Patch Changes diff --git a/packages/solid-start/package.json b/packages/solid-start/package.json index 9aae38d246..70b265ae94 100644 --- a/packages/solid-start/package.json +++ b/packages/solid-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-start", - "version": "1.168.30", + "version": "1.168.31", "description": "Modern and scalable routing for Solid applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-plugin-core/CHANGELOG.md b/packages/start-plugin-core/CHANGELOG.md index 0ba16a28ab..6fb57bccc7 100644 --- a/packages/start-plugin-core/CHANGELOG.md +++ b/packages/start-plugin-core/CHANGELOG.md @@ -1,5 +1,11 @@ # @tanstack/start-plugin-core +## 1.171.23 + +### Patch Changes + +- [#7844](https://github.com/TanStack/router/pull/7844) [`ffdd64e`](https://github.com/TanStack/router/commit/ffdd64e842acacbc9d368a4803a9e474e9f0c0ff) - Preserve exact Vite-resolved module IDs, including virtual prefixes and query variants, when the Start compiler recursively loads imports. Clean IDs only for file-oriented diagnostics and invalidation, and preserve existing query strings when adding compiler-owned lookup flags, so virtual modules such as import-protection mocks reach their plugin load hooks unchanged. + ## 1.171.22 ### Patch Changes diff --git a/packages/start-plugin-core/package.json b/packages/start-plugin-core/package.json index 64f0278ec7..8479116586 100644 --- a/packages/start-plugin-core/package.json +++ b/packages/start-plugin-core/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-plugin-core", - "version": "1.171.22", + "version": "1.171.23", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/vue-start/CHANGELOG.md b/packages/vue-start/CHANGELOG.md index 150151c95b..eb1db3fd04 100644 --- a/packages/vue-start/CHANGELOG.md +++ b/packages/vue-start/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/vue-start +## 1.168.30 + +### Patch Changes + +- Updated dependencies [[`ffdd64e`](https://github.com/TanStack/router/commit/ffdd64e842acacbc9d368a4803a9e474e9f0c0ff)]: + - @tanstack/start-plugin-core@1.171.23 + ## 1.168.29 ### Patch Changes diff --git a/packages/vue-start/package.json b/packages/vue-start/package.json index 3121d9a0b8..10215ef5d9 100644 --- a/packages/vue-start/package.json +++ b/packages/vue-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/vue-start", - "version": "1.168.29", + "version": "1.168.30", "description": "Modern and scalable routing for Vue applications", "author": "Tanner Linsley", "license": "MIT", From 218eab2c3bc6fd8ab7d249ba95ef3a23eb41f8cd Mon Sep 17 00:00:00 2001 From: Manuel Schiller <6340397+schiller-manuel@users.noreply.github.com> Date: Sun, 19 Jul 2026 09:20:56 +0200 Subject: [PATCH 21/51] chore: regenerate route trees (#7853) --- .../async-pipeline/react/src/routeTree.gen.ts | 62 +- .../control-flow/react/src/routeTree.gen.ts | 72 +- .../scenarios/head/react/src/routeTree.gen.ts | 62 +- .../history/react/src/routeTree.gen.ts | 44 +- .../links/react/src/routeTree.gen.ts | 26 +- .../loaders/react/src/routeTree.gen.ts | 44 +- .../mount/react/src/routeTree.gen.ts | 140 +- .../preload/react/src/routeTree.gen.ts | 26 +- .../react/src/routeTree.gen.ts | 700 ++--- .../search-params/react/src/routeTree.gen.ts | 36 +- .../error-paths/react/src/routeTree.gen.ts | 62 +- .../react/src/routeTree.gen.ts | 26 +- .../control-flow/react/src/routeTree.gen.ts | 72 +- .../react/src/routeTree.gen.ts | 26 +- .../serialization/react/src/routeTree.gen.ts | 26 +- .../server-fns/react/src/routeTree.gen.ts | 26 +- .../basepath-file-based/src/routeTree.gen.ts | 72 +- .../src/routeTree.gen.ts | 136 +- .../basic-file-based/src/routeTree.gen.ts | 2384 ++++++++--------- .../src/routeTree.gen.ts | 82 +- .../src/routeTree.gen.ts | 158 +- .../src/routeTree.gen.ts | 72 +- .../generator-cli-only/src/routeTree.gen.ts | 72 +- .../js-only-file-based/src/routeTree.gen.js | 36 +- .../src/routeTree.gen.ts | 122 +- .../view-transitions/src/routeTree.gen.ts | 44 +- .../basic-auth/src/routeTree.gen.ts | 70 +- .../basic-cloudflare/src/routeTree.gen.ts | 26 +- .../basic-react-query/src/routeTree.gen.ts | 284 +- e2e/react-start/basic/src/routeTree.gen.ts | 1254 ++++----- .../clerk-basic/src/routeTree.gen.ts | 50 +- .../css-inline/src/routeTree.gen.ts | 26 +- .../css-modules/src/routeTree.gen.ts | 62 +- .../custom-basepath/src/routeTree.gen.ts | 222 +- .../deferred-hydration/src/routeTree.gen.ts | 98 +- .../early-hints/src/routeTree.gen.ts | 84 +- .../flamegraph-bench/src/routeTree.gen.ts | 44 +- e2e/react-start/hmr/src/routeTree.gen.ts | 188 +- .../i18n-paraglide/src/routeTree.gen.ts | 26 +- .../src/routeTree.gen.ts | 36 +- .../query-integration/src/routeTree.gen.ts | 36 +- .../src/routeTree.gen.ts | 62 +- .../rsc-rsbuild/src/routeTree.gen.ts | 26 +- e2e/react-start/rsc/src/routeTree.gen.ts | 808 +++--- .../scroll-restoration/src/routeTree.gen.ts | 254 +- .../selective-ssr/src/routeTree.gen.ts | 26 +- .../src/routeTree.gen.ts | 110 +- .../src/routeTree.gen.ts | 72 +- .../server-functions/src/routeTree.gen.ts | 736 ++--- .../src/routeTree.gen.ts | 26 +- .../server-routes/src/routeTree.gen.ts | 144 +- e2e/react-start/spa-mode/src/routeTree.gen.ts | 26 +- .../src/routeTree.gen.ts | 26 +- .../start-manifest/src/routeTree.gen.ts | 242 +- .../src/routeTree.gen.ts | 26 +- .../streaming-ssr/src/routeTree.gen.ts | 180 +- .../transform-asset-urls/src/routeTree.gen.ts | 26 +- .../virtual-routes/src/routeTree.gen.ts | 154 +- e2e/react-start/website/src/routeTree.gen.ts | 80 +- .../basepath-file-based/src/routeTree.gen.ts | 26 +- .../src/routeTree.gen.ts | 116 +- .../basic-file-based/src/routeTree.gen.ts | 2044 +++++++------- .../src/routeTree.gen.ts | 82 +- .../src/routeTree.gen.ts | 158 +- .../generator-cli-only/src/routeTree.gen.ts | 72 +- .../js-only-file-based/src/routeTree.gen.js | 36 +- .../src/routeTree.gen.ts | 82 +- .../view-transitions/src/routeTree.gen.ts | 44 +- .../basic-auth/src/routeTree.gen.ts | 70 +- .../basic-cloudflare/src/routeTree.gen.ts | 26 +- .../basic-solid-query/src/routeTree.gen.ts | 268 +- e2e/solid-start/basic/src/routeTree.gen.ts | 1108 ++++---- .../custom-basepath/src/routeTree.gen.ts | 222 +- .../deferred-hydration/src/routeTree.gen.ts | 72 +- .../query-integration/src/routeTree.gen.ts | 26 +- .../scroll-restoration/src/routeTree.gen.ts | 36 +- .../selective-ssr/src/routeTree.gen.ts | 36 +- .../src/routeTree.gen.ts | 82 +- .../server-functions/src/routeTree.gen.ts | 488 ++-- .../server-routes/src/routeTree.gen.ts | 70 +- .../src/routeTree.gen.ts | 26 +- e2e/solid-start/spa-mode/src/routeTree.gen.ts | 26 +- .../start-manifest/src/routeTree.gen.ts | 242 +- .../virtual-routes/src/routeTree.gen.ts | 154 +- e2e/solid-start/website/src/routeTree.gen.ts | 52 +- .../basepath-file-based/src/routeTree.gen.ts | 26 +- .../basic-file-based-jsx/src/routeTree.gen.ts | 366 +-- .../basic-file-based-sfc/src/routeTree.gen.ts | 428 +-- .../src/routeTree.gen.ts | 158 +- .../src/routeTree.gen.ts | 82 +- .../generator-cli-only/src/routeTree.gen.ts | 72 +- .../js-only-file-based/src/routeTree.gen.ts | 72 +- .../src/routeTree.gen.ts | 82 +- .../view-transitions/src/routeTree.gen.ts | 44 +- e2e/vue-start/basic-auth/src/routeTree.gen.ts | 70 +- .../basic-cloudflare/src/routeTree.gen.ts | 26 +- .../basic-vue-query/src/routeTree.gen.ts | 268 +- e2e/vue-start/basic/src/routeTree.gen.ts | 1038 +++---- .../custom-basepath/src/routeTree.gen.ts | 202 +- .../query-integration/src/routeTree.gen.ts | 26 +- .../scroll-restoration/src/routeTree.gen.ts | 36 +- .../selective-ssr/src/routeTree.gen.ts | 26 +- .../src/routeTree.gen.ts | 82 +- .../server-functions/src/routeTree.gen.ts | 474 ++-- .../server-routes/src/routeTree.gen.ts | 70 +- e2e/vue-start/spa-mode/src/routeTree.gen.ts | 26 +- .../start-manifest/src/routeTree.gen.ts | 242 +- .../virtual-routes/src/routeTree.gen.ts | 154 +- e2e/vue-start/website/src/routeTree.gen.ts | 52 +- .../src/routeTree.gen.ts | 62 +- .../authenticated-routes/src/routeTree.gen.ts | 62 +- .../basic-file-based/src/routeTree.gen.ts | 90 +- .../src/routeTree.gen.ts | 72 +- .../basic-ssr-file-based/src/routeTree.gen.ts | 26 +- .../src/routeTree.gen.ts | 26 +- .../src/routeTree.gen.ts | 132 +- .../src/routeTree.gen.ts | 102 +- .../react/i18n-paraglide/src/routeTree.gen.ts | 26 +- .../src/routeTree.gen.ts | 244 +- .../src/routeTree.gen.ts | 242 +- .../large-file-based/src/routeTree.gen.ts | 102 +- .../src/routeTree.gen.ts | 26 +- .../src/routeTree.gen.ts | 26 +- .../src/routeTree.gen.ts | 26 +- .../packages/router/src/routeTree.gen.ts | 26 +- .../packages/router/src/routeTree.gen.ts | 26 +- .../packages/router/src/routeTree.gen.ts | 26 +- .../src/routeTree.gen.ts | 36 +- .../react/start-bare/src/routeTree.gen.ts | 26 +- .../start-basic-auth/src/routeTree.gen.ts | 70 +- .../start-basic-authjs/src/routeTree.gen.ts | 36 +- .../src/routeTree.gen.ts | 270 +- .../src/routeTree.gen.ts | 244 +- .../start-basic-static/src/routeTree.gen.ts | 210 +- .../react/start-basic/src/routeTree.gen.ts | 270 +- examples/react/start-bun/src/routeTree.gen.ts | 26 +- .../start-clerk-basic/src/routeTree.gen.ts | 50 +- .../start-i18n-paraglide/src/routeTree.gen.ts | 26 +- .../react/start-large/src/routeTree.gen.ts | 102 +- .../start-material-ui/src/routeTree.gen.ts | 26 +- .../react/start-rscs/src/routeTree.gen.ts | 72 +- .../start-supabase-basic/src/routeTree.gen.ts | 70 +- .../react/start-workos/src/routeTree.gen.ts | 60 +- .../view-transitions/src/routeTree.gen.ts | 44 +- .../src/routeTree.gen.ts | 26 +- examples/react/with-trpc/src/routeTree.gen.ts | 26 +- .../src/routeTree.gen.ts | 62 +- .../authenticated-routes/src/routeTree.gen.ts | 62 +- .../basic-file-based/src/routeTree.gen.ts | 86 +- .../src/routeTree.gen.ts | 86 +- .../basic-ssr-file-based/src/routeTree.gen.ts | 26 +- .../src/routeTree.gen.ts | 106 +- .../src/routeTree.gen.ts | 132 +- .../src/routeTree.gen.ts | 102 +- .../solid/i18n-paraglide/src/routeTree.gen.ts | 26 +- .../src/routeTree.gen.ts | 244 +- .../src/routeTree.gen.ts | 242 +- .../large-file-based/src/routeTree.gen.ts | 102 +- .../src/routeTree.gen.ts | 26 +- .../src/routeTree.gen.ts | 26 +- .../src/routeTree.gen.ts | 26 +- .../packages/router/src/routeTree.gen.ts | 26 +- .../packages/router/src/routeTree.gen.ts | 26 +- .../packages/router/src/routeTree.gen.ts | 26 +- .../src/routeTree.gen.ts | 36 +- .../start-basic-auth/src/routeTree.gen.ts | 70 +- .../start-basic-authjs/src/routeTree.gen.ts | 36 +- .../src/routeTree.gen.ts | 270 +- .../start-basic-netlify/src/routeTree.gen.ts | 270 +- .../start-basic-nitro/src/routeTree.gen.ts | 270 +- .../src/routeTree.gen.ts | 244 +- .../start-basic-static/src/routeTree.gen.ts | 210 +- .../solid/start-basic/src/routeTree.gen.ts | 254 +- examples/solid/start-bun/src/routeTree.gen.ts | 26 +- .../src/routeTree.gen.ts | 36 +- .../solid/start-counter/src/routeTree.gen.ts | 26 +- .../start-i18n-paraglide/src/routeTree.gen.ts | 26 +- .../solid/start-large/src/routeTree.gen.ts | 102 +- .../start-supabase-basic/src/routeTree.gen.ts | 70 +- .../view-transitions/src/routeTree.gen.ts | 44 +- examples/solid/with-trpc/src/routeTree.gen.ts | 26 +- .../basic-file-based-jsx/src/routeTree.gen.ts | 366 +-- .../basic-file-based-sfc/src/routeTree.gen.ts | 428 +-- 183 files changed, 13609 insertions(+), 13609 deletions(-) diff --git a/benchmarks/client-nav/scenarios/async-pipeline/react/src/routeTree.gen.ts b/benchmarks/client-nav/scenarios/async-pipeline/react/src/routeTree.gen.ts index 2b3336859a..9c79bab826 100644 --- a/benchmarks/client-nav/scenarios/async-pipeline/react/src/routeTree.gen.ts +++ b/benchmarks/client-nav/scenarios/async-pipeline/react/src/routeTree.gen.ts @@ -9,25 +9,25 @@ // 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 NestedRouteImport } from './routes/nested' import { Route as IndexRouteImport } from './routes/index' -import { Route as SlowIdRouteImport } from './routes/slow.$id' -import { Route as NestedIdRouteImport } from './routes/nested.$id' +import { Route as NestedRouteImport } from './routes/nested' import { Route as CtxIdRouteImport } from './routes/ctx.$id' +import { Route as NestedIdRouteImport } from './routes/nested.$id' +import { Route as SlowIdRouteImport } from './routes/slow.$id' -const NestedRoute = NestedRouteImport.update({ - id: '/nested', - path: '/nested', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) -const SlowIdRoute = SlowIdRouteImport.update({ - id: '/slow/$id', - path: '/slow/$id', +const NestedRoute = NestedRouteImport.update({ + id: '/nested', + path: '/nested', + getParentRoute: () => rootRouteImport, +} as any) +const CtxIdRoute = CtxIdRouteImport.update({ + id: '/ctx/$id', + path: '/ctx/$id', getParentRoute: () => rootRouteImport, } as any) const NestedIdRoute = NestedIdRouteImport.update({ @@ -35,9 +35,9 @@ const NestedIdRoute = NestedIdRouteImport.update({ path: '/$id', getParentRoute: () => NestedRoute, } as any) -const CtxIdRoute = CtxIdRouteImport.update({ - id: '/ctx/$id', - path: '/ctx/$id', +const SlowIdRoute = SlowIdRouteImport.update({ + id: '/slow/$id', + path: '/slow/$id', getParentRoute: () => rootRouteImport, } as any) @@ -80,13 +80,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/nested': { - id: '/nested' - path: '/nested' - fullPath: '/nested' - preLoaderRoute: typeof NestedRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -94,11 +87,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/slow/$id': { - id: '/slow/$id' - path: '/slow/$id' - fullPath: '/slow/$id' - preLoaderRoute: typeof SlowIdRouteImport + '/nested': { + id: '/nested' + path: '/nested' + fullPath: '/nested' + preLoaderRoute: typeof NestedRouteImport + parentRoute: typeof rootRouteImport + } + '/ctx/$id': { + id: '/ctx/$id' + path: '/ctx/$id' + fullPath: '/ctx/$id' + preLoaderRoute: typeof CtxIdRouteImport parentRoute: typeof rootRouteImport } '/nested/$id': { @@ -108,11 +108,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof NestedIdRouteImport parentRoute: typeof NestedRoute } - '/ctx/$id': { - id: '/ctx/$id' - path: '/ctx/$id' - fullPath: '/ctx/$id' - preLoaderRoute: typeof CtxIdRouteImport + '/slow/$id': { + id: '/slow/$id' + path: '/slow/$id' + fullPath: '/slow/$id' + preLoaderRoute: typeof SlowIdRouteImport parentRoute: typeof rootRouteImport } } diff --git a/benchmarks/client-nav/scenarios/control-flow/react/src/routeTree.gen.ts b/benchmarks/client-nav/scenarios/control-flow/react/src/routeTree.gen.ts index 86edcc8472..7b6db9b237 100644 --- a/benchmarks/client-nav/scenarios/control-flow/react/src/routeTree.gen.ts +++ b/benchmarks/client-nav/scenarios/control-flow/react/src/routeTree.gen.ts @@ -9,21 +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 TargetRouteImport } from './routes/target' -import { Route as Hop2RouteImport } from './routes/hop2' -import { Route as Hop1RouteImport } from './routes/hop1' -import { Route as BrokenRouteImport } from './routes/broken' import { Route as IndexRouteImport } from './routes/index' +import { Route as BrokenRouteImport } from './routes/broken' +import { Route as Hop1RouteImport } from './routes/hop1' +import { Route as Hop2RouteImport } from './routes/hop2' +import { Route as TargetRouteImport } from './routes/target' import { Route as MissingIdRouteImport } from './routes/missing.$id' -const TargetRoute = TargetRouteImport.update({ - id: '/target', - path: '/target', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const Hop2Route = Hop2RouteImport.update({ - id: '/hop2', - path: '/hop2', +const BrokenRoute = BrokenRouteImport.update({ + id: '/broken', + path: '/broken', getParentRoute: () => rootRouteImport, } as any) const Hop1Route = Hop1RouteImport.update({ @@ -31,14 +31,14 @@ const Hop1Route = Hop1RouteImport.update({ path: '/hop1', getParentRoute: () => rootRouteImport, } as any) -const BrokenRoute = BrokenRouteImport.update({ - id: '/broken', - path: '/broken', +const Hop2Route = Hop2RouteImport.update({ + id: '/hop2', + path: '/hop2', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const TargetRoute = TargetRouteImport.update({ + id: '/target', + path: '/target', getParentRoute: () => rootRouteImport, } as any) const MissingIdRoute = MissingIdRouteImport.update({ @@ -98,18 +98,18 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/target': { - id: '/target' - path: '/target' - fullPath: '/target' - preLoaderRoute: typeof TargetRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/hop2': { - id: '/hop2' - path: '/hop2' - fullPath: '/hop2' - preLoaderRoute: typeof Hop2RouteImport + '/broken': { + id: '/broken' + path: '/broken' + fullPath: '/broken' + preLoaderRoute: typeof BrokenRouteImport parentRoute: typeof rootRouteImport } '/hop1': { @@ -119,18 +119,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof Hop1RouteImport parentRoute: typeof rootRouteImport } - '/broken': { - id: '/broken' - path: '/broken' - fullPath: '/broken' - preLoaderRoute: typeof BrokenRouteImport + '/hop2': { + id: '/hop2' + path: '/hop2' + fullPath: '/hop2' + preLoaderRoute: typeof Hop2RouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/target': { + id: '/target' + path: '/target' + fullPath: '/target' + preLoaderRoute: typeof TargetRouteImport parentRoute: typeof rootRouteImport } '/missing/$id': { diff --git a/benchmarks/client-nav/scenarios/head/react/src/routeTree.gen.ts b/benchmarks/client-nav/scenarios/head/react/src/routeTree.gen.ts index 5c80a3104e..88867d9190 100644 --- a/benchmarks/client-nav/scenarios/head/react/src/routeTree.gen.ts +++ b/benchmarks/client-nav/scenarios/head/react/src/routeTree.gen.ts @@ -9,15 +9,15 @@ // 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 SettingsRouteImport } from './routes/settings' -import { Route as DocsRouteImport } from './routes/docs' import { Route as IndexRouteImport } from './routes/index' -import { Route as DocsSectionRouteImport } from './routes/docs.$section' +import { Route as DocsRouteImport } from './routes/docs' +import { Route as SettingsRouteImport } from './routes/settings' import { Route as ArticlesIdRouteImport } from './routes/articles.$id' +import { Route as DocsSectionRouteImport } from './routes/docs.$section' -const SettingsRoute = SettingsRouteImport.update({ - id: '/settings', - path: '/settings', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const DocsRoute = DocsRouteImport.update({ @@ -25,21 +25,21 @@ const DocsRoute = DocsRouteImport.update({ path: '/docs', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const SettingsRoute = SettingsRouteImport.update({ + id: '/settings', + path: '/settings', getParentRoute: () => rootRouteImport, } as any) -const DocsSectionRoute = DocsSectionRouteImport.update({ - id: '/$section', - path: '/$section', - getParentRoute: () => DocsRoute, -} as any) const ArticlesIdRoute = ArticlesIdRouteImport.update({ id: '/articles/$id', path: '/articles/$id', getParentRoute: () => rootRouteImport, } as any) +const DocsSectionRoute = DocsSectionRouteImport.update({ + id: '/$section', + path: '/$section', + getParentRoute: () => DocsRoute, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -86,11 +86,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/settings': { - id: '/settings' - path: '/settings' - fullPath: '/settings' - preLoaderRoute: typeof SettingsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/docs': { @@ -100,20 +100,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DocsRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/settings': { + id: '/settings' + path: '/settings' + fullPath: '/settings' + preLoaderRoute: typeof SettingsRouteImport parentRoute: typeof rootRouteImport } - '/docs/$section': { - id: '/docs/$section' - path: '/$section' - fullPath: '/docs/$section' - preLoaderRoute: typeof DocsSectionRouteImport - parentRoute: typeof DocsRoute - } '/articles/$id': { id: '/articles/$id' path: '/articles/$id' @@ -121,6 +114,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ArticlesIdRouteImport parentRoute: typeof rootRouteImport } + '/docs/$section': { + id: '/docs/$section' + path: '/$section' + fullPath: '/docs/$section' + preLoaderRoute: typeof DocsSectionRouteImport + parentRoute: typeof DocsRoute + } } } diff --git a/benchmarks/client-nav/scenarios/history/react/src/routeTree.gen.ts b/benchmarks/client-nav/scenarios/history/react/src/routeTree.gen.ts index 61cc83c4c8..21bb12ba35 100644 --- a/benchmarks/client-nav/scenarios/history/react/src/routeTree.gen.ts +++ b/benchmarks/client-nav/scenarios/history/react/src/routeTree.gen.ts @@ -9,24 +9,19 @@ // 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 GalleryRouteImport } from './routes/gallery' import { Route as IndexRouteImport } from './routes/index' -import { Route as PhotosPhotoIdRouteImport } from './routes/photos.$photoId' +import { Route as GalleryRouteImport } from './routes/gallery' import { Route as PagesNRouteImport } from './routes/pages.$n' +import { Route as PhotosPhotoIdRouteImport } from './routes/photos.$photoId' -const GalleryRoute = GalleryRouteImport.update({ - id: '/gallery', - path: '/gallery', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) -const PhotosPhotoIdRoute = PhotosPhotoIdRouteImport.update({ - id: '/photos/$photoId', - path: '/photos/$photoId', +const GalleryRoute = GalleryRouteImport.update({ + id: '/gallery', + path: '/gallery', getParentRoute: () => rootRouteImport, } as any) const PagesNRoute = PagesNRouteImport.update({ @@ -34,6 +29,11 @@ const PagesNRoute = PagesNRouteImport.update({ path: '/pages/$n', getParentRoute: () => rootRouteImport, } as any) +const PhotosPhotoIdRoute = PhotosPhotoIdRouteImport.update({ + id: '/photos/$photoId', + path: '/photos/$photoId', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -71,13 +71,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/gallery': { - id: '/gallery' - path: '/gallery' - fullPath: '/gallery' - preLoaderRoute: typeof GalleryRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -85,11 +78,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/photos/$photoId': { - id: '/photos/$photoId' - path: '/photos/$photoId' - fullPath: '/photos/$photoId' - preLoaderRoute: typeof PhotosPhotoIdRouteImport + '/gallery': { + id: '/gallery' + path: '/gallery' + fullPath: '/gallery' + preLoaderRoute: typeof GalleryRouteImport parentRoute: typeof rootRouteImport } '/pages/$n': { @@ -99,6 +92,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PagesNRouteImport parentRoute: typeof rootRouteImport } + '/photos/$photoId': { + id: '/photos/$photoId' + path: '/photos/$photoId' + fullPath: '/photos/$photoId' + preLoaderRoute: typeof PhotosPhotoIdRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/benchmarks/client-nav/scenarios/links/react/src/routeTree.gen.ts b/benchmarks/client-nav/scenarios/links/react/src/routeTree.gen.ts index 97ec042c1c..6e0c29ca88 100644 --- a/benchmarks/client-nav/scenarios/links/react/src/routeTree.gen.ts +++ b/benchmarks/client-nav/scenarios/links/react/src/routeTree.gen.ts @@ -9,20 +9,20 @@ // 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 AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' +import { Route as AboutRouteImport } from './routes/about' import { Route as ItemsIdRouteImport } from './routes/items.$id' -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) const ItemsIdRoute = ItemsIdRouteImport.update({ id: '/items/$id', path: '/items/$id', @@ -61,13 +61,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -75,6 +68,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } '/items/$id': { id: '/items/$id' path: '/items/$id' diff --git a/benchmarks/client-nav/scenarios/loaders/react/src/routeTree.gen.ts b/benchmarks/client-nav/scenarios/loaders/react/src/routeTree.gen.ts index 50f0a8863b..35908a225f 100644 --- a/benchmarks/client-nav/scenarios/loaders/react/src/routeTree.gen.ts +++ b/benchmarks/client-nav/scenarios/loaders/react/src/routeTree.gen.ts @@ -9,24 +9,19 @@ // 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 DepsRouteImport } from './routes/deps' import { Route as IndexRouteImport } from './routes/index' -import { Route as FreshIdRouteImport } from './routes/fresh.$id' +import { Route as DepsRouteImport } from './routes/deps' import { Route as CachedIdRouteImport } from './routes/cached.$id' +import { Route as FreshIdRouteImport } from './routes/fresh.$id' -const DepsRoute = DepsRouteImport.update({ - id: '/deps', - path: '/deps', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) -const FreshIdRoute = FreshIdRouteImport.update({ - id: '/fresh/$id', - path: '/fresh/$id', +const DepsRoute = DepsRouteImport.update({ + id: '/deps', + path: '/deps', getParentRoute: () => rootRouteImport, } as any) const CachedIdRoute = CachedIdRouteImport.update({ @@ -34,6 +29,11 @@ const CachedIdRoute = CachedIdRouteImport.update({ path: '/cached/$id', getParentRoute: () => rootRouteImport, } as any) +const FreshIdRoute = FreshIdRouteImport.update({ + id: '/fresh/$id', + path: '/fresh/$id', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -71,13 +71,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/deps': { - id: '/deps' - path: '/deps' - fullPath: '/deps' - preLoaderRoute: typeof DepsRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -85,11 +78,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/fresh/$id': { - id: '/fresh/$id' - path: '/fresh/$id' - fullPath: '/fresh/$id' - preLoaderRoute: typeof FreshIdRouteImport + '/deps': { + id: '/deps' + path: '/deps' + fullPath: '/deps' + preLoaderRoute: typeof DepsRouteImport parentRoute: typeof rootRouteImport } '/cached/$id': { @@ -99,6 +92,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof CachedIdRouteImport parentRoute: typeof rootRouteImport } + '/fresh/$id': { + id: '/fresh/$id' + path: '/fresh/$id' + fullPath: '/fresh/$id' + preLoaderRoute: typeof FreshIdRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/benchmarks/client-nav/scenarios/mount/react/src/routeTree.gen.ts b/benchmarks/client-nav/scenarios/mount/react/src/routeTree.gen.ts index a796abe0ec..f9b7e5c268 100644 --- a/benchmarks/client-nav/scenarios/mount/react/src/routeTree.gen.ts +++ b/benchmarks/client-nav/scenarios/mount/react/src/routeTree.gen.ts @@ -9,25 +9,30 @@ // 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 ShopRouteImport } from './routes/shop' -import { Route as SettingsRouteImport } from './routes/settings' -import { Route as SearchRouteImport } from './routes/search' -import { Route as BlogRouteImport } from './routes/blog' -import { Route as AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' -import { Route as ShopIndexRouteImport } from './routes/shop.index' +import { Route as AboutRouteImport } from './routes/about' +import { Route as BlogRouteImport } from './routes/blog' +import { Route as SearchRouteImport } from './routes/search' +import { Route as SettingsRouteImport } from './routes/settings' +import { Route as ShopRouteImport } from './routes/shop' import { Route as BlogIndexRouteImport } from './routes/blog.index' -import { Route as ShopProductIdRouteImport } from './routes/shop.$productId' import { Route as BlogSlugRouteImport } from './routes/blog.$slug' +import { Route as ShopIndexRouteImport } from './routes/shop.index' +import { Route as ShopProductIdRouteImport } from './routes/shop.$productId' -const ShopRoute = ShopRouteImport.update({ - id: '/shop', - path: '/shop', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const SettingsRoute = SettingsRouteImport.update({ - id: '/settings', - path: '/settings', +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) +const BlogRoute = BlogRouteImport.update({ + id: '/blog', + path: '/blog', getParentRoute: () => rootRouteImport, } as any) const SearchRoute = SearchRouteImport.update({ @@ -35,41 +40,36 @@ const SearchRoute = SearchRouteImport.update({ path: '/search', getParentRoute: () => rootRouteImport, } as any) -const BlogRoute = BlogRouteImport.update({ - id: '/blog', - path: '/blog', +const SettingsRoute = SettingsRouteImport.update({ + id: '/settings', + path: '/settings', getParentRoute: () => rootRouteImport, } as any) -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', +const ShopRoute = ShopRouteImport.update({ + id: '/shop', + path: '/shop', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ +const BlogIndexRoute = BlogIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => rootRouteImport, + getParentRoute: () => BlogRoute, +} as any) +const BlogSlugRoute = BlogSlugRouteImport.update({ + id: '/$slug', + path: '/$slug', + getParentRoute: () => BlogRoute, } as any) const ShopIndexRoute = ShopIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => ShopRoute, } as any) -const BlogIndexRoute = BlogIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => BlogRoute, -} as any) const ShopProductIdRoute = ShopProductIdRouteImport.update({ id: '/$productId', path: '/$productId', getParentRoute: () => ShopRoute, } as any) -const BlogSlugRoute = BlogSlugRouteImport.update({ - id: '/$slug', - path: '/$slug', - getParentRoute: () => BlogRoute, -} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -154,18 +154,25 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/shop': { - id: '/shop' - path: '/shop' - fullPath: '/shop' - preLoaderRoute: typeof ShopRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/settings': { - id: '/settings' - path: '/settings' - fullPath: '/settings' - preLoaderRoute: typeof SettingsRouteImport + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } + '/blog': { + id: '/blog' + path: '/blog' + fullPath: '/blog' + preLoaderRoute: typeof BlogRouteImport parentRoute: typeof rootRouteImport } '/search': { @@ -175,26 +182,33 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof SearchRouteImport parentRoute: typeof rootRouteImport } - '/blog': { - id: '/blog' - path: '/blog' - fullPath: '/blog' - preLoaderRoute: typeof BlogRouteImport + '/settings': { + id: '/settings' + path: '/settings' + fullPath: '/settings' + preLoaderRoute: typeof SettingsRouteImport parentRoute: typeof rootRouteImport } - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport + '/shop': { + id: '/shop' + path: '/shop' + fullPath: '/shop' + preLoaderRoute: typeof ShopRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' + '/blog/': { + id: '/blog/' path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport + fullPath: '/blog/' + preLoaderRoute: typeof BlogIndexRouteImport + parentRoute: typeof BlogRoute + } + '/blog/$slug': { + id: '/blog/$slug' + path: '/$slug' + fullPath: '/blog/$slug' + preLoaderRoute: typeof BlogSlugRouteImport + parentRoute: typeof BlogRoute } '/shop/': { id: '/shop/' @@ -203,13 +217,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ShopIndexRouteImport parentRoute: typeof ShopRoute } - '/blog/': { - id: '/blog/' - path: '/' - fullPath: '/blog/' - preLoaderRoute: typeof BlogIndexRouteImport - parentRoute: typeof BlogRoute - } '/shop/$productId': { id: '/shop/$productId' path: '/$productId' @@ -217,13 +224,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ShopProductIdRouteImport parentRoute: typeof ShopRoute } - '/blog/$slug': { - id: '/blog/$slug' - path: '/$slug' - fullPath: '/blog/$slug' - preLoaderRoute: typeof BlogSlugRouteImport - parentRoute: typeof BlogRoute - } } } diff --git a/benchmarks/client-nav/scenarios/preload/react/src/routeTree.gen.ts b/benchmarks/client-nav/scenarios/preload/react/src/routeTree.gen.ts index 0c76e85974..20619341c0 100644 --- a/benchmarks/client-nav/scenarios/preload/react/src/routeTree.gen.ts +++ b/benchmarks/client-nav/scenarios/preload/react/src/routeTree.gen.ts @@ -9,20 +9,20 @@ // 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 DocsRouteImport } from './routes/docs' import { Route as IndexRouteImport } from './routes/index' +import { Route as DocsRouteImport } from './routes/docs' import { Route as SectionsSectionRouteImport } from './routes/sections.$section' -const DocsRoute = DocsRouteImport.update({ - id: '/docs', - path: '/docs', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const DocsRoute = DocsRouteImport.update({ + id: '/docs', + path: '/docs', + getParentRoute: () => rootRouteImport, +} as any) const SectionsSectionRoute = SectionsSectionRouteImport.update({ id: '/sections/$section', path: '/sections/$section', @@ -61,13 +61,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/docs': { - id: '/docs' - path: '/docs' - fullPath: '/docs' - preLoaderRoute: typeof DocsRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -75,6 +68,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/docs': { + id: '/docs' + path: '/docs' + fullPath: '/docs' + preLoaderRoute: typeof DocsRouteImport + parentRoute: typeof rootRouteImport + } '/sections/$section': { id: '/sections/$section' path: '/sections/$section' diff --git a/benchmarks/client-nav/scenarios/route-tree-scale/react/src/routeTree.gen.ts b/benchmarks/client-nav/scenarios/route-tree-scale/react/src/routeTree.gen.ts index b58b75180f..f8eeb73dda 100644 --- a/benchmarks/client-nav/scenarios/route-tree-scale/react/src/routeTree.gen.ts +++ b/benchmarks/client-nav/scenarios/route-tree-scale/react/src/routeTree.gen.ts @@ -9,58 +9,62 @@ // 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 SecFRouteImport } from './routes/sec-f' -import { Route as SecERouteImport } from './routes/sec-e' -import { Route as SecDRouteImport } from './routes/sec-d' -import { Route as SecCRouteImport } from './routes/sec-c' -import { Route as SecBRouteImport } from './routes/sec-b' -import { Route as SecARouteImport } from './routes/sec-a' -import { Route as LayoutRouteImport } from './routes/_layout' import { Route as IndexRouteImport } from './routes/index' -import { Route as SecFIndexRouteImport } from './routes/sec-f.index' -import { Route as SecEIndexRouteImport } from './routes/sec-e.index' -import { Route as SecDIndexRouteImport } from './routes/sec-d.index' -import { Route as SecCIndexRouteImport } from './routes/sec-c.index' -import { Route as SecBIndexRouteImport } from './routes/sec-b.index' +import { Route as LayoutRouteImport } from './routes/_layout' +import { Route as SecARouteImport } from './routes/sec-a' +import { Route as SecBRouteImport } from './routes/sec-b' +import { Route as SecCRouteImport } from './routes/sec-c' +import { Route as SecDRouteImport } from './routes/sec-d' +import { Route as SecERouteImport } from './routes/sec-e' +import { Route as SecFRouteImport } from './routes/sec-f' +import { Route as marketingPricingRouteImport } from './routes/(marketing)/pricing' +import { Route as marketingPromoRouteImport } from './routes/(marketing)/promo' +import { Route as LayoutAlphaRouteImport } from './routes/_layout.alpha' +import { Route as LayoutBetaRouteImport } from './routes/_layout.beta' +import { Route as FilesSplatRouteImport } from './routes/files.$' +import { Route as ReleaseVChar123versionChar125RouteImport } from './routes/release.v{$version}' import { Route as SecAIndexRouteImport } from './routes/sec-a.index' -import { Route as SecFSettingsRouteImport } from './routes/sec-f.settings' -import { Route as SecFAboutRouteImport } from './routes/sec-f.about' -import { Route as SecFIdRouteImport } from './routes/sec-f.$id' -import { Route as SecESettingsRouteImport } from './routes/sec-e.settings' -import { Route as SecEAboutRouteImport } from './routes/sec-e.about' -import { Route as SecEIdRouteImport } from './routes/sec-e.$id' -import { Route as SecDSettingsRouteImport } from './routes/sec-d.settings' -import { Route as SecDAboutRouteImport } from './routes/sec-d.about' -import { Route as SecDIdRouteImport } from './routes/sec-d.$id' -import { Route as SecCSettingsRouteImport } from './routes/sec-c.settings' -import { Route as SecCAboutRouteImport } from './routes/sec-c.about' -import { Route as SecCIdRouteImport } from './routes/sec-c.$id' -import { Route as SecBSettingsRouteImport } from './routes/sec-b.settings' -import { Route as SecBAboutRouteImport } from './routes/sec-b.about' -import { Route as SecBIdRouteImport } from './routes/sec-b.$id' -import { Route as SecASettingsRouteImport } from './routes/sec-a.settings' -import { Route as SecAAboutRouteImport } from './routes/sec-a.about' import { Route as SecAIdRouteImport } from './routes/sec-a.$id' -import { Route as ReleaseVChar123versionChar125RouteImport } from './routes/release.v{$version}' -import { Route as FilesSplatRouteImport } from './routes/files.$' -import { Route as LayoutBetaRouteImport } from './routes/_layout.beta' -import { Route as LayoutAlphaRouteImport } from './routes/_layout.alpha' -import { Route as marketingPromoRouteImport } from './routes/(marketing)/promo' -import { Route as marketingPricingRouteImport } from './routes/(marketing)/pricing' +import { Route as SecAAboutRouteImport } from './routes/sec-a.about' +import { Route as SecASettingsRouteImport } from './routes/sec-a.settings' +import { Route as SecBIndexRouteImport } from './routes/sec-b.index' +import { Route as SecBIdRouteImport } from './routes/sec-b.$id' +import { Route as SecBAboutRouteImport } from './routes/sec-b.about' +import { Route as SecBSettingsRouteImport } from './routes/sec-b.settings' +import { Route as SecCIndexRouteImport } from './routes/sec-c.index' +import { Route as SecCIdRouteImport } from './routes/sec-c.$id' +import { Route as SecCAboutRouteImport } from './routes/sec-c.about' +import { Route as SecCSettingsRouteImport } from './routes/sec-c.settings' +import { Route as SecDIndexRouteImport } from './routes/sec-d.index' +import { Route as SecDIdRouteImport } from './routes/sec-d.$id' +import { Route as SecDAboutRouteImport } from './routes/sec-d.about' +import { Route as SecDSettingsRouteImport } from './routes/sec-d.settings' +import { Route as SecEIndexRouteImport } from './routes/sec-e.index' +import { Route as SecEIdRouteImport } from './routes/sec-e.$id' +import { Route as SecEAboutRouteImport } from './routes/sec-e.about' +import { Route as SecESettingsRouteImport } from './routes/sec-e.settings' +import { Route as SecFIndexRouteImport } from './routes/sec-f.index' +import { Route as SecFIdRouteImport } from './routes/sec-f.$id' +import { Route as SecFAboutRouteImport } from './routes/sec-f.about' +import { Route as SecFSettingsRouteImport } from './routes/sec-f.settings' -const SecFRoute = SecFRouteImport.update({ - id: '/sec-f', - path: '/sec-f', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const SecERoute = SecERouteImport.update({ - id: '/sec-e', - path: '/sec-e', +const LayoutRoute = LayoutRouteImport.update({ + id: '/_layout', getParentRoute: () => rootRouteImport, } as any) -const SecDRoute = SecDRouteImport.update({ - id: '/sec-d', - path: '/sec-d', +const SecARoute = SecARouteImport.update({ + id: '/sec-a', + path: '/sec-a', + getParentRoute: () => rootRouteImport, +} as any) +const SecBRoute = SecBRouteImport.update({ + id: '/sec-b', + path: '/sec-b', getParentRoute: () => rootRouteImport, } as any) const SecCRoute = SecCRouteImport.update({ @@ -68,108 +72,95 @@ const SecCRoute = SecCRouteImport.update({ path: '/sec-c', getParentRoute: () => rootRouteImport, } as any) -const SecBRoute = SecBRouteImport.update({ - id: '/sec-b', - path: '/sec-b', +const SecDRoute = SecDRouteImport.update({ + id: '/sec-d', + path: '/sec-d', getParentRoute: () => rootRouteImport, } as any) -const SecARoute = SecARouteImport.update({ - id: '/sec-a', - path: '/sec-a', +const SecERoute = SecERouteImport.update({ + id: '/sec-e', + path: '/sec-e', getParentRoute: () => rootRouteImport, } as any) -const LayoutRoute = LayoutRouteImport.update({ - id: '/_layout', +const SecFRoute = SecFRouteImport.update({ + id: '/sec-f', + path: '/sec-f', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const marketingPricingRoute = marketingPricingRouteImport.update({ + id: '/(marketing)/pricing', + path: '/pricing', getParentRoute: () => rootRouteImport, } as any) -const SecFIndexRoute = SecFIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => SecFRoute, -} as any) -const SecEIndexRoute = SecEIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => SecERoute, +const marketingPromoRoute = marketingPromoRouteImport.update({ + id: '/(marketing)/promo', + path: '/promo', + getParentRoute: () => rootRouteImport, } as any) -const SecDIndexRoute = SecDIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => SecDRoute, +const LayoutAlphaRoute = LayoutAlphaRouteImport.update({ + id: '/alpha', + path: '/alpha', + getParentRoute: () => LayoutRoute, } as any) -const SecCIndexRoute = SecCIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => SecCRoute, +const LayoutBetaRoute = LayoutBetaRouteImport.update({ + id: '/beta', + path: '/beta', + getParentRoute: () => LayoutRoute, } as any) -const SecBIndexRoute = SecBIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => SecBRoute, +const FilesSplatRoute = FilesSplatRouteImport.update({ + id: '/files/$', + path: '/files/$', + getParentRoute: () => rootRouteImport, } as any) +const ReleaseVChar123versionChar125Route = + ReleaseVChar123versionChar125RouteImport.update({ + id: '/release/v{$version}', + path: '/release/v{$version}', + getParentRoute: () => rootRouteImport, + } as any) const SecAIndexRoute = SecAIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => SecARoute, } as any) -const SecFSettingsRoute = SecFSettingsRouteImport.update({ - id: '/settings', - path: '/settings', - getParentRoute: () => SecFRoute, +const SecAIdRoute = SecAIdRouteImport.update({ + id: '/$id', + path: '/$id', + getParentRoute: () => SecARoute, } as any) -const SecFAboutRoute = SecFAboutRouteImport.update({ +const SecAAboutRoute = SecAAboutRouteImport.update({ id: '/about', path: '/about', - getParentRoute: () => SecFRoute, -} as any) -const SecFIdRoute = SecFIdRouteImport.update({ - id: '/$id', - path: '/$id', - getParentRoute: () => SecFRoute, + getParentRoute: () => SecARoute, } as any) -const SecESettingsRoute = SecESettingsRouteImport.update({ +const SecASettingsRoute = SecASettingsRouteImport.update({ id: '/settings', path: '/settings', - getParentRoute: () => SecERoute, + getParentRoute: () => SecARoute, } as any) -const SecEAboutRoute = SecEAboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => SecERoute, +const SecBIndexRoute = SecBIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => SecBRoute, } as any) -const SecEIdRoute = SecEIdRouteImport.update({ +const SecBIdRoute = SecBIdRouteImport.update({ id: '/$id', path: '/$id', - getParentRoute: () => SecERoute, -} as any) -const SecDSettingsRoute = SecDSettingsRouteImport.update({ - id: '/settings', - path: '/settings', - getParentRoute: () => SecDRoute, + getParentRoute: () => SecBRoute, } as any) -const SecDAboutRoute = SecDAboutRouteImport.update({ +const SecBAboutRoute = SecBAboutRouteImport.update({ id: '/about', path: '/about', - getParentRoute: () => SecDRoute, -} as any) -const SecDIdRoute = SecDIdRouteImport.update({ - id: '/$id', - path: '/$id', - getParentRoute: () => SecDRoute, + getParentRoute: () => SecBRoute, } as any) -const SecCSettingsRoute = SecCSettingsRouteImport.update({ +const SecBSettingsRoute = SecBSettingsRouteImport.update({ id: '/settings', path: '/settings', - getParentRoute: () => SecCRoute, + getParentRoute: () => SecBRoute, } as any) -const SecCAboutRoute = SecCAboutRouteImport.update({ - id: '/about', - path: '/about', +const SecCIndexRoute = SecCIndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => SecCRoute, } as any) const SecCIdRoute = SecCIdRouteImport.update({ @@ -177,66 +168,75 @@ const SecCIdRoute = SecCIdRouteImport.update({ path: '/$id', getParentRoute: () => SecCRoute, } as any) -const SecBSettingsRoute = SecBSettingsRouteImport.update({ +const SecCAboutRoute = SecCAboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => SecCRoute, +} as any) +const SecCSettingsRoute = SecCSettingsRouteImport.update({ id: '/settings', path: '/settings', - getParentRoute: () => SecBRoute, + getParentRoute: () => SecCRoute, } as any) -const SecBAboutRoute = SecBAboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => SecBRoute, +const SecDIndexRoute = SecDIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => SecDRoute, } as any) -const SecBIdRoute = SecBIdRouteImport.update({ +const SecDIdRoute = SecDIdRouteImport.update({ id: '/$id', path: '/$id', - getParentRoute: () => SecBRoute, + getParentRoute: () => SecDRoute, } as any) -const SecASettingsRoute = SecASettingsRouteImport.update({ +const SecDAboutRoute = SecDAboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => SecDRoute, +} as any) +const SecDSettingsRoute = SecDSettingsRouteImport.update({ id: '/settings', path: '/settings', - getParentRoute: () => SecARoute, + getParentRoute: () => SecDRoute, } as any) -const SecAAboutRoute = SecAAboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => SecARoute, +const SecEIndexRoute = SecEIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => SecERoute, } as any) -const SecAIdRoute = SecAIdRouteImport.update({ +const SecEIdRoute = SecEIdRouteImport.update({ id: '/$id', path: '/$id', - getParentRoute: () => SecARoute, + getParentRoute: () => SecERoute, } as any) -const ReleaseVChar123versionChar125Route = - ReleaseVChar123versionChar125RouteImport.update({ - id: '/release/v{$version}', - path: '/release/v{$version}', - getParentRoute: () => rootRouteImport, - } as any) -const FilesSplatRoute = FilesSplatRouteImport.update({ - id: '/files/$', - path: '/files/$', - getParentRoute: () => rootRouteImport, +const SecEAboutRoute = SecEAboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => SecERoute, } as any) -const LayoutBetaRoute = LayoutBetaRouteImport.update({ - id: '/beta', - path: '/beta', - getParentRoute: () => LayoutRoute, +const SecESettingsRoute = SecESettingsRouteImport.update({ + id: '/settings', + path: '/settings', + getParentRoute: () => SecERoute, } as any) -const LayoutAlphaRoute = LayoutAlphaRouteImport.update({ - id: '/alpha', - path: '/alpha', - getParentRoute: () => LayoutRoute, +const SecFIndexRoute = SecFIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => SecFRoute, } as any) -const marketingPromoRoute = marketingPromoRouteImport.update({ - id: '/(marketing)/promo', - path: '/promo', - getParentRoute: () => rootRouteImport, +const SecFIdRoute = SecFIdRouteImport.update({ + id: '/$id', + path: '/$id', + getParentRoute: () => SecFRoute, } as any) -const marketingPricingRoute = marketingPricingRouteImport.update({ - id: '/(marketing)/pricing', - path: '/pricing', - getParentRoute: () => rootRouteImport, +const SecFAboutRoute = SecFAboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => SecFRoute, +} as any) +const SecFSettingsRoute = SecFSettingsRouteImport.update({ + id: '/settings', + path: '/settings', + getParentRoute: () => SecFRoute, } as any) export interface FileRoutesByFullPath { @@ -484,25 +484,32 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/sec-f': { - id: '/sec-f' - path: '/sec-f' - fullPath: '/sec-f' - preLoaderRoute: typeof SecFRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/sec-e': { - id: '/sec-e' - path: '/sec-e' - fullPath: '/sec-e' - preLoaderRoute: typeof SecERouteImport + '/_layout': { + id: '/_layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } - '/sec-d': { - id: '/sec-d' - path: '/sec-d' - fullPath: '/sec-d' - preLoaderRoute: typeof SecDRouteImport + '/sec-a': { + id: '/sec-a' + path: '/sec-a' + fullPath: '/sec-a' + preLoaderRoute: typeof SecARouteImport + parentRoute: typeof rootRouteImport + } + '/sec-b': { + id: '/sec-b' + path: '/sec-b' + fullPath: '/sec-b' + preLoaderRoute: typeof SecBRouteImport parentRoute: typeof rootRouteImport } '/sec-c': { @@ -512,68 +519,68 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof SecCRouteImport parentRoute: typeof rootRouteImport } - '/sec-b': { - id: '/sec-b' - path: '/sec-b' - fullPath: '/sec-b' - preLoaderRoute: typeof SecBRouteImport + '/sec-d': { + id: '/sec-d' + path: '/sec-d' + fullPath: '/sec-d' + preLoaderRoute: typeof SecDRouteImport parentRoute: typeof rootRouteImport } - '/sec-a': { - id: '/sec-a' - path: '/sec-a' - fullPath: '/sec-a' - preLoaderRoute: typeof SecARouteImport + '/sec-e': { + id: '/sec-e' + path: '/sec-e' + fullPath: '/sec-e' + preLoaderRoute: typeof SecERouteImport parentRoute: typeof rootRouteImport } - '/_layout': { - id: '/_layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutRouteImport + '/sec-f': { + id: '/sec-f' + path: '/sec-f' + fullPath: '/sec-f' + preLoaderRoute: typeof SecFRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/(marketing)/pricing': { + id: '/(marketing)/pricing' + path: '/pricing' + fullPath: '/pricing' + preLoaderRoute: typeof marketingPricingRouteImport parentRoute: typeof rootRouteImport } - '/sec-f/': { - id: '/sec-f/' - path: '/' - fullPath: '/sec-f/' - preLoaderRoute: typeof SecFIndexRouteImport - parentRoute: typeof SecFRoute + '/(marketing)/promo': { + id: '/(marketing)/promo' + path: '/promo' + fullPath: '/promo' + preLoaderRoute: typeof marketingPromoRouteImport + parentRoute: typeof rootRouteImport } - '/sec-e/': { - id: '/sec-e/' - path: '/' - fullPath: '/sec-e/' - preLoaderRoute: typeof SecEIndexRouteImport - parentRoute: typeof SecERoute + '/_layout/alpha': { + id: '/_layout/alpha' + path: '/alpha' + fullPath: '/alpha' + preLoaderRoute: typeof LayoutAlphaRouteImport + parentRoute: typeof LayoutRoute } - '/sec-d/': { - id: '/sec-d/' - path: '/' - fullPath: '/sec-d/' - preLoaderRoute: typeof SecDIndexRouteImport - parentRoute: typeof SecDRoute + '/_layout/beta': { + id: '/_layout/beta' + path: '/beta' + fullPath: '/beta' + preLoaderRoute: typeof LayoutBetaRouteImport + parentRoute: typeof LayoutRoute } - '/sec-c/': { - id: '/sec-c/' - path: '/' - fullPath: '/sec-c/' - preLoaderRoute: typeof SecCIndexRouteImport - parentRoute: typeof SecCRoute + '/files/$': { + id: '/files/$' + path: '/files/$' + fullPath: '/files/$' + preLoaderRoute: typeof FilesSplatRouteImport + parentRoute: typeof rootRouteImport } - '/sec-b/': { - id: '/sec-b/' - path: '/' - fullPath: '/sec-b/' - preLoaderRoute: typeof SecBIndexRouteImport - parentRoute: typeof SecBRoute + '/release/v{$version}': { + id: '/release/v{$version}' + path: '/release/v{$version}' + fullPath: '/release/v{$version}' + preLoaderRoute: typeof ReleaseVChar123versionChar125RouteImport + parentRoute: typeof rootRouteImport } '/sec-a/': { id: '/sec-a/' @@ -582,81 +589,60 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof SecAIndexRouteImport parentRoute: typeof SecARoute } - '/sec-f/settings': { - id: '/sec-f/settings' - path: '/settings' - fullPath: '/sec-f/settings' - preLoaderRoute: typeof SecFSettingsRouteImport - parentRoute: typeof SecFRoute + '/sec-a/$id': { + id: '/sec-a/$id' + path: '/$id' + fullPath: '/sec-a/$id' + preLoaderRoute: typeof SecAIdRouteImport + parentRoute: typeof SecARoute } - '/sec-f/about': { - id: '/sec-f/about' + '/sec-a/about': { + id: '/sec-a/about' path: '/about' - fullPath: '/sec-f/about' - preLoaderRoute: typeof SecFAboutRouteImport - parentRoute: typeof SecFRoute - } - '/sec-f/$id': { - id: '/sec-f/$id' - path: '/$id' - fullPath: '/sec-f/$id' - preLoaderRoute: typeof SecFIdRouteImport - parentRoute: typeof SecFRoute + fullPath: '/sec-a/about' + preLoaderRoute: typeof SecAAboutRouteImport + parentRoute: typeof SecARoute } - '/sec-e/settings': { - id: '/sec-e/settings' + '/sec-a/settings': { + id: '/sec-a/settings' path: '/settings' - fullPath: '/sec-e/settings' - preLoaderRoute: typeof SecESettingsRouteImport - parentRoute: typeof SecERoute + fullPath: '/sec-a/settings' + preLoaderRoute: typeof SecASettingsRouteImport + parentRoute: typeof SecARoute } - '/sec-e/about': { - id: '/sec-e/about' - path: '/about' - fullPath: '/sec-e/about' - preLoaderRoute: typeof SecEAboutRouteImport - parentRoute: typeof SecERoute + '/sec-b/': { + id: '/sec-b/' + path: '/' + fullPath: '/sec-b/' + preLoaderRoute: typeof SecBIndexRouteImport + parentRoute: typeof SecBRoute } - '/sec-e/$id': { - id: '/sec-e/$id' + '/sec-b/$id': { + id: '/sec-b/$id' path: '/$id' - fullPath: '/sec-e/$id' - preLoaderRoute: typeof SecEIdRouteImport - parentRoute: typeof SecERoute - } - '/sec-d/settings': { - id: '/sec-d/settings' - path: '/settings' - fullPath: '/sec-d/settings' - preLoaderRoute: typeof SecDSettingsRouteImport - parentRoute: typeof SecDRoute + fullPath: '/sec-b/$id' + preLoaderRoute: typeof SecBIdRouteImport + parentRoute: typeof SecBRoute } - '/sec-d/about': { - id: '/sec-d/about' + '/sec-b/about': { + id: '/sec-b/about' path: '/about' - fullPath: '/sec-d/about' - preLoaderRoute: typeof SecDAboutRouteImport - parentRoute: typeof SecDRoute - } - '/sec-d/$id': { - id: '/sec-d/$id' - path: '/$id' - fullPath: '/sec-d/$id' - preLoaderRoute: typeof SecDIdRouteImport - parentRoute: typeof SecDRoute + fullPath: '/sec-b/about' + preLoaderRoute: typeof SecBAboutRouteImport + parentRoute: typeof SecBRoute } - '/sec-c/settings': { - id: '/sec-c/settings' + '/sec-b/settings': { + id: '/sec-b/settings' path: '/settings' - fullPath: '/sec-c/settings' - preLoaderRoute: typeof SecCSettingsRouteImport - parentRoute: typeof SecCRoute + fullPath: '/sec-b/settings' + preLoaderRoute: typeof SecBSettingsRouteImport + parentRoute: typeof SecBRoute } - '/sec-c/about': { - id: '/sec-c/about' - path: '/about' - fullPath: '/sec-c/about' - preLoaderRoute: typeof SecCAboutRouteImport + '/sec-c/': { + id: '/sec-c/' + path: '/' + fullPath: '/sec-c/' + preLoaderRoute: typeof SecCIndexRouteImport parentRoute: typeof SecCRoute } '/sec-c/$id': { @@ -666,89 +652,103 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof SecCIdRouteImport parentRoute: typeof SecCRoute } - '/sec-b/settings': { - id: '/sec-b/settings' + '/sec-c/about': { + id: '/sec-c/about' + path: '/about' + fullPath: '/sec-c/about' + preLoaderRoute: typeof SecCAboutRouteImport + parentRoute: typeof SecCRoute + } + '/sec-c/settings': { + id: '/sec-c/settings' path: '/settings' - fullPath: '/sec-b/settings' - preLoaderRoute: typeof SecBSettingsRouteImport - parentRoute: typeof SecBRoute + fullPath: '/sec-c/settings' + preLoaderRoute: typeof SecCSettingsRouteImport + parentRoute: typeof SecCRoute } - '/sec-b/about': { - id: '/sec-b/about' - path: '/about' - fullPath: '/sec-b/about' - preLoaderRoute: typeof SecBAboutRouteImport - parentRoute: typeof SecBRoute + '/sec-d/': { + id: '/sec-d/' + path: '/' + fullPath: '/sec-d/' + preLoaderRoute: typeof SecDIndexRouteImport + parentRoute: typeof SecDRoute } - '/sec-b/$id': { - id: '/sec-b/$id' + '/sec-d/$id': { + id: '/sec-d/$id' path: '/$id' - fullPath: '/sec-b/$id' - preLoaderRoute: typeof SecBIdRouteImport - parentRoute: typeof SecBRoute + fullPath: '/sec-d/$id' + preLoaderRoute: typeof SecDIdRouteImport + parentRoute: typeof SecDRoute } - '/sec-a/settings': { - id: '/sec-a/settings' + '/sec-d/about': { + id: '/sec-d/about' + path: '/about' + fullPath: '/sec-d/about' + preLoaderRoute: typeof SecDAboutRouteImport + parentRoute: typeof SecDRoute + } + '/sec-d/settings': { + id: '/sec-d/settings' path: '/settings' - fullPath: '/sec-a/settings' - preLoaderRoute: typeof SecASettingsRouteImport - parentRoute: typeof SecARoute + fullPath: '/sec-d/settings' + preLoaderRoute: typeof SecDSettingsRouteImport + parentRoute: typeof SecDRoute } - '/sec-a/about': { - id: '/sec-a/about' - path: '/about' - fullPath: '/sec-a/about' - preLoaderRoute: typeof SecAAboutRouteImport - parentRoute: typeof SecARoute + '/sec-e/': { + id: '/sec-e/' + path: '/' + fullPath: '/sec-e/' + preLoaderRoute: typeof SecEIndexRouteImport + parentRoute: typeof SecERoute } - '/sec-a/$id': { - id: '/sec-a/$id' + '/sec-e/$id': { + id: '/sec-e/$id' path: '/$id' - fullPath: '/sec-a/$id' - preLoaderRoute: typeof SecAIdRouteImport - parentRoute: typeof SecARoute + fullPath: '/sec-e/$id' + preLoaderRoute: typeof SecEIdRouteImport + parentRoute: typeof SecERoute } - '/release/v{$version}': { - id: '/release/v{$version}' - path: '/release/v{$version}' - fullPath: '/release/v{$version}' - preLoaderRoute: typeof ReleaseVChar123versionChar125RouteImport - parentRoute: typeof rootRouteImport + '/sec-e/about': { + id: '/sec-e/about' + path: '/about' + fullPath: '/sec-e/about' + preLoaderRoute: typeof SecEAboutRouteImport + parentRoute: typeof SecERoute } - '/files/$': { - id: '/files/$' - path: '/files/$' - fullPath: '/files/$' - preLoaderRoute: typeof FilesSplatRouteImport - parentRoute: typeof rootRouteImport + '/sec-e/settings': { + id: '/sec-e/settings' + path: '/settings' + fullPath: '/sec-e/settings' + preLoaderRoute: typeof SecESettingsRouteImport + parentRoute: typeof SecERoute } - '/_layout/beta': { - id: '/_layout/beta' - path: '/beta' - fullPath: '/beta' - preLoaderRoute: typeof LayoutBetaRouteImport - parentRoute: typeof LayoutRoute + '/sec-f/': { + id: '/sec-f/' + path: '/' + fullPath: '/sec-f/' + preLoaderRoute: typeof SecFIndexRouteImport + parentRoute: typeof SecFRoute } - '/_layout/alpha': { - id: '/_layout/alpha' - path: '/alpha' - fullPath: '/alpha' - preLoaderRoute: typeof LayoutAlphaRouteImport - parentRoute: typeof LayoutRoute + '/sec-f/$id': { + id: '/sec-f/$id' + path: '/$id' + fullPath: '/sec-f/$id' + preLoaderRoute: typeof SecFIdRouteImport + parentRoute: typeof SecFRoute } - '/(marketing)/promo': { - id: '/(marketing)/promo' - path: '/promo' - fullPath: '/promo' - preLoaderRoute: typeof marketingPromoRouteImport - parentRoute: typeof rootRouteImport + '/sec-f/about': { + id: '/sec-f/about' + path: '/about' + fullPath: '/sec-f/about' + preLoaderRoute: typeof SecFAboutRouteImport + parentRoute: typeof SecFRoute } - '/(marketing)/pricing': { - id: '/(marketing)/pricing' - path: '/pricing' - fullPath: '/pricing' - preLoaderRoute: typeof marketingPricingRouteImport - parentRoute: typeof rootRouteImport + '/sec-f/settings': { + id: '/sec-f/settings' + path: '/settings' + fullPath: '/sec-f/settings' + preLoaderRoute: typeof SecFSettingsRouteImport + parentRoute: typeof SecFRoute } } } diff --git a/benchmarks/client-nav/scenarios/search-params/react/src/routeTree.gen.ts b/benchmarks/client-nav/scenarios/search-params/react/src/routeTree.gen.ts index 7380b704e1..cff9dca167 100644 --- a/benchmarks/client-nav/scenarios/search-params/react/src/routeTree.gen.ts +++ b/benchmarks/client-nav/scenarios/search-params/react/src/routeTree.gen.ts @@ -9,13 +9,13 @@ // 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 ProductsRouteImport } from './routes/products' -import { Route as CatalogRouteImport } from './routes/catalog' import { Route as IndexRouteImport } from './routes/index' +import { Route as CatalogRouteImport } from './routes/catalog' +import { Route as ProductsRouteImport } from './routes/products' -const ProductsRoute = ProductsRouteImport.update({ - id: '/products', - path: '/products', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const CatalogRoute = CatalogRouteImport.update({ @@ -23,9 +23,9 @@ const CatalogRoute = CatalogRouteImport.update({ path: '/catalog', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const ProductsRoute = ProductsRouteImport.update({ + id: '/products', + path: '/products', getParentRoute: () => rootRouteImport, } as any) @@ -61,11 +61,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/products': { - id: '/products' - path: '/products' - fullPath: '/products' - preLoaderRoute: typeof ProductsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/catalog': { @@ -75,11 +75,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof CatalogRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/products': { + id: '/products' + path: '/products' + fullPath: '/products' + preLoaderRoute: typeof ProductsRouteImport parentRoute: typeof rootRouteImport } } diff --git a/benchmarks/memory/server/scenarios/error-paths/react/src/routeTree.gen.ts b/benchmarks/memory/server/scenarios/error-paths/react/src/routeTree.gen.ts index fd373be6b4..4e8e4ce0e4 100644 --- a/benchmarks/memory/server/scenarios/error-paths/react/src/routeTree.gen.ts +++ b/benchmarks/memory/server/scenarios/error-paths/react/src/routeTree.gen.ts @@ -10,24 +10,19 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' -import { Route as TargetIdRouteImport } from './routes/target.$id' -import { Route as MissingIdRouteImport } from './routes/missing.$id' -import { Route as FromIdRouteImport } from './routes/from.$id' import { Route as BoomIdRouteImport } from './routes/boom.$id' +import { Route as FromIdRouteImport } from './routes/from.$id' +import { Route as MissingIdRouteImport } from './routes/missing.$id' +import { Route as TargetIdRouteImport } from './routes/target.$id' const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) -const TargetIdRoute = TargetIdRouteImport.update({ - id: '/target/$id', - path: '/target/$id', - getParentRoute: () => rootRouteImport, -} as any) -const MissingIdRoute = MissingIdRouteImport.update({ - id: '/missing/$id', - path: '/missing/$id', +const BoomIdRoute = BoomIdRouteImport.update({ + id: '/boom/$id', + path: '/boom/$id', getParentRoute: () => rootRouteImport, } as any) const FromIdRoute = FromIdRouteImport.update({ @@ -35,9 +30,14 @@ const FromIdRoute = FromIdRouteImport.update({ path: '/from/$id', getParentRoute: () => rootRouteImport, } as any) -const BoomIdRoute = BoomIdRouteImport.update({ - id: '/boom/$id', - path: '/boom/$id', +const MissingIdRoute = MissingIdRouteImport.update({ + id: '/missing/$id', + path: '/missing/$id', + getParentRoute: () => rootRouteImport, +} as any) +const TargetIdRoute = TargetIdRouteImport.update({ + id: '/target/$id', + path: '/target/$id', getParentRoute: () => rootRouteImport, } as any) @@ -94,18 +94,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/target/$id': { - id: '/target/$id' - path: '/target/$id' - fullPath: '/target/$id' - preLoaderRoute: typeof TargetIdRouteImport - parentRoute: typeof rootRouteImport - } - '/missing/$id': { - id: '/missing/$id' - path: '/missing/$id' - fullPath: '/missing/$id' - preLoaderRoute: typeof MissingIdRouteImport + '/boom/$id': { + id: '/boom/$id' + path: '/boom/$id' + fullPath: '/boom/$id' + preLoaderRoute: typeof BoomIdRouteImport parentRoute: typeof rootRouteImport } '/from/$id': { @@ -115,11 +108,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof FromIdRouteImport parentRoute: typeof rootRouteImport } - '/boom/$id': { - id: '/boom/$id' - path: '/boom/$id' - fullPath: '/boom/$id' - preLoaderRoute: typeof BoomIdRouteImport + '/missing/$id': { + id: '/missing/$id' + path: '/missing/$id' + fullPath: '/missing/$id' + preLoaderRoute: typeof MissingIdRouteImport + parentRoute: typeof rootRouteImport + } + '/target/$id': { + id: '/target/$id' + path: '/target/$id' + fullPath: '/target/$id' + preLoaderRoute: typeof TargetIdRouteImport parentRoute: typeof rootRouteImport } } diff --git a/benchmarks/memory/server/scenarios/peak-large-page/react/src/routeTree.gen.ts b/benchmarks/memory/server/scenarios/peak-large-page/react/src/routeTree.gen.ts index 60a1bd1fbc..6004590f9b 100644 --- a/benchmarks/memory/server/scenarios/peak-large-page/react/src/routeTree.gen.ts +++ b/benchmarks/memory/server/scenarios/peak-large-page/react/src/routeTree.gen.ts @@ -9,8 +9,8 @@ // 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 L1RouteImport } from './routes/l1' import { Route as IndexRouteImport } from './routes/index' +import { Route as L1RouteImport } from './routes/l1' import { Route as L1L2RouteImport } from './routes/l1.l2' import { Route as L1L2L3RouteImport } from './routes/l1.l2.l3' import { Route as L1L2L3L4RouteImport } from './routes/l1.l2.l3.l4' @@ -19,16 +19,16 @@ import { Route as L1L2L3L4L5L6RouteImport } from './routes/l1.l2.l3.l4.l5.l6' import { Route as L1L2L3L4L5L6L7RouteImport } from './routes/l1.l2.l3.l4.l5.l6.l7' import { Route as L1L2L3L4L5L6L7L8RouteImport } from './routes/l1.l2.l3.l4.l5.l6.l7.l8' -const L1Route = L1RouteImport.update({ - id: '/l1', - path: '/l1', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const L1Route = L1RouteImport.update({ + id: '/l1', + path: '/l1', + getParentRoute: () => rootRouteImport, +} as any) const L1L2Route = L1L2RouteImport.update({ id: '/l2', path: '/l2', @@ -142,13 +142,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/l1': { - id: '/l1' - path: '/l1' - fullPath: '/l1' - preLoaderRoute: typeof L1RouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -156,6 +149,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/l1': { + id: '/l1' + path: '/l1' + fullPath: '/l1' + preLoaderRoute: typeof L1RouteImport + parentRoute: typeof rootRouteImport + } '/l1/l2': { id: '/l1/l2' path: '/l2' diff --git a/benchmarks/ssr/scenarios/control-flow/react/src/routeTree.gen.ts b/benchmarks/ssr/scenarios/control-flow/react/src/routeTree.gen.ts index ed682bf62f..4d4424c222 100644 --- a/benchmarks/ssr/scenarios/control-flow/react/src/routeTree.gen.ts +++ b/benchmarks/ssr/scenarios/control-flow/react/src/routeTree.gen.ts @@ -10,25 +10,25 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' -import { Route as TargetIdRouteImport } from './routes/target.$id' -import { Route as MissingIdRouteImport } from './routes/missing.$id' -import { Route as HeadersIdRouteImport } from './routes/headers.$id' -import { Route as FromIdRouteImport } from './routes/from.$id' import { Route as BoomIdRouteImport } from './routes/boom.$id' +import { Route as FromIdRouteImport } from './routes/from.$id' +import { Route as HeadersIdRouteImport } from './routes/headers.$id' +import { Route as MissingIdRouteImport } from './routes/missing.$id' +import { Route as TargetIdRouteImport } from './routes/target.$id' const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) -const TargetIdRoute = TargetIdRouteImport.update({ - id: '/target/$id', - path: '/target/$id', +const BoomIdRoute = BoomIdRouteImport.update({ + id: '/boom/$id', + path: '/boom/$id', getParentRoute: () => rootRouteImport, } as any) -const MissingIdRoute = MissingIdRouteImport.update({ - id: '/missing/$id', - path: '/missing/$id', +const FromIdRoute = FromIdRouteImport.update({ + id: '/from/$id', + path: '/from/$id', getParentRoute: () => rootRouteImport, } as any) const HeadersIdRoute = HeadersIdRouteImport.update({ @@ -36,14 +36,14 @@ const HeadersIdRoute = HeadersIdRouteImport.update({ path: '/headers/$id', getParentRoute: () => rootRouteImport, } as any) -const FromIdRoute = FromIdRouteImport.update({ - id: '/from/$id', - path: '/from/$id', +const MissingIdRoute = MissingIdRouteImport.update({ + id: '/missing/$id', + path: '/missing/$id', getParentRoute: () => rootRouteImport, } as any) -const BoomIdRoute = BoomIdRouteImport.update({ - id: '/boom/$id', - path: '/boom/$id', +const TargetIdRoute = TargetIdRouteImport.update({ + id: '/target/$id', + path: '/target/$id', getParentRoute: () => rootRouteImport, } as any) @@ -117,18 +117,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/target/$id': { - id: '/target/$id' - path: '/target/$id' - fullPath: '/target/$id' - preLoaderRoute: typeof TargetIdRouteImport + '/boom/$id': { + id: '/boom/$id' + path: '/boom/$id' + fullPath: '/boom/$id' + preLoaderRoute: typeof BoomIdRouteImport parentRoute: typeof rootRouteImport } - '/missing/$id': { - id: '/missing/$id' - path: '/missing/$id' - fullPath: '/missing/$id' - preLoaderRoute: typeof MissingIdRouteImport + '/from/$id': { + id: '/from/$id' + path: '/from/$id' + fullPath: '/from/$id' + preLoaderRoute: typeof FromIdRouteImport parentRoute: typeof rootRouteImport } '/headers/$id': { @@ -138,18 +138,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof HeadersIdRouteImport parentRoute: typeof rootRouteImport } - '/from/$id': { - id: '/from/$id' - path: '/from/$id' - fullPath: '/from/$id' - preLoaderRoute: typeof FromIdRouteImport + '/missing/$id': { + id: '/missing/$id' + path: '/missing/$id' + fullPath: '/missing/$id' + preLoaderRoute: typeof MissingIdRouteImport parentRoute: typeof rootRouteImport } - '/boom/$id': { - id: '/boom/$id' - path: '/boom/$id' - fullPath: '/boom/$id' - preLoaderRoute: typeof BoomIdRouteImport + '/target/$id': { + id: '/target/$id' + path: '/target/$id' + fullPath: '/target/$id' + preLoaderRoute: typeof TargetIdRouteImport parentRoute: typeof rootRouteImport } } diff --git a/benchmarks/ssr/scenarios/global-middleware/react/src/routeTree.gen.ts b/benchmarks/ssr/scenarios/global-middleware/react/src/routeTree.gen.ts index aa916f0800..8dfaa80562 100644 --- a/benchmarks/ssr/scenarios/global-middleware/react/src/routeTree.gen.ts +++ b/benchmarks/ssr/scenarios/global-middleware/react/src/routeTree.gen.ts @@ -10,8 +10,8 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' -import { Route as PageIdRouteImport } from './routes/page.$id' import { Route as ApiFnUrlsRouteImport } from './routes/api.fn-urls' +import { Route as PageIdRouteImport } from './routes/page.$id' import { Route as ApiPingIdRouteImport } from './routes/api.ping.$id' const IndexRoute = IndexRouteImport.update({ @@ -19,16 +19,16 @@ const IndexRoute = IndexRouteImport.update({ path: '/', getParentRoute: () => rootRouteImport, } as any) -const PageIdRoute = PageIdRouteImport.update({ - id: '/page/$id', - path: '/page/$id', - getParentRoute: () => rootRouteImport, -} as any) const ApiFnUrlsRoute = ApiFnUrlsRouteImport.update({ id: '/api/fn-urls', path: '/api/fn-urls', getParentRoute: () => rootRouteImport, } as any) +const PageIdRoute = PageIdRouteImport.update({ + id: '/page/$id', + path: '/page/$id', + getParentRoute: () => rootRouteImport, +} as any) const ApiPingIdRoute = ApiPingIdRouteImport.update({ id: '/api/ping/$id', path: '/api/ping/$id', @@ -78,13 +78,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/page/$id': { - id: '/page/$id' - path: '/page/$id' - fullPath: '/page/$id' - preLoaderRoute: typeof PageIdRouteImport - parentRoute: typeof rootRouteImport - } '/api/fn-urls': { id: '/api/fn-urls' path: '/api/fn-urls' @@ -92,6 +85,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ApiFnUrlsRouteImport parentRoute: typeof rootRouteImport } + '/page/$id': { + id: '/page/$id' + path: '/page/$id' + fullPath: '/page/$id' + preLoaderRoute: typeof PageIdRouteImport + parentRoute: typeof rootRouteImport + } '/api/ping/$id': { id: '/api/ping/$id' path: '/api/ping/$id' diff --git a/benchmarks/ssr/scenarios/serialization/react/src/routeTree.gen.ts b/benchmarks/ssr/scenarios/serialization/react/src/routeTree.gen.ts index 92bf0bb732..49bdbd036b 100644 --- a/benchmarks/ssr/scenarios/serialization/react/src/routeTree.gen.ts +++ b/benchmarks/ssr/scenarios/serialization/react/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 RichIdRouteImport } from './routes/rich.$id' import { Route as PlainIdRouteImport } from './routes/plain.$id' +import { Route as RichIdRouteImport } from './routes/rich.$id' -const RichIdRoute = RichIdRouteImport.update({ - id: '/rich/$id', - path: '/rich/$id', - getParentRoute: () => rootRouteImport, -} as any) const PlainIdRoute = PlainIdRouteImport.update({ id: '/plain/$id', path: '/plain/$id', getParentRoute: () => rootRouteImport, } as any) +const RichIdRoute = RichIdRouteImport.update({ + id: '/rich/$id', + path: '/rich/$id', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/plain/$id': typeof PlainIdRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/rich/$id': { - id: '/rich/$id' - path: '/rich/$id' - fullPath: '/rich/$id' - preLoaderRoute: typeof RichIdRouteImport - parentRoute: typeof rootRouteImport - } '/plain/$id': { id: '/plain/$id' path: '/plain/$id' @@ -65,6 +58,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PlainIdRouteImport parentRoute: typeof rootRouteImport } + '/rich/$id': { + id: '/rich/$id' + path: '/rich/$id' + fullPath: '/rich/$id' + preLoaderRoute: typeof RichIdRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/benchmarks/ssr/scenarios/server-fns/react/src/routeTree.gen.ts b/benchmarks/ssr/scenarios/server-fns/react/src/routeTree.gen.ts index 1abf49d56b..4daa5f03b0 100644 --- a/benchmarks/ssr/scenarios/server-fns/react/src/routeTree.gen.ts +++ b/benchmarks/ssr/scenarios/server-fns/react/src/routeTree.gen.ts @@ -10,24 +10,24 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' -import { Route as SsrCallIdRouteImport } from './routes/ssr-call.$id' import { Route as ApiFnUrlsRouteImport } from './routes/api.fn-urls' +import { Route as SsrCallIdRouteImport } from './routes/ssr-call.$id' const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) -const SsrCallIdRoute = SsrCallIdRouteImport.update({ - id: '/ssr-call/$id', - path: '/ssr-call/$id', - getParentRoute: () => rootRouteImport, -} as any) const ApiFnUrlsRoute = ApiFnUrlsRouteImport.update({ id: '/api/fn-urls', path: '/api/fn-urls', getParentRoute: () => rootRouteImport, } as any) +const SsrCallIdRoute = SsrCallIdRouteImport.update({ + id: '/ssr-call/$id', + path: '/ssr-call/$id', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -68,13 +68,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/ssr-call/$id': { - id: '/ssr-call/$id' - path: '/ssr-call/$id' - fullPath: '/ssr-call/$id' - preLoaderRoute: typeof SsrCallIdRouteImport - parentRoute: typeof rootRouteImport - } '/api/fn-urls': { id: '/api/fn-urls' path: '/api/fn-urls' @@ -82,6 +75,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ApiFnUrlsRouteImport parentRoute: typeof rootRouteImport } + '/ssr-call/$id': { + id: '/ssr-call/$id' + path: '/ssr-call/$id' + fullPath: '/ssr-call/$id' + preLoaderRoute: typeof SsrCallIdRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/e2e/react-router/basepath-file-based/src/routeTree.gen.ts b/e2e/react-router/basepath-file-based/src/routeTree.gen.ts index 2989a394d3..3badc3f843 100644 --- a/e2e/react-router/basepath-file-based/src/routeTree.gen.ts +++ b/e2e/react-router/basepath-file-based/src/routeTree.gen.ts @@ -9,20 +9,20 @@ // 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 ScrollErrorRouteImport } from './routes/scroll-error' -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' +import { Route as AboutRouteImport } from './routes/about' +import { Route as RedirectRouteImport } from './routes/redirect' +import { Route as RedirectReloadRouteImport } from './routes/redirectReload' +import { Route as ScrollErrorRouteImport } from './routes/scroll-error' -const ScrollErrorRoute = ScrollErrorRouteImport.update({ - id: '/scroll-error', - path: '/scroll-error', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const RedirectReloadRoute = RedirectReloadRouteImport.update({ - id: '/redirectReload', - path: '/redirectReload', +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', getParentRoute: () => rootRouteImport, } as any) const RedirectRoute = RedirectRouteImport.update({ @@ -30,14 +30,14 @@ const RedirectRoute = RedirectRouteImport.update({ path: '/redirect', getParentRoute: () => rootRouteImport, } as any) -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', +const RedirectReloadRoute = RedirectReloadRouteImport.update({ + id: '/redirectReload', + path: '/redirectReload', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const ScrollErrorRoute = ScrollErrorRouteImport.update({ + id: '/scroll-error', + path: '/scroll-error', getParentRoute: () => rootRouteImport, } as any) @@ -87,18 +87,18 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/scroll-error': { - id: '/scroll-error' - path: '/scroll-error' - fullPath: '/scroll-error' - preLoaderRoute: typeof ScrollErrorRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/redirectReload': { - id: '/redirectReload' - path: '/redirectReload' - fullPath: '/redirectReload' - preLoaderRoute: typeof RedirectReloadRouteImport + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport parentRoute: typeof rootRouteImport } '/redirect': { @@ -108,18 +108,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof RedirectRouteImport parentRoute: typeof rootRouteImport } - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport + '/redirectReload': { + id: '/redirectReload' + path: '/redirectReload' + fullPath: '/redirectReload' + preLoaderRoute: typeof RedirectReloadRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/scroll-error': { + id: '/scroll-error' + path: '/scroll-error' + fullPath: '/scroll-error' + preLoaderRoute: typeof ScrollErrorRouteImport parentRoute: typeof rootRouteImport } } diff --git a/e2e/react-router/basic-file-based-code-splitting/src/routeTree.gen.ts b/e2e/react-router/basic-file-based-code-splitting/src/routeTree.gen.ts index 618ad52e34..c7a15cd84e 100644 --- a/e2e/react-router/basic-file-based-code-splitting/src/routeTree.gen.ts +++ b/e2e/react-router/basic-file-based-code-splitting/src/routeTree.gen.ts @@ -9,26 +9,30 @@ // 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 WithoutLoaderRouteImport } from './routes/without-loader' -import { Route as ViewportTestRouteImport } from './routes/viewport-test' -import { Route as SharedSingletonRouteImport } from './routes/shared-singleton' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as LayoutRouteImport } from './routes/_layout' import { Route as IndexRouteImport } from './routes/index' +import { Route as LayoutRouteImport } from './routes/_layout' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as SharedSingletonRouteImport } from './routes/shared-singleton' +import { Route as ViewportTestRouteImport } from './routes/viewport-test' +import { Route as WithoutLoaderRouteImport } from './routes/without-loader' +import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' -import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' +import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' -const WithoutLoaderRoute = WithoutLoaderRouteImport.update({ - id: '/without-loader', - path: '/without-loader', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const ViewportTestRoute = ViewportTestRouteImport.update({ - id: '/viewport-test', - path: '/viewport-test', +const LayoutRoute = LayoutRouteImport.update({ + id: '/_layout', + getParentRoute: () => rootRouteImport, +} as any) +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) const SharedSingletonRoute = SharedSingletonRouteImport.update({ @@ -36,19 +40,19 @@ const SharedSingletonRoute = SharedSingletonRouteImport.update({ path: '/shared-singleton', getParentRoute: () => rootRouteImport, } as any) -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const ViewportTestRoute = ViewportTestRouteImport.update({ + id: '/viewport-test', + path: '/viewport-test', getParentRoute: () => rootRouteImport, } as any) -const LayoutRoute = LayoutRouteImport.update({ - id: '/_layout', +const WithoutLoaderRoute = WithoutLoaderRouteImport.update({ + id: '/without-loader', + path: '/without-loader', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, +const LayoutLayout2Route = LayoutLayout2RouteImport.update({ + id: '/_layout-2', + getParentRoute: () => LayoutRoute, } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', @@ -60,20 +64,16 @@ const PostsPostIdRoute = PostsPostIdRouteImport.update({ path: '/$postId', getParentRoute: () => PostsRoute, } as any) -const LayoutLayout2Route = LayoutLayout2RouteImport.update({ - id: '/_layout-2', - getParentRoute: () => LayoutRoute, +const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ + id: '/layout-a', + path: '/layout-a', + getParentRoute: () => LayoutLayout2Route, } as any) const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ id: '/layout-b', path: '/layout-b', getParentRoute: () => LayoutLayout2Route, } as any) -const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ - id: '/layout-a', - path: '/layout-a', - getParentRoute: () => LayoutLayout2Route, -} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -158,18 +158,25 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/without-loader': { - id: '/without-loader' - path: '/without-loader' - fullPath: '/without-loader' - preLoaderRoute: typeof WithoutLoaderRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/viewport-test': { - id: '/viewport-test' - path: '/viewport-test' - fullPath: '/viewport-test' - preLoaderRoute: typeof ViewportTestRouteImport + '/_layout': { + id: '/_layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutRouteImport + parentRoute: typeof rootRouteImport + } + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } '/shared-singleton': { @@ -179,26 +186,26 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof SharedSingletonRouteImport parentRoute: typeof rootRouteImport } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/viewport-test': { + id: '/viewport-test' + path: '/viewport-test' + fullPath: '/viewport-test' + preLoaderRoute: typeof ViewportTestRouteImport parentRoute: typeof rootRouteImport } - '/_layout': { - id: '/_layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutRouteImport + '/without-loader': { + id: '/without-loader' + path: '/without-loader' + fullPath: '/without-loader' + preLoaderRoute: typeof WithoutLoaderRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' + '/_layout/_layout-2': { + id: '/_layout/_layout-2' + path: '' fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport + preLoaderRoute: typeof LayoutLayout2RouteImport + parentRoute: typeof LayoutRoute } '/posts/': { id: '/posts/' @@ -214,12 +221,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/_layout/_layout-2': { - id: '/_layout/_layout-2' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutLayout2RouteImport - parentRoute: typeof LayoutRoute + '/_layout/_layout-2/layout-a': { + id: '/_layout/_layout-2/layout-a' + path: '/layout-a' + fullPath: '/layout-a' + preLoaderRoute: typeof LayoutLayout2LayoutARouteImport + parentRoute: typeof LayoutLayout2Route } '/_layout/_layout-2/layout-b': { id: '/_layout/_layout-2/layout-b' @@ -228,13 +235,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport parentRoute: typeof LayoutLayout2Route } - '/_layout/_layout-2/layout-a': { - id: '/_layout/_layout-2/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: typeof LayoutLayout2LayoutARouteImport - parentRoute: typeof LayoutLayout2Route - } } } diff --git a/e2e/react-router/basic-file-based/src/routeTree.gen.ts b/e2e/react-router/basic-file-based/src/routeTree.gen.ts index 7ee0051549..2493a8c675 100644 --- a/e2e/react-router/basic-file-based/src/routeTree.gen.ts +++ b/e2e/react-router/basic-file-based/src/routeTree.gen.ts @@ -9,155 +9,149 @@ // 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 RemountDepsRouteImport } from './routes/remountDeps' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as NotRemountDepsRouteImport } from './routes/notRemountDeps' -import { Route as MasksRouteImport } from './routes/masks' -import { Route as LazyErrorRouteImport } from './routes/lazy-error' -import { Route as HoverPreloadHashRouteImport } from './routes/hover-preload-hash' -import { Route as EditingBRouteImport } from './routes/editing-b' -import { Route as EditingARouteImport } from './routes/editing-a' -import { Route as ComponentTypesTestRouteImport } from './routes/component-types-test' -import { Route as AnchorRouteImport } from './routes/anchor' +import { Route as IndexRouteImport } from './routes/index' import { Route as LayoutRouteImport } from './routes/_layout' -import { Route as Char45824Char54620Char48124Char44397RouteRouteImport } from './routes/대한민국/route' -import { Route as SearchParamsRouteRouteImport } from './routes/search-params/route' -import { Route as PathlessLayoutRouteRouteImport } from './routes/pathless-layout/route' -import { Route as NonNestedRouteRouteImport } from './routes/non-nested/route' +import { Route as AnchorRouteImport } from './routes/anchor' +import { Route as ComponentTypesTestRouteImport } from './routes/component-types-test' +import { Route as EditingARouteImport } from './routes/editing-a' +import { Route as EditingBRouteImport } from './routes/editing-b' import { Route as FullpathTestRouteRouteImport } from './routes/fullpath-test/route' -import { Route as IndexRouteImport } from './routes/index' -import { Route as SearchParamsIndexRouteImport } from './routes/search-params/index' -import { Route as RelativeIndexRouteImport } from './routes/relative/index' -import { Route as RedirectIndexRouteImport } from './routes/redirect/index' -import { Route as PostsIndexRouteImport } from './routes/posts.index' +import { Route as HoverPreloadHashRouteImport } from './routes/hover-preload-hash' +import { Route as LazyErrorRouteImport } from './routes/lazy-error' +import { Route as MasksRouteImport } from './routes/masks' +import { Route as NonNestedRouteRouteImport } from './routes/non-nested/route' +import { Route as NotRemountDepsRouteImport } from './routes/notRemountDeps' +import { Route as PathlessLayoutRouteRouteImport } from './routes/pathless-layout/route' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as RemountDepsRouteImport } from './routes/remountDeps' +import { Route as SearchParamsRouteRouteImport } from './routes/search-params/route' +import { Route as Char45824Char54620Char48124Char44397RouteRouteImport } from './routes/대한민국/route' +import { Route as anotherGroupOnlyrouteinsideRouteImport } from './routes/(another-group)/onlyrouteinside' +import { Route as groupLayoutRouteImport } from './routes/(group)/_layout' +import { Route as groupInsideRouteImport } from './routes/(group)/inside' +import { Route as groupLazyinsideRouteImport } from './routes/(group)/lazyinside' +import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' +import { Route as FullpathTestLayoutRouteRouteImport } from './routes/fullpath-test/_layout/route' +import { Route as NonNestedDeepRouteRouteImport } from './routes/non-nested/deep/route' +import { Route as NonNestedNamedRouteRouteImport } from './routes/non-nested/named/route' +import { Route as NonNestedPathRouteRouteImport } from './routes/non-nested/path/route' +import { Route as NonNestedPrefixRouteRouteImport } from './routes/non-nested/prefix/route' +import { Route as NonNestedSuffixRouteRouteImport } from './routes/non-nested/suffix/route' import { Route as ParamsPsIndexRouteImport } from './routes/params-ps/index' -import { Route as StructuralSharingEnabledRouteImport } from './routes/structural-sharing.$enabled' -import { Route as SearchParamsDefaultRouteImport } from './routes/search-params/default' -import { Route as RedirectTargetRouteImport } from './routes/redirect/$target' +import { Route as ParamsPsNonNestedRouteRouteImport } from './routes/params-ps/non-nested/route' +import { Route as ParamsPsStrictFalseRouteRouteImport } from './routes/params-ps/strict-false/route' +import { Route as PathlessLayoutLayoutRouteRouteImport } from './routes/pathless-layout/_layout/route' +import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' -import { Route as groupLazyinsideRouteImport } from './routes/(group)/lazyinside' -import { Route as groupInsideRouteImport } from './routes/(group)/inside' -import { Route as groupLayoutRouteImport } from './routes/(group)/_layout' -import { Route as anotherGroupOnlyrouteinsideRouteImport } from './routes/(another-group)/onlyrouteinside' -import { Route as RelativeUseNavigateRouteRouteImport } from './routes/relative/useNavigate/route' +import { Route as RedirectIndexRouteImport } from './routes/redirect/index' +import { Route as RedirectTargetRouteImport } from './routes/redirect/$target' +import { Route as RelativeIndexRouteImport } from './routes/relative/index' import { Route as RelativeLinkRouteRouteImport } from './routes/relative/link/route' -import { Route as PathlessLayoutLayoutRouteRouteImport } from './routes/pathless-layout/_layout/route' -import { Route as ParamsPsStrictFalseRouteRouteImport } from './routes/params-ps/strict-false/route' -import { Route as ParamsPsNonNestedRouteRouteImport } from './routes/params-ps/non-nested/route' -import { Route as NonNestedSuffixRouteRouteImport } from './routes/non-nested/suffix/route' -import { Route as NonNestedPrefixRouteRouteImport } from './routes/non-nested/prefix/route' -import { Route as NonNestedPathRouteRouteImport } from './routes/non-nested/path/route' -import { Route as NonNestedNamedRouteRouteImport } from './routes/non-nested/named/route' -import { Route as NonNestedDeepRouteRouteImport } from './routes/non-nested/deep/route' -import { Route as FullpathTestLayoutRouteRouteImport } from './routes/fullpath-test/_layout/route' -import { Route as RedirectTargetIndexRouteImport } from './routes/redirect/$target/index' -import { Route as PathlessLayoutLayoutIndexRouteImport } from './routes/pathless-layout/_layout/index' -import { Route as ParamsPsWildcardIndexRouteImport } from './routes/params-ps/wildcard/index' -import { Route as ParamsPsNamedIndexRouteImport } from './routes/params-ps/named/index' +import { Route as RelativeUseNavigateRouteRouteImport } from './routes/relative/useNavigate/route' +import { Route as SearchParamsIndexRouteImport } from './routes/search-params/index' +import { Route as SearchParamsDefaultRouteImport } from './routes/search-params/default' +import { Route as StructuralSharingEnabledRouteImport } from './routes/structural-sharing.$enabled' +import { Route as groupLayoutInsidelayoutRouteImport } from './routes/(group)/_layout.insidelayout' +import { Route as groupSubfolderInsideRouteImport } from './routes/(group)/subfolder/inside' +import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' +import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' import { Route as FullpathTestLayoutIndexRouteImport } from './routes/fullpath-test/_layout/index' -import { Route as Char45824Char54620Char48124Char44397Char55357Char56960IdRouteImport } from './routes/대한민국/🚀.$id' -import { Route as Char45824Char54620Char48124Char44397WildcardSplatRouteImport } from './routes/대한민국/wildcard.$' -import { Route as RelativeUseNavigateRelativeUseNavigateBRouteImport } from './routes/relative/useNavigate/relative-useNavigate-b' -import { Route as RelativeUseNavigateRelativeUseNavigateARouteImport } from './routes/relative/useNavigate/relative-useNavigate-a' -import { Route as RelativeLinkRelativeLinkBRouteImport } from './routes/relative/link/relative-link-b' -import { Route as RelativeLinkRelativeLinkARouteImport } from './routes/relative/link/relative-link-a' -import { Route as RedirectPreloadThirdRouteImport } from './routes/redirect/preload/third' -import { Route as RedirectPreloadSecondRouteImport } from './routes/redirect/preload/second' -import { Route as RedirectPreloadFirstRouteImport } from './routes/redirect/preload/first' -import { Route as RedirectTargetViaRouteApiRedirectLoaderRouteImport } from './routes/redirect/$target/via-routeApi-redirect-loader' -import { Route as RedirectTargetViaRouteApiRedirectBeforeLoadRouteImport } from './routes/redirect/$target/via-routeApi-redirect-beforeLoad' -import { Route as RedirectTargetViaRouteRedirectLoaderRouteImport } from './routes/redirect/$target/via-route-redirect-loader' -import { Route as RedirectTargetViaRouteRedirectBeforeLoadRouteImport } from './routes/redirect/$target/via-route-redirect-beforeLoad' -import { Route as RedirectTargetViaLoaderRouteImport } from './routes/redirect/$target/via-loader' -import { Route as RedirectTargetViaBeforeLoadRouteImport } from './routes/redirect/$target/via-beforeLoad' -import { Route as RedirectTargetDestinationRouteImport } from './routes/redirect/$target/destination' -import { Route as PostsPostIdEditRouteImport } from './routes/posts_.$postId.edit' -import { Route as PathlessLayoutLayoutChildRouteImport } from './routes/pathless-layout/_layout/child' -import { Route as ParamsSingleValueRouteImport } from './routes/params.single.$value' -import { Route as ParamsPsWildcardChar123Char125suffixAtChar45824RouteImport } from './routes/params-ps/wildcard/{$}suffix@대' -import { Route as ParamsPsWildcardChar123Char125suffixRouteImport } from './routes/params-ps/wildcard/{$}suffix' -import { Route as ParamsPsWildcardPrefixChar123Char125RouteImport } from './routes/params-ps/wildcard/prefix{$}' -import { Route as ParamsPsWildcardPrefixAtChar45824Char123Char125RouteImport } from './routes/params-ps/wildcard/prefix@대{$}' -import { Route as ParamsPsWildcardSplatRouteImport } from './routes/params-ps/wildcard/$' -import { Route as ParamsPsNamedChar123fooChar125suffixRouteImport } from './routes/params-ps/named/{$foo}suffix' -import { Route as ParamsPsNamedPrefixChar123fooChar125RouteImport } from './routes/params-ps/named/prefix{$foo}' -import { Route as MasksPublicUsernameRouteImport } from './routes/masks.public.$username' -import { Route as MasksAdminUserIdRouteImport } from './routes/masks.admin.$userId' import { Route as FullpathTestLayoutIdRouteImport } from './routes/fullpath-test/_layout/$id' -import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' -import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' -import { Route as groupSubfolderInsideRouteImport } from './routes/(group)/subfolder/inside' -import { Route as groupLayoutInsidelayoutRouteImport } from './routes/(group)/_layout.insidelayout' -import { Route as ParamsPsStrictFalseVersionRouteRouteImport } from './routes/params-ps/strict-false/$version.route' -import { Route as ParamsPsNonNestedFooRouteRouteImport } from './routes/params-ps/non-nested/$foo_/route' -import { Route as ParamsPsNamedFooRouteRouteImport } from './routes/params-ps/named/$foo/route' -import { Route as NonNestedSuffixChar123bazChar125suffixRouteRouteImport } from './routes/non-nested/suffix/{$baz}suffix.route' -import { Route as NonNestedPrefixPrefixChar123bazChar125RouteRouteImport } from './routes/non-nested/prefix/prefix{$baz}.route' -import { Route as NonNestedPathBazRouteRouteImport } from './routes/non-nested/path/baz.route' -import { Route as NonNestedNamedBazRouteRouteImport } from './routes/non-nested/named/$baz.route' +import { Route as MasksAdminUserIdRouteImport } from './routes/masks.admin.$userId' +import { Route as MasksPublicUsernameRouteImport } from './routes/masks.public.$username' import { Route as NonNestedDeepBazRouteRouteImport } from './routes/non-nested/deep/$baz.route' -import { Route as RelativeUseNavigateWithSearchIndexRouteImport } from './routes/relative/useNavigate/with-search/index' -import { Route as RelativeUseNavigatePathIndexRouteImport } from './routes/relative/useNavigate/path/index' -import { Route as RelativeUseNavigateNestedIndexRouteImport } from './routes/relative/useNavigate/nested/index' -import { Route as RelativeLinkWithSearchIndexRouteImport } from './routes/relative/link/with-search/index' -import { Route as RelativeLinkPathIndexRouteImport } from './routes/relative/link/path/index' -import { Route as RelativeLinkNestedIndexRouteImport } from './routes/relative/link/nested/index' -import { Route as NonNestedSuffixChar123bazChar125suffixIndexRouteImport } from './routes/non-nested/suffix/{$baz}suffix.index' -import { Route as NonNestedPrefixPrefixChar123bazChar125IndexRouteImport } from './routes/non-nested/prefix/prefix{$baz}.index' -import { Route as NonNestedPathBazIndexRouteImport } from './routes/non-nested/path/baz.index' -import { Route as NonNestedNamedBazIndexRouteImport } from './routes/non-nested/named/$baz.index' +import { Route as NonNestedNamedBazRouteRouteImport } from './routes/non-nested/named/$baz.route' +import { Route as NonNestedPathBazRouteRouteImport } from './routes/non-nested/path/baz.route' +import { Route as NonNestedPrefixPrefixChar123bazChar125RouteRouteImport } from './routes/non-nested/prefix/prefix{$baz}.route' +import { Route as NonNestedSuffixChar123bazChar125suffixRouteRouteImport } from './routes/non-nested/suffix/{$baz}suffix.route' +import { Route as ParamsPsNamedIndexRouteImport } from './routes/params-ps/named/index' +import { Route as ParamsPsNamedFooRouteRouteImport } from './routes/params-ps/named/$foo/route' +import { Route as ParamsPsNamedPrefixChar123fooChar125RouteImport } from './routes/params-ps/named/prefix{$foo}' +import { Route as ParamsPsNamedChar123fooChar125suffixRouteImport } from './routes/params-ps/named/{$foo}suffix' +import { Route as ParamsPsNonNestedFooRouteRouteImport } from './routes/params-ps/non-nested/$foo_/route' +import { Route as ParamsPsStrictFalseVersionRouteRouteImport } from './routes/params-ps/strict-false/$version.route' +import { Route as ParamsPsWildcardIndexRouteImport } from './routes/params-ps/wildcard/index' +import { Route as ParamsPsWildcardSplatRouteImport } from './routes/params-ps/wildcard/$' +import { Route as ParamsPsWildcardPrefixAtChar45824Char123Char125RouteImport } from './routes/params-ps/wildcard/prefix@대{$}' +import { Route as ParamsPsWildcardPrefixChar123Char125RouteImport } from './routes/params-ps/wildcard/prefix{$}' +import { Route as ParamsPsWildcardChar123Char125suffixRouteImport } from './routes/params-ps/wildcard/{$}suffix' +import { Route as ParamsPsWildcardChar123Char125suffixAtChar45824RouteImport } from './routes/params-ps/wildcard/{$}suffix@대' +import { Route as ParamsSingleValueRouteImport } from './routes/params.single.$value' +import { Route as PathlessLayoutLayoutIndexRouteImport } from './routes/pathless-layout/_layout/index' +import { Route as PathlessLayoutLayoutChildRouteImport } from './routes/pathless-layout/_layout/child' +import { Route as PostsPostIdEditRouteImport } from './routes/posts_.$postId.edit' +import { Route as RedirectTargetIndexRouteImport } from './routes/redirect/$target/index' +import { Route as RedirectTargetDestinationRouteImport } from './routes/redirect/$target/destination' +import { Route as RedirectTargetViaBeforeLoadRouteImport } from './routes/redirect/$target/via-beforeLoad' +import { Route as RedirectTargetViaLoaderRouteImport } from './routes/redirect/$target/via-loader' +import { Route as RedirectTargetViaRouteRedirectBeforeLoadRouteImport } from './routes/redirect/$target/via-route-redirect-beforeLoad' +import { Route as RedirectTargetViaRouteRedirectLoaderRouteImport } from './routes/redirect/$target/via-route-redirect-loader' +import { Route as RedirectTargetViaRouteApiRedirectBeforeLoadRouteImport } from './routes/redirect/$target/via-routeApi-redirect-beforeLoad' +import { Route as RedirectTargetViaRouteApiRedirectLoaderRouteImport } from './routes/redirect/$target/via-routeApi-redirect-loader' +import { Route as RedirectPreloadFirstRouteImport } from './routes/redirect/preload/first' +import { Route as RedirectPreloadSecondRouteImport } from './routes/redirect/preload/second' +import { Route as RedirectPreloadThirdRouteImport } from './routes/redirect/preload/third' +import { Route as RelativeLinkRelativeLinkARouteImport } from './routes/relative/link/relative-link-a' +import { Route as RelativeLinkRelativeLinkBRouteImport } from './routes/relative/link/relative-link-b' +import { Route as RelativeUseNavigateRelativeUseNavigateARouteImport } from './routes/relative/useNavigate/relative-useNavigate-a' +import { Route as RelativeUseNavigateRelativeUseNavigateBRouteImport } from './routes/relative/useNavigate/relative-useNavigate-b' +import { Route as Char45824Char54620Char48124Char44397WildcardSplatRouteImport } from './routes/대한민국/wildcard.$' +import { Route as Char45824Char54620Char48124Char44397Char55357Char56960IdRouteImport } from './routes/대한민국/🚀.$id' import { Route as NonNestedDeepBazIndexRouteImport } from './routes/non-nested/deep/$baz.index' -import { Route as ParamsPsNonNestedFooBarRouteImport } from './routes/params-ps/non-nested/$foo_/$bar' -import { Route as NonNestedSuffixChar123bazChar125suffixBarRouteImport } from './routes/non-nested/suffix/{$baz}suffix_.bar' -import { Route as NonNestedSuffixChar123bazChar125suffixFooRouteImport } from './routes/non-nested/suffix/{$baz}suffix.foo' -import { Route as NonNestedPrefixPrefixChar123bazChar125BarRouteImport } from './routes/non-nested/prefix/prefix{$baz}_.bar' -import { Route as NonNestedPrefixPrefixChar123bazChar125FooRouteImport } from './routes/non-nested/prefix/prefix{$baz}.foo' -import { Route as NonNestedPathBazBarRouteImport } from './routes/non-nested/path/baz_.bar' -import { Route as NonNestedPathBazFooRouteImport } from './routes/non-nested/path/baz.foo' -import { Route as NonNestedNamedBazBarRouteImport } from './routes/non-nested/named/$baz_.bar' +import { Route as NonNestedDeepBazBarRouteRouteImport } from './routes/non-nested/deep/$baz_.bar.route' +import { Route as NonNestedNamedBazIndexRouteImport } from './routes/non-nested/named/$baz.index' import { Route as NonNestedNamedBazFooRouteImport } from './routes/non-nested/named/$baz.foo' +import { Route as NonNestedNamedBazBarRouteImport } from './routes/non-nested/named/$baz_.bar' +import { Route as NonNestedPathBazIndexRouteImport } from './routes/non-nested/path/baz.index' +import { Route as NonNestedPathBazFooRouteImport } from './routes/non-nested/path/baz.foo' +import { Route as NonNestedPathBazBarRouteImport } from './routes/non-nested/path/baz_.bar' +import { Route as NonNestedPrefixPrefixChar123bazChar125IndexRouteImport } from './routes/non-nested/prefix/prefix{$baz}.index' +import { Route as NonNestedPrefixPrefixChar123bazChar125FooRouteImport } from './routes/non-nested/prefix/prefix{$baz}.foo' +import { Route as NonNestedPrefixPrefixChar123bazChar125BarRouteImport } from './routes/non-nested/prefix/prefix{$baz}_.bar' +import { Route as NonNestedSuffixChar123bazChar125suffixIndexRouteImport } from './routes/non-nested/suffix/{$baz}suffix.index' +import { Route as NonNestedSuffixChar123bazChar125suffixFooRouteImport } from './routes/non-nested/suffix/{$baz}suffix.foo' +import { Route as NonNestedSuffixChar123bazChar125suffixBarRouteImport } from './routes/non-nested/suffix/{$baz}suffix_.bar' import { Route as ParamsPsNamedFooBarRouteRouteImport } from './routes/params-ps/named/$foo/$bar.route' -import { Route as NonNestedDeepBazBarRouteRouteImport } from './routes/non-nested/deep/$baz_.bar.route' -import { Route as RelativeUseNavigatePathPathIndexRouteImport } from './routes/relative/useNavigate/path/$path/index' -import { Route as RelativeUseNavigateNestedDeepIndexRouteImport } from './routes/relative/useNavigate/nested/deep/index' -import { Route as RelativeLinkPathPathIndexRouteImport } from './routes/relative/link/path/$path/index' -import { Route as RelativeLinkNestedDeepIndexRouteImport } from './routes/relative/link/nested/deep/index' +import { Route as ParamsPsNonNestedFooBarRouteImport } from './routes/params-ps/non-nested/$foo_/$bar' +import { Route as RelativeLinkNestedIndexRouteImport } from './routes/relative/link/nested/index' +import { Route as RelativeLinkPathIndexRouteImport } from './routes/relative/link/path/index' +import { Route as RelativeLinkWithSearchIndexRouteImport } from './routes/relative/link/with-search/index' +import { Route as RelativeUseNavigateNestedIndexRouteImport } from './routes/relative/useNavigate/nested/index' +import { Route as RelativeUseNavigatePathIndexRouteImport } from './routes/relative/useNavigate/path/index' +import { Route as RelativeUseNavigateWithSearchIndexRouteImport } from './routes/relative/useNavigate/with-search/index' import { Route as NonNestedDeepBazBarIndexRouteImport } from './routes/non-nested/deep/$baz_.bar.index' -import { Route as ParamsPsNamedFooBarBazRouteImport } from './routes/params-ps/named/$foo/$bar.$baz' -import { Route as NonNestedDeepBazBarQuxRouteImport } from './routes/non-nested/deep/$baz_.bar_.qux' import { Route as NonNestedDeepBazBarFooRouteRouteImport } from './routes/non-nested/deep/$baz_.bar.$foo.route' +import { Route as NonNestedDeepBazBarQuxRouteImport } from './routes/non-nested/deep/$baz_.bar_.qux' +import { Route as ParamsPsNamedFooBarBazRouteImport } from './routes/params-ps/named/$foo/$bar.$baz' +import { Route as RelativeLinkNestedDeepIndexRouteImport } from './routes/relative/link/nested/deep/index' +import { Route as RelativeLinkPathPathIndexRouteImport } from './routes/relative/link/path/$path/index' +import { Route as RelativeUseNavigateNestedDeepIndexRouteImport } from './routes/relative/useNavigate/nested/deep/index' +import { Route as RelativeUseNavigatePathPathIndexRouteImport } from './routes/relative/useNavigate/path/$path/index' import { Route as NonNestedDeepBazBarFooIndexRouteImport } from './routes/non-nested/deep/$baz_.bar.$foo.index' import { Route as NonNestedDeepBazBarFooQuxRouteImport } from './routes/non-nested/deep/$baz_.bar.$foo_.qux' -const RemountDepsRoute = RemountDepsRouteImport.update({ - id: '/remountDeps', - path: '/remountDeps', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const LayoutRoute = LayoutRouteImport.update({ + id: '/_layout', getParentRoute: () => rootRouteImport, } as any) -const NotRemountDepsRoute = NotRemountDepsRouteImport.update({ - id: '/notRemountDeps', - path: '/notRemountDeps', +const AnchorRoute = AnchorRouteImport.update({ + id: '/anchor', + path: '/anchor', getParentRoute: () => rootRouteImport, } as any) -const MasksRoute = MasksRouteImport.update({ - id: '/masks', - path: '/masks', +const ComponentTypesTestRoute = ComponentTypesTestRouteImport.update({ + id: '/component-types-test', + path: '/component-types-test', getParentRoute: () => rootRouteImport, } as any) -const LazyErrorRoute = LazyErrorRouteImport.update({ - id: '/lazy-error', - path: '/lazy-error', - getParentRoute: () => rootRouteImport, -} as any).lazy(() => import('./routes/lazy-error.lazy').then((d) => d.Route)) -const HoverPreloadHashRoute = HoverPreloadHashRouteImport.update({ - id: '/hover-preload-hash', - path: '/hover-preload-hash', +const EditingARoute = EditingARouteImport.update({ + id: '/editing-a', + path: '/editing-a', getParentRoute: () => rootRouteImport, } as any) const EditingBRoute = EditingBRouteImport.update({ @@ -165,39 +159,24 @@ const EditingBRoute = EditingBRouteImport.update({ path: '/editing-b', getParentRoute: () => rootRouteImport, } as any) -const EditingARoute = EditingARouteImport.update({ - id: '/editing-a', - path: '/editing-a', +const FullpathTestRouteRoute = FullpathTestRouteRouteImport.update({ + id: '/fullpath-test', + path: '/fullpath-test', getParentRoute: () => rootRouteImport, } as any) -const ComponentTypesTestRoute = ComponentTypesTestRouteImport.update({ - id: '/component-types-test', - path: '/component-types-test', +const HoverPreloadHashRoute = HoverPreloadHashRouteImport.update({ + id: '/hover-preload-hash', + path: '/hover-preload-hash', getParentRoute: () => rootRouteImport, } as any) -const AnchorRoute = AnchorRouteImport.update({ - id: '/anchor', - path: '/anchor', - getParentRoute: () => rootRouteImport, -} as any) -const LayoutRoute = LayoutRouteImport.update({ - id: '/_layout', - getParentRoute: () => rootRouteImport, -} as any) -const Char45824Char54620Char48124Char44397RouteRoute = - Char45824Char54620Char48124Char44397RouteRouteImport.update({ - id: '/대한민국', - path: '/대한민국', - getParentRoute: () => rootRouteImport, - } as any) -const SearchParamsRouteRoute = SearchParamsRouteRouteImport.update({ - id: '/search-params', - path: '/search-params', +const LazyErrorRoute = LazyErrorRouteImport.update({ + id: '/lazy-error', + path: '/lazy-error', getParentRoute: () => rootRouteImport, -} as any) -const PathlessLayoutRouteRoute = PathlessLayoutRouteRouteImport.update({ - id: '/pathless-layout', - path: '/pathless-layout', +} as any).lazy(() => import('./routes/lazy-error.lazy').then((d) => d.Route)) +const MasksRoute = MasksRouteImport.update({ + id: '/masks', + path: '/masks', getParentRoute: () => rootRouteImport, } as any) const NonNestedRouteRoute = NonNestedRouteRouteImport.update({ @@ -205,65 +184,51 @@ const NonNestedRouteRoute = NonNestedRouteRouteImport.update({ path: '/non-nested', getParentRoute: () => rootRouteImport, } as any) -const FullpathTestRouteRoute = FullpathTestRouteRouteImport.update({ - id: '/fullpath-test', - path: '/fullpath-test', +const NotRemountDepsRoute = NotRemountDepsRouteImport.update({ + id: '/notRemountDeps', + path: '/notRemountDeps', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const PathlessLayoutRouteRoute = PathlessLayoutRouteRouteImport.update({ + id: '/pathless-layout', + path: '/pathless-layout', getParentRoute: () => rootRouteImport, } as any) -const SearchParamsIndexRoute = SearchParamsIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => SearchParamsRouteRoute, -} as any) -const RelativeIndexRoute = RelativeIndexRouteImport.update({ - id: '/relative/', - path: '/relative/', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const RedirectIndexRoute = RedirectIndexRouteImport.update({ - id: '/redirect/', - path: '/redirect/', +const RemountDepsRoute = RemountDepsRouteImport.update({ + id: '/remountDeps', + path: '/remountDeps', getParentRoute: () => rootRouteImport, } as any) -const PostsIndexRoute = PostsIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => PostsRoute, -} as any) -const ParamsPsIndexRoute = ParamsPsIndexRouteImport.update({ - id: '/params-ps/', - path: '/params-ps/', +const SearchParamsRouteRoute = SearchParamsRouteRouteImport.update({ + id: '/search-params', + path: '/search-params', getParentRoute: () => rootRouteImport, } as any) -const StructuralSharingEnabledRoute = - StructuralSharingEnabledRouteImport.update({ - id: '/structural-sharing/$enabled', - path: '/structural-sharing/$enabled', +const Char45824Char54620Char48124Char44397RouteRoute = + Char45824Char54620Char48124Char44397RouteRouteImport.update({ + id: '/대한민국', + path: '/대한민국', getParentRoute: () => rootRouteImport, } as any) -const SearchParamsDefaultRoute = SearchParamsDefaultRouteImport.update({ - id: '/default', - path: '/default', - getParentRoute: () => SearchParamsRouteRoute, -} as any) -const RedirectTargetRoute = RedirectTargetRouteImport.update({ - id: '/redirect/$target', - path: '/redirect/$target', +const anotherGroupOnlyrouteinsideRoute = + anotherGroupOnlyrouteinsideRouteImport.update({ + id: '/(another-group)/onlyrouteinside', + path: '/onlyrouteinside', + getParentRoute: () => rootRouteImport, + } as any) +const groupLayoutRoute = groupLayoutRouteImport.update({ + id: '/(group)/_layout', getParentRoute: () => rootRouteImport, } as any) -const PostsPostIdRoute = PostsPostIdRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => PostsRoute, -} as any) -const LayoutLayout2Route = LayoutLayout2RouteImport.update({ - id: '/_layout-2', - getParentRoute: () => LayoutRoute, +const groupInsideRoute = groupInsideRouteImport.update({ + id: '/(group)/inside', + path: '/inside', + getParentRoute: () => rootRouteImport, } as any) const groupLazyinsideRoute = groupLazyinsideRouteImport .update({ @@ -272,56 +237,22 @@ const groupLazyinsideRoute = groupLazyinsideRouteImport getParentRoute: () => rootRouteImport, } as any) .lazy(() => import('./routes/(group)/lazyinside.lazy').then((d) => d.Route)) -const groupInsideRoute = groupInsideRouteImport.update({ - id: '/(group)/inside', - path: '/inside', - getParentRoute: () => rootRouteImport, -} as any) -const groupLayoutRoute = groupLayoutRouteImport.update({ - id: '/(group)/_layout', - getParentRoute: () => rootRouteImport, -} as any) -const anotherGroupOnlyrouteinsideRoute = - anotherGroupOnlyrouteinsideRouteImport.update({ - id: '/(another-group)/onlyrouteinside', - path: '/onlyrouteinside', - getParentRoute: () => rootRouteImport, - } as any) -const RelativeUseNavigateRouteRoute = - RelativeUseNavigateRouteRouteImport.update({ - id: '/relative/useNavigate', - path: '/relative/useNavigate', - getParentRoute: () => rootRouteImport, - } as any) -const RelativeLinkRouteRoute = RelativeLinkRouteRouteImport.update({ - id: '/relative/link', - path: '/relative/link', - getParentRoute: () => rootRouteImport, +const LayoutLayout2Route = LayoutLayout2RouteImport.update({ + id: '/_layout-2', + getParentRoute: () => LayoutRoute, } as any) -const PathlessLayoutLayoutRouteRoute = - PathlessLayoutLayoutRouteRouteImport.update({ - id: '/_layout', - getParentRoute: () => PathlessLayoutRouteRoute, - } as any) -const ParamsPsStrictFalseRouteRoute = - ParamsPsStrictFalseRouteRouteImport.update({ - id: '/params-ps/strict-false', - path: '/params-ps/strict-false', - getParentRoute: () => rootRouteImport, - } as any) -const ParamsPsNonNestedRouteRoute = ParamsPsNonNestedRouteRouteImport.update({ - id: '/params-ps/non-nested', - path: '/params-ps/non-nested', - getParentRoute: () => rootRouteImport, +const FullpathTestLayoutRouteRoute = FullpathTestLayoutRouteRouteImport.update({ + id: '/_layout', + getParentRoute: () => FullpathTestRouteRoute, } as any) -const NonNestedSuffixRouteRoute = NonNestedSuffixRouteRouteImport.update({ - id: '/suffix', - path: '/suffix', +const NonNestedDeepRouteRoute = NonNestedDeepRouteRouteImport.update({ + id: '/deep', + path: '/deep', getParentRoute: () => NonNestedRouteRoute, } as any) -const NonNestedPrefixRouteRoute = NonNestedPrefixRouteRouteImport.update({ - id: '/prefix', - path: '/prefix', +const NonNestedNamedRouteRoute = NonNestedNamedRouteRouteImport.update({ + id: '/named', + path: '/named', getParentRoute: () => NonNestedRouteRoute, } as any) const NonNestedPathRouteRoute = NonNestedPathRouteRouteImport.update({ @@ -329,164 +260,204 @@ const NonNestedPathRouteRoute = NonNestedPathRouteRouteImport.update({ path: '/path', getParentRoute: () => NonNestedRouteRoute, } as any) -const NonNestedNamedRouteRoute = NonNestedNamedRouteRouteImport.update({ - id: '/named', - path: '/named', +const NonNestedPrefixRouteRoute = NonNestedPrefixRouteRouteImport.update({ + id: '/prefix', + path: '/prefix', getParentRoute: () => NonNestedRouteRoute, } as any) -const NonNestedDeepRouteRoute = NonNestedDeepRouteRouteImport.update({ - id: '/deep', - path: '/deep', +const NonNestedSuffixRouteRoute = NonNestedSuffixRouteRouteImport.update({ + id: '/suffix', + path: '/suffix', getParentRoute: () => NonNestedRouteRoute, } as any) -const FullpathTestLayoutRouteRoute = FullpathTestLayoutRouteRouteImport.update({ - id: '/_layout', - getParentRoute: () => FullpathTestRouteRoute, +const ParamsPsIndexRoute = ParamsPsIndexRouteImport.update({ + id: '/params-ps/', + path: '/params-ps/', + getParentRoute: () => rootRouteImport, } as any) -const RedirectTargetIndexRoute = RedirectTargetIndexRouteImport.update({ +const ParamsPsNonNestedRouteRoute = ParamsPsNonNestedRouteRouteImport.update({ + id: '/params-ps/non-nested', + path: '/params-ps/non-nested', + getParentRoute: () => rootRouteImport, +} as any) +const ParamsPsStrictFalseRouteRoute = + ParamsPsStrictFalseRouteRouteImport.update({ + id: '/params-ps/strict-false', + path: '/params-ps/strict-false', + getParentRoute: () => rootRouteImport, + } as any) +const PathlessLayoutLayoutRouteRoute = + PathlessLayoutLayoutRouteRouteImport.update({ + id: '/_layout', + getParentRoute: () => PathlessLayoutRouteRoute, + } as any) +const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => RedirectTargetRoute, + getParentRoute: () => PostsRoute, } as any) -const PathlessLayoutLayoutIndexRoute = - PathlessLayoutLayoutIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => PathlessLayoutLayoutRouteRoute, - } as any) -const ParamsPsWildcardIndexRoute = ParamsPsWildcardIndexRouteImport.update({ - id: '/params-ps/wildcard/', - path: '/params-ps/wildcard/', +const PostsPostIdRoute = PostsPostIdRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => PostsRoute, +} as any) +const RedirectIndexRoute = RedirectIndexRouteImport.update({ + id: '/redirect/', + path: '/redirect/', getParentRoute: () => rootRouteImport, } as any) -const ParamsPsNamedIndexRoute = ParamsPsNamedIndexRouteImport.update({ - id: '/params-ps/named/', - path: '/params-ps/named/', +const RedirectTargetRoute = RedirectTargetRouteImport.update({ + id: '/redirect/$target', + path: '/redirect/$target', getParentRoute: () => rootRouteImport, } as any) -const FullpathTestLayoutIndexRoute = FullpathTestLayoutIndexRouteImport.update({ +const RelativeIndexRoute = RelativeIndexRouteImport.update({ + id: '/relative/', + path: '/relative/', + getParentRoute: () => rootRouteImport, +} as any) +const RelativeLinkRouteRoute = RelativeLinkRouteRouteImport.update({ + id: '/relative/link', + path: '/relative/link', + getParentRoute: () => rootRouteImport, +} as any) +const RelativeUseNavigateRouteRoute = + RelativeUseNavigateRouteRouteImport.update({ + id: '/relative/useNavigate', + path: '/relative/useNavigate', + getParentRoute: () => rootRouteImport, + } as any) +const SearchParamsIndexRoute = SearchParamsIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => FullpathTestLayoutRouteRoute, + getParentRoute: () => SearchParamsRouteRoute, } as any) -const Char45824Char54620Char48124Char44397Char55357Char56960IdRoute = - Char45824Char54620Char48124Char44397Char55357Char56960IdRouteImport.update({ - id: '/🚀/$id', - path: '/🚀/$id', - getParentRoute: () => Char45824Char54620Char48124Char44397RouteRoute, - } as any) -const Char45824Char54620Char48124Char44397WildcardSplatRoute = - Char45824Char54620Char48124Char44397WildcardSplatRouteImport.update({ - id: '/wildcard/$', - path: '/wildcard/$', - getParentRoute: () => Char45824Char54620Char48124Char44397RouteRoute, - } as any) -const RelativeUseNavigateRelativeUseNavigateBRoute = - RelativeUseNavigateRelativeUseNavigateBRouteImport.update({ - id: '/relative-useNavigate-b', - path: '/relative-useNavigate-b', - getParentRoute: () => RelativeUseNavigateRouteRoute, - } as any) -const RelativeUseNavigateRelativeUseNavigateARoute = - RelativeUseNavigateRelativeUseNavigateARouteImport.update({ - id: '/relative-useNavigate-a', - path: '/relative-useNavigate-a', - getParentRoute: () => RelativeUseNavigateRouteRoute, - } as any) -const RelativeLinkRelativeLinkBRoute = - RelativeLinkRelativeLinkBRouteImport.update({ - id: '/relative-link-b', - path: '/relative-link-b', - getParentRoute: () => RelativeLinkRouteRoute, - } as any) -const RelativeLinkRelativeLinkARoute = - RelativeLinkRelativeLinkARouteImport.update({ - id: '/relative-link-a', - path: '/relative-link-a', - getParentRoute: () => RelativeLinkRouteRoute, +const SearchParamsDefaultRoute = SearchParamsDefaultRouteImport.update({ + id: '/default', + path: '/default', + getParentRoute: () => SearchParamsRouteRoute, +} as any) +const StructuralSharingEnabledRoute = + StructuralSharingEnabledRouteImport.update({ + id: '/structural-sharing/$enabled', + path: '/structural-sharing/$enabled', + getParentRoute: () => rootRouteImport, } as any) -const RedirectPreloadThirdRoute = RedirectPreloadThirdRouteImport.update({ - id: '/redirect/preload/third', - path: '/redirect/preload/third', - getParentRoute: () => rootRouteImport, +const groupLayoutInsidelayoutRoute = groupLayoutInsidelayoutRouteImport.update({ + id: '/insidelayout', + path: '/insidelayout', + getParentRoute: () => groupLayoutRoute, } as any) -const RedirectPreloadSecondRoute = RedirectPreloadSecondRouteImport.update({ - id: '/redirect/preload/second', - path: '/redirect/preload/second', +const groupSubfolderInsideRoute = groupSubfolderInsideRouteImport.update({ + id: '/(group)/subfolder/inside', + path: '/subfolder/inside', getParentRoute: () => rootRouteImport, } as any) -const RedirectPreloadFirstRoute = RedirectPreloadFirstRouteImport.update({ - id: '/redirect/preload/first', - path: '/redirect/preload/first', - getParentRoute: () => rootRouteImport, +const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ + id: '/layout-a', + path: '/layout-a', + getParentRoute: () => LayoutLayout2Route, } as any) -const RedirectTargetViaRouteApiRedirectLoaderRoute = - RedirectTargetViaRouteApiRedirectLoaderRouteImport.update({ - id: '/via-routeApi-redirect-loader', - path: '/via-routeApi-redirect-loader', - getParentRoute: () => RedirectTargetRoute, +const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ + id: '/layout-b', + path: '/layout-b', + getParentRoute: () => LayoutLayout2Route, +} as any) +const FullpathTestLayoutIndexRoute = FullpathTestLayoutIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => FullpathTestLayoutRouteRoute, +} as any) +const FullpathTestLayoutIdRoute = FullpathTestLayoutIdRouteImport.update({ + id: '/$id', + path: '/$id', + getParentRoute: () => FullpathTestLayoutRouteRoute, +} as any) +const MasksAdminUserIdRoute = MasksAdminUserIdRouteImport.update({ + id: '/admin/$userId', + path: '/admin/$userId', + getParentRoute: () => MasksRoute, +} as any) +const MasksPublicUsernameRoute = MasksPublicUsernameRouteImport.update({ + id: '/public/$username', + path: '/public/$username', + getParentRoute: () => MasksRoute, +} as any) +const NonNestedDeepBazRouteRoute = NonNestedDeepBazRouteRouteImport.update({ + id: '/$baz', + path: '/$baz', + getParentRoute: () => NonNestedDeepRouteRoute, +} as any) +const NonNestedNamedBazRouteRoute = NonNestedNamedBazRouteRouteImport.update({ + id: '/$baz', + path: '/$baz', + getParentRoute: () => NonNestedNamedRouteRoute, +} as any) +const NonNestedPathBazRouteRoute = NonNestedPathBazRouteRouteImport.update({ + id: '/baz', + path: '/baz', + getParentRoute: () => NonNestedPathRouteRoute, +} as any) +const NonNestedPrefixPrefixChar123bazChar125RouteRoute = + NonNestedPrefixPrefixChar123bazChar125RouteRouteImport.update({ + id: '/prefix{$baz}', + path: '/prefix{$baz}', + getParentRoute: () => NonNestedPrefixRouteRoute, } as any) -const RedirectTargetViaRouteApiRedirectBeforeLoadRoute = - RedirectTargetViaRouteApiRedirectBeforeLoadRouteImport.update({ - id: '/via-routeApi-redirect-beforeLoad', - path: '/via-routeApi-redirect-beforeLoad', - getParentRoute: () => RedirectTargetRoute, +const NonNestedSuffixChar123bazChar125suffixRouteRoute = + NonNestedSuffixChar123bazChar125suffixRouteRouteImport.update({ + id: '/{$baz}suffix', + path: '/{$baz}suffix', + getParentRoute: () => NonNestedSuffixRouteRoute, } as any) -const RedirectTargetViaRouteRedirectLoaderRoute = - RedirectTargetViaRouteRedirectLoaderRouteImport.update({ - id: '/via-route-redirect-loader', - path: '/via-route-redirect-loader', - getParentRoute: () => RedirectTargetRoute, +const ParamsPsNamedIndexRoute = ParamsPsNamedIndexRouteImport.update({ + id: '/params-ps/named/', + path: '/params-ps/named/', + getParentRoute: () => rootRouteImport, +} as any) +const ParamsPsNamedFooRouteRoute = ParamsPsNamedFooRouteRouteImport.update({ + id: '/params-ps/named/$foo', + path: '/params-ps/named/$foo', + getParentRoute: () => rootRouteImport, +} as any) +const ParamsPsNamedPrefixChar123fooChar125Route = + ParamsPsNamedPrefixChar123fooChar125RouteImport.update({ + id: '/params-ps/named/prefix{$foo}', + path: '/params-ps/named/prefix{$foo}', + getParentRoute: () => rootRouteImport, } as any) -const RedirectTargetViaRouteRedirectBeforeLoadRoute = - RedirectTargetViaRouteRedirectBeforeLoadRouteImport.update({ - id: '/via-route-redirect-beforeLoad', - path: '/via-route-redirect-beforeLoad', - getParentRoute: () => RedirectTargetRoute, +const ParamsPsNamedChar123fooChar125suffixRoute = + ParamsPsNamedChar123fooChar125suffixRouteImport.update({ + id: '/params-ps/named/{$foo}suffix', + path: '/params-ps/named/{$foo}suffix', + getParentRoute: () => rootRouteImport, } as any) -const RedirectTargetViaLoaderRoute = RedirectTargetViaLoaderRouteImport.update({ - id: '/via-loader', - path: '/via-loader', - getParentRoute: () => RedirectTargetRoute, -} as any) -const RedirectTargetViaBeforeLoadRoute = - RedirectTargetViaBeforeLoadRouteImport.update({ - id: '/via-beforeLoad', - path: '/via-beforeLoad', - getParentRoute: () => RedirectTargetRoute, +const ParamsPsNonNestedFooRouteRoute = + ParamsPsNonNestedFooRouteRouteImport.update({ + id: '/$foo_', + path: '/$foo', + getParentRoute: () => ParamsPsNonNestedRouteRoute, } as any) -const RedirectTargetDestinationRoute = - RedirectTargetDestinationRouteImport.update({ - id: '/destination', - path: '/destination', - getParentRoute: () => RedirectTargetRoute, +const ParamsPsStrictFalseVersionRouteRoute = + ParamsPsStrictFalseVersionRouteRouteImport.update({ + id: '/$version', + path: '/$version', + getParentRoute: () => ParamsPsStrictFalseRouteRoute, } as any) -const PostsPostIdEditRoute = PostsPostIdEditRouteImport.update({ - id: '/posts_/$postId/edit', - path: '/posts/$postId/edit', +const ParamsPsWildcardIndexRoute = ParamsPsWildcardIndexRouteImport.update({ + id: '/params-ps/wildcard/', + path: '/params-ps/wildcard/', getParentRoute: () => rootRouteImport, } as any) -const PathlessLayoutLayoutChildRoute = - PathlessLayoutLayoutChildRouteImport.update({ - id: '/child', - path: '/child', - getParentRoute: () => PathlessLayoutLayoutRouteRoute, - } as any) -const ParamsSingleValueRoute = ParamsSingleValueRouteImport.update({ - id: '/params/single/$value', - path: '/params/single/$value', +const ParamsPsWildcardSplatRoute = ParamsPsWildcardSplatRouteImport.update({ + id: '/params-ps/wildcard/$', + path: '/params-ps/wildcard/$', getParentRoute: () => rootRouteImport, } as any) -const ParamsPsWildcardChar123Char125suffixAtChar45824Route = - ParamsPsWildcardChar123Char125suffixAtChar45824RouteImport.update({ - id: '/params-ps/wildcard/{$}suffix@대', - path: '/params-ps/wildcard/{$}suffix@대', - getParentRoute: () => rootRouteImport, - } as any) -const ParamsPsWildcardChar123Char125suffixRoute = - ParamsPsWildcardChar123Char125suffixRouteImport.update({ - id: '/params-ps/wildcard/{$}suffix', - path: '/params-ps/wildcard/{$}suffix', +const ParamsPsWildcardPrefixAtChar45824Char123Char125Route = + ParamsPsWildcardPrefixAtChar45824Char123Char125RouteImport.update({ + id: '/params-ps/wildcard/prefix@대{$}', + path: '/params-ps/wildcard/prefix@대{$}', getParentRoute: () => rootRouteImport, } as any) const ParamsPsWildcardPrefixChar123Char125Route = @@ -495,185 +466,189 @@ const ParamsPsWildcardPrefixChar123Char125Route = path: '/params-ps/wildcard/prefix{$}', getParentRoute: () => rootRouteImport, } as any) -const ParamsPsWildcardPrefixAtChar45824Char123Char125Route = - ParamsPsWildcardPrefixAtChar45824Char123Char125RouteImport.update({ - id: '/params-ps/wildcard/prefix@대{$}', - path: '/params-ps/wildcard/prefix@대{$}', +const ParamsPsWildcardChar123Char125suffixRoute = + ParamsPsWildcardChar123Char125suffixRouteImport.update({ + id: '/params-ps/wildcard/{$}suffix', + path: '/params-ps/wildcard/{$}suffix', getParentRoute: () => rootRouteImport, } as any) -const ParamsPsWildcardSplatRoute = ParamsPsWildcardSplatRouteImport.update({ - id: '/params-ps/wildcard/$', - path: '/params-ps/wildcard/$', +const ParamsPsWildcardChar123Char125suffixAtChar45824Route = + ParamsPsWildcardChar123Char125suffixAtChar45824RouteImport.update({ + id: '/params-ps/wildcard/{$}suffix@대', + path: '/params-ps/wildcard/{$}suffix@대', + getParentRoute: () => rootRouteImport, + } as any) +const ParamsSingleValueRoute = ParamsSingleValueRouteImport.update({ + id: '/params/single/$value', + path: '/params/single/$value', getParentRoute: () => rootRouteImport, } as any) -const ParamsPsNamedChar123fooChar125suffixRoute = - ParamsPsNamedChar123fooChar125suffixRouteImport.update({ - id: '/params-ps/named/{$foo}suffix', - path: '/params-ps/named/{$foo}suffix', - getParentRoute: () => rootRouteImport, +const PathlessLayoutLayoutIndexRoute = + PathlessLayoutLayoutIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => PathlessLayoutLayoutRouteRoute, } as any) -const ParamsPsNamedPrefixChar123fooChar125Route = - ParamsPsNamedPrefixChar123fooChar125RouteImport.update({ - id: '/params-ps/named/prefix{$foo}', - path: '/params-ps/named/prefix{$foo}', - getParentRoute: () => rootRouteImport, +const PathlessLayoutLayoutChildRoute = + PathlessLayoutLayoutChildRouteImport.update({ + id: '/child', + path: '/child', + getParentRoute: () => PathlessLayoutLayoutRouteRoute, } as any) -const MasksPublicUsernameRoute = MasksPublicUsernameRouteImport.update({ - id: '/public/$username', - path: '/public/$username', - getParentRoute: () => MasksRoute, -} as any) -const MasksAdminUserIdRoute = MasksAdminUserIdRouteImport.update({ - id: '/admin/$userId', - path: '/admin/$userId', - getParentRoute: () => MasksRoute, +const PostsPostIdEditRoute = PostsPostIdEditRouteImport.update({ + id: '/posts_/$postId/edit', + path: '/posts/$postId/edit', + getParentRoute: () => rootRouteImport, } as any) -const FullpathTestLayoutIdRoute = FullpathTestLayoutIdRouteImport.update({ - id: '/$id', - path: '/$id', - getParentRoute: () => FullpathTestLayoutRouteRoute, -} as any) -const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ - id: '/layout-b', - path: '/layout-b', - getParentRoute: () => LayoutLayout2Route, -} as any) -const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ - id: '/layout-a', - path: '/layout-a', - getParentRoute: () => LayoutLayout2Route, -} as any) -const groupSubfolderInsideRoute = groupSubfolderInsideRouteImport.update({ - id: '/(group)/subfolder/inside', - path: '/subfolder/inside', - getParentRoute: () => rootRouteImport, -} as any) -const groupLayoutInsidelayoutRoute = groupLayoutInsidelayoutRouteImport.update({ - id: '/insidelayout', - path: '/insidelayout', - getParentRoute: () => groupLayoutRoute, +const RedirectTargetIndexRoute = RedirectTargetIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => RedirectTargetRoute, } as any) -const ParamsPsStrictFalseVersionRouteRoute = - ParamsPsStrictFalseVersionRouteRouteImport.update({ - id: '/$version', - path: '/$version', - getParentRoute: () => ParamsPsStrictFalseRouteRoute, +const RedirectTargetDestinationRoute = + RedirectTargetDestinationRouteImport.update({ + id: '/destination', + path: '/destination', + getParentRoute: () => RedirectTargetRoute, } as any) -const ParamsPsNonNestedFooRouteRoute = - ParamsPsNonNestedFooRouteRouteImport.update({ - id: '/$foo_', - path: '/$foo', - getParentRoute: () => ParamsPsNonNestedRouteRoute, +const RedirectTargetViaBeforeLoadRoute = + RedirectTargetViaBeforeLoadRouteImport.update({ + id: '/via-beforeLoad', + path: '/via-beforeLoad', + getParentRoute: () => RedirectTargetRoute, } as any) -const ParamsPsNamedFooRouteRoute = ParamsPsNamedFooRouteRouteImport.update({ - id: '/params-ps/named/$foo', - path: '/params-ps/named/$foo', - getParentRoute: () => rootRouteImport, +const RedirectTargetViaLoaderRoute = RedirectTargetViaLoaderRouteImport.update({ + id: '/via-loader', + path: '/via-loader', + getParentRoute: () => RedirectTargetRoute, } as any) -const NonNestedSuffixChar123bazChar125suffixRouteRoute = - NonNestedSuffixChar123bazChar125suffixRouteRouteImport.update({ - id: '/{$baz}suffix', - path: '/{$baz}suffix', - getParentRoute: () => NonNestedSuffixRouteRoute, +const RedirectTargetViaRouteRedirectBeforeLoadRoute = + RedirectTargetViaRouteRedirectBeforeLoadRouteImport.update({ + id: '/via-route-redirect-beforeLoad', + path: '/via-route-redirect-beforeLoad', + getParentRoute: () => RedirectTargetRoute, } as any) -const NonNestedPrefixPrefixChar123bazChar125RouteRoute = - NonNestedPrefixPrefixChar123bazChar125RouteRouteImport.update({ - id: '/prefix{$baz}', - path: '/prefix{$baz}', - getParentRoute: () => NonNestedPrefixRouteRoute, +const RedirectTargetViaRouteRedirectLoaderRoute = + RedirectTargetViaRouteRedirectLoaderRouteImport.update({ + id: '/via-route-redirect-loader', + path: '/via-route-redirect-loader', + getParentRoute: () => RedirectTargetRoute, } as any) -const NonNestedPathBazRouteRoute = NonNestedPathBazRouteRouteImport.update({ - id: '/baz', - path: '/baz', - getParentRoute: () => NonNestedPathRouteRoute, +const RedirectTargetViaRouteApiRedirectBeforeLoadRoute = + RedirectTargetViaRouteApiRedirectBeforeLoadRouteImport.update({ + id: '/via-routeApi-redirect-beforeLoad', + path: '/via-routeApi-redirect-beforeLoad', + getParentRoute: () => RedirectTargetRoute, + } as any) +const RedirectTargetViaRouteApiRedirectLoaderRoute = + RedirectTargetViaRouteApiRedirectLoaderRouteImport.update({ + id: '/via-routeApi-redirect-loader', + path: '/via-routeApi-redirect-loader', + getParentRoute: () => RedirectTargetRoute, + } as any) +const RedirectPreloadFirstRoute = RedirectPreloadFirstRouteImport.update({ + id: '/redirect/preload/first', + path: '/redirect/preload/first', + getParentRoute: () => rootRouteImport, } as any) -const NonNestedNamedBazRouteRoute = NonNestedNamedBazRouteRouteImport.update({ - id: '/$baz', - path: '/$baz', - getParentRoute: () => NonNestedNamedRouteRoute, +const RedirectPreloadSecondRoute = RedirectPreloadSecondRouteImport.update({ + id: '/redirect/preload/second', + path: '/redirect/preload/second', + getParentRoute: () => rootRouteImport, } as any) -const NonNestedDeepBazRouteRoute = NonNestedDeepBazRouteRouteImport.update({ - id: '/$baz', - path: '/$baz', - getParentRoute: () => NonNestedDeepRouteRoute, +const RedirectPreloadThirdRoute = RedirectPreloadThirdRouteImport.update({ + id: '/redirect/preload/third', + path: '/redirect/preload/third', + getParentRoute: () => rootRouteImport, } as any) -const RelativeUseNavigateWithSearchIndexRoute = - RelativeUseNavigateWithSearchIndexRouteImport.update({ - id: '/with-search/', - path: '/with-search/', - getParentRoute: () => RelativeUseNavigateRouteRoute, +const RelativeLinkRelativeLinkARoute = + RelativeLinkRelativeLinkARouteImport.update({ + id: '/relative-link-a', + path: '/relative-link-a', + getParentRoute: () => RelativeLinkRouteRoute, } as any) -const RelativeUseNavigatePathIndexRoute = - RelativeUseNavigatePathIndexRouteImport.update({ - id: '/path/', - path: '/path/', - getParentRoute: () => RelativeUseNavigateRouteRoute, +const RelativeLinkRelativeLinkBRoute = + RelativeLinkRelativeLinkBRouteImport.update({ + id: '/relative-link-b', + path: '/relative-link-b', + getParentRoute: () => RelativeLinkRouteRoute, } as any) -const RelativeUseNavigateNestedIndexRoute = - RelativeUseNavigateNestedIndexRouteImport.update({ - id: '/nested/', - path: '/nested/', +const RelativeUseNavigateRelativeUseNavigateARoute = + RelativeUseNavigateRelativeUseNavigateARouteImport.update({ + id: '/relative-useNavigate-a', + path: '/relative-useNavigate-a', getParentRoute: () => RelativeUseNavigateRouteRoute, } as any) -const RelativeLinkWithSearchIndexRoute = - RelativeLinkWithSearchIndexRouteImport.update({ - id: '/with-search/', - path: '/with-search/', - getParentRoute: () => RelativeLinkRouteRoute, +const RelativeUseNavigateRelativeUseNavigateBRoute = + RelativeUseNavigateRelativeUseNavigateBRouteImport.update({ + id: '/relative-useNavigate-b', + path: '/relative-useNavigate-b', + getParentRoute: () => RelativeUseNavigateRouteRoute, } as any) -const RelativeLinkPathIndexRoute = RelativeLinkPathIndexRouteImport.update({ - id: '/path/', - path: '/path/', - getParentRoute: () => RelativeLinkRouteRoute, -} as any) -const RelativeLinkNestedIndexRoute = RelativeLinkNestedIndexRouteImport.update({ - id: '/nested/', - path: '/nested/', - getParentRoute: () => RelativeLinkRouteRoute, -} as any) -const NonNestedSuffixChar123bazChar125suffixIndexRoute = - NonNestedSuffixChar123bazChar125suffixIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => NonNestedSuffixChar123bazChar125suffixRouteRoute, +const Char45824Char54620Char48124Char44397WildcardSplatRoute = + Char45824Char54620Char48124Char44397WildcardSplatRouteImport.update({ + id: '/wildcard/$', + path: '/wildcard/$', + getParentRoute: () => Char45824Char54620Char48124Char44397RouteRoute, } as any) -const NonNestedPrefixPrefixChar123bazChar125IndexRoute = - NonNestedPrefixPrefixChar123bazChar125IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => NonNestedPrefixPrefixChar123bazChar125RouteRoute, +const Char45824Char54620Char48124Char44397Char55357Char56960IdRoute = + Char45824Char54620Char48124Char44397Char55357Char56960IdRouteImport.update({ + id: '/🚀/$id', + path: '/🚀/$id', + getParentRoute: () => Char45824Char54620Char48124Char44397RouteRoute, } as any) -const NonNestedPathBazIndexRoute = NonNestedPathBazIndexRouteImport.update({ +const NonNestedDeepBazIndexRoute = NonNestedDeepBazIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => NonNestedPathBazRouteRoute, + getParentRoute: () => NonNestedDeepBazRouteRoute, } as any) +const NonNestedDeepBazBarRouteRoute = + NonNestedDeepBazBarRouteRouteImport.update({ + id: '/$baz_/bar', + path: '/$baz/bar', + getParentRoute: () => NonNestedDeepRouteRoute, + } as any) const NonNestedNamedBazIndexRoute = NonNestedNamedBazIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => NonNestedNamedBazRouteRoute, } as any) -const NonNestedDeepBazIndexRoute = NonNestedDeepBazIndexRouteImport.update({ +const NonNestedNamedBazFooRoute = NonNestedNamedBazFooRouteImport.update({ + id: '/foo', + path: '/foo', + getParentRoute: () => NonNestedNamedBazRouteRoute, +} as any) +const NonNestedNamedBazBarRoute = NonNestedNamedBazBarRouteImport.update({ + id: '/$baz_/bar', + path: '/$baz/bar', + getParentRoute: () => NonNestedNamedRouteRoute, +} as any) +const NonNestedPathBazIndexRoute = NonNestedPathBazIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => NonNestedDeepBazRouteRoute, + getParentRoute: () => NonNestedPathBazRouteRoute, } as any) -const ParamsPsNonNestedFooBarRoute = ParamsPsNonNestedFooBarRouteImport.update({ - id: '/$bar', - path: '/$bar', - getParentRoute: () => ParamsPsNonNestedFooRouteRoute, +const NonNestedPathBazFooRoute = NonNestedPathBazFooRouteImport.update({ + id: '/foo', + path: '/foo', + getParentRoute: () => NonNestedPathBazRouteRoute, } as any) -const NonNestedSuffixChar123bazChar125suffixBarRoute = - NonNestedSuffixChar123bazChar125suffixBarRouteImport.update({ - id: '/{$baz}suffix_/bar', - path: '/{$baz}suffix/bar', - getParentRoute: () => NonNestedSuffixRouteRoute, +const NonNestedPathBazBarRoute = NonNestedPathBazBarRouteImport.update({ + id: '/baz_/bar', + path: '/baz/bar', + getParentRoute: () => NonNestedPathRouteRoute, +} as any) +const NonNestedPrefixPrefixChar123bazChar125IndexRoute = + NonNestedPrefixPrefixChar123bazChar125IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => NonNestedPrefixPrefixChar123bazChar125RouteRoute, } as any) -const NonNestedSuffixChar123bazChar125suffixFooRoute = - NonNestedSuffixChar123bazChar125suffixFooRouteImport.update({ +const NonNestedPrefixPrefixChar123bazChar125FooRoute = + NonNestedPrefixPrefixChar123bazChar125FooRouteImport.update({ id: '/foo', path: '/foo', - getParentRoute: () => NonNestedSuffixChar123bazChar125suffixRouteRoute, + getParentRoute: () => NonNestedPrefixPrefixChar123bazChar125RouteRoute, } as any) const NonNestedPrefixPrefixChar123bazChar125BarRoute = NonNestedPrefixPrefixChar123bazChar125BarRouteImport.update({ @@ -681,67 +656,68 @@ const NonNestedPrefixPrefixChar123bazChar125BarRoute = path: '/prefix{$baz}/bar', getParentRoute: () => NonNestedPrefixRouteRoute, } as any) -const NonNestedPrefixPrefixChar123bazChar125FooRoute = - NonNestedPrefixPrefixChar123bazChar125FooRouteImport.update({ +const NonNestedSuffixChar123bazChar125suffixIndexRoute = + NonNestedSuffixChar123bazChar125suffixIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => NonNestedSuffixChar123bazChar125suffixRouteRoute, + } as any) +const NonNestedSuffixChar123bazChar125suffixFooRoute = + NonNestedSuffixChar123bazChar125suffixFooRouteImport.update({ id: '/foo', path: '/foo', - getParentRoute: () => NonNestedPrefixPrefixChar123bazChar125RouteRoute, + getParentRoute: () => NonNestedSuffixChar123bazChar125suffixRouteRoute, + } as any) +const NonNestedSuffixChar123bazChar125suffixBarRoute = + NonNestedSuffixChar123bazChar125suffixBarRouteImport.update({ + id: '/{$baz}suffix_/bar', + path: '/{$baz}suffix/bar', + getParentRoute: () => NonNestedSuffixRouteRoute, } as any) -const NonNestedPathBazBarRoute = NonNestedPathBazBarRouteImport.update({ - id: '/baz_/bar', - path: '/baz/bar', - getParentRoute: () => NonNestedPathRouteRoute, -} as any) -const NonNestedPathBazFooRoute = NonNestedPathBazFooRouteImport.update({ - id: '/foo', - path: '/foo', - getParentRoute: () => NonNestedPathBazRouteRoute, -} as any) -const NonNestedNamedBazBarRoute = NonNestedNamedBazBarRouteImport.update({ - id: '/$baz_/bar', - path: '/$baz/bar', - getParentRoute: () => NonNestedNamedRouteRoute, -} as any) -const NonNestedNamedBazFooRoute = NonNestedNamedBazFooRouteImport.update({ - id: '/foo', - path: '/foo', - getParentRoute: () => NonNestedNamedBazRouteRoute, -} as any) const ParamsPsNamedFooBarRouteRoute = ParamsPsNamedFooBarRouteRouteImport.update({ id: '/$bar', path: '/$bar', getParentRoute: () => ParamsPsNamedFooRouteRoute, } as any) -const NonNestedDeepBazBarRouteRoute = - NonNestedDeepBazBarRouteRouteImport.update({ - id: '/$baz_/bar', - path: '/$baz/bar', - getParentRoute: () => NonNestedDeepRouteRoute, +const ParamsPsNonNestedFooBarRoute = ParamsPsNonNestedFooBarRouteImport.update({ + id: '/$bar', + path: '/$bar', + getParentRoute: () => ParamsPsNonNestedFooRouteRoute, +} as any) +const RelativeLinkNestedIndexRoute = RelativeLinkNestedIndexRouteImport.update({ + id: '/nested/', + path: '/nested/', + getParentRoute: () => RelativeLinkRouteRoute, +} as any) +const RelativeLinkPathIndexRoute = RelativeLinkPathIndexRouteImport.update({ + id: '/path/', + path: '/path/', + getParentRoute: () => RelativeLinkRouteRoute, +} as any) +const RelativeLinkWithSearchIndexRoute = + RelativeLinkWithSearchIndexRouteImport.update({ + id: '/with-search/', + path: '/with-search/', + getParentRoute: () => RelativeLinkRouteRoute, } as any) -const RelativeUseNavigatePathPathIndexRoute = - RelativeUseNavigatePathPathIndexRouteImport.update({ - id: '/path/$path/', - path: '/path/$path/', +const RelativeUseNavigateNestedIndexRoute = + RelativeUseNavigateNestedIndexRouteImport.update({ + id: '/nested/', + path: '/nested/', getParentRoute: () => RelativeUseNavigateRouteRoute, } as any) -const RelativeUseNavigateNestedDeepIndexRoute = - RelativeUseNavigateNestedDeepIndexRouteImport.update({ - id: '/nested/deep/', - path: '/nested/deep/', +const RelativeUseNavigatePathIndexRoute = + RelativeUseNavigatePathIndexRouteImport.update({ + id: '/path/', + path: '/path/', getParentRoute: () => RelativeUseNavigateRouteRoute, } as any) -const RelativeLinkPathPathIndexRoute = - RelativeLinkPathPathIndexRouteImport.update({ - id: '/path/$path/', - path: '/path/$path/', - getParentRoute: () => RelativeLinkRouteRoute, - } as any) -const RelativeLinkNestedDeepIndexRoute = - RelativeLinkNestedDeepIndexRouteImport.update({ - id: '/nested/deep/', - path: '/nested/deep/', - getParentRoute: () => RelativeLinkRouteRoute, +const RelativeUseNavigateWithSearchIndexRoute = + RelativeUseNavigateWithSearchIndexRouteImport.update({ + id: '/with-search/', + path: '/with-search/', + getParentRoute: () => RelativeUseNavigateRouteRoute, } as any) const NonNestedDeepBazBarIndexRoute = NonNestedDeepBazBarIndexRouteImport.update({ @@ -749,22 +725,46 @@ const NonNestedDeepBazBarIndexRoute = path: '/', getParentRoute: () => NonNestedDeepBazBarRouteRoute, } as any) -const ParamsPsNamedFooBarBazRoute = ParamsPsNamedFooBarBazRouteImport.update({ - id: '/$baz', - path: '/$baz', - getParentRoute: () => ParamsPsNamedFooBarRouteRoute, -} as any) -const NonNestedDeepBazBarQuxRoute = NonNestedDeepBazBarQuxRouteImport.update({ - id: '/$baz_/bar_/qux', - path: '/$baz/bar/qux', - getParentRoute: () => NonNestedDeepRouteRoute, -} as any) const NonNestedDeepBazBarFooRouteRoute = NonNestedDeepBazBarFooRouteRouteImport.update({ id: '/$foo', path: '/$foo', getParentRoute: () => NonNestedDeepBazBarRouteRoute, } as any) +const NonNestedDeepBazBarQuxRoute = NonNestedDeepBazBarQuxRouteImport.update({ + id: '/$baz_/bar_/qux', + path: '/$baz/bar/qux', + getParentRoute: () => NonNestedDeepRouteRoute, +} as any) +const ParamsPsNamedFooBarBazRoute = ParamsPsNamedFooBarBazRouteImport.update({ + id: '/$baz', + path: '/$baz', + getParentRoute: () => ParamsPsNamedFooBarRouteRoute, +} as any) +const RelativeLinkNestedDeepIndexRoute = + RelativeLinkNestedDeepIndexRouteImport.update({ + id: '/nested/deep/', + path: '/nested/deep/', + getParentRoute: () => RelativeLinkRouteRoute, + } as any) +const RelativeLinkPathPathIndexRoute = + RelativeLinkPathPathIndexRouteImport.update({ + id: '/path/$path/', + path: '/path/$path/', + getParentRoute: () => RelativeLinkRouteRoute, + } as any) +const RelativeUseNavigateNestedDeepIndexRoute = + RelativeUseNavigateNestedDeepIndexRouteImport.update({ + id: '/nested/deep/', + path: '/nested/deep/', + getParentRoute: () => RelativeUseNavigateRouteRoute, + } as any) +const RelativeUseNavigatePathPathIndexRoute = + RelativeUseNavigatePathPathIndexRouteImport.update({ + id: '/path/$path/', + path: '/path/$path/', + getParentRoute: () => RelativeUseNavigateRouteRoute, + } as any) const NonNestedDeepBazBarFooIndexRoute = NonNestedDeepBazBarFooIndexRouteImport.update({ id: '/', @@ -1521,46 +1521,39 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/remountDeps': { - id: '/remountDeps' - path: '/remountDeps' - fullPath: '/remountDeps' - preLoaderRoute: typeof RemountDepsRouteImport - parentRoute: typeof rootRouteImport - } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/notRemountDeps': { - id: '/notRemountDeps' - path: '/notRemountDeps' - fullPath: '/notRemountDeps' - preLoaderRoute: typeof NotRemountDepsRouteImport + '/_layout': { + id: '/_layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } - '/masks': { - id: '/masks' - path: '/masks' - fullPath: '/masks' - preLoaderRoute: typeof MasksRouteImport + '/anchor': { + id: '/anchor' + path: '/anchor' + fullPath: '/anchor' + preLoaderRoute: typeof AnchorRouteImport parentRoute: typeof rootRouteImport } - '/lazy-error': { - id: '/lazy-error' - path: '/lazy-error' - fullPath: '/lazy-error' - preLoaderRoute: typeof LazyErrorRouteImport + '/component-types-test': { + id: '/component-types-test' + path: '/component-types-test' + fullPath: '/component-types-test' + preLoaderRoute: typeof ComponentTypesTestRouteImport parentRoute: typeof rootRouteImport } - '/hover-preload-hash': { - id: '/hover-preload-hash' - path: '/hover-preload-hash' - fullPath: '/hover-preload-hash' - preLoaderRoute: typeof HoverPreloadHashRouteImport + '/editing-a': { + id: '/editing-a' + path: '/editing-a' + fullPath: '/editing-a' + preLoaderRoute: typeof EditingARouteImport parentRoute: typeof rootRouteImport } '/editing-b': { @@ -1570,46 +1563,46 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof EditingBRouteImport parentRoute: typeof rootRouteImport } - '/editing-a': { - id: '/editing-a' - path: '/editing-a' - fullPath: '/editing-a' - preLoaderRoute: typeof EditingARouteImport + '/fullpath-test': { + id: '/fullpath-test' + path: '/fullpath-test' + fullPath: '/fullpath-test' + preLoaderRoute: typeof FullpathTestRouteRouteImport parentRoute: typeof rootRouteImport } - '/component-types-test': { - id: '/component-types-test' - path: '/component-types-test' - fullPath: '/component-types-test' - preLoaderRoute: typeof ComponentTypesTestRouteImport + '/hover-preload-hash': { + id: '/hover-preload-hash' + path: '/hover-preload-hash' + fullPath: '/hover-preload-hash' + preLoaderRoute: typeof HoverPreloadHashRouteImport parentRoute: typeof rootRouteImport } - '/anchor': { - id: '/anchor' - path: '/anchor' - fullPath: '/anchor' - preLoaderRoute: typeof AnchorRouteImport + '/lazy-error': { + id: '/lazy-error' + path: '/lazy-error' + fullPath: '/lazy-error' + preLoaderRoute: typeof LazyErrorRouteImport parentRoute: typeof rootRouteImport } - '/_layout': { - id: '/_layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutRouteImport + '/masks': { + id: '/masks' + path: '/masks' + fullPath: '/masks' + preLoaderRoute: typeof MasksRouteImport parentRoute: typeof rootRouteImport } - '/대한민국': { - id: '/대한민국' - path: '/대한민국' - fullPath: '/대한민국' - preLoaderRoute: typeof Char45824Char54620Char48124Char44397RouteRouteImport + '/non-nested': { + id: '/non-nested' + path: '/non-nested' + fullPath: '/non-nested' + preLoaderRoute: typeof NonNestedRouteRouteImport parentRoute: typeof rootRouteImport } - '/search-params': { - id: '/search-params' - path: '/search-params' - fullPath: '/search-params' - preLoaderRoute: typeof SearchParamsRouteRouteImport + '/notRemountDeps': { + id: '/notRemountDeps' + path: '/notRemountDeps' + fullPath: '/notRemountDeps' + preLoaderRoute: typeof NotRemountDepsRouteImport parentRoute: typeof rootRouteImport } '/pathless-layout': { @@ -1619,54 +1612,110 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PathlessLayoutRouteRouteImport parentRoute: typeof rootRouteImport } - '/non-nested': { - id: '/non-nested' - path: '/non-nested' - fullPath: '/non-nested' - preLoaderRoute: typeof NonNestedRouteRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/fullpath-test': { - id: '/fullpath-test' - path: '/fullpath-test' - fullPath: '/fullpath-test' - preLoaderRoute: typeof FullpathTestRouteRouteImport + '/remountDeps': { + id: '/remountDeps' + path: '/remountDeps' + fullPath: '/remountDeps' + preLoaderRoute: typeof RemountDepsRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/search-params': { + id: '/search-params' + path: '/search-params' + fullPath: '/search-params' + preLoaderRoute: typeof SearchParamsRouteRouteImport parentRoute: typeof rootRouteImport } - '/search-params/': { - id: '/search-params/' - path: '/' - fullPath: '/search-params/' - preLoaderRoute: typeof SearchParamsIndexRouteImport - parentRoute: typeof SearchParamsRouteRoute + '/대한민국': { + id: '/대한민국' + path: '/대한민국' + fullPath: '/대한민국' + preLoaderRoute: typeof Char45824Char54620Char48124Char44397RouteRouteImport + parentRoute: typeof rootRouteImport } - '/relative/': { - id: '/relative/' - path: '/relative' - fullPath: '/relative/' - preLoaderRoute: typeof RelativeIndexRouteImport + '/(another-group)/onlyrouteinside': { + id: '/(another-group)/onlyrouteinside' + path: '/onlyrouteinside' + fullPath: '/onlyrouteinside' + preLoaderRoute: typeof anotherGroupOnlyrouteinsideRouteImport parentRoute: typeof rootRouteImport } - '/redirect/': { - id: '/redirect/' - path: '/redirect' - fullPath: '/redirect/' - preLoaderRoute: typeof RedirectIndexRouteImport + '/(group)/_layout': { + id: '/(group)/_layout' + path: '' + fullPath: '' + preLoaderRoute: typeof groupLayoutRouteImport parentRoute: typeof rootRouteImport } - '/posts/': { - id: '/posts/' - path: '/' - fullPath: '/posts/' - preLoaderRoute: typeof PostsIndexRouteImport - parentRoute: typeof PostsRoute + '/(group)/inside': { + id: '/(group)/inside' + path: '/inside' + fullPath: '/inside' + preLoaderRoute: typeof groupInsideRouteImport + parentRoute: typeof rootRouteImport + } + '/(group)/lazyinside': { + id: '/(group)/lazyinside' + path: '/lazyinside' + fullPath: '/lazyinside' + preLoaderRoute: typeof groupLazyinsideRouteImport + parentRoute: typeof rootRouteImport + } + '/_layout/_layout-2': { + id: '/_layout/_layout-2' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutLayout2RouteImport + parentRoute: typeof LayoutRoute + } + '/fullpath-test/_layout': { + id: '/fullpath-test/_layout' + path: '' + fullPath: '/fullpath-test' + preLoaderRoute: typeof FullpathTestLayoutRouteRouteImport + parentRoute: typeof FullpathTestRouteRoute + } + '/non-nested/deep': { + id: '/non-nested/deep' + path: '/deep' + fullPath: '/non-nested/deep' + preLoaderRoute: typeof NonNestedDeepRouteRouteImport + parentRoute: typeof NonNestedRouteRoute + } + '/non-nested/named': { + id: '/non-nested/named' + path: '/named' + fullPath: '/non-nested/named' + preLoaderRoute: typeof NonNestedNamedRouteRouteImport + parentRoute: typeof NonNestedRouteRoute + } + '/non-nested/path': { + id: '/non-nested/path' + path: '/path' + fullPath: '/non-nested/path' + preLoaderRoute: typeof NonNestedPathRouteRouteImport + parentRoute: typeof NonNestedRouteRoute + } + '/non-nested/prefix': { + id: '/non-nested/prefix' + path: '/prefix' + fullPath: '/non-nested/prefix' + preLoaderRoute: typeof NonNestedPrefixRouteRouteImport + parentRoute: typeof NonNestedRouteRoute + } + '/non-nested/suffix': { + id: '/non-nested/suffix' + path: '/suffix' + fullPath: '/non-nested/suffix' + preLoaderRoute: typeof NonNestedSuffixRouteRouteImport + parentRoute: typeof NonNestedRouteRoute } '/params-ps/': { id: '/params-ps/' @@ -1675,27 +1724,34 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ParamsPsIndexRouteImport parentRoute: typeof rootRouteImport } - '/structural-sharing/$enabled': { - id: '/structural-sharing/$enabled' - path: '/structural-sharing/$enabled' - fullPath: '/structural-sharing/$enabled' - preLoaderRoute: typeof StructuralSharingEnabledRouteImport + '/params-ps/non-nested': { + id: '/params-ps/non-nested' + path: '/params-ps/non-nested' + fullPath: '/params-ps/non-nested' + preLoaderRoute: typeof ParamsPsNonNestedRouteRouteImport parentRoute: typeof rootRouteImport } - '/search-params/default': { - id: '/search-params/default' - path: '/default' - fullPath: '/search-params/default' - preLoaderRoute: typeof SearchParamsDefaultRouteImport - parentRoute: typeof SearchParamsRouteRoute - } - '/redirect/$target': { - id: '/redirect/$target' - path: '/redirect/$target' - fullPath: '/redirect/$target' - preLoaderRoute: typeof RedirectTargetRouteImport + '/params-ps/strict-false': { + id: '/params-ps/strict-false' + path: '/params-ps/strict-false' + fullPath: '/params-ps/strict-false' + preLoaderRoute: typeof ParamsPsStrictFalseRouteRouteImport parentRoute: typeof rootRouteImport } + '/pathless-layout/_layout': { + id: '/pathless-layout/_layout' + path: '' + fullPath: '/pathless-layout' + preLoaderRoute: typeof PathlessLayoutLayoutRouteRouteImport + parentRoute: typeof PathlessLayoutRouteRoute + } + '/posts/': { + id: '/posts/' + path: '/' + fullPath: '/posts/' + preLoaderRoute: typeof PostsIndexRouteImport + parentRoute: typeof PostsRoute + } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' @@ -1703,39 +1759,32 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/_layout/_layout-2': { - id: '/_layout/_layout-2' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutLayout2RouteImport - parentRoute: typeof LayoutRoute - } - '/(group)/lazyinside': { - id: '/(group)/lazyinside' - path: '/lazyinside' - fullPath: '/lazyinside' - preLoaderRoute: typeof groupLazyinsideRouteImport + '/redirect/': { + id: '/redirect/' + path: '/redirect' + fullPath: '/redirect/' + preLoaderRoute: typeof RedirectIndexRouteImport parentRoute: typeof rootRouteImport } - '/(group)/inside': { - id: '/(group)/inside' - path: '/inside' - fullPath: '/inside' - preLoaderRoute: typeof groupInsideRouteImport + '/redirect/$target': { + id: '/redirect/$target' + path: '/redirect/$target' + fullPath: '/redirect/$target' + preLoaderRoute: typeof RedirectTargetRouteImport parentRoute: typeof rootRouteImport } - '/(group)/_layout': { - id: '/(group)/_layout' - path: '' - fullPath: '' - preLoaderRoute: typeof groupLayoutRouteImport + '/relative/': { + id: '/relative/' + path: '/relative' + fullPath: '/relative/' + preLoaderRoute: typeof RelativeIndexRouteImport parentRoute: typeof rootRouteImport } - '/(another-group)/onlyrouteinside': { - id: '/(another-group)/onlyrouteinside' - path: '/onlyrouteinside' - fullPath: '/onlyrouteinside' - preLoaderRoute: typeof anotherGroupOnlyrouteinsideRouteImport + '/relative/link': { + id: '/relative/link' + path: '/relative/link' + fullPath: '/relative/link' + preLoaderRoute: typeof RelativeLinkRouteRouteImport parentRoute: typeof rootRouteImport } '/relative/useNavigate': { @@ -1745,96 +1794,117 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof RelativeUseNavigateRouteRouteImport parentRoute: typeof rootRouteImport } - '/relative/link': { - id: '/relative/link' - path: '/relative/link' - fullPath: '/relative/link' - preLoaderRoute: typeof RelativeLinkRouteRouteImport - parentRoute: typeof rootRouteImport + '/search-params/': { + id: '/search-params/' + path: '/' + fullPath: '/search-params/' + preLoaderRoute: typeof SearchParamsIndexRouteImport + parentRoute: typeof SearchParamsRouteRoute } - '/pathless-layout/_layout': { - id: '/pathless-layout/_layout' - path: '' - fullPath: '/pathless-layout' - preLoaderRoute: typeof PathlessLayoutLayoutRouteRouteImport - parentRoute: typeof PathlessLayoutRouteRoute + '/search-params/default': { + id: '/search-params/default' + path: '/default' + fullPath: '/search-params/default' + preLoaderRoute: typeof SearchParamsDefaultRouteImport + parentRoute: typeof SearchParamsRouteRoute } - '/params-ps/strict-false': { - id: '/params-ps/strict-false' - path: '/params-ps/strict-false' - fullPath: '/params-ps/strict-false' - preLoaderRoute: typeof ParamsPsStrictFalseRouteRouteImport + '/structural-sharing/$enabled': { + id: '/structural-sharing/$enabled' + path: '/structural-sharing/$enabled' + fullPath: '/structural-sharing/$enabled' + preLoaderRoute: typeof StructuralSharingEnabledRouteImport parentRoute: typeof rootRouteImport } - '/params-ps/non-nested': { - id: '/params-ps/non-nested' - path: '/params-ps/non-nested' - fullPath: '/params-ps/non-nested' - preLoaderRoute: typeof ParamsPsNonNestedRouteRouteImport - parentRoute: typeof rootRouteImport + '/(group)/_layout/insidelayout': { + id: '/(group)/_layout/insidelayout' + path: '/insidelayout' + fullPath: '/insidelayout' + preLoaderRoute: typeof groupLayoutInsidelayoutRouteImport + parentRoute: typeof groupLayoutRoute } - '/non-nested/suffix': { - id: '/non-nested/suffix' - path: '/suffix' - fullPath: '/non-nested/suffix' - preLoaderRoute: typeof NonNestedSuffixRouteRouteImport - parentRoute: typeof NonNestedRouteRoute + '/(group)/subfolder/inside': { + id: '/(group)/subfolder/inside' + path: '/subfolder/inside' + fullPath: '/subfolder/inside' + preLoaderRoute: typeof groupSubfolderInsideRouteImport + parentRoute: typeof rootRouteImport } - '/non-nested/prefix': { - id: '/non-nested/prefix' - path: '/prefix' - fullPath: '/non-nested/prefix' - preLoaderRoute: typeof NonNestedPrefixRouteRouteImport - parentRoute: typeof NonNestedRouteRoute + '/_layout/_layout-2/layout-a': { + id: '/_layout/_layout-2/layout-a' + path: '/layout-a' + fullPath: '/layout-a' + preLoaderRoute: typeof LayoutLayout2LayoutARouteImport + parentRoute: typeof LayoutLayout2Route } - '/non-nested/path': { - id: '/non-nested/path' - path: '/path' - fullPath: '/non-nested/path' - preLoaderRoute: typeof NonNestedPathRouteRouteImport - parentRoute: typeof NonNestedRouteRoute + '/_layout/_layout-2/layout-b': { + id: '/_layout/_layout-2/layout-b' + path: '/layout-b' + fullPath: '/layout-b' + preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport + parentRoute: typeof LayoutLayout2Route } - '/non-nested/named': { - id: '/non-nested/named' - path: '/named' - fullPath: '/non-nested/named' - preLoaderRoute: typeof NonNestedNamedRouteRouteImport - parentRoute: typeof NonNestedRouteRoute + '/fullpath-test/_layout/': { + id: '/fullpath-test/_layout/' + path: '/' + fullPath: '/fullpath-test/' + preLoaderRoute: typeof FullpathTestLayoutIndexRouteImport + parentRoute: typeof FullpathTestLayoutRouteRoute } - '/non-nested/deep': { - id: '/non-nested/deep' - path: '/deep' - fullPath: '/non-nested/deep' - preLoaderRoute: typeof NonNestedDeepRouteRouteImport - parentRoute: typeof NonNestedRouteRoute + '/fullpath-test/_layout/$id': { + id: '/fullpath-test/_layout/$id' + path: '/$id' + fullPath: '/fullpath-test/$id' + preLoaderRoute: typeof FullpathTestLayoutIdRouteImport + parentRoute: typeof FullpathTestLayoutRouteRoute } - '/fullpath-test/_layout': { - id: '/fullpath-test/_layout' - path: '' - fullPath: '/fullpath-test' - preLoaderRoute: typeof FullpathTestLayoutRouteRouteImport - parentRoute: typeof FullpathTestRouteRoute + '/masks/admin/$userId': { + id: '/masks/admin/$userId' + path: '/admin/$userId' + fullPath: '/masks/admin/$userId' + preLoaderRoute: typeof MasksAdminUserIdRouteImport + parentRoute: typeof MasksRoute } - '/redirect/$target/': { - id: '/redirect/$target/' - path: '/' - fullPath: '/redirect/$target/' - preLoaderRoute: typeof RedirectTargetIndexRouteImport - parentRoute: typeof RedirectTargetRoute + '/masks/public/$username': { + id: '/masks/public/$username' + path: '/public/$username' + fullPath: '/masks/public/$username' + preLoaderRoute: typeof MasksPublicUsernameRouteImport + parentRoute: typeof MasksRoute } - '/pathless-layout/_layout/': { - id: '/pathless-layout/_layout/' - path: '/' - fullPath: '/pathless-layout/' - preLoaderRoute: typeof PathlessLayoutLayoutIndexRouteImport - parentRoute: typeof PathlessLayoutLayoutRouteRoute + '/non-nested/deep/$baz': { + id: '/non-nested/deep/$baz' + path: '/$baz' + fullPath: '/non-nested/deep/$baz' + preLoaderRoute: typeof NonNestedDeepBazRouteRouteImport + parentRoute: typeof NonNestedDeepRouteRoute } - '/params-ps/wildcard/': { - id: '/params-ps/wildcard/' - path: '/params-ps/wildcard' - fullPath: '/params-ps/wildcard/' - preLoaderRoute: typeof ParamsPsWildcardIndexRouteImport - parentRoute: typeof rootRouteImport + '/non-nested/named/$baz': { + id: '/non-nested/named/$baz' + path: '/$baz' + fullPath: '/non-nested/named/$baz' + preLoaderRoute: typeof NonNestedNamedBazRouteRouteImport + parentRoute: typeof NonNestedNamedRouteRoute + } + '/non-nested/path/baz': { + id: '/non-nested/path/baz' + path: '/baz' + fullPath: '/non-nested/path/baz' + preLoaderRoute: typeof NonNestedPathBazRouteRouteImport + parentRoute: typeof NonNestedPathRouteRoute + } + '/non-nested/prefix/prefix{$baz}': { + id: '/non-nested/prefix/prefix{$baz}' + path: '/prefix{$baz}' + fullPath: '/non-nested/prefix/prefix{$baz}' + preLoaderRoute: typeof NonNestedPrefixPrefixChar123bazChar125RouteRouteImport + parentRoute: typeof NonNestedPrefixRouteRoute + } + '/non-nested/suffix/{$baz}suffix': { + id: '/non-nested/suffix/{$baz}suffix' + path: '/{$baz}suffix' + fullPath: '/non-nested/suffix/{$baz}suffix' + preLoaderRoute: typeof NonNestedSuffixChar123bazChar125suffixRouteRouteImport + parentRoute: typeof NonNestedSuffixRouteRoute } '/params-ps/named/': { id: '/params-ps/named/' @@ -1843,116 +1913,116 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ParamsPsNamedIndexRouteImport parentRoute: typeof rootRouteImport } - '/fullpath-test/_layout/': { - id: '/fullpath-test/_layout/' - path: '/' - fullPath: '/fullpath-test/' - preLoaderRoute: typeof FullpathTestLayoutIndexRouteImport - parentRoute: typeof FullpathTestLayoutRouteRoute + '/params-ps/named/$foo': { + id: '/params-ps/named/$foo' + path: '/params-ps/named/$foo' + fullPath: '/params-ps/named/$foo' + preLoaderRoute: typeof ParamsPsNamedFooRouteRouteImport + parentRoute: typeof rootRouteImport } - '/대한민국/🚀/$id': { - id: '/대한민국/🚀/$id' - path: '/🚀/$id' - fullPath: '/대한민국/🚀/$id' - preLoaderRoute: typeof Char45824Char54620Char48124Char44397Char55357Char56960IdRouteImport - parentRoute: typeof Char45824Char54620Char48124Char44397RouteRoute + '/params-ps/named/prefix{$foo}': { + id: '/params-ps/named/prefix{$foo}' + path: '/params-ps/named/prefix{$foo}' + fullPath: '/params-ps/named/prefix{$foo}' + preLoaderRoute: typeof ParamsPsNamedPrefixChar123fooChar125RouteImport + parentRoute: typeof rootRouteImport } - '/대한민국/wildcard/$': { - id: '/대한민국/wildcard/$' - path: '/wildcard/$' - fullPath: '/대한민국/wildcard/$' - preLoaderRoute: typeof Char45824Char54620Char48124Char44397WildcardSplatRouteImport - parentRoute: typeof Char45824Char54620Char48124Char44397RouteRoute + '/params-ps/named/{$foo}suffix': { + id: '/params-ps/named/{$foo}suffix' + path: '/params-ps/named/{$foo}suffix' + fullPath: '/params-ps/named/{$foo}suffix' + preLoaderRoute: typeof ParamsPsNamedChar123fooChar125suffixRouteImport + parentRoute: typeof rootRouteImport } - '/relative/useNavigate/relative-useNavigate-b': { - id: '/relative/useNavigate/relative-useNavigate-b' - path: '/relative-useNavigate-b' - fullPath: '/relative/useNavigate/relative-useNavigate-b' - preLoaderRoute: typeof RelativeUseNavigateRelativeUseNavigateBRouteImport - parentRoute: typeof RelativeUseNavigateRouteRoute + '/params-ps/non-nested/$foo_': { + id: '/params-ps/non-nested/$foo_' + path: '/$foo' + fullPath: '/params-ps/non-nested/$foo' + preLoaderRoute: typeof ParamsPsNonNestedFooRouteRouteImport + parentRoute: typeof ParamsPsNonNestedRouteRoute } - '/relative/useNavigate/relative-useNavigate-a': { - id: '/relative/useNavigate/relative-useNavigate-a' - path: '/relative-useNavigate-a' - fullPath: '/relative/useNavigate/relative-useNavigate-a' - preLoaderRoute: typeof RelativeUseNavigateRelativeUseNavigateARouteImport - parentRoute: typeof RelativeUseNavigateRouteRoute + '/params-ps/strict-false/$version': { + id: '/params-ps/strict-false/$version' + path: '/$version' + fullPath: '/params-ps/strict-false/$version' + preLoaderRoute: typeof ParamsPsStrictFalseVersionRouteRouteImport + parentRoute: typeof ParamsPsStrictFalseRouteRoute } - '/relative/link/relative-link-b': { - id: '/relative/link/relative-link-b' - path: '/relative-link-b' - fullPath: '/relative/link/relative-link-b' - preLoaderRoute: typeof RelativeLinkRelativeLinkBRouteImport - parentRoute: typeof RelativeLinkRouteRoute + '/params-ps/wildcard/': { + id: '/params-ps/wildcard/' + path: '/params-ps/wildcard' + fullPath: '/params-ps/wildcard/' + preLoaderRoute: typeof ParamsPsWildcardIndexRouteImport + parentRoute: typeof rootRouteImport } - '/relative/link/relative-link-a': { - id: '/relative/link/relative-link-a' - path: '/relative-link-a' - fullPath: '/relative/link/relative-link-a' - preLoaderRoute: typeof RelativeLinkRelativeLinkARouteImport - parentRoute: typeof RelativeLinkRouteRoute + '/params-ps/wildcard/$': { + id: '/params-ps/wildcard/$' + path: '/params-ps/wildcard/$' + fullPath: '/params-ps/wildcard/$' + preLoaderRoute: typeof ParamsPsWildcardSplatRouteImport + parentRoute: typeof rootRouteImport } - '/redirect/preload/third': { - id: '/redirect/preload/third' - path: '/redirect/preload/third' - fullPath: '/redirect/preload/third' - preLoaderRoute: typeof RedirectPreloadThirdRouteImport + '/params-ps/wildcard/prefix@대{$}': { + id: '/params-ps/wildcard/prefix@대{$}' + path: '/params-ps/wildcard/prefix@대{$}' + fullPath: '/params-ps/wildcard/prefix@대{$}' + preLoaderRoute: typeof ParamsPsWildcardPrefixAtChar45824Char123Char125RouteImport parentRoute: typeof rootRouteImport } - '/redirect/preload/second': { - id: '/redirect/preload/second' - path: '/redirect/preload/second' - fullPath: '/redirect/preload/second' - preLoaderRoute: typeof RedirectPreloadSecondRouteImport + '/params-ps/wildcard/prefix{$}': { + id: '/params-ps/wildcard/prefix{$}' + path: '/params-ps/wildcard/prefix{$}' + fullPath: '/params-ps/wildcard/prefix{$}' + preLoaderRoute: typeof ParamsPsWildcardPrefixChar123Char125RouteImport parentRoute: typeof rootRouteImport } - '/redirect/preload/first': { - id: '/redirect/preload/first' - path: '/redirect/preload/first' - fullPath: '/redirect/preload/first' - preLoaderRoute: typeof RedirectPreloadFirstRouteImport + '/params-ps/wildcard/{$}suffix': { + id: '/params-ps/wildcard/{$}suffix' + path: '/params-ps/wildcard/{$}suffix' + fullPath: '/params-ps/wildcard/{$}suffix' + preLoaderRoute: typeof ParamsPsWildcardChar123Char125suffixRouteImport parentRoute: typeof rootRouteImport } - '/redirect/$target/via-routeApi-redirect-loader': { - id: '/redirect/$target/via-routeApi-redirect-loader' - path: '/via-routeApi-redirect-loader' - fullPath: '/redirect/$target/via-routeApi-redirect-loader' - preLoaderRoute: typeof RedirectTargetViaRouteApiRedirectLoaderRouteImport - parentRoute: typeof RedirectTargetRoute + '/params-ps/wildcard/{$}suffix@대': { + id: '/params-ps/wildcard/{$}suffix@대' + path: '/params-ps/wildcard/{$}suffix@대' + fullPath: '/params-ps/wildcard/{$}suffix@대' + preLoaderRoute: typeof ParamsPsWildcardChar123Char125suffixAtChar45824RouteImport + parentRoute: typeof rootRouteImport } - '/redirect/$target/via-routeApi-redirect-beforeLoad': { - id: '/redirect/$target/via-routeApi-redirect-beforeLoad' - path: '/via-routeApi-redirect-beforeLoad' - fullPath: '/redirect/$target/via-routeApi-redirect-beforeLoad' - preLoaderRoute: typeof RedirectTargetViaRouteApiRedirectBeforeLoadRouteImport - parentRoute: typeof RedirectTargetRoute + '/params/single/$value': { + id: '/params/single/$value' + path: '/params/single/$value' + fullPath: '/params/single/$value' + preLoaderRoute: typeof ParamsSingleValueRouteImport + parentRoute: typeof rootRouteImport } - '/redirect/$target/via-route-redirect-loader': { - id: '/redirect/$target/via-route-redirect-loader' - path: '/via-route-redirect-loader' - fullPath: '/redirect/$target/via-route-redirect-loader' - preLoaderRoute: typeof RedirectTargetViaRouteRedirectLoaderRouteImport - parentRoute: typeof RedirectTargetRoute + '/pathless-layout/_layout/': { + id: '/pathless-layout/_layout/' + path: '/' + fullPath: '/pathless-layout/' + preLoaderRoute: typeof PathlessLayoutLayoutIndexRouteImport + parentRoute: typeof PathlessLayoutLayoutRouteRoute } - '/redirect/$target/via-route-redirect-beforeLoad': { - id: '/redirect/$target/via-route-redirect-beforeLoad' - path: '/via-route-redirect-beforeLoad' - fullPath: '/redirect/$target/via-route-redirect-beforeLoad' - preLoaderRoute: typeof RedirectTargetViaRouteRedirectBeforeLoadRouteImport - parentRoute: typeof RedirectTargetRoute + '/pathless-layout/_layout/child': { + id: '/pathless-layout/_layout/child' + path: '/child' + fullPath: '/pathless-layout/child' + preLoaderRoute: typeof PathlessLayoutLayoutChildRouteImport + parentRoute: typeof PathlessLayoutLayoutRouteRoute } - '/redirect/$target/via-loader': { - id: '/redirect/$target/via-loader' - path: '/via-loader' - fullPath: '/redirect/$target/via-loader' - preLoaderRoute: typeof RedirectTargetViaLoaderRouteImport - parentRoute: typeof RedirectTargetRoute + '/posts_/$postId/edit': { + id: '/posts_/$postId/edit' + path: '/posts/$postId/edit' + fullPath: '/posts/$postId/edit' + preLoaderRoute: typeof PostsPostIdEditRouteImport + parentRoute: typeof rootRouteImport } - '/redirect/$target/via-beforeLoad': { - id: '/redirect/$target/via-beforeLoad' - path: '/via-beforeLoad' - fullPath: '/redirect/$target/via-beforeLoad' - preLoaderRoute: typeof RedirectTargetViaBeforeLoadRouteImport + '/redirect/$target/': { + id: '/redirect/$target/' + path: '/' + fullPath: '/redirect/$target/' + preLoaderRoute: typeof RedirectTargetIndexRouteImport parentRoute: typeof RedirectTargetRoute } '/redirect/$target/destination': { @@ -1962,250 +2032,110 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof RedirectTargetDestinationRouteImport parentRoute: typeof RedirectTargetRoute } - '/posts_/$postId/edit': { - id: '/posts_/$postId/edit' - path: '/posts/$postId/edit' - fullPath: '/posts/$postId/edit' - preLoaderRoute: typeof PostsPostIdEditRouteImport - parentRoute: typeof rootRouteImport - } - '/pathless-layout/_layout/child': { - id: '/pathless-layout/_layout/child' - path: '/child' - fullPath: '/pathless-layout/child' - preLoaderRoute: typeof PathlessLayoutLayoutChildRouteImport - parentRoute: typeof PathlessLayoutLayoutRouteRoute - } - '/params/single/$value': { - id: '/params/single/$value' - path: '/params/single/$value' - fullPath: '/params/single/$value' - preLoaderRoute: typeof ParamsSingleValueRouteImport - parentRoute: typeof rootRouteImport - } - '/params-ps/wildcard/{$}suffix@대': { - id: '/params-ps/wildcard/{$}suffix@대' - path: '/params-ps/wildcard/{$}suffix@대' - fullPath: '/params-ps/wildcard/{$}suffix@대' - preLoaderRoute: typeof ParamsPsWildcardChar123Char125suffixAtChar45824RouteImport - parentRoute: typeof rootRouteImport + '/redirect/$target/via-beforeLoad': { + id: '/redirect/$target/via-beforeLoad' + path: '/via-beforeLoad' + fullPath: '/redirect/$target/via-beforeLoad' + preLoaderRoute: typeof RedirectTargetViaBeforeLoadRouteImport + parentRoute: typeof RedirectTargetRoute } - '/params-ps/wildcard/{$}suffix': { - id: '/params-ps/wildcard/{$}suffix' - path: '/params-ps/wildcard/{$}suffix' - fullPath: '/params-ps/wildcard/{$}suffix' - preLoaderRoute: typeof ParamsPsWildcardChar123Char125suffixRouteImport - parentRoute: typeof rootRouteImport + '/redirect/$target/via-loader': { + id: '/redirect/$target/via-loader' + path: '/via-loader' + fullPath: '/redirect/$target/via-loader' + preLoaderRoute: typeof RedirectTargetViaLoaderRouteImport + parentRoute: typeof RedirectTargetRoute } - '/params-ps/wildcard/prefix{$}': { - id: '/params-ps/wildcard/prefix{$}' - path: '/params-ps/wildcard/prefix{$}' - fullPath: '/params-ps/wildcard/prefix{$}' - preLoaderRoute: typeof ParamsPsWildcardPrefixChar123Char125RouteImport - parentRoute: typeof rootRouteImport + '/redirect/$target/via-route-redirect-beforeLoad': { + id: '/redirect/$target/via-route-redirect-beforeLoad' + path: '/via-route-redirect-beforeLoad' + fullPath: '/redirect/$target/via-route-redirect-beforeLoad' + preLoaderRoute: typeof RedirectTargetViaRouteRedirectBeforeLoadRouteImport + parentRoute: typeof RedirectTargetRoute } - '/params-ps/wildcard/prefix@대{$}': { - id: '/params-ps/wildcard/prefix@대{$}' - path: '/params-ps/wildcard/prefix@대{$}' - fullPath: '/params-ps/wildcard/prefix@대{$}' - preLoaderRoute: typeof ParamsPsWildcardPrefixAtChar45824Char123Char125RouteImport - parentRoute: typeof rootRouteImport + '/redirect/$target/via-route-redirect-loader': { + id: '/redirect/$target/via-route-redirect-loader' + path: '/via-route-redirect-loader' + fullPath: '/redirect/$target/via-route-redirect-loader' + preLoaderRoute: typeof RedirectTargetViaRouteRedirectLoaderRouteImport + parentRoute: typeof RedirectTargetRoute } - '/params-ps/wildcard/$': { - id: '/params-ps/wildcard/$' - path: '/params-ps/wildcard/$' - fullPath: '/params-ps/wildcard/$' - preLoaderRoute: typeof ParamsPsWildcardSplatRouteImport - parentRoute: typeof rootRouteImport + '/redirect/$target/via-routeApi-redirect-beforeLoad': { + id: '/redirect/$target/via-routeApi-redirect-beforeLoad' + path: '/via-routeApi-redirect-beforeLoad' + fullPath: '/redirect/$target/via-routeApi-redirect-beforeLoad' + preLoaderRoute: typeof RedirectTargetViaRouteApiRedirectBeforeLoadRouteImport + parentRoute: typeof RedirectTargetRoute } - '/params-ps/named/{$foo}suffix': { - id: '/params-ps/named/{$foo}suffix' - path: '/params-ps/named/{$foo}suffix' - fullPath: '/params-ps/named/{$foo}suffix' - preLoaderRoute: typeof ParamsPsNamedChar123fooChar125suffixRouteImport - parentRoute: typeof rootRouteImport + '/redirect/$target/via-routeApi-redirect-loader': { + id: '/redirect/$target/via-routeApi-redirect-loader' + path: '/via-routeApi-redirect-loader' + fullPath: '/redirect/$target/via-routeApi-redirect-loader' + preLoaderRoute: typeof RedirectTargetViaRouteApiRedirectLoaderRouteImport + parentRoute: typeof RedirectTargetRoute } - '/params-ps/named/prefix{$foo}': { - id: '/params-ps/named/prefix{$foo}' - path: '/params-ps/named/prefix{$foo}' - fullPath: '/params-ps/named/prefix{$foo}' - preLoaderRoute: typeof ParamsPsNamedPrefixChar123fooChar125RouteImport + '/redirect/preload/first': { + id: '/redirect/preload/first' + path: '/redirect/preload/first' + fullPath: '/redirect/preload/first' + preLoaderRoute: typeof RedirectPreloadFirstRouteImport parentRoute: typeof rootRouteImport } - '/masks/public/$username': { - id: '/masks/public/$username' - path: '/public/$username' - fullPath: '/masks/public/$username' - preLoaderRoute: typeof MasksPublicUsernameRouteImport - parentRoute: typeof MasksRoute - } - '/masks/admin/$userId': { - id: '/masks/admin/$userId' - path: '/admin/$userId' - fullPath: '/masks/admin/$userId' - preLoaderRoute: typeof MasksAdminUserIdRouteImport - parentRoute: typeof MasksRoute - } - '/fullpath-test/_layout/$id': { - id: '/fullpath-test/_layout/$id' - path: '/$id' - fullPath: '/fullpath-test/$id' - preLoaderRoute: typeof FullpathTestLayoutIdRouteImport - parentRoute: typeof FullpathTestLayoutRouteRoute - } - '/_layout/_layout-2/layout-b': { - id: '/_layout/_layout-2/layout-b' - path: '/layout-b' - fullPath: '/layout-b' - preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport - parentRoute: typeof LayoutLayout2Route - } - '/_layout/_layout-2/layout-a': { - id: '/_layout/_layout-2/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: typeof LayoutLayout2LayoutARouteImport - parentRoute: typeof LayoutLayout2Route - } - '/(group)/subfolder/inside': { - id: '/(group)/subfolder/inside' - path: '/subfolder/inside' - fullPath: '/subfolder/inside' - preLoaderRoute: typeof groupSubfolderInsideRouteImport + '/redirect/preload/second': { + id: '/redirect/preload/second' + path: '/redirect/preload/second' + fullPath: '/redirect/preload/second' + preLoaderRoute: typeof RedirectPreloadSecondRouteImport parentRoute: typeof rootRouteImport } - '/(group)/_layout/insidelayout': { - id: '/(group)/_layout/insidelayout' - path: '/insidelayout' - fullPath: '/insidelayout' - preLoaderRoute: typeof groupLayoutInsidelayoutRouteImport - parentRoute: typeof groupLayoutRoute - } - '/params-ps/strict-false/$version': { - id: '/params-ps/strict-false/$version' - path: '/$version' - fullPath: '/params-ps/strict-false/$version' - preLoaderRoute: typeof ParamsPsStrictFalseVersionRouteRouteImport - parentRoute: typeof ParamsPsStrictFalseRouteRoute - } - '/params-ps/non-nested/$foo_': { - id: '/params-ps/non-nested/$foo_' - path: '/$foo' - fullPath: '/params-ps/non-nested/$foo' - preLoaderRoute: typeof ParamsPsNonNestedFooRouteRouteImport - parentRoute: typeof ParamsPsNonNestedRouteRoute - } - '/params-ps/named/$foo': { - id: '/params-ps/named/$foo' - path: '/params-ps/named/$foo' - fullPath: '/params-ps/named/$foo' - preLoaderRoute: typeof ParamsPsNamedFooRouteRouteImport + '/redirect/preload/third': { + id: '/redirect/preload/third' + path: '/redirect/preload/third' + fullPath: '/redirect/preload/third' + preLoaderRoute: typeof RedirectPreloadThirdRouteImport parentRoute: typeof rootRouteImport } - '/non-nested/suffix/{$baz}suffix': { - id: '/non-nested/suffix/{$baz}suffix' - path: '/{$baz}suffix' - fullPath: '/non-nested/suffix/{$baz}suffix' - preLoaderRoute: typeof NonNestedSuffixChar123bazChar125suffixRouteRouteImport - parentRoute: typeof NonNestedSuffixRouteRoute - } - '/non-nested/prefix/prefix{$baz}': { - id: '/non-nested/prefix/prefix{$baz}' - path: '/prefix{$baz}' - fullPath: '/non-nested/prefix/prefix{$baz}' - preLoaderRoute: typeof NonNestedPrefixPrefixChar123bazChar125RouteRouteImport - parentRoute: typeof NonNestedPrefixRouteRoute - } - '/non-nested/path/baz': { - id: '/non-nested/path/baz' - path: '/baz' - fullPath: '/non-nested/path/baz' - preLoaderRoute: typeof NonNestedPathBazRouteRouteImport - parentRoute: typeof NonNestedPathRouteRoute - } - '/non-nested/named/$baz': { - id: '/non-nested/named/$baz' - path: '/$baz' - fullPath: '/non-nested/named/$baz' - preLoaderRoute: typeof NonNestedNamedBazRouteRouteImport - parentRoute: typeof NonNestedNamedRouteRoute - } - '/non-nested/deep/$baz': { - id: '/non-nested/deep/$baz' - path: '/$baz' - fullPath: '/non-nested/deep/$baz' - preLoaderRoute: typeof NonNestedDeepBazRouteRouteImport - parentRoute: typeof NonNestedDeepRouteRoute - } - '/relative/useNavigate/with-search/': { - id: '/relative/useNavigate/with-search/' - path: '/with-search' - fullPath: '/relative/useNavigate/with-search/' - preLoaderRoute: typeof RelativeUseNavigateWithSearchIndexRouteImport - parentRoute: typeof RelativeUseNavigateRouteRoute - } - '/relative/useNavigate/path/': { - id: '/relative/useNavigate/path/' - path: '/path' - fullPath: '/relative/useNavigate/path/' - preLoaderRoute: typeof RelativeUseNavigatePathIndexRouteImport - parentRoute: typeof RelativeUseNavigateRouteRoute - } - '/relative/useNavigate/nested/': { - id: '/relative/useNavigate/nested/' - path: '/nested' - fullPath: '/relative/useNavigate/nested/' - preLoaderRoute: typeof RelativeUseNavigateNestedIndexRouteImport - parentRoute: typeof RelativeUseNavigateRouteRoute - } - '/relative/link/with-search/': { - id: '/relative/link/with-search/' - path: '/with-search' - fullPath: '/relative/link/with-search/' - preLoaderRoute: typeof RelativeLinkWithSearchIndexRouteImport - parentRoute: typeof RelativeLinkRouteRoute - } - '/relative/link/path/': { - id: '/relative/link/path/' - path: '/path' - fullPath: '/relative/link/path/' - preLoaderRoute: typeof RelativeLinkPathIndexRouteImport + '/relative/link/relative-link-a': { + id: '/relative/link/relative-link-a' + path: '/relative-link-a' + fullPath: '/relative/link/relative-link-a' + preLoaderRoute: typeof RelativeLinkRelativeLinkARouteImport parentRoute: typeof RelativeLinkRouteRoute } - '/relative/link/nested/': { - id: '/relative/link/nested/' - path: '/nested' - fullPath: '/relative/link/nested/' - preLoaderRoute: typeof RelativeLinkNestedIndexRouteImport + '/relative/link/relative-link-b': { + id: '/relative/link/relative-link-b' + path: '/relative-link-b' + fullPath: '/relative/link/relative-link-b' + preLoaderRoute: typeof RelativeLinkRelativeLinkBRouteImport parentRoute: typeof RelativeLinkRouteRoute } - '/non-nested/suffix/{$baz}suffix/': { - id: '/non-nested/suffix/{$baz}suffix/' - path: '/' - fullPath: '/non-nested/suffix/{$baz}suffix/' - preLoaderRoute: typeof NonNestedSuffixChar123bazChar125suffixIndexRouteImport - parentRoute: typeof NonNestedSuffixChar123bazChar125suffixRouteRoute + '/relative/useNavigate/relative-useNavigate-a': { + id: '/relative/useNavigate/relative-useNavigate-a' + path: '/relative-useNavigate-a' + fullPath: '/relative/useNavigate/relative-useNavigate-a' + preLoaderRoute: typeof RelativeUseNavigateRelativeUseNavigateARouteImport + parentRoute: typeof RelativeUseNavigateRouteRoute } - '/non-nested/prefix/prefix{$baz}/': { - id: '/non-nested/prefix/prefix{$baz}/' - path: '/' - fullPath: '/non-nested/prefix/prefix{$baz}/' - preLoaderRoute: typeof NonNestedPrefixPrefixChar123bazChar125IndexRouteImport - parentRoute: typeof NonNestedPrefixPrefixChar123bazChar125RouteRoute + '/relative/useNavigate/relative-useNavigate-b': { + id: '/relative/useNavigate/relative-useNavigate-b' + path: '/relative-useNavigate-b' + fullPath: '/relative/useNavigate/relative-useNavigate-b' + preLoaderRoute: typeof RelativeUseNavigateRelativeUseNavigateBRouteImport + parentRoute: typeof RelativeUseNavigateRouteRoute } - '/non-nested/path/baz/': { - id: '/non-nested/path/baz/' - path: '/' - fullPath: '/non-nested/path/baz/' - preLoaderRoute: typeof NonNestedPathBazIndexRouteImport - parentRoute: typeof NonNestedPathBazRouteRoute + '/대한민국/wildcard/$': { + id: '/대한민국/wildcard/$' + path: '/wildcard/$' + fullPath: '/대한민국/wildcard/$' + preLoaderRoute: typeof Char45824Char54620Char48124Char44397WildcardSplatRouteImport + parentRoute: typeof Char45824Char54620Char48124Char44397RouteRoute } - '/non-nested/named/$baz/': { - id: '/non-nested/named/$baz/' - path: '/' - fullPath: '/non-nested/named/$baz/' - preLoaderRoute: typeof NonNestedNamedBazIndexRouteImport - parentRoute: typeof NonNestedNamedBazRouteRoute + '/대한민국/🚀/$id': { + id: '/대한민국/🚀/$id' + path: '/🚀/$id' + fullPath: '/대한민국/🚀/$id' + preLoaderRoute: typeof Char45824Char54620Char48124Char44397Char55357Char56960IdRouteImport + parentRoute: typeof Char45824Char54620Char48124Char44397RouteRoute } '/non-nested/deep/$baz/': { id: '/non-nested/deep/$baz/' @@ -2214,40 +2144,47 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof NonNestedDeepBazIndexRouteImport parentRoute: typeof NonNestedDeepBazRouteRoute } - '/params-ps/non-nested/$foo_/$bar': { - id: '/params-ps/non-nested/$foo_/$bar' - path: '/$bar' - fullPath: '/params-ps/non-nested/$foo/$bar' - preLoaderRoute: typeof ParamsPsNonNestedFooBarRouteImport - parentRoute: typeof ParamsPsNonNestedFooRouteRoute + '/non-nested/deep/$baz_/bar': { + id: '/non-nested/deep/$baz_/bar' + path: '/$baz/bar' + fullPath: '/non-nested/deep/$baz/bar' + preLoaderRoute: typeof NonNestedDeepBazBarRouteRouteImport + parentRoute: typeof NonNestedDeepRouteRoute } - '/non-nested/suffix/{$baz}suffix_/bar': { - id: '/non-nested/suffix/{$baz}suffix_/bar' - path: '/{$baz}suffix/bar' - fullPath: '/non-nested/suffix/{$baz}suffix/bar' - preLoaderRoute: typeof NonNestedSuffixChar123bazChar125suffixBarRouteImport - parentRoute: typeof NonNestedSuffixRouteRoute + '/non-nested/named/$baz/': { + id: '/non-nested/named/$baz/' + path: '/' + fullPath: '/non-nested/named/$baz/' + preLoaderRoute: typeof NonNestedNamedBazIndexRouteImport + parentRoute: typeof NonNestedNamedBazRouteRoute } - '/non-nested/suffix/{$baz}suffix/foo': { - id: '/non-nested/suffix/{$baz}suffix/foo' + '/non-nested/named/$baz/foo': { + id: '/non-nested/named/$baz/foo' path: '/foo' - fullPath: '/non-nested/suffix/{$baz}suffix/foo' - preLoaderRoute: typeof NonNestedSuffixChar123bazChar125suffixFooRouteImport - parentRoute: typeof NonNestedSuffixChar123bazChar125suffixRouteRoute + fullPath: '/non-nested/named/$baz/foo' + preLoaderRoute: typeof NonNestedNamedBazFooRouteImport + parentRoute: typeof NonNestedNamedBazRouteRoute } - '/non-nested/prefix/prefix{$baz}_/bar': { - id: '/non-nested/prefix/prefix{$baz}_/bar' - path: '/prefix{$baz}/bar' - fullPath: '/non-nested/prefix/prefix{$baz}/bar' - preLoaderRoute: typeof NonNestedPrefixPrefixChar123bazChar125BarRouteImport - parentRoute: typeof NonNestedPrefixRouteRoute + '/non-nested/named/$baz_/bar': { + id: '/non-nested/named/$baz_/bar' + path: '/$baz/bar' + fullPath: '/non-nested/named/$baz/bar' + preLoaderRoute: typeof NonNestedNamedBazBarRouteImport + parentRoute: typeof NonNestedNamedRouteRoute } - '/non-nested/prefix/prefix{$baz}/foo': { - id: '/non-nested/prefix/prefix{$baz}/foo' + '/non-nested/path/baz/': { + id: '/non-nested/path/baz/' + path: '/' + fullPath: '/non-nested/path/baz/' + preLoaderRoute: typeof NonNestedPathBazIndexRouteImport + parentRoute: typeof NonNestedPathBazRouteRoute + } + '/non-nested/path/baz/foo': { + id: '/non-nested/path/baz/foo' path: '/foo' - fullPath: '/non-nested/prefix/prefix{$baz}/foo' - preLoaderRoute: typeof NonNestedPrefixPrefixChar123bazChar125FooRouteImport - parentRoute: typeof NonNestedPrefixPrefixChar123bazChar125RouteRoute + fullPath: '/non-nested/path/baz/foo' + preLoaderRoute: typeof NonNestedPathBazFooRouteImport + parentRoute: typeof NonNestedPathBazRouteRoute } '/non-nested/path/baz_/bar': { id: '/non-nested/path/baz_/bar' @@ -2256,26 +2193,47 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof NonNestedPathBazBarRouteImport parentRoute: typeof NonNestedPathRouteRoute } - '/non-nested/path/baz/foo': { - id: '/non-nested/path/baz/foo' + '/non-nested/prefix/prefix{$baz}/': { + id: '/non-nested/prefix/prefix{$baz}/' + path: '/' + fullPath: '/non-nested/prefix/prefix{$baz}/' + preLoaderRoute: typeof NonNestedPrefixPrefixChar123bazChar125IndexRouteImport + parentRoute: typeof NonNestedPrefixPrefixChar123bazChar125RouteRoute + } + '/non-nested/prefix/prefix{$baz}/foo': { + id: '/non-nested/prefix/prefix{$baz}/foo' path: '/foo' - fullPath: '/non-nested/path/baz/foo' - preLoaderRoute: typeof NonNestedPathBazFooRouteImport - parentRoute: typeof NonNestedPathBazRouteRoute + fullPath: '/non-nested/prefix/prefix{$baz}/foo' + preLoaderRoute: typeof NonNestedPrefixPrefixChar123bazChar125FooRouteImport + parentRoute: typeof NonNestedPrefixPrefixChar123bazChar125RouteRoute } - '/non-nested/named/$baz_/bar': { - id: '/non-nested/named/$baz_/bar' - path: '/$baz/bar' - fullPath: '/non-nested/named/$baz/bar' - preLoaderRoute: typeof NonNestedNamedBazBarRouteImport - parentRoute: typeof NonNestedNamedRouteRoute + '/non-nested/prefix/prefix{$baz}_/bar': { + id: '/non-nested/prefix/prefix{$baz}_/bar' + path: '/prefix{$baz}/bar' + fullPath: '/non-nested/prefix/prefix{$baz}/bar' + preLoaderRoute: typeof NonNestedPrefixPrefixChar123bazChar125BarRouteImport + parentRoute: typeof NonNestedPrefixRouteRoute } - '/non-nested/named/$baz/foo': { - id: '/non-nested/named/$baz/foo' + '/non-nested/suffix/{$baz}suffix/': { + id: '/non-nested/suffix/{$baz}suffix/' + path: '/' + fullPath: '/non-nested/suffix/{$baz}suffix/' + preLoaderRoute: typeof NonNestedSuffixChar123bazChar125suffixIndexRouteImport + parentRoute: typeof NonNestedSuffixChar123bazChar125suffixRouteRoute + } + '/non-nested/suffix/{$baz}suffix/foo': { + id: '/non-nested/suffix/{$baz}suffix/foo' path: '/foo' - fullPath: '/non-nested/named/$baz/foo' - preLoaderRoute: typeof NonNestedNamedBazFooRouteImport - parentRoute: typeof NonNestedNamedBazRouteRoute + fullPath: '/non-nested/suffix/{$baz}suffix/foo' + preLoaderRoute: typeof NonNestedSuffixChar123bazChar125suffixFooRouteImport + parentRoute: typeof NonNestedSuffixChar123bazChar125suffixRouteRoute + } + '/non-nested/suffix/{$baz}suffix_/bar': { + id: '/non-nested/suffix/{$baz}suffix_/bar' + path: '/{$baz}suffix/bar' + fullPath: '/non-nested/suffix/{$baz}suffix/bar' + preLoaderRoute: typeof NonNestedSuffixChar123bazChar125suffixBarRouteImport + parentRoute: typeof NonNestedSuffixRouteRoute } '/params-ps/named/$foo/$bar': { id: '/params-ps/named/$foo/$bar' @@ -2284,41 +2242,55 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ParamsPsNamedFooBarRouteRouteImport parentRoute: typeof ParamsPsNamedFooRouteRoute } - '/non-nested/deep/$baz_/bar': { - id: '/non-nested/deep/$baz_/bar' - path: '/$baz/bar' - fullPath: '/non-nested/deep/$baz/bar' - preLoaderRoute: typeof NonNestedDeepBazBarRouteRouteImport - parentRoute: typeof NonNestedDeepRouteRoute - } - '/relative/useNavigate/path/$path/': { - id: '/relative/useNavigate/path/$path/' - path: '/path/$path' - fullPath: '/relative/useNavigate/path/$path/' - preLoaderRoute: typeof RelativeUseNavigatePathPathIndexRouteImport - parentRoute: typeof RelativeUseNavigateRouteRoute + '/params-ps/non-nested/$foo_/$bar': { + id: '/params-ps/non-nested/$foo_/$bar' + path: '/$bar' + fullPath: '/params-ps/non-nested/$foo/$bar' + preLoaderRoute: typeof ParamsPsNonNestedFooBarRouteImport + parentRoute: typeof ParamsPsNonNestedFooRouteRoute } - '/relative/useNavigate/nested/deep/': { - id: '/relative/useNavigate/nested/deep/' - path: '/nested/deep' - fullPath: '/relative/useNavigate/nested/deep/' - preLoaderRoute: typeof RelativeUseNavigateNestedDeepIndexRouteImport - parentRoute: typeof RelativeUseNavigateRouteRoute + '/relative/link/nested/': { + id: '/relative/link/nested/' + path: '/nested' + fullPath: '/relative/link/nested/' + preLoaderRoute: typeof RelativeLinkNestedIndexRouteImport + parentRoute: typeof RelativeLinkRouteRoute } - '/relative/link/path/$path/': { - id: '/relative/link/path/$path/' - path: '/path/$path' - fullPath: '/relative/link/path/$path/' - preLoaderRoute: typeof RelativeLinkPathPathIndexRouteImport + '/relative/link/path/': { + id: '/relative/link/path/' + path: '/path' + fullPath: '/relative/link/path/' + preLoaderRoute: typeof RelativeLinkPathIndexRouteImport parentRoute: typeof RelativeLinkRouteRoute } - '/relative/link/nested/deep/': { - id: '/relative/link/nested/deep/' - path: '/nested/deep' - fullPath: '/relative/link/nested/deep/' - preLoaderRoute: typeof RelativeLinkNestedDeepIndexRouteImport + '/relative/link/with-search/': { + id: '/relative/link/with-search/' + path: '/with-search' + fullPath: '/relative/link/with-search/' + preLoaderRoute: typeof RelativeLinkWithSearchIndexRouteImport parentRoute: typeof RelativeLinkRouteRoute } + '/relative/useNavigate/nested/': { + id: '/relative/useNavigate/nested/' + path: '/nested' + fullPath: '/relative/useNavigate/nested/' + preLoaderRoute: typeof RelativeUseNavigateNestedIndexRouteImport + parentRoute: typeof RelativeUseNavigateRouteRoute + } + '/relative/useNavigate/path/': { + id: '/relative/useNavigate/path/' + path: '/path' + fullPath: '/relative/useNavigate/path/' + preLoaderRoute: typeof RelativeUseNavigatePathIndexRouteImport + parentRoute: typeof RelativeUseNavigateRouteRoute + } + '/relative/useNavigate/with-search/': { + id: '/relative/useNavigate/with-search/' + path: '/with-search' + fullPath: '/relative/useNavigate/with-search/' + preLoaderRoute: typeof RelativeUseNavigateWithSearchIndexRouteImport + parentRoute: typeof RelativeUseNavigateRouteRoute + } '/non-nested/deep/$baz_/bar/': { id: '/non-nested/deep/$baz_/bar/' path: '/' @@ -2326,12 +2298,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof NonNestedDeepBazBarIndexRouteImport parentRoute: typeof NonNestedDeepBazBarRouteRoute } - '/params-ps/named/$foo/$bar/$baz': { - id: '/params-ps/named/$foo/$bar/$baz' - path: '/$baz' - fullPath: '/params-ps/named/$foo/$bar/$baz' - preLoaderRoute: typeof ParamsPsNamedFooBarBazRouteImport - parentRoute: typeof ParamsPsNamedFooBarRouteRoute + '/non-nested/deep/$baz_/bar/$foo': { + id: '/non-nested/deep/$baz_/bar/$foo' + path: '/$foo' + fullPath: '/non-nested/deep/$baz/bar/$foo' + preLoaderRoute: typeof NonNestedDeepBazBarFooRouteRouteImport + parentRoute: typeof NonNestedDeepBazBarRouteRoute } '/non-nested/deep/$baz_/bar_/qux': { id: '/non-nested/deep/$baz_/bar_/qux' @@ -2340,12 +2312,40 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof NonNestedDeepBazBarQuxRouteImport parentRoute: typeof NonNestedDeepRouteRoute } - '/non-nested/deep/$baz_/bar/$foo': { - id: '/non-nested/deep/$baz_/bar/$foo' - path: '/$foo' - fullPath: '/non-nested/deep/$baz/bar/$foo' - preLoaderRoute: typeof NonNestedDeepBazBarFooRouteRouteImport - parentRoute: typeof NonNestedDeepBazBarRouteRoute + '/params-ps/named/$foo/$bar/$baz': { + id: '/params-ps/named/$foo/$bar/$baz' + path: '/$baz' + fullPath: '/params-ps/named/$foo/$bar/$baz' + preLoaderRoute: typeof ParamsPsNamedFooBarBazRouteImport + parentRoute: typeof ParamsPsNamedFooBarRouteRoute + } + '/relative/link/nested/deep/': { + id: '/relative/link/nested/deep/' + path: '/nested/deep' + fullPath: '/relative/link/nested/deep/' + preLoaderRoute: typeof RelativeLinkNestedDeepIndexRouteImport + parentRoute: typeof RelativeLinkRouteRoute + } + '/relative/link/path/$path/': { + id: '/relative/link/path/$path/' + path: '/path/$path' + fullPath: '/relative/link/path/$path/' + preLoaderRoute: typeof RelativeLinkPathPathIndexRouteImport + parentRoute: typeof RelativeLinkRouteRoute + } + '/relative/useNavigate/nested/deep/': { + id: '/relative/useNavigate/nested/deep/' + path: '/nested/deep' + fullPath: '/relative/useNavigate/nested/deep/' + preLoaderRoute: typeof RelativeUseNavigateNestedDeepIndexRouteImport + parentRoute: typeof RelativeUseNavigateRouteRoute + } + '/relative/useNavigate/path/$path/': { + id: '/relative/useNavigate/path/$path/' + path: '/path/$path' + fullPath: '/relative/useNavigate/path/$path/' + preLoaderRoute: typeof RelativeUseNavigatePathPathIndexRouteImport + parentRoute: typeof RelativeUseNavigateRouteRoute } '/non-nested/deep/$baz_/bar/$foo/': { id: '/non-nested/deep/$baz_/bar/$foo/' diff --git a/e2e/react-router/basic-react-query-file-based/src/routeTree.gen.ts b/e2e/react-router/basic-react-query-file-based/src/routeTree.gen.ts index 62ce94f2c8..115e4bce47 100644 --- a/e2e/react-router/basic-react-query-file-based/src/routeTree.gen.ts +++ b/e2e/react-router/basic-react-query-file-based/src/routeTree.gen.ts @@ -9,29 +9,33 @@ // 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 PostsRouteImport } from './routes/posts' -import { Route as LayoutRouteImport } from './routes/_layout' import { Route as IndexRouteImport } from './routes/index' +import { Route as LayoutRouteImport } from './routes/_layout' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' -import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' +import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const LayoutRoute = LayoutRouteImport.update({ id: '/_layout', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) +const LayoutLayout2Route = LayoutLayout2RouteImport.update({ + id: '/_layout-2', + getParentRoute: () => LayoutRoute, +} as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -42,20 +46,16 @@ const PostsPostIdRoute = PostsPostIdRouteImport.update({ path: '/$postId', getParentRoute: () => PostsRoute, } as any) -const LayoutLayout2Route = LayoutLayout2RouteImport.update({ - id: '/_layout-2', - getParentRoute: () => LayoutRoute, +const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ + id: '/layout-a', + path: '/layout-a', + getParentRoute: () => LayoutLayout2Route, } as any) const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ id: '/layout-b', path: '/layout-b', getParentRoute: () => LayoutLayout2Route, } as any) -const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ - id: '/layout-a', - path: '/layout-a', - getParentRoute: () => LayoutLayout2Route, -} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -114,11 +114,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/_layout': { @@ -128,13 +128,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } + '/_layout/_layout-2': { + id: '/_layout/_layout-2' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutLayout2RouteImport + parentRoute: typeof LayoutRoute + } '/posts/': { id: '/posts/' path: '/' @@ -149,12 +156,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/_layout/_layout-2': { - id: '/_layout/_layout-2' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutLayout2RouteImport - parentRoute: typeof LayoutRoute + '/_layout/_layout-2/layout-a': { + id: '/_layout/_layout-2/layout-a' + path: '/layout-a' + fullPath: '/layout-a' + preLoaderRoute: typeof LayoutLayout2LayoutARouteImport + parentRoute: typeof LayoutLayout2Route } '/_layout/_layout-2/layout-b': { id: '/_layout/_layout-2/layout-b' @@ -163,13 +170,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport parentRoute: typeof LayoutLayout2Route } - '/_layout/_layout-2/layout-a': { - id: '/_layout/_layout-2/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: typeof LayoutLayout2LayoutARouteImport - parentRoute: typeof LayoutLayout2Route - } } } diff --git a/e2e/react-router/basic-virtual-file-based/src/routeTree.gen.ts b/e2e/react-router/basic-virtual-file-based/src/routeTree.gen.ts index 72e6928d43..eb98ba28c5 100644 --- a/e2e/react-router/basic-virtual-file-based/src/routeTree.gen.ts +++ b/e2e/react-router/basic-virtual-file-based/src/routeTree.gen.ts @@ -9,76 +9,76 @@ // 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 postsPostsRouteImport } from './routes/posts/posts' -import { Route as layoutFirstLayoutRouteImport } from './routes/layout/first-layout' import { Route as homeRouteImport } from './routes/home' -import { Route as postsPostsDetailRouteImport } from './routes/posts/posts-detail' +import { Route as layoutFirstLayoutRouteImport } from './routes/layout/first-layout' +import { Route as postsPostsRouteImport } from './routes/posts/posts' import { Route as layoutSecondLayoutRouteImport } from './routes/layout/second-layout' -import { Route as postsPostsHomeRouteImport } from './routes/posts/posts-home' import { Route as ClassicHelloRouteRouteImport } from './routes/file-based-subtree/hello/route' +import { Route as postsPostsHomeRouteImport } from './routes/posts/posts-home' +import { Route as postsPostsDetailRouteImport } from './routes/posts/posts-detail' +import { Route as aRouteImport } from './routes/a' +import { Route as bRouteImport } from './routes/b' import { Route as ClassicHelloIndexRouteImport } from './routes/file-based-subtree/hello/index' -import { Route as ClassicHelloWorldRouteImport } from './routes/file-based-subtree/hello/world' import { Route as ClassicHelloUniverseRouteImport } from './routes/file-based-subtree/hello/universe' -import { Route as bRouteImport } from './routes/b' -import { Route as aRouteImport } from './routes/a' +import { Route as ClassicHelloWorldRouteImport } from './routes/file-based-subtree/hello/world' -const postsPostsRoute = postsPostsRouteImport.update({ - id: '/posts', - path: '/posts', +const homeRoute = homeRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const layoutFirstLayoutRoute = layoutFirstLayoutRouteImport.update({ id: '/_first', getParentRoute: () => rootRouteImport, } as any) -const homeRoute = homeRouteImport.update({ - id: '/', - path: '/', +const postsPostsRoute = postsPostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const postsPostsDetailRoute = postsPostsDetailRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => postsPostsRoute, -} as any) const layoutSecondLayoutRoute = layoutSecondLayoutRouteImport.update({ id: '/_second', getParentRoute: () => layoutFirstLayoutRoute, } as any) +const ClassicHelloRouteRoute = ClassicHelloRouteRouteImport.update({ + id: '/classic/hello', + path: '/classic/hello', + getParentRoute: () => rootRouteImport, +} as any) const postsPostsHomeRoute = postsPostsHomeRouteImport.update({ id: '/', path: '/', getParentRoute: () => postsPostsRoute, } as any) -const ClassicHelloRouteRoute = ClassicHelloRouteRouteImport.update({ - id: '/classic/hello', - path: '/classic/hello', - getParentRoute: () => rootRouteImport, +const postsPostsDetailRoute = postsPostsDetailRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => postsPostsRoute, +} as any) +const aRoute = aRouteImport.update({ + id: '/layout-a', + path: '/layout-a', + getParentRoute: () => layoutSecondLayoutRoute, +} as any) +const bRoute = bRouteImport.update({ + id: '/layout-b', + path: '/layout-b', + getParentRoute: () => layoutSecondLayoutRoute, } as any) const ClassicHelloIndexRoute = ClassicHelloIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => ClassicHelloRouteRoute, } as any) -const ClassicHelloWorldRoute = ClassicHelloWorldRouteImport.update({ - id: '/world', - path: '/world', - getParentRoute: () => ClassicHelloRouteRoute, -} as any) const ClassicHelloUniverseRoute = ClassicHelloUniverseRouteImport.update({ id: '/universe', path: '/universe', getParentRoute: () => ClassicHelloRouteRoute, } as any) -const bRoute = bRouteImport.update({ - id: '/layout-b', - path: '/layout-b', - getParentRoute: () => layoutSecondLayoutRoute, -} as any) -const aRoute = aRouteImport.update({ - id: '/layout-a', - path: '/layout-a', - getParentRoute: () => layoutSecondLayoutRoute, +const ClassicHelloWorldRoute = ClassicHelloWorldRouteImport.update({ + id: '/world', + path: '/world', + getParentRoute: () => ClassicHelloRouteRoute, } as any) export interface FileRoutesByFullPath { @@ -166,11 +166,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof postsPostsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof homeRouteImport parentRoute: typeof rootRouteImport } '/_first': { @@ -180,20 +180,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof layoutFirstLayoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof homeRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof postsPostsRouteImport parentRoute: typeof rootRouteImport } - '/posts/$postId': { - id: '/posts/$postId' - path: '/$postId' - fullPath: '/posts/$postId' - preLoaderRoute: typeof postsPostsDetailRouteImport - parentRoute: typeof postsPostsRoute - } '/_first/_second': { id: '/_first/_second' path: '' @@ -201,6 +194,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof layoutSecondLayoutRouteImport parentRoute: typeof layoutFirstLayoutRoute } + '/classic/hello': { + id: '/classic/hello' + path: '/classic/hello' + fullPath: '/classic/hello' + preLoaderRoute: typeof ClassicHelloRouteRouteImport + parentRoute: typeof rootRouteImport + } '/posts/': { id: '/posts/' path: '/' @@ -208,12 +208,26 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof postsPostsHomeRouteImport parentRoute: typeof postsPostsRoute } - '/classic/hello': { - id: '/classic/hello' - path: '/classic/hello' - fullPath: '/classic/hello' - preLoaderRoute: typeof ClassicHelloRouteRouteImport - parentRoute: typeof rootRouteImport + '/posts/$postId': { + id: '/posts/$postId' + path: '/$postId' + fullPath: '/posts/$postId' + preLoaderRoute: typeof postsPostsDetailRouteImport + parentRoute: typeof postsPostsRoute + } + '/_first/_second/layout-a': { + id: '/_first/_second/layout-a' + path: '/layout-a' + fullPath: '/layout-a' + preLoaderRoute: typeof aRouteImport + parentRoute: typeof layoutSecondLayoutRoute + } + '/_first/_second/layout-b': { + id: '/_first/_second/layout-b' + path: '/layout-b' + fullPath: '/layout-b' + preLoaderRoute: typeof bRouteImport + parentRoute: typeof layoutSecondLayoutRoute } '/classic/hello/': { id: '/classic/hello/' @@ -222,13 +236,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ClassicHelloIndexRouteImport parentRoute: typeof ClassicHelloRouteRoute } - '/classic/hello/world': { - id: '/classic/hello/world' - path: '/world' - fullPath: '/classic/hello/world' - preLoaderRoute: typeof ClassicHelloWorldRouteImport - parentRoute: typeof ClassicHelloRouteRoute - } '/classic/hello/universe': { id: '/classic/hello/universe' path: '/universe' @@ -236,19 +243,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ClassicHelloUniverseRouteImport parentRoute: typeof ClassicHelloRouteRoute } - '/_first/_second/layout-b': { - id: '/_first/_second/layout-b' - path: '/layout-b' - fullPath: '/layout-b' - preLoaderRoute: typeof bRouteImport - parentRoute: typeof layoutSecondLayoutRoute - } - '/_first/_second/layout-a': { - id: '/_first/_second/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: typeof aRouteImport - parentRoute: typeof layoutSecondLayoutRoute + '/classic/hello/world': { + id: '/classic/hello/world' + path: '/world' + fullPath: '/classic/hello/world' + preLoaderRoute: typeof ClassicHelloWorldRouteImport + parentRoute: typeof ClassicHelloRouteRoute } } } diff --git a/e2e/react-router/escaped-special-strings/src/routeTree.gen.ts b/e2e/react-router/escaped-special-strings/src/routeTree.gen.ts index b0e04cd8e8..50a5f8b305 100644 --- a/e2e/react-router/escaped-special-strings/src/routeTree.gen.ts +++ b/e2e/react-router/escaped-special-strings/src/routeTree.gen.ts @@ -10,25 +10,25 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as Char91indexChar93RouteImport } from './routes/[index]' -import { Route as Char91routeChar93RouteImport } from './routes/[route]' -import { Route as Char91lazyChar93RouteImport } from './routes/[lazy]' -import { Route as BlogRouteImport } from './routes/blog[_]' -import { Route as LayoutRouteImport } from './routes/[_]layout' import { Route as IndexRouteImport } from './routes/index' +import { Route as LayoutRouteImport } from './routes/[_]layout' +import { Route as BlogRouteImport } from './routes/blog[_]' +import { Route as Char91lazyChar93RouteImport } from './routes/[lazy]' +import { Route as Char91routeChar93RouteImport } from './routes/[route]' const Char91indexChar93Route = Char91indexChar93RouteImport.update({ id: '/index', path: '/index', getParentRoute: () => rootRouteImport, } as any) -const Char91routeChar93Route = Char91routeChar93RouteImport.update({ - id: '/route', - path: '/route', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const Char91lazyChar93Route = Char91lazyChar93RouteImport.update({ - id: '/lazy', - path: '/lazy', +const LayoutRoute = LayoutRouteImport.update({ + id: '/_layout', + path: '/_layout', getParentRoute: () => rootRouteImport, } as any) const BlogRoute = BlogRouteImport.update({ @@ -36,14 +36,14 @@ const BlogRoute = BlogRouteImport.update({ path: '/blog_', getParentRoute: () => rootRouteImport, } as any) -const LayoutRoute = LayoutRouteImport.update({ - id: '/_layout', - path: '/_layout', +const Char91lazyChar93Route = Char91lazyChar93RouteImport.update({ + id: '/lazy', + path: '/lazy', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const Char91routeChar93Route = Char91routeChar93RouteImport.update({ + id: '/route', + path: '/route', getParentRoute: () => rootRouteImport, } as any) @@ -98,18 +98,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof Char91indexChar93RouteImport parentRoute: typeof rootRouteImport } - '/route': { - id: '/route' - path: '/route' - fullPath: '/route' - preLoaderRoute: typeof Char91routeChar93RouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/lazy': { - id: '/lazy' - path: '/lazy' - fullPath: '/lazy' - preLoaderRoute: typeof Char91lazyChar93RouteImport + '/_layout': { + id: '/_layout' + path: '/_layout' + fullPath: '/_layout' + preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } '/blog_': { @@ -119,18 +119,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof BlogRouteImport parentRoute: typeof rootRouteImport } - '/_layout': { - id: '/_layout' - path: '/_layout' - fullPath: '/_layout' - preLoaderRoute: typeof LayoutRouteImport + '/lazy': { + id: '/lazy' + path: '/lazy' + fullPath: '/lazy' + preLoaderRoute: typeof Char91lazyChar93RouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/route': { + id: '/route' + path: '/route' + fullPath: '/route' + preLoaderRoute: typeof Char91routeChar93RouteImport parentRoute: typeof rootRouteImport } } diff --git a/e2e/react-router/generator-cli-only/src/routeTree.gen.ts b/e2e/react-router/generator-cli-only/src/routeTree.gen.ts index 28cfbec8ed..9676858c7c 100644 --- a/e2e/react-router/generator-cli-only/src/routeTree.gen.ts +++ b/e2e/react-router/generator-cli-only/src/routeTree.gen.ts @@ -9,15 +9,20 @@ // 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 IndexRouteImport } from './routes/index' import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as PostsRouteRouteImport } from './routes/posts.route' -import { Route as IndexRouteImport } from './routes/index' +import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' -import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' +import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, @@ -27,11 +32,11 @@ const PostsRouteRoute = PostsRouteRouteImport.update({ path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) +const PathlessLayoutNestedLayoutRoute = + PathlessLayoutNestedLayoutRouteImport.update({ + id: '/_nested-layout', + getParentRoute: () => PathlessLayoutRoute, + } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -42,10 +47,11 @@ const PostsPostIdRoute = PostsPostIdRouteImport.update({ path: '/$postId', getParentRoute: () => PostsRouteRoute, } as any) -const PathlessLayoutNestedLayoutRoute = - PathlessLayoutNestedLayoutRouteImport.update({ - id: '/_nested-layout', - getParentRoute: () => PathlessLayoutRoute, +const PathlessLayoutNestedLayoutRouteARoute = + PathlessLayoutNestedLayoutRouteARouteImport.update({ + id: '/route-a', + path: '/route-a', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) const PathlessLayoutNestedLayoutRouteBRoute = PathlessLayoutNestedLayoutRouteBRouteImport.update({ @@ -53,12 +59,6 @@ const PathlessLayoutNestedLayoutRouteBRoute = path: '/route-b', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) -const PathlessLayoutNestedLayoutRouteARoute = - PathlessLayoutNestedLayoutRouteARouteImport.update({ - id: '/route-a', - path: '/route-a', - getParentRoute: () => PathlessLayoutNestedLayoutRoute, - } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -117,6 +117,13 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } '/_pathlessLayout': { id: '/_pathlessLayout' path: '' @@ -131,12 +138,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' + '/_pathlessLayout/_nested-layout': { + id: '/_pathlessLayout/_nested-layout' + path: '' fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport + parentRoute: typeof PathlessLayoutRoute } '/posts/': { id: '/posts/' @@ -152,12 +159,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRouteRoute } - '/_pathlessLayout/_nested-layout': { - id: '/_pathlessLayout/_nested-layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport - parentRoute: typeof PathlessLayoutRoute + '/_pathlessLayout/_nested-layout/route-a': { + id: '/_pathlessLayout/_nested-layout/route-a' + path: '/route-a' + fullPath: '/route-a' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport + parentRoute: typeof PathlessLayoutNestedLayoutRoute } '/_pathlessLayout/_nested-layout/route-b': { id: '/_pathlessLayout/_nested-layout/route-b' @@ -166,13 +173,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } - '/_pathlessLayout/_nested-layout/route-a': { - id: '/_pathlessLayout/_nested-layout/route-a' - path: '/route-a' - fullPath: '/route-a' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport - parentRoute: typeof PathlessLayoutNestedLayoutRoute - } } } diff --git a/e2e/react-router/js-only-file-based/src/routeTree.gen.js b/e2e/react-router/js-only-file-based/src/routeTree.gen.js index f2d42e5a6c..80683abb30 100644 --- a/e2e/react-router/js-only-file-based/src/routeTree.gen.js +++ b/e2e/react-router/js-only-file-based/src/routeTree.gen.js @@ -9,15 +9,20 @@ // 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 IndexRouteImport } from './routes/index' import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as PostsRouteRouteImport } from './routes/posts.route' -import { Route as IndexRouteImport } from './routes/index' +import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' -import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' +import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +}) const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, @@ -27,11 +32,11 @@ const PostsRouteRoute = PostsRouteRouteImport.update({ path: '/posts', getParentRoute: () => rootRouteImport, }) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -}) +const PathlessLayoutNestedLayoutRoute = + PathlessLayoutNestedLayoutRouteImport.update({ + id: '/_nested-layout', + getParentRoute: () => PathlessLayoutRoute, + }) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -42,10 +47,11 @@ const PostsPostIdRoute = PostsPostIdRouteImport.update({ path: '/$postId', getParentRoute: () => PostsRouteRoute, }) -const PathlessLayoutNestedLayoutRoute = - PathlessLayoutNestedLayoutRouteImport.update({ - id: '/_nested-layout', - getParentRoute: () => PathlessLayoutRoute, +const PathlessLayoutNestedLayoutRouteARoute = + PathlessLayoutNestedLayoutRouteARouteImport.update({ + id: '/route-a', + path: '/route-a', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, }) const PathlessLayoutNestedLayoutRouteBRoute = PathlessLayoutNestedLayoutRouteBRouteImport.update({ @@ -53,12 +59,6 @@ const PathlessLayoutNestedLayoutRouteBRoute = path: '/route-b', getParentRoute: () => PathlessLayoutNestedLayoutRoute, }) -const PathlessLayoutNestedLayoutRouteARoute = - PathlessLayoutNestedLayoutRouteARouteImport.update({ - id: '/route-a', - path: '/route-a', - getParentRoute: () => PathlessLayoutNestedLayoutRoute, - }) const PostsRouteRouteChildren = { PostsPostIdRoute: PostsPostIdRoute, diff --git a/e2e/react-router/scroll-restoration-sandbox-vite/src/routeTree.gen.ts b/e2e/react-router/scroll-restoration-sandbox-vite/src/routeTree.gen.ts index a4dc8750b3..0c6823c368 100644 --- a/e2e/react-router/scroll-restoration-sandbox-vite/src/routeTree.gen.ts +++ b/e2e/react-router/scroll-restoration-sandbox-vite/src/routeTree.gen.ts @@ -12,12 +12,12 @@ import { createFileRoute } from '@tanstack/react-router' import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' -import { Route as testsPageWithSearchRouteImport } from './routes/(tests)/page-with-search' -import { Route as testsNormalPageRouteImport } from './routes/(tests)/normal-page' -import { Route as testsLazyWithLoaderPageRouteImport } from './routes/(tests)/lazy-with-loader-page' -import { Route as testsLazyPageRouteImport } from './routes/(tests)/lazy-page' -import { Route as testsIssue7040TargetRouteImport } from './routes/(tests)/issue-7040-target' import { Route as testsIssue7040SourceRouteImport } from './routes/(tests)/issue-7040-source' +import { Route as testsIssue7040TargetRouteImport } from './routes/(tests)/issue-7040-target' +import { Route as testsLazyPageRouteImport } from './routes/(tests)/lazy-page' +import { Route as testsLazyWithLoaderPageRouteImport } from './routes/(tests)/lazy-with-loader-page' +import { Route as testsNormalPageRouteImport } from './routes/(tests)/normal-page' +import { Route as testsPageWithSearchRouteImport } from './routes/(tests)/page-with-search' const testsVirtualPageLazyRouteImport = createFileRoute( '/(tests)/virtual-page', @@ -28,23 +28,23 @@ const IndexRoute = IndexRouteImport.update({ path: '/', getParentRoute: () => rootRouteImport, } as any) -const testsVirtualPageLazyRoute = testsVirtualPageLazyRouteImport - .update({ - id: '/(tests)/virtual-page', - path: '/virtual-page', - getParentRoute: () => rootRouteImport, - } as any) - .lazy(() => import('./routes/(tests)/virtual-page.lazy').then((d) => d.Route)) -const testsPageWithSearchRoute = testsPageWithSearchRouteImport.update({ - id: '/(tests)/page-with-search', - path: '/page-with-search', +const testsIssue7040SourceRoute = testsIssue7040SourceRouteImport.update({ + id: '/(tests)/issue-7040-source', + path: '/issue-7040-source', getParentRoute: () => rootRouteImport, } as any) -const testsNormalPageRoute = testsNormalPageRouteImport.update({ - id: '/(tests)/normal-page', - path: '/normal-page', +const testsIssue7040TargetRoute = testsIssue7040TargetRouteImport.update({ + id: '/(tests)/issue-7040-target', + path: '/issue-7040-target', getParentRoute: () => rootRouteImport, } as any) +const testsLazyPageRoute = testsLazyPageRouteImport + .update({ + id: '/(tests)/lazy-page', + path: '/lazy-page', + getParentRoute: () => rootRouteImport, + } as any) + .lazy(() => import('./routes/(tests)/lazy-page.lazy').then((d) => d.Route)) const testsLazyWithLoaderPageRoute = testsLazyWithLoaderPageRouteImport .update({ id: '/(tests)/lazy-with-loader-page', @@ -54,23 +54,23 @@ const testsLazyWithLoaderPageRoute = testsLazyWithLoaderPageRouteImport .lazy(() => import('./routes/(tests)/lazy-with-loader-page.lazy').then((d) => d.Route), ) -const testsLazyPageRoute = testsLazyPageRouteImport - .update({ - id: '/(tests)/lazy-page', - path: '/lazy-page', - getParentRoute: () => rootRouteImport, - } as any) - .lazy(() => import('./routes/(tests)/lazy-page.lazy').then((d) => d.Route)) -const testsIssue7040TargetRoute = testsIssue7040TargetRouteImport.update({ - id: '/(tests)/issue-7040-target', - path: '/issue-7040-target', +const testsNormalPageRoute = testsNormalPageRouteImport.update({ + id: '/(tests)/normal-page', + path: '/normal-page', getParentRoute: () => rootRouteImport, } as any) -const testsIssue7040SourceRoute = testsIssue7040SourceRouteImport.update({ - id: '/(tests)/issue-7040-source', - path: '/issue-7040-source', +const testsPageWithSearchRoute = testsPageWithSearchRouteImport.update({ + id: '/(tests)/page-with-search', + path: '/page-with-search', getParentRoute: () => rootRouteImport, } as any) +const testsVirtualPageLazyRoute = testsVirtualPageLazyRouteImport + .update({ + id: '/(tests)/virtual-page', + path: '/virtual-page', + getParentRoute: () => rootRouteImport, + } as any) + .lazy(() => import('./routes/(tests)/virtual-page.lazy').then((d) => d.Route)) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -156,25 +156,25 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/virtual-page': { - id: '/(tests)/virtual-page' - path: '/virtual-page' - fullPath: '/virtual-page' - preLoaderRoute: typeof testsVirtualPageLazyRouteImport + '/(tests)/issue-7040-source': { + id: '/(tests)/issue-7040-source' + path: '/issue-7040-source' + fullPath: '/issue-7040-source' + preLoaderRoute: typeof testsIssue7040SourceRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/page-with-search': { - id: '/(tests)/page-with-search' - path: '/page-with-search' - fullPath: '/page-with-search' - preLoaderRoute: typeof testsPageWithSearchRouteImport + '/(tests)/issue-7040-target': { + id: '/(tests)/issue-7040-target' + path: '/issue-7040-target' + fullPath: '/issue-7040-target' + preLoaderRoute: typeof testsIssue7040TargetRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/normal-page': { - id: '/(tests)/normal-page' - path: '/normal-page' - fullPath: '/normal-page' - preLoaderRoute: typeof testsNormalPageRouteImport + '/(tests)/lazy-page': { + id: '/(tests)/lazy-page' + path: '/lazy-page' + fullPath: '/lazy-page' + preLoaderRoute: typeof testsLazyPageRouteImport parentRoute: typeof rootRouteImport } '/(tests)/lazy-with-loader-page': { @@ -184,25 +184,25 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof testsLazyWithLoaderPageRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/lazy-page': { - id: '/(tests)/lazy-page' - path: '/lazy-page' - fullPath: '/lazy-page' - preLoaderRoute: typeof testsLazyPageRouteImport + '/(tests)/normal-page': { + id: '/(tests)/normal-page' + path: '/normal-page' + fullPath: '/normal-page' + preLoaderRoute: typeof testsNormalPageRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/issue-7040-target': { - id: '/(tests)/issue-7040-target' - path: '/issue-7040-target' - fullPath: '/issue-7040-target' - preLoaderRoute: typeof testsIssue7040TargetRouteImport + '/(tests)/page-with-search': { + id: '/(tests)/page-with-search' + path: '/page-with-search' + fullPath: '/page-with-search' + preLoaderRoute: typeof testsPageWithSearchRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/issue-7040-source': { - id: '/(tests)/issue-7040-source' - path: '/issue-7040-source' - fullPath: '/issue-7040-source' - preLoaderRoute: typeof testsIssue7040SourceRouteImport + '/(tests)/virtual-page': { + id: '/(tests)/virtual-page' + path: '/virtual-page' + fullPath: '/virtual-page' + preLoaderRoute: typeof testsVirtualPageLazyRouteImport parentRoute: typeof rootRouteImport } } diff --git a/e2e/react-router/view-transitions/src/routeTree.gen.ts b/e2e/react-router/view-transitions/src/routeTree.gen.ts index 20ee76a1a5..8982fca68f 100644 --- a/e2e/react-router/view-transitions/src/routeTree.gen.ts +++ b/e2e/react-router/view-transitions/src/routeTree.gen.ts @@ -9,16 +9,16 @@ // 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 HowItWorksRouteImport } from './routes/how-it-works' +import { Route as IndexRouteImport } from './routes/index' import { Route as ExploreRouteImport } from './routes/explore' +import { Route as HowItWorksRouteImport } from './routes/how-it-works' import { Route as PostsRouteRouteImport } from './routes/posts.route' -import { Route as IndexRouteImport } from './routes/index' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -const HowItWorksRoute = HowItWorksRouteImport.update({ - id: '/how-it-works', - path: '/how-it-works', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const ExploreRoute = ExploreRouteImport.update({ @@ -26,16 +26,16 @@ const ExploreRoute = ExploreRouteImport.update({ path: '/explore', getParentRoute: () => rootRouteImport, } as any) +const HowItWorksRoute = HowItWorksRouteImport.update({ + id: '/how-it-works', + path: '/how-it-works', + getParentRoute: () => rootRouteImport, +} as any) const PostsRouteRoute = PostsRouteRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -101,11 +101,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/how-it-works': { - id: '/how-it-works' - path: '/how-it-works' - fullPath: '/how-it-works' - preLoaderRoute: typeof HowItWorksRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/explore': { @@ -115,6 +115,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ExploreRouteImport parentRoute: typeof rootRouteImport } + '/how-it-works': { + id: '/how-it-works' + path: '/how-it-works' + fullPath: '/how-it-works' + preLoaderRoute: typeof HowItWorksRouteImport + parentRoute: typeof rootRouteImport + } '/posts': { id: '/posts' path: '/posts' @@ -122,13 +129,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport - } '/posts/': { id: '/posts/' path: '/' diff --git a/e2e/react-start/basic-auth/src/routeTree.gen.ts b/e2e/react-start/basic-auth/src/routeTree.gen.ts index 6be84444eb..998d34a709 100644 --- a/e2e/react-start/basic-auth/src/routeTree.gen.ts +++ b/e2e/react-start/basic-auth/src/routeTree.gen.ts @@ -9,23 +9,22 @@ // 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 SignupRouteImport } from './routes/signup' -import { Route as LogoutRouteImport } from './routes/logout' -import { Route as LoginRouteImport } from './routes/login' -import { Route as AuthedRouteImport } from './routes/_authed' import { Route as IndexRouteImport } from './routes/index' +import { Route as AuthedRouteImport } from './routes/_authed' +import { Route as LoginRouteImport } from './routes/login' +import { Route as LogoutRouteImport } from './routes/logout' +import { Route as SignupRouteImport } from './routes/signup' import { Route as AuthedPostsRouteImport } from './routes/_authed/posts' import { Route as AuthedPostsIndexRouteImport } from './routes/_authed/posts.index' import { Route as AuthedPostsPostIdRouteImport } from './routes/_authed/posts.$postId' -const SignupRoute = SignupRouteImport.update({ - id: '/signup', - path: '/signup', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const LogoutRoute = LogoutRouteImport.update({ - id: '/logout', - path: '/logout', +const AuthedRoute = AuthedRouteImport.update({ + id: '/_authed', getParentRoute: () => rootRouteImport, } as any) const LoginRoute = LoginRouteImport.update({ @@ -33,13 +32,14 @@ const LoginRoute = LoginRouteImport.update({ path: '/login', getParentRoute: () => rootRouteImport, } as any) -const AuthedRoute = AuthedRouteImport.update({ - id: '/_authed', +const LogoutRoute = LogoutRouteImport.update({ + id: '/logout', + path: '/logout', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const SignupRoute = SignupRouteImport.update({ + id: '/signup', + path: '/signup', getParentRoute: () => rootRouteImport, } as any) const AuthedPostsRoute = AuthedPostsRouteImport.update({ @@ -120,18 +120,18 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/signup': { - id: '/signup' - path: '/signup' - fullPath: '/signup' - preLoaderRoute: typeof SignupRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/logout': { - id: '/logout' - path: '/logout' - fullPath: '/logout' - preLoaderRoute: typeof LogoutRouteImport + '/_authed': { + id: '/_authed' + path: '' + fullPath: '/' + preLoaderRoute: typeof AuthedRouteImport parentRoute: typeof rootRouteImport } '/login': { @@ -141,18 +141,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LoginRouteImport parentRoute: typeof rootRouteImport } - '/_authed': { - id: '/_authed' - path: '' - fullPath: '/' - preLoaderRoute: typeof AuthedRouteImport + '/logout': { + id: '/logout' + path: '/logout' + fullPath: '/logout' + preLoaderRoute: typeof LogoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/signup': { + id: '/signup' + path: '/signup' + fullPath: '/signup' + preLoaderRoute: typeof SignupRouteImport parentRoute: typeof rootRouteImport } '/_authed/posts': { diff --git a/e2e/react-start/basic-cloudflare/src/routeTree.gen.ts b/e2e/react-start/basic-cloudflare/src/routeTree.gen.ts index 4219501f5e..b29f0d8d68 100644 --- a/e2e/react-start/basic-cloudflare/src/routeTree.gen.ts +++ b/e2e/react-start/basic-cloudflare/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 StaticRouteImport } from './routes/static' import { Route as IndexRouteImport } from './routes/index' +import { Route as StaticRouteImport } from './routes/static' -const StaticRoute = StaticRouteImport.update({ - id: '/static', - path: '/static', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const StaticRoute = StaticRouteImport.update({ + id: '/static', + path: '/static', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/static': { - id: '/static' - path: '/static' - fullPath: '/static' - preLoaderRoute: typeof StaticRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/static': { + id: '/static' + path: '/static' + fullPath: '/static' + preLoaderRoute: typeof StaticRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/e2e/react-start/basic-react-query/src/routeTree.gen.ts b/e2e/react-start/basic-react-query/src/routeTree.gen.ts index a1bed86dfa..72bd9579f6 100644 --- a/e2e/react-start/basic-react-query/src/routeTree.gen.ts +++ b/e2e/react-start/basic-react-query/src/routeTree.gen.ts @@ -9,38 +9,37 @@ // 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 UsersRouteImport } from './routes/users' -import { Route as SuspenseTransitionRouteImport } from './routes/suspense-transition' -import { Route as RedirectRouteImport } from './routes/redirect' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as DeferredRouteImport } from './routes/deferred' -import { Route as LayoutRouteImport } from './routes/_layout' import { Route as IndexRouteImport } from './routes/index' -import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as LayoutRouteImport } from './routes/_layout' +import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as RedirectRouteImport } from './routes/redirect' +import { Route as SuspenseTransitionRouteImport } from './routes/suspense-transition' +import { Route as UsersRouteImport } from './routes/users' +import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' +import { Route as ApiUsersRouteImport } from './routes/api.users' import { Route as PostsIndexRouteImport } from './routes/posts.index' -import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as ApiUsersRouteImport } from './routes/api.users' -import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' -import { Route as TransitionCountQueryRouteImport } from './routes/transition/count/query' -import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -import { Route as ApiUsersIdRouteImport } from './routes/api/users.$id' -import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' +import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' +import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' +import { Route as ApiUsersIdRouteImport } from './routes/api/users.$id' +import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' +import { Route as TransitionCountQueryRouteImport } from './routes/transition/count/query' -const UsersRoute = UsersRouteImport.update({ - id: '/users', - path: '/users', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const SuspenseTransitionRoute = SuspenseTransitionRouteImport.update({ - id: '/suspense-transition', - path: '/suspense-transition', +const LayoutRoute = LayoutRouteImport.update({ + id: '/_layout', getParentRoute: () => rootRouteImport, } as any) -const RedirectRoute = RedirectRouteImport.update({ - id: '/redirect', - path: '/redirect', +const DeferredRoute = DeferredRouteImport.update({ + id: '/deferred', + path: '/deferred', getParentRoute: () => rootRouteImport, } as any) const PostsRoute = PostsRouteImport.update({ @@ -48,73 +47,74 @@ const PostsRoute = PostsRouteImport.update({ path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const DeferredRoute = DeferredRouteImport.update({ - id: '/deferred', - path: '/deferred', +const RedirectRoute = RedirectRouteImport.update({ + id: '/redirect', + path: '/redirect', getParentRoute: () => rootRouteImport, } as any) -const LayoutRoute = LayoutRouteImport.update({ - id: '/_layout', +const SuspenseTransitionRoute = SuspenseTransitionRouteImport.update({ + id: '/suspense-transition', + path: '/suspense-transition', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const UsersRoute = UsersRouteImport.update({ + id: '/users', + path: '/users', getParentRoute: () => rootRouteImport, } as any) -const UsersIndexRoute = UsersIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => UsersRoute, +const LayoutLayout2Route = LayoutLayout2RouteImport.update({ + id: '/_layout-2', + getParentRoute: () => LayoutRoute, +} as any) +const ApiUsersRoute = ApiUsersRouteImport.update({ + id: '/api/users', + path: '/api/users', + getParentRoute: () => rootRouteImport, } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRoute, } as any) -const UsersUserIdRoute = UsersUserIdRouteImport.update({ - id: '/$userId', - path: '/$userId', - getParentRoute: () => UsersRoute, -} as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRoute, } as any) -const ApiUsersRoute = ApiUsersRouteImport.update({ - id: '/api/users', - path: '/api/users', - getParentRoute: () => rootRouteImport, +const UsersIndexRoute = UsersIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => UsersRoute, } as any) -const LayoutLayout2Route = LayoutLayout2RouteImport.update({ - id: '/_layout-2', - getParentRoute: () => LayoutRoute, +const UsersUserIdRoute = UsersUserIdRouteImport.update({ + id: '/$userId', + path: '/$userId', + getParentRoute: () => UsersRoute, } as any) -const TransitionCountQueryRoute = TransitionCountQueryRouteImport.update({ - id: '/transition/count/query', - path: '/transition/count/query', - getParentRoute: () => rootRouteImport, +const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ + id: '/layout-a', + path: '/layout-a', + getParentRoute: () => LayoutLayout2Route, } as any) -const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ - id: '/posts_/$postId/deep', - path: '/posts/$postId/deep', - getParentRoute: () => rootRouteImport, +const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ + id: '/layout-b', + path: '/layout-b', + getParentRoute: () => LayoutLayout2Route, } as any) const ApiUsersIdRoute = ApiUsersIdRouteImport.update({ id: '/$id', path: '/$id', getParentRoute: () => ApiUsersRoute, } as any) -const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ - id: '/layout-b', - path: '/layout-b', - getParentRoute: () => LayoutLayout2Route, +const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ + id: '/posts_/$postId/deep', + path: '/posts/$postId/deep', + getParentRoute: () => rootRouteImport, } as any) -const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ - id: '/layout-a', - path: '/layout-a', - getParentRoute: () => LayoutLayout2Route, +const TransitionCountQueryRoute = TransitionCountQueryRouteImport.update({ + id: '/transition/count/query', + path: '/transition/count/query', + getParentRoute: () => rootRouteImport, } as any) export interface FileRoutesByFullPath { @@ -244,25 +244,25 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/users': { - id: '/users' - path: '/users' - fullPath: '/users' - preLoaderRoute: typeof UsersRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/suspense-transition': { - id: '/suspense-transition' - path: '/suspense-transition' - fullPath: '/suspense-transition' - preLoaderRoute: typeof SuspenseTransitionRouteImport + '/_layout': { + id: '/_layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } - '/redirect': { - id: '/redirect' - path: '/redirect' - fullPath: '/redirect' - preLoaderRoute: typeof RedirectRouteImport + '/deferred': { + id: '/deferred' + path: '/deferred' + fullPath: '/deferred' + preLoaderRoute: typeof DeferredRouteImport parentRoute: typeof rootRouteImport } '/posts': { @@ -272,33 +272,40 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/deferred': { - id: '/deferred' - path: '/deferred' - fullPath: '/deferred' - preLoaderRoute: typeof DeferredRouteImport + '/redirect': { + id: '/redirect' + path: '/redirect' + fullPath: '/redirect' + preLoaderRoute: typeof RedirectRouteImport parentRoute: typeof rootRouteImport } - '/_layout': { - id: '/_layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutRouteImport + '/suspense-transition': { + id: '/suspense-transition' + path: '/suspense-transition' + fullPath: '/suspense-transition' + preLoaderRoute: typeof SuspenseTransitionRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/users': { + id: '/users' + path: '/users' + fullPath: '/users' + preLoaderRoute: typeof UsersRouteImport parentRoute: typeof rootRouteImport } - '/users/': { - id: '/users/' - path: '/' - fullPath: '/users/' - preLoaderRoute: typeof UsersIndexRouteImport - parentRoute: typeof UsersRoute + '/_layout/_layout-2': { + id: '/_layout/_layout-2' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutLayout2RouteImport + parentRoute: typeof LayoutRoute + } + '/api/users': { + id: '/api/users' + path: '/api/users' + fullPath: '/api/users' + preLoaderRoute: typeof ApiUsersRouteImport + parentRoute: typeof rootRouteImport } '/posts/': { id: '/posts/' @@ -307,13 +314,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRoute } - '/users/$userId': { - id: '/users/$userId' - path: '/$userId' - fullPath: '/users/$userId' - preLoaderRoute: typeof UsersUserIdRouteImport - parentRoute: typeof UsersRoute - } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' @@ -321,33 +321,33 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/api/users': { - id: '/api/users' - path: '/api/users' - fullPath: '/api/users' - preLoaderRoute: typeof ApiUsersRouteImport - parentRoute: typeof rootRouteImport + '/users/': { + id: '/users/' + path: '/' + fullPath: '/users/' + preLoaderRoute: typeof UsersIndexRouteImport + parentRoute: typeof UsersRoute } - '/_layout/_layout-2': { - id: '/_layout/_layout-2' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutLayout2RouteImport - parentRoute: typeof LayoutRoute + '/users/$userId': { + id: '/users/$userId' + path: '/$userId' + fullPath: '/users/$userId' + preLoaderRoute: typeof UsersUserIdRouteImport + parentRoute: typeof UsersRoute } - '/transition/count/query': { - id: '/transition/count/query' - path: '/transition/count/query' - fullPath: '/transition/count/query' - preLoaderRoute: typeof TransitionCountQueryRouteImport - parentRoute: typeof rootRouteImport + '/_layout/_layout-2/layout-a': { + id: '/_layout/_layout-2/layout-a' + path: '/layout-a' + fullPath: '/layout-a' + preLoaderRoute: typeof LayoutLayout2LayoutARouteImport + parentRoute: typeof LayoutLayout2Route } - '/posts_/$postId/deep': { - id: '/posts_/$postId/deep' - path: '/posts/$postId/deep' - fullPath: '/posts/$postId/deep' - preLoaderRoute: typeof PostsPostIdDeepRouteImport - parentRoute: typeof rootRouteImport + '/_layout/_layout-2/layout-b': { + id: '/_layout/_layout-2/layout-b' + path: '/layout-b' + fullPath: '/layout-b' + preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport + parentRoute: typeof LayoutLayout2Route } '/api/users/$id': { id: '/api/users/$id' @@ -356,19 +356,19 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ApiUsersIdRouteImport parentRoute: typeof ApiUsersRoute } - '/_layout/_layout-2/layout-b': { - id: '/_layout/_layout-2/layout-b' - path: '/layout-b' - fullPath: '/layout-b' - preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport - parentRoute: typeof LayoutLayout2Route + '/posts_/$postId/deep': { + id: '/posts_/$postId/deep' + path: '/posts/$postId/deep' + fullPath: '/posts/$postId/deep' + preLoaderRoute: typeof PostsPostIdDeepRouteImport + parentRoute: typeof rootRouteImport } - '/_layout/_layout-2/layout-a': { - id: '/_layout/_layout-2/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: typeof LayoutLayout2LayoutARouteImport - parentRoute: typeof LayoutLayout2Route + '/transition/count/query': { + id: '/transition/count/query' + path: '/transition/count/query' + fullPath: '/transition/count/query' + preLoaderRoute: typeof TransitionCountQueryRouteImport + parentRoute: typeof rootRouteImport } } } diff --git a/e2e/react-start/basic/src/routeTree.gen.ts b/e2e/react-start/basic/src/routeTree.gen.ts index 103853f3b8..10a40bc044 100644 --- a/e2e/react-start/basic/src/routeTree.gen.ts +++ b/e2e/react-start/basic/src/routeTree.gen.ts @@ -9,116 +9,104 @@ // 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 UsersRouteImport } from './routes/users' -import { Route as TypeOnlyReexportRouteImport } from './routes/type-only-reexport' -import { Route as StreamRouteImport } from './routes/stream' -import { Route as ScriptsRouteImport } from './routes/scripts' -import { Route as RawStreamRouteImport } from './routes/raw-stream' -import { Route as PrimitiveBeforeloadErrorRouteImport } from './routes/primitive-beforeload-error' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as PlainTsTypeAssertionRouteImport } from './routes/plain-ts-type-assertion' -import { Route as LinksRouteImport } from './routes/links' -import { Route as InlineScriptsRouteImport } from './routes/inline-scripts' -import { Route as DeferredRouteImport } from './routes/deferred' -import { Route as ClientOnlyRouteImport } from './routes/client-only' -import { Route as AsyncScriptsRouteImport } from './routes/async-scripts' +import { Route as IndexRouteImport } from './routes/index' import { Route as LayoutRouteImport } from './routes/_layout' -import { Route as SpecialCharsRouteRouteImport } from './routes/specialChars/route' -import { Route as SearchParamsRouteRouteImport } from './routes/search-params/route' +import { Route as AsyncScriptsRouteImport } from './routes/async-scripts' +import { Route as ClientOnlyRouteImport } from './routes/client-only' +import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as InlineScriptsRouteImport } from './routes/inline-scripts' +import { Route as LinksRouteImport } from './routes/links' import { Route as NotFoundRouteRouteImport } from './routes/not-found/route' -import { Route as IndexRouteImport } from './routes/index' -import { Route as UsersIndexRouteImport } from './routes/users.index' -import { Route as SearchParamsIndexRouteImport } from './routes/search-params/index' -import { Route as RedirectIndexRouteImport } from './routes/redirect/index' -import { Route as RawStreamIndexRouteImport } from './routes/raw-stream/index' -import { Route as PostsIndexRouteImport } from './routes/posts.index' -import { Route as NotFoundIndexRouteImport } from './routes/not-found/index' +import { Route as PlainTsTypeAssertionRouteImport } from './routes/plain-ts-type-assertion' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as PrimitiveBeforeloadErrorRouteImport } from './routes/primitive-beforeload-error' +import { Route as RawStreamRouteImport } from './routes/raw-stream' +import { Route as ScriptsRouteImport } from './routes/scripts' +import { Route as SearchParamsRouteRouteImport } from './routes/search-params/route' +import { Route as SpecialCharsRouteRouteImport } from './routes/specialChars/route' +import { Route as StreamRouteImport } from './routes/stream' +import { Route as TypeOnlyReexportRouteImport } from './routes/type-only-reexport' +import { Route as UsersRouteImport } from './routes/users' +import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' +import { Route as ApiUsersRouteImport } from './routes/api.users' import { Route as MultiCookieRedirectIndexRouteImport } from './routes/multi-cookie-redirect/index' -import { Route as UsersUserIdRouteImport } from './routes/users.$userId' -import { Route as SpecialCharsChar45824Char54620Char48124Char44397RouteImport } from './routes/specialChars/대한민국' -import { Route as SpecialCharsSearchRouteImport } from './routes/specialChars/search' -import { Route as SpecialCharsHashRouteImport } from './routes/specialChars/hash' -import { Route as SpecialCharsParamRouteImport } from './routes/specialChars/$param' -import { Route as SearchParamsLoaderThrowsRedirectRouteImport } from './routes/search-params/loader-throws-redirect' -import { Route as SearchParamsDefaultRouteImport } from './routes/search-params/default' -import { Route as RedirectTargetRouteImport } from './routes/redirect/$target' -import { Route as RawStreamSsrTextHintRouteImport } from './routes/raw-stream/ssr-text-hint' -import { Route as RawStreamSsrSingleRouteImport } from './routes/raw-stream/ssr-single' -import { Route as RawStreamSsrMultipleRouteImport } from './routes/raw-stream/ssr-multiple' -import { Route as RawStreamSsrMixedRouteImport } from './routes/raw-stream/ssr-mixed' -import { Route as RawStreamSsrBinaryHintRouteImport } from './routes/raw-stream/ssr-binary-hint' -import { Route as RawStreamClientCallRouteImport } from './routes/raw-stream/client-call' -import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as NotFoundViaLoaderRouteImport } from './routes/not-found/via-loader' -import { Route as NotFoundViaBeforeLoadTargetRootRouteImport } from './routes/not-found/via-beforeLoad-target-root' -import { Route as NotFoundViaBeforeLoadRouteImport } from './routes/not-found/via-beforeLoad' import { Route as MultiCookieRedirectTargetRouteImport } from './routes/multi-cookie-redirect/target' -import { Route as ApiUsersRouteImport } from './routes/api.users' -import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' -import { Route as SpecialCharsMalformedRouteRouteImport } from './routes/specialChars/malformed/route' -import { Route as NotFoundParentBoundaryRouteRouteImport } from './routes/not-found/parent-boundary/route' +import { Route as NotFoundIndexRouteImport } from './routes/not-found/index' import { Route as NotFoundDeepRouteRouteImport } from './routes/not-found/deep/route' -import { Route as RedirectTargetIndexRouteImport } from './routes/redirect/$target/index' -import { Route as NotFoundParentBoundaryIndexRouteImport } from './routes/not-found/parent-boundary/index' -import { Route as NotFoundDeepIndexRouteImport } from './routes/not-found/deep/index' -import { Route as SpecialCharsMalformedSearchRouteImport } from './routes/specialChars/malformed/search' -import { Route as SpecialCharsMalformedParamRouteImport } from './routes/specialChars/malformed/$param' -import { Route as RedirectTargetViaLoaderRouteImport } from './routes/redirect/$target/via-loader' -import { Route as RedirectTargetViaBeforeLoadRouteImport } from './routes/redirect/$target/via-beforeLoad' -import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -import { Route as NotFoundParentBoundaryViaBeforeLoadRouteImport } from './routes/not-found/parent-boundary/via-beforeLoad' -import { Route as ApiUsersIdRouteImport } from './routes/api/users.$id' -import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' +import { Route as NotFoundParentBoundaryRouteRouteImport } from './routes/not-found/parent-boundary/route' +import { Route as NotFoundViaBeforeLoadRouteImport } from './routes/not-found/via-beforeLoad' +import { Route as NotFoundViaBeforeLoadTargetRootRouteImport } from './routes/not-found/via-beforeLoad-target-root' +import { Route as NotFoundViaLoaderRouteImport } from './routes/not-found/via-loader' +import { Route as PostsIndexRouteImport } from './routes/posts.index' +import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' +import { Route as RawStreamIndexRouteImport } from './routes/raw-stream/index' +import { Route as RawStreamClientCallRouteImport } from './routes/raw-stream/client-call' +import { Route as RawStreamSsrBinaryHintRouteImport } from './routes/raw-stream/ssr-binary-hint' +import { Route as RawStreamSsrMixedRouteImport } from './routes/raw-stream/ssr-mixed' +import { Route as RawStreamSsrMultipleRouteImport } from './routes/raw-stream/ssr-multiple' +import { Route as RawStreamSsrSingleRouteImport } from './routes/raw-stream/ssr-single' +import { Route as RawStreamSsrTextHintRouteImport } from './routes/raw-stream/ssr-text-hint' +import { Route as RedirectIndexRouteImport } from './routes/redirect/index' +import { Route as RedirectTargetRouteImport } from './routes/redirect/$target' +import { Route as SearchParamsIndexRouteImport } from './routes/search-params/index' +import { Route as SearchParamsDefaultRouteImport } from './routes/search-params/default' +import { Route as SearchParamsLoaderThrowsRedirectRouteImport } from './routes/search-params/loader-throws-redirect' +import { Route as SpecialCharsParamRouteImport } from './routes/specialChars/$param' +import { Route as SpecialCharsHashRouteImport } from './routes/specialChars/hash' +import { Route as SpecialCharsMalformedRouteRouteImport } from './routes/specialChars/malformed/route' +import { Route as SpecialCharsSearchRouteImport } from './routes/specialChars/search' +import { Route as SpecialCharsChar45824Char54620Char48124Char44397RouteImport } from './routes/specialChars/대한민국' +import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' +import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' +import { Route as ApiUsersIdRouteImport } from './routes/api/users.$id' +import { Route as NotFoundDeepIndexRouteImport } from './routes/not-found/deep/index' import { Route as NotFoundDeepBRouteRouteImport } from './routes/not-found/deep/b/route' -import { Route as RedirectTargetServerFnIndexRouteImport } from './routes/redirect/$target/serverFn/index' -import { Route as RedirectTargetServerFnViaUseServerFnRouteImport } from './routes/redirect/$target/serverFn/via-useServerFn' -import { Route as RedirectTargetServerFnViaLoaderRouteImport } from './routes/redirect/$target/serverFn/via-loader' -import { Route as RedirectTargetServerFnViaBeforeLoadRouteImport } from './routes/redirect/$target/serverFn/via-beforeLoad' +import { Route as NotFoundParentBoundaryIndexRouteImport } from './routes/not-found/parent-boundary/index' +import { Route as NotFoundParentBoundaryViaBeforeLoadRouteImport } from './routes/not-found/parent-boundary/via-beforeLoad' +import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' +import { Route as RedirectTargetIndexRouteImport } from './routes/redirect/$target/index' +import { Route as RedirectTargetViaBeforeLoadRouteImport } from './routes/redirect/$target/via-beforeLoad' +import { Route as RedirectTargetViaLoaderRouteImport } from './routes/redirect/$target/via-loader' +import { Route as SpecialCharsMalformedParamRouteImport } from './routes/specialChars/malformed/$param' +import { Route as SpecialCharsMalformedSearchRouteImport } from './routes/specialChars/malformed/search' import { Route as FooBarQuxHereRouteImport } from './routes/foo/$bar/$qux/_here' import { Route as NotFoundDeepBCRouteRouteImport } from './routes/not-found/deep/b/c/route' +import { Route as RedirectTargetServerFnIndexRouteImport } from './routes/redirect/$target/serverFn/index' +import { Route as RedirectTargetServerFnViaBeforeLoadRouteImport } from './routes/redirect/$target/serverFn/via-beforeLoad' +import { Route as RedirectTargetServerFnViaLoaderRouteImport } from './routes/redirect/$target/serverFn/via-loader' +import { Route as RedirectTargetServerFnViaUseServerFnRouteImport } from './routes/redirect/$target/serverFn/via-useServerFn' import { Route as FooBarQuxHereIndexRouteImport } from './routes/foo/$bar/$qux/_here/index' import { Route as NotFoundDeepBCDRouteImport } from './routes/not-found/deep/b/c/d' -const UsersRoute = UsersRouteImport.update({ - id: '/users', - path: '/users', - getParentRoute: () => rootRouteImport, -} as any) -const TypeOnlyReexportRoute = TypeOnlyReexportRouteImport.update({ - id: '/type-only-reexport', - path: '/type-only-reexport', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const StreamRoute = StreamRouteImport.update({ - id: '/stream', - path: '/stream', +const LayoutRoute = LayoutRouteImport.update({ + id: '/_layout', getParentRoute: () => rootRouteImport, } as any) -const ScriptsRoute = ScriptsRouteImport.update({ - id: '/scripts', - path: '/scripts', +const AsyncScriptsRoute = AsyncScriptsRouteImport.update({ + id: '/async-scripts', + path: '/async-scripts', getParentRoute: () => rootRouteImport, } as any) -const RawStreamRoute = RawStreamRouteImport.update({ - id: '/raw-stream', - path: '/raw-stream', +const ClientOnlyRoute = ClientOnlyRouteImport.update({ + id: '/client-only', + path: '/client-only', getParentRoute: () => rootRouteImport, } as any) -const PrimitiveBeforeloadErrorRoute = - PrimitiveBeforeloadErrorRouteImport.update({ - id: '/primitive-beforeload-error', - path: '/primitive-beforeload-error', - getParentRoute: () => rootRouteImport, - } as any) -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const DeferredRoute = DeferredRouteImport.update({ + id: '/deferred', + path: '/deferred', getParentRoute: () => rootRouteImport, } as any) -const PlainTsTypeAssertionRoute = PlainTsTypeAssertionRouteImport.update({ - id: '/plain-ts-type-assertion', - path: '/plain-ts-type-assertion', +const InlineScriptsRoute = InlineScriptsRouteImport.update({ + id: '/inline-scripts', + path: '/inline-scripts', getParentRoute: () => rootRouteImport, } as any) const LinksRoute = LinksRouteImport.update({ @@ -126,33 +114,35 @@ const LinksRoute = LinksRouteImport.update({ path: '/links', getParentRoute: () => rootRouteImport, } as any) -const InlineScriptsRoute = InlineScriptsRouteImport.update({ - id: '/inline-scripts', - path: '/inline-scripts', - getParentRoute: () => rootRouteImport, -} as any) -const DeferredRoute = DeferredRouteImport.update({ - id: '/deferred', - path: '/deferred', +const NotFoundRouteRoute = NotFoundRouteRouteImport.update({ + id: '/not-found', + path: '/not-found', getParentRoute: () => rootRouteImport, } as any) -const ClientOnlyRoute = ClientOnlyRouteImport.update({ - id: '/client-only', - path: '/client-only', +const PlainTsTypeAssertionRoute = PlainTsTypeAssertionRouteImport.update({ + id: '/plain-ts-type-assertion', + path: '/plain-ts-type-assertion', getParentRoute: () => rootRouteImport, } as any) -const AsyncScriptsRoute = AsyncScriptsRouteImport.update({ - id: '/async-scripts', - path: '/async-scripts', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const LayoutRoute = LayoutRouteImport.update({ - id: '/_layout', +const PrimitiveBeforeloadErrorRoute = + PrimitiveBeforeloadErrorRouteImport.update({ + id: '/primitive-beforeload-error', + path: '/primitive-beforeload-error', + getParentRoute: () => rootRouteImport, + } as any) +const RawStreamRoute = RawStreamRouteImport.update({ + id: '/raw-stream', + path: '/raw-stream', getParentRoute: () => rootRouteImport, } as any) -const SpecialCharsRouteRoute = SpecialCharsRouteRouteImport.update({ - id: '/specialChars', - path: '/specialChars', +const ScriptsRoute = ScriptsRouteImport.update({ + id: '/scripts', + path: '/scripts', getParentRoute: () => rootRouteImport, } as any) const SearchParamsRouteRoute = SearchParamsRouteRouteImport.update({ @@ -160,45 +150,34 @@ const SearchParamsRouteRoute = SearchParamsRouteRouteImport.update({ path: '/search-params', getParentRoute: () => rootRouteImport, } as any) -const NotFoundRouteRoute = NotFoundRouteRouteImport.update({ - id: '/not-found', - path: '/not-found', +const SpecialCharsRouteRoute = SpecialCharsRouteRouteImport.update({ + id: '/specialChars', + path: '/specialChars', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const StreamRoute = StreamRouteImport.update({ + id: '/stream', + path: '/stream', getParentRoute: () => rootRouteImport, } as any) -const UsersIndexRoute = UsersIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => UsersRoute, -} as any) -const SearchParamsIndexRoute = SearchParamsIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => SearchParamsRouteRoute, -} as any) -const RedirectIndexRoute = RedirectIndexRouteImport.update({ - id: '/redirect/', - path: '/redirect/', +const TypeOnlyReexportRoute = TypeOnlyReexportRouteImport.update({ + id: '/type-only-reexport', + path: '/type-only-reexport', getParentRoute: () => rootRouteImport, } as any) -const RawStreamIndexRoute = RawStreamIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => RawStreamRoute, +const UsersRoute = UsersRouteImport.update({ + id: '/users', + path: '/users', + getParentRoute: () => rootRouteImport, } as any) -const PostsIndexRoute = PostsIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => PostsRoute, +const LayoutLayout2Route = LayoutLayout2RouteImport.update({ + id: '/_layout-2', + getParentRoute: () => LayoutRoute, } as any) -const NotFoundIndexRoute = NotFoundIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => NotFoundRouteRoute, +const ApiUsersRoute = ApiUsersRouteImport.update({ + id: '/api/users', + path: '/api/users', + getParentRoute: () => rootRouteImport, } as any) const MultiCookieRedirectIndexRoute = MultiCookieRedirectIndexRouteImport.update({ @@ -206,61 +185,67 @@ const MultiCookieRedirectIndexRoute = path: '/multi-cookie-redirect/', getParentRoute: () => rootRouteImport, } as any) -const UsersUserIdRoute = UsersUserIdRouteImport.update({ - id: '/$userId', - path: '/$userId', - getParentRoute: () => UsersRoute, -} as any) -const SpecialCharsChar45824Char54620Char48124Char44397Route = - SpecialCharsChar45824Char54620Char48124Char44397RouteImport.update({ - id: '/대한민국', - path: '/대한민국', - getParentRoute: () => SpecialCharsRouteRoute, +const MultiCookieRedirectTargetRoute = + MultiCookieRedirectTargetRouteImport.update({ + id: '/multi-cookie-redirect/target', + path: '/multi-cookie-redirect/target', + getParentRoute: () => rootRouteImport, } as any) -const SpecialCharsSearchRoute = SpecialCharsSearchRouteImport.update({ - id: '/search', - path: '/search', - getParentRoute: () => SpecialCharsRouteRoute, +const NotFoundIndexRoute = NotFoundIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => NotFoundRouteRoute, } as any) -const SpecialCharsHashRoute = SpecialCharsHashRouteImport.update({ - id: '/hash', - path: '/hash', - getParentRoute: () => SpecialCharsRouteRoute, +const NotFoundDeepRouteRoute = NotFoundDeepRouteRouteImport.update({ + id: '/deep', + path: '/deep', + getParentRoute: () => NotFoundRouteRoute, } as any) -const SpecialCharsParamRoute = SpecialCharsParamRouteImport.update({ - id: '/$param', - path: '/$param', - getParentRoute: () => SpecialCharsRouteRoute, +const NotFoundParentBoundaryRouteRoute = + NotFoundParentBoundaryRouteRouteImport.update({ + id: '/parent-boundary', + path: '/parent-boundary', + getParentRoute: () => NotFoundRouteRoute, + } as any) +const NotFoundViaBeforeLoadRoute = NotFoundViaBeforeLoadRouteImport.update({ + id: '/via-beforeLoad', + path: '/via-beforeLoad', + getParentRoute: () => NotFoundRouteRoute, } as any) -const SearchParamsLoaderThrowsRedirectRoute = - SearchParamsLoaderThrowsRedirectRouteImport.update({ - id: '/loader-throws-redirect', - path: '/loader-throws-redirect', - getParentRoute: () => SearchParamsRouteRoute, +const NotFoundViaBeforeLoadTargetRootRoute = + NotFoundViaBeforeLoadTargetRootRouteImport.update({ + id: '/via-beforeLoad-target-root', + path: '/via-beforeLoad-target-root', + getParentRoute: () => NotFoundRouteRoute, } as any) -const SearchParamsDefaultRoute = SearchParamsDefaultRouteImport.update({ - id: '/default', - path: '/default', - getParentRoute: () => SearchParamsRouteRoute, +const NotFoundViaLoaderRoute = NotFoundViaLoaderRouteImport.update({ + id: '/via-loader', + path: '/via-loader', + getParentRoute: () => NotFoundRouteRoute, } as any) -const RedirectTargetRoute = RedirectTargetRouteImport.update({ - id: '/redirect/$target', - path: '/redirect/$target', - getParentRoute: () => rootRouteImport, +const PostsIndexRoute = PostsIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => PostsRoute, } as any) -const RawStreamSsrTextHintRoute = RawStreamSsrTextHintRouteImport.update({ - id: '/ssr-text-hint', - path: '/ssr-text-hint', +const PostsPostIdRoute = PostsPostIdRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => PostsRoute, +} as any) +const RawStreamIndexRoute = RawStreamIndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => RawStreamRoute, } as any) -const RawStreamSsrSingleRoute = RawStreamSsrSingleRouteImport.update({ - id: '/ssr-single', - path: '/ssr-single', +const RawStreamClientCallRoute = RawStreamClientCallRouteImport.update({ + id: '/client-call', + path: '/client-call', getParentRoute: () => RawStreamRoute, } as any) -const RawStreamSsrMultipleRoute = RawStreamSsrMultipleRouteImport.update({ - id: '/ssr-multiple', - path: '/ssr-multiple', +const RawStreamSsrBinaryHintRoute = RawStreamSsrBinaryHintRouteImport.update({ + id: '/ssr-binary-hint', + path: '/ssr-binary-hint', getParentRoute: () => RawStreamRoute, } as any) const RawStreamSsrMixedRoute = RawStreamSsrMixedRouteImport.update({ @@ -268,51 +253,56 @@ const RawStreamSsrMixedRoute = RawStreamSsrMixedRouteImport.update({ path: '/ssr-mixed', getParentRoute: () => RawStreamRoute, } as any) -const RawStreamSsrBinaryHintRoute = RawStreamSsrBinaryHintRouteImport.update({ - id: '/ssr-binary-hint', - path: '/ssr-binary-hint', +const RawStreamSsrMultipleRoute = RawStreamSsrMultipleRouteImport.update({ + id: '/ssr-multiple', + path: '/ssr-multiple', getParentRoute: () => RawStreamRoute, } as any) -const RawStreamClientCallRoute = RawStreamClientCallRouteImport.update({ - id: '/client-call', - path: '/client-call', +const RawStreamSsrSingleRoute = RawStreamSsrSingleRouteImport.update({ + id: '/ssr-single', + path: '/ssr-single', getParentRoute: () => RawStreamRoute, } as any) -const PostsPostIdRoute = PostsPostIdRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => PostsRoute, +const RawStreamSsrTextHintRoute = RawStreamSsrTextHintRouteImport.update({ + id: '/ssr-text-hint', + path: '/ssr-text-hint', + getParentRoute: () => RawStreamRoute, } as any) -const NotFoundViaLoaderRoute = NotFoundViaLoaderRouteImport.update({ - id: '/via-loader', - path: '/via-loader', - getParentRoute: () => NotFoundRouteRoute, +const RedirectIndexRoute = RedirectIndexRouteImport.update({ + id: '/redirect/', + path: '/redirect/', + getParentRoute: () => rootRouteImport, } as any) -const NotFoundViaBeforeLoadTargetRootRoute = - NotFoundViaBeforeLoadTargetRootRouteImport.update({ - id: '/via-beforeLoad-target-root', - path: '/via-beforeLoad-target-root', - getParentRoute: () => NotFoundRouteRoute, - } as any) -const NotFoundViaBeforeLoadRoute = NotFoundViaBeforeLoadRouteImport.update({ - id: '/via-beforeLoad', - path: '/via-beforeLoad', - getParentRoute: () => NotFoundRouteRoute, +const RedirectTargetRoute = RedirectTargetRouteImport.update({ + id: '/redirect/$target', + path: '/redirect/$target', + getParentRoute: () => rootRouteImport, } as any) -const MultiCookieRedirectTargetRoute = - MultiCookieRedirectTargetRouteImport.update({ - id: '/multi-cookie-redirect/target', - path: '/multi-cookie-redirect/target', - getParentRoute: () => rootRouteImport, +const SearchParamsIndexRoute = SearchParamsIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => SearchParamsRouteRoute, +} as any) +const SearchParamsDefaultRoute = SearchParamsDefaultRouteImport.update({ + id: '/default', + path: '/default', + getParentRoute: () => SearchParamsRouteRoute, +} as any) +const SearchParamsLoaderThrowsRedirectRoute = + SearchParamsLoaderThrowsRedirectRouteImport.update({ + id: '/loader-throws-redirect', + path: '/loader-throws-redirect', + getParentRoute: () => SearchParamsRouteRoute, } as any) -const ApiUsersRoute = ApiUsersRouteImport.update({ - id: '/api/users', - path: '/api/users', - getParentRoute: () => rootRouteImport, +const SpecialCharsParamRoute = SpecialCharsParamRouteImport.update({ + id: '/$param', + path: '/$param', + getParentRoute: () => SpecialCharsRouteRoute, } as any) -const LayoutLayout2Route = LayoutLayout2RouteImport.update({ - id: '/_layout-2', - getParentRoute: () => LayoutRoute, +const SpecialCharsHashRoute = SpecialCharsHashRouteImport.update({ + id: '/hash', + path: '/hash', + getParentRoute: () => SpecialCharsRouteRoute, } as any) const SpecialCharsMalformedRouteRoute = SpecialCharsMalformedRouteRouteImport.update({ @@ -320,21 +310,51 @@ const SpecialCharsMalformedRouteRoute = path: '/malformed', getParentRoute: () => SpecialCharsRouteRoute, } as any) -const NotFoundParentBoundaryRouteRoute = - NotFoundParentBoundaryRouteRouteImport.update({ - id: '/parent-boundary', - path: '/parent-boundary', - getParentRoute: () => NotFoundRouteRoute, +const SpecialCharsSearchRoute = SpecialCharsSearchRouteImport.update({ + id: '/search', + path: '/search', + getParentRoute: () => SpecialCharsRouteRoute, +} as any) +const SpecialCharsChar45824Char54620Char48124Char44397Route = + SpecialCharsChar45824Char54620Char48124Char44397RouteImport.update({ + id: '/대한민국', + path: '/대한민국', + getParentRoute: () => SpecialCharsRouteRoute, } as any) -const NotFoundDeepRouteRoute = NotFoundDeepRouteRouteImport.update({ - id: '/deep', - path: '/deep', - getParentRoute: () => NotFoundRouteRoute, +const UsersIndexRoute = UsersIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => UsersRoute, } as any) -const RedirectTargetIndexRoute = RedirectTargetIndexRouteImport.update({ +const UsersUserIdRoute = UsersUserIdRouteImport.update({ + id: '/$userId', + path: '/$userId', + getParentRoute: () => UsersRoute, +} as any) +const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ + id: '/layout-a', + path: '/layout-a', + getParentRoute: () => LayoutLayout2Route, +} as any) +const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ + id: '/layout-b', + path: '/layout-b', + getParentRoute: () => LayoutLayout2Route, +} as any) +const ApiUsersIdRoute = ApiUsersIdRouteImport.update({ + id: '/$id', + path: '/$id', + getParentRoute: () => ApiUsersRoute, +} as any) +const NotFoundDeepIndexRoute = NotFoundDeepIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => RedirectTargetRoute, + getParentRoute: () => NotFoundDeepRouteRoute, +} as any) +const NotFoundDeepBRouteRoute = NotFoundDeepBRouteRouteImport.update({ + id: '/b', + path: '/b', + getParentRoute: () => NotFoundDeepRouteRoute, } as any) const NotFoundParentBoundaryIndexRoute = NotFoundParentBoundaryIndexRouteImport.update({ @@ -342,26 +362,20 @@ const NotFoundParentBoundaryIndexRoute = path: '/', getParentRoute: () => NotFoundParentBoundaryRouteRoute, } as any) -const NotFoundDeepIndexRoute = NotFoundDeepIndexRouteImport.update({ +const NotFoundParentBoundaryViaBeforeLoadRoute = + NotFoundParentBoundaryViaBeforeLoadRouteImport.update({ + id: '/via-beforeLoad', + path: '/via-beforeLoad', + getParentRoute: () => NotFoundParentBoundaryRouteRoute, + } as any) +const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ + id: '/posts_/$postId/deep', + path: '/posts/$postId/deep', + getParentRoute: () => rootRouteImport, +} as any) +const RedirectTargetIndexRoute = RedirectTargetIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => NotFoundDeepRouteRoute, -} as any) -const SpecialCharsMalformedSearchRoute = - SpecialCharsMalformedSearchRouteImport.update({ - id: '/search', - path: '/search', - getParentRoute: () => SpecialCharsMalformedRouteRoute, - } as any) -const SpecialCharsMalformedParamRoute = - SpecialCharsMalformedParamRouteImport.update({ - id: '/$param', - path: '/$param', - getParentRoute: () => SpecialCharsMalformedRouteRoute, - } as any) -const RedirectTargetViaLoaderRoute = RedirectTargetViaLoaderRouteImport.update({ - id: '/via-loader', - path: '/via-loader', getParentRoute: () => RedirectTargetRoute, } as any) const RedirectTargetViaBeforeLoadRoute = @@ -370,36 +384,32 @@ const RedirectTargetViaBeforeLoadRoute = path: '/via-beforeLoad', getParentRoute: () => RedirectTargetRoute, } as any) -const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ - id: '/posts_/$postId/deep', - path: '/posts/$postId/deep', - getParentRoute: () => rootRouteImport, +const RedirectTargetViaLoaderRoute = RedirectTargetViaLoaderRouteImport.update({ + id: '/via-loader', + path: '/via-loader', + getParentRoute: () => RedirectTargetRoute, } as any) -const NotFoundParentBoundaryViaBeforeLoadRoute = - NotFoundParentBoundaryViaBeforeLoadRouteImport.update({ - id: '/via-beforeLoad', - path: '/via-beforeLoad', - getParentRoute: () => NotFoundParentBoundaryRouteRoute, +const SpecialCharsMalformedParamRoute = + SpecialCharsMalformedParamRouteImport.update({ + id: '/$param', + path: '/$param', + getParentRoute: () => SpecialCharsMalformedRouteRoute, } as any) -const ApiUsersIdRoute = ApiUsersIdRouteImport.update({ - id: '/$id', - path: '/$id', - getParentRoute: () => ApiUsersRoute, -} as any) -const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ - id: '/layout-b', - path: '/layout-b', - getParentRoute: () => LayoutLayout2Route, -} as any) -const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ - id: '/layout-a', - path: '/layout-a', - getParentRoute: () => LayoutLayout2Route, +const SpecialCharsMalformedSearchRoute = + SpecialCharsMalformedSearchRouteImport.update({ + id: '/search', + path: '/search', + getParentRoute: () => SpecialCharsMalformedRouteRoute, + } as any) +const FooBarQuxHereRoute = FooBarQuxHereRouteImport.update({ + id: '/foo/$bar/$qux/_here', + path: '/foo/$bar/$qux', + getParentRoute: () => rootRouteImport, } as any) -const NotFoundDeepBRouteRoute = NotFoundDeepBRouteRouteImport.update({ - id: '/b', - path: '/b', - getParentRoute: () => NotFoundDeepRouteRoute, +const NotFoundDeepBCRouteRoute = NotFoundDeepBCRouteRouteImport.update({ + id: '/c', + path: '/c', + getParentRoute: () => NotFoundDeepBRouteRoute, } as any) const RedirectTargetServerFnIndexRoute = RedirectTargetServerFnIndexRouteImport.update({ @@ -407,10 +417,10 @@ const RedirectTargetServerFnIndexRoute = path: '/serverFn/', getParentRoute: () => RedirectTargetRoute, } as any) -const RedirectTargetServerFnViaUseServerFnRoute = - RedirectTargetServerFnViaUseServerFnRouteImport.update({ - id: '/serverFn/via-useServerFn', - path: '/serverFn/via-useServerFn', +const RedirectTargetServerFnViaBeforeLoadRoute = + RedirectTargetServerFnViaBeforeLoadRouteImport.update({ + id: '/serverFn/via-beforeLoad', + path: '/serverFn/via-beforeLoad', getParentRoute: () => RedirectTargetRoute, } as any) const RedirectTargetServerFnViaLoaderRoute = @@ -419,22 +429,12 @@ const RedirectTargetServerFnViaLoaderRoute = path: '/serverFn/via-loader', getParentRoute: () => RedirectTargetRoute, } as any) -const RedirectTargetServerFnViaBeforeLoadRoute = - RedirectTargetServerFnViaBeforeLoadRouteImport.update({ - id: '/serverFn/via-beforeLoad', - path: '/serverFn/via-beforeLoad', +const RedirectTargetServerFnViaUseServerFnRoute = + RedirectTargetServerFnViaUseServerFnRouteImport.update({ + id: '/serverFn/via-useServerFn', + path: '/serverFn/via-useServerFn', getParentRoute: () => RedirectTargetRoute, } as any) -const FooBarQuxHereRoute = FooBarQuxHereRouteImport.update({ - id: '/foo/$bar/$qux/_here', - path: '/foo/$bar/$qux', - getParentRoute: () => rootRouteImport, -} as any) -const NotFoundDeepBCRouteRoute = NotFoundDeepBCRouteRouteImport.update({ - id: '/c', - path: '/c', - getParentRoute: () => NotFoundDeepBRouteRoute, -} as any) const FooBarQuxHereIndexRoute = FooBarQuxHereIndexRouteImport.update({ id: '/', path: '/', @@ -886,53 +886,60 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/users': { - id: '/users' - path: '/users' - fullPath: '/users' - preLoaderRoute: typeof UsersRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/type-only-reexport': { - id: '/type-only-reexport' - path: '/type-only-reexport' - fullPath: '/type-only-reexport' - preLoaderRoute: typeof TypeOnlyReexportRouteImport + '/_layout': { + id: '/_layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } - '/stream': { - id: '/stream' - path: '/stream' - fullPath: '/stream' - preLoaderRoute: typeof StreamRouteImport + '/async-scripts': { + id: '/async-scripts' + path: '/async-scripts' + fullPath: '/async-scripts' + preLoaderRoute: typeof AsyncScriptsRouteImport parentRoute: typeof rootRouteImport } - '/scripts': { - id: '/scripts' - path: '/scripts' - fullPath: '/scripts' - preLoaderRoute: typeof ScriptsRouteImport + '/client-only': { + id: '/client-only' + path: '/client-only' + fullPath: '/client-only' + preLoaderRoute: typeof ClientOnlyRouteImport parentRoute: typeof rootRouteImport } - '/raw-stream': { - id: '/raw-stream' - path: '/raw-stream' - fullPath: '/raw-stream' - preLoaderRoute: typeof RawStreamRouteImport + '/deferred': { + id: '/deferred' + path: '/deferred' + fullPath: '/deferred' + preLoaderRoute: typeof DeferredRouteImport parentRoute: typeof rootRouteImport } - '/primitive-beforeload-error': { - id: '/primitive-beforeload-error' - path: '/primitive-beforeload-error' - fullPath: '/primitive-beforeload-error' - preLoaderRoute: typeof PrimitiveBeforeloadErrorRouteImport + '/inline-scripts': { + id: '/inline-scripts' + path: '/inline-scripts' + fullPath: '/inline-scripts' + preLoaderRoute: typeof InlineScriptsRouteImport parentRoute: typeof rootRouteImport } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/links': { + id: '/links' + path: '/links' + fullPath: '/links' + preLoaderRoute: typeof LinksRouteImport + parentRoute: typeof rootRouteImport + } + '/not-found': { + id: '/not-found' + path: '/not-found' + fullPath: '/not-found' + preLoaderRoute: typeof NotFoundRouteRouteImport parentRoute: typeof rootRouteImport } '/plain-ts-type-assertion': { @@ -942,46 +949,39 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PlainTsTypeAssertionRouteImport parentRoute: typeof rootRouteImport } - '/links': { - id: '/links' - path: '/links' - fullPath: '/links' - preLoaderRoute: typeof LinksRouteImport - parentRoute: typeof rootRouteImport - } - '/inline-scripts': { - id: '/inline-scripts' - path: '/inline-scripts' - fullPath: '/inline-scripts' - preLoaderRoute: typeof InlineScriptsRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/deferred': { - id: '/deferred' - path: '/deferred' - fullPath: '/deferred' - preLoaderRoute: typeof DeferredRouteImport + '/primitive-beforeload-error': { + id: '/primitive-beforeload-error' + path: '/primitive-beforeload-error' + fullPath: '/primitive-beforeload-error' + preLoaderRoute: typeof PrimitiveBeforeloadErrorRouteImport parentRoute: typeof rootRouteImport } - '/client-only': { - id: '/client-only' - path: '/client-only' - fullPath: '/client-only' - preLoaderRoute: typeof ClientOnlyRouteImport + '/raw-stream': { + id: '/raw-stream' + path: '/raw-stream' + fullPath: '/raw-stream' + preLoaderRoute: typeof RawStreamRouteImport parentRoute: typeof rootRouteImport } - '/async-scripts': { - id: '/async-scripts' - path: '/async-scripts' - fullPath: '/async-scripts' - preLoaderRoute: typeof AsyncScriptsRouteImport + '/scripts': { + id: '/scripts' + path: '/scripts' + fullPath: '/scripts' + preLoaderRoute: typeof ScriptsRouteImport parentRoute: typeof rootRouteImport } - '/_layout': { - id: '/_layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutRouteImport + '/search-params': { + id: '/search-params' + path: '/search-params' + fullPath: '/search-params' + preLoaderRoute: typeof SearchParamsRouteRouteImport parentRoute: typeof rootRouteImport } '/specialChars': { @@ -991,61 +991,54 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof SpecialCharsRouteRouteImport parentRoute: typeof rootRouteImport } - '/search-params': { - id: '/search-params' - path: '/search-params' - fullPath: '/search-params' - preLoaderRoute: typeof SearchParamsRouteRouteImport + '/stream': { + id: '/stream' + path: '/stream' + fullPath: '/stream' + preLoaderRoute: typeof StreamRouteImport parentRoute: typeof rootRouteImport } - '/not-found': { - id: '/not-found' - path: '/not-found' - fullPath: '/not-found' - preLoaderRoute: typeof NotFoundRouteRouteImport + '/type-only-reexport': { + id: '/type-only-reexport' + path: '/type-only-reexport' + fullPath: '/type-only-reexport' + preLoaderRoute: typeof TypeOnlyReexportRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/users': { + id: '/users' + path: '/users' + fullPath: '/users' + preLoaderRoute: typeof UsersRouteImport parentRoute: typeof rootRouteImport } - '/users/': { - id: '/users/' - path: '/' - fullPath: '/users/' - preLoaderRoute: typeof UsersIndexRouteImport - parentRoute: typeof UsersRoute - } - '/search-params/': { - id: '/search-params/' - path: '/' - fullPath: '/search-params/' - preLoaderRoute: typeof SearchParamsIndexRouteImport - parentRoute: typeof SearchParamsRouteRoute + '/_layout/_layout-2': { + id: '/_layout/_layout-2' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutLayout2RouteImport + parentRoute: typeof LayoutRoute } - '/redirect/': { - id: '/redirect/' - path: '/redirect' - fullPath: '/redirect/' - preLoaderRoute: typeof RedirectIndexRouteImport + '/api/users': { + id: '/api/users' + path: '/api/users' + fullPath: '/api/users' + preLoaderRoute: typeof ApiUsersRouteImport parentRoute: typeof rootRouteImport } - '/raw-stream/': { - id: '/raw-stream/' - path: '/' - fullPath: '/raw-stream/' - preLoaderRoute: typeof RawStreamIndexRouteImport - parentRoute: typeof RawStreamRoute + '/multi-cookie-redirect/': { + id: '/multi-cookie-redirect/' + path: '/multi-cookie-redirect' + fullPath: '/multi-cookie-redirect/' + preLoaderRoute: typeof MultiCookieRedirectIndexRouteImport + parentRoute: typeof rootRouteImport } - '/posts/': { - id: '/posts/' - path: '/' - fullPath: '/posts/' - preLoaderRoute: typeof PostsIndexRouteImport - parentRoute: typeof PostsRoute + '/multi-cookie-redirect/target': { + id: '/multi-cookie-redirect/target' + path: '/multi-cookie-redirect/target' + fullPath: '/multi-cookie-redirect/target' + preLoaderRoute: typeof MultiCookieRedirectTargetRouteImport + parentRoute: typeof rootRouteImport } '/not-found/': { id: '/not-found/' @@ -1054,88 +1047,74 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof NotFoundIndexRouteImport parentRoute: typeof NotFoundRouteRoute } - '/multi-cookie-redirect/': { - id: '/multi-cookie-redirect/' - path: '/multi-cookie-redirect' - fullPath: '/multi-cookie-redirect/' - preLoaderRoute: typeof MultiCookieRedirectIndexRouteImport - parentRoute: typeof rootRouteImport - } - '/users/$userId': { - id: '/users/$userId' - path: '/$userId' - fullPath: '/users/$userId' - preLoaderRoute: typeof UsersUserIdRouteImport - parentRoute: typeof UsersRoute - } - '/specialChars/대한민국': { - id: '/specialChars/대한민국' - path: '/대한민국' - fullPath: '/specialChars/대한민국' - preLoaderRoute: typeof SpecialCharsChar45824Char54620Char48124Char44397RouteImport - parentRoute: typeof SpecialCharsRouteRoute + '/not-found/deep': { + id: '/not-found/deep' + path: '/deep' + fullPath: '/not-found/deep' + preLoaderRoute: typeof NotFoundDeepRouteRouteImport + parentRoute: typeof NotFoundRouteRoute } - '/specialChars/search': { - id: '/specialChars/search' - path: '/search' - fullPath: '/specialChars/search' - preLoaderRoute: typeof SpecialCharsSearchRouteImport - parentRoute: typeof SpecialCharsRouteRoute + '/not-found/parent-boundary': { + id: '/not-found/parent-boundary' + path: '/parent-boundary' + fullPath: '/not-found/parent-boundary' + preLoaderRoute: typeof NotFoundParentBoundaryRouteRouteImport + parentRoute: typeof NotFoundRouteRoute } - '/specialChars/hash': { - id: '/specialChars/hash' - path: '/hash' - fullPath: '/specialChars/hash' - preLoaderRoute: typeof SpecialCharsHashRouteImport - parentRoute: typeof SpecialCharsRouteRoute + '/not-found/via-beforeLoad': { + id: '/not-found/via-beforeLoad' + path: '/via-beforeLoad' + fullPath: '/not-found/via-beforeLoad' + preLoaderRoute: typeof NotFoundViaBeforeLoadRouteImport + parentRoute: typeof NotFoundRouteRoute } - '/specialChars/$param': { - id: '/specialChars/$param' - path: '/$param' - fullPath: '/specialChars/$param' - preLoaderRoute: typeof SpecialCharsParamRouteImport - parentRoute: typeof SpecialCharsRouteRoute + '/not-found/via-beforeLoad-target-root': { + id: '/not-found/via-beforeLoad-target-root' + path: '/via-beforeLoad-target-root' + fullPath: '/not-found/via-beforeLoad-target-root' + preLoaderRoute: typeof NotFoundViaBeforeLoadTargetRootRouteImport + parentRoute: typeof NotFoundRouteRoute } - '/search-params/loader-throws-redirect': { - id: '/search-params/loader-throws-redirect' - path: '/loader-throws-redirect' - fullPath: '/search-params/loader-throws-redirect' - preLoaderRoute: typeof SearchParamsLoaderThrowsRedirectRouteImport - parentRoute: typeof SearchParamsRouteRoute + '/not-found/via-loader': { + id: '/not-found/via-loader' + path: '/via-loader' + fullPath: '/not-found/via-loader' + preLoaderRoute: typeof NotFoundViaLoaderRouteImport + parentRoute: typeof NotFoundRouteRoute } - '/search-params/default': { - id: '/search-params/default' - path: '/default' - fullPath: '/search-params/default' - preLoaderRoute: typeof SearchParamsDefaultRouteImport - parentRoute: typeof SearchParamsRouteRoute + '/posts/': { + id: '/posts/' + path: '/' + fullPath: '/posts/' + preLoaderRoute: typeof PostsIndexRouteImport + parentRoute: typeof PostsRoute } - '/redirect/$target': { - id: '/redirect/$target' - path: '/redirect/$target' - fullPath: '/redirect/$target' - preLoaderRoute: typeof RedirectTargetRouteImport - parentRoute: typeof rootRouteImport + '/posts/$postId': { + id: '/posts/$postId' + path: '/$postId' + fullPath: '/posts/$postId' + preLoaderRoute: typeof PostsPostIdRouteImport + parentRoute: typeof PostsRoute } - '/raw-stream/ssr-text-hint': { - id: '/raw-stream/ssr-text-hint' - path: '/ssr-text-hint' - fullPath: '/raw-stream/ssr-text-hint' - preLoaderRoute: typeof RawStreamSsrTextHintRouteImport + '/raw-stream/': { + id: '/raw-stream/' + path: '/' + fullPath: '/raw-stream/' + preLoaderRoute: typeof RawStreamIndexRouteImport parentRoute: typeof RawStreamRoute } - '/raw-stream/ssr-single': { - id: '/raw-stream/ssr-single' - path: '/ssr-single' - fullPath: '/raw-stream/ssr-single' - preLoaderRoute: typeof RawStreamSsrSingleRouteImport + '/raw-stream/client-call': { + id: '/raw-stream/client-call' + path: '/client-call' + fullPath: '/raw-stream/client-call' + preLoaderRoute: typeof RawStreamClientCallRouteImport parentRoute: typeof RawStreamRoute } - '/raw-stream/ssr-multiple': { - id: '/raw-stream/ssr-multiple' - path: '/ssr-multiple' - fullPath: '/raw-stream/ssr-multiple' - preLoaderRoute: typeof RawStreamSsrMultipleRouteImport + '/raw-stream/ssr-binary-hint': { + id: '/raw-stream/ssr-binary-hint' + path: '/ssr-binary-hint' + fullPath: '/raw-stream/ssr-binary-hint' + preLoaderRoute: typeof RawStreamSsrBinaryHintRouteImport parentRoute: typeof RawStreamRoute } '/raw-stream/ssr-mixed': { @@ -1145,68 +1124,75 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof RawStreamSsrMixedRouteImport parentRoute: typeof RawStreamRoute } - '/raw-stream/ssr-binary-hint': { - id: '/raw-stream/ssr-binary-hint' - path: '/ssr-binary-hint' - fullPath: '/raw-stream/ssr-binary-hint' - preLoaderRoute: typeof RawStreamSsrBinaryHintRouteImport + '/raw-stream/ssr-multiple': { + id: '/raw-stream/ssr-multiple' + path: '/ssr-multiple' + fullPath: '/raw-stream/ssr-multiple' + preLoaderRoute: typeof RawStreamSsrMultipleRouteImport parentRoute: typeof RawStreamRoute } - '/raw-stream/client-call': { - id: '/raw-stream/client-call' - path: '/client-call' - fullPath: '/raw-stream/client-call' - preLoaderRoute: typeof RawStreamClientCallRouteImport + '/raw-stream/ssr-single': { + id: '/raw-stream/ssr-single' + path: '/ssr-single' + fullPath: '/raw-stream/ssr-single' + preLoaderRoute: typeof RawStreamSsrSingleRouteImport parentRoute: typeof RawStreamRoute } - '/posts/$postId': { - id: '/posts/$postId' - path: '/$postId' - fullPath: '/posts/$postId' - preLoaderRoute: typeof PostsPostIdRouteImport - parentRoute: typeof PostsRoute + '/raw-stream/ssr-text-hint': { + id: '/raw-stream/ssr-text-hint' + path: '/ssr-text-hint' + fullPath: '/raw-stream/ssr-text-hint' + preLoaderRoute: typeof RawStreamSsrTextHintRouteImport + parentRoute: typeof RawStreamRoute } - '/not-found/via-loader': { - id: '/not-found/via-loader' - path: '/via-loader' - fullPath: '/not-found/via-loader' - preLoaderRoute: typeof NotFoundViaLoaderRouteImport - parentRoute: typeof NotFoundRouteRoute + '/redirect/': { + id: '/redirect/' + path: '/redirect' + fullPath: '/redirect/' + preLoaderRoute: typeof RedirectIndexRouteImport + parentRoute: typeof rootRouteImport } - '/not-found/via-beforeLoad-target-root': { - id: '/not-found/via-beforeLoad-target-root' - path: '/via-beforeLoad-target-root' - fullPath: '/not-found/via-beforeLoad-target-root' - preLoaderRoute: typeof NotFoundViaBeforeLoadTargetRootRouteImport - parentRoute: typeof NotFoundRouteRoute + '/redirect/$target': { + id: '/redirect/$target' + path: '/redirect/$target' + fullPath: '/redirect/$target' + preLoaderRoute: typeof RedirectTargetRouteImport + parentRoute: typeof rootRouteImport } - '/not-found/via-beforeLoad': { - id: '/not-found/via-beforeLoad' - path: '/via-beforeLoad' - fullPath: '/not-found/via-beforeLoad' - preLoaderRoute: typeof NotFoundViaBeforeLoadRouteImport - parentRoute: typeof NotFoundRouteRoute + '/search-params/': { + id: '/search-params/' + path: '/' + fullPath: '/search-params/' + preLoaderRoute: typeof SearchParamsIndexRouteImport + parentRoute: typeof SearchParamsRouteRoute + } + '/search-params/default': { + id: '/search-params/default' + path: '/default' + fullPath: '/search-params/default' + preLoaderRoute: typeof SearchParamsDefaultRouteImport + parentRoute: typeof SearchParamsRouteRoute } - '/multi-cookie-redirect/target': { - id: '/multi-cookie-redirect/target' - path: '/multi-cookie-redirect/target' - fullPath: '/multi-cookie-redirect/target' - preLoaderRoute: typeof MultiCookieRedirectTargetRouteImport - parentRoute: typeof rootRouteImport + '/search-params/loader-throws-redirect': { + id: '/search-params/loader-throws-redirect' + path: '/loader-throws-redirect' + fullPath: '/search-params/loader-throws-redirect' + preLoaderRoute: typeof SearchParamsLoaderThrowsRedirectRouteImport + parentRoute: typeof SearchParamsRouteRoute } - '/api/users': { - id: '/api/users' - path: '/api/users' - fullPath: '/api/users' - preLoaderRoute: typeof ApiUsersRouteImport - parentRoute: typeof rootRouteImport + '/specialChars/$param': { + id: '/specialChars/$param' + path: '/$param' + fullPath: '/specialChars/$param' + preLoaderRoute: typeof SpecialCharsParamRouteImport + parentRoute: typeof SpecialCharsRouteRoute } - '/_layout/_layout-2': { - id: '/_layout/_layout-2' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutLayout2RouteImport - parentRoute: typeof LayoutRoute + '/specialChars/hash': { + id: '/specialChars/hash' + path: '/hash' + fullPath: '/specialChars/hash' + preLoaderRoute: typeof SpecialCharsHashRouteImport + parentRoute: typeof SpecialCharsRouteRoute } '/specialChars/malformed': { id: '/specialChars/malformed' @@ -1215,33 +1201,54 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof SpecialCharsMalformedRouteRouteImport parentRoute: typeof SpecialCharsRouteRoute } - '/not-found/parent-boundary': { - id: '/not-found/parent-boundary' - path: '/parent-boundary' - fullPath: '/not-found/parent-boundary' - preLoaderRoute: typeof NotFoundParentBoundaryRouteRouteImport - parentRoute: typeof NotFoundRouteRoute + '/specialChars/search': { + id: '/specialChars/search' + path: '/search' + fullPath: '/specialChars/search' + preLoaderRoute: typeof SpecialCharsSearchRouteImport + parentRoute: typeof SpecialCharsRouteRoute } - '/not-found/deep': { - id: '/not-found/deep' - path: '/deep' - fullPath: '/not-found/deep' - preLoaderRoute: typeof NotFoundDeepRouteRouteImport - parentRoute: typeof NotFoundRouteRoute + '/specialChars/대한민국': { + id: '/specialChars/대한민국' + path: '/대한민국' + fullPath: '/specialChars/대한민국' + preLoaderRoute: typeof SpecialCharsChar45824Char54620Char48124Char44397RouteImport + parentRoute: typeof SpecialCharsRouteRoute } - '/redirect/$target/': { - id: '/redirect/$target/' + '/users/': { + id: '/users/' path: '/' - fullPath: '/redirect/$target/' - preLoaderRoute: typeof RedirectTargetIndexRouteImport - parentRoute: typeof RedirectTargetRoute + fullPath: '/users/' + preLoaderRoute: typeof UsersIndexRouteImport + parentRoute: typeof UsersRoute } - '/not-found/parent-boundary/': { - id: '/not-found/parent-boundary/' - path: '/' - fullPath: '/not-found/parent-boundary/' - preLoaderRoute: typeof NotFoundParentBoundaryIndexRouteImport - parentRoute: typeof NotFoundParentBoundaryRouteRoute + '/users/$userId': { + id: '/users/$userId' + path: '/$userId' + fullPath: '/users/$userId' + preLoaderRoute: typeof UsersUserIdRouteImport + parentRoute: typeof UsersRoute + } + '/_layout/_layout-2/layout-a': { + id: '/_layout/_layout-2/layout-a' + path: '/layout-a' + fullPath: '/layout-a' + preLoaderRoute: typeof LayoutLayout2LayoutARouteImport + parentRoute: typeof LayoutLayout2Route + } + '/_layout/_layout-2/layout-b': { + id: '/_layout/_layout-2/layout-b' + path: '/layout-b' + fullPath: '/layout-b' + preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport + parentRoute: typeof LayoutLayout2Route + } + '/api/users/$id': { + id: '/api/users/$id' + path: '/$id' + fullPath: '/api/users/$id' + preLoaderRoute: typeof ApiUsersIdRouteImport + parentRoute: typeof ApiUsersRoute } '/not-found/deep/': { id: '/not-found/deep/' @@ -1250,33 +1257,26 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof NotFoundDeepIndexRouteImport parentRoute: typeof NotFoundDeepRouteRoute } - '/specialChars/malformed/search': { - id: '/specialChars/malformed/search' - path: '/search' - fullPath: '/specialChars/malformed/search' - preLoaderRoute: typeof SpecialCharsMalformedSearchRouteImport - parentRoute: typeof SpecialCharsMalformedRouteRoute - } - '/specialChars/malformed/$param': { - id: '/specialChars/malformed/$param' - path: '/$param' - fullPath: '/specialChars/malformed/$param' - preLoaderRoute: typeof SpecialCharsMalformedParamRouteImport - parentRoute: typeof SpecialCharsMalformedRouteRoute + '/not-found/deep/b': { + id: '/not-found/deep/b' + path: '/b' + fullPath: '/not-found/deep/b' + preLoaderRoute: typeof NotFoundDeepBRouteRouteImport + parentRoute: typeof NotFoundDeepRouteRoute } - '/redirect/$target/via-loader': { - id: '/redirect/$target/via-loader' - path: '/via-loader' - fullPath: '/redirect/$target/via-loader' - preLoaderRoute: typeof RedirectTargetViaLoaderRouteImport - parentRoute: typeof RedirectTargetRoute + '/not-found/parent-boundary/': { + id: '/not-found/parent-boundary/' + path: '/' + fullPath: '/not-found/parent-boundary/' + preLoaderRoute: typeof NotFoundParentBoundaryIndexRouteImport + parentRoute: typeof NotFoundParentBoundaryRouteRoute } - '/redirect/$target/via-beforeLoad': { - id: '/redirect/$target/via-beforeLoad' + '/not-found/parent-boundary/via-beforeLoad': { + id: '/not-found/parent-boundary/via-beforeLoad' path: '/via-beforeLoad' - fullPath: '/redirect/$target/via-beforeLoad' - preLoaderRoute: typeof RedirectTargetViaBeforeLoadRouteImport - parentRoute: typeof RedirectTargetRoute + fullPath: '/not-found/parent-boundary/via-beforeLoad' + preLoaderRoute: typeof NotFoundParentBoundaryViaBeforeLoadRouteImport + parentRoute: typeof NotFoundParentBoundaryRouteRoute } '/posts_/$postId/deep': { id: '/posts_/$postId/deep' @@ -1285,40 +1285,54 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsPostIdDeepRouteImport parentRoute: typeof rootRouteImport } - '/not-found/parent-boundary/via-beforeLoad': { - id: '/not-found/parent-boundary/via-beforeLoad' + '/redirect/$target/': { + id: '/redirect/$target/' + path: '/' + fullPath: '/redirect/$target/' + preLoaderRoute: typeof RedirectTargetIndexRouteImport + parentRoute: typeof RedirectTargetRoute + } + '/redirect/$target/via-beforeLoad': { + id: '/redirect/$target/via-beforeLoad' path: '/via-beforeLoad' - fullPath: '/not-found/parent-boundary/via-beforeLoad' - preLoaderRoute: typeof NotFoundParentBoundaryViaBeforeLoadRouteImport - parentRoute: typeof NotFoundParentBoundaryRouteRoute + fullPath: '/redirect/$target/via-beforeLoad' + preLoaderRoute: typeof RedirectTargetViaBeforeLoadRouteImport + parentRoute: typeof RedirectTargetRoute } - '/api/users/$id': { - id: '/api/users/$id' - path: '/$id' - fullPath: '/api/users/$id' - preLoaderRoute: typeof ApiUsersIdRouteImport - parentRoute: typeof ApiUsersRoute + '/redirect/$target/via-loader': { + id: '/redirect/$target/via-loader' + path: '/via-loader' + fullPath: '/redirect/$target/via-loader' + preLoaderRoute: typeof RedirectTargetViaLoaderRouteImport + parentRoute: typeof RedirectTargetRoute } - '/_layout/_layout-2/layout-b': { - id: '/_layout/_layout-2/layout-b' - path: '/layout-b' - fullPath: '/layout-b' - preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport - parentRoute: typeof LayoutLayout2Route + '/specialChars/malformed/$param': { + id: '/specialChars/malformed/$param' + path: '/$param' + fullPath: '/specialChars/malformed/$param' + preLoaderRoute: typeof SpecialCharsMalformedParamRouteImport + parentRoute: typeof SpecialCharsMalformedRouteRoute } - '/_layout/_layout-2/layout-a': { - id: '/_layout/_layout-2/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: typeof LayoutLayout2LayoutARouteImport - parentRoute: typeof LayoutLayout2Route + '/specialChars/malformed/search': { + id: '/specialChars/malformed/search' + path: '/search' + fullPath: '/specialChars/malformed/search' + preLoaderRoute: typeof SpecialCharsMalformedSearchRouteImport + parentRoute: typeof SpecialCharsMalformedRouteRoute } - '/not-found/deep/b': { - id: '/not-found/deep/b' - path: '/b' - fullPath: '/not-found/deep/b' - preLoaderRoute: typeof NotFoundDeepBRouteRouteImport - parentRoute: typeof NotFoundDeepRouteRoute + '/foo/$bar/$qux/_here': { + id: '/foo/$bar/$qux/_here' + path: '/foo/$bar/$qux' + fullPath: '/foo/$bar/$qux' + preLoaderRoute: typeof FooBarQuxHereRouteImport + parentRoute: typeof rootRouteImport + } + '/not-found/deep/b/c': { + id: '/not-found/deep/b/c' + path: '/c' + fullPath: '/not-found/deep/b/c' + preLoaderRoute: typeof NotFoundDeepBCRouteRouteImport + parentRoute: typeof NotFoundDeepBRouteRoute } '/redirect/$target/serverFn/': { id: '/redirect/$target/serverFn/' @@ -1327,11 +1341,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof RedirectTargetServerFnIndexRouteImport parentRoute: typeof RedirectTargetRoute } - '/redirect/$target/serverFn/via-useServerFn': { - id: '/redirect/$target/serverFn/via-useServerFn' - path: '/serverFn/via-useServerFn' - fullPath: '/redirect/$target/serverFn/via-useServerFn' - preLoaderRoute: typeof RedirectTargetServerFnViaUseServerFnRouteImport + '/redirect/$target/serverFn/via-beforeLoad': { + id: '/redirect/$target/serverFn/via-beforeLoad' + path: '/serverFn/via-beforeLoad' + fullPath: '/redirect/$target/serverFn/via-beforeLoad' + preLoaderRoute: typeof RedirectTargetServerFnViaBeforeLoadRouteImport parentRoute: typeof RedirectTargetRoute } '/redirect/$target/serverFn/via-loader': { @@ -1341,27 +1355,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof RedirectTargetServerFnViaLoaderRouteImport parentRoute: typeof RedirectTargetRoute } - '/redirect/$target/serverFn/via-beforeLoad': { - id: '/redirect/$target/serverFn/via-beforeLoad' - path: '/serverFn/via-beforeLoad' - fullPath: '/redirect/$target/serverFn/via-beforeLoad' - preLoaderRoute: typeof RedirectTargetServerFnViaBeforeLoadRouteImport + '/redirect/$target/serverFn/via-useServerFn': { + id: '/redirect/$target/serverFn/via-useServerFn' + path: '/serverFn/via-useServerFn' + fullPath: '/redirect/$target/serverFn/via-useServerFn' + preLoaderRoute: typeof RedirectTargetServerFnViaUseServerFnRouteImport parentRoute: typeof RedirectTargetRoute } - '/foo/$bar/$qux/_here': { - id: '/foo/$bar/$qux/_here' - path: '/foo/$bar/$qux' - fullPath: '/foo/$bar/$qux' - preLoaderRoute: typeof FooBarQuxHereRouteImport - parentRoute: typeof rootRouteImport - } - '/not-found/deep/b/c': { - id: '/not-found/deep/b/c' - path: '/c' - fullPath: '/not-found/deep/b/c' - preLoaderRoute: typeof NotFoundDeepBCRouteRouteImport - parentRoute: typeof NotFoundDeepBRouteRoute - } '/foo/$bar/$qux/_here/': { id: '/foo/$bar/$qux/_here/' path: '/' diff --git a/e2e/react-start/clerk-basic/src/routeTree.gen.ts b/e2e/react-start/clerk-basic/src/routeTree.gen.ts index 5b9f75e27a..d8111b637a 100644 --- a/e2e/react-start/clerk-basic/src/routeTree.gen.ts +++ b/e2e/react-start/clerk-basic/src/routeTree.gen.ts @@ -9,22 +9,22 @@ // 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 AuthedRouteImport } from './routes/_authed' import { Route as IndexRouteImport } from './routes/index' +import { Route as AuthedRouteImport } from './routes/_authed' import { Route as AuthedPostsRouteImport } from './routes/_authed/posts' import { Route as AuthedPostsIndexRouteImport } from './routes/_authed/posts.index' -import { Route as AuthedProfileSplatRouteImport } from './routes/_authed/profile.$' import { Route as AuthedPostsPostIdRouteImport } from './routes/_authed/posts.$postId' +import { Route as AuthedProfileSplatRouteImport } from './routes/_authed/profile.$' -const AuthedRoute = AuthedRouteImport.update({ - id: '/_authed', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AuthedRoute = AuthedRouteImport.update({ + id: '/_authed', + getParentRoute: () => rootRouteImport, +} as any) const AuthedPostsRoute = AuthedPostsRouteImport.update({ id: '/posts', path: '/posts', @@ -35,16 +35,16 @@ const AuthedPostsIndexRoute = AuthedPostsIndexRouteImport.update({ path: '/', getParentRoute: () => AuthedPostsRoute, } as any) -const AuthedProfileSplatRoute = AuthedProfileSplatRouteImport.update({ - id: '/profile/$', - path: '/profile/$', - getParentRoute: () => AuthedRoute, -} as any) const AuthedPostsPostIdRoute = AuthedPostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => AuthedPostsRoute, } as any) +const AuthedProfileSplatRoute = AuthedProfileSplatRouteImport.update({ + id: '/profile/$', + path: '/profile/$', + getParentRoute: () => AuthedRoute, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -90,13 +90,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/_authed': { - id: '/_authed' - path: '' - fullPath: '/' - preLoaderRoute: typeof AuthedRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -104,6 +97,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/_authed': { + id: '/_authed' + path: '' + fullPath: '/' + preLoaderRoute: typeof AuthedRouteImport + parentRoute: typeof rootRouteImport + } '/_authed/posts': { id: '/_authed/posts' path: '/posts' @@ -118,13 +118,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AuthedPostsIndexRouteImport parentRoute: typeof AuthedPostsRoute } - '/_authed/profile/$': { - id: '/_authed/profile/$' - path: '/profile/$' - fullPath: '/profile/$' - preLoaderRoute: typeof AuthedProfileSplatRouteImport - parentRoute: typeof AuthedRoute - } '/_authed/posts/$postId': { id: '/_authed/posts/$postId' path: '/$postId' @@ -132,6 +125,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AuthedPostsPostIdRouteImport parentRoute: typeof AuthedPostsRoute } + '/_authed/profile/$': { + id: '/_authed/profile/$' + path: '/profile/$' + fullPath: '/profile/$' + preLoaderRoute: typeof AuthedProfileSplatRouteImport + parentRoute: typeof AuthedRoute + } } } diff --git a/e2e/react-start/css-inline/src/routeTree.gen.ts b/e2e/react-start/css-inline/src/routeTree.gen.ts index f663ab1b4e..1b5e5886b1 100644 --- a/e2e/react-start/css-inline/src/routeTree.gen.ts +++ b/e2e/react-start/css-inline/src/routeTree.gen.ts @@ -9,22 +9,22 @@ // 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 AppRouteRouteImport } from './routes/app/route' import { Route as IndexRouteImport } from './routes/index' +import { Route as AppRouteRouteImport } from './routes/app/route' import { Route as AppDashboardRouteRouteImport } from './routes/app/dashboard/route' import { Route as AppDashboardIndexRouteImport } from './routes/app/dashboard/index' import { Route as AppDashboardDetailsRouteImport } from './routes/app/dashboard/details' -const AppRouteRoute = AppRouteRouteImport.update({ - id: '/app', - path: '/app', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AppRouteRoute = AppRouteRouteImport.update({ + id: '/app', + path: '/app', + getParentRoute: () => rootRouteImport, +} as any) const AppDashboardRouteRoute = AppDashboardRouteRouteImport.update({ id: '/dashboard', path: '/dashboard', @@ -88,13 +88,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/app': { - id: '/app' - path: '/app' - fullPath: '/app' - preLoaderRoute: typeof AppRouteRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -102,6 +95,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/app': { + id: '/app' + path: '/app' + fullPath: '/app' + preLoaderRoute: typeof AppRouteRouteImport + parentRoute: typeof rootRouteImport + } '/app/dashboard': { id: '/app/dashboard' path: '/dashboard' diff --git a/e2e/react-start/css-modules/src/routeTree.gen.ts b/e2e/react-start/css-modules/src/routeTree.gen.ts index d19ae80e5a..822118990d 100644 --- a/e2e/react-start/css-modules/src/routeTree.gen.ts +++ b/e2e/react-start/css-modules/src/routeTree.gen.ts @@ -9,19 +9,14 @@ // 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 SassMixinRouteImport } from './routes/sass-mixin' -import { Route as QuotesRouteImport } from './routes/quotes' -import { Route as ModulesRouteImport } from './routes/modules' import { Route as IndexRouteImport } from './routes/index' +import { Route as ModulesRouteImport } from './routes/modules' +import { Route as QuotesRouteImport } from './routes/quotes' +import { Route as SassMixinRouteImport } from './routes/sass-mixin' -const SassMixinRoute = SassMixinRouteImport.update({ - id: '/sass-mixin', - path: '/sass-mixin', - getParentRoute: () => rootRouteImport, -} as any) -const QuotesRoute = QuotesRouteImport.update({ - id: '/quotes', - path: '/quotes', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const ModulesRoute = ModulesRouteImport.update({ @@ -29,9 +24,14 @@ const ModulesRoute = ModulesRouteImport.update({ path: '/modules', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const QuotesRoute = QuotesRouteImport.update({ + id: '/quotes', + path: '/quotes', + getParentRoute: () => rootRouteImport, +} as any) +const SassMixinRoute = SassMixinRouteImport.update({ + id: '/sass-mixin', + path: '/sass-mixin', getParentRoute: () => rootRouteImport, } as any) @@ -71,18 +71,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/sass-mixin': { - id: '/sass-mixin' - path: '/sass-mixin' - fullPath: '/sass-mixin' - preLoaderRoute: typeof SassMixinRouteImport - parentRoute: typeof rootRouteImport - } - '/quotes': { - id: '/quotes' - path: '/quotes' - fullPath: '/quotes' - preLoaderRoute: typeof QuotesRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/modules': { @@ -92,11 +85,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ModulesRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/quotes': { + id: '/quotes' + path: '/quotes' + fullPath: '/quotes' + preLoaderRoute: typeof QuotesRouteImport + parentRoute: typeof rootRouteImport + } + '/sass-mixin': { + id: '/sass-mixin' + path: '/sass-mixin' + fullPath: '/sass-mixin' + preLoaderRoute: typeof SassMixinRouteImport parentRoute: typeof rootRouteImport } } diff --git a/e2e/react-start/custom-basepath/src/routeTree.gen.ts b/e2e/react-start/custom-basepath/src/routeTree.gen.ts index 9aacbfbdf0..18cc9b5163 100644 --- a/e2e/react-start/custom-basepath/src/routeTree.gen.ts +++ b/e2e/react-start/custom-basepath/src/routeTree.gen.ts @@ -9,30 +9,35 @@ // 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 UsersRouteImport } from './routes/users' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as NavigateTestRouteImport } from './routes/navigate-test' -import { Route as LogoutRouteImport } from './routes/logout' -import { Route as DeferredRouteImport } from './routes/deferred' import { Route as IndexRouteImport } from './routes/index' -import { Route as UsersIndexRouteImport } from './routes/users.index' -import { Route as RedirectIndexRouteImport } from './routes/redirect/index' +import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as LogoutRouteImport } from './routes/logout' +import { Route as NavigateTestRouteImport } from './routes/navigate-test' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as UsersRouteImport } from './routes/users' +import { Route as ApiUsersRouteImport } from './routes/api.users' import { Route as PostsIndexRouteImport } from './routes/posts.index' -import { Route as UsersUserIdRouteImport } from './routes/users.$userId' -import { Route as RedirectThrowItRouteImport } from './routes/redirect/throw-it' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as ApiUsersRouteImport } from './routes/api.users' -import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' +import { Route as RedirectIndexRouteImport } from './routes/redirect/index' +import { Route as RedirectThrowItRouteImport } from './routes/redirect/throw-it' +import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as ApiUsersIdRouteImport } from './routes/api/users.$id' +import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -const UsersRoute = UsersRouteImport.update({ - id: '/users', - path: '/users', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const DeferredRoute = DeferredRouteImport.update({ + id: '/deferred', + path: '/deferred', + getParentRoute: () => rootRouteImport, +} as any) +const LogoutRoute = LogoutRouteImport.update({ + id: '/logout', + path: '/logout', getParentRoute: () => rootRouteImport, } as any) const NavigateTestRoute = NavigateTestRouteImport.update({ @@ -40,66 +45,61 @@ const NavigateTestRoute = NavigateTestRouteImport.update({ path: '/navigate-test', getParentRoute: () => rootRouteImport, } as any) -const LogoutRoute = LogoutRouteImport.update({ - id: '/logout', - path: '/logout', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const DeferredRoute = DeferredRouteImport.update({ - id: '/deferred', - path: '/deferred', +const UsersRoute = UsersRouteImport.update({ + id: '/users', + path: '/users', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const ApiUsersRoute = ApiUsersRouteImport.update({ + id: '/api/users', + path: '/api/users', getParentRoute: () => rootRouteImport, } as any) -const UsersIndexRoute = UsersIndexRouteImport.update({ +const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => UsersRoute, + getParentRoute: () => PostsRoute, +} as any) +const PostsPostIdRoute = PostsPostIdRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => PostsRoute, } as any) const RedirectIndexRoute = RedirectIndexRouteImport.update({ id: '/redirect/', path: '/redirect/', getParentRoute: () => rootRouteImport, } as any) -const PostsIndexRoute = PostsIndexRouteImport.update({ +const RedirectThrowItRoute = RedirectThrowItRouteImport.update({ + id: '/redirect/throw-it', + path: '/redirect/throw-it', + getParentRoute: () => rootRouteImport, +} as any) +const UsersIndexRoute = UsersIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => PostsRoute, + getParentRoute: () => UsersRoute, } as any) const UsersUserIdRoute = UsersUserIdRouteImport.update({ id: '/$userId', path: '/$userId', getParentRoute: () => UsersRoute, } as any) -const RedirectThrowItRoute = RedirectThrowItRouteImport.update({ - id: '/redirect/throw-it', - path: '/redirect/throw-it', - getParentRoute: () => rootRouteImport, -} as any) -const PostsPostIdRoute = PostsPostIdRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => PostsRoute, -} as any) -const ApiUsersRoute = ApiUsersRouteImport.update({ - id: '/api/users', - path: '/api/users', - getParentRoute: () => rootRouteImport, +const ApiUsersIdRoute = ApiUsersIdRouteImport.update({ + id: '/$id', + path: '/$id', + getParentRoute: () => ApiUsersRoute, } as any) const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ id: '/posts_/$postId/deep', path: '/posts/$postId/deep', getParentRoute: () => rootRouteImport, } as any) -const ApiUsersIdRoute = ApiUsersIdRouteImport.update({ - id: '/$id', - path: '/$id', - getParentRoute: () => ApiUsersRoute, -} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -218,25 +218,18 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/users': { - id: '/users' - path: '/users' - fullPath: '/users' - preLoaderRoute: typeof UsersRouteImport - parentRoute: typeof rootRouteImport - } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/navigate-test': { - id: '/navigate-test' - path: '/navigate-test' - fullPath: '/navigate-test' - preLoaderRoute: typeof NavigateTestRouteImport + '/deferred': { + id: '/deferred' + path: '/deferred' + fullPath: '/deferred' + preLoaderRoute: typeof DeferredRouteImport parentRoute: typeof rootRouteImport } '/logout': { @@ -246,32 +239,32 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LogoutRouteImport parentRoute: typeof rootRouteImport } - '/deferred': { - id: '/deferred' - path: '/deferred' - fullPath: '/deferred' - preLoaderRoute: typeof DeferredRouteImport + '/navigate-test': { + id: '/navigate-test' + path: '/navigate-test' + fullPath: '/navigate-test' + preLoaderRoute: typeof NavigateTestRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/users/': { - id: '/users/' - path: '/' - fullPath: '/users/' - preLoaderRoute: typeof UsersIndexRouteImport - parentRoute: typeof UsersRoute + '/users': { + id: '/users' + path: '/users' + fullPath: '/users' + preLoaderRoute: typeof UsersRouteImport + parentRoute: typeof rootRouteImport } - '/redirect/': { - id: '/redirect/' - path: '/redirect' - fullPath: '/redirect/' - preLoaderRoute: typeof RedirectIndexRouteImport + '/api/users': { + id: '/api/users' + path: '/api/users' + fullPath: '/api/users' + preLoaderRoute: typeof ApiUsersRouteImport parentRoute: typeof rootRouteImport } '/posts/': { @@ -281,20 +274,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRoute } - '/users/$userId': { - id: '/users/$userId' - path: '/$userId' - fullPath: '/users/$userId' - preLoaderRoute: typeof UsersUserIdRouteImport - parentRoute: typeof UsersRoute - } - '/redirect/throw-it': { - id: '/redirect/throw-it' - path: '/redirect/throw-it' - fullPath: '/redirect/throw-it' - preLoaderRoute: typeof RedirectThrowItRouteImport - parentRoute: typeof rootRouteImport - } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' @@ -302,20 +281,34 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/api/users': { - id: '/api/users' - path: '/api/users' - fullPath: '/api/users' - preLoaderRoute: typeof ApiUsersRouteImport + '/redirect/': { + id: '/redirect/' + path: '/redirect' + fullPath: '/redirect/' + preLoaderRoute: typeof RedirectIndexRouteImport parentRoute: typeof rootRouteImport } - '/posts_/$postId/deep': { - id: '/posts_/$postId/deep' - path: '/posts/$postId/deep' - fullPath: '/posts/$postId/deep' - preLoaderRoute: typeof PostsPostIdDeepRouteImport + '/redirect/throw-it': { + id: '/redirect/throw-it' + path: '/redirect/throw-it' + fullPath: '/redirect/throw-it' + preLoaderRoute: typeof RedirectThrowItRouteImport parentRoute: typeof rootRouteImport } + '/users/': { + id: '/users/' + path: '/' + fullPath: '/users/' + preLoaderRoute: typeof UsersIndexRouteImport + parentRoute: typeof UsersRoute + } + '/users/$userId': { + id: '/users/$userId' + path: '/$userId' + fullPath: '/users/$userId' + preLoaderRoute: typeof UsersUserIdRouteImport + parentRoute: typeof UsersRoute + } '/api/users/$id': { id: '/api/users/$id' path: '/$id' @@ -323,6 +316,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ApiUsersIdRouteImport parentRoute: typeof ApiUsersRoute } + '/posts_/$postId/deep': { + id: '/posts_/$postId/deep' + path: '/posts/$postId/deep' + fullPath: '/posts/$postId/deep' + preLoaderRoute: typeof PostsPostIdDeepRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/e2e/react-start/deferred-hydration/src/routeTree.gen.ts b/e2e/react-start/deferred-hydration/src/routeTree.gen.ts index a5b35a698f..451af49ddf 100644 --- a/e2e/react-start/deferred-hydration/src/routeTree.gen.ts +++ b/e2e/react-start/deferred-hydration/src/routeTree.gen.ts @@ -9,26 +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 ScrollRestorationRouteImport } from './routes/scroll-restoration' -import { Route as ImportedRouteImport } from './routes/imported' -import { Route as EnhancedRouteImport } from './routes/enhanced' -import { Route as CssRouteImport } from './routes/css' -import { Route as ComponentsRouteImport } from './routes/components' import { Route as IndexRouteImport } from './routes/index' +import { Route as ComponentsRouteImport } from './routes/components' +import { Route as CssRouteImport } from './routes/css' +import { Route as EnhancedRouteImport } from './routes/enhanced' +import { Route as ImportedRouteImport } from './routes/imported' +import { Route as ScrollRestorationRouteImport } from './routes/scroll-restoration' -const ScrollRestorationRoute = ScrollRestorationRouteImport.update({ - id: '/scroll-restoration', - path: '/scroll-restoration', - getParentRoute: () => rootRouteImport, -} as any) -const ImportedRoute = ImportedRouteImport.update({ - id: '/imported', - path: '/imported', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const EnhancedRoute = EnhancedRouteImport.update({ - id: '/enhanced', - path: '/enhanced', +const ComponentsRoute = ComponentsRouteImport.update({ + id: '/components', + path: '/components', getParentRoute: () => rootRouteImport, } as any) const CssRoute = CssRouteImport.update({ @@ -36,14 +31,19 @@ const CssRoute = CssRouteImport.update({ path: '/css', getParentRoute: () => rootRouteImport, } as any) -const ComponentsRoute = ComponentsRouteImport.update({ - id: '/components', - path: '/components', +const EnhancedRoute = EnhancedRouteImport.update({ + id: '/enhanced', + path: '/enhanced', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const ImportedRoute = ImportedRouteImport.update({ + id: '/imported', + path: '/imported', + getParentRoute: () => rootRouteImport, +} as any) +const ScrollRestorationRoute = ScrollRestorationRouteImport.update({ + id: '/scroll-restoration', + path: '/scroll-restoration', getParentRoute: () => rootRouteImport, } as any) @@ -110,25 +110,18 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/scroll-restoration': { - id: '/scroll-restoration' - path: '/scroll-restoration' - fullPath: '/scroll-restoration' - preLoaderRoute: typeof ScrollRestorationRouteImport - parentRoute: typeof rootRouteImport - } - '/imported': { - id: '/imported' - path: '/imported' - fullPath: '/imported' - preLoaderRoute: typeof ImportedRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/enhanced': { - id: '/enhanced' - path: '/enhanced' - fullPath: '/enhanced' - preLoaderRoute: typeof EnhancedRouteImport + '/components': { + id: '/components' + path: '/components' + fullPath: '/components' + preLoaderRoute: typeof ComponentsRouteImport parentRoute: typeof rootRouteImport } '/css': { @@ -138,18 +131,25 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof CssRouteImport parentRoute: typeof rootRouteImport } - '/components': { - id: '/components' - path: '/components' - fullPath: '/components' - preLoaderRoute: typeof ComponentsRouteImport + '/enhanced': { + id: '/enhanced' + path: '/enhanced' + fullPath: '/enhanced' + preLoaderRoute: typeof EnhancedRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/imported': { + id: '/imported' + path: '/imported' + fullPath: '/imported' + preLoaderRoute: typeof ImportedRouteImport + parentRoute: typeof rootRouteImport + } + '/scroll-restoration': { + id: '/scroll-restoration' + path: '/scroll-restoration' + fullPath: '/scroll-restoration' + preLoaderRoute: typeof ScrollRestorationRouteImport parentRoute: typeof rootRouteImport } } diff --git a/e2e/react-start/early-hints/src/routeTree.gen.ts b/e2e/react-start/early-hints/src/routeTree.gen.ts index 14e2fd341e..5196090061 100644 --- a/e2e/react-start/early-hints/src/routeTree.gen.ts +++ b/e2e/react-start/early-hints/src/routeTree.gen.ts @@ -9,22 +9,17 @@ // 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 RedirectRouteImport } from './routes/redirect' -import { Route as ParentRouteImport } from './routes/parent' -import { Route as OtherRouteImport } from './routes/other' import { Route as IndexRouteImport } from './routes/index' -import { Route as ParentChildRouteImport } from './routes/parent/child' +import { Route as OtherRouteImport } from './routes/other' +import { Route as ParentRouteImport } from './routes/parent' +import { Route as RedirectRouteImport } from './routes/redirect' import { Route as OtherNestedRouteImport } from './routes/other/nested' +import { Route as ParentChildRouteImport } from './routes/parent/child' import { Route as ParentChildGrandchildRouteImport } from './routes/parent/child/grandchild' -const RedirectRoute = RedirectRouteImport.update({ - id: '/redirect', - path: '/redirect', - getParentRoute: () => rootRouteImport, -} as any) -const ParentRoute = ParentRouteImport.update({ - id: '/parent', - path: '/parent', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const OtherRoute = OtherRouteImport.update({ @@ -32,21 +27,26 @@ const OtherRoute = OtherRouteImport.update({ path: '/other', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const ParentRoute = ParentRouteImport.update({ + id: '/parent', + path: '/parent', getParentRoute: () => rootRouteImport, } as any) -const ParentChildRoute = ParentChildRouteImport.update({ - id: '/child', - path: '/child', - getParentRoute: () => ParentRoute, +const RedirectRoute = RedirectRouteImport.update({ + id: '/redirect', + path: '/redirect', + getParentRoute: () => rootRouteImport, } as any) const OtherNestedRoute = OtherNestedRouteImport.update({ id: '/nested', path: '/nested', getParentRoute: () => OtherRoute, } as any) +const ParentChildRoute = ParentChildRouteImport.update({ + id: '/child', + path: '/child', + getParentRoute: () => ParentRoute, +} as any) const ParentChildGrandchildRoute = ParentChildGrandchildRouteImport.update({ id: '/grandchild', path: '/grandchild', @@ -120,18 +120,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/redirect': { - id: '/redirect' - path: '/redirect' - fullPath: '/redirect' - preLoaderRoute: typeof RedirectRouteImport - parentRoute: typeof rootRouteImport - } - '/parent': { - id: '/parent' - path: '/parent' - fullPath: '/parent' - preLoaderRoute: typeof ParentRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/other': { @@ -141,19 +134,19 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof OtherRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/parent': { + id: '/parent' + path: '/parent' + fullPath: '/parent' + preLoaderRoute: typeof ParentRouteImport parentRoute: typeof rootRouteImport } - '/parent/child': { - id: '/parent/child' - path: '/child' - fullPath: '/parent/child' - preLoaderRoute: typeof ParentChildRouteImport - parentRoute: typeof ParentRoute + '/redirect': { + id: '/redirect' + path: '/redirect' + fullPath: '/redirect' + preLoaderRoute: typeof RedirectRouteImport + parentRoute: typeof rootRouteImport } '/other/nested': { id: '/other/nested' @@ -162,6 +155,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof OtherNestedRouteImport parentRoute: typeof OtherRoute } + '/parent/child': { + id: '/parent/child' + path: '/child' + fullPath: '/parent/child' + preLoaderRoute: typeof ParentChildRouteImport + parentRoute: typeof ParentRoute + } '/parent/child/grandchild': { id: '/parent/child/grandchild' path: '/grandchild' diff --git a/e2e/react-start/flamegraph-bench/src/routeTree.gen.ts b/e2e/react-start/flamegraph-bench/src/routeTree.gen.ts index 198d538cd1..cac5558477 100644 --- a/e2e/react-start/flamegraph-bench/src/routeTree.gen.ts +++ b/e2e/react-start/flamegraph-bench/src/routeTree.gen.ts @@ -9,10 +9,10 @@ // 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 SearchRouteImport } from './routes/search' import { Route as IndexRouteImport } from './routes/index' -import { Route as PageIdRouteImport } from './routes/page.$id' +import { Route as SearchRouteImport } from './routes/search' import { Route as NestedARouteImport } from './routes/nested/$a' +import { Route as PageIdRouteImport } from './routes/page.$id' import { Route as NestedABRouteImport } from './routes/nested/$a.$b' import { Route as NestedABCRouteImport } from './routes/nested/$a.$b.$c' import { Route as NestedABCDRouteImport } from './routes/nested/$a.$b.$c.$d' @@ -39,19 +39,14 @@ import { Route as NestedABCDEFGHIJKLMNOPQRSTUVWXRouteImport } from './routes/nes import { Route as NestedABCDEFGHIJKLMNOPQRSTUVWXYRouteImport } from './routes/nested/$a.$b.$c.$d.$e.$f.$g.$h.$i.$j.$k.$l.$m.$n.$o.$p.$q.$r.$s.$t.$u.$v.$w.$x.$y' import { Route as NestedABCDEFGHIJKLMNOPQRSTUVWXYZRouteImport } from './routes/nested/$a.$b.$c.$d.$e.$f.$g.$h.$i.$j.$k.$l.$m.$n.$o.$p.$q.$r.$s.$t.$u.$v.$w.$x.$y.$z' -const SearchRoute = SearchRouteImport.update({ - id: '/search', - path: '/search', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) -const PageIdRoute = PageIdRouteImport.update({ - id: '/page/$id', - path: '/page/$id', +const SearchRoute = SearchRouteImport.update({ + id: '/search', + path: '/search', getParentRoute: () => rootRouteImport, } as any) const NestedARoute = NestedARouteImport.update({ @@ -59,6 +54,11 @@ const NestedARoute = NestedARouteImport.update({ path: '/nested/$a', getParentRoute: () => rootRouteImport, } as any) +const PageIdRoute = PageIdRouteImport.update({ + id: '/page/$id', + path: '/page/$id', + getParentRoute: () => rootRouteImport, +} as any) const NestedABRoute = NestedABRouteImport.update({ id: '/$b', path: '/$b', @@ -393,13 +393,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/search': { - id: '/search' - path: '/search' - fullPath: '/search' - preLoaderRoute: typeof SearchRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -407,11 +400,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/page/$id': { - id: '/page/$id' - path: '/page/$id' - fullPath: '/page/$id' - preLoaderRoute: typeof PageIdRouteImport + '/search': { + id: '/search' + path: '/search' + fullPath: '/search' + preLoaderRoute: typeof SearchRouteImport parentRoute: typeof rootRouteImport } '/nested/$a': { @@ -421,6 +414,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof NestedARouteImport parentRoute: typeof rootRouteImport } + '/page/$id': { + id: '/page/$id' + path: '/page/$id' + fullPath: '/page/$id' + preLoaderRoute: typeof PageIdRouteImport + parentRoute: typeof rootRouteImport + } '/nested/$a/$b': { id: '/nested/$a/$b' path: '/$b' diff --git a/e2e/react-start/hmr/src/routeTree.gen.ts b/e2e/react-start/hmr/src/routeTree.gen.ts index 53c89f8eb5..8de7587b66 100644 --- a/e2e/react-start/hmr/src/routeTree.gen.ts +++ b/e2e/react-start/hmr/src/routeTree.gen.ts @@ -9,45 +9,45 @@ // 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 ServerFnHmrRouteImport } from './routes/server-fn-hmr' -import { Route as InputsRouteImport } from './routes/inputs' -import { Route as ComponentHmrNamedSplitRouteImport } from './routes/component-hmr-named-split' -import { Route as ComponentHmrNamedNosplitRouteImport } from './routes/component-hmr-named-nosplit' -import { Route as ComponentHmrNamedErrorSplitRouteImport } from './routes/component-hmr-named-error-split' -import { Route as ComponentHmrInlineSplitRouteImport } from './routes/component-hmr-inline-split' -import { Route as ComponentHmrInlineNosplitRouteImport } from './routes/component-hmr-inline-nosplit' -import { Route as ComponentHmrInlineErrorSplitRouteImport } from './routes/component-hmr-inline-error-split' -import { Route as ComponentHmrRouteImport } from './routes/component-hmr' -import { Route as ChildRouteImport } from './routes/child' import { Route as IndexRouteImport } from './routes/index' +import { Route as ChildRouteImport } from './routes/child' +import { Route as ComponentHmrRouteImport } from './routes/component-hmr' +import { Route as ComponentHmrInlineErrorSplitRouteImport } from './routes/component-hmr-inline-error-split' +import { Route as ComponentHmrInlineNosplitRouteImport } from './routes/component-hmr-inline-nosplit' +import { Route as ComponentHmrInlineSplitRouteImport } from './routes/component-hmr-inline-split' +import { Route as ComponentHmrNamedErrorSplitRouteImport } from './routes/component-hmr-named-error-split' +import { Route as ComponentHmrNamedNosplitRouteImport } from './routes/component-hmr-named-nosplit' +import { Route as ComponentHmrNamedSplitRouteImport } from './routes/component-hmr-named-split' +import { Route as InputsRouteImport } from './routes/inputs' +import { Route as ServerFnHmrRouteImport } from './routes/server-fn-hmr' import { Route as AliasedRouteImportsIdRouteImport } from './routes/aliased-route-imports.$id' import { Route as AliasedRouteImportsIdChildRouteImport } from './routes/aliased-route-imports.$id.child' -const ServerFnHmrRoute = ServerFnHmrRouteImport.update({ - id: '/server-fn-hmr', - path: '/server-fn-hmr', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const InputsRoute = InputsRouteImport.update({ - id: '/inputs', - path: '/inputs', +const ChildRoute = ChildRouteImport.update({ + id: '/child', + path: '/child', getParentRoute: () => rootRouteImport, } as any) -const ComponentHmrNamedSplitRoute = ComponentHmrNamedSplitRouteImport.update({ - id: '/component-hmr-named-split', - path: '/component-hmr-named-split', +const ComponentHmrRoute = ComponentHmrRouteImport.update({ + id: '/component-hmr', + path: '/component-hmr', getParentRoute: () => rootRouteImport, } as any) -const ComponentHmrNamedNosplitRoute = - ComponentHmrNamedNosplitRouteImport.update({ - id: '/component-hmr-named-nosplit', - path: '/component-hmr-named-nosplit', +const ComponentHmrInlineErrorSplitRoute = + ComponentHmrInlineErrorSplitRouteImport.update({ + id: '/component-hmr-inline-error-split', + path: '/component-hmr-inline-error-split', getParentRoute: () => rootRouteImport, } as any) -const ComponentHmrNamedErrorSplitRoute = - ComponentHmrNamedErrorSplitRouteImport.update({ - id: '/component-hmr-named-error-split', - path: '/component-hmr-named-error-split', +const ComponentHmrInlineNosplitRoute = + ComponentHmrInlineNosplitRouteImport.update({ + id: '/component-hmr-inline-nosplit', + path: '/component-hmr-inline-nosplit', getParentRoute: () => rootRouteImport, } as any) const ComponentHmrInlineSplitRoute = ComponentHmrInlineSplitRouteImport.update({ @@ -55,31 +55,31 @@ const ComponentHmrInlineSplitRoute = ComponentHmrInlineSplitRouteImport.update({ path: '/component-hmr-inline-split', getParentRoute: () => rootRouteImport, } as any) -const ComponentHmrInlineNosplitRoute = - ComponentHmrInlineNosplitRouteImport.update({ - id: '/component-hmr-inline-nosplit', - path: '/component-hmr-inline-nosplit', +const ComponentHmrNamedErrorSplitRoute = + ComponentHmrNamedErrorSplitRouteImport.update({ + id: '/component-hmr-named-error-split', + path: '/component-hmr-named-error-split', getParentRoute: () => rootRouteImport, } as any) -const ComponentHmrInlineErrorSplitRoute = - ComponentHmrInlineErrorSplitRouteImport.update({ - id: '/component-hmr-inline-error-split', - path: '/component-hmr-inline-error-split', +const ComponentHmrNamedNosplitRoute = + ComponentHmrNamedNosplitRouteImport.update({ + id: '/component-hmr-named-nosplit', + path: '/component-hmr-named-nosplit', getParentRoute: () => rootRouteImport, } as any) -const ComponentHmrRoute = ComponentHmrRouteImport.update({ - id: '/component-hmr', - path: '/component-hmr', +const ComponentHmrNamedSplitRoute = ComponentHmrNamedSplitRouteImport.update({ + id: '/component-hmr-named-split', + path: '/component-hmr-named-split', getParentRoute: () => rootRouteImport, } as any) -const ChildRoute = ChildRouteImport.update({ - id: '/child', - path: '/child', +const InputsRoute = InputsRouteImport.update({ + id: '/inputs', + path: '/inputs', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const ServerFnHmrRoute = ServerFnHmrRouteImport.update({ + id: '/server-fn-hmr', + path: '/server-fn-hmr', getParentRoute: () => rootRouteImport, } as any) const AliasedRouteImportsIdRoute = AliasedRouteImportsIdRouteImport.update({ @@ -205,39 +205,39 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/server-fn-hmr': { - id: '/server-fn-hmr' - path: '/server-fn-hmr' - fullPath: '/server-fn-hmr' - preLoaderRoute: typeof ServerFnHmrRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/inputs': { - id: '/inputs' - path: '/inputs' - fullPath: '/inputs' - preLoaderRoute: typeof InputsRouteImport + '/child': { + id: '/child' + path: '/child' + fullPath: '/child' + preLoaderRoute: typeof ChildRouteImport parentRoute: typeof rootRouteImport } - '/component-hmr-named-split': { - id: '/component-hmr-named-split' - path: '/component-hmr-named-split' - fullPath: '/component-hmr-named-split' - preLoaderRoute: typeof ComponentHmrNamedSplitRouteImport + '/component-hmr': { + id: '/component-hmr' + path: '/component-hmr' + fullPath: '/component-hmr' + preLoaderRoute: typeof ComponentHmrRouteImport parentRoute: typeof rootRouteImport } - '/component-hmr-named-nosplit': { - id: '/component-hmr-named-nosplit' - path: '/component-hmr-named-nosplit' - fullPath: '/component-hmr-named-nosplit' - preLoaderRoute: typeof ComponentHmrNamedNosplitRouteImport + '/component-hmr-inline-error-split': { + id: '/component-hmr-inline-error-split' + path: '/component-hmr-inline-error-split' + fullPath: '/component-hmr-inline-error-split' + preLoaderRoute: typeof ComponentHmrInlineErrorSplitRouteImport parentRoute: typeof rootRouteImport } - '/component-hmr-named-error-split': { - id: '/component-hmr-named-error-split' - path: '/component-hmr-named-error-split' - fullPath: '/component-hmr-named-error-split' - preLoaderRoute: typeof ComponentHmrNamedErrorSplitRouteImport + '/component-hmr-inline-nosplit': { + id: '/component-hmr-inline-nosplit' + path: '/component-hmr-inline-nosplit' + fullPath: '/component-hmr-inline-nosplit' + preLoaderRoute: typeof ComponentHmrInlineNosplitRouteImport parentRoute: typeof rootRouteImport } '/component-hmr-inline-split': { @@ -247,39 +247,39 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ComponentHmrInlineSplitRouteImport parentRoute: typeof rootRouteImport } - '/component-hmr-inline-nosplit': { - id: '/component-hmr-inline-nosplit' - path: '/component-hmr-inline-nosplit' - fullPath: '/component-hmr-inline-nosplit' - preLoaderRoute: typeof ComponentHmrInlineNosplitRouteImport + '/component-hmr-named-error-split': { + id: '/component-hmr-named-error-split' + path: '/component-hmr-named-error-split' + fullPath: '/component-hmr-named-error-split' + preLoaderRoute: typeof ComponentHmrNamedErrorSplitRouteImport parentRoute: typeof rootRouteImport } - '/component-hmr-inline-error-split': { - id: '/component-hmr-inline-error-split' - path: '/component-hmr-inline-error-split' - fullPath: '/component-hmr-inline-error-split' - preLoaderRoute: typeof ComponentHmrInlineErrorSplitRouteImport + '/component-hmr-named-nosplit': { + id: '/component-hmr-named-nosplit' + path: '/component-hmr-named-nosplit' + fullPath: '/component-hmr-named-nosplit' + preLoaderRoute: typeof ComponentHmrNamedNosplitRouteImport parentRoute: typeof rootRouteImport } - '/component-hmr': { - id: '/component-hmr' - path: '/component-hmr' - fullPath: '/component-hmr' - preLoaderRoute: typeof ComponentHmrRouteImport + '/component-hmr-named-split': { + id: '/component-hmr-named-split' + path: '/component-hmr-named-split' + fullPath: '/component-hmr-named-split' + preLoaderRoute: typeof ComponentHmrNamedSplitRouteImport parentRoute: typeof rootRouteImport } - '/child': { - id: '/child' - path: '/child' - fullPath: '/child' - preLoaderRoute: typeof ChildRouteImport + '/inputs': { + id: '/inputs' + path: '/inputs' + fullPath: '/inputs' + preLoaderRoute: typeof InputsRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/server-fn-hmr': { + id: '/server-fn-hmr' + path: '/server-fn-hmr' + fullPath: '/server-fn-hmr' + preLoaderRoute: typeof ServerFnHmrRouteImport parentRoute: typeof rootRouteImport } '/aliased-route-imports/$id': { diff --git a/e2e/react-start/i18n-paraglide/src/routeTree.gen.ts b/e2e/react-start/i18n-paraglide/src/routeTree.gen.ts index 421daf2790..d89758e684 100644 --- a/e2e/react-start/i18n-paraglide/src/routeTree.gen.ts +++ b/e2e/react-start/i18n-paraglide/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' +import { Route as AboutRouteImport } from './routes/about' -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/e2e/react-start/import-protection-custom-config/src/routeTree.gen.ts b/e2e/react-start/import-protection-custom-config/src/routeTree.gen.ts index 44861559e3..6a2acc53ef 100644 --- a/e2e/react-start/import-protection-custom-config/src/routeTree.gen.ts +++ b/e2e/react-start/import-protection-custom-config/src/routeTree.gen.ts @@ -9,13 +9,13 @@ // 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 FrontendLeakRouteImport } from './routes/frontend-leak' -import { Route as BackendLeakRouteImport } from './routes/backend-leak' import { Route as IndexRouteImport } from './routes/index' +import { Route as BackendLeakRouteImport } from './routes/backend-leak' +import { Route as FrontendLeakRouteImport } from './routes/frontend-leak' -const FrontendLeakRoute = FrontendLeakRouteImport.update({ - id: '/frontend-leak', - path: '/frontend-leak', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const BackendLeakRoute = BackendLeakRouteImport.update({ @@ -23,9 +23,9 @@ const BackendLeakRoute = BackendLeakRouteImport.update({ path: '/backend-leak', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const FrontendLeakRoute = FrontendLeakRouteImport.update({ + id: '/frontend-leak', + path: '/frontend-leak', getParentRoute: () => rootRouteImport, } as any) @@ -61,11 +61,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/frontend-leak': { - id: '/frontend-leak' - path: '/frontend-leak' - fullPath: '/frontend-leak' - preLoaderRoute: typeof FrontendLeakRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/backend-leak': { @@ -75,11 +75,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof BackendLeakRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/frontend-leak': { + id: '/frontend-leak' + path: '/frontend-leak' + fullPath: '/frontend-leak' + preLoaderRoute: typeof FrontendLeakRouteImport parentRoute: typeof rootRouteImport } } diff --git a/e2e/react-start/query-integration/src/routeTree.gen.ts b/e2e/react-start/query-integration/src/routeTree.gen.ts index 22c6f3293d..ee7ef43f26 100644 --- a/e2e/react-start/query-integration/src/routeTree.gen.ts +++ b/e2e/react-start/query-integration/src/routeTree.gen.ts @@ -9,14 +9,14 @@ // 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 UseSuspenseQueryRouteImport } from './routes/useSuspenseQuery' -import { Route as UseQueryRouteImport } from './routes/useQuery' import { Route as IndexRouteImport } from './routes/index' +import { Route as UseQueryRouteImport } from './routes/useQuery' +import { Route as UseSuspenseQueryRouteImport } from './routes/useSuspenseQuery' import { Route as LoaderFetchQueryTypeRouteImport } from './routes/loader-fetchQuery/$type' -const UseSuspenseQueryRoute = UseSuspenseQueryRouteImport.update({ - id: '/useSuspenseQuery', - path: '/useSuspenseQuery', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const UseQueryRoute = UseQueryRouteImport.update({ @@ -24,9 +24,9 @@ const UseQueryRoute = UseQueryRouteImport.update({ path: '/useQuery', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const UseSuspenseQueryRoute = UseSuspenseQueryRouteImport.update({ + id: '/useSuspenseQuery', + path: '/useSuspenseQuery', getParentRoute: () => rootRouteImport, } as any) const LoaderFetchQueryTypeRoute = LoaderFetchQueryTypeRouteImport.update({ @@ -80,11 +80,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/useSuspenseQuery': { - id: '/useSuspenseQuery' - path: '/useSuspenseQuery' - fullPath: '/useSuspenseQuery' - preLoaderRoute: typeof UseSuspenseQueryRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/useQuery': { @@ -94,11 +94,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof UseQueryRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/useSuspenseQuery': { + id: '/useSuspenseQuery' + path: '/useSuspenseQuery' + fullPath: '/useSuspenseQuery' + preLoaderRoute: typeof UseSuspenseQueryRouteImport parentRoute: typeof rootRouteImport } '/loader-fetchQuery/$type': { diff --git a/e2e/react-start/rsc-deferred-hydration/src/routeTree.gen.ts b/e2e/react-start/rsc-deferred-hydration/src/routeTree.gen.ts index 246737b515..f2a58f4f6f 100644 --- a/e2e/react-start/rsc-deferred-hydration/src/routeTree.gen.ts +++ b/e2e/react-start/rsc-deferred-hydration/src/routeTree.gen.ts @@ -9,19 +9,14 @@ // 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 ServerClientRouteImport } from './routes/server-client' -import { Route as CssRouteImport } from './routes/css' -import { Route as CompositeRouteImport } from './routes/composite' import { Route as IndexRouteImport } from './routes/index' +import { Route as CompositeRouteImport } from './routes/composite' +import { Route as CssRouteImport } from './routes/css' +import { Route as ServerClientRouteImport } from './routes/server-client' -const ServerClientRoute = ServerClientRouteImport.update({ - id: '/server-client', - path: '/server-client', - getParentRoute: () => rootRouteImport, -} as any) -const CssRoute = CssRouteImport.update({ - id: '/css', - path: '/css', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const CompositeRoute = CompositeRouteImport.update({ @@ -29,9 +24,14 @@ const CompositeRoute = CompositeRouteImport.update({ path: '/composite', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const CssRoute = CssRouteImport.update({ + id: '/css', + path: '/css', + getParentRoute: () => rootRouteImport, +} as any) +const ServerClientRoute = ServerClientRouteImport.update({ + id: '/server-client', + path: '/server-client', getParentRoute: () => rootRouteImport, } as any) @@ -71,18 +71,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/server-client': { - id: '/server-client' - path: '/server-client' - fullPath: '/server-client' - preLoaderRoute: typeof ServerClientRouteImport - parentRoute: typeof rootRouteImport - } - '/css': { - id: '/css' - path: '/css' - fullPath: '/css' - preLoaderRoute: typeof CssRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/composite': { @@ -92,11 +85,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof CompositeRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/css': { + id: '/css' + path: '/css' + fullPath: '/css' + preLoaderRoute: typeof CssRouteImport + parentRoute: typeof rootRouteImport + } + '/server-client': { + id: '/server-client' + path: '/server-client' + fullPath: '/server-client' + preLoaderRoute: typeof ServerClientRouteImport parentRoute: typeof rootRouteImport } } diff --git a/e2e/react-start/rsc-rsbuild/src/routeTree.gen.ts b/e2e/react-start/rsc-rsbuild/src/routeTree.gen.ts index 84c964d11b..80c2e951f9 100644 --- a/e2e/react-start/rsc-rsbuild/src/routeTree.gen.ts +++ b/e2e/react-start/rsc-rsbuild/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 RscNodeModuleClientRouteImport } from './routes/rsc-node-module-client' import { Route as IndexRouteImport } from './routes/index' +import { Route as RscNodeModuleClientRouteImport } from './routes/rsc-node-module-client' -const RscNodeModuleClientRoute = RscNodeModuleClientRouteImport.update({ - id: '/rsc-node-module-client', - path: '/rsc-node-module-client', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const RscNodeModuleClientRoute = RscNodeModuleClientRouteImport.update({ + id: '/rsc-node-module-client', + path: '/rsc-node-module-client', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/rsc-node-module-client': { - id: '/rsc-node-module-client' - path: '/rsc-node-module-client' - fullPath: '/rsc-node-module-client' - preLoaderRoute: typeof RscNodeModuleClientRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/rsc-node-module-client': { + id: '/rsc-node-module-client' + path: '/rsc-node-module-client' + fullPath: '/rsc-node-module-client' + preLoaderRoute: typeof RscNodeModuleClientRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/e2e/react-start/rsc/src/routeTree.gen.ts b/e2e/react-start/rsc/src/routeTree.gen.ts index 015d3edf82..209c514f89 100644 --- a/e2e/react-start/rsc/src/routeTree.gen.ts +++ b/e2e/react-start/rsc/src/routeTree.gen.ts @@ -9,157 +9,152 @@ // 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 RscUseServerFnRouteImport } from './routes/rsc-use-server-fn' -import { Route as RscTreeRouteImport } from './routes/rsc-tree' -import { Route as RscSuspenseRouteImport } from './routes/rsc-suspense' -import { Route as RscStreamingRouteImport } from './routes/rsc-streaming' -import { Route as RscStreamReadableRouteImport } from './routes/rsc-stream-readable' -import { Route as RscStreamLoaderRouteImport } from './routes/rsc-stream-loader' -import { Route as RscStreamGeneratorRouteImport } from './routes/rsc-stream-generator' -import { Route as RscSsrFalseRouteImport } from './routes/rsc-ssr-false' -import { Route as RscSsrDataOnlyRouteImport } from './routes/rsc-ssr-data-only' -import { Route as RscSlotsRouteImport } from './routes/rsc-slots' -import { Route as RscSlotJsxArgsRouteImport } from './routes/rsc-slot-jsx-args' -import { Route as RscServerRedirectRouteImport } from './routes/rsc-server-redirect' -import { Route as RscServerNotFoundRouteImport } from './routes/rsc-server-not-found' -import { Route as RscRequestHeadersRouteImport } from './routes/rsc-request-headers' -import { Route as RscReactCacheRouteImport } from './routes/rsc-react-cache' -import { Route as RscParallelRouteImport } from './routes/rsc-parallel' -import { Route as RscNestedStructureRouteImport } from './routes/rsc-nested-structure' -import { Route as RscNestedRouteImport } from './routes/rsc-nested' -import { Route as RscMultiRouteImport } from './routes/rsc-multi' -import { Route as RscLinkRouteImport } from './routes/rsc-link' -import { Route as RscLargeRouteImport } from './routes/rsc-large' -import { Route as RscInvalidationRouteImport } from './routes/rsc-invalidation' -import { Route as RscHydrationRouteImport } from './routes/rsc-hydration' -import { Route as RscGlobalCssRouteImport } from './routes/rsc-global-css' -import { Route as RscFormsRouteImport } from './routes/rsc-forms' -import { Route as RscFlightApiRouteImport } from './routes/rsc-flight-api' -import { Route as RscExternalRouteImport } from './routes/rsc-external' -import { Route as RscErrorRouteImport } from './routes/rsc-error' -import { Route as RscDirectLoaderRouteImport } from './routes/rsc-direct-loader' -import { Route as RscDeferredComponentRouteImport } from './routes/rsc-deferred-component' -import { Route as RscDeferredRouteImport } from './routes/rsc-deferred' -import { Route as RscCssPreloadComplexRouteImport } from './routes/rsc-css-preload-complex' -import { Route as RscCssModulesRouteImport } from './routes/rsc-css-modules' -import { Route as RscCssAutoinjectRouteImport } from './routes/rsc-css-autoinject' -import { Route as RscContextRouteImport } from './routes/rsc-context' -import { Route as RscComponentSlotRouteImport } from './routes/rsc-component-slot' -import { Route as RscClientPreloadRouteImport } from './routes/rsc-client-preload' -import { Route as RscCachingRouteImport } from './routes/rsc-caching' -import { Route as RscBundleRouteImport } from './routes/rsc-bundle' -import { Route as RscBasicRouteImport } from './routes/rsc-basic' -import { Route as RscAsyncBundleRouteImport } from './routes/rsc-async-bundle' import { Route as IndexRouteImport } from './routes/index' -import { Route as RscParamIndexRouteImport } from './routes/rsc-param/index' +import { Route as RscAsyncBundleRouteImport } from './routes/rsc-async-bundle' +import { Route as RscBasicRouteImport } from './routes/rsc-basic' +import { Route as RscBundleRouteImport } from './routes/rsc-bundle' +import { Route as RscCachingRouteImport } from './routes/rsc-caching' +import { Route as RscClientPreloadRouteImport } from './routes/rsc-client-preload' +import { Route as RscComponentSlotRouteImport } from './routes/rsc-component-slot' +import { Route as RscContextRouteImport } from './routes/rsc-context' +import { Route as RscCssAutoinjectRouteImport } from './routes/rsc-css-autoinject' +import { Route as RscCssModulesRouteImport } from './routes/rsc-css-modules' +import { Route as RscCssPreloadComplexRouteImport } from './routes/rsc-css-preload-complex' +import { Route as RscDeferredRouteImport } from './routes/rsc-deferred' +import { Route as RscDeferredComponentRouteImport } from './routes/rsc-deferred-component' +import { Route as RscDirectLoaderRouteImport } from './routes/rsc-direct-loader' +import { Route as RscErrorRouteImport } from './routes/rsc-error' +import { Route as RscExternalRouteImport } from './routes/rsc-external' +import { Route as RscFlightApiRouteImport } from './routes/rsc-flight-api' +import { Route as RscFormsRouteImport } from './routes/rsc-forms' +import { Route as RscGlobalCssRouteImport } from './routes/rsc-global-css' +import { Route as RscHydrationRouteImport } from './routes/rsc-hydration' +import { Route as RscInvalidationRouteImport } from './routes/rsc-invalidation' +import { Route as RscLargeRouteImport } from './routes/rsc-large' +import { Route as RscLinkRouteImport } from './routes/rsc-link' +import { Route as RscMultiRouteImport } from './routes/rsc-multi' +import { Route as RscNestedRouteImport } from './routes/rsc-nested' +import { Route as RscNestedStructureRouteImport } from './routes/rsc-nested-structure' +import { Route as RscParallelRouteImport } from './routes/rsc-parallel' +import { Route as RscReactCacheRouteImport } from './routes/rsc-react-cache' +import { Route as RscRequestHeadersRouteImport } from './routes/rsc-request-headers' +import { Route as RscServerNotFoundRouteImport } from './routes/rsc-server-not-found' +import { Route as RscServerRedirectRouteImport } from './routes/rsc-server-redirect' +import { Route as RscSlotJsxArgsRouteImport } from './routes/rsc-slot-jsx-args' +import { Route as RscSlotsRouteImport } from './routes/rsc-slots' +import { Route as RscSsrDataOnlyRouteImport } from './routes/rsc-ssr-data-only' +import { Route as RscSsrFalseRouteImport } from './routes/rsc-ssr-false' +import { Route as RscStreamGeneratorRouteImport } from './routes/rsc-stream-generator' +import { Route as RscStreamLoaderRouteImport } from './routes/rsc-stream-loader' +import { Route as RscStreamReadableRouteImport } from './routes/rsc-stream-readable' +import { Route as RscStreamingRouteImport } from './routes/rsc-streaming' +import { Route as RscSuspenseRouteImport } from './routes/rsc-suspense' +import { Route as RscTreeRouteImport } from './routes/rsc-tree' +import { Route as RscUseServerFnRouteImport } from './routes/rsc-use-server-fn' +import { Route as ApiRscFlightRouteImport } from './routes/api.rsc-flight' import { Route as RscCssConditionalIndexRouteImport } from './routes/rsc-css-conditional.index' -import { Route as RscParamIdRouteImport } from './routes/rsc-param/$id' import { Route as RscCssConditionalBranchRouteImport } from './routes/rsc-css-conditional.$branch' -import { Route as ApiRscFlightRouteImport } from './routes/api.rsc-flight' +import { Route as RscParamIndexRouteImport } from './routes/rsc-param/index' +import { Route as RscParamIdRouteImport } from './routes/rsc-param/$id' -const RscUseServerFnRoute = RscUseServerFnRouteImport.update({ - id: '/rsc-use-server-fn', - path: '/rsc-use-server-fn', - getParentRoute: () => rootRouteImport, -} as any) -const RscTreeRoute = RscTreeRouteImport.update({ - id: '/rsc-tree', - path: '/rsc-tree', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const RscSuspenseRoute = RscSuspenseRouteImport.update({ - id: '/rsc-suspense', - path: '/rsc-suspense', +const RscAsyncBundleRoute = RscAsyncBundleRouteImport.update({ + id: '/rsc-async-bundle', + path: '/rsc-async-bundle', getParentRoute: () => rootRouteImport, } as any) -const RscStreamingRoute = RscStreamingRouteImport.update({ - id: '/rsc-streaming', - path: '/rsc-streaming', +const RscBasicRoute = RscBasicRouteImport.update({ + id: '/rsc-basic', + path: '/rsc-basic', getParentRoute: () => rootRouteImport, } as any) -const RscStreamReadableRoute = RscStreamReadableRouteImport.update({ - id: '/rsc-stream-readable', - path: '/rsc-stream-readable', +const RscBundleRoute = RscBundleRouteImport.update({ + id: '/rsc-bundle', + path: '/rsc-bundle', getParentRoute: () => rootRouteImport, } as any) -const RscStreamLoaderRoute = RscStreamLoaderRouteImport.update({ - id: '/rsc-stream-loader', - path: '/rsc-stream-loader', +const RscCachingRoute = RscCachingRouteImport.update({ + id: '/rsc-caching', + path: '/rsc-caching', getParentRoute: () => rootRouteImport, } as any) -const RscStreamGeneratorRoute = RscStreamGeneratorRouteImport.update({ - id: '/rsc-stream-generator', - path: '/rsc-stream-generator', +const RscClientPreloadRoute = RscClientPreloadRouteImport.update({ + id: '/rsc-client-preload', + path: '/rsc-client-preload', getParentRoute: () => rootRouteImport, } as any) -const RscSsrFalseRoute = RscSsrFalseRouteImport.update({ - id: '/rsc-ssr-false', - path: '/rsc-ssr-false', +const RscComponentSlotRoute = RscComponentSlotRouteImport.update({ + id: '/rsc-component-slot', + path: '/rsc-component-slot', getParentRoute: () => rootRouteImport, } as any) -const RscSsrDataOnlyRoute = RscSsrDataOnlyRouteImport.update({ - id: '/rsc-ssr-data-only', - path: '/rsc-ssr-data-only', +const RscContextRoute = RscContextRouteImport.update({ + id: '/rsc-context', + path: '/rsc-context', getParentRoute: () => rootRouteImport, } as any) -const RscSlotsRoute = RscSlotsRouteImport.update({ - id: '/rsc-slots', - path: '/rsc-slots', +const RscCssAutoinjectRoute = RscCssAutoinjectRouteImport.update({ + id: '/rsc-css-autoinject', + path: '/rsc-css-autoinject', getParentRoute: () => rootRouteImport, } as any) -const RscSlotJsxArgsRoute = RscSlotJsxArgsRouteImport.update({ - id: '/rsc-slot-jsx-args', - path: '/rsc-slot-jsx-args', +const RscCssModulesRoute = RscCssModulesRouteImport.update({ + id: '/rsc-css-modules', + path: '/rsc-css-modules', getParentRoute: () => rootRouteImport, } as any) -const RscServerRedirectRoute = RscServerRedirectRouteImport.update({ - id: '/rsc-server-redirect', - path: '/rsc-server-redirect', +const RscCssPreloadComplexRoute = RscCssPreloadComplexRouteImport.update({ + id: '/rsc-css-preload-complex', + path: '/rsc-css-preload-complex', getParentRoute: () => rootRouteImport, } as any) -const RscServerNotFoundRoute = RscServerNotFoundRouteImport.update({ - id: '/rsc-server-not-found', - path: '/rsc-server-not-found', +const RscDeferredRoute = RscDeferredRouteImport.update({ + id: '/rsc-deferred', + path: '/rsc-deferred', getParentRoute: () => rootRouteImport, } as any) -const RscRequestHeadersRoute = RscRequestHeadersRouteImport.update({ - id: '/rsc-request-headers', - path: '/rsc-request-headers', +const RscDeferredComponentRoute = RscDeferredComponentRouteImport.update({ + id: '/rsc-deferred-component', + path: '/rsc-deferred-component', getParentRoute: () => rootRouteImport, } as any) -const RscReactCacheRoute = RscReactCacheRouteImport.update({ - id: '/rsc-react-cache', - path: '/rsc-react-cache', +const RscDirectLoaderRoute = RscDirectLoaderRouteImport.update({ + id: '/rsc-direct-loader', + path: '/rsc-direct-loader', getParentRoute: () => rootRouteImport, } as any) -const RscParallelRoute = RscParallelRouteImport.update({ - id: '/rsc-parallel', - path: '/rsc-parallel', +const RscErrorRoute = RscErrorRouteImport.update({ + id: '/rsc-error', + path: '/rsc-error', getParentRoute: () => rootRouteImport, } as any) -const RscNestedStructureRoute = RscNestedStructureRouteImport.update({ - id: '/rsc-nested-structure', - path: '/rsc-nested-structure', +const RscExternalRoute = RscExternalRouteImport.update({ + id: '/rsc-external', + path: '/rsc-external', getParentRoute: () => rootRouteImport, } as any) -const RscNestedRoute = RscNestedRouteImport.update({ - id: '/rsc-nested', - path: '/rsc-nested', +const RscFlightApiRoute = RscFlightApiRouteImport.update({ + id: '/rsc-flight-api', + path: '/rsc-flight-api', getParentRoute: () => rootRouteImport, } as any) -const RscMultiRoute = RscMultiRouteImport.update({ - id: '/rsc-multi', - path: '/rsc-multi', +const RscFormsRoute = RscFormsRouteImport.update({ + id: '/rsc-forms', + path: '/rsc-forms', getParentRoute: () => rootRouteImport, } as any) -const RscLinkRoute = RscLinkRouteImport.update({ - id: '/rsc-link', - path: '/rsc-link', +const RscGlobalCssRoute = RscGlobalCssRouteImport.update({ + id: '/rsc-global-css', + path: '/rsc-global-css', getParentRoute: () => rootRouteImport, } as any) -const RscLargeRoute = RscLargeRouteImport.update({ - id: '/rsc-large', - path: '/rsc-large', +const RscHydrationRoute = RscHydrationRouteImport.update({ + id: '/rsc-hydration', + path: '/rsc-hydration', getParentRoute: () => rootRouteImport, } as any) const RscInvalidationRoute = RscInvalidationRouteImport.update({ @@ -167,109 +162,114 @@ const RscInvalidationRoute = RscInvalidationRouteImport.update({ path: '/rsc-invalidation', getParentRoute: () => rootRouteImport, } as any) -const RscHydrationRoute = RscHydrationRouteImport.update({ - id: '/rsc-hydration', - path: '/rsc-hydration', +const RscLargeRoute = RscLargeRouteImport.update({ + id: '/rsc-large', + path: '/rsc-large', getParentRoute: () => rootRouteImport, } as any) -const RscGlobalCssRoute = RscGlobalCssRouteImport.update({ - id: '/rsc-global-css', - path: '/rsc-global-css', +const RscLinkRoute = RscLinkRouteImport.update({ + id: '/rsc-link', + path: '/rsc-link', getParentRoute: () => rootRouteImport, } as any) -const RscFormsRoute = RscFormsRouteImport.update({ - id: '/rsc-forms', - path: '/rsc-forms', +const RscMultiRoute = RscMultiRouteImport.update({ + id: '/rsc-multi', + path: '/rsc-multi', getParentRoute: () => rootRouteImport, } as any) -const RscFlightApiRoute = RscFlightApiRouteImport.update({ - id: '/rsc-flight-api', - path: '/rsc-flight-api', +const RscNestedRoute = RscNestedRouteImport.update({ + id: '/rsc-nested', + path: '/rsc-nested', getParentRoute: () => rootRouteImport, } as any) -const RscExternalRoute = RscExternalRouteImport.update({ - id: '/rsc-external', - path: '/rsc-external', +const RscNestedStructureRoute = RscNestedStructureRouteImport.update({ + id: '/rsc-nested-structure', + path: '/rsc-nested-structure', getParentRoute: () => rootRouteImport, } as any) -const RscErrorRoute = RscErrorRouteImport.update({ - id: '/rsc-error', - path: '/rsc-error', +const RscParallelRoute = RscParallelRouteImport.update({ + id: '/rsc-parallel', + path: '/rsc-parallel', getParentRoute: () => rootRouteImport, } as any) -const RscDirectLoaderRoute = RscDirectLoaderRouteImport.update({ - id: '/rsc-direct-loader', - path: '/rsc-direct-loader', +const RscReactCacheRoute = RscReactCacheRouteImport.update({ + id: '/rsc-react-cache', + path: '/rsc-react-cache', getParentRoute: () => rootRouteImport, } as any) -const RscDeferredComponentRoute = RscDeferredComponentRouteImport.update({ - id: '/rsc-deferred-component', - path: '/rsc-deferred-component', +const RscRequestHeadersRoute = RscRequestHeadersRouteImport.update({ + id: '/rsc-request-headers', + path: '/rsc-request-headers', getParentRoute: () => rootRouteImport, } as any) -const RscDeferredRoute = RscDeferredRouteImport.update({ - id: '/rsc-deferred', - path: '/rsc-deferred', +const RscServerNotFoundRoute = RscServerNotFoundRouteImport.update({ + id: '/rsc-server-not-found', + path: '/rsc-server-not-found', getParentRoute: () => rootRouteImport, } as any) -const RscCssPreloadComplexRoute = RscCssPreloadComplexRouteImport.update({ - id: '/rsc-css-preload-complex', - path: '/rsc-css-preload-complex', +const RscServerRedirectRoute = RscServerRedirectRouteImport.update({ + id: '/rsc-server-redirect', + path: '/rsc-server-redirect', getParentRoute: () => rootRouteImport, } as any) -const RscCssModulesRoute = RscCssModulesRouteImport.update({ - id: '/rsc-css-modules', - path: '/rsc-css-modules', +const RscSlotJsxArgsRoute = RscSlotJsxArgsRouteImport.update({ + id: '/rsc-slot-jsx-args', + path: '/rsc-slot-jsx-args', getParentRoute: () => rootRouteImport, } as any) -const RscCssAutoinjectRoute = RscCssAutoinjectRouteImport.update({ - id: '/rsc-css-autoinject', - path: '/rsc-css-autoinject', +const RscSlotsRoute = RscSlotsRouteImport.update({ + id: '/rsc-slots', + path: '/rsc-slots', getParentRoute: () => rootRouteImport, } as any) -const RscContextRoute = RscContextRouteImport.update({ - id: '/rsc-context', - path: '/rsc-context', +const RscSsrDataOnlyRoute = RscSsrDataOnlyRouteImport.update({ + id: '/rsc-ssr-data-only', + path: '/rsc-ssr-data-only', + getParentRoute: () => rootRouteImport, +} as any) +const RscSsrFalseRoute = RscSsrFalseRouteImport.update({ + id: '/rsc-ssr-false', + path: '/rsc-ssr-false', getParentRoute: () => rootRouteImport, } as any) -const RscComponentSlotRoute = RscComponentSlotRouteImport.update({ - id: '/rsc-component-slot', - path: '/rsc-component-slot', +const RscStreamGeneratorRoute = RscStreamGeneratorRouteImport.update({ + id: '/rsc-stream-generator', + path: '/rsc-stream-generator', getParentRoute: () => rootRouteImport, } as any) -const RscClientPreloadRoute = RscClientPreloadRouteImport.update({ - id: '/rsc-client-preload', - path: '/rsc-client-preload', +const RscStreamLoaderRoute = RscStreamLoaderRouteImport.update({ + id: '/rsc-stream-loader', + path: '/rsc-stream-loader', getParentRoute: () => rootRouteImport, } as any) -const RscCachingRoute = RscCachingRouteImport.update({ - id: '/rsc-caching', - path: '/rsc-caching', +const RscStreamReadableRoute = RscStreamReadableRouteImport.update({ + id: '/rsc-stream-readable', + path: '/rsc-stream-readable', getParentRoute: () => rootRouteImport, } as any) -const RscBundleRoute = RscBundleRouteImport.update({ - id: '/rsc-bundle', - path: '/rsc-bundle', +const RscStreamingRoute = RscStreamingRouteImport.update({ + id: '/rsc-streaming', + path: '/rsc-streaming', getParentRoute: () => rootRouteImport, } as any) -const RscBasicRoute = RscBasicRouteImport.update({ - id: '/rsc-basic', - path: '/rsc-basic', +const RscSuspenseRoute = RscSuspenseRouteImport.update({ + id: '/rsc-suspense', + path: '/rsc-suspense', getParentRoute: () => rootRouteImport, } as any) -const RscAsyncBundleRoute = RscAsyncBundleRouteImport.update({ - id: '/rsc-async-bundle', - path: '/rsc-async-bundle', +const RscTreeRoute = RscTreeRouteImport.update({ + id: '/rsc-tree', + path: '/rsc-tree', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const RscUseServerFnRoute = RscUseServerFnRouteImport.update({ + id: '/rsc-use-server-fn', + path: '/rsc-use-server-fn', getParentRoute: () => rootRouteImport, } as any) -const RscParamIndexRoute = RscParamIndexRouteImport.update({ - id: '/rsc-param/', - path: '/rsc-param/', +const ApiRscFlightRoute = ApiRscFlightRouteImport.update({ + id: '/api/rsc-flight', + path: '/api/rsc-flight', getParentRoute: () => rootRouteImport, } as any) const RscCssConditionalIndexRoute = RscCssConditionalIndexRouteImport.update({ @@ -277,19 +277,19 @@ const RscCssConditionalIndexRoute = RscCssConditionalIndexRouteImport.update({ path: '/rsc-css-conditional/', getParentRoute: () => rootRouteImport, } as any) -const RscParamIdRoute = RscParamIdRouteImport.update({ - id: '/rsc-param/$id', - path: '/rsc-param/$id', - getParentRoute: () => rootRouteImport, -} as any) const RscCssConditionalBranchRoute = RscCssConditionalBranchRouteImport.update({ id: '/rsc-css-conditional/$branch', path: '/rsc-css-conditional/$branch', getParentRoute: () => rootRouteImport, } as any) -const ApiRscFlightRoute = ApiRscFlightRouteImport.update({ - id: '/api/rsc-flight', - path: '/api/rsc-flight', +const RscParamIndexRoute = RscParamIndexRouteImport.update({ + id: '/rsc-param/', + path: '/rsc-param/', + getParentRoute: () => rootRouteImport, +} as any) +const RscParamIdRoute = RscParamIdRouteImport.update({ + id: '/rsc-param/$id', + path: '/rsc-param/$id', getParentRoute: () => rootRouteImport, } as any) @@ -643,151 +643,144 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/rsc-use-server-fn': { - id: '/rsc-use-server-fn' - path: '/rsc-use-server-fn' - fullPath: '/rsc-use-server-fn' - preLoaderRoute: typeof RscUseServerFnRouteImport - parentRoute: typeof rootRouteImport - } - '/rsc-tree': { - id: '/rsc-tree' - path: '/rsc-tree' - fullPath: '/rsc-tree' - preLoaderRoute: typeof RscTreeRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/rsc-suspense': { - id: '/rsc-suspense' - path: '/rsc-suspense' - fullPath: '/rsc-suspense' - preLoaderRoute: typeof RscSuspenseRouteImport + '/rsc-async-bundle': { + id: '/rsc-async-bundle' + path: '/rsc-async-bundle' + fullPath: '/rsc-async-bundle' + preLoaderRoute: typeof RscAsyncBundleRouteImport parentRoute: typeof rootRouteImport } - '/rsc-streaming': { - id: '/rsc-streaming' - path: '/rsc-streaming' - fullPath: '/rsc-streaming' - preLoaderRoute: typeof RscStreamingRouteImport + '/rsc-basic': { + id: '/rsc-basic' + path: '/rsc-basic' + fullPath: '/rsc-basic' + preLoaderRoute: typeof RscBasicRouteImport parentRoute: typeof rootRouteImport } - '/rsc-stream-readable': { - id: '/rsc-stream-readable' - path: '/rsc-stream-readable' - fullPath: '/rsc-stream-readable' - preLoaderRoute: typeof RscStreamReadableRouteImport + '/rsc-bundle': { + id: '/rsc-bundle' + path: '/rsc-bundle' + fullPath: '/rsc-bundle' + preLoaderRoute: typeof RscBundleRouteImport parentRoute: typeof rootRouteImport } - '/rsc-stream-loader': { - id: '/rsc-stream-loader' - path: '/rsc-stream-loader' - fullPath: '/rsc-stream-loader' - preLoaderRoute: typeof RscStreamLoaderRouteImport + '/rsc-caching': { + id: '/rsc-caching' + path: '/rsc-caching' + fullPath: '/rsc-caching' + preLoaderRoute: typeof RscCachingRouteImport parentRoute: typeof rootRouteImport } - '/rsc-stream-generator': { - id: '/rsc-stream-generator' - path: '/rsc-stream-generator' - fullPath: '/rsc-stream-generator' - preLoaderRoute: typeof RscStreamGeneratorRouteImport + '/rsc-client-preload': { + id: '/rsc-client-preload' + path: '/rsc-client-preload' + fullPath: '/rsc-client-preload' + preLoaderRoute: typeof RscClientPreloadRouteImport parentRoute: typeof rootRouteImport } - '/rsc-ssr-false': { - id: '/rsc-ssr-false' - path: '/rsc-ssr-false' - fullPath: '/rsc-ssr-false' - preLoaderRoute: typeof RscSsrFalseRouteImport + '/rsc-component-slot': { + id: '/rsc-component-slot' + path: '/rsc-component-slot' + fullPath: '/rsc-component-slot' + preLoaderRoute: typeof RscComponentSlotRouteImport parentRoute: typeof rootRouteImport } - '/rsc-ssr-data-only': { - id: '/rsc-ssr-data-only' - path: '/rsc-ssr-data-only' - fullPath: '/rsc-ssr-data-only' - preLoaderRoute: typeof RscSsrDataOnlyRouteImport + '/rsc-context': { + id: '/rsc-context' + path: '/rsc-context' + fullPath: '/rsc-context' + preLoaderRoute: typeof RscContextRouteImport parentRoute: typeof rootRouteImport } - '/rsc-slots': { - id: '/rsc-slots' - path: '/rsc-slots' - fullPath: '/rsc-slots' - preLoaderRoute: typeof RscSlotsRouteImport + '/rsc-css-autoinject': { + id: '/rsc-css-autoinject' + path: '/rsc-css-autoinject' + fullPath: '/rsc-css-autoinject' + preLoaderRoute: typeof RscCssAutoinjectRouteImport parentRoute: typeof rootRouteImport } - '/rsc-slot-jsx-args': { - id: '/rsc-slot-jsx-args' - path: '/rsc-slot-jsx-args' - fullPath: '/rsc-slot-jsx-args' - preLoaderRoute: typeof RscSlotJsxArgsRouteImport + '/rsc-css-modules': { + id: '/rsc-css-modules' + path: '/rsc-css-modules' + fullPath: '/rsc-css-modules' + preLoaderRoute: typeof RscCssModulesRouteImport parentRoute: typeof rootRouteImport } - '/rsc-server-redirect': { - id: '/rsc-server-redirect' - path: '/rsc-server-redirect' - fullPath: '/rsc-server-redirect' - preLoaderRoute: typeof RscServerRedirectRouteImport + '/rsc-css-preload-complex': { + id: '/rsc-css-preload-complex' + path: '/rsc-css-preload-complex' + fullPath: '/rsc-css-preload-complex' + preLoaderRoute: typeof RscCssPreloadComplexRouteImport parentRoute: typeof rootRouteImport } - '/rsc-server-not-found': { - id: '/rsc-server-not-found' - path: '/rsc-server-not-found' - fullPath: '/rsc-server-not-found' - preLoaderRoute: typeof RscServerNotFoundRouteImport + '/rsc-deferred': { + id: '/rsc-deferred' + path: '/rsc-deferred' + fullPath: '/rsc-deferred' + preLoaderRoute: typeof RscDeferredRouteImport parentRoute: typeof rootRouteImport } - '/rsc-request-headers': { - id: '/rsc-request-headers' - path: '/rsc-request-headers' - fullPath: '/rsc-request-headers' - preLoaderRoute: typeof RscRequestHeadersRouteImport + '/rsc-deferred-component': { + id: '/rsc-deferred-component' + path: '/rsc-deferred-component' + fullPath: '/rsc-deferred-component' + preLoaderRoute: typeof RscDeferredComponentRouteImport parentRoute: typeof rootRouteImport } - '/rsc-react-cache': { - id: '/rsc-react-cache' - path: '/rsc-react-cache' - fullPath: '/rsc-react-cache' - preLoaderRoute: typeof RscReactCacheRouteImport + '/rsc-direct-loader': { + id: '/rsc-direct-loader' + path: '/rsc-direct-loader' + fullPath: '/rsc-direct-loader' + preLoaderRoute: typeof RscDirectLoaderRouteImport parentRoute: typeof rootRouteImport } - '/rsc-parallel': { - id: '/rsc-parallel' - path: '/rsc-parallel' - fullPath: '/rsc-parallel' - preLoaderRoute: typeof RscParallelRouteImport + '/rsc-error': { + id: '/rsc-error' + path: '/rsc-error' + fullPath: '/rsc-error' + preLoaderRoute: typeof RscErrorRouteImport parentRoute: typeof rootRouteImport } - '/rsc-nested-structure': { - id: '/rsc-nested-structure' - path: '/rsc-nested-structure' - fullPath: '/rsc-nested-structure' - preLoaderRoute: typeof RscNestedStructureRouteImport + '/rsc-external': { + id: '/rsc-external' + path: '/rsc-external' + fullPath: '/rsc-external' + preLoaderRoute: typeof RscExternalRouteImport parentRoute: typeof rootRouteImport } - '/rsc-nested': { - id: '/rsc-nested' - path: '/rsc-nested' - fullPath: '/rsc-nested' - preLoaderRoute: typeof RscNestedRouteImport + '/rsc-flight-api': { + id: '/rsc-flight-api' + path: '/rsc-flight-api' + fullPath: '/rsc-flight-api' + preLoaderRoute: typeof RscFlightApiRouteImport parentRoute: typeof rootRouteImport } - '/rsc-multi': { - id: '/rsc-multi' - path: '/rsc-multi' - fullPath: '/rsc-multi' - preLoaderRoute: typeof RscMultiRouteImport + '/rsc-forms': { + id: '/rsc-forms' + path: '/rsc-forms' + fullPath: '/rsc-forms' + preLoaderRoute: typeof RscFormsRouteImport parentRoute: typeof rootRouteImport } - '/rsc-link': { - id: '/rsc-link' - path: '/rsc-link' - fullPath: '/rsc-link' - preLoaderRoute: typeof RscLinkRouteImport + '/rsc-global-css': { + id: '/rsc-global-css' + path: '/rsc-global-css' + fullPath: '/rsc-global-css' + preLoaderRoute: typeof RscGlobalCssRouteImport parentRoute: typeof rootRouteImport } - '/rsc-large': { - id: '/rsc-large' - path: '/rsc-large' - fullPath: '/rsc-large' - preLoaderRoute: typeof RscLargeRouteImport + '/rsc-hydration': { + id: '/rsc-hydration' + path: '/rsc-hydration' + fullPath: '/rsc-hydration' + preLoaderRoute: typeof RscHydrationRouteImport parentRoute: typeof rootRouteImport } '/rsc-invalidation': { @@ -797,151 +790,158 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof RscInvalidationRouteImport parentRoute: typeof rootRouteImport } - '/rsc-hydration': { - id: '/rsc-hydration' - path: '/rsc-hydration' - fullPath: '/rsc-hydration' - preLoaderRoute: typeof RscHydrationRouteImport + '/rsc-large': { + id: '/rsc-large' + path: '/rsc-large' + fullPath: '/rsc-large' + preLoaderRoute: typeof RscLargeRouteImport parentRoute: typeof rootRouteImport } - '/rsc-global-css': { - id: '/rsc-global-css' - path: '/rsc-global-css' - fullPath: '/rsc-global-css' - preLoaderRoute: typeof RscGlobalCssRouteImport + '/rsc-link': { + id: '/rsc-link' + path: '/rsc-link' + fullPath: '/rsc-link' + preLoaderRoute: typeof RscLinkRouteImport parentRoute: typeof rootRouteImport } - '/rsc-forms': { - id: '/rsc-forms' - path: '/rsc-forms' - fullPath: '/rsc-forms' - preLoaderRoute: typeof RscFormsRouteImport + '/rsc-multi': { + id: '/rsc-multi' + path: '/rsc-multi' + fullPath: '/rsc-multi' + preLoaderRoute: typeof RscMultiRouteImport parentRoute: typeof rootRouteImport } - '/rsc-flight-api': { - id: '/rsc-flight-api' - path: '/rsc-flight-api' - fullPath: '/rsc-flight-api' - preLoaderRoute: typeof RscFlightApiRouteImport + '/rsc-nested': { + id: '/rsc-nested' + path: '/rsc-nested' + fullPath: '/rsc-nested' + preLoaderRoute: typeof RscNestedRouteImport parentRoute: typeof rootRouteImport } - '/rsc-external': { - id: '/rsc-external' - path: '/rsc-external' - fullPath: '/rsc-external' - preLoaderRoute: typeof RscExternalRouteImport + '/rsc-nested-structure': { + id: '/rsc-nested-structure' + path: '/rsc-nested-structure' + fullPath: '/rsc-nested-structure' + preLoaderRoute: typeof RscNestedStructureRouteImport parentRoute: typeof rootRouteImport } - '/rsc-error': { - id: '/rsc-error' - path: '/rsc-error' - fullPath: '/rsc-error' - preLoaderRoute: typeof RscErrorRouteImport + '/rsc-parallel': { + id: '/rsc-parallel' + path: '/rsc-parallel' + fullPath: '/rsc-parallel' + preLoaderRoute: typeof RscParallelRouteImport parentRoute: typeof rootRouteImport } - '/rsc-direct-loader': { - id: '/rsc-direct-loader' - path: '/rsc-direct-loader' - fullPath: '/rsc-direct-loader' - preLoaderRoute: typeof RscDirectLoaderRouteImport + '/rsc-react-cache': { + id: '/rsc-react-cache' + path: '/rsc-react-cache' + fullPath: '/rsc-react-cache' + preLoaderRoute: typeof RscReactCacheRouteImport parentRoute: typeof rootRouteImport } - '/rsc-deferred-component': { - id: '/rsc-deferred-component' - path: '/rsc-deferred-component' - fullPath: '/rsc-deferred-component' - preLoaderRoute: typeof RscDeferredComponentRouteImport + '/rsc-request-headers': { + id: '/rsc-request-headers' + path: '/rsc-request-headers' + fullPath: '/rsc-request-headers' + preLoaderRoute: typeof RscRequestHeadersRouteImport parentRoute: typeof rootRouteImport } - '/rsc-deferred': { - id: '/rsc-deferred' - path: '/rsc-deferred' - fullPath: '/rsc-deferred' - preLoaderRoute: typeof RscDeferredRouteImport + '/rsc-server-not-found': { + id: '/rsc-server-not-found' + path: '/rsc-server-not-found' + fullPath: '/rsc-server-not-found' + preLoaderRoute: typeof RscServerNotFoundRouteImport parentRoute: typeof rootRouteImport } - '/rsc-css-preload-complex': { - id: '/rsc-css-preload-complex' - path: '/rsc-css-preload-complex' - fullPath: '/rsc-css-preload-complex' - preLoaderRoute: typeof RscCssPreloadComplexRouteImport + '/rsc-server-redirect': { + id: '/rsc-server-redirect' + path: '/rsc-server-redirect' + fullPath: '/rsc-server-redirect' + preLoaderRoute: typeof RscServerRedirectRouteImport parentRoute: typeof rootRouteImport } - '/rsc-css-modules': { - id: '/rsc-css-modules' - path: '/rsc-css-modules' - fullPath: '/rsc-css-modules' - preLoaderRoute: typeof RscCssModulesRouteImport + '/rsc-slot-jsx-args': { + id: '/rsc-slot-jsx-args' + path: '/rsc-slot-jsx-args' + fullPath: '/rsc-slot-jsx-args' + preLoaderRoute: typeof RscSlotJsxArgsRouteImport parentRoute: typeof rootRouteImport } - '/rsc-css-autoinject': { - id: '/rsc-css-autoinject' - path: '/rsc-css-autoinject' - fullPath: '/rsc-css-autoinject' - preLoaderRoute: typeof RscCssAutoinjectRouteImport + '/rsc-slots': { + id: '/rsc-slots' + path: '/rsc-slots' + fullPath: '/rsc-slots' + preLoaderRoute: typeof RscSlotsRouteImport parentRoute: typeof rootRouteImport } - '/rsc-context': { - id: '/rsc-context' - path: '/rsc-context' - fullPath: '/rsc-context' - preLoaderRoute: typeof RscContextRouteImport + '/rsc-ssr-data-only': { + id: '/rsc-ssr-data-only' + path: '/rsc-ssr-data-only' + fullPath: '/rsc-ssr-data-only' + preLoaderRoute: typeof RscSsrDataOnlyRouteImport parentRoute: typeof rootRouteImport } - '/rsc-component-slot': { - id: '/rsc-component-slot' - path: '/rsc-component-slot' - fullPath: '/rsc-component-slot' - preLoaderRoute: typeof RscComponentSlotRouteImport + '/rsc-ssr-false': { + id: '/rsc-ssr-false' + path: '/rsc-ssr-false' + fullPath: '/rsc-ssr-false' + preLoaderRoute: typeof RscSsrFalseRouteImport parentRoute: typeof rootRouteImport } - '/rsc-client-preload': { - id: '/rsc-client-preload' - path: '/rsc-client-preload' - fullPath: '/rsc-client-preload' - preLoaderRoute: typeof RscClientPreloadRouteImport + '/rsc-stream-generator': { + id: '/rsc-stream-generator' + path: '/rsc-stream-generator' + fullPath: '/rsc-stream-generator' + preLoaderRoute: typeof RscStreamGeneratorRouteImport parentRoute: typeof rootRouteImport } - '/rsc-caching': { - id: '/rsc-caching' - path: '/rsc-caching' - fullPath: '/rsc-caching' - preLoaderRoute: typeof RscCachingRouteImport + '/rsc-stream-loader': { + id: '/rsc-stream-loader' + path: '/rsc-stream-loader' + fullPath: '/rsc-stream-loader' + preLoaderRoute: typeof RscStreamLoaderRouteImport parentRoute: typeof rootRouteImport } - '/rsc-bundle': { - id: '/rsc-bundle' - path: '/rsc-bundle' - fullPath: '/rsc-bundle' - preLoaderRoute: typeof RscBundleRouteImport + '/rsc-stream-readable': { + id: '/rsc-stream-readable' + path: '/rsc-stream-readable' + fullPath: '/rsc-stream-readable' + preLoaderRoute: typeof RscStreamReadableRouteImport parentRoute: typeof rootRouteImport } - '/rsc-basic': { - id: '/rsc-basic' - path: '/rsc-basic' - fullPath: '/rsc-basic' - preLoaderRoute: typeof RscBasicRouteImport + '/rsc-streaming': { + id: '/rsc-streaming' + path: '/rsc-streaming' + fullPath: '/rsc-streaming' + preLoaderRoute: typeof RscStreamingRouteImport parentRoute: typeof rootRouteImport } - '/rsc-async-bundle': { - id: '/rsc-async-bundle' - path: '/rsc-async-bundle' - fullPath: '/rsc-async-bundle' - preLoaderRoute: typeof RscAsyncBundleRouteImport + '/rsc-suspense': { + id: '/rsc-suspense' + path: '/rsc-suspense' + fullPath: '/rsc-suspense' + preLoaderRoute: typeof RscSuspenseRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/rsc-tree': { + id: '/rsc-tree' + path: '/rsc-tree' + fullPath: '/rsc-tree' + preLoaderRoute: typeof RscTreeRouteImport parentRoute: typeof rootRouteImport } - '/rsc-param/': { - id: '/rsc-param/' - path: '/rsc-param' - fullPath: '/rsc-param/' - preLoaderRoute: typeof RscParamIndexRouteImport + '/rsc-use-server-fn': { + id: '/rsc-use-server-fn' + path: '/rsc-use-server-fn' + fullPath: '/rsc-use-server-fn' + preLoaderRoute: typeof RscUseServerFnRouteImport + parentRoute: typeof rootRouteImport + } + '/api/rsc-flight': { + id: '/api/rsc-flight' + path: '/api/rsc-flight' + fullPath: '/api/rsc-flight' + preLoaderRoute: typeof ApiRscFlightRouteImport parentRoute: typeof rootRouteImport } '/rsc-css-conditional/': { @@ -951,13 +951,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof RscCssConditionalIndexRouteImport parentRoute: typeof rootRouteImport } - '/rsc-param/$id': { - id: '/rsc-param/$id' - path: '/rsc-param/$id' - fullPath: '/rsc-param/$id' - preLoaderRoute: typeof RscParamIdRouteImport - parentRoute: typeof rootRouteImport - } '/rsc-css-conditional/$branch': { id: '/rsc-css-conditional/$branch' path: '/rsc-css-conditional/$branch' @@ -965,11 +958,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof RscCssConditionalBranchRouteImport parentRoute: typeof rootRouteImport } - '/api/rsc-flight': { - id: '/api/rsc-flight' - path: '/api/rsc-flight' - fullPath: '/api/rsc-flight' - preLoaderRoute: typeof ApiRscFlightRouteImport + '/rsc-param/': { + id: '/rsc-param/' + path: '/rsc-param' + fullPath: '/rsc-param/' + preLoaderRoute: typeof RscParamIndexRouteImport + parentRoute: typeof rootRouteImport + } + '/rsc-param/$id': { + id: '/rsc-param/$id' + path: '/rsc-param/$id' + fullPath: '/rsc-param/$id' + preLoaderRoute: typeof RscParamIdRouteImport parentRoute: typeof rootRouteImport } } diff --git a/e2e/react-start/scroll-restoration/src/routeTree.gen.ts b/e2e/react-start/scroll-restoration/src/routeTree.gen.ts index 7acedcb1d7..f25d1f25c0 100644 --- a/e2e/react-start/scroll-restoration/src/routeTree.gen.ts +++ b/e2e/react-start/scroll-restoration/src/routeTree.gen.ts @@ -10,20 +10,20 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' -import { Route as testsWithSearchRouteImport } from './routes/(tests)/with-search' -import { Route as testsWithLoaderRouteImport } from './routes/(tests)/with-loader' -import { Route as testsSsrScrollKeyRouteImport } from './routes/(tests)/ssr-scroll-key' -import { Route as testsResetScrollFalseCRouteImport } from './routes/(tests)/reset-scroll-false-c' -import { Route as testsResetScrollFalseBRouteImport } from './routes/(tests)/reset-scroll-false-b' -import { Route as testsResetScrollFalseARouteImport } from './routes/(tests)/reset-scroll-false-a' -import { Route as testsNormalPageRouteImport } from './routes/(tests)/normal-page' -import { Route as testsNestedScrollSearchRouteImport } from './routes/(tests)/nested-scroll-search' -import { Route as testsNestedScrollCarryOverBRouteImport } from './routes/(tests)/nested-scroll-carry-over-b' -import { Route as testsNestedScrollCarryOverARouteImport } from './routes/(tests)/nested-scroll-carry-over-a' -import { Route as testsNestedScrollAwayRouteImport } from './routes/(tests)/nested-scroll-away' -import { Route as testsIssue7687RouteImport } from './routes/(tests)/issue-7687' -import { Route as testsHashScrollReproRouteImport } from './routes/(tests)/hash-scroll-repro' import { Route as testsHashScrollAboutRouteImport } from './routes/(tests)/hash-scroll-about' +import { Route as testsHashScrollReproRouteImport } from './routes/(tests)/hash-scroll-repro' +import { Route as testsIssue7687RouteImport } from './routes/(tests)/issue-7687' +import { Route as testsNestedScrollAwayRouteImport } from './routes/(tests)/nested-scroll-away' +import { Route as testsNestedScrollCarryOverARouteImport } from './routes/(tests)/nested-scroll-carry-over-a' +import { Route as testsNestedScrollCarryOverBRouteImport } from './routes/(tests)/nested-scroll-carry-over-b' +import { Route as testsNestedScrollSearchRouteImport } from './routes/(tests)/nested-scroll-search' +import { Route as testsNormalPageRouteImport } from './routes/(tests)/normal-page' +import { Route as testsResetScrollFalseARouteImport } from './routes/(tests)/reset-scroll-false-a' +import { Route as testsResetScrollFalseBRouteImport } from './routes/(tests)/reset-scroll-false-b' +import { Route as testsResetScrollFalseCRouteImport } from './routes/(tests)/reset-scroll-false-c' +import { Route as testsSsrScrollKeyRouteImport } from './routes/(tests)/ssr-scroll-key' +import { Route as testsWithLoaderRouteImport } from './routes/(tests)/with-loader' +import { Route as testsWithSearchRouteImport } from './routes/(tests)/with-search' import { Route as testsIssue7687IndexRouteImport } from './routes/(tests)/issue-7687.index' import { Route as testsIssue7687DetailRouteImport } from './routes/(tests)/issue-7687.detail' @@ -32,34 +32,41 @@ const IndexRoute = IndexRouteImport.update({ path: '/', getParentRoute: () => rootRouteImport, } as any) -const testsWithSearchRoute = testsWithSearchRouteImport.update({ - id: '/(tests)/with-search', - path: '/with-search', - getParentRoute: () => rootRouteImport, -} as any) -const testsWithLoaderRoute = testsWithLoaderRouteImport.update({ - id: '/(tests)/with-loader', - path: '/with-loader', +const testsHashScrollAboutRoute = testsHashScrollAboutRouteImport.update({ + id: '/(tests)/hash-scroll-about', + path: '/hash-scroll-about', getParentRoute: () => rootRouteImport, } as any) -const testsSsrScrollKeyRoute = testsSsrScrollKeyRouteImport.update({ - id: '/(tests)/ssr-scroll-key', - path: '/ssr-scroll-key', +const testsHashScrollReproRoute = testsHashScrollReproRouteImport.update({ + id: '/(tests)/hash-scroll-repro', + path: '/hash-scroll-repro', getParentRoute: () => rootRouteImport, } as any) -const testsResetScrollFalseCRoute = testsResetScrollFalseCRouteImport.update({ - id: '/(tests)/reset-scroll-false-c', - path: '/reset-scroll-false-c', +const testsIssue7687Route = testsIssue7687RouteImport.update({ + id: '/(tests)/issue-7687', + path: '/issue-7687', getParentRoute: () => rootRouteImport, } as any) -const testsResetScrollFalseBRoute = testsResetScrollFalseBRouteImport.update({ - id: '/(tests)/reset-scroll-false-b', - path: '/reset-scroll-false-b', +const testsNestedScrollAwayRoute = testsNestedScrollAwayRouteImport.update({ + id: '/(tests)/nested-scroll-away', + path: '/nested-scroll-away', getParentRoute: () => rootRouteImport, } as any) -const testsResetScrollFalseARoute = testsResetScrollFalseARouteImport.update({ - id: '/(tests)/reset-scroll-false-a', - path: '/reset-scroll-false-a', +const testsNestedScrollCarryOverARoute = + testsNestedScrollCarryOverARouteImport.update({ + id: '/(tests)/nested-scroll-carry-over-a', + path: '/nested-scroll-carry-over-a', + getParentRoute: () => rootRouteImport, + } as any) +const testsNestedScrollCarryOverBRoute = + testsNestedScrollCarryOverBRouteImport.update({ + id: '/(tests)/nested-scroll-carry-over-b', + path: '/nested-scroll-carry-over-b', + getParentRoute: () => rootRouteImport, + } as any) +const testsNestedScrollSearchRoute = testsNestedScrollSearchRouteImport.update({ + id: '/(tests)/nested-scroll-search', + path: '/nested-scroll-search', getParentRoute: () => rootRouteImport, } as any) const testsNormalPageRoute = testsNormalPageRouteImport.update({ @@ -67,41 +74,34 @@ const testsNormalPageRoute = testsNormalPageRouteImport.update({ path: '/normal-page', getParentRoute: () => rootRouteImport, } as any) -const testsNestedScrollSearchRoute = testsNestedScrollSearchRouteImport.update({ - id: '/(tests)/nested-scroll-search', - path: '/nested-scroll-search', +const testsResetScrollFalseARoute = testsResetScrollFalseARouteImport.update({ + id: '/(tests)/reset-scroll-false-a', + path: '/reset-scroll-false-a', getParentRoute: () => rootRouteImport, } as any) -const testsNestedScrollCarryOverBRoute = - testsNestedScrollCarryOverBRouteImport.update({ - id: '/(tests)/nested-scroll-carry-over-b', - path: '/nested-scroll-carry-over-b', - getParentRoute: () => rootRouteImport, - } as any) -const testsNestedScrollCarryOverARoute = - testsNestedScrollCarryOverARouteImport.update({ - id: '/(tests)/nested-scroll-carry-over-a', - path: '/nested-scroll-carry-over-a', - getParentRoute: () => rootRouteImport, - } as any) -const testsNestedScrollAwayRoute = testsNestedScrollAwayRouteImport.update({ - id: '/(tests)/nested-scroll-away', - path: '/nested-scroll-away', +const testsResetScrollFalseBRoute = testsResetScrollFalseBRouteImport.update({ + id: '/(tests)/reset-scroll-false-b', + path: '/reset-scroll-false-b', getParentRoute: () => rootRouteImport, } as any) -const testsIssue7687Route = testsIssue7687RouteImport.update({ - id: '/(tests)/issue-7687', - path: '/issue-7687', +const testsResetScrollFalseCRoute = testsResetScrollFalseCRouteImport.update({ + id: '/(tests)/reset-scroll-false-c', + path: '/reset-scroll-false-c', getParentRoute: () => rootRouteImport, } as any) -const testsHashScrollReproRoute = testsHashScrollReproRouteImport.update({ - id: '/(tests)/hash-scroll-repro', - path: '/hash-scroll-repro', +const testsSsrScrollKeyRoute = testsSsrScrollKeyRouteImport.update({ + id: '/(tests)/ssr-scroll-key', + path: '/ssr-scroll-key', getParentRoute: () => rootRouteImport, } as any) -const testsHashScrollAboutRoute = testsHashScrollAboutRouteImport.update({ - id: '/(tests)/hash-scroll-about', - path: '/hash-scroll-about', +const testsWithLoaderRoute = testsWithLoaderRouteImport.update({ + id: '/(tests)/with-loader', + path: '/with-loader', + getParentRoute: () => rootRouteImport, +} as any) +const testsWithSearchRoute = testsWithSearchRouteImport.update({ + id: '/(tests)/with-search', + path: '/with-search', getParentRoute: () => rootRouteImport, } as any) const testsIssue7687IndexRoute = testsIssue7687IndexRouteImport.update({ @@ -258,53 +258,46 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/with-search': { - id: '/(tests)/with-search' - path: '/with-search' - fullPath: '/with-search' - preLoaderRoute: typeof testsWithSearchRouteImport - parentRoute: typeof rootRouteImport - } - '/(tests)/with-loader': { - id: '/(tests)/with-loader' - path: '/with-loader' - fullPath: '/with-loader' - preLoaderRoute: typeof testsWithLoaderRouteImport + '/(tests)/hash-scroll-about': { + id: '/(tests)/hash-scroll-about' + path: '/hash-scroll-about' + fullPath: '/hash-scroll-about' + preLoaderRoute: typeof testsHashScrollAboutRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/ssr-scroll-key': { - id: '/(tests)/ssr-scroll-key' - path: '/ssr-scroll-key' - fullPath: '/ssr-scroll-key' - preLoaderRoute: typeof testsSsrScrollKeyRouteImport + '/(tests)/hash-scroll-repro': { + id: '/(tests)/hash-scroll-repro' + path: '/hash-scroll-repro' + fullPath: '/hash-scroll-repro' + preLoaderRoute: typeof testsHashScrollReproRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/reset-scroll-false-c': { - id: '/(tests)/reset-scroll-false-c' - path: '/reset-scroll-false-c' - fullPath: '/reset-scroll-false-c' - preLoaderRoute: typeof testsResetScrollFalseCRouteImport + '/(tests)/issue-7687': { + id: '/(tests)/issue-7687' + path: '/issue-7687' + fullPath: '/issue-7687' + preLoaderRoute: typeof testsIssue7687RouteImport parentRoute: typeof rootRouteImport } - '/(tests)/reset-scroll-false-b': { - id: '/(tests)/reset-scroll-false-b' - path: '/reset-scroll-false-b' - fullPath: '/reset-scroll-false-b' - preLoaderRoute: typeof testsResetScrollFalseBRouteImport + '/(tests)/nested-scroll-away': { + id: '/(tests)/nested-scroll-away' + path: '/nested-scroll-away' + fullPath: '/nested-scroll-away' + preLoaderRoute: typeof testsNestedScrollAwayRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/reset-scroll-false-a': { - id: '/(tests)/reset-scroll-false-a' - path: '/reset-scroll-false-a' - fullPath: '/reset-scroll-false-a' - preLoaderRoute: typeof testsResetScrollFalseARouteImport + '/(tests)/nested-scroll-carry-over-a': { + id: '/(tests)/nested-scroll-carry-over-a' + path: '/nested-scroll-carry-over-a' + fullPath: '/nested-scroll-carry-over-a' + preLoaderRoute: typeof testsNestedScrollCarryOverARouteImport parentRoute: typeof rootRouteImport } - '/(tests)/normal-page': { - id: '/(tests)/normal-page' - path: '/normal-page' - fullPath: '/normal-page' - preLoaderRoute: typeof testsNormalPageRouteImport + '/(tests)/nested-scroll-carry-over-b': { + id: '/(tests)/nested-scroll-carry-over-b' + path: '/nested-scroll-carry-over-b' + fullPath: '/nested-scroll-carry-over-b' + preLoaderRoute: typeof testsNestedScrollCarryOverBRouteImport parentRoute: typeof rootRouteImport } '/(tests)/nested-scroll-search': { @@ -314,46 +307,53 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof testsNestedScrollSearchRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/nested-scroll-carry-over-b': { - id: '/(tests)/nested-scroll-carry-over-b' - path: '/nested-scroll-carry-over-b' - fullPath: '/nested-scroll-carry-over-b' - preLoaderRoute: typeof testsNestedScrollCarryOverBRouteImport + '/(tests)/normal-page': { + id: '/(tests)/normal-page' + path: '/normal-page' + fullPath: '/normal-page' + preLoaderRoute: typeof testsNormalPageRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/nested-scroll-carry-over-a': { - id: '/(tests)/nested-scroll-carry-over-a' - path: '/nested-scroll-carry-over-a' - fullPath: '/nested-scroll-carry-over-a' - preLoaderRoute: typeof testsNestedScrollCarryOverARouteImport + '/(tests)/reset-scroll-false-a': { + id: '/(tests)/reset-scroll-false-a' + path: '/reset-scroll-false-a' + fullPath: '/reset-scroll-false-a' + preLoaderRoute: typeof testsResetScrollFalseARouteImport parentRoute: typeof rootRouteImport } - '/(tests)/nested-scroll-away': { - id: '/(tests)/nested-scroll-away' - path: '/nested-scroll-away' - fullPath: '/nested-scroll-away' - preLoaderRoute: typeof testsNestedScrollAwayRouteImport + '/(tests)/reset-scroll-false-b': { + id: '/(tests)/reset-scroll-false-b' + path: '/reset-scroll-false-b' + fullPath: '/reset-scroll-false-b' + preLoaderRoute: typeof testsResetScrollFalseBRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/issue-7687': { - id: '/(tests)/issue-7687' - path: '/issue-7687' - fullPath: '/issue-7687' - preLoaderRoute: typeof testsIssue7687RouteImport + '/(tests)/reset-scroll-false-c': { + id: '/(tests)/reset-scroll-false-c' + path: '/reset-scroll-false-c' + fullPath: '/reset-scroll-false-c' + preLoaderRoute: typeof testsResetScrollFalseCRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/hash-scroll-repro': { - id: '/(tests)/hash-scroll-repro' - path: '/hash-scroll-repro' - fullPath: '/hash-scroll-repro' - preLoaderRoute: typeof testsHashScrollReproRouteImport + '/(tests)/ssr-scroll-key': { + id: '/(tests)/ssr-scroll-key' + path: '/ssr-scroll-key' + fullPath: '/ssr-scroll-key' + preLoaderRoute: typeof testsSsrScrollKeyRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/hash-scroll-about': { - id: '/(tests)/hash-scroll-about' - path: '/hash-scroll-about' - fullPath: '/hash-scroll-about' - preLoaderRoute: typeof testsHashScrollAboutRouteImport + '/(tests)/with-loader': { + id: '/(tests)/with-loader' + path: '/with-loader' + fullPath: '/with-loader' + preLoaderRoute: typeof testsWithLoaderRouteImport + parentRoute: typeof rootRouteImport + } + '/(tests)/with-search': { + id: '/(tests)/with-search' + path: '/with-search' + fullPath: '/with-search' + preLoaderRoute: typeof testsWithSearchRouteImport parentRoute: typeof rootRouteImport } '/(tests)/issue-7687/': { diff --git a/e2e/react-start/selective-ssr/src/routeTree.gen.ts b/e2e/react-start/selective-ssr/src/routeTree.gen.ts index 188cd5c91a..1bba3ef326 100644 --- a/e2e/react-start/selective-ssr/src/routeTree.gen.ts +++ b/e2e/react-start/selective-ssr/src/routeTree.gen.ts @@ -9,20 +9,20 @@ // 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 PostsRouteImport } from './routes/posts' import { Route as IndexRouteImport } from './routes/index' +import { Route as PostsRouteImport } from './routes/posts' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', + getParentRoute: () => rootRouteImport, +} as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', @@ -60,13 +60,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -74,6 +67,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport + parentRoute: typeof rootRouteImport + } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' diff --git a/e2e/react-start/serialization-adapters/src/routeTree.gen.ts b/e2e/react-start/serialization-adapters/src/routeTree.gen.ts index 3536724473..1b21d4200b 100644 --- a/e2e/react-start/serialization-adapters/src/routeTree.gen.ts +++ b/e2e/react-start/serialization-adapters/src/routeTree.gen.ts @@ -10,26 +10,33 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' -import { Route as SsrStreamRouteImport } from './routes/ssr/stream' -import { Route as SsrNestedRouteImport } from './routes/ssr/nested' -import { Route as SsrDataOnlyRouteImport } from './routes/ssr/data-only' -import { Route as ServerFunctionNestedRouteImport } from './routes/server-function/nested' -import { Route as ServerFunctionLateRawStreamRouteImport } from './routes/server-function/late-raw-stream' import { Route as ServerFunctionCustomErrorRouteImport } from './routes/server-function/custom-error' +import { Route as ServerFunctionLateRawStreamRouteImport } from './routes/server-function/late-raw-stream' +import { Route as ServerFunctionNestedRouteImport } from './routes/server-function/nested' +import { Route as SsrDataOnlyRouteImport } from './routes/ssr/data-only' +import { Route as SsrNestedRouteImport } from './routes/ssr/nested' +import { Route as SsrStreamRouteImport } from './routes/ssr/stream' const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) -const SsrStreamRoute = SsrStreamRouteImport.update({ - id: '/ssr/stream', - path: '/ssr/stream', - getParentRoute: () => rootRouteImport, -} as any) -const SsrNestedRoute = SsrNestedRouteImport.update({ - id: '/ssr/nested', - path: '/ssr/nested', +const ServerFunctionCustomErrorRoute = + ServerFunctionCustomErrorRouteImport.update({ + id: '/server-function/custom-error', + path: '/server-function/custom-error', + getParentRoute: () => rootRouteImport, + } as any) +const ServerFunctionLateRawStreamRoute = + ServerFunctionLateRawStreamRouteImport.update({ + id: '/server-function/late-raw-stream', + path: '/server-function/late-raw-stream', + getParentRoute: () => rootRouteImport, + } as any) +const ServerFunctionNestedRoute = ServerFunctionNestedRouteImport.update({ + id: '/server-function/nested', + path: '/server-function/nested', getParentRoute: () => rootRouteImport, } as any) const SsrDataOnlyRoute = SsrDataOnlyRouteImport.update({ @@ -37,23 +44,16 @@ const SsrDataOnlyRoute = SsrDataOnlyRouteImport.update({ path: '/ssr/data-only', getParentRoute: () => rootRouteImport, } as any) -const ServerFunctionNestedRoute = ServerFunctionNestedRouteImport.update({ - id: '/server-function/nested', - path: '/server-function/nested', +const SsrNestedRoute = SsrNestedRouteImport.update({ + id: '/ssr/nested', + path: '/ssr/nested', + getParentRoute: () => rootRouteImport, +} as any) +const SsrStreamRoute = SsrStreamRouteImport.update({ + id: '/ssr/stream', + path: '/ssr/stream', getParentRoute: () => rootRouteImport, } as any) -const ServerFunctionLateRawStreamRoute = - ServerFunctionLateRawStreamRouteImport.update({ - id: '/server-function/late-raw-stream', - path: '/server-function/late-raw-stream', - getParentRoute: () => rootRouteImport, - } as any) -const ServerFunctionCustomErrorRoute = - ServerFunctionCustomErrorRouteImport.update({ - id: '/server-function/custom-error', - path: '/server-function/custom-error', - getParentRoute: () => rootRouteImport, - } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -132,25 +132,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/ssr/stream': { - id: '/ssr/stream' - path: '/ssr/stream' - fullPath: '/ssr/stream' - preLoaderRoute: typeof SsrStreamRouteImport - parentRoute: typeof rootRouteImport - } - '/ssr/nested': { - id: '/ssr/nested' - path: '/ssr/nested' - fullPath: '/ssr/nested' - preLoaderRoute: typeof SsrNestedRouteImport + '/server-function/custom-error': { + id: '/server-function/custom-error' + path: '/server-function/custom-error' + fullPath: '/server-function/custom-error' + preLoaderRoute: typeof ServerFunctionCustomErrorRouteImport parentRoute: typeof rootRouteImport } - '/ssr/data-only': { - id: '/ssr/data-only' - path: '/ssr/data-only' - fullPath: '/ssr/data-only' - preLoaderRoute: typeof SsrDataOnlyRouteImport + '/server-function/late-raw-stream': { + id: '/server-function/late-raw-stream' + path: '/server-function/late-raw-stream' + fullPath: '/server-function/late-raw-stream' + preLoaderRoute: typeof ServerFunctionLateRawStreamRouteImport parentRoute: typeof rootRouteImport } '/server-function/nested': { @@ -160,18 +153,25 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ServerFunctionNestedRouteImport parentRoute: typeof rootRouteImport } - '/server-function/late-raw-stream': { - id: '/server-function/late-raw-stream' - path: '/server-function/late-raw-stream' - fullPath: '/server-function/late-raw-stream' - preLoaderRoute: typeof ServerFunctionLateRawStreamRouteImport + '/ssr/data-only': { + id: '/ssr/data-only' + path: '/ssr/data-only' + fullPath: '/ssr/data-only' + preLoaderRoute: typeof SsrDataOnlyRouteImport parentRoute: typeof rootRouteImport } - '/server-function/custom-error': { - id: '/server-function/custom-error' - path: '/server-function/custom-error' - fullPath: '/server-function/custom-error' - preLoaderRoute: typeof ServerFunctionCustomErrorRouteImport + '/ssr/nested': { + id: '/ssr/nested' + path: '/ssr/nested' + fullPath: '/ssr/nested' + preLoaderRoute: typeof SsrNestedRouteImport + parentRoute: typeof rootRouteImport + } + '/ssr/stream': { + id: '/ssr/stream' + path: '/ssr/stream' + fullPath: '/ssr/stream' + preLoaderRoute: typeof SsrStreamRouteImport parentRoute: typeof rootRouteImport } } diff --git a/e2e/react-start/server-functions-global-middleware/src/routeTree.gen.ts b/e2e/react-start/server-functions-global-middleware/src/routeTree.gen.ts index 6fb74e28a2..739df05d7a 100644 --- a/e2e/react-start/server-functions-global-middleware/src/routeTree.gen.ts +++ b/e2e/react-start/server-functions-global-middleware/src/routeTree.gen.ts @@ -9,20 +9,20 @@ // 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 SimpleRouteImport } from './routes/simple' -import { Route as PathnameMiddlewareRouteImport } from './routes/pathname-middleware' -import { Route as MultipleServerFunctionsRouteImport } from './routes/multiple-server-functions' -import { Route as ContextCollisionRouteImport } from './routes/context-collision' import { Route as IndexRouteImport } from './routes/index' +import { Route as ContextCollisionRouteImport } from './routes/context-collision' +import { Route as MultipleServerFunctionsRouteImport } from './routes/multiple-server-functions' +import { Route as PathnameMiddlewareRouteImport } from './routes/pathname-middleware' +import { Route as SimpleRouteImport } from './routes/simple' -const SimpleRoute = SimpleRouteImport.update({ - id: '/simple', - path: '/simple', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const PathnameMiddlewareRoute = PathnameMiddlewareRouteImport.update({ - id: '/pathname-middleware', - path: '/pathname-middleware', +const ContextCollisionRoute = ContextCollisionRouteImport.update({ + id: '/context-collision', + path: '/context-collision', getParentRoute: () => rootRouteImport, } as any) const MultipleServerFunctionsRoute = MultipleServerFunctionsRouteImport.update({ @@ -30,14 +30,14 @@ const MultipleServerFunctionsRoute = MultipleServerFunctionsRouteImport.update({ path: '/multiple-server-functions', getParentRoute: () => rootRouteImport, } as any) -const ContextCollisionRoute = ContextCollisionRouteImport.update({ - id: '/context-collision', - path: '/context-collision', +const PathnameMiddlewareRoute = PathnameMiddlewareRouteImport.update({ + id: '/pathname-middleware', + path: '/pathname-middleware', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const SimpleRoute = SimpleRouteImport.update({ + id: '/simple', + path: '/simple', getParentRoute: () => rootRouteImport, } as any) @@ -97,18 +97,18 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/simple': { - id: '/simple' - path: '/simple' - fullPath: '/simple' - preLoaderRoute: typeof SimpleRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/pathname-middleware': { - id: '/pathname-middleware' - path: '/pathname-middleware' - fullPath: '/pathname-middleware' - preLoaderRoute: typeof PathnameMiddlewareRouteImport + '/context-collision': { + id: '/context-collision' + path: '/context-collision' + fullPath: '/context-collision' + preLoaderRoute: typeof ContextCollisionRouteImport parentRoute: typeof rootRouteImport } '/multiple-server-functions': { @@ -118,18 +118,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof MultipleServerFunctionsRouteImport parentRoute: typeof rootRouteImport } - '/context-collision': { - id: '/context-collision' - path: '/context-collision' - fullPath: '/context-collision' - preLoaderRoute: typeof ContextCollisionRouteImport + '/pathname-middleware': { + id: '/pathname-middleware' + path: '/pathname-middleware' + fullPath: '/pathname-middleware' + preLoaderRoute: typeof PathnameMiddlewareRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/simple': { + id: '/simple' + path: '/simple' + fullPath: '/simple' + preLoaderRoute: typeof SimpleRouteImport parentRoute: typeof rootRouteImport } } diff --git a/e2e/react-start/server-functions/src/routeTree.gen.ts b/e2e/react-start/server-functions/src/routeTree.gen.ts index 249a9c42ce..459d8fd595 100644 --- a/e2e/react-start/server-functions/src/routeTree.gen.ts +++ b/e2e/react-start/server-functions/src/routeTree.gen.ts @@ -9,94 +9,84 @@ // 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 SubmitPostFormdataRouteImport } from './routes/submit-post-formdata' -import { Route as StatusRouteImport } from './routes/status' -import { Route as ServerOnlyFnRouteImport } from './routes/server-only-fn' -import { Route as ServerFnInClientOnlyFnRouteImport } from './routes/server-fn-in-client-only-fn' -import { Route as SerializeFormDataRouteImport } from './routes/serialize-form-data' -import { Route as ReturnNullRouteImport } from './routes/return-null' -import { Route as RawResponseRouteImport } from './routes/raw-response' -import { Route as MultipartRouteImport } from './routes/multipart' -import { Route as IsomorphicFnsRouteImport } from './routes/isomorphic-fns' -import { Route as HeadersRouteImport } from './routes/headers' -import { Route as FormdataContextRouteImport } from './routes/formdata-context' -import { Route as EnvOnlyRouteImport } from './routes/env-only' -import { Route as DeadCodePreserveRouteImport } from './routes/dead-code-preserve' -import { Route as CustomFetchRouteImport } from './routes/custom-fetch' -import { Route as ConsistentRouteImport } from './routes/consistent' -import { Route as AsyncValidationRouteImport } from './routes/async-validation' import { Route as IndexRouteImport } from './routes/index' -import { Route as RedirectTestIndexRouteImport } from './routes/redirect-test/index' -import { Route as RedirectTestSsrIndexRouteImport } from './routes/redirect-test-ssr/index' -import { Route as PrimitivesIndexRouteImport } from './routes/primitives/index' -import { Route as MiddlewareIndexRouteImport } from './routes/middleware/index' -import { Route as MethodNotAllowedIndexRouteImport } from './routes/method-not-allowed/index' -import { Route as FunctionMethodIndexRouteImport } from './routes/function-method/index' -import { Route as FunctionMetadataIndexRouteImport } from './routes/function-metadata/index' -import { Route as FormdataRedirectIndexRouteImport } from './routes/formdata-redirect/index' -import { Route as FactoryIndexRouteImport } from './routes/factory/index' -import { Route as CookiesIndexRouteImport } from './routes/cookies/index' +import { Route as AsyncValidationRouteImport } from './routes/async-validation' +import { Route as ConsistentRouteImport } from './routes/consistent' +import { Route as CustomFetchRouteImport } from './routes/custom-fetch' +import { Route as DeadCodePreserveRouteImport } from './routes/dead-code-preserve' +import { Route as EnvOnlyRouteImport } from './routes/env-only' +import { Route as FormdataContextRouteImport } from './routes/formdata-context' +import { Route as HeadersRouteImport } from './routes/headers' +import { Route as IsomorphicFnsRouteImport } from './routes/isomorphic-fns' +import { Route as MultipartRouteImport } from './routes/multipart' +import { Route as RawResponseRouteImport } from './routes/raw-response' +import { Route as ReturnNullRouteImport } from './routes/return-null' +import { Route as SerializeFormDataRouteImport } from './routes/serialize-form-data' +import { Route as ServerFnInClientOnlyFnRouteImport } from './routes/server-fn-in-client-only-fn' +import { Route as ServerOnlyFnRouteImport } from './routes/server-only-fn' +import { Route as StatusRouteImport } from './routes/status' +import { Route as SubmitPostFormdataRouteImport } from './routes/submit-post-formdata' import { Route as AbortSignalIndexRouteImport } from './routes/abort-signal/index' -import { Route as RedirectTestTargetRouteImport } from './routes/redirect-test/target' -import { Route as RedirectTestSsrTargetRouteImport } from './routes/redirect-test-ssr/target' -import { Route as MiddlewareUnhandledExceptionRouteImport } from './routes/middleware/unhandled-exception' -import { Route as MiddlewareServerImportMiddlewareRouteImport } from './routes/middleware/server-import-middleware' -import { Route as MiddlewareSendServerFnRouteImport } from './routes/middleware/send-serverFn' -import { Route as MiddlewareRequestMiddlewareRouteImport } from './routes/middleware/request-middleware' -import { Route as MiddlewareMiddlewareFactoryRouteImport } from './routes/middleware/middleware-factory' -import { Route as MiddlewareFunctionMetadataRouteImport } from './routes/middleware/function-metadata' -import { Route as MiddlewareClientMiddlewareRouterRouteImport } from './routes/middleware/client-middleware-router' -import { Route as MiddlewareCatchHandlerErrorRouteImport } from './routes/middleware/catch-handler-error' -import { Route as MethodNotAllowedMethodRouteImport } from './routes/method-not-allowed/$method' -import { Route as CookiesSetRouteImport } from './routes/cookies/set' import { Route as AbortSignalMethodRouteImport } from './routes/abort-signal/$method' +import { Route as CookiesIndexRouteImport } from './routes/cookies/index' +import { Route as CookiesSetRouteImport } from './routes/cookies/set' +import { Route as FactoryIndexRouteImport } from './routes/factory/index' +import { Route as FormdataRedirectIndexRouteImport } from './routes/formdata-redirect/index' +import { Route as FunctionMetadataIndexRouteImport } from './routes/function-metadata/index' +import { Route as FunctionMethodIndexRouteImport } from './routes/function-method/index' +import { Route as MethodNotAllowedIndexRouteImport } from './routes/method-not-allowed/index' +import { Route as MethodNotAllowedMethodRouteImport } from './routes/method-not-allowed/$method' +import { Route as MiddlewareIndexRouteImport } from './routes/middleware/index' +import { Route as MiddlewareCatchHandlerErrorRouteImport } from './routes/middleware/catch-handler-error' +import { Route as MiddlewareClientMiddlewareRouterRouteImport } from './routes/middleware/client-middleware-router' +import { Route as MiddlewareFunctionMetadataRouteImport } from './routes/middleware/function-metadata' +import { Route as MiddlewareMiddlewareFactoryRouteImport } from './routes/middleware/middleware-factory' +import { Route as MiddlewareRequestMiddlewareRouteImport } from './routes/middleware/request-middleware' +import { Route as MiddlewareSendServerFnRouteImport } from './routes/middleware/send-serverFn' +import { Route as MiddlewareServerImportMiddlewareRouteImport } from './routes/middleware/server-import-middleware' +import { Route as MiddlewareUnhandledExceptionRouteImport } from './routes/middleware/unhandled-exception' +import { Route as PrimitivesIndexRouteImport } from './routes/primitives/index' +import { Route as RedirectTestSsrIndexRouteImport } from './routes/redirect-test-ssr/index' +import { Route as RedirectTestSsrTargetRouteImport } from './routes/redirect-test-ssr/target' +import { Route as RedirectTestIndexRouteImport } from './routes/redirect-test/index' +import { Route as RedirectTestTargetRouteImport } from './routes/redirect-test/target' +import { Route as FormdataRedirectTargetNameRouteImport } from './routes/formdata-redirect/target.$name' import { Route as MiddlewareRedirectWithMiddlewareIndexRouteImport } from './routes/middleware/redirect-with-middleware/index' import { Route as MiddlewareRedirectWithMiddlewareTargetRouteImport } from './routes/middleware/redirect-with-middleware/target' -import { Route as FormdataRedirectTargetNameRouteImport } from './routes/formdata-redirect/target.$name' -const SubmitPostFormdataRoute = SubmitPostFormdataRouteImport.update({ - id: '/submit-post-formdata', - path: '/submit-post-formdata', - getParentRoute: () => rootRouteImport, -} as any) -const StatusRoute = StatusRouteImport.update({ - id: '/status', - path: '/status', - getParentRoute: () => rootRouteImport, -} as any) -const ServerOnlyFnRoute = ServerOnlyFnRouteImport.update({ - id: '/server-only-fn', - path: '/server-only-fn', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const ServerFnInClientOnlyFnRoute = ServerFnInClientOnlyFnRouteImport.update({ - id: '/server-fn-in-client-only-fn', - path: '/server-fn-in-client-only-fn', +const AsyncValidationRoute = AsyncValidationRouteImport.update({ + id: '/async-validation', + path: '/async-validation', getParentRoute: () => rootRouteImport, } as any) -const SerializeFormDataRoute = SerializeFormDataRouteImport.update({ - id: '/serialize-form-data', - path: '/serialize-form-data', +const ConsistentRoute = ConsistentRouteImport.update({ + id: '/consistent', + path: '/consistent', getParentRoute: () => rootRouteImport, } as any) -const ReturnNullRoute = ReturnNullRouteImport.update({ - id: '/return-null', - path: '/return-null', +const CustomFetchRoute = CustomFetchRouteImport.update({ + id: '/custom-fetch', + path: '/custom-fetch', getParentRoute: () => rootRouteImport, } as any) -const RawResponseRoute = RawResponseRouteImport.update({ - id: '/raw-response', - path: '/raw-response', +const DeadCodePreserveRoute = DeadCodePreserveRouteImport.update({ + id: '/dead-code-preserve', + path: '/dead-code-preserve', getParentRoute: () => rootRouteImport, } as any) -const MultipartRoute = MultipartRouteImport.update({ - id: '/multipart', - path: '/multipart', +const EnvOnlyRoute = EnvOnlyRouteImport.update({ + id: '/env-only', + path: '/env-only', getParentRoute: () => rootRouteImport, } as any) -const IsomorphicFnsRoute = IsomorphicFnsRouteImport.update({ - id: '/isomorphic-fns', - path: '/isomorphic-fns', +const FormdataContextRoute = FormdataContextRouteImport.update({ + id: '/formdata-context', + path: '/formdata-context', getParentRoute: () => rootRouteImport, } as any) const HeadersRoute = HeadersRouteImport.update({ @@ -104,74 +94,74 @@ const HeadersRoute = HeadersRouteImport.update({ path: '/headers', getParentRoute: () => rootRouteImport, } as any) -const FormdataContextRoute = FormdataContextRouteImport.update({ - id: '/formdata-context', - path: '/formdata-context', +const IsomorphicFnsRoute = IsomorphicFnsRouteImport.update({ + id: '/isomorphic-fns', + path: '/isomorphic-fns', getParentRoute: () => rootRouteImport, } as any) -const EnvOnlyRoute = EnvOnlyRouteImport.update({ - id: '/env-only', - path: '/env-only', +const MultipartRoute = MultipartRouteImport.update({ + id: '/multipart', + path: '/multipart', getParentRoute: () => rootRouteImport, } as any) -const DeadCodePreserveRoute = DeadCodePreserveRouteImport.update({ - id: '/dead-code-preserve', - path: '/dead-code-preserve', +const RawResponseRoute = RawResponseRouteImport.update({ + id: '/raw-response', + path: '/raw-response', getParentRoute: () => rootRouteImport, } as any) -const CustomFetchRoute = CustomFetchRouteImport.update({ - id: '/custom-fetch', - path: '/custom-fetch', +const ReturnNullRoute = ReturnNullRouteImport.update({ + id: '/return-null', + path: '/return-null', getParentRoute: () => rootRouteImport, } as any) -const ConsistentRoute = ConsistentRouteImport.update({ - id: '/consistent', - path: '/consistent', +const SerializeFormDataRoute = SerializeFormDataRouteImport.update({ + id: '/serialize-form-data', + path: '/serialize-form-data', getParentRoute: () => rootRouteImport, } as any) -const AsyncValidationRoute = AsyncValidationRouteImport.update({ - id: '/async-validation', - path: '/async-validation', +const ServerFnInClientOnlyFnRoute = ServerFnInClientOnlyFnRouteImport.update({ + id: '/server-fn-in-client-only-fn', + path: '/server-fn-in-client-only-fn', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const ServerOnlyFnRoute = ServerOnlyFnRouteImport.update({ + id: '/server-only-fn', + path: '/server-only-fn', getParentRoute: () => rootRouteImport, } as any) -const RedirectTestIndexRoute = RedirectTestIndexRouteImport.update({ - id: '/redirect-test/', - path: '/redirect-test/', +const StatusRoute = StatusRouteImport.update({ + id: '/status', + path: '/status', getParentRoute: () => rootRouteImport, } as any) -const RedirectTestSsrIndexRoute = RedirectTestSsrIndexRouteImport.update({ - id: '/redirect-test-ssr/', - path: '/redirect-test-ssr/', +const SubmitPostFormdataRoute = SubmitPostFormdataRouteImport.update({ + id: '/submit-post-formdata', + path: '/submit-post-formdata', getParentRoute: () => rootRouteImport, } as any) -const PrimitivesIndexRoute = PrimitivesIndexRouteImport.update({ - id: '/primitives/', - path: '/primitives/', +const AbortSignalIndexRoute = AbortSignalIndexRouteImport.update({ + id: '/abort-signal/', + path: '/abort-signal/', getParentRoute: () => rootRouteImport, } as any) -const MiddlewareIndexRoute = MiddlewareIndexRouteImport.update({ - id: '/middleware/', - path: '/middleware/', +const AbortSignalMethodRoute = AbortSignalMethodRouteImport.update({ + id: '/abort-signal/$method', + path: '/abort-signal/$method', getParentRoute: () => rootRouteImport, } as any) -const MethodNotAllowedIndexRoute = MethodNotAllowedIndexRouteImport.update({ - id: '/method-not-allowed/', - path: '/method-not-allowed/', +const CookiesIndexRoute = CookiesIndexRouteImport.update({ + id: '/cookies/', + path: '/cookies/', getParentRoute: () => rootRouteImport, } as any) -const FunctionMethodIndexRoute = FunctionMethodIndexRouteImport.update({ - id: '/function-method/', - path: '/function-method/', +const CookiesSetRoute = CookiesSetRouteImport.update({ + id: '/cookies/set', + path: '/cookies/set', getParentRoute: () => rootRouteImport, } as any) -const FunctionMetadataIndexRoute = FunctionMetadataIndexRouteImport.update({ - id: '/function-metadata/', - path: '/function-metadata/', +const FactoryIndexRoute = FactoryIndexRouteImport.update({ + id: '/factory/', + path: '/factory/', getParentRoute: () => rootRouteImport, } as any) const FormdataRedirectIndexRoute = FormdataRedirectIndexRouteImport.update({ @@ -179,52 +169,47 @@ const FormdataRedirectIndexRoute = FormdataRedirectIndexRouteImport.update({ path: '/formdata-redirect/', getParentRoute: () => rootRouteImport, } as any) -const FactoryIndexRoute = FactoryIndexRouteImport.update({ - id: '/factory/', - path: '/factory/', +const FunctionMetadataIndexRoute = FunctionMetadataIndexRouteImport.update({ + id: '/function-metadata/', + path: '/function-metadata/', getParentRoute: () => rootRouteImport, } as any) -const CookiesIndexRoute = CookiesIndexRouteImport.update({ - id: '/cookies/', - path: '/cookies/', +const FunctionMethodIndexRoute = FunctionMethodIndexRouteImport.update({ + id: '/function-method/', + path: '/function-method/', getParentRoute: () => rootRouteImport, } as any) -const AbortSignalIndexRoute = AbortSignalIndexRouteImport.update({ - id: '/abort-signal/', - path: '/abort-signal/', +const MethodNotAllowedIndexRoute = MethodNotAllowedIndexRouteImport.update({ + id: '/method-not-allowed/', + path: '/method-not-allowed/', getParentRoute: () => rootRouteImport, } as any) -const RedirectTestTargetRoute = RedirectTestTargetRouteImport.update({ - id: '/redirect-test/target', - path: '/redirect-test/target', +const MethodNotAllowedMethodRoute = MethodNotAllowedMethodRouteImport.update({ + id: '/method-not-allowed/$method', + path: '/method-not-allowed/$method', getParentRoute: () => rootRouteImport, } as any) -const RedirectTestSsrTargetRoute = RedirectTestSsrTargetRouteImport.update({ - id: '/redirect-test-ssr/target', - path: '/redirect-test-ssr/target', +const MiddlewareIndexRoute = MiddlewareIndexRouteImport.update({ + id: '/middleware/', + path: '/middleware/', getParentRoute: () => rootRouteImport, } as any) -const MiddlewareUnhandledExceptionRoute = - MiddlewareUnhandledExceptionRouteImport.update({ - id: '/middleware/unhandled-exception', - path: '/middleware/unhandled-exception', +const MiddlewareCatchHandlerErrorRoute = + MiddlewareCatchHandlerErrorRouteImport.update({ + id: '/middleware/catch-handler-error', + path: '/middleware/catch-handler-error', getParentRoute: () => rootRouteImport, } as any) -const MiddlewareServerImportMiddlewareRoute = - MiddlewareServerImportMiddlewareRouteImport.update({ - id: '/middleware/server-import-middleware', - path: '/middleware/server-import-middleware', +const MiddlewareClientMiddlewareRouterRoute = + MiddlewareClientMiddlewareRouterRouteImport.update({ + id: '/middleware/client-middleware-router', + path: '/middleware/client-middleware-router', getParentRoute: () => rootRouteImport, } as any) -const MiddlewareSendServerFnRoute = MiddlewareSendServerFnRouteImport.update({ - id: '/middleware/send-serverFn', - path: '/middleware/send-serverFn', - getParentRoute: () => rootRouteImport, -} as any) -const MiddlewareRequestMiddlewareRoute = - MiddlewareRequestMiddlewareRouteImport.update({ - id: '/middleware/request-middleware', - path: '/middleware/request-middleware', +const MiddlewareFunctionMetadataRoute = + MiddlewareFunctionMetadataRouteImport.update({ + id: '/middleware/function-metadata', + path: '/middleware/function-metadata', getParentRoute: () => rootRouteImport, } as any) const MiddlewareMiddlewareFactoryRoute = @@ -233,39 +218,60 @@ const MiddlewareMiddlewareFactoryRoute = path: '/middleware/middleware-factory', getParentRoute: () => rootRouteImport, } as any) -const MiddlewareFunctionMetadataRoute = - MiddlewareFunctionMetadataRouteImport.update({ - id: '/middleware/function-metadata', - path: '/middleware/function-metadata', +const MiddlewareRequestMiddlewareRoute = + MiddlewareRequestMiddlewareRouteImport.update({ + id: '/middleware/request-middleware', + path: '/middleware/request-middleware', getParentRoute: () => rootRouteImport, } as any) -const MiddlewareClientMiddlewareRouterRoute = - MiddlewareClientMiddlewareRouterRouteImport.update({ - id: '/middleware/client-middleware-router', - path: '/middleware/client-middleware-router', +const MiddlewareSendServerFnRoute = MiddlewareSendServerFnRouteImport.update({ + id: '/middleware/send-serverFn', + path: '/middleware/send-serverFn', + getParentRoute: () => rootRouteImport, +} as any) +const MiddlewareServerImportMiddlewareRoute = + MiddlewareServerImportMiddlewareRouteImport.update({ + id: '/middleware/server-import-middleware', + path: '/middleware/server-import-middleware', getParentRoute: () => rootRouteImport, } as any) -const MiddlewareCatchHandlerErrorRoute = - MiddlewareCatchHandlerErrorRouteImport.update({ - id: '/middleware/catch-handler-error', - path: '/middleware/catch-handler-error', +const MiddlewareUnhandledExceptionRoute = + MiddlewareUnhandledExceptionRouteImport.update({ + id: '/middleware/unhandled-exception', + path: '/middleware/unhandled-exception', getParentRoute: () => rootRouteImport, } as any) -const MethodNotAllowedMethodRoute = MethodNotAllowedMethodRouteImport.update({ - id: '/method-not-allowed/$method', - path: '/method-not-allowed/$method', +const PrimitivesIndexRoute = PrimitivesIndexRouteImport.update({ + id: '/primitives/', + path: '/primitives/', getParentRoute: () => rootRouteImport, } as any) -const CookiesSetRoute = CookiesSetRouteImport.update({ - id: '/cookies/set', - path: '/cookies/set', +const RedirectTestSsrIndexRoute = RedirectTestSsrIndexRouteImport.update({ + id: '/redirect-test-ssr/', + path: '/redirect-test-ssr/', getParentRoute: () => rootRouteImport, } as any) -const AbortSignalMethodRoute = AbortSignalMethodRouteImport.update({ - id: '/abort-signal/$method', - path: '/abort-signal/$method', +const RedirectTestSsrTargetRoute = RedirectTestSsrTargetRouteImport.update({ + id: '/redirect-test-ssr/target', + path: '/redirect-test-ssr/target', + getParentRoute: () => rootRouteImport, +} as any) +const RedirectTestIndexRoute = RedirectTestIndexRouteImport.update({ + id: '/redirect-test/', + path: '/redirect-test/', + getParentRoute: () => rootRouteImport, +} as any) +const RedirectTestTargetRoute = RedirectTestTargetRouteImport.update({ + id: '/redirect-test/target', + path: '/redirect-test/target', getParentRoute: () => rootRouteImport, } as any) +const FormdataRedirectTargetNameRoute = + FormdataRedirectTargetNameRouteImport.update({ + id: '/formdata-redirect/target/$name', + path: '/formdata-redirect/target/$name', + getParentRoute: () => rootRouteImport, + } as any) const MiddlewareRedirectWithMiddlewareIndexRoute = MiddlewareRedirectWithMiddlewareIndexRouteImport.update({ id: '/middleware/redirect-with-middleware/', @@ -278,12 +284,6 @@ const MiddlewareRedirectWithMiddlewareTargetRoute = path: '/middleware/redirect-with-middleware/target', getParentRoute: () => rootRouteImport, } as any) -const FormdataRedirectTargetNameRoute = - FormdataRedirectTargetNameRouteImport.update({ - id: '/formdata-redirect/target/$name', - path: '/formdata-redirect/target/$name', - getParentRoute: () => rootRouteImport, - } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -614,60 +614,60 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/submit-post-formdata': { - id: '/submit-post-formdata' - path: '/submit-post-formdata' - fullPath: '/submit-post-formdata' - preLoaderRoute: typeof SubmitPostFormdataRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/status': { - id: '/status' - path: '/status' - fullPath: '/status' - preLoaderRoute: typeof StatusRouteImport + '/async-validation': { + id: '/async-validation' + path: '/async-validation' + fullPath: '/async-validation' + preLoaderRoute: typeof AsyncValidationRouteImport parentRoute: typeof rootRouteImport } - '/server-only-fn': { - id: '/server-only-fn' - path: '/server-only-fn' - fullPath: '/server-only-fn' - preLoaderRoute: typeof ServerOnlyFnRouteImport + '/consistent': { + id: '/consistent' + path: '/consistent' + fullPath: '/consistent' + preLoaderRoute: typeof ConsistentRouteImport parentRoute: typeof rootRouteImport } - '/server-fn-in-client-only-fn': { - id: '/server-fn-in-client-only-fn' - path: '/server-fn-in-client-only-fn' - fullPath: '/server-fn-in-client-only-fn' - preLoaderRoute: typeof ServerFnInClientOnlyFnRouteImport + '/custom-fetch': { + id: '/custom-fetch' + path: '/custom-fetch' + fullPath: '/custom-fetch' + preLoaderRoute: typeof CustomFetchRouteImport parentRoute: typeof rootRouteImport } - '/serialize-form-data': { - id: '/serialize-form-data' - path: '/serialize-form-data' - fullPath: '/serialize-form-data' - preLoaderRoute: typeof SerializeFormDataRouteImport + '/dead-code-preserve': { + id: '/dead-code-preserve' + path: '/dead-code-preserve' + fullPath: '/dead-code-preserve' + preLoaderRoute: typeof DeadCodePreserveRouteImport parentRoute: typeof rootRouteImport } - '/return-null': { - id: '/return-null' - path: '/return-null' - fullPath: '/return-null' - preLoaderRoute: typeof ReturnNullRouteImport + '/env-only': { + id: '/env-only' + path: '/env-only' + fullPath: '/env-only' + preLoaderRoute: typeof EnvOnlyRouteImport parentRoute: typeof rootRouteImport } - '/raw-response': { - id: '/raw-response' - path: '/raw-response' - fullPath: '/raw-response' - preLoaderRoute: typeof RawResponseRouteImport + '/formdata-context': { + id: '/formdata-context' + path: '/formdata-context' + fullPath: '/formdata-context' + preLoaderRoute: typeof FormdataContextRouteImport parentRoute: typeof rootRouteImport } - '/multipart': { - id: '/multipart' - path: '/multipart' - fullPath: '/multipart' - preLoaderRoute: typeof MultipartRouteImport + '/headers': { + id: '/headers' + path: '/headers' + fullPath: '/headers' + preLoaderRoute: typeof HeadersRouteImport parentRoute: typeof rootRouteImport } '/isomorphic-fns': { @@ -677,102 +677,102 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IsomorphicFnsRouteImport parentRoute: typeof rootRouteImport } - '/headers': { - id: '/headers' - path: '/headers' - fullPath: '/headers' - preLoaderRoute: typeof HeadersRouteImport + '/multipart': { + id: '/multipart' + path: '/multipart' + fullPath: '/multipart' + preLoaderRoute: typeof MultipartRouteImport parentRoute: typeof rootRouteImport } - '/formdata-context': { - id: '/formdata-context' - path: '/formdata-context' - fullPath: '/formdata-context' - preLoaderRoute: typeof FormdataContextRouteImport + '/raw-response': { + id: '/raw-response' + path: '/raw-response' + fullPath: '/raw-response' + preLoaderRoute: typeof RawResponseRouteImport parentRoute: typeof rootRouteImport } - '/env-only': { - id: '/env-only' - path: '/env-only' - fullPath: '/env-only' - preLoaderRoute: typeof EnvOnlyRouteImport + '/return-null': { + id: '/return-null' + path: '/return-null' + fullPath: '/return-null' + preLoaderRoute: typeof ReturnNullRouteImport parentRoute: typeof rootRouteImport } - '/dead-code-preserve': { - id: '/dead-code-preserve' - path: '/dead-code-preserve' - fullPath: '/dead-code-preserve' - preLoaderRoute: typeof DeadCodePreserveRouteImport + '/serialize-form-data': { + id: '/serialize-form-data' + path: '/serialize-form-data' + fullPath: '/serialize-form-data' + preLoaderRoute: typeof SerializeFormDataRouteImport parentRoute: typeof rootRouteImport } - '/custom-fetch': { - id: '/custom-fetch' - path: '/custom-fetch' - fullPath: '/custom-fetch' - preLoaderRoute: typeof CustomFetchRouteImport + '/server-fn-in-client-only-fn': { + id: '/server-fn-in-client-only-fn' + path: '/server-fn-in-client-only-fn' + fullPath: '/server-fn-in-client-only-fn' + preLoaderRoute: typeof ServerFnInClientOnlyFnRouteImport parentRoute: typeof rootRouteImport } - '/consistent': { - id: '/consistent' - path: '/consistent' - fullPath: '/consistent' - preLoaderRoute: typeof ConsistentRouteImport + '/server-only-fn': { + id: '/server-only-fn' + path: '/server-only-fn' + fullPath: '/server-only-fn' + preLoaderRoute: typeof ServerOnlyFnRouteImport parentRoute: typeof rootRouteImport } - '/async-validation': { - id: '/async-validation' - path: '/async-validation' - fullPath: '/async-validation' - preLoaderRoute: typeof AsyncValidationRouteImport + '/status': { + id: '/status' + path: '/status' + fullPath: '/status' + preLoaderRoute: typeof StatusRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/submit-post-formdata': { + id: '/submit-post-formdata' + path: '/submit-post-formdata' + fullPath: '/submit-post-formdata' + preLoaderRoute: typeof SubmitPostFormdataRouteImport parentRoute: typeof rootRouteImport } - '/redirect-test/': { - id: '/redirect-test/' - path: '/redirect-test' - fullPath: '/redirect-test/' - preLoaderRoute: typeof RedirectTestIndexRouteImport + '/abort-signal/': { + id: '/abort-signal/' + path: '/abort-signal' + fullPath: '/abort-signal/' + preLoaderRoute: typeof AbortSignalIndexRouteImport parentRoute: typeof rootRouteImport } - '/redirect-test-ssr/': { - id: '/redirect-test-ssr/' - path: '/redirect-test-ssr' - fullPath: '/redirect-test-ssr/' - preLoaderRoute: typeof RedirectTestSsrIndexRouteImport + '/abort-signal/$method': { + id: '/abort-signal/$method' + path: '/abort-signal/$method' + fullPath: '/abort-signal/$method' + preLoaderRoute: typeof AbortSignalMethodRouteImport parentRoute: typeof rootRouteImport } - '/primitives/': { - id: '/primitives/' - path: '/primitives' - fullPath: '/primitives/' - preLoaderRoute: typeof PrimitivesIndexRouteImport + '/cookies/': { + id: '/cookies/' + path: '/cookies' + fullPath: '/cookies/' + preLoaderRoute: typeof CookiesIndexRouteImport parentRoute: typeof rootRouteImport } - '/middleware/': { - id: '/middleware/' - path: '/middleware' - fullPath: '/middleware/' - preLoaderRoute: typeof MiddlewareIndexRouteImport + '/cookies/set': { + id: '/cookies/set' + path: '/cookies/set' + fullPath: '/cookies/set' + preLoaderRoute: typeof CookiesSetRouteImport parentRoute: typeof rootRouteImport } - '/method-not-allowed/': { - id: '/method-not-allowed/' - path: '/method-not-allowed' - fullPath: '/method-not-allowed/' - preLoaderRoute: typeof MethodNotAllowedIndexRouteImport + '/factory/': { + id: '/factory/' + path: '/factory' + fullPath: '/factory/' + preLoaderRoute: typeof FactoryIndexRouteImport parentRoute: typeof rootRouteImport } - '/function-method/': { - id: '/function-method/' - path: '/function-method' - fullPath: '/function-method/' - preLoaderRoute: typeof FunctionMethodIndexRouteImport + '/formdata-redirect/': { + id: '/formdata-redirect/' + path: '/formdata-redirect' + fullPath: '/formdata-redirect/' + preLoaderRoute: typeof FormdataRedirectIndexRouteImport parentRoute: typeof rootRouteImport } '/function-metadata/': { @@ -782,60 +782,67 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof FunctionMetadataIndexRouteImport parentRoute: typeof rootRouteImport } - '/formdata-redirect/': { - id: '/formdata-redirect/' - path: '/formdata-redirect' - fullPath: '/formdata-redirect/' - preLoaderRoute: typeof FormdataRedirectIndexRouteImport + '/function-method/': { + id: '/function-method/' + path: '/function-method' + fullPath: '/function-method/' + preLoaderRoute: typeof FunctionMethodIndexRouteImport parentRoute: typeof rootRouteImport } - '/factory/': { - id: '/factory/' - path: '/factory' - fullPath: '/factory/' - preLoaderRoute: typeof FactoryIndexRouteImport + '/method-not-allowed/': { + id: '/method-not-allowed/' + path: '/method-not-allowed' + fullPath: '/method-not-allowed/' + preLoaderRoute: typeof MethodNotAllowedIndexRouteImport parentRoute: typeof rootRouteImport } - '/cookies/': { - id: '/cookies/' - path: '/cookies' - fullPath: '/cookies/' - preLoaderRoute: typeof CookiesIndexRouteImport + '/method-not-allowed/$method': { + id: '/method-not-allowed/$method' + path: '/method-not-allowed/$method' + fullPath: '/method-not-allowed/$method' + preLoaderRoute: typeof MethodNotAllowedMethodRouteImport parentRoute: typeof rootRouteImport } - '/abort-signal/': { - id: '/abort-signal/' - path: '/abort-signal' - fullPath: '/abort-signal/' - preLoaderRoute: typeof AbortSignalIndexRouteImport + '/middleware/': { + id: '/middleware/' + path: '/middleware' + fullPath: '/middleware/' + preLoaderRoute: typeof MiddlewareIndexRouteImport parentRoute: typeof rootRouteImport } - '/redirect-test/target': { - id: '/redirect-test/target' - path: '/redirect-test/target' - fullPath: '/redirect-test/target' - preLoaderRoute: typeof RedirectTestTargetRouteImport + '/middleware/catch-handler-error': { + id: '/middleware/catch-handler-error' + path: '/middleware/catch-handler-error' + fullPath: '/middleware/catch-handler-error' + preLoaderRoute: typeof MiddlewareCatchHandlerErrorRouteImport parentRoute: typeof rootRouteImport } - '/redirect-test-ssr/target': { - id: '/redirect-test-ssr/target' - path: '/redirect-test-ssr/target' - fullPath: '/redirect-test-ssr/target' - preLoaderRoute: typeof RedirectTestSsrTargetRouteImport + '/middleware/client-middleware-router': { + id: '/middleware/client-middleware-router' + path: '/middleware/client-middleware-router' + fullPath: '/middleware/client-middleware-router' + preLoaderRoute: typeof MiddlewareClientMiddlewareRouterRouteImport parentRoute: typeof rootRouteImport } - '/middleware/unhandled-exception': { - id: '/middleware/unhandled-exception' - path: '/middleware/unhandled-exception' - fullPath: '/middleware/unhandled-exception' - preLoaderRoute: typeof MiddlewareUnhandledExceptionRouteImport + '/middleware/function-metadata': { + id: '/middleware/function-metadata' + path: '/middleware/function-metadata' + fullPath: '/middleware/function-metadata' + preLoaderRoute: typeof MiddlewareFunctionMetadataRouteImport parentRoute: typeof rootRouteImport } - '/middleware/server-import-middleware': { - id: '/middleware/server-import-middleware' - path: '/middleware/server-import-middleware' - fullPath: '/middleware/server-import-middleware' - preLoaderRoute: typeof MiddlewareServerImportMiddlewareRouteImport + '/middleware/middleware-factory': { + id: '/middleware/middleware-factory' + path: '/middleware/middleware-factory' + fullPath: '/middleware/middleware-factory' + preLoaderRoute: typeof MiddlewareMiddlewareFactoryRouteImport + parentRoute: typeof rootRouteImport + } + '/middleware/request-middleware': { + id: '/middleware/request-middleware' + path: '/middleware/request-middleware' + fullPath: '/middleware/request-middleware' + preLoaderRoute: typeof MiddlewareRequestMiddlewareRouteImport parentRoute: typeof rootRouteImport } '/middleware/send-serverFn': { @@ -845,60 +852,60 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof MiddlewareSendServerFnRouteImport parentRoute: typeof rootRouteImport } - '/middleware/request-middleware': { - id: '/middleware/request-middleware' - path: '/middleware/request-middleware' - fullPath: '/middleware/request-middleware' - preLoaderRoute: typeof MiddlewareRequestMiddlewareRouteImport + '/middleware/server-import-middleware': { + id: '/middleware/server-import-middleware' + path: '/middleware/server-import-middleware' + fullPath: '/middleware/server-import-middleware' + preLoaderRoute: typeof MiddlewareServerImportMiddlewareRouteImport parentRoute: typeof rootRouteImport } - '/middleware/middleware-factory': { - id: '/middleware/middleware-factory' - path: '/middleware/middleware-factory' - fullPath: '/middleware/middleware-factory' - preLoaderRoute: typeof MiddlewareMiddlewareFactoryRouteImport + '/middleware/unhandled-exception': { + id: '/middleware/unhandled-exception' + path: '/middleware/unhandled-exception' + fullPath: '/middleware/unhandled-exception' + preLoaderRoute: typeof MiddlewareUnhandledExceptionRouteImport parentRoute: typeof rootRouteImport } - '/middleware/function-metadata': { - id: '/middleware/function-metadata' - path: '/middleware/function-metadata' - fullPath: '/middleware/function-metadata' - preLoaderRoute: typeof MiddlewareFunctionMetadataRouteImport + '/primitives/': { + id: '/primitives/' + path: '/primitives' + fullPath: '/primitives/' + preLoaderRoute: typeof PrimitivesIndexRouteImport parentRoute: typeof rootRouteImport } - '/middleware/client-middleware-router': { - id: '/middleware/client-middleware-router' - path: '/middleware/client-middleware-router' - fullPath: '/middleware/client-middleware-router' - preLoaderRoute: typeof MiddlewareClientMiddlewareRouterRouteImport + '/redirect-test-ssr/': { + id: '/redirect-test-ssr/' + path: '/redirect-test-ssr' + fullPath: '/redirect-test-ssr/' + preLoaderRoute: typeof RedirectTestSsrIndexRouteImport parentRoute: typeof rootRouteImport } - '/middleware/catch-handler-error': { - id: '/middleware/catch-handler-error' - path: '/middleware/catch-handler-error' - fullPath: '/middleware/catch-handler-error' - preLoaderRoute: typeof MiddlewareCatchHandlerErrorRouteImport + '/redirect-test-ssr/target': { + id: '/redirect-test-ssr/target' + path: '/redirect-test-ssr/target' + fullPath: '/redirect-test-ssr/target' + preLoaderRoute: typeof RedirectTestSsrTargetRouteImport parentRoute: typeof rootRouteImport } - '/method-not-allowed/$method': { - id: '/method-not-allowed/$method' - path: '/method-not-allowed/$method' - fullPath: '/method-not-allowed/$method' - preLoaderRoute: typeof MethodNotAllowedMethodRouteImport + '/redirect-test/': { + id: '/redirect-test/' + path: '/redirect-test' + fullPath: '/redirect-test/' + preLoaderRoute: typeof RedirectTestIndexRouteImport parentRoute: typeof rootRouteImport } - '/cookies/set': { - id: '/cookies/set' - path: '/cookies/set' - fullPath: '/cookies/set' - preLoaderRoute: typeof CookiesSetRouteImport + '/redirect-test/target': { + id: '/redirect-test/target' + path: '/redirect-test/target' + fullPath: '/redirect-test/target' + preLoaderRoute: typeof RedirectTestTargetRouteImport parentRoute: typeof rootRouteImport } - '/abort-signal/$method': { - id: '/abort-signal/$method' - path: '/abort-signal/$method' - fullPath: '/abort-signal/$method' - preLoaderRoute: typeof AbortSignalMethodRouteImport + '/formdata-redirect/target/$name': { + id: '/formdata-redirect/target/$name' + path: '/formdata-redirect/target/$name' + fullPath: '/formdata-redirect/target/$name' + preLoaderRoute: typeof FormdataRedirectTargetNameRouteImport parentRoute: typeof rootRouteImport } '/middleware/redirect-with-middleware/': { @@ -915,13 +922,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof MiddlewareRedirectWithMiddlewareTargetRouteImport parentRoute: typeof rootRouteImport } - '/formdata-redirect/target/$name': { - id: '/formdata-redirect/target/$name' - path: '/formdata-redirect/target/$name' - fullPath: '/formdata-redirect/target/$name' - preLoaderRoute: typeof FormdataRedirectTargetNameRouteImport - parentRoute: typeof rootRouteImport - } } } diff --git a/e2e/react-start/server-routes-global-middleware/src/routeTree.gen.ts b/e2e/react-start/server-routes-global-middleware/src/routeTree.gen.ts index 6ec0a95128..3221c03ad6 100644 --- a/e2e/react-start/server-routes-global-middleware/src/routeTree.gen.ts +++ b/e2e/react-start/server-routes-global-middleware/src/routeTree.gen.ts @@ -9,20 +9,20 @@ // 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 ServerRouteWithMiddlewareRouteImport } from './routes/server-route-with-middleware' import { Route as IndexRouteImport } from './routes/index' +import { Route as ServerRouteWithMiddlewareRouteImport } from './routes/server-route-with-middleware' +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) const ServerRouteWithMiddlewareRoute = ServerRouteWithMiddlewareRouteImport.update({ id: '/server-route-with-middleware', path: '/server-route-with-middleware', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -52,13 +52,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/server-route-with-middleware': { - id: '/server-route-with-middleware' - path: '/server-route-with-middleware' - fullPath: '/server-route-with-middleware' - preLoaderRoute: typeof ServerRouteWithMiddlewareRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -66,6 +59,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/server-route-with-middleware': { + id: '/server-route-with-middleware' + path: '/server-route-with-middleware' + fullPath: '/server-route-with-middleware' + preLoaderRoute: typeof ServerRouteWithMiddlewareRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/e2e/react-start/server-routes/src/routeTree.gen.ts b/e2e/react-start/server-routes/src/routeTree.gen.ts index 2b8bac6e09..2454c3bb32 100644 --- a/e2e/react-start/server-routes/src/routeTree.gen.ts +++ b/e2e/react-start/server-routes/src/routeTree.gen.ts @@ -9,19 +9,24 @@ // 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 IndexRouteImport } from './routes/index' import { Route as MergeMiddlewareContextRouteImport } from './routes/merge-middleware-context' import { Route as MethodsRouteRouteImport } from './routes/methods/route' -import { Route as IndexRouteImport } from './routes/index' +import { Route as ApiGetAndAnyRouteImport } from './routes/api/get-and-any' +import { Route as ApiHeadFallbackRouteImport } from './routes/api/head-fallback' +import { Route as ApiHeadRedirectFallbackRouteImport } from './routes/api/head-redirect-fallback' +import { Route as ApiMiddlewareContextRouteImport } from './routes/api/middleware-context' +import { Route as ApiOnlyAnyRouteImport } from './routes/api/only-any' import { Route as MethodsIndexRouteImport } from './routes/methods/index' import { Route as MethodsOnlyAnyRouteImport } from './routes/methods/only-any' -import { Route as ApiOnlyAnyRouteImport } from './routes/api/only-any' -import { Route as ApiMiddlewareContextRouteImport } from './routes/api/middleware-context' -import { Route as ApiHeadRedirectFallbackRouteImport } from './routes/api/head-redirect-fallback' -import { Route as ApiHeadFallbackRouteImport } from './routes/api/head-fallback' -import { Route as ApiGetAndAnyRouteImport } from './routes/api/get-and-any' import { Route as ApiParamsFooRouteRouteImport } from './routes/api/params/$foo/route' import { Route as ApiParamsFooBarRouteImport } from './routes/api/params/$foo/$bar' +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) const MergeMiddlewareContextRoute = MergeMiddlewareContextRouteImport.update({ id: '/merge-middleware-context', path: '/merge-middleware-context', @@ -32,24 +37,19 @@ const MethodsRouteRoute = MethodsRouteRouteImport.update({ path: '/methods', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const ApiGetAndAnyRoute = ApiGetAndAnyRouteImport.update({ + id: '/api/get-and-any', + path: '/api/get-and-any', getParentRoute: () => rootRouteImport, } as any) -const MethodsIndexRoute = MethodsIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => MethodsRouteRoute, -} as any) -const MethodsOnlyAnyRoute = MethodsOnlyAnyRouteImport.update({ - id: '/only-any', - path: '/only-any', - getParentRoute: () => MethodsRouteRoute, +const ApiHeadFallbackRoute = ApiHeadFallbackRouteImport.update({ + id: '/api/head-fallback', + path: '/api/head-fallback', + getParentRoute: () => rootRouteImport, } as any) -const ApiOnlyAnyRoute = ApiOnlyAnyRouteImport.update({ - id: '/api/only-any', - path: '/api/only-any', +const ApiHeadRedirectFallbackRoute = ApiHeadRedirectFallbackRouteImport.update({ + id: '/api/head-redirect-fallback', + path: '/api/head-redirect-fallback', getParentRoute: () => rootRouteImport, } as any) const ApiMiddlewareContextRoute = ApiMiddlewareContextRouteImport.update({ @@ -57,20 +57,20 @@ const ApiMiddlewareContextRoute = ApiMiddlewareContextRouteImport.update({ path: '/api/middleware-context', getParentRoute: () => rootRouteImport, } as any) -const ApiHeadRedirectFallbackRoute = ApiHeadRedirectFallbackRouteImport.update({ - id: '/api/head-redirect-fallback', - path: '/api/head-redirect-fallback', +const ApiOnlyAnyRoute = ApiOnlyAnyRouteImport.update({ + id: '/api/only-any', + path: '/api/only-any', getParentRoute: () => rootRouteImport, } as any) -const ApiHeadFallbackRoute = ApiHeadFallbackRouteImport.update({ - id: '/api/head-fallback', - path: '/api/head-fallback', - getParentRoute: () => rootRouteImport, +const MethodsIndexRoute = MethodsIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => MethodsRouteRoute, } as any) -const ApiGetAndAnyRoute = ApiGetAndAnyRouteImport.update({ - id: '/api/get-and-any', - path: '/api/get-and-any', - getParentRoute: () => rootRouteImport, +const MethodsOnlyAnyRoute = MethodsOnlyAnyRouteImport.update({ + id: '/only-any', + path: '/only-any', + getParentRoute: () => MethodsRouteRoute, } as any) const ApiParamsFooRouteRoute = ApiParamsFooRouteRouteImport.update({ id: '/api/params/$foo', @@ -183,6 +183,13 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } '/merge-middleware-context': { id: '/merge-middleware-context' path: '/merge-middleware-context' @@ -197,32 +204,25 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof MethodsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/api/get-and-any': { + id: '/api/get-and-any' + path: '/api/get-and-any' + fullPath: '/api/get-and-any' + preLoaderRoute: typeof ApiGetAndAnyRouteImport parentRoute: typeof rootRouteImport } - '/methods/': { - id: '/methods/' - path: '/' - fullPath: '/methods/' - preLoaderRoute: typeof MethodsIndexRouteImport - parentRoute: typeof MethodsRouteRoute - } - '/methods/only-any': { - id: '/methods/only-any' - path: '/only-any' - fullPath: '/methods/only-any' - preLoaderRoute: typeof MethodsOnlyAnyRouteImport - parentRoute: typeof MethodsRouteRoute + '/api/head-fallback': { + id: '/api/head-fallback' + path: '/api/head-fallback' + fullPath: '/api/head-fallback' + preLoaderRoute: typeof ApiHeadFallbackRouteImport + parentRoute: typeof rootRouteImport } - '/api/only-any': { - id: '/api/only-any' - path: '/api/only-any' - fullPath: '/api/only-any' - preLoaderRoute: typeof ApiOnlyAnyRouteImport + '/api/head-redirect-fallback': { + id: '/api/head-redirect-fallback' + path: '/api/head-redirect-fallback' + fullPath: '/api/head-redirect-fallback' + preLoaderRoute: typeof ApiHeadRedirectFallbackRouteImport parentRoute: typeof rootRouteImport } '/api/middleware-context': { @@ -232,26 +232,26 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ApiMiddlewareContextRouteImport parentRoute: typeof rootRouteImport } - '/api/head-redirect-fallback': { - id: '/api/head-redirect-fallback' - path: '/api/head-redirect-fallback' - fullPath: '/api/head-redirect-fallback' - preLoaderRoute: typeof ApiHeadRedirectFallbackRouteImport + '/api/only-any': { + id: '/api/only-any' + path: '/api/only-any' + fullPath: '/api/only-any' + preLoaderRoute: typeof ApiOnlyAnyRouteImport parentRoute: typeof rootRouteImport } - '/api/head-fallback': { - id: '/api/head-fallback' - path: '/api/head-fallback' - fullPath: '/api/head-fallback' - preLoaderRoute: typeof ApiHeadFallbackRouteImport - parentRoute: typeof rootRouteImport + '/methods/': { + id: '/methods/' + path: '/' + fullPath: '/methods/' + preLoaderRoute: typeof MethodsIndexRouteImport + parentRoute: typeof MethodsRouteRoute } - '/api/get-and-any': { - id: '/api/get-and-any' - path: '/api/get-and-any' - fullPath: '/api/get-and-any' - preLoaderRoute: typeof ApiGetAndAnyRouteImport - parentRoute: typeof rootRouteImport + '/methods/only-any': { + id: '/methods/only-any' + path: '/only-any' + fullPath: '/methods/only-any' + preLoaderRoute: typeof MethodsOnlyAnyRouteImport + parentRoute: typeof MethodsRouteRoute } '/api/params/$foo': { id: '/api/params/$foo' diff --git a/e2e/react-start/spa-mode/src/routeTree.gen.ts b/e2e/react-start/spa-mode/src/routeTree.gen.ts index 188cd5c91a..1bba3ef326 100644 --- a/e2e/react-start/spa-mode/src/routeTree.gen.ts +++ b/e2e/react-start/spa-mode/src/routeTree.gen.ts @@ -9,20 +9,20 @@ // 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 PostsRouteImport } from './routes/posts' import { Route as IndexRouteImport } from './routes/index' +import { Route as PostsRouteImport } from './routes/posts' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', + getParentRoute: () => rootRouteImport, +} as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', @@ -60,13 +60,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -74,6 +67,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport + parentRoute: typeof rootRouteImport + } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' diff --git a/e2e/react-start/split-base-and-basepath/src/routeTree.gen.ts b/e2e/react-start/split-base-and-basepath/src/routeTree.gen.ts index 421daf2790..d89758e684 100644 --- a/e2e/react-start/split-base-and-basepath/src/routeTree.gen.ts +++ b/e2e/react-start/split-base-and-basepath/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' +import { Route as AboutRouteImport } from './routes/about' -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/e2e/react-start/start-manifest/src/routeTree.gen.ts b/e2e/react-start/start-manifest/src/routeTree.gen.ts index b7ee033842..f89a740ba6 100644 --- a/e2e/react-start/start-manifest/src/routeTree.gen.ts +++ b/e2e/react-start/start-manifest/src/routeTree.gen.ts @@ -9,54 +9,49 @@ // 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 SharedCRouteImport } from './routes/shared-c' -import { Route as SharedBRouteImport } from './routes/shared-b' -import { Route as SharedARouteImport } from './routes/shared-a' -import { Route as R6RouteImport } from './routes/r6' -import { Route as R5RouteImport } from './routes/r5' -import { Route as R4RouteImport } from './routes/r4' -import { Route as R3RouteImport } from './routes/r3' -import { Route as R2RouteImport } from './routes/r2' -import { Route as R1RouteImport } from './routes/r1' -import { Route as LazyCssStaticRouteImport } from './routes/lazy-css-static' -import { Route as LazyCssLazyRouteImport } from './routes/lazy-css-lazy' -import { Route as BRouteImport } from './routes/b' -import { Route as ARouteImport } from './routes/a' import { Route as IndexRouteImport } from './routes/index' +import { Route as ARouteImport } from './routes/a' +import { Route as BRouteImport } from './routes/b' +import { Route as LazyCssLazyRouteImport } from './routes/lazy-css-lazy' +import { Route as LazyCssStaticRouteImport } from './routes/lazy-css-static' +import { Route as R1RouteImport } from './routes/r1' +import { Route as R2RouteImport } from './routes/r2' +import { Route as R3RouteImport } from './routes/r3' +import { Route as R4RouteImport } from './routes/r4' +import { Route as R5RouteImport } from './routes/r5' +import { Route as R6RouteImport } from './routes/r6' +import { Route as SharedARouteImport } from './routes/shared-a' +import { Route as SharedBRouteImport } from './routes/shared-b' +import { Route as SharedCRouteImport } from './routes/shared-c' -const SharedCRoute = SharedCRouteImport.update({ - id: '/shared-c', - path: '/shared-c', - getParentRoute: () => rootRouteImport, -} as any) -const SharedBRoute = SharedBRouteImport.update({ - id: '/shared-b', - path: '/shared-b', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const SharedARoute = SharedARouteImport.update({ - id: '/shared-a', - path: '/shared-a', +const ARoute = ARouteImport.update({ + id: '/a', + path: '/a', getParentRoute: () => rootRouteImport, } as any) -const R6Route = R6RouteImport.update({ - id: '/r6', - path: '/r6', +const BRoute = BRouteImport.update({ + id: '/b', + path: '/b', getParentRoute: () => rootRouteImport, } as any) -const R5Route = R5RouteImport.update({ - id: '/r5', - path: '/r5', +const LazyCssLazyRoute = LazyCssLazyRouteImport.update({ + id: '/lazy-css-lazy', + path: '/lazy-css-lazy', getParentRoute: () => rootRouteImport, } as any) -const R4Route = R4RouteImport.update({ - id: '/r4', - path: '/r4', +const LazyCssStaticRoute = LazyCssStaticRouteImport.update({ + id: '/lazy-css-static', + path: '/lazy-css-static', getParentRoute: () => rootRouteImport, } as any) -const R3Route = R3RouteImport.update({ - id: '/r3', - path: '/r3', +const R1Route = R1RouteImport.update({ + id: '/r1', + path: '/r1', getParentRoute: () => rootRouteImport, } as any) const R2Route = R2RouteImport.update({ @@ -64,34 +59,39 @@ const R2Route = R2RouteImport.update({ path: '/r2', getParentRoute: () => rootRouteImport, } as any) -const R1Route = R1RouteImport.update({ - id: '/r1', - path: '/r1', +const R3Route = R3RouteImport.update({ + id: '/r3', + path: '/r3', getParentRoute: () => rootRouteImport, } as any) -const LazyCssStaticRoute = LazyCssStaticRouteImport.update({ - id: '/lazy-css-static', - path: '/lazy-css-static', +const R4Route = R4RouteImport.update({ + id: '/r4', + path: '/r4', getParentRoute: () => rootRouteImport, } as any) -const LazyCssLazyRoute = LazyCssLazyRouteImport.update({ - id: '/lazy-css-lazy', - path: '/lazy-css-lazy', +const R5Route = R5RouteImport.update({ + id: '/r5', + path: '/r5', getParentRoute: () => rootRouteImport, } as any) -const BRoute = BRouteImport.update({ - id: '/b', - path: '/b', +const R6Route = R6RouteImport.update({ + id: '/r6', + path: '/r6', getParentRoute: () => rootRouteImport, } as any) -const ARoute = ARouteImport.update({ - id: '/a', - path: '/a', +const SharedARoute = SharedARouteImport.update({ + id: '/shared-a', + path: '/shared-a', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const SharedBRoute = SharedBRouteImport.update({ + id: '/shared-b', + path: '/shared-b', + getParentRoute: () => rootRouteImport, +} as any) +const SharedCRoute = SharedCRouteImport.update({ + id: '/shared-c', + path: '/shared-c', getParentRoute: () => rootRouteImport, } as any) @@ -214,53 +214,46 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/shared-c': { - id: '/shared-c' - path: '/shared-c' - fullPath: '/shared-c' - preLoaderRoute: typeof SharedCRouteImport - parentRoute: typeof rootRouteImport - } - '/shared-b': { - id: '/shared-b' - path: '/shared-b' - fullPath: '/shared-b' - preLoaderRoute: typeof SharedBRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/shared-a': { - id: '/shared-a' - path: '/shared-a' - fullPath: '/shared-a' - preLoaderRoute: typeof SharedARouteImport + '/a': { + id: '/a' + path: '/a' + fullPath: '/a' + preLoaderRoute: typeof ARouteImport parentRoute: typeof rootRouteImport } - '/r6': { - id: '/r6' - path: '/r6' - fullPath: '/r6' - preLoaderRoute: typeof R6RouteImport + '/b': { + id: '/b' + path: '/b' + fullPath: '/b' + preLoaderRoute: typeof BRouteImport parentRoute: typeof rootRouteImport } - '/r5': { - id: '/r5' - path: '/r5' - fullPath: '/r5' - preLoaderRoute: typeof R5RouteImport + '/lazy-css-lazy': { + id: '/lazy-css-lazy' + path: '/lazy-css-lazy' + fullPath: '/lazy-css-lazy' + preLoaderRoute: typeof LazyCssLazyRouteImport parentRoute: typeof rootRouteImport } - '/r4': { - id: '/r4' - path: '/r4' - fullPath: '/r4' - preLoaderRoute: typeof R4RouteImport + '/lazy-css-static': { + id: '/lazy-css-static' + path: '/lazy-css-static' + fullPath: '/lazy-css-static' + preLoaderRoute: typeof LazyCssStaticRouteImport parentRoute: typeof rootRouteImport } - '/r3': { - id: '/r3' - path: '/r3' - fullPath: '/r3' - preLoaderRoute: typeof R3RouteImport + '/r1': { + id: '/r1' + path: '/r1' + fullPath: '/r1' + preLoaderRoute: typeof R1RouteImport parentRoute: typeof rootRouteImport } '/r2': { @@ -270,46 +263,53 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof R2RouteImport parentRoute: typeof rootRouteImport } - '/r1': { - id: '/r1' - path: '/r1' - fullPath: '/r1' - preLoaderRoute: typeof R1RouteImport + '/r3': { + id: '/r3' + path: '/r3' + fullPath: '/r3' + preLoaderRoute: typeof R3RouteImport parentRoute: typeof rootRouteImport } - '/lazy-css-static': { - id: '/lazy-css-static' - path: '/lazy-css-static' - fullPath: '/lazy-css-static' - preLoaderRoute: typeof LazyCssStaticRouteImport + '/r4': { + id: '/r4' + path: '/r4' + fullPath: '/r4' + preLoaderRoute: typeof R4RouteImport parentRoute: typeof rootRouteImport } - '/lazy-css-lazy': { - id: '/lazy-css-lazy' - path: '/lazy-css-lazy' - fullPath: '/lazy-css-lazy' - preLoaderRoute: typeof LazyCssLazyRouteImport + '/r5': { + id: '/r5' + path: '/r5' + fullPath: '/r5' + preLoaderRoute: typeof R5RouteImport parentRoute: typeof rootRouteImport } - '/b': { - id: '/b' - path: '/b' - fullPath: '/b' - preLoaderRoute: typeof BRouteImport + '/r6': { + id: '/r6' + path: '/r6' + fullPath: '/r6' + preLoaderRoute: typeof R6RouteImport parentRoute: typeof rootRouteImport } - '/a': { - id: '/a' - path: '/a' - fullPath: '/a' - preLoaderRoute: typeof ARouteImport + '/shared-a': { + id: '/shared-a' + path: '/shared-a' + fullPath: '/shared-a' + preLoaderRoute: typeof SharedARouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/shared-b': { + id: '/shared-b' + path: '/shared-b' + fullPath: '/shared-b' + preLoaderRoute: typeof SharedBRouteImport + parentRoute: typeof rootRouteImport + } + '/shared-c': { + id: '/shared-c' + path: '/shared-c' + fullPath: '/shared-c' + preLoaderRoute: typeof SharedCRouteImport parentRoute: typeof rootRouteImport } } diff --git a/e2e/react-start/static-server-functions/src/routeTree.gen.ts b/e2e/react-start/static-server-functions/src/routeTree.gen.ts index fa19f26a67..ea6a6a3806 100644 --- a/e2e/react-start/static-server-functions/src/routeTree.gen.ts +++ b/e2e/react-start/static-server-functions/src/routeTree.gen.ts @@ -9,20 +9,20 @@ // 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 PostsRouteImport } from './routes/posts' import { Route as IndexRouteImport } from './routes/index' +import { Route as PostsRouteImport } from './routes/posts' import { Route as PostsIndexRouteImport } from './routes/posts.index' -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', + getParentRoute: () => rootRouteImport, +} as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -59,13 +59,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -73,6 +66,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport + parentRoute: typeof rootRouteImport + } '/posts/': { id: '/posts/' path: '/' diff --git a/e2e/react-start/streaming-ssr/src/routeTree.gen.ts b/e2e/react-start/streaming-ssr/src/routeTree.gen.ts index e1e3b367f1..c1a51b62f8 100644 --- a/e2e/react-start/streaming-ssr/src/routeTree.gen.ts +++ b/e2e/react-start/streaming-ssr/src/routeTree.gen.ts @@ -9,41 +9,41 @@ // 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 SyncOnlyRouteImport } from './routes/sync-only' -import { Route as StreamRouteImport } from './routes/stream' -import { Route as SlowRenderRouteImport } from './routes/slow-render' -import { Route as QueryHeavyRouteImport } from './routes/query-heavy' -import { Route as NestedDeferredRouteImport } from './routes/nested-deferred' -import { Route as ManyPromisesRouteImport } from './routes/many-promises' -import { Route as FastSerialRouteImport } from './routes/fast-serial' -import { Route as DeferredRejectionRouteImport } from './routes/deferred-rejection' -import { Route as DeferredRouteImport } from './routes/deferred' -import { Route as ConcurrentRouteImport } from './routes/concurrent' import { Route as IndexRouteImport } from './routes/index' +import { Route as ConcurrentRouteImport } from './routes/concurrent' +import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as DeferredRejectionRouteImport } from './routes/deferred-rejection' +import { Route as FastSerialRouteImport } from './routes/fast-serial' +import { Route as ManyPromisesRouteImport } from './routes/many-promises' +import { Route as NestedDeferredRouteImport } from './routes/nested-deferred' +import { Route as QueryHeavyRouteImport } from './routes/query-heavy' +import { Route as SlowRenderRouteImport } from './routes/slow-render' +import { Route as StreamRouteImport } from './routes/stream' +import { Route as SyncOnlyRouteImport } from './routes/sync-only' -const SyncOnlyRoute = SyncOnlyRouteImport.update({ - id: '/sync-only', - path: '/sync-only', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const StreamRoute = StreamRouteImport.update({ - id: '/stream', - path: '/stream', +const ConcurrentRoute = ConcurrentRouteImport.update({ + id: '/concurrent', + path: '/concurrent', getParentRoute: () => rootRouteImport, } as any) -const SlowRenderRoute = SlowRenderRouteImport.update({ - id: '/slow-render', - path: '/slow-render', +const DeferredRoute = DeferredRouteImport.update({ + id: '/deferred', + path: '/deferred', getParentRoute: () => rootRouteImport, } as any) -const QueryHeavyRoute = QueryHeavyRouteImport.update({ - id: '/query-heavy', - path: '/query-heavy', +const DeferredRejectionRoute = DeferredRejectionRouteImport.update({ + id: '/deferred-rejection', + path: '/deferred-rejection', getParentRoute: () => rootRouteImport, } as any) -const NestedDeferredRoute = NestedDeferredRouteImport.update({ - id: '/nested-deferred', - path: '/nested-deferred', +const FastSerialRoute = FastSerialRouteImport.update({ + id: '/fast-serial', + path: '/fast-serial', getParentRoute: () => rootRouteImport, } as any) const ManyPromisesRoute = ManyPromisesRouteImport.update({ @@ -51,29 +51,29 @@ const ManyPromisesRoute = ManyPromisesRouteImport.update({ path: '/many-promises', getParentRoute: () => rootRouteImport, } as any) -const FastSerialRoute = FastSerialRouteImport.update({ - id: '/fast-serial', - path: '/fast-serial', +const NestedDeferredRoute = NestedDeferredRouteImport.update({ + id: '/nested-deferred', + path: '/nested-deferred', getParentRoute: () => rootRouteImport, } as any) -const DeferredRejectionRoute = DeferredRejectionRouteImport.update({ - id: '/deferred-rejection', - path: '/deferred-rejection', +const QueryHeavyRoute = QueryHeavyRouteImport.update({ + id: '/query-heavy', + path: '/query-heavy', getParentRoute: () => rootRouteImport, } as any) -const DeferredRoute = DeferredRouteImport.update({ - id: '/deferred', - path: '/deferred', +const SlowRenderRoute = SlowRenderRouteImport.update({ + id: '/slow-render', + path: '/slow-render', getParentRoute: () => rootRouteImport, } as any) -const ConcurrentRoute = ConcurrentRouteImport.update({ - id: '/concurrent', - path: '/concurrent', +const StreamRoute = StreamRouteImport.update({ + id: '/stream', + path: '/stream', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const SyncOnlyRoute = SyncOnlyRouteImport.update({ + id: '/sync-only', + path: '/sync-only', getParentRoute: () => rootRouteImport, } as any) @@ -175,39 +175,39 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/sync-only': { - id: '/sync-only' - path: '/sync-only' - fullPath: '/sync-only' - preLoaderRoute: typeof SyncOnlyRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/stream': { - id: '/stream' - path: '/stream' - fullPath: '/stream' - preLoaderRoute: typeof StreamRouteImport + '/concurrent': { + id: '/concurrent' + path: '/concurrent' + fullPath: '/concurrent' + preLoaderRoute: typeof ConcurrentRouteImport parentRoute: typeof rootRouteImport } - '/slow-render': { - id: '/slow-render' - path: '/slow-render' - fullPath: '/slow-render' - preLoaderRoute: typeof SlowRenderRouteImport + '/deferred': { + id: '/deferred' + path: '/deferred' + fullPath: '/deferred' + preLoaderRoute: typeof DeferredRouteImport parentRoute: typeof rootRouteImport } - '/query-heavy': { - id: '/query-heavy' - path: '/query-heavy' - fullPath: '/query-heavy' - preLoaderRoute: typeof QueryHeavyRouteImport + '/deferred-rejection': { + id: '/deferred-rejection' + path: '/deferred-rejection' + fullPath: '/deferred-rejection' + preLoaderRoute: typeof DeferredRejectionRouteImport parentRoute: typeof rootRouteImport } - '/nested-deferred': { - id: '/nested-deferred' - path: '/nested-deferred' - fullPath: '/nested-deferred' - preLoaderRoute: typeof NestedDeferredRouteImport + '/fast-serial': { + id: '/fast-serial' + path: '/fast-serial' + fullPath: '/fast-serial' + preLoaderRoute: typeof FastSerialRouteImport parentRoute: typeof rootRouteImport } '/many-promises': { @@ -217,39 +217,39 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ManyPromisesRouteImport parentRoute: typeof rootRouteImport } - '/fast-serial': { - id: '/fast-serial' - path: '/fast-serial' - fullPath: '/fast-serial' - preLoaderRoute: typeof FastSerialRouteImport + '/nested-deferred': { + id: '/nested-deferred' + path: '/nested-deferred' + fullPath: '/nested-deferred' + preLoaderRoute: typeof NestedDeferredRouteImport parentRoute: typeof rootRouteImport } - '/deferred-rejection': { - id: '/deferred-rejection' - path: '/deferred-rejection' - fullPath: '/deferred-rejection' - preLoaderRoute: typeof DeferredRejectionRouteImport + '/query-heavy': { + id: '/query-heavy' + path: '/query-heavy' + fullPath: '/query-heavy' + preLoaderRoute: typeof QueryHeavyRouteImport parentRoute: typeof rootRouteImport } - '/deferred': { - id: '/deferred' - path: '/deferred' - fullPath: '/deferred' - preLoaderRoute: typeof DeferredRouteImport + '/slow-render': { + id: '/slow-render' + path: '/slow-render' + fullPath: '/slow-render' + preLoaderRoute: typeof SlowRenderRouteImport parentRoute: typeof rootRouteImport } - '/concurrent': { - id: '/concurrent' - path: '/concurrent' - fullPath: '/concurrent' - preLoaderRoute: typeof ConcurrentRouteImport + '/stream': { + id: '/stream' + path: '/stream' + fullPath: '/stream' + preLoaderRoute: typeof StreamRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/sync-only': { + id: '/sync-only' + path: '/sync-only' + fullPath: '/sync-only' + preLoaderRoute: typeof SyncOnlyRouteImport parentRoute: typeof rootRouteImport } } diff --git a/e2e/react-start/transform-asset-urls/src/routeTree.gen.ts b/e2e/react-start/transform-asset-urls/src/routeTree.gen.ts index 421daf2790..d89758e684 100644 --- a/e2e/react-start/transform-asset-urls/src/routeTree.gen.ts +++ b/e2e/react-start/transform-asset-urls/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' +import { Route as AboutRouteImport } from './routes/about' -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/e2e/react-start/virtual-routes/src/routeTree.gen.ts b/e2e/react-start/virtual-routes/src/routeTree.gen.ts index 6a1de929b3..9addb6f078 100644 --- a/e2e/react-start/virtual-routes/src/routeTree.gen.ts +++ b/e2e/react-start/virtual-routes/src/routeTree.gen.ts @@ -9,83 +9,83 @@ // 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 pipeRouteImport } from './routes/pipe' -import { Route as postsPostsRouteImport } from './routes/posts/posts' -import { Route as layoutFirstLayoutRouteImport } from './routes/layout/first-layout' import { Route as homeRouteImport } from './routes/home' -import { Route as postsPostsDetailRouteImport } from './routes/posts/posts-detail' +import { Route as layoutFirstLayoutRouteImport } from './routes/layout/first-layout' +import { Route as postsPostsRouteImport } from './routes/posts/posts' +import { Route as pipeRouteImport } from './routes/pipe' import { Route as layoutSecondLayoutRouteImport } from './routes/layout/second-layout' -import { Route as postsPostsHomeRouteImport } from './routes/posts/posts-home' import { Route as ClassicHelloRouteRouteImport } from './routes/file-based-subtree/hello/route' +import { Route as postsPostsHomeRouteImport } from './routes/posts/posts-home' +import { Route as postsPostsDetailRouteImport } from './routes/posts/posts-detail' import { Route as ClassicHelloIndexRouteImport } from './routes/file-based-subtree/hello/index' -import { Route as ClassicHelloWorldRouteImport } from './routes/file-based-subtree/hello/world' import { Route as ClassicHelloUniverseRouteImport } from './routes/file-based-subtree/hello/universe' -import { Route as bRouteImport } from './routes/b' +import { Route as ClassicHelloWorldRouteImport } from './routes/file-based-subtree/hello/world' import { Route as aRouteImport } from './routes/a' +import { Route as bRouteImport } from './routes/b' -const pipeRoute = pipeRouteImport.update({ - id: '/special|pipe', - path: '/special|pipe', - getParentRoute: () => rootRouteImport, -} as any) -const postsPostsRoute = postsPostsRouteImport.update({ - id: '/posts', - path: '/posts', +const homeRoute = homeRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const layoutFirstLayoutRoute = layoutFirstLayoutRouteImport.update({ id: '/_first', getParentRoute: () => rootRouteImport, } as any) -const homeRoute = homeRouteImport.update({ - id: '/', - path: '/', +const postsPostsRoute = postsPostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const postsPostsDetailRoute = postsPostsDetailRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => postsPostsRoute, +const pipeRoute = pipeRouteImport.update({ + id: '/special|pipe', + path: '/special|pipe', + getParentRoute: () => rootRouteImport, } as any) const layoutSecondLayoutRoute = layoutSecondLayoutRouteImport.update({ id: '/_second-layout', getParentRoute: () => layoutFirstLayoutRoute, } as any) +const ClassicHelloRouteRoute = ClassicHelloRouteRouteImport.update({ + id: '/classic/hello', + path: '/classic/hello', + getParentRoute: () => rootRouteImport, +} as any) const postsPostsHomeRoute = postsPostsHomeRouteImport.update({ id: '/', path: '/', getParentRoute: () => postsPostsRoute, } as any) -const ClassicHelloRouteRoute = ClassicHelloRouteRouteImport.update({ - id: '/classic/hello', - path: '/classic/hello', - getParentRoute: () => rootRouteImport, +const postsPostsDetailRoute = postsPostsDetailRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => postsPostsRoute, } as any) const ClassicHelloIndexRoute = ClassicHelloIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => ClassicHelloRouteRoute, } as any) -const ClassicHelloWorldRoute = ClassicHelloWorldRouteImport.update({ - id: '/world', - path: '/world', - getParentRoute: () => ClassicHelloRouteRoute, -} as any) const ClassicHelloUniverseRoute = ClassicHelloUniverseRouteImport.update({ id: '/universe', path: '/universe', getParentRoute: () => ClassicHelloRouteRoute, } as any) -const bRoute = bRouteImport.update({ - id: '/route-without-file/layout-b', - path: '/route-without-file/layout-b', - getParentRoute: () => layoutSecondLayoutRoute, +const ClassicHelloWorldRoute = ClassicHelloWorldRouteImport.update({ + id: '/world', + path: '/world', + getParentRoute: () => ClassicHelloRouteRoute, } as any) const aRoute = aRouteImport.update({ id: '/route-without-file/layout-a', path: '/route-without-file/layout-a', getParentRoute: () => layoutSecondLayoutRoute, } as any) +const bRoute = bRouteImport.update({ + id: '/route-without-file/layout-b', + path: '/route-without-file/layout-b', + getParentRoute: () => layoutSecondLayoutRoute, +} as any) export interface FileRoutesByFullPath { '/': typeof homeRoute @@ -179,18 +179,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/special|pipe': { - id: '/special|pipe' - path: '/special|pipe' - fullPath: '/special|pipe' - preLoaderRoute: typeof pipeRouteImport - parentRoute: typeof rootRouteImport - } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof postsPostsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof homeRouteImport parentRoute: typeof rootRouteImport } '/_first': { @@ -200,19 +193,19 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof layoutFirstLayoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof homeRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof postsPostsRouteImport parentRoute: typeof rootRouteImport } - '/posts/$postId': { - id: '/posts/$postId' - path: '/$postId' - fullPath: '/posts/$postId' - preLoaderRoute: typeof postsPostsDetailRouteImport - parentRoute: typeof postsPostsRoute + '/special|pipe': { + id: '/special|pipe' + path: '/special|pipe' + fullPath: '/special|pipe' + preLoaderRoute: typeof pipeRouteImport + parentRoute: typeof rootRouteImport } '/_first/_second-layout': { id: '/_first/_second-layout' @@ -221,6 +214,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof layoutSecondLayoutRouteImport parentRoute: typeof layoutFirstLayoutRoute } + '/classic/hello': { + id: '/classic/hello' + path: '/classic/hello' + fullPath: '/classic/hello' + preLoaderRoute: typeof ClassicHelloRouteRouteImport + parentRoute: typeof rootRouteImport + } '/posts/': { id: '/posts/' path: '/' @@ -228,12 +228,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof postsPostsHomeRouteImport parentRoute: typeof postsPostsRoute } - '/classic/hello': { - id: '/classic/hello' - path: '/classic/hello' - fullPath: '/classic/hello' - preLoaderRoute: typeof ClassicHelloRouteRouteImport - parentRoute: typeof rootRouteImport + '/posts/$postId': { + id: '/posts/$postId' + path: '/$postId' + fullPath: '/posts/$postId' + preLoaderRoute: typeof postsPostsDetailRouteImport + parentRoute: typeof postsPostsRoute } '/classic/hello/': { id: '/classic/hello/' @@ -242,13 +242,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ClassicHelloIndexRouteImport parentRoute: typeof ClassicHelloRouteRoute } - '/classic/hello/world': { - id: '/classic/hello/world' - path: '/world' - fullPath: '/classic/hello/world' - preLoaderRoute: typeof ClassicHelloWorldRouteImport - parentRoute: typeof ClassicHelloRouteRoute - } '/classic/hello/universe': { id: '/classic/hello/universe' path: '/universe' @@ -256,12 +249,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ClassicHelloUniverseRouteImport parentRoute: typeof ClassicHelloRouteRoute } - '/_first/_second-layout/route-without-file/layout-b': { - id: '/_first/_second-layout/route-without-file/layout-b' - path: '/route-without-file/layout-b' - fullPath: '/route-without-file/layout-b' - preLoaderRoute: typeof bRouteImport - parentRoute: typeof layoutSecondLayoutRoute + '/classic/hello/world': { + id: '/classic/hello/world' + path: '/world' + fullPath: '/classic/hello/world' + preLoaderRoute: typeof ClassicHelloWorldRouteImport + parentRoute: typeof ClassicHelloRouteRoute } '/_first/_second-layout/route-without-file/layout-a': { id: '/_first/_second-layout/route-without-file/layout-a' @@ -270,6 +263,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof aRouteImport parentRoute: typeof layoutSecondLayoutRoute } + '/_first/_second-layout/route-without-file/layout-b': { + id: '/_first/_second-layout/route-without-file/layout-b' + path: '/route-without-file/layout-b' + fullPath: '/route-without-file/layout-b' + preLoaderRoute: typeof bRouteImport + parentRoute: typeof layoutSecondLayoutRoute + } } } diff --git a/e2e/react-start/website/src/routeTree.gen.ts b/e2e/react-start/website/src/routeTree.gen.ts index e26402107f..03770b5ad9 100644 --- a/e2e/react-start/website/src/routeTree.gen.ts +++ b/e2e/react-start/website/src/routeTree.gen.ts @@ -10,47 +10,47 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as LibraryRouteImport } from './routes/_library' -import { Route as LibraryIndexRouteImport } from './routes/_library.index' import { Route as ProjectIndexRouteImport } from './routes/$project.index' +import { Route as LibraryIndexRouteImport } from './routes/_library.index' import { Route as LibraryProjectRouteImport } from './routes/_library.$project' -import { Route as LibraryProjectVersionIndexRouteImport } from './routes/_library.$project.$version.index' import { Route as ProjectVersionDocsIndexRouteImport } from './routes/$project.$version.docs.index' +import { Route as LibraryProjectVersionIndexRouteImport } from './routes/_library.$project.$version.index' import { Route as ProjectVersionDocsFrameworkFrameworkRouteImport } from './routes/$project.$version.docs.framework.$framework' import { Route as ProjectVersionDocsFrameworkFrameworkIndexRouteImport } from './routes/$project.$version.docs.framework.$framework.index' -import { Route as ProjectVersionDocsFrameworkFrameworkChar123Char125DotmdRouteImport } from './routes/$project.$version.docs.framework.$framework.{$}[.]md' import { Route as ProjectVersionDocsFrameworkFrameworkSplatRouteImport } from './routes/$project.$version.docs.framework.$framework.$' +import { Route as ProjectVersionDocsFrameworkFrameworkChar123Char125DotmdRouteImport } from './routes/$project.$version.docs.framework.$framework.{$}[.]md' import { Route as ProjectVersionDocsFrameworkFrameworkExamplesSplatRouteImport } from './routes/$project.$version.docs.framework.$framework.examples.$' const LibraryRoute = LibraryRouteImport.update({ id: '/_library', getParentRoute: () => rootRouteImport, } as any) -const LibraryIndexRoute = LibraryIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => LibraryRoute, -} as any) const ProjectIndexRoute = ProjectIndexRouteImport.update({ id: '/$project/', path: '/$project/', getParentRoute: () => rootRouteImport, } as any) +const LibraryIndexRoute = LibraryIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => LibraryRoute, +} as any) const LibraryProjectRoute = LibraryProjectRouteImport.update({ id: '/$project', path: '/$project', getParentRoute: () => LibraryRoute, } as any) +const ProjectVersionDocsIndexRoute = ProjectVersionDocsIndexRouteImport.update({ + id: '/$project/$version/docs/', + path: '/$project/$version/docs/', + getParentRoute: () => rootRouteImport, +} as any) const LibraryProjectVersionIndexRoute = LibraryProjectVersionIndexRouteImport.update({ id: '/$version/', path: '/$version/', getParentRoute: () => LibraryProjectRoute, } as any) -const ProjectVersionDocsIndexRoute = ProjectVersionDocsIndexRouteImport.update({ - id: '/$project/$version/docs/', - path: '/$project/$version/docs/', - getParentRoute: () => rootRouteImport, -} as any) const ProjectVersionDocsFrameworkFrameworkRoute = ProjectVersionDocsFrameworkFrameworkRouteImport.update({ id: '/$project/$version/docs/framework/$framework', @@ -63,18 +63,18 @@ const ProjectVersionDocsFrameworkFrameworkIndexRoute = path: '/', getParentRoute: () => ProjectVersionDocsFrameworkFrameworkRoute, } as any) -const ProjectVersionDocsFrameworkFrameworkChar123Char125DotmdRoute = - ProjectVersionDocsFrameworkFrameworkChar123Char125DotmdRouteImport.update({ - id: '/{$}.md', - path: '/{$}.md', - getParentRoute: () => ProjectVersionDocsFrameworkFrameworkRoute, - } as any) const ProjectVersionDocsFrameworkFrameworkSplatRoute = ProjectVersionDocsFrameworkFrameworkSplatRouteImport.update({ id: '/$', path: '/$', getParentRoute: () => ProjectVersionDocsFrameworkFrameworkRoute, } as any) +const ProjectVersionDocsFrameworkFrameworkChar123Char125DotmdRoute = + ProjectVersionDocsFrameworkFrameworkChar123Char125DotmdRouteImport.update({ + id: '/{$}.md', + path: '/{$}.md', + getParentRoute: () => ProjectVersionDocsFrameworkFrameworkRoute, + } as any) const ProjectVersionDocsFrameworkFrameworkExamplesSplatRoute = ProjectVersionDocsFrameworkFrameworkExamplesSplatRouteImport.update({ id: '/examples/$', @@ -172,13 +172,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LibraryRouteImport parentRoute: typeof rootRouteImport } - '/_library/': { - id: '/_library/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof LibraryIndexRouteImport - parentRoute: typeof LibraryRoute - } '/$project/': { id: '/$project/' path: '/$project' @@ -186,6 +179,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ProjectIndexRouteImport parentRoute: typeof rootRouteImport } + '/_library/': { + id: '/_library/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof LibraryIndexRouteImport + parentRoute: typeof LibraryRoute + } '/_library/$project': { id: '/_library/$project' path: '/$project' @@ -193,13 +193,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LibraryProjectRouteImport parentRoute: typeof LibraryRoute } - '/_library/$project/$version/': { - id: '/_library/$project/$version/' - path: '/$version' - fullPath: '/$project/$version/' - preLoaderRoute: typeof LibraryProjectVersionIndexRouteImport - parentRoute: typeof LibraryProjectRoute - } '/$project/$version/docs/': { id: '/$project/$version/docs/' path: '/$project/$version/docs' @@ -207,6 +200,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ProjectVersionDocsIndexRouteImport parentRoute: typeof rootRouteImport } + '/_library/$project/$version/': { + id: '/_library/$project/$version/' + path: '/$version' + fullPath: '/$project/$version/' + preLoaderRoute: typeof LibraryProjectVersionIndexRouteImport + parentRoute: typeof LibraryProjectRoute + } '/$project/$version/docs/framework/$framework': { id: '/$project/$version/docs/framework/$framework' path: '/$project/$version/docs/framework/$framework' @@ -221,13 +221,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ProjectVersionDocsFrameworkFrameworkIndexRouteImport parentRoute: typeof ProjectVersionDocsFrameworkFrameworkRoute } - '/$project/$version/docs/framework/$framework/{$}.md': { - id: '/$project/$version/docs/framework/$framework/{$}.md' - path: '/{$}.md' - fullPath: '/$project/$version/docs/framework/$framework/{$}.md' - preLoaderRoute: typeof ProjectVersionDocsFrameworkFrameworkChar123Char125DotmdRouteImport - parentRoute: typeof ProjectVersionDocsFrameworkFrameworkRoute - } '/$project/$version/docs/framework/$framework/$': { id: '/$project/$version/docs/framework/$framework/$' path: '/$' @@ -235,6 +228,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ProjectVersionDocsFrameworkFrameworkSplatRouteImport parentRoute: typeof ProjectVersionDocsFrameworkFrameworkRoute } + '/$project/$version/docs/framework/$framework/{$}.md': { + id: '/$project/$version/docs/framework/$framework/{$}.md' + path: '/{$}.md' + fullPath: '/$project/$version/docs/framework/$framework/{$}.md' + preLoaderRoute: typeof ProjectVersionDocsFrameworkFrameworkChar123Char125DotmdRouteImport + parentRoute: typeof ProjectVersionDocsFrameworkFrameworkRoute + } '/$project/$version/docs/framework/$framework/examples/$': { id: '/$project/$version/docs/framework/$framework/examples/$' path: '/examples/$' diff --git a/e2e/solid-router/basepath-file-based/src/routeTree.gen.ts b/e2e/solid-router/basepath-file-based/src/routeTree.gen.ts index 333a815c38..98df1e83d8 100644 --- a/e2e/solid-router/basepath-file-based/src/routeTree.gen.ts +++ b/e2e/solid-router/basepath-file-based/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' +import { Route as AboutRouteImport } from './routes/about' -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/e2e/solid-router/basic-file-based-code-splitting/src/routeTree.gen.ts b/e2e/solid-router/basic-file-based-code-splitting/src/routeTree.gen.ts index c0f1e2bc16..17076d5404 100644 --- a/e2e/solid-router/basic-file-based-code-splitting/src/routeTree.gen.ts +++ b/e2e/solid-router/basic-file-based-code-splitting/src/routeTree.gen.ts @@ -9,25 +9,24 @@ // 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 WithoutLoaderRouteImport } from './routes/without-loader' -import { Route as ViewportTestRouteImport } from './routes/viewport-test' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as LayoutRouteImport } from './routes/_layout' import { Route as IndexRouteImport } from './routes/index' +import { Route as LayoutRouteImport } from './routes/_layout' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as ViewportTestRouteImport } from './routes/viewport-test' +import { Route as WithoutLoaderRouteImport } from './routes/without-loader' +import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' -import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' +import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' -const WithoutLoaderRoute = WithoutLoaderRouteImport.update({ - id: '/without-loader', - path: '/without-loader', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const ViewportTestRoute = ViewportTestRouteImport.update({ - id: '/viewport-test', - path: '/viewport-test', +const LayoutRoute = LayoutRouteImport.update({ + id: '/_layout', getParentRoute: () => rootRouteImport, } as any) const PostsRoute = PostsRouteImport.update({ @@ -35,15 +34,20 @@ const PostsRoute = PostsRouteImport.update({ path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const LayoutRoute = LayoutRouteImport.update({ - id: '/_layout', +const ViewportTestRoute = ViewportTestRouteImport.update({ + id: '/viewport-test', + path: '/viewport-test', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const WithoutLoaderRoute = WithoutLoaderRouteImport.update({ + id: '/without-loader', + path: '/without-loader', getParentRoute: () => rootRouteImport, } as any) +const LayoutLayout2Route = LayoutLayout2RouteImport.update({ + id: '/_layout-2', + getParentRoute: () => LayoutRoute, +} as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -54,20 +58,16 @@ const PostsPostIdRoute = PostsPostIdRouteImport.update({ path: '/$postId', getParentRoute: () => PostsRoute, } as any) -const LayoutLayout2Route = LayoutLayout2RouteImport.update({ - id: '/_layout-2', - getParentRoute: () => LayoutRoute, +const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ + id: '/layout-a', + path: '/layout-a', + getParentRoute: () => LayoutLayout2Route, } as any) const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ id: '/layout-b', path: '/layout-b', getParentRoute: () => LayoutLayout2Route, } as any) -const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ - id: '/layout-a', - path: '/layout-a', - getParentRoute: () => LayoutLayout2Route, -} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -145,18 +145,18 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/without-loader': { - id: '/without-loader' - path: '/without-loader' - fullPath: '/without-loader' - preLoaderRoute: typeof WithoutLoaderRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/viewport-test': { - id: '/viewport-test' - path: '/viewport-test' - fullPath: '/viewport-test' - preLoaderRoute: typeof ViewportTestRouteImport + '/_layout': { + id: '/_layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } '/posts': { @@ -166,20 +166,27 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/_layout': { - id: '/_layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutRouteImport + '/viewport-test': { + id: '/viewport-test' + path: '/viewport-test' + fullPath: '/viewport-test' + preLoaderRoute: typeof ViewportTestRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/without-loader': { + id: '/without-loader' + path: '/without-loader' + fullPath: '/without-loader' + preLoaderRoute: typeof WithoutLoaderRouteImport parentRoute: typeof rootRouteImport } + '/_layout/_layout-2': { + id: '/_layout/_layout-2' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutLayout2RouteImport + parentRoute: typeof LayoutRoute + } '/posts/': { id: '/posts/' path: '/' @@ -194,12 +201,12 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/_layout/_layout-2': { - id: '/_layout/_layout-2' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutLayout2RouteImport - parentRoute: typeof LayoutRoute + '/_layout/_layout-2/layout-a': { + id: '/_layout/_layout-2/layout-a' + path: '/layout-a' + fullPath: '/layout-a' + preLoaderRoute: typeof LayoutLayout2LayoutARouteImport + parentRoute: typeof LayoutLayout2Route } '/_layout/_layout-2/layout-b': { id: '/_layout/_layout-2/layout-b' @@ -208,13 +215,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport parentRoute: typeof LayoutLayout2Route } - '/_layout/_layout-2/layout-a': { - id: '/_layout/_layout-2/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: typeof LayoutLayout2LayoutARouteImport - parentRoute: typeof LayoutLayout2Route - } } } diff --git a/e2e/solid-router/basic-file-based/src/routeTree.gen.ts b/e2e/solid-router/basic-file-based/src/routeTree.gen.ts index 04fe587374..c7cbb94b56 100644 --- a/e2e/solid-router/basic-file-based/src/routeTree.gen.ts +++ b/e2e/solid-router/basic-file-based/src/routeTree.gen.ts @@ -9,135 +9,134 @@ // 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 RemountDepsRouteImport } from './routes/remountDeps' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as NotRemountDepsRouteImport } from './routes/notRemountDeps' -import { Route as MasksRouteImport } from './routes/masks' -import { Route as HoverPreloadHashRouteImport } from './routes/hover-preload-hash' -import { Route as EditingBRouteImport } from './routes/editing-b' -import { Route as EditingARouteImport } from './routes/editing-a' -import { Route as ComponentTypesTestRouteImport } from './routes/component-types-test' -import { Route as AnchorRouteImport } from './routes/anchor' +import { Route as IndexRouteImport } from './routes/index' import { Route as LayoutRouteImport } from './routes/_layout' -import { Route as Char45824Char54620Char48124Char44397RouteRouteImport } from './routes/대한민국/route' -import { Route as SearchParamsRouteRouteImport } from './routes/search-params/route' +import { Route as AnchorRouteImport } from './routes/anchor' +import { Route as ComponentTypesTestRouteImport } from './routes/component-types-test' +import { Route as EditingARouteImport } from './routes/editing-a' +import { Route as EditingBRouteImport } from './routes/editing-b' +import { Route as HoverPreloadHashRouteImport } from './routes/hover-preload-hash' +import { Route as MasksRouteImport } from './routes/masks' import { Route as NonNestedRouteRouteImport } from './routes/non-nested/route' -import { Route as IndexRouteImport } from './routes/index' -import { Route as SearchParamsIndexRouteImport } from './routes/search-params/index' -import { Route as RelativeIndexRouteImport } from './routes/relative/index' -import { Route as RedirectIndexRouteImport } from './routes/redirect/index' -import { Route as PostsIndexRouteImport } from './routes/posts.index' +import { Route as NotRemountDepsRouteImport } from './routes/notRemountDeps' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as RemountDepsRouteImport } from './routes/remountDeps' +import { Route as SearchParamsRouteRouteImport } from './routes/search-params/route' +import { Route as Char45824Char54620Char48124Char44397RouteRouteImport } from './routes/대한민국/route' +import { Route as anotherGroupOnlyrouteinsideRouteImport } from './routes/(another-group)/onlyrouteinside' +import { Route as groupLayoutRouteImport } from './routes/(group)/_layout' +import { Route as groupInsideRouteImport } from './routes/(group)/inside' +import { Route as groupLazyinsideRouteImport } from './routes/(group)/lazyinside' +import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' +import { Route as NonNestedDeepRouteRouteImport } from './routes/non-nested/deep/route' +import { Route as NonNestedNamedRouteRouteImport } from './routes/non-nested/named/route' +import { Route as NonNestedPathRouteRouteImport } from './routes/non-nested/path/route' +import { Route as NonNestedPrefixRouteRouteImport } from './routes/non-nested/prefix/route' +import { Route as NonNestedSuffixRouteRouteImport } from './routes/non-nested/suffix/route' import { Route as ParamsPsIndexRouteImport } from './routes/params-ps/index' -import { Route as SearchParamsDefaultRouteImport } from './routes/search-params/default' -import { Route as RedirectTargetRouteImport } from './routes/redirect/$target' +import { Route as ParamsPsNonNestedRouteRouteImport } from './routes/params-ps/non-nested/route' +import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' -import { Route as groupLazyinsideRouteImport } from './routes/(group)/lazyinside' -import { Route as groupInsideRouteImport } from './routes/(group)/inside' -import { Route as groupLayoutRouteImport } from './routes/(group)/_layout' -import { Route as anotherGroupOnlyrouteinsideRouteImport } from './routes/(another-group)/onlyrouteinside' -import { Route as RelativeUseNavigateRouteRouteImport } from './routes/relative/useNavigate/route' +import { Route as RedirectIndexRouteImport } from './routes/redirect/index' +import { Route as RedirectTargetRouteImport } from './routes/redirect/$target' +import { Route as RelativeIndexRouteImport } from './routes/relative/index' import { Route as RelativeLinkRouteRouteImport } from './routes/relative/link/route' -import { Route as ParamsPsNonNestedRouteRouteImport } from './routes/params-ps/non-nested/route' -import { Route as NonNestedSuffixRouteRouteImport } from './routes/non-nested/suffix/route' -import { Route as NonNestedPrefixRouteRouteImport } from './routes/non-nested/prefix/route' -import { Route as NonNestedPathRouteRouteImport } from './routes/non-nested/path/route' -import { Route as NonNestedNamedRouteRouteImport } from './routes/non-nested/named/route' -import { Route as NonNestedDeepRouteRouteImport } from './routes/non-nested/deep/route' -import { Route as RedirectTargetIndexRouteImport } from './routes/redirect/$target/index' -import { Route as ParamsPsWildcardIndexRouteImport } from './routes/params-ps/wildcard/index' +import { Route as RelativeUseNavigateRouteRouteImport } from './routes/relative/useNavigate/route' +import { Route as SearchParamsIndexRouteImport } from './routes/search-params/index' +import { Route as SearchParamsDefaultRouteImport } from './routes/search-params/default' +import { Route as groupLayoutInsidelayoutRouteImport } from './routes/(group)/_layout.insidelayout' +import { Route as groupSubfolderInsideRouteImport } from './routes/(group)/subfolder/inside' +import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' +import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' +import { Route as MasksAdminUserIdRouteImport } from './routes/masks.admin.$userId' +import { Route as MasksPublicUsernameRouteImport } from './routes/masks.public.$username' +import { Route as NonNestedDeepBazRouteRouteImport } from './routes/non-nested/deep/$baz.route' +import { Route as NonNestedNamedBazRouteRouteImport } from './routes/non-nested/named/$baz.route' +import { Route as NonNestedPathBazRouteRouteImport } from './routes/non-nested/path/baz.route' +import { Route as NonNestedPrefixPrefixChar123bazChar125RouteRouteImport } from './routes/non-nested/prefix/prefix{$baz}.route' +import { Route as NonNestedSuffixChar123bazChar125suffixRouteRouteImport } from './routes/non-nested/suffix/{$baz}suffix.route' import { Route as ParamsPsNamedIndexRouteImport } from './routes/params-ps/named/index' -import { Route as Char45824Char54620Char48124Char44397Char55357Char56960IdRouteImport } from './routes/대한민국/🚀.$id' -import { Route as Char45824Char54620Char48124Char44397WildcardSplatRouteImport } from './routes/대한민국/wildcard.$' -import { Route as TransitionTypingCreateResourceRouteImport } from './routes/transition/typing/create-resource' -import { Route as TransitionCountCreateResourceRouteImport } from './routes/transition/count/create-resource' -import { Route as RelativeUseNavigateRelativeUseNavigateBRouteImport } from './routes/relative/useNavigate/relative-useNavigate-b' -import { Route as RelativeUseNavigateRelativeUseNavigateARouteImport } from './routes/relative/useNavigate/relative-useNavigate-a' -import { Route as RelativeLinkRelativeLinkBRouteImport } from './routes/relative/link/relative-link-b' -import { Route as RelativeLinkRelativeLinkARouteImport } from './routes/relative/link/relative-link-a' -import { Route as RedirectPreloadThirdRouteImport } from './routes/redirect/preload/third' -import { Route as RedirectPreloadSecondRouteImport } from './routes/redirect/preload/second' -import { Route as RedirectPreloadFirstRouteImport } from './routes/redirect/preload/first' -import { Route as RedirectTargetViaLoaderRouteImport } from './routes/redirect/$target/via-loader' -import { Route as RedirectTargetViaBeforeLoadRouteImport } from './routes/redirect/$target/via-beforeLoad' -import { Route as PostsPostIdEditRouteImport } from './routes/posts_.$postId.edit' -import { Route as ParamsSingleValueRouteImport } from './routes/params.single.$value' -import { Route as ParamsPsWildcardChar123Char125suffixAtChar45824RouteImport } from './routes/params-ps/wildcard/{$}suffix@대' -import { Route as ParamsPsWildcardChar123Char125suffixRouteImport } from './routes/params-ps/wildcard/{$}suffix' -import { Route as ParamsPsWildcardPrefixChar123Char125RouteImport } from './routes/params-ps/wildcard/prefix{$}' -import { Route as ParamsPsWildcardPrefixAtChar45824Char123Char125RouteImport } from './routes/params-ps/wildcard/prefix@대{$}' -import { Route as ParamsPsWildcardSplatRouteImport } from './routes/params-ps/wildcard/$' -import { Route as ParamsPsNamedChar123fooChar125suffixRouteImport } from './routes/params-ps/named/{$foo}suffix' +import { Route as ParamsPsNamedFooRouteRouteImport } from './routes/params-ps/named/$foo/route' import { Route as ParamsPsNamedPrefixChar123fooChar125RouteImport } from './routes/params-ps/named/prefix{$foo}' -import { Route as MasksPublicUsernameRouteImport } from './routes/masks.public.$username' -import { Route as MasksAdminUserIdRouteImport } from './routes/masks.admin.$userId' -import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' -import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' -import { Route as groupSubfolderInsideRouteImport } from './routes/(group)/subfolder/inside' -import { Route as groupLayoutInsidelayoutRouteImport } from './routes/(group)/_layout.insidelayout' +import { Route as ParamsPsNamedChar123fooChar125suffixRouteImport } from './routes/params-ps/named/{$foo}suffix' import { Route as ParamsPsNonNestedFooRouteRouteImport } from './routes/params-ps/non-nested/$foo_/route' -import { Route as ParamsPsNamedFooRouteRouteImport } from './routes/params-ps/named/$foo/route' -import { Route as NonNestedSuffixChar123bazChar125suffixRouteRouteImport } from './routes/non-nested/suffix/{$baz}suffix.route' -import { Route as NonNestedPrefixPrefixChar123bazChar125RouteRouteImport } from './routes/non-nested/prefix/prefix{$baz}.route' -import { Route as NonNestedPathBazRouteRouteImport } from './routes/non-nested/path/baz.route' -import { Route as NonNestedNamedBazRouteRouteImport } from './routes/non-nested/named/$baz.route' -import { Route as NonNestedDeepBazRouteRouteImport } from './routes/non-nested/deep/$baz.route' -import { Route as RelativeUseNavigateWithSearchIndexRouteImport } from './routes/relative/useNavigate/with-search/index' -import { Route as RelativeUseNavigatePathIndexRouteImport } from './routes/relative/useNavigate/path/index' -import { Route as RelativeUseNavigateNestedIndexRouteImport } from './routes/relative/useNavigate/nested/index' -import { Route as RelativeLinkWithSearchIndexRouteImport } from './routes/relative/link/with-search/index' -import { Route as RelativeLinkPathIndexRouteImport } from './routes/relative/link/path/index' -import { Route as RelativeLinkNestedIndexRouteImport } from './routes/relative/link/nested/index' -import { Route as NonNestedSuffixChar123bazChar125suffixIndexRouteImport } from './routes/non-nested/suffix/{$baz}suffix.index' -import { Route as NonNestedPrefixPrefixChar123bazChar125IndexRouteImport } from './routes/non-nested/prefix/prefix{$baz}.index' -import { Route as NonNestedPathBazIndexRouteImport } from './routes/non-nested/path/baz.index' -import { Route as NonNestedNamedBazIndexRouteImport } from './routes/non-nested/named/$baz.index' +import { Route as ParamsPsWildcardIndexRouteImport } from './routes/params-ps/wildcard/index' +import { Route as ParamsPsWildcardSplatRouteImport } from './routes/params-ps/wildcard/$' +import { Route as ParamsPsWildcardPrefixAtChar45824Char123Char125RouteImport } from './routes/params-ps/wildcard/prefix@대{$}' +import { Route as ParamsPsWildcardPrefixChar123Char125RouteImport } from './routes/params-ps/wildcard/prefix{$}' +import { Route as ParamsPsWildcardChar123Char125suffixRouteImport } from './routes/params-ps/wildcard/{$}suffix' +import { Route as ParamsPsWildcardChar123Char125suffixAtChar45824RouteImport } from './routes/params-ps/wildcard/{$}suffix@대' +import { Route as ParamsSingleValueRouteImport } from './routes/params.single.$value' +import { Route as PostsPostIdEditRouteImport } from './routes/posts_.$postId.edit' +import { Route as RedirectTargetIndexRouteImport } from './routes/redirect/$target/index' +import { Route as RedirectTargetViaBeforeLoadRouteImport } from './routes/redirect/$target/via-beforeLoad' +import { Route as RedirectTargetViaLoaderRouteImport } from './routes/redirect/$target/via-loader' +import { Route as RedirectPreloadFirstRouteImport } from './routes/redirect/preload/first' +import { Route as RedirectPreloadSecondRouteImport } from './routes/redirect/preload/second' +import { Route as RedirectPreloadThirdRouteImport } from './routes/redirect/preload/third' +import { Route as RelativeLinkRelativeLinkARouteImport } from './routes/relative/link/relative-link-a' +import { Route as RelativeLinkRelativeLinkBRouteImport } from './routes/relative/link/relative-link-b' +import { Route as RelativeUseNavigateRelativeUseNavigateARouteImport } from './routes/relative/useNavigate/relative-useNavigate-a' +import { Route as RelativeUseNavigateRelativeUseNavigateBRouteImport } from './routes/relative/useNavigate/relative-useNavigate-b' +import { Route as TransitionCountCreateResourceRouteImport } from './routes/transition/count/create-resource' +import { Route as TransitionTypingCreateResourceRouteImport } from './routes/transition/typing/create-resource' +import { Route as Char45824Char54620Char48124Char44397WildcardSplatRouteImport } from './routes/대한민국/wildcard.$' +import { Route as Char45824Char54620Char48124Char44397Char55357Char56960IdRouteImport } from './routes/대한민국/🚀.$id' import { Route as NonNestedDeepBazIndexRouteImport } from './routes/non-nested/deep/$baz.index' -import { Route as ParamsPsNonNestedFooBarRouteImport } from './routes/params-ps/non-nested/$foo_/$bar' -import { Route as NonNestedSuffixChar123bazChar125suffixBarRouteImport } from './routes/non-nested/suffix/{$baz}suffix_.bar' -import { Route as NonNestedSuffixChar123bazChar125suffixFooRouteImport } from './routes/non-nested/suffix/{$baz}suffix.foo' -import { Route as NonNestedPrefixPrefixChar123bazChar125BarRouteImport } from './routes/non-nested/prefix/prefix{$baz}_.bar' -import { Route as NonNestedPrefixPrefixChar123bazChar125FooRouteImport } from './routes/non-nested/prefix/prefix{$baz}.foo' -import { Route as NonNestedPathBazBarRouteImport } from './routes/non-nested/path/baz_.bar' -import { Route as NonNestedPathBazFooRouteImport } from './routes/non-nested/path/baz.foo' -import { Route as NonNestedNamedBazBarRouteImport } from './routes/non-nested/named/$baz_.bar' +import { Route as NonNestedDeepBazBarRouteRouteImport } from './routes/non-nested/deep/$baz_.bar.route' +import { Route as NonNestedNamedBazIndexRouteImport } from './routes/non-nested/named/$baz.index' import { Route as NonNestedNamedBazFooRouteImport } from './routes/non-nested/named/$baz.foo' +import { Route as NonNestedNamedBazBarRouteImport } from './routes/non-nested/named/$baz_.bar' +import { Route as NonNestedPathBazIndexRouteImport } from './routes/non-nested/path/baz.index' +import { Route as NonNestedPathBazFooRouteImport } from './routes/non-nested/path/baz.foo' +import { Route as NonNestedPathBazBarRouteImport } from './routes/non-nested/path/baz_.bar' +import { Route as NonNestedPrefixPrefixChar123bazChar125IndexRouteImport } from './routes/non-nested/prefix/prefix{$baz}.index' +import { Route as NonNestedPrefixPrefixChar123bazChar125FooRouteImport } from './routes/non-nested/prefix/prefix{$baz}.foo' +import { Route as NonNestedPrefixPrefixChar123bazChar125BarRouteImport } from './routes/non-nested/prefix/prefix{$baz}_.bar' +import { Route as NonNestedSuffixChar123bazChar125suffixIndexRouteImport } from './routes/non-nested/suffix/{$baz}suffix.index' +import { Route as NonNestedSuffixChar123bazChar125suffixFooRouteImport } from './routes/non-nested/suffix/{$baz}suffix.foo' +import { Route as NonNestedSuffixChar123bazChar125suffixBarRouteImport } from './routes/non-nested/suffix/{$baz}suffix_.bar' import { Route as ParamsPsNamedFooBarRouteRouteImport } from './routes/params-ps/named/$foo/$bar.route' -import { Route as NonNestedDeepBazBarRouteRouteImport } from './routes/non-nested/deep/$baz_.bar.route' -import { Route as RelativeUseNavigatePathPathIndexRouteImport } from './routes/relative/useNavigate/path/$path/index' -import { Route as RelativeUseNavigateNestedDeepIndexRouteImport } from './routes/relative/useNavigate/nested/deep/index' -import { Route as RelativeLinkPathPathIndexRouteImport } from './routes/relative/link/path/$path/index' -import { Route as RelativeLinkNestedDeepIndexRouteImport } from './routes/relative/link/nested/deep/index' +import { Route as ParamsPsNonNestedFooBarRouteImport } from './routes/params-ps/non-nested/$foo_/$bar' +import { Route as RelativeLinkNestedIndexRouteImport } from './routes/relative/link/nested/index' +import { Route as RelativeLinkPathIndexRouteImport } from './routes/relative/link/path/index' +import { Route as RelativeLinkWithSearchIndexRouteImport } from './routes/relative/link/with-search/index' +import { Route as RelativeUseNavigateNestedIndexRouteImport } from './routes/relative/useNavigate/nested/index' +import { Route as RelativeUseNavigatePathIndexRouteImport } from './routes/relative/useNavigate/path/index' +import { Route as RelativeUseNavigateWithSearchIndexRouteImport } from './routes/relative/useNavigate/with-search/index' import { Route as NonNestedDeepBazBarIndexRouteImport } from './routes/non-nested/deep/$baz_.bar.index' -import { Route as ParamsPsNamedFooBarBazRouteImport } from './routes/params-ps/named/$foo/$bar.$baz' -import { Route as NonNestedDeepBazBarQuxRouteImport } from './routes/non-nested/deep/$baz_.bar_.qux' import { Route as NonNestedDeepBazBarFooRouteRouteImport } from './routes/non-nested/deep/$baz_.bar.$foo.route' +import { Route as NonNestedDeepBazBarQuxRouteImport } from './routes/non-nested/deep/$baz_.bar_.qux' +import { Route as ParamsPsNamedFooBarBazRouteImport } from './routes/params-ps/named/$foo/$bar.$baz' +import { Route as RelativeLinkNestedDeepIndexRouteImport } from './routes/relative/link/nested/deep/index' +import { Route as RelativeLinkPathPathIndexRouteImport } from './routes/relative/link/path/$path/index' +import { Route as RelativeUseNavigateNestedDeepIndexRouteImport } from './routes/relative/useNavigate/nested/deep/index' +import { Route as RelativeUseNavigatePathPathIndexRouteImport } from './routes/relative/useNavigate/path/$path/index' import { Route as NonNestedDeepBazBarFooIndexRouteImport } from './routes/non-nested/deep/$baz_.bar.$foo.index' import { Route as NonNestedDeepBazBarFooQuxRouteImport } from './routes/non-nested/deep/$baz_.bar.$foo_.qux' -const RemountDepsRoute = RemountDepsRouteImport.update({ - id: '/remountDeps', - path: '/remountDeps', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const LayoutRoute = LayoutRouteImport.update({ + id: '/_layout', getParentRoute: () => rootRouteImport, } as any) -const NotRemountDepsRoute = NotRemountDepsRouteImport.update({ - id: '/notRemountDeps', - path: '/notRemountDeps', +const AnchorRoute = AnchorRouteImport.update({ + id: '/anchor', + path: '/anchor', getParentRoute: () => rootRouteImport, } as any) -const MasksRoute = MasksRouteImport.update({ - id: '/masks', - path: '/masks', +const ComponentTypesTestRoute = ComponentTypesTestRouteImport.update({ + id: '/component-types-test', + path: '/component-types-test', getParentRoute: () => rootRouteImport, } as any) -const HoverPreloadHashRoute = HoverPreloadHashRouteImport.update({ - id: '/hover-preload-hash', - path: '/hover-preload-hash', +const EditingARoute = EditingARouteImport.update({ + id: '/editing-a', + path: '/editing-a', getParentRoute: () => rootRouteImport, } as any) const EditingBRoute = EditingBRouteImport.update({ @@ -145,234 +144,286 @@ const EditingBRoute = EditingBRouteImport.update({ path: '/editing-b', getParentRoute: () => rootRouteImport, } as any) -const EditingARoute = EditingARouteImport.update({ - id: '/editing-a', - path: '/editing-a', +const HoverPreloadHashRoute = HoverPreloadHashRouteImport.update({ + id: '/hover-preload-hash', + path: '/hover-preload-hash', getParentRoute: () => rootRouteImport, } as any) -const ComponentTypesTestRoute = ComponentTypesTestRouteImport.update({ - id: '/component-types-test', - path: '/component-types-test', +const MasksRoute = MasksRouteImport.update({ + id: '/masks', + path: '/masks', getParentRoute: () => rootRouteImport, } as any) -const AnchorRoute = AnchorRouteImport.update({ - id: '/anchor', - path: '/anchor', +const NonNestedRouteRoute = NonNestedRouteRouteImport.update({ + id: '/non-nested', + path: '/non-nested', getParentRoute: () => rootRouteImport, } as any) -const LayoutRoute = LayoutRouteImport.update({ - id: '/_layout', +const NotRemountDepsRoute = NotRemountDepsRouteImport.update({ + id: '/notRemountDeps', + path: '/notRemountDeps', getParentRoute: () => rootRouteImport, } as any) -const Char45824Char54620Char48124Char44397RouteRoute = - Char45824Char54620Char48124Char44397RouteRouteImport.update({ - id: '/대한민국', - path: '/대한민국', - getParentRoute: () => rootRouteImport, - } as any) -const SearchParamsRouteRoute = SearchParamsRouteRouteImport.update({ - id: '/search-params', - path: '/search-params', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const NonNestedRouteRoute = NonNestedRouteRouteImport.update({ - id: '/non-nested', - path: '/non-nested', +const RemountDepsRoute = RemountDepsRouteImport.update({ + id: '/remountDeps', + path: '/remountDeps', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const SearchParamsRouteRoute = SearchParamsRouteRouteImport.update({ + id: '/search-params', + path: '/search-params', getParentRoute: () => rootRouteImport, } as any) -const SearchParamsIndexRoute = SearchParamsIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => SearchParamsRouteRoute, -} as any) -const RelativeIndexRoute = RelativeIndexRouteImport.update({ - id: '/relative/', - path: '/relative/', +const Char45824Char54620Char48124Char44397RouteRoute = + Char45824Char54620Char48124Char44397RouteRouteImport.update({ + id: '/대한민국', + path: '/대한민국', + getParentRoute: () => rootRouteImport, + } as any) +const anotherGroupOnlyrouteinsideRoute = + anotherGroupOnlyrouteinsideRouteImport.update({ + id: '/(another-group)/onlyrouteinside', + path: '/onlyrouteinside', + getParentRoute: () => rootRouteImport, + } as any) +const groupLayoutRoute = groupLayoutRouteImport.update({ + id: '/(group)/_layout', getParentRoute: () => rootRouteImport, } as any) -const RedirectIndexRoute = RedirectIndexRouteImport.update({ - id: '/redirect/', - path: '/redirect/', +const groupInsideRoute = groupInsideRouteImport.update({ + id: '/(group)/inside', + path: '/inside', getParentRoute: () => rootRouteImport, } as any) -const PostsIndexRoute = PostsIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => PostsRoute, +const groupLazyinsideRoute = groupLazyinsideRouteImport + .update({ + id: '/(group)/lazyinside', + path: '/lazyinside', + getParentRoute: () => rootRouteImport, + } as any) + .lazy(() => import('./routes/(group)/lazyinside.lazy').then((d) => d.Route)) +const LayoutLayout2Route = LayoutLayout2RouteImport.update({ + id: '/_layout-2', + getParentRoute: () => LayoutRoute, +} as any) +const NonNestedDeepRouteRoute = NonNestedDeepRouteRouteImport.update({ + id: '/deep', + path: '/deep', + getParentRoute: () => NonNestedRouteRoute, +} as any) +const NonNestedNamedRouteRoute = NonNestedNamedRouteRouteImport.update({ + id: '/named', + path: '/named', + getParentRoute: () => NonNestedRouteRoute, +} as any) +const NonNestedPathRouteRoute = NonNestedPathRouteRouteImport.update({ + id: '/path', + path: '/path', + getParentRoute: () => NonNestedRouteRoute, +} as any) +const NonNestedPrefixRouteRoute = NonNestedPrefixRouteRouteImport.update({ + id: '/prefix', + path: '/prefix', + getParentRoute: () => NonNestedRouteRoute, +} as any) +const NonNestedSuffixRouteRoute = NonNestedSuffixRouteRouteImport.update({ + id: '/suffix', + path: '/suffix', + getParentRoute: () => NonNestedRouteRoute, } as any) const ParamsPsIndexRoute = ParamsPsIndexRouteImport.update({ id: '/params-ps/', path: '/params-ps/', getParentRoute: () => rootRouteImport, } as any) -const SearchParamsDefaultRoute = SearchParamsDefaultRouteImport.update({ - id: '/default', - path: '/default', - getParentRoute: () => SearchParamsRouteRoute, -} as any) -const RedirectTargetRoute = RedirectTargetRouteImport.update({ - id: '/redirect/$target', - path: '/redirect/$target', +const ParamsPsNonNestedRouteRoute = ParamsPsNonNestedRouteRouteImport.update({ + id: '/params-ps/non-nested', + path: '/params-ps/non-nested', getParentRoute: () => rootRouteImport, } as any) +const PostsIndexRoute = PostsIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => PostsRoute, +} as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRoute, } as any) -const LayoutLayout2Route = LayoutLayout2RouteImport.update({ - id: '/_layout-2', - getParentRoute: () => LayoutRoute, +const RedirectIndexRoute = RedirectIndexRouteImport.update({ + id: '/redirect/', + path: '/redirect/', + getParentRoute: () => rootRouteImport, } as any) -const groupLazyinsideRoute = groupLazyinsideRouteImport - .update({ - id: '/(group)/lazyinside', - path: '/lazyinside', - getParentRoute: () => rootRouteImport, - } as any) - .lazy(() => import('./routes/(group)/lazyinside.lazy').then((d) => d.Route)) -const groupInsideRoute = groupInsideRouteImport.update({ - id: '/(group)/inside', - path: '/inside', +const RedirectTargetRoute = RedirectTargetRouteImport.update({ + id: '/redirect/$target', + path: '/redirect/$target', getParentRoute: () => rootRouteImport, } as any) -const groupLayoutRoute = groupLayoutRouteImport.update({ - id: '/(group)/_layout', +const RelativeIndexRoute = RelativeIndexRouteImport.update({ + id: '/relative/', + path: '/relative/', + getParentRoute: () => rootRouteImport, +} as any) +const RelativeLinkRouteRoute = RelativeLinkRouteRouteImport.update({ + id: '/relative/link', + path: '/relative/link', getParentRoute: () => rootRouteImport, } as any) -const anotherGroupOnlyrouteinsideRoute = - anotherGroupOnlyrouteinsideRouteImport.update({ - id: '/(another-group)/onlyrouteinside', - path: '/onlyrouteinside', - getParentRoute: () => rootRouteImport, - } as any) const RelativeUseNavigateRouteRoute = RelativeUseNavigateRouteRouteImport.update({ id: '/relative/useNavigate', path: '/relative/useNavigate', getParentRoute: () => rootRouteImport, } as any) -const RelativeLinkRouteRoute = RelativeLinkRouteRouteImport.update({ - id: '/relative/link', - path: '/relative/link', - getParentRoute: () => rootRouteImport, +const SearchParamsIndexRoute = SearchParamsIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => SearchParamsRouteRoute, } as any) -const ParamsPsNonNestedRouteRoute = ParamsPsNonNestedRouteRouteImport.update({ - id: '/params-ps/non-nested', - path: '/params-ps/non-nested', +const SearchParamsDefaultRoute = SearchParamsDefaultRouteImport.update({ + id: '/default', + path: '/default', + getParentRoute: () => SearchParamsRouteRoute, +} as any) +const groupLayoutInsidelayoutRoute = groupLayoutInsidelayoutRouteImport.update({ + id: '/insidelayout', + path: '/insidelayout', + getParentRoute: () => groupLayoutRoute, +} as any) +const groupSubfolderInsideRoute = groupSubfolderInsideRouteImport.update({ + id: '/(group)/subfolder/inside', + path: '/subfolder/inside', getParentRoute: () => rootRouteImport, } as any) -const NonNestedSuffixRouteRoute = NonNestedSuffixRouteRouteImport.update({ - id: '/suffix', - path: '/suffix', - getParentRoute: () => NonNestedRouteRoute, +const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ + id: '/layout-a', + path: '/layout-a', + getParentRoute: () => LayoutLayout2Route, } as any) -const NonNestedPrefixRouteRoute = NonNestedPrefixRouteRouteImport.update({ - id: '/prefix', - path: '/prefix', - getParentRoute: () => NonNestedRouteRoute, +const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ + id: '/layout-b', + path: '/layout-b', + getParentRoute: () => LayoutLayout2Route, } as any) -const NonNestedPathRouteRoute = NonNestedPathRouteRouteImport.update({ - id: '/path', - path: '/path', - getParentRoute: () => NonNestedRouteRoute, +const MasksAdminUserIdRoute = MasksAdminUserIdRouteImport.update({ + id: '/admin/$userId', + path: '/admin/$userId', + getParentRoute: () => MasksRoute, } as any) -const NonNestedNamedRouteRoute = NonNestedNamedRouteRouteImport.update({ - id: '/named', - path: '/named', - getParentRoute: () => NonNestedRouteRoute, +const MasksPublicUsernameRoute = MasksPublicUsernameRouteImport.update({ + id: '/public/$username', + path: '/public/$username', + getParentRoute: () => MasksRoute, } as any) -const NonNestedDeepRouteRoute = NonNestedDeepRouteRouteImport.update({ - id: '/deep', - path: '/deep', - getParentRoute: () => NonNestedRouteRoute, +const NonNestedDeepBazRouteRoute = NonNestedDeepBazRouteRouteImport.update({ + id: '/$baz', + path: '/$baz', + getParentRoute: () => NonNestedDeepRouteRoute, } as any) -const RedirectTargetIndexRoute = RedirectTargetIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => RedirectTargetRoute, +const NonNestedNamedBazRouteRoute = NonNestedNamedBazRouteRouteImport.update({ + id: '/$baz', + path: '/$baz', + getParentRoute: () => NonNestedNamedRouteRoute, } as any) -const ParamsPsWildcardIndexRoute = ParamsPsWildcardIndexRouteImport.update({ - id: '/params-ps/wildcard/', - path: '/params-ps/wildcard/', - getParentRoute: () => rootRouteImport, +const NonNestedPathBazRouteRoute = NonNestedPathBazRouteRouteImport.update({ + id: '/baz', + path: '/baz', + getParentRoute: () => NonNestedPathRouteRoute, } as any) +const NonNestedPrefixPrefixChar123bazChar125RouteRoute = + NonNestedPrefixPrefixChar123bazChar125RouteRouteImport.update({ + id: '/prefix{$baz}', + path: '/prefix{$baz}', + getParentRoute: () => NonNestedPrefixRouteRoute, + } as any) +const NonNestedSuffixChar123bazChar125suffixRouteRoute = + NonNestedSuffixChar123bazChar125suffixRouteRouteImport.update({ + id: '/{$baz}suffix', + path: '/{$baz}suffix', + getParentRoute: () => NonNestedSuffixRouteRoute, + } as any) const ParamsPsNamedIndexRoute = ParamsPsNamedIndexRouteImport.update({ id: '/params-ps/named/', path: '/params-ps/named/', getParentRoute: () => rootRouteImport, } as any) -const Char45824Char54620Char48124Char44397Char55357Char56960IdRoute = - Char45824Char54620Char48124Char44397Char55357Char56960IdRouteImport.update({ - id: '/🚀/$id', - path: '/🚀/$id', - getParentRoute: () => Char45824Char54620Char48124Char44397RouteRoute, - } as any) -const Char45824Char54620Char48124Char44397WildcardSplatRoute = - Char45824Char54620Char48124Char44397WildcardSplatRouteImport.update({ - id: '/wildcard/$', - path: '/wildcard/$', - getParentRoute: () => Char45824Char54620Char48124Char44397RouteRoute, - } as any) -const TransitionTypingCreateResourceRoute = - TransitionTypingCreateResourceRouteImport.update({ - id: '/transition/typing/create-resource', - path: '/transition/typing/create-resource', +const ParamsPsNamedFooRouteRoute = ParamsPsNamedFooRouteRouteImport.update({ + id: '/params-ps/named/$foo', + path: '/params-ps/named/$foo', + getParentRoute: () => rootRouteImport, +} as any) +const ParamsPsNamedPrefixChar123fooChar125Route = + ParamsPsNamedPrefixChar123fooChar125RouteImport.update({ + id: '/params-ps/named/prefix{$foo}', + path: '/params-ps/named/prefix{$foo}', getParentRoute: () => rootRouteImport, } as any) -const TransitionCountCreateResourceRoute = - TransitionCountCreateResourceRouteImport.update({ - id: '/transition/count/create-resource', - path: '/transition/count/create-resource', +const ParamsPsNamedChar123fooChar125suffixRoute = + ParamsPsNamedChar123fooChar125suffixRouteImport.update({ + id: '/params-ps/named/{$foo}suffix', + path: '/params-ps/named/{$foo}suffix', getParentRoute: () => rootRouteImport, } as any) -const RelativeUseNavigateRelativeUseNavigateBRoute = - RelativeUseNavigateRelativeUseNavigateBRouteImport.update({ - id: '/relative-useNavigate-b', - path: '/relative-useNavigate-b', - getParentRoute: () => RelativeUseNavigateRouteRoute, +const ParamsPsNonNestedFooRouteRoute = + ParamsPsNonNestedFooRouteRouteImport.update({ + id: '/$foo_', + path: '/$foo', + getParentRoute: () => ParamsPsNonNestedRouteRoute, } as any) -const RelativeUseNavigateRelativeUseNavigateARoute = - RelativeUseNavigateRelativeUseNavigateARouteImport.update({ - id: '/relative-useNavigate-a', - path: '/relative-useNavigate-a', - getParentRoute: () => RelativeUseNavigateRouteRoute, +const ParamsPsWildcardIndexRoute = ParamsPsWildcardIndexRouteImport.update({ + id: '/params-ps/wildcard/', + path: '/params-ps/wildcard/', + getParentRoute: () => rootRouteImport, +} as any) +const ParamsPsWildcardSplatRoute = ParamsPsWildcardSplatRouteImport.update({ + id: '/params-ps/wildcard/$', + path: '/params-ps/wildcard/$', + getParentRoute: () => rootRouteImport, +} as any) +const ParamsPsWildcardPrefixAtChar45824Char123Char125Route = + ParamsPsWildcardPrefixAtChar45824Char123Char125RouteImport.update({ + id: '/params-ps/wildcard/prefix@대{$}', + path: '/params-ps/wildcard/prefix@대{$}', + getParentRoute: () => rootRouteImport, } as any) -const RelativeLinkRelativeLinkBRoute = - RelativeLinkRelativeLinkBRouteImport.update({ - id: '/relative-link-b', - path: '/relative-link-b', - getParentRoute: () => RelativeLinkRouteRoute, +const ParamsPsWildcardPrefixChar123Char125Route = + ParamsPsWildcardPrefixChar123Char125RouteImport.update({ + id: '/params-ps/wildcard/prefix{$}', + path: '/params-ps/wildcard/prefix{$}', + getParentRoute: () => rootRouteImport, } as any) -const RelativeLinkRelativeLinkARoute = - RelativeLinkRelativeLinkARouteImport.update({ - id: '/relative-link-a', - path: '/relative-link-a', - getParentRoute: () => RelativeLinkRouteRoute, +const ParamsPsWildcardChar123Char125suffixRoute = + ParamsPsWildcardChar123Char125suffixRouteImport.update({ + id: '/params-ps/wildcard/{$}suffix', + path: '/params-ps/wildcard/{$}suffix', + getParentRoute: () => rootRouteImport, } as any) -const RedirectPreloadThirdRoute = RedirectPreloadThirdRouteImport.update({ - id: '/redirect/preload/third', - path: '/redirect/preload/third', - getParentRoute: () => rootRouteImport, -} as any) -const RedirectPreloadSecondRoute = RedirectPreloadSecondRouteImport.update({ - id: '/redirect/preload/second', - path: '/redirect/preload/second', +const ParamsPsWildcardChar123Char125suffixAtChar45824Route = + ParamsPsWildcardChar123Char125suffixAtChar45824RouteImport.update({ + id: '/params-ps/wildcard/{$}suffix@대', + path: '/params-ps/wildcard/{$}suffix@대', + getParentRoute: () => rootRouteImport, + } as any) +const ParamsSingleValueRoute = ParamsSingleValueRouteImport.update({ + id: '/params/single/$value', + path: '/params/single/$value', getParentRoute: () => rootRouteImport, } as any) -const RedirectPreloadFirstRoute = RedirectPreloadFirstRouteImport.update({ - id: '/redirect/preload/first', - path: '/redirect/preload/first', +const PostsPostIdEditRoute = PostsPostIdEditRouteImport.update({ + id: '/posts_/$postId/edit', + path: '/posts/$postId/edit', getParentRoute: () => rootRouteImport, } as any) -const RedirectTargetViaLoaderRoute = RedirectTargetViaLoaderRouteImport.update({ - id: '/via-loader', - path: '/via-loader', +const RedirectTargetIndexRoute = RedirectTargetIndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => RedirectTargetRoute, } as any) const RedirectTargetViaBeforeLoadRoute = @@ -381,270 +432,195 @@ const RedirectTargetViaBeforeLoadRoute = path: '/via-beforeLoad', getParentRoute: () => RedirectTargetRoute, } as any) -const PostsPostIdEditRoute = PostsPostIdEditRouteImport.update({ - id: '/posts_/$postId/edit', - path: '/posts/$postId/edit', +const RedirectTargetViaLoaderRoute = RedirectTargetViaLoaderRouteImport.update({ + id: '/via-loader', + path: '/via-loader', + getParentRoute: () => RedirectTargetRoute, +} as any) +const RedirectPreloadFirstRoute = RedirectPreloadFirstRouteImport.update({ + id: '/redirect/preload/first', + path: '/redirect/preload/first', getParentRoute: () => rootRouteImport, } as any) -const ParamsSingleValueRoute = ParamsSingleValueRouteImport.update({ - id: '/params/single/$value', - path: '/params/single/$value', +const RedirectPreloadSecondRoute = RedirectPreloadSecondRouteImport.update({ + id: '/redirect/preload/second', + path: '/redirect/preload/second', getParentRoute: () => rootRouteImport, } as any) -const ParamsPsWildcardChar123Char125suffixAtChar45824Route = - ParamsPsWildcardChar123Char125suffixAtChar45824RouteImport.update({ - id: '/params-ps/wildcard/{$}suffix@대', - path: '/params-ps/wildcard/{$}suffix@대', - getParentRoute: () => rootRouteImport, +const RedirectPreloadThirdRoute = RedirectPreloadThirdRouteImport.update({ + id: '/redirect/preload/third', + path: '/redirect/preload/third', + getParentRoute: () => rootRouteImport, +} as any) +const RelativeLinkRelativeLinkARoute = + RelativeLinkRelativeLinkARouteImport.update({ + id: '/relative-link-a', + path: '/relative-link-a', + getParentRoute: () => RelativeLinkRouteRoute, } as any) -const ParamsPsWildcardChar123Char125suffixRoute = - ParamsPsWildcardChar123Char125suffixRouteImport.update({ - id: '/params-ps/wildcard/{$}suffix', - path: '/params-ps/wildcard/{$}suffix', - getParentRoute: () => rootRouteImport, +const RelativeLinkRelativeLinkBRoute = + RelativeLinkRelativeLinkBRouteImport.update({ + id: '/relative-link-b', + path: '/relative-link-b', + getParentRoute: () => RelativeLinkRouteRoute, } as any) -const ParamsPsWildcardPrefixChar123Char125Route = - ParamsPsWildcardPrefixChar123Char125RouteImport.update({ - id: '/params-ps/wildcard/prefix{$}', - path: '/params-ps/wildcard/prefix{$}', - getParentRoute: () => rootRouteImport, +const RelativeUseNavigateRelativeUseNavigateARoute = + RelativeUseNavigateRelativeUseNavigateARouteImport.update({ + id: '/relative-useNavigate-a', + path: '/relative-useNavigate-a', + getParentRoute: () => RelativeUseNavigateRouteRoute, } as any) -const ParamsPsWildcardPrefixAtChar45824Char123Char125Route = - ParamsPsWildcardPrefixAtChar45824Char123Char125RouteImport.update({ - id: '/params-ps/wildcard/prefix@대{$}', - path: '/params-ps/wildcard/prefix@대{$}', - getParentRoute: () => rootRouteImport, +const RelativeUseNavigateRelativeUseNavigateBRoute = + RelativeUseNavigateRelativeUseNavigateBRouteImport.update({ + id: '/relative-useNavigate-b', + path: '/relative-useNavigate-b', + getParentRoute: () => RelativeUseNavigateRouteRoute, } as any) -const ParamsPsWildcardSplatRoute = ParamsPsWildcardSplatRouteImport.update({ - id: '/params-ps/wildcard/$', - path: '/params-ps/wildcard/$', - getParentRoute: () => rootRouteImport, -} as any) -const ParamsPsNamedChar123fooChar125suffixRoute = - ParamsPsNamedChar123fooChar125suffixRouteImport.update({ - id: '/params-ps/named/{$foo}suffix', - path: '/params-ps/named/{$foo}suffix', +const TransitionCountCreateResourceRoute = + TransitionCountCreateResourceRouteImport.update({ + id: '/transition/count/create-resource', + path: '/transition/count/create-resource', getParentRoute: () => rootRouteImport, } as any) -const ParamsPsNamedPrefixChar123fooChar125Route = - ParamsPsNamedPrefixChar123fooChar125RouteImport.update({ - id: '/params-ps/named/prefix{$foo}', - path: '/params-ps/named/prefix{$foo}', +const TransitionTypingCreateResourceRoute = + TransitionTypingCreateResourceRouteImport.update({ + id: '/transition/typing/create-resource', + path: '/transition/typing/create-resource', getParentRoute: () => rootRouteImport, } as any) -const MasksPublicUsernameRoute = MasksPublicUsernameRouteImport.update({ - id: '/public/$username', - path: '/public/$username', - getParentRoute: () => MasksRoute, +const Char45824Char54620Char48124Char44397WildcardSplatRoute = + Char45824Char54620Char48124Char44397WildcardSplatRouteImport.update({ + id: '/wildcard/$', + path: '/wildcard/$', + getParentRoute: () => Char45824Char54620Char48124Char44397RouteRoute, + } as any) +const Char45824Char54620Char48124Char44397Char55357Char56960IdRoute = + Char45824Char54620Char48124Char44397Char55357Char56960IdRouteImport.update({ + id: '/🚀/$id', + path: '/🚀/$id', + getParentRoute: () => Char45824Char54620Char48124Char44397RouteRoute, + } as any) +const NonNestedDeepBazIndexRoute = NonNestedDeepBazIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => NonNestedDeepBazRouteRoute, } as any) -const MasksAdminUserIdRoute = MasksAdminUserIdRouteImport.update({ - id: '/admin/$userId', - path: '/admin/$userId', - getParentRoute: () => MasksRoute, +const NonNestedDeepBazBarRouteRoute = + NonNestedDeepBazBarRouteRouteImport.update({ + id: '/$baz_/bar', + path: '/$baz/bar', + getParentRoute: () => NonNestedDeepRouteRoute, + } as any) +const NonNestedNamedBazIndexRoute = NonNestedNamedBazIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => NonNestedNamedBazRouteRoute, } as any) -const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ - id: '/layout-b', - path: '/layout-b', - getParentRoute: () => LayoutLayout2Route, +const NonNestedNamedBazFooRoute = NonNestedNamedBazFooRouteImport.update({ + id: '/foo', + path: '/foo', + getParentRoute: () => NonNestedNamedBazRouteRoute, } as any) -const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ - id: '/layout-a', - path: '/layout-a', - getParentRoute: () => LayoutLayout2Route, +const NonNestedNamedBazBarRoute = NonNestedNamedBazBarRouteImport.update({ + id: '/$baz_/bar', + path: '/$baz/bar', + getParentRoute: () => NonNestedNamedRouteRoute, } as any) -const groupSubfolderInsideRoute = groupSubfolderInsideRouteImport.update({ - id: '/(group)/subfolder/inside', - path: '/subfolder/inside', - getParentRoute: () => rootRouteImport, +const NonNestedPathBazIndexRoute = NonNestedPathBazIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => NonNestedPathBazRouteRoute, } as any) -const groupLayoutInsidelayoutRoute = groupLayoutInsidelayoutRouteImport.update({ - id: '/insidelayout', - path: '/insidelayout', - getParentRoute: () => groupLayoutRoute, +const NonNestedPathBazFooRoute = NonNestedPathBazFooRouteImport.update({ + id: '/foo', + path: '/foo', + getParentRoute: () => NonNestedPathBazRouteRoute, } as any) -const ParamsPsNonNestedFooRouteRoute = - ParamsPsNonNestedFooRouteRouteImport.update({ - id: '/$foo_', - path: '/$foo', - getParentRoute: () => ParamsPsNonNestedRouteRoute, - } as any) -const ParamsPsNamedFooRouteRoute = ParamsPsNamedFooRouteRouteImport.update({ - id: '/params-ps/named/$foo', - path: '/params-ps/named/$foo', - getParentRoute: () => rootRouteImport, +const NonNestedPathBazBarRoute = NonNestedPathBazBarRouteImport.update({ + id: '/baz_/bar', + path: '/baz/bar', + getParentRoute: () => NonNestedPathRouteRoute, } as any) -const NonNestedSuffixChar123bazChar125suffixRouteRoute = - NonNestedSuffixChar123bazChar125suffixRouteRouteImport.update({ - id: '/{$baz}suffix', - path: '/{$baz}suffix', - getParentRoute: () => NonNestedSuffixRouteRoute, +const NonNestedPrefixPrefixChar123bazChar125IndexRoute = + NonNestedPrefixPrefixChar123bazChar125IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => NonNestedPrefixPrefixChar123bazChar125RouteRoute, } as any) -const NonNestedPrefixPrefixChar123bazChar125RouteRoute = - NonNestedPrefixPrefixChar123bazChar125RouteRouteImport.update({ - id: '/prefix{$baz}', - path: '/prefix{$baz}', +const NonNestedPrefixPrefixChar123bazChar125FooRoute = + NonNestedPrefixPrefixChar123bazChar125FooRouteImport.update({ + id: '/foo', + path: '/foo', + getParentRoute: () => NonNestedPrefixPrefixChar123bazChar125RouteRoute, + } as any) +const NonNestedPrefixPrefixChar123bazChar125BarRoute = + NonNestedPrefixPrefixChar123bazChar125BarRouteImport.update({ + id: '/prefix{$baz}_/bar', + path: '/prefix{$baz}/bar', getParentRoute: () => NonNestedPrefixRouteRoute, } as any) -const NonNestedPathBazRouteRoute = NonNestedPathBazRouteRouteImport.update({ - id: '/baz', - path: '/baz', - getParentRoute: () => NonNestedPathRouteRoute, -} as any) -const NonNestedNamedBazRouteRoute = NonNestedNamedBazRouteRouteImport.update({ - id: '/$baz', - path: '/$baz', - getParentRoute: () => NonNestedNamedRouteRoute, -} as any) -const NonNestedDeepBazRouteRoute = NonNestedDeepBazRouteRouteImport.update({ - id: '/$baz', - path: '/$baz', - getParentRoute: () => NonNestedDeepRouteRoute, -} as any) -const RelativeUseNavigateWithSearchIndexRoute = - RelativeUseNavigateWithSearchIndexRouteImport.update({ - id: '/with-search/', - path: '/with-search/', - getParentRoute: () => RelativeUseNavigateRouteRoute, - } as any) -const RelativeUseNavigatePathIndexRoute = - RelativeUseNavigatePathIndexRouteImport.update({ - id: '/path/', - path: '/path/', - getParentRoute: () => RelativeUseNavigateRouteRoute, - } as any) -const RelativeUseNavigateNestedIndexRoute = - RelativeUseNavigateNestedIndexRouteImport.update({ - id: '/nested/', - path: '/nested/', - getParentRoute: () => RelativeUseNavigateRouteRoute, - } as any) -const RelativeLinkWithSearchIndexRoute = - RelativeLinkWithSearchIndexRouteImport.update({ - id: '/with-search/', - path: '/with-search/', - getParentRoute: () => RelativeLinkRouteRoute, - } as any) -const RelativeLinkPathIndexRoute = RelativeLinkPathIndexRouteImport.update({ - id: '/path/', - path: '/path/', - getParentRoute: () => RelativeLinkRouteRoute, -} as any) -const RelativeLinkNestedIndexRoute = RelativeLinkNestedIndexRouteImport.update({ - id: '/nested/', - path: '/nested/', - getParentRoute: () => RelativeLinkRouteRoute, -} as any) const NonNestedSuffixChar123bazChar125suffixIndexRoute = NonNestedSuffixChar123bazChar125suffixIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => NonNestedSuffixChar123bazChar125suffixRouteRoute, } as any) -const NonNestedPrefixPrefixChar123bazChar125IndexRoute = - NonNestedPrefixPrefixChar123bazChar125IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => NonNestedPrefixPrefixChar123bazChar125RouteRoute, - } as any) -const NonNestedPathBazIndexRoute = NonNestedPathBazIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => NonNestedPathBazRouteRoute, -} as any) -const NonNestedNamedBazIndexRoute = NonNestedNamedBazIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => NonNestedNamedBazRouteRoute, -} as any) -const NonNestedDeepBazIndexRoute = NonNestedDeepBazIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => NonNestedDeepBazRouteRoute, -} as any) -const ParamsPsNonNestedFooBarRoute = ParamsPsNonNestedFooBarRouteImport.update({ - id: '/$bar', - path: '/$bar', - getParentRoute: () => ParamsPsNonNestedFooRouteRoute, -} as any) -const NonNestedSuffixChar123bazChar125suffixBarRoute = - NonNestedSuffixChar123bazChar125suffixBarRouteImport.update({ - id: '/{$baz}suffix_/bar', - path: '/{$baz}suffix/bar', - getParentRoute: () => NonNestedSuffixRouteRoute, - } as any) const NonNestedSuffixChar123bazChar125suffixFooRoute = NonNestedSuffixChar123bazChar125suffixFooRouteImport.update({ id: '/foo', path: '/foo', getParentRoute: () => NonNestedSuffixChar123bazChar125suffixRouteRoute, } as any) -const NonNestedPrefixPrefixChar123bazChar125BarRoute = - NonNestedPrefixPrefixChar123bazChar125BarRouteImport.update({ - id: '/prefix{$baz}_/bar', - path: '/prefix{$baz}/bar', - getParentRoute: () => NonNestedPrefixRouteRoute, - } as any) -const NonNestedPrefixPrefixChar123bazChar125FooRoute = - NonNestedPrefixPrefixChar123bazChar125FooRouteImport.update({ - id: '/foo', - path: '/foo', - getParentRoute: () => NonNestedPrefixPrefixChar123bazChar125RouteRoute, +const NonNestedSuffixChar123bazChar125suffixBarRoute = + NonNestedSuffixChar123bazChar125suffixBarRouteImport.update({ + id: '/{$baz}suffix_/bar', + path: '/{$baz}suffix/bar', + getParentRoute: () => NonNestedSuffixRouteRoute, } as any) -const NonNestedPathBazBarRoute = NonNestedPathBazBarRouteImport.update({ - id: '/baz_/bar', - path: '/baz/bar', - getParentRoute: () => NonNestedPathRouteRoute, -} as any) -const NonNestedPathBazFooRoute = NonNestedPathBazFooRouteImport.update({ - id: '/foo', - path: '/foo', - getParentRoute: () => NonNestedPathBazRouteRoute, -} as any) -const NonNestedNamedBazBarRoute = NonNestedNamedBazBarRouteImport.update({ - id: '/$baz_/bar', - path: '/$baz/bar', - getParentRoute: () => NonNestedNamedRouteRoute, -} as any) -const NonNestedNamedBazFooRoute = NonNestedNamedBazFooRouteImport.update({ - id: '/foo', - path: '/foo', - getParentRoute: () => NonNestedNamedBazRouteRoute, -} as any) const ParamsPsNamedFooBarRouteRoute = ParamsPsNamedFooBarRouteRouteImport.update({ id: '/$bar', path: '/$bar', getParentRoute: () => ParamsPsNamedFooRouteRoute, } as any) -const NonNestedDeepBazBarRouteRoute = - NonNestedDeepBazBarRouteRouteImport.update({ - id: '/$baz_/bar', - path: '/$baz/bar', - getParentRoute: () => NonNestedDeepRouteRoute, +const ParamsPsNonNestedFooBarRoute = ParamsPsNonNestedFooBarRouteImport.update({ + id: '/$bar', + path: '/$bar', + getParentRoute: () => ParamsPsNonNestedFooRouteRoute, +} as any) +const RelativeLinkNestedIndexRoute = RelativeLinkNestedIndexRouteImport.update({ + id: '/nested/', + path: '/nested/', + getParentRoute: () => RelativeLinkRouteRoute, +} as any) +const RelativeLinkPathIndexRoute = RelativeLinkPathIndexRouteImport.update({ + id: '/path/', + path: '/path/', + getParentRoute: () => RelativeLinkRouteRoute, +} as any) +const RelativeLinkWithSearchIndexRoute = + RelativeLinkWithSearchIndexRouteImport.update({ + id: '/with-search/', + path: '/with-search/', + getParentRoute: () => RelativeLinkRouteRoute, } as any) -const RelativeUseNavigatePathPathIndexRoute = - RelativeUseNavigatePathPathIndexRouteImport.update({ - id: '/path/$path/', - path: '/path/$path/', +const RelativeUseNavigateNestedIndexRoute = + RelativeUseNavigateNestedIndexRouteImport.update({ + id: '/nested/', + path: '/nested/', getParentRoute: () => RelativeUseNavigateRouteRoute, } as any) -const RelativeUseNavigateNestedDeepIndexRoute = - RelativeUseNavigateNestedDeepIndexRouteImport.update({ - id: '/nested/deep/', - path: '/nested/deep/', +const RelativeUseNavigatePathIndexRoute = + RelativeUseNavigatePathIndexRouteImport.update({ + id: '/path/', + path: '/path/', getParentRoute: () => RelativeUseNavigateRouteRoute, } as any) -const RelativeLinkPathPathIndexRoute = - RelativeLinkPathPathIndexRouteImport.update({ - id: '/path/$path/', - path: '/path/$path/', - getParentRoute: () => RelativeLinkRouteRoute, - } as any) -const RelativeLinkNestedDeepIndexRoute = - RelativeLinkNestedDeepIndexRouteImport.update({ - id: '/nested/deep/', - path: '/nested/deep/', - getParentRoute: () => RelativeLinkRouteRoute, +const RelativeUseNavigateWithSearchIndexRoute = + RelativeUseNavigateWithSearchIndexRouteImport.update({ + id: '/with-search/', + path: '/with-search/', + getParentRoute: () => RelativeUseNavigateRouteRoute, } as any) const NonNestedDeepBazBarIndexRoute = NonNestedDeepBazBarIndexRouteImport.update({ @@ -652,22 +628,46 @@ const NonNestedDeepBazBarIndexRoute = path: '/', getParentRoute: () => NonNestedDeepBazBarRouteRoute, } as any) -const ParamsPsNamedFooBarBazRoute = ParamsPsNamedFooBarBazRouteImport.update({ - id: '/$baz', - path: '/$baz', - getParentRoute: () => ParamsPsNamedFooBarRouteRoute, -} as any) -const NonNestedDeepBazBarQuxRoute = NonNestedDeepBazBarQuxRouteImport.update({ - id: '/$baz_/bar_/qux', - path: '/$baz/bar/qux', - getParentRoute: () => NonNestedDeepRouteRoute, -} as any) const NonNestedDeepBazBarFooRouteRoute = NonNestedDeepBazBarFooRouteRouteImport.update({ id: '/$foo', path: '/$foo', getParentRoute: () => NonNestedDeepBazBarRouteRoute, } as any) +const NonNestedDeepBazBarQuxRoute = NonNestedDeepBazBarQuxRouteImport.update({ + id: '/$baz_/bar_/qux', + path: '/$baz/bar/qux', + getParentRoute: () => NonNestedDeepRouteRoute, +} as any) +const ParamsPsNamedFooBarBazRoute = ParamsPsNamedFooBarBazRouteImport.update({ + id: '/$baz', + path: '/$baz', + getParentRoute: () => ParamsPsNamedFooBarRouteRoute, +} as any) +const RelativeLinkNestedDeepIndexRoute = + RelativeLinkNestedDeepIndexRouteImport.update({ + id: '/nested/deep/', + path: '/nested/deep/', + getParentRoute: () => RelativeLinkRouteRoute, + } as any) +const RelativeLinkPathPathIndexRoute = + RelativeLinkPathPathIndexRouteImport.update({ + id: '/path/$path/', + path: '/path/$path/', + getParentRoute: () => RelativeLinkRouteRoute, + } as any) +const RelativeUseNavigateNestedDeepIndexRoute = + RelativeUseNavigateNestedDeepIndexRouteImport.update({ + id: '/nested/deep/', + path: '/nested/deep/', + getParentRoute: () => RelativeUseNavigateRouteRoute, + } as any) +const RelativeUseNavigatePathPathIndexRoute = + RelativeUseNavigatePathPathIndexRouteImport.update({ + id: '/path/$path/', + path: '/path/$path/', + getParentRoute: () => RelativeUseNavigateRouteRoute, + } as any) const NonNestedDeepBazBarFooIndexRoute = NonNestedDeepBazBarFooIndexRouteImport.update({ id: '/', @@ -1343,39 +1343,39 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/remountDeps': { - id: '/remountDeps' - path: '/remountDeps' - fullPath: '/remountDeps' - preLoaderRoute: typeof RemountDepsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/_layout': { + id: '/_layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } - '/notRemountDeps': { - id: '/notRemountDeps' - path: '/notRemountDeps' - fullPath: '/notRemountDeps' - preLoaderRoute: typeof NotRemountDepsRouteImport + '/anchor': { + id: '/anchor' + path: '/anchor' + fullPath: '/anchor' + preLoaderRoute: typeof AnchorRouteImport parentRoute: typeof rootRouteImport } - '/masks': { - id: '/masks' - path: '/masks' - fullPath: '/masks' - preLoaderRoute: typeof MasksRouteImport + '/component-types-test': { + id: '/component-types-test' + path: '/component-types-test' + fullPath: '/component-types-test' + preLoaderRoute: typeof ComponentTypesTestRouteImport parentRoute: typeof rootRouteImport } - '/hover-preload-hash': { - id: '/hover-preload-hash' - path: '/hover-preload-hash' - fullPath: '/hover-preload-hash' - preLoaderRoute: typeof HoverPreloadHashRouteImport + '/editing-a': { + id: '/editing-a' + path: '/editing-a' + fullPath: '/editing-a' + preLoaderRoute: typeof EditingARouteImport parentRoute: typeof rootRouteImport } '/editing-b': { @@ -1385,39 +1385,46 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof EditingBRouteImport parentRoute: typeof rootRouteImport } - '/editing-a': { - id: '/editing-a' - path: '/editing-a' - fullPath: '/editing-a' - preLoaderRoute: typeof EditingARouteImport + '/hover-preload-hash': { + id: '/hover-preload-hash' + path: '/hover-preload-hash' + fullPath: '/hover-preload-hash' + preLoaderRoute: typeof HoverPreloadHashRouteImport parentRoute: typeof rootRouteImport } - '/component-types-test': { - id: '/component-types-test' - path: '/component-types-test' - fullPath: '/component-types-test' - preLoaderRoute: typeof ComponentTypesTestRouteImport + '/masks': { + id: '/masks' + path: '/masks' + fullPath: '/masks' + preLoaderRoute: typeof MasksRouteImport parentRoute: typeof rootRouteImport } - '/anchor': { - id: '/anchor' - path: '/anchor' - fullPath: '/anchor' - preLoaderRoute: typeof AnchorRouteImport + '/non-nested': { + id: '/non-nested' + path: '/non-nested' + fullPath: '/non-nested' + preLoaderRoute: typeof NonNestedRouteRouteImport parentRoute: typeof rootRouteImport } - '/_layout': { - id: '/_layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutRouteImport + '/notRemountDeps': { + id: '/notRemountDeps' + path: '/notRemountDeps' + fullPath: '/notRemountDeps' + preLoaderRoute: typeof NotRemountDepsRouteImport parentRoute: typeof rootRouteImport } - '/대한민국': { - id: '/대한민국' - path: '/대한민국' - fullPath: '/대한민국' - preLoaderRoute: typeof Char45824Char54620Char48124Char44397RouteRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport + parentRoute: typeof rootRouteImport + } + '/remountDeps': { + id: '/remountDeps' + path: '/remountDeps' + fullPath: '/remountDeps' + preLoaderRoute: typeof RemountDepsRouteImport parentRoute: typeof rootRouteImport } '/search-params': { @@ -1427,47 +1434,82 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof SearchParamsRouteRouteImport parentRoute: typeof rootRouteImport } - '/non-nested': { - id: '/non-nested' - path: '/non-nested' - fullPath: '/non-nested' - preLoaderRoute: typeof NonNestedRouteRouteImport + '/대한민국': { + id: '/대한민국' + path: '/대한민국' + fullPath: '/대한민국' + preLoaderRoute: typeof Char45824Char54620Char48124Char44397RouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/(another-group)/onlyrouteinside': { + id: '/(another-group)/onlyrouteinside' + path: '/onlyrouteinside' + fullPath: '/onlyrouteinside' + preLoaderRoute: typeof anotherGroupOnlyrouteinsideRouteImport parentRoute: typeof rootRouteImport } - '/search-params/': { - id: '/search-params/' - path: '/' - fullPath: '/search-params/' - preLoaderRoute: typeof SearchParamsIndexRouteImport - parentRoute: typeof SearchParamsRouteRoute + '/(group)/_layout': { + id: '/(group)/_layout' + path: '' + fullPath: '' + preLoaderRoute: typeof groupLayoutRouteImport + parentRoute: typeof rootRouteImport } - '/relative/': { - id: '/relative/' - path: '/relative' - fullPath: '/relative/' - preLoaderRoute: typeof RelativeIndexRouteImport + '/(group)/inside': { + id: '/(group)/inside' + path: '/inside' + fullPath: '/inside' + preLoaderRoute: typeof groupInsideRouteImport parentRoute: typeof rootRouteImport } - '/redirect/': { - id: '/redirect/' - path: '/redirect' - fullPath: '/redirect/' - preLoaderRoute: typeof RedirectIndexRouteImport + '/(group)/lazyinside': { + id: '/(group)/lazyinside' + path: '/lazyinside' + fullPath: '/lazyinside' + preLoaderRoute: typeof groupLazyinsideRouteImport parentRoute: typeof rootRouteImport } - '/posts/': { - id: '/posts/' - path: '/' - fullPath: '/posts/' - preLoaderRoute: typeof PostsIndexRouteImport - parentRoute: typeof PostsRoute + '/_layout/_layout-2': { + id: '/_layout/_layout-2' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutLayout2RouteImport + parentRoute: typeof LayoutRoute + } + '/non-nested/deep': { + id: '/non-nested/deep' + path: '/deep' + fullPath: '/non-nested/deep' + preLoaderRoute: typeof NonNestedDeepRouteRouteImport + parentRoute: typeof NonNestedRouteRoute + } + '/non-nested/named': { + id: '/non-nested/named' + path: '/named' + fullPath: '/non-nested/named' + preLoaderRoute: typeof NonNestedNamedRouteRouteImport + parentRoute: typeof NonNestedRouteRoute + } + '/non-nested/path': { + id: '/non-nested/path' + path: '/path' + fullPath: '/non-nested/path' + preLoaderRoute: typeof NonNestedPathRouteRouteImport + parentRoute: typeof NonNestedRouteRoute + } + '/non-nested/prefix': { + id: '/non-nested/prefix' + path: '/prefix' + fullPath: '/non-nested/prefix' + preLoaderRoute: typeof NonNestedPrefixRouteRouteImport + parentRoute: typeof NonNestedRouteRoute + } + '/non-nested/suffix': { + id: '/non-nested/suffix' + path: '/suffix' + fullPath: '/non-nested/suffix' + preLoaderRoute: typeof NonNestedSuffixRouteRouteImport + parentRoute: typeof NonNestedRouteRoute } '/params-ps/': { id: '/params-ps/' @@ -1476,20 +1518,20 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof ParamsPsIndexRouteImport parentRoute: typeof rootRouteImport } - '/search-params/default': { - id: '/search-params/default' - path: '/default' - fullPath: '/search-params/default' - preLoaderRoute: typeof SearchParamsDefaultRouteImport - parentRoute: typeof SearchParamsRouteRoute - } - '/redirect/$target': { - id: '/redirect/$target' - path: '/redirect/$target' - fullPath: '/redirect/$target' - preLoaderRoute: typeof RedirectTargetRouteImport + '/params-ps/non-nested': { + id: '/params-ps/non-nested' + path: '/params-ps/non-nested' + fullPath: '/params-ps/non-nested' + preLoaderRoute: typeof ParamsPsNonNestedRouteRouteImport parentRoute: typeof rootRouteImport } + '/posts/': { + id: '/posts/' + path: '/' + fullPath: '/posts/' + preLoaderRoute: typeof PostsIndexRouteImport + parentRoute: typeof PostsRoute + } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' @@ -1497,39 +1539,32 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/_layout/_layout-2': { - id: '/_layout/_layout-2' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutLayout2RouteImport - parentRoute: typeof LayoutRoute - } - '/(group)/lazyinside': { - id: '/(group)/lazyinside' - path: '/lazyinside' - fullPath: '/lazyinside' - preLoaderRoute: typeof groupLazyinsideRouteImport + '/redirect/': { + id: '/redirect/' + path: '/redirect' + fullPath: '/redirect/' + preLoaderRoute: typeof RedirectIndexRouteImport parentRoute: typeof rootRouteImport } - '/(group)/inside': { - id: '/(group)/inside' - path: '/inside' - fullPath: '/inside' - preLoaderRoute: typeof groupInsideRouteImport + '/redirect/$target': { + id: '/redirect/$target' + path: '/redirect/$target' + fullPath: '/redirect/$target' + preLoaderRoute: typeof RedirectTargetRouteImport parentRoute: typeof rootRouteImport } - '/(group)/_layout': { - id: '/(group)/_layout' - path: '' - fullPath: '' - preLoaderRoute: typeof groupLayoutRouteImport + '/relative/': { + id: '/relative/' + path: '/relative' + fullPath: '/relative/' + preLoaderRoute: typeof RelativeIndexRouteImport parentRoute: typeof rootRouteImport } - '/(another-group)/onlyrouteinside': { - id: '/(another-group)/onlyrouteinside' - path: '/onlyrouteinside' - fullPath: '/onlyrouteinside' - preLoaderRoute: typeof anotherGroupOnlyrouteinsideRouteImport + '/relative/link': { + id: '/relative/link' + path: '/relative/link' + fullPath: '/relative/link' + preLoaderRoute: typeof RelativeLinkRouteRouteImport parentRoute: typeof rootRouteImport } '/relative/useNavigate': { @@ -1539,193 +1574,151 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof RelativeUseNavigateRouteRouteImport parentRoute: typeof rootRouteImport } - '/relative/link': { - id: '/relative/link' - path: '/relative/link' - fullPath: '/relative/link' - preLoaderRoute: typeof RelativeLinkRouteRouteImport - parentRoute: typeof rootRouteImport - } - '/params-ps/non-nested': { - id: '/params-ps/non-nested' - path: '/params-ps/non-nested' - fullPath: '/params-ps/non-nested' - preLoaderRoute: typeof ParamsPsNonNestedRouteRouteImport - parentRoute: typeof rootRouteImport + '/search-params/': { + id: '/search-params/' + path: '/' + fullPath: '/search-params/' + preLoaderRoute: typeof SearchParamsIndexRouteImport + parentRoute: typeof SearchParamsRouteRoute } - '/non-nested/suffix': { - id: '/non-nested/suffix' - path: '/suffix' - fullPath: '/non-nested/suffix' - preLoaderRoute: typeof NonNestedSuffixRouteRouteImport - parentRoute: typeof NonNestedRouteRoute - } - '/non-nested/prefix': { - id: '/non-nested/prefix' - path: '/prefix' - fullPath: '/non-nested/prefix' - preLoaderRoute: typeof NonNestedPrefixRouteRouteImport - parentRoute: typeof NonNestedRouteRoute - } - '/non-nested/path': { - id: '/non-nested/path' - path: '/path' - fullPath: '/non-nested/path' - preLoaderRoute: typeof NonNestedPathRouteRouteImport - parentRoute: typeof NonNestedRouteRoute - } - '/non-nested/named': { - id: '/non-nested/named' - path: '/named' - fullPath: '/non-nested/named' - preLoaderRoute: typeof NonNestedNamedRouteRouteImport - parentRoute: typeof NonNestedRouteRoute - } - '/non-nested/deep': { - id: '/non-nested/deep' - path: '/deep' - fullPath: '/non-nested/deep' - preLoaderRoute: typeof NonNestedDeepRouteRouteImport - parentRoute: typeof NonNestedRouteRoute + '/search-params/default': { + id: '/search-params/default' + path: '/default' + fullPath: '/search-params/default' + preLoaderRoute: typeof SearchParamsDefaultRouteImport + parentRoute: typeof SearchParamsRouteRoute } - '/redirect/$target/': { - id: '/redirect/$target/' - path: '/' - fullPath: '/redirect/$target/' - preLoaderRoute: typeof RedirectTargetIndexRouteImport - parentRoute: typeof RedirectTargetRoute + '/(group)/_layout/insidelayout': { + id: '/(group)/_layout/insidelayout' + path: '/insidelayout' + fullPath: '/insidelayout' + preLoaderRoute: typeof groupLayoutInsidelayoutRouteImport + parentRoute: typeof groupLayoutRoute } - '/params-ps/wildcard/': { - id: '/params-ps/wildcard/' - path: '/params-ps/wildcard' - fullPath: '/params-ps/wildcard/' - preLoaderRoute: typeof ParamsPsWildcardIndexRouteImport + '/(group)/subfolder/inside': { + id: '/(group)/subfolder/inside' + path: '/subfolder/inside' + fullPath: '/subfolder/inside' + preLoaderRoute: typeof groupSubfolderInsideRouteImport parentRoute: typeof rootRouteImport } - '/params-ps/named/': { - id: '/params-ps/named/' - path: '/params-ps/named' - fullPath: '/params-ps/named/' - preLoaderRoute: typeof ParamsPsNamedIndexRouteImport - parentRoute: typeof rootRouteImport + '/_layout/_layout-2/layout-a': { + id: '/_layout/_layout-2/layout-a' + path: '/layout-a' + fullPath: '/layout-a' + preLoaderRoute: typeof LayoutLayout2LayoutARouteImport + parentRoute: typeof LayoutLayout2Route } - '/대한민국/🚀/$id': { - id: '/대한민국/🚀/$id' - path: '/🚀/$id' - fullPath: '/대한민국/🚀/$id' - preLoaderRoute: typeof Char45824Char54620Char48124Char44397Char55357Char56960IdRouteImport - parentRoute: typeof Char45824Char54620Char48124Char44397RouteRoute + '/_layout/_layout-2/layout-b': { + id: '/_layout/_layout-2/layout-b' + path: '/layout-b' + fullPath: '/layout-b' + preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport + parentRoute: typeof LayoutLayout2Route } - '/대한민국/wildcard/$': { - id: '/대한민국/wildcard/$' - path: '/wildcard/$' - fullPath: '/대한민국/wildcard/$' - preLoaderRoute: typeof Char45824Char54620Char48124Char44397WildcardSplatRouteImport - parentRoute: typeof Char45824Char54620Char48124Char44397RouteRoute + '/masks/admin/$userId': { + id: '/masks/admin/$userId' + path: '/admin/$userId' + fullPath: '/masks/admin/$userId' + preLoaderRoute: typeof MasksAdminUserIdRouteImport + parentRoute: typeof MasksRoute } - '/transition/typing/create-resource': { - id: '/transition/typing/create-resource' - path: '/transition/typing/create-resource' - fullPath: '/transition/typing/create-resource' - preLoaderRoute: typeof TransitionTypingCreateResourceRouteImport - parentRoute: typeof rootRouteImport + '/masks/public/$username': { + id: '/masks/public/$username' + path: '/public/$username' + fullPath: '/masks/public/$username' + preLoaderRoute: typeof MasksPublicUsernameRouteImport + parentRoute: typeof MasksRoute } - '/transition/count/create-resource': { - id: '/transition/count/create-resource' - path: '/transition/count/create-resource' - fullPath: '/transition/count/create-resource' - preLoaderRoute: typeof TransitionCountCreateResourceRouteImport - parentRoute: typeof rootRouteImport + '/non-nested/deep/$baz': { + id: '/non-nested/deep/$baz' + path: '/$baz' + fullPath: '/non-nested/deep/$baz' + preLoaderRoute: typeof NonNestedDeepBazRouteRouteImport + parentRoute: typeof NonNestedDeepRouteRoute } - '/relative/useNavigate/relative-useNavigate-b': { - id: '/relative/useNavigate/relative-useNavigate-b' - path: '/relative-useNavigate-b' - fullPath: '/relative/useNavigate/relative-useNavigate-b' - preLoaderRoute: typeof RelativeUseNavigateRelativeUseNavigateBRouteImport - parentRoute: typeof RelativeUseNavigateRouteRoute + '/non-nested/named/$baz': { + id: '/non-nested/named/$baz' + path: '/$baz' + fullPath: '/non-nested/named/$baz' + preLoaderRoute: typeof NonNestedNamedBazRouteRouteImport + parentRoute: typeof NonNestedNamedRouteRoute } - '/relative/useNavigate/relative-useNavigate-a': { - id: '/relative/useNavigate/relative-useNavigate-a' - path: '/relative-useNavigate-a' - fullPath: '/relative/useNavigate/relative-useNavigate-a' - preLoaderRoute: typeof RelativeUseNavigateRelativeUseNavigateARouteImport - parentRoute: typeof RelativeUseNavigateRouteRoute + '/non-nested/path/baz': { + id: '/non-nested/path/baz' + path: '/baz' + fullPath: '/non-nested/path/baz' + preLoaderRoute: typeof NonNestedPathBazRouteRouteImport + parentRoute: typeof NonNestedPathRouteRoute } - '/relative/link/relative-link-b': { - id: '/relative/link/relative-link-b' - path: '/relative-link-b' - fullPath: '/relative/link/relative-link-b' - preLoaderRoute: typeof RelativeLinkRelativeLinkBRouteImport - parentRoute: typeof RelativeLinkRouteRoute + '/non-nested/prefix/prefix{$baz}': { + id: '/non-nested/prefix/prefix{$baz}' + path: '/prefix{$baz}' + fullPath: '/non-nested/prefix/prefix{$baz}' + preLoaderRoute: typeof NonNestedPrefixPrefixChar123bazChar125RouteRouteImport + parentRoute: typeof NonNestedPrefixRouteRoute } - '/relative/link/relative-link-a': { - id: '/relative/link/relative-link-a' - path: '/relative-link-a' - fullPath: '/relative/link/relative-link-a' - preLoaderRoute: typeof RelativeLinkRelativeLinkARouteImport - parentRoute: typeof RelativeLinkRouteRoute + '/non-nested/suffix/{$baz}suffix': { + id: '/non-nested/suffix/{$baz}suffix' + path: '/{$baz}suffix' + fullPath: '/non-nested/suffix/{$baz}suffix' + preLoaderRoute: typeof NonNestedSuffixChar123bazChar125suffixRouteRouteImport + parentRoute: typeof NonNestedSuffixRouteRoute } - '/redirect/preload/third': { - id: '/redirect/preload/third' - path: '/redirect/preload/third' - fullPath: '/redirect/preload/third' - preLoaderRoute: typeof RedirectPreloadThirdRouteImport + '/params-ps/named/': { + id: '/params-ps/named/' + path: '/params-ps/named' + fullPath: '/params-ps/named/' + preLoaderRoute: typeof ParamsPsNamedIndexRouteImport parentRoute: typeof rootRouteImport } - '/redirect/preload/second': { - id: '/redirect/preload/second' - path: '/redirect/preload/second' - fullPath: '/redirect/preload/second' - preLoaderRoute: typeof RedirectPreloadSecondRouteImport + '/params-ps/named/$foo': { + id: '/params-ps/named/$foo' + path: '/params-ps/named/$foo' + fullPath: '/params-ps/named/$foo' + preLoaderRoute: typeof ParamsPsNamedFooRouteRouteImport parentRoute: typeof rootRouteImport } - '/redirect/preload/first': { - id: '/redirect/preload/first' - path: '/redirect/preload/first' - fullPath: '/redirect/preload/first' - preLoaderRoute: typeof RedirectPreloadFirstRouteImport + '/params-ps/named/prefix{$foo}': { + id: '/params-ps/named/prefix{$foo}' + path: '/params-ps/named/prefix{$foo}' + fullPath: '/params-ps/named/prefix{$foo}' + preLoaderRoute: typeof ParamsPsNamedPrefixChar123fooChar125RouteImport parentRoute: typeof rootRouteImport } - '/redirect/$target/via-loader': { - id: '/redirect/$target/via-loader' - path: '/via-loader' - fullPath: '/redirect/$target/via-loader' - preLoaderRoute: typeof RedirectTargetViaLoaderRouteImport - parentRoute: typeof RedirectTargetRoute - } - '/redirect/$target/via-beforeLoad': { - id: '/redirect/$target/via-beforeLoad' - path: '/via-beforeLoad' - fullPath: '/redirect/$target/via-beforeLoad' - preLoaderRoute: typeof RedirectTargetViaBeforeLoadRouteImport - parentRoute: typeof RedirectTargetRoute - } - '/posts_/$postId/edit': { - id: '/posts_/$postId/edit' - path: '/posts/$postId/edit' - fullPath: '/posts/$postId/edit' - preLoaderRoute: typeof PostsPostIdEditRouteImport + '/params-ps/named/{$foo}suffix': { + id: '/params-ps/named/{$foo}suffix' + path: '/params-ps/named/{$foo}suffix' + fullPath: '/params-ps/named/{$foo}suffix' + preLoaderRoute: typeof ParamsPsNamedChar123fooChar125suffixRouteImport parentRoute: typeof rootRouteImport } - '/params/single/$value': { - id: '/params/single/$value' - path: '/params/single/$value' - fullPath: '/params/single/$value' - preLoaderRoute: typeof ParamsSingleValueRouteImport + '/params-ps/non-nested/$foo_': { + id: '/params-ps/non-nested/$foo_' + path: '/$foo' + fullPath: '/params-ps/non-nested/$foo' + preLoaderRoute: typeof ParamsPsNonNestedFooRouteRouteImport + parentRoute: typeof ParamsPsNonNestedRouteRoute + } + '/params-ps/wildcard/': { + id: '/params-ps/wildcard/' + path: '/params-ps/wildcard' + fullPath: '/params-ps/wildcard/' + preLoaderRoute: typeof ParamsPsWildcardIndexRouteImport parentRoute: typeof rootRouteImport } - '/params-ps/wildcard/{$}suffix@대': { - id: '/params-ps/wildcard/{$}suffix@대' - path: '/params-ps/wildcard/{$}suffix@대' - fullPath: '/params-ps/wildcard/{$}suffix@대' - preLoaderRoute: typeof ParamsPsWildcardChar123Char125suffixAtChar45824RouteImport + '/params-ps/wildcard/$': { + id: '/params-ps/wildcard/$' + path: '/params-ps/wildcard/$' + fullPath: '/params-ps/wildcard/$' + preLoaderRoute: typeof ParamsPsWildcardSplatRouteImport parentRoute: typeof rootRouteImport } - '/params-ps/wildcard/{$}suffix': { - id: '/params-ps/wildcard/{$}suffix' - path: '/params-ps/wildcard/{$}suffix' - fullPath: '/params-ps/wildcard/{$}suffix' - preLoaderRoute: typeof ParamsPsWildcardChar123Char125suffixRouteImport + '/params-ps/wildcard/prefix@대{$}': { + id: '/params-ps/wildcard/prefix@대{$}' + path: '/params-ps/wildcard/prefix@대{$}' + fullPath: '/params-ps/wildcard/prefix@대{$}' + preLoaderRoute: typeof ParamsPsWildcardPrefixAtChar45824Char123Char125RouteImport parentRoute: typeof rootRouteImport } '/params-ps/wildcard/prefix{$}': { @@ -1735,194 +1728,131 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof ParamsPsWildcardPrefixChar123Char125RouteImport parentRoute: typeof rootRouteImport } - '/params-ps/wildcard/prefix@대{$}': { - id: '/params-ps/wildcard/prefix@대{$}' - path: '/params-ps/wildcard/prefix@대{$}' - fullPath: '/params-ps/wildcard/prefix@대{$}' - preLoaderRoute: typeof ParamsPsWildcardPrefixAtChar45824Char123Char125RouteImport + '/params-ps/wildcard/{$}suffix': { + id: '/params-ps/wildcard/{$}suffix' + path: '/params-ps/wildcard/{$}suffix' + fullPath: '/params-ps/wildcard/{$}suffix' + preLoaderRoute: typeof ParamsPsWildcardChar123Char125suffixRouteImport parentRoute: typeof rootRouteImport } - '/params-ps/wildcard/$': { - id: '/params-ps/wildcard/$' - path: '/params-ps/wildcard/$' - fullPath: '/params-ps/wildcard/$' - preLoaderRoute: typeof ParamsPsWildcardSplatRouteImport + '/params-ps/wildcard/{$}suffix@대': { + id: '/params-ps/wildcard/{$}suffix@대' + path: '/params-ps/wildcard/{$}suffix@대' + fullPath: '/params-ps/wildcard/{$}suffix@대' + preLoaderRoute: typeof ParamsPsWildcardChar123Char125suffixAtChar45824RouteImport parentRoute: typeof rootRouteImport } - '/params-ps/named/{$foo}suffix': { - id: '/params-ps/named/{$foo}suffix' - path: '/params-ps/named/{$foo}suffix' - fullPath: '/params-ps/named/{$foo}suffix' - preLoaderRoute: typeof ParamsPsNamedChar123fooChar125suffixRouteImport + '/params/single/$value': { + id: '/params/single/$value' + path: '/params/single/$value' + fullPath: '/params/single/$value' + preLoaderRoute: typeof ParamsSingleValueRouteImport parentRoute: typeof rootRouteImport } - '/params-ps/named/prefix{$foo}': { - id: '/params-ps/named/prefix{$foo}' - path: '/params-ps/named/prefix{$foo}' - fullPath: '/params-ps/named/prefix{$foo}' - preLoaderRoute: typeof ParamsPsNamedPrefixChar123fooChar125RouteImport + '/posts_/$postId/edit': { + id: '/posts_/$postId/edit' + path: '/posts/$postId/edit' + fullPath: '/posts/$postId/edit' + preLoaderRoute: typeof PostsPostIdEditRouteImport parentRoute: typeof rootRouteImport } - '/masks/public/$username': { - id: '/masks/public/$username' - path: '/public/$username' - fullPath: '/masks/public/$username' - preLoaderRoute: typeof MasksPublicUsernameRouteImport - parentRoute: typeof MasksRoute - } - '/masks/admin/$userId': { - id: '/masks/admin/$userId' - path: '/admin/$userId' - fullPath: '/masks/admin/$userId' - preLoaderRoute: typeof MasksAdminUserIdRouteImport - parentRoute: typeof MasksRoute + '/redirect/$target/': { + id: '/redirect/$target/' + path: '/' + fullPath: '/redirect/$target/' + preLoaderRoute: typeof RedirectTargetIndexRouteImport + parentRoute: typeof RedirectTargetRoute } - '/_layout/_layout-2/layout-b': { - id: '/_layout/_layout-2/layout-b' - path: '/layout-b' - fullPath: '/layout-b' - preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport - parentRoute: typeof LayoutLayout2Route + '/redirect/$target/via-beforeLoad': { + id: '/redirect/$target/via-beforeLoad' + path: '/via-beforeLoad' + fullPath: '/redirect/$target/via-beforeLoad' + preLoaderRoute: typeof RedirectTargetViaBeforeLoadRouteImport + parentRoute: typeof RedirectTargetRoute } - '/_layout/_layout-2/layout-a': { - id: '/_layout/_layout-2/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: typeof LayoutLayout2LayoutARouteImport - parentRoute: typeof LayoutLayout2Route + '/redirect/$target/via-loader': { + id: '/redirect/$target/via-loader' + path: '/via-loader' + fullPath: '/redirect/$target/via-loader' + preLoaderRoute: typeof RedirectTargetViaLoaderRouteImport + parentRoute: typeof RedirectTargetRoute } - '/(group)/subfolder/inside': { - id: '/(group)/subfolder/inside' - path: '/subfolder/inside' - fullPath: '/subfolder/inside' - preLoaderRoute: typeof groupSubfolderInsideRouteImport + '/redirect/preload/first': { + id: '/redirect/preload/first' + path: '/redirect/preload/first' + fullPath: '/redirect/preload/first' + preLoaderRoute: typeof RedirectPreloadFirstRouteImport parentRoute: typeof rootRouteImport } - '/(group)/_layout/insidelayout': { - id: '/(group)/_layout/insidelayout' - path: '/insidelayout' - fullPath: '/insidelayout' - preLoaderRoute: typeof groupLayoutInsidelayoutRouteImport - parentRoute: typeof groupLayoutRoute - } - '/params-ps/non-nested/$foo_': { - id: '/params-ps/non-nested/$foo_' - path: '/$foo' - fullPath: '/params-ps/non-nested/$foo' - preLoaderRoute: typeof ParamsPsNonNestedFooRouteRouteImport - parentRoute: typeof ParamsPsNonNestedRouteRoute - } - '/params-ps/named/$foo': { - id: '/params-ps/named/$foo' - path: '/params-ps/named/$foo' - fullPath: '/params-ps/named/$foo' - preLoaderRoute: typeof ParamsPsNamedFooRouteRouteImport + '/redirect/preload/second': { + id: '/redirect/preload/second' + path: '/redirect/preload/second' + fullPath: '/redirect/preload/second' + preLoaderRoute: typeof RedirectPreloadSecondRouteImport parentRoute: typeof rootRouteImport } - '/non-nested/suffix/{$baz}suffix': { - id: '/non-nested/suffix/{$baz}suffix' - path: '/{$baz}suffix' - fullPath: '/non-nested/suffix/{$baz}suffix' - preLoaderRoute: typeof NonNestedSuffixChar123bazChar125suffixRouteRouteImport - parentRoute: typeof NonNestedSuffixRouteRoute - } - '/non-nested/prefix/prefix{$baz}': { - id: '/non-nested/prefix/prefix{$baz}' - path: '/prefix{$baz}' - fullPath: '/non-nested/prefix/prefix{$baz}' - preLoaderRoute: typeof NonNestedPrefixPrefixChar123bazChar125RouteRouteImport - parentRoute: typeof NonNestedPrefixRouteRoute - } - '/non-nested/path/baz': { - id: '/non-nested/path/baz' - path: '/baz' - fullPath: '/non-nested/path/baz' - preLoaderRoute: typeof NonNestedPathBazRouteRouteImport - parentRoute: typeof NonNestedPathRouteRoute - } - '/non-nested/named/$baz': { - id: '/non-nested/named/$baz' - path: '/$baz' - fullPath: '/non-nested/named/$baz' - preLoaderRoute: typeof NonNestedNamedBazRouteRouteImport - parentRoute: typeof NonNestedNamedRouteRoute + '/redirect/preload/third': { + id: '/redirect/preload/third' + path: '/redirect/preload/third' + fullPath: '/redirect/preload/third' + preLoaderRoute: typeof RedirectPreloadThirdRouteImport + parentRoute: typeof rootRouteImport } - '/non-nested/deep/$baz': { - id: '/non-nested/deep/$baz' - path: '/$baz' - fullPath: '/non-nested/deep/$baz' - preLoaderRoute: typeof NonNestedDeepBazRouteRouteImport - parentRoute: typeof NonNestedDeepRouteRoute + '/relative/link/relative-link-a': { + id: '/relative/link/relative-link-a' + path: '/relative-link-a' + fullPath: '/relative/link/relative-link-a' + preLoaderRoute: typeof RelativeLinkRelativeLinkARouteImport + parentRoute: typeof RelativeLinkRouteRoute } - '/relative/useNavigate/with-search/': { - id: '/relative/useNavigate/with-search/' - path: '/with-search' - fullPath: '/relative/useNavigate/with-search/' - preLoaderRoute: typeof RelativeUseNavigateWithSearchIndexRouteImport - parentRoute: typeof RelativeUseNavigateRouteRoute + '/relative/link/relative-link-b': { + id: '/relative/link/relative-link-b' + path: '/relative-link-b' + fullPath: '/relative/link/relative-link-b' + preLoaderRoute: typeof RelativeLinkRelativeLinkBRouteImport + parentRoute: typeof RelativeLinkRouteRoute } - '/relative/useNavigate/path/': { - id: '/relative/useNavigate/path/' - path: '/path' - fullPath: '/relative/useNavigate/path/' - preLoaderRoute: typeof RelativeUseNavigatePathIndexRouteImport + '/relative/useNavigate/relative-useNavigate-a': { + id: '/relative/useNavigate/relative-useNavigate-a' + path: '/relative-useNavigate-a' + fullPath: '/relative/useNavigate/relative-useNavigate-a' + preLoaderRoute: typeof RelativeUseNavigateRelativeUseNavigateARouteImport parentRoute: typeof RelativeUseNavigateRouteRoute } - '/relative/useNavigate/nested/': { - id: '/relative/useNavigate/nested/' - path: '/nested' - fullPath: '/relative/useNavigate/nested/' - preLoaderRoute: typeof RelativeUseNavigateNestedIndexRouteImport + '/relative/useNavigate/relative-useNavigate-b': { + id: '/relative/useNavigate/relative-useNavigate-b' + path: '/relative-useNavigate-b' + fullPath: '/relative/useNavigate/relative-useNavigate-b' + preLoaderRoute: typeof RelativeUseNavigateRelativeUseNavigateBRouteImport parentRoute: typeof RelativeUseNavigateRouteRoute } - '/relative/link/with-search/': { - id: '/relative/link/with-search/' - path: '/with-search' - fullPath: '/relative/link/with-search/' - preLoaderRoute: typeof RelativeLinkWithSearchIndexRouteImport - parentRoute: typeof RelativeLinkRouteRoute - } - '/relative/link/path/': { - id: '/relative/link/path/' - path: '/path' - fullPath: '/relative/link/path/' - preLoaderRoute: typeof RelativeLinkPathIndexRouteImport - parentRoute: typeof RelativeLinkRouteRoute - } - '/relative/link/nested/': { - id: '/relative/link/nested/' - path: '/nested' - fullPath: '/relative/link/nested/' - preLoaderRoute: typeof RelativeLinkNestedIndexRouteImport - parentRoute: typeof RelativeLinkRouteRoute - } - '/non-nested/suffix/{$baz}suffix/': { - id: '/non-nested/suffix/{$baz}suffix/' - path: '/' - fullPath: '/non-nested/suffix/{$baz}suffix/' - preLoaderRoute: typeof NonNestedSuffixChar123bazChar125suffixIndexRouteImport - parentRoute: typeof NonNestedSuffixChar123bazChar125suffixRouteRoute + '/transition/count/create-resource': { + id: '/transition/count/create-resource' + path: '/transition/count/create-resource' + fullPath: '/transition/count/create-resource' + preLoaderRoute: typeof TransitionCountCreateResourceRouteImport + parentRoute: typeof rootRouteImport } - '/non-nested/prefix/prefix{$baz}/': { - id: '/non-nested/prefix/prefix{$baz}/' - path: '/' - fullPath: '/non-nested/prefix/prefix{$baz}/' - preLoaderRoute: typeof NonNestedPrefixPrefixChar123bazChar125IndexRouteImport - parentRoute: typeof NonNestedPrefixPrefixChar123bazChar125RouteRoute + '/transition/typing/create-resource': { + id: '/transition/typing/create-resource' + path: '/transition/typing/create-resource' + fullPath: '/transition/typing/create-resource' + preLoaderRoute: typeof TransitionTypingCreateResourceRouteImport + parentRoute: typeof rootRouteImport } - '/non-nested/path/baz/': { - id: '/non-nested/path/baz/' - path: '/' - fullPath: '/non-nested/path/baz/' - preLoaderRoute: typeof NonNestedPathBazIndexRouteImport - parentRoute: typeof NonNestedPathBazRouteRoute + '/대한민국/wildcard/$': { + id: '/대한민국/wildcard/$' + path: '/wildcard/$' + fullPath: '/대한민국/wildcard/$' + preLoaderRoute: typeof Char45824Char54620Char48124Char44397WildcardSplatRouteImport + parentRoute: typeof Char45824Char54620Char48124Char44397RouteRoute } - '/non-nested/named/$baz/': { - id: '/non-nested/named/$baz/' - path: '/' - fullPath: '/non-nested/named/$baz/' - preLoaderRoute: typeof NonNestedNamedBazIndexRouteImport - parentRoute: typeof NonNestedNamedBazRouteRoute + '/대한민국/🚀/$id': { + id: '/대한민국/🚀/$id' + path: '/🚀/$id' + fullPath: '/대한민국/🚀/$id' + preLoaderRoute: typeof Char45824Char54620Char48124Char44397Char55357Char56960IdRouteImport + parentRoute: typeof Char45824Char54620Char48124Char44397RouteRoute } '/non-nested/deep/$baz/': { id: '/non-nested/deep/$baz/' @@ -1931,40 +1861,47 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof NonNestedDeepBazIndexRouteImport parentRoute: typeof NonNestedDeepBazRouteRoute } - '/params-ps/non-nested/$foo_/$bar': { - id: '/params-ps/non-nested/$foo_/$bar' - path: '/$bar' - fullPath: '/params-ps/non-nested/$foo/$bar' - preLoaderRoute: typeof ParamsPsNonNestedFooBarRouteImport - parentRoute: typeof ParamsPsNonNestedFooRouteRoute + '/non-nested/deep/$baz_/bar': { + id: '/non-nested/deep/$baz_/bar' + path: '/$baz/bar' + fullPath: '/non-nested/deep/$baz/bar' + preLoaderRoute: typeof NonNestedDeepBazBarRouteRouteImport + parentRoute: typeof NonNestedDeepRouteRoute } - '/non-nested/suffix/{$baz}suffix_/bar': { - id: '/non-nested/suffix/{$baz}suffix_/bar' - path: '/{$baz}suffix/bar' - fullPath: '/non-nested/suffix/{$baz}suffix/bar' - preLoaderRoute: typeof NonNestedSuffixChar123bazChar125suffixBarRouteImport - parentRoute: typeof NonNestedSuffixRouteRoute + '/non-nested/named/$baz/': { + id: '/non-nested/named/$baz/' + path: '/' + fullPath: '/non-nested/named/$baz/' + preLoaderRoute: typeof NonNestedNamedBazIndexRouteImport + parentRoute: typeof NonNestedNamedBazRouteRoute } - '/non-nested/suffix/{$baz}suffix/foo': { - id: '/non-nested/suffix/{$baz}suffix/foo' + '/non-nested/named/$baz/foo': { + id: '/non-nested/named/$baz/foo' path: '/foo' - fullPath: '/non-nested/suffix/{$baz}suffix/foo' - preLoaderRoute: typeof NonNestedSuffixChar123bazChar125suffixFooRouteImport - parentRoute: typeof NonNestedSuffixChar123bazChar125suffixRouteRoute + fullPath: '/non-nested/named/$baz/foo' + preLoaderRoute: typeof NonNestedNamedBazFooRouteImport + parentRoute: typeof NonNestedNamedBazRouteRoute } - '/non-nested/prefix/prefix{$baz}_/bar': { - id: '/non-nested/prefix/prefix{$baz}_/bar' - path: '/prefix{$baz}/bar' - fullPath: '/non-nested/prefix/prefix{$baz}/bar' - preLoaderRoute: typeof NonNestedPrefixPrefixChar123bazChar125BarRouteImport - parentRoute: typeof NonNestedPrefixRouteRoute + '/non-nested/named/$baz_/bar': { + id: '/non-nested/named/$baz_/bar' + path: '/$baz/bar' + fullPath: '/non-nested/named/$baz/bar' + preLoaderRoute: typeof NonNestedNamedBazBarRouteImport + parentRoute: typeof NonNestedNamedRouteRoute } - '/non-nested/prefix/prefix{$baz}/foo': { - id: '/non-nested/prefix/prefix{$baz}/foo' + '/non-nested/path/baz/': { + id: '/non-nested/path/baz/' + path: '/' + fullPath: '/non-nested/path/baz/' + preLoaderRoute: typeof NonNestedPathBazIndexRouteImport + parentRoute: typeof NonNestedPathBazRouteRoute + } + '/non-nested/path/baz/foo': { + id: '/non-nested/path/baz/foo' path: '/foo' - fullPath: '/non-nested/prefix/prefix{$baz}/foo' - preLoaderRoute: typeof NonNestedPrefixPrefixChar123bazChar125FooRouteImport - parentRoute: typeof NonNestedPrefixPrefixChar123bazChar125RouteRoute + fullPath: '/non-nested/path/baz/foo' + preLoaderRoute: typeof NonNestedPathBazFooRouteImport + parentRoute: typeof NonNestedPathBazRouteRoute } '/non-nested/path/baz_/bar': { id: '/non-nested/path/baz_/bar' @@ -1973,26 +1910,47 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof NonNestedPathBazBarRouteImport parentRoute: typeof NonNestedPathRouteRoute } - '/non-nested/path/baz/foo': { - id: '/non-nested/path/baz/foo' + '/non-nested/prefix/prefix{$baz}/': { + id: '/non-nested/prefix/prefix{$baz}/' + path: '/' + fullPath: '/non-nested/prefix/prefix{$baz}/' + preLoaderRoute: typeof NonNestedPrefixPrefixChar123bazChar125IndexRouteImport + parentRoute: typeof NonNestedPrefixPrefixChar123bazChar125RouteRoute + } + '/non-nested/prefix/prefix{$baz}/foo': { + id: '/non-nested/prefix/prefix{$baz}/foo' path: '/foo' - fullPath: '/non-nested/path/baz/foo' - preLoaderRoute: typeof NonNestedPathBazFooRouteImport - parentRoute: typeof NonNestedPathBazRouteRoute + fullPath: '/non-nested/prefix/prefix{$baz}/foo' + preLoaderRoute: typeof NonNestedPrefixPrefixChar123bazChar125FooRouteImport + parentRoute: typeof NonNestedPrefixPrefixChar123bazChar125RouteRoute } - '/non-nested/named/$baz_/bar': { - id: '/non-nested/named/$baz_/bar' - path: '/$baz/bar' - fullPath: '/non-nested/named/$baz/bar' - preLoaderRoute: typeof NonNestedNamedBazBarRouteImport - parentRoute: typeof NonNestedNamedRouteRoute + '/non-nested/prefix/prefix{$baz}_/bar': { + id: '/non-nested/prefix/prefix{$baz}_/bar' + path: '/prefix{$baz}/bar' + fullPath: '/non-nested/prefix/prefix{$baz}/bar' + preLoaderRoute: typeof NonNestedPrefixPrefixChar123bazChar125BarRouteImport + parentRoute: typeof NonNestedPrefixRouteRoute } - '/non-nested/named/$baz/foo': { - id: '/non-nested/named/$baz/foo' + '/non-nested/suffix/{$baz}suffix/': { + id: '/non-nested/suffix/{$baz}suffix/' + path: '/' + fullPath: '/non-nested/suffix/{$baz}suffix/' + preLoaderRoute: typeof NonNestedSuffixChar123bazChar125suffixIndexRouteImport + parentRoute: typeof NonNestedSuffixChar123bazChar125suffixRouteRoute + } + '/non-nested/suffix/{$baz}suffix/foo': { + id: '/non-nested/suffix/{$baz}suffix/foo' path: '/foo' - fullPath: '/non-nested/named/$baz/foo' - preLoaderRoute: typeof NonNestedNamedBazFooRouteImport - parentRoute: typeof NonNestedNamedBazRouteRoute + fullPath: '/non-nested/suffix/{$baz}suffix/foo' + preLoaderRoute: typeof NonNestedSuffixChar123bazChar125suffixFooRouteImport + parentRoute: typeof NonNestedSuffixChar123bazChar125suffixRouteRoute + } + '/non-nested/suffix/{$baz}suffix_/bar': { + id: '/non-nested/suffix/{$baz}suffix_/bar' + path: '/{$baz}suffix/bar' + fullPath: '/non-nested/suffix/{$baz}suffix/bar' + preLoaderRoute: typeof NonNestedSuffixChar123bazChar125suffixBarRouteImport + parentRoute: typeof NonNestedSuffixRouteRoute } '/params-ps/named/$foo/$bar': { id: '/params-ps/named/$foo/$bar' @@ -2001,41 +1959,55 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof ParamsPsNamedFooBarRouteRouteImport parentRoute: typeof ParamsPsNamedFooRouteRoute } - '/non-nested/deep/$baz_/bar': { - id: '/non-nested/deep/$baz_/bar' - path: '/$baz/bar' - fullPath: '/non-nested/deep/$baz/bar' - preLoaderRoute: typeof NonNestedDeepBazBarRouteRouteImport - parentRoute: typeof NonNestedDeepRouteRoute - } - '/relative/useNavigate/path/$path/': { - id: '/relative/useNavigate/path/$path/' - path: '/path/$path' - fullPath: '/relative/useNavigate/path/$path/' - preLoaderRoute: typeof RelativeUseNavigatePathPathIndexRouteImport - parentRoute: typeof RelativeUseNavigateRouteRoute + '/params-ps/non-nested/$foo_/$bar': { + id: '/params-ps/non-nested/$foo_/$bar' + path: '/$bar' + fullPath: '/params-ps/non-nested/$foo/$bar' + preLoaderRoute: typeof ParamsPsNonNestedFooBarRouteImport + parentRoute: typeof ParamsPsNonNestedFooRouteRoute } - '/relative/useNavigate/nested/deep/': { - id: '/relative/useNavigate/nested/deep/' - path: '/nested/deep' - fullPath: '/relative/useNavigate/nested/deep/' - preLoaderRoute: typeof RelativeUseNavigateNestedDeepIndexRouteImport - parentRoute: typeof RelativeUseNavigateRouteRoute + '/relative/link/nested/': { + id: '/relative/link/nested/' + path: '/nested' + fullPath: '/relative/link/nested/' + preLoaderRoute: typeof RelativeLinkNestedIndexRouteImport + parentRoute: typeof RelativeLinkRouteRoute } - '/relative/link/path/$path/': { - id: '/relative/link/path/$path/' - path: '/path/$path' - fullPath: '/relative/link/path/$path/' - preLoaderRoute: typeof RelativeLinkPathPathIndexRouteImport + '/relative/link/path/': { + id: '/relative/link/path/' + path: '/path' + fullPath: '/relative/link/path/' + preLoaderRoute: typeof RelativeLinkPathIndexRouteImport parentRoute: typeof RelativeLinkRouteRoute } - '/relative/link/nested/deep/': { - id: '/relative/link/nested/deep/' - path: '/nested/deep' - fullPath: '/relative/link/nested/deep/' - preLoaderRoute: typeof RelativeLinkNestedDeepIndexRouteImport + '/relative/link/with-search/': { + id: '/relative/link/with-search/' + path: '/with-search' + fullPath: '/relative/link/with-search/' + preLoaderRoute: typeof RelativeLinkWithSearchIndexRouteImport parentRoute: typeof RelativeLinkRouteRoute } + '/relative/useNavigate/nested/': { + id: '/relative/useNavigate/nested/' + path: '/nested' + fullPath: '/relative/useNavigate/nested/' + preLoaderRoute: typeof RelativeUseNavigateNestedIndexRouteImport + parentRoute: typeof RelativeUseNavigateRouteRoute + } + '/relative/useNavigate/path/': { + id: '/relative/useNavigate/path/' + path: '/path' + fullPath: '/relative/useNavigate/path/' + preLoaderRoute: typeof RelativeUseNavigatePathIndexRouteImport + parentRoute: typeof RelativeUseNavigateRouteRoute + } + '/relative/useNavigate/with-search/': { + id: '/relative/useNavigate/with-search/' + path: '/with-search' + fullPath: '/relative/useNavigate/with-search/' + preLoaderRoute: typeof RelativeUseNavigateWithSearchIndexRouteImport + parentRoute: typeof RelativeUseNavigateRouteRoute + } '/non-nested/deep/$baz_/bar/': { id: '/non-nested/deep/$baz_/bar/' path: '/' @@ -2043,12 +2015,12 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof NonNestedDeepBazBarIndexRouteImport parentRoute: typeof NonNestedDeepBazBarRouteRoute } - '/params-ps/named/$foo/$bar/$baz': { - id: '/params-ps/named/$foo/$bar/$baz' - path: '/$baz' - fullPath: '/params-ps/named/$foo/$bar/$baz' - preLoaderRoute: typeof ParamsPsNamedFooBarBazRouteImport - parentRoute: typeof ParamsPsNamedFooBarRouteRoute + '/non-nested/deep/$baz_/bar/$foo': { + id: '/non-nested/deep/$baz_/bar/$foo' + path: '/$foo' + fullPath: '/non-nested/deep/$baz/bar/$foo' + preLoaderRoute: typeof NonNestedDeepBazBarFooRouteRouteImport + parentRoute: typeof NonNestedDeepBazBarRouteRoute } '/non-nested/deep/$baz_/bar_/qux': { id: '/non-nested/deep/$baz_/bar_/qux' @@ -2057,12 +2029,40 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof NonNestedDeepBazBarQuxRouteImport parentRoute: typeof NonNestedDeepRouteRoute } - '/non-nested/deep/$baz_/bar/$foo': { - id: '/non-nested/deep/$baz_/bar/$foo' - path: '/$foo' - fullPath: '/non-nested/deep/$baz/bar/$foo' - preLoaderRoute: typeof NonNestedDeepBazBarFooRouteRouteImport - parentRoute: typeof NonNestedDeepBazBarRouteRoute + '/params-ps/named/$foo/$bar/$baz': { + id: '/params-ps/named/$foo/$bar/$baz' + path: '/$baz' + fullPath: '/params-ps/named/$foo/$bar/$baz' + preLoaderRoute: typeof ParamsPsNamedFooBarBazRouteImport + parentRoute: typeof ParamsPsNamedFooBarRouteRoute + } + '/relative/link/nested/deep/': { + id: '/relative/link/nested/deep/' + path: '/nested/deep' + fullPath: '/relative/link/nested/deep/' + preLoaderRoute: typeof RelativeLinkNestedDeepIndexRouteImport + parentRoute: typeof RelativeLinkRouteRoute + } + '/relative/link/path/$path/': { + id: '/relative/link/path/$path/' + path: '/path/$path' + fullPath: '/relative/link/path/$path/' + preLoaderRoute: typeof RelativeLinkPathPathIndexRouteImport + parentRoute: typeof RelativeLinkRouteRoute + } + '/relative/useNavigate/nested/deep/': { + id: '/relative/useNavigate/nested/deep/' + path: '/nested/deep' + fullPath: '/relative/useNavigate/nested/deep/' + preLoaderRoute: typeof RelativeUseNavigateNestedDeepIndexRouteImport + parentRoute: typeof RelativeUseNavigateRouteRoute + } + '/relative/useNavigate/path/$path/': { + id: '/relative/useNavigate/path/$path/' + path: '/path/$path' + fullPath: '/relative/useNavigate/path/$path/' + preLoaderRoute: typeof RelativeUseNavigatePathPathIndexRouteImport + parentRoute: typeof RelativeUseNavigateRouteRoute } '/non-nested/deep/$baz_/bar/$foo/': { id: '/non-nested/deep/$baz_/bar/$foo/' diff --git a/e2e/solid-router/basic-solid-query-file-based/src/routeTree.gen.ts b/e2e/solid-router/basic-solid-query-file-based/src/routeTree.gen.ts index e955e85ecc..9cc1d71391 100644 --- a/e2e/solid-router/basic-solid-query-file-based/src/routeTree.gen.ts +++ b/e2e/solid-router/basic-solid-query-file-based/src/routeTree.gen.ts @@ -9,29 +9,33 @@ // 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 PostsRouteImport } from './routes/posts' -import { Route as LayoutRouteImport } from './routes/_layout' import { Route as IndexRouteImport } from './routes/index' +import { Route as LayoutRouteImport } from './routes/_layout' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' -import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' +import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const LayoutRoute = LayoutRouteImport.update({ id: '/_layout', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) +const LayoutLayout2Route = LayoutLayout2RouteImport.update({ + id: '/_layout-2', + getParentRoute: () => LayoutRoute, +} as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -42,20 +46,16 @@ const PostsPostIdRoute = PostsPostIdRouteImport.update({ path: '/$postId', getParentRoute: () => PostsRoute, } as any) -const LayoutLayout2Route = LayoutLayout2RouteImport.update({ - id: '/_layout-2', - getParentRoute: () => LayoutRoute, +const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ + id: '/layout-a', + path: '/layout-a', + getParentRoute: () => LayoutLayout2Route, } as any) const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ id: '/layout-b', path: '/layout-b', getParentRoute: () => LayoutLayout2Route, } as any) -const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ - id: '/layout-a', - path: '/layout-a', - getParentRoute: () => LayoutLayout2Route, -} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -114,11 +114,11 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/_layout': { @@ -128,13 +128,20 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } + '/_layout/_layout-2': { + id: '/_layout/_layout-2' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutLayout2RouteImport + parentRoute: typeof LayoutRoute + } '/posts/': { id: '/posts/' path: '/' @@ -149,12 +156,12 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/_layout/_layout-2': { - id: '/_layout/_layout-2' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutLayout2RouteImport - parentRoute: typeof LayoutRoute + '/_layout/_layout-2/layout-a': { + id: '/_layout/_layout-2/layout-a' + path: '/layout-a' + fullPath: '/layout-a' + preLoaderRoute: typeof LayoutLayout2LayoutARouteImport + parentRoute: typeof LayoutLayout2Route } '/_layout/_layout-2/layout-b': { id: '/_layout/_layout-2/layout-b' @@ -163,13 +170,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport parentRoute: typeof LayoutLayout2Route } - '/_layout/_layout-2/layout-a': { - id: '/_layout/_layout-2/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: typeof LayoutLayout2LayoutARouteImport - parentRoute: typeof LayoutLayout2Route - } } } diff --git a/e2e/solid-router/basic-virtual-file-based/src/routeTree.gen.ts b/e2e/solid-router/basic-virtual-file-based/src/routeTree.gen.ts index 1c9691ab23..0aa373b49f 100644 --- a/e2e/solid-router/basic-virtual-file-based/src/routeTree.gen.ts +++ b/e2e/solid-router/basic-virtual-file-based/src/routeTree.gen.ts @@ -9,76 +9,76 @@ // 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 postsPostsRouteImport } from './routes/posts/posts' -import { Route as layoutFirstLayoutRouteImport } from './routes/layout/first-layout' import { Route as homeRouteImport } from './routes/home' -import { Route as postsPostsDetailRouteImport } from './routes/posts/posts-detail' +import { Route as layoutFirstLayoutRouteImport } from './routes/layout/first-layout' +import { Route as postsPostsRouteImport } from './routes/posts/posts' import { Route as layoutSecondLayoutRouteImport } from './routes/layout/second-layout' -import { Route as postsPostsHomeRouteImport } from './routes/posts/posts-home' import { Route as ClassicHelloRouteRouteImport } from './routes/file-based-subtree/hello/route' +import { Route as postsPostsHomeRouteImport } from './routes/posts/posts-home' +import { Route as postsPostsDetailRouteImport } from './routes/posts/posts-detail' +import { Route as aRouteImport } from './routes/a' +import { Route as bRouteImport } from './routes/b' import { Route as ClassicHelloIndexRouteImport } from './routes/file-based-subtree/hello/index' -import { Route as ClassicHelloWorldRouteImport } from './routes/file-based-subtree/hello/world' import { Route as ClassicHelloUniverseRouteImport } from './routes/file-based-subtree/hello/universe' -import { Route as bRouteImport } from './routes/b' -import { Route as aRouteImport } from './routes/a' +import { Route as ClassicHelloWorldRouteImport } from './routes/file-based-subtree/hello/world' -const postsPostsRoute = postsPostsRouteImport.update({ - id: '/posts', - path: '/posts', +const homeRoute = homeRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const layoutFirstLayoutRoute = layoutFirstLayoutRouteImport.update({ id: '/_first', getParentRoute: () => rootRouteImport, } as any) -const homeRoute = homeRouteImport.update({ - id: '/', - path: '/', +const postsPostsRoute = postsPostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const postsPostsDetailRoute = postsPostsDetailRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => postsPostsRoute, -} as any) const layoutSecondLayoutRoute = layoutSecondLayoutRouteImport.update({ id: '/_second', getParentRoute: () => layoutFirstLayoutRoute, } as any) +const ClassicHelloRouteRoute = ClassicHelloRouteRouteImport.update({ + id: '/classic/hello', + path: '/classic/hello', + getParentRoute: () => rootRouteImport, +} as any) const postsPostsHomeRoute = postsPostsHomeRouteImport.update({ id: '/', path: '/', getParentRoute: () => postsPostsRoute, } as any) -const ClassicHelloRouteRoute = ClassicHelloRouteRouteImport.update({ - id: '/classic/hello', - path: '/classic/hello', - getParentRoute: () => rootRouteImport, +const postsPostsDetailRoute = postsPostsDetailRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => postsPostsRoute, +} as any) +const aRoute = aRouteImport.update({ + id: '/layout-a', + path: '/layout-a', + getParentRoute: () => layoutSecondLayoutRoute, +} as any) +const bRoute = bRouteImport.update({ + id: '/layout-b', + path: '/layout-b', + getParentRoute: () => layoutSecondLayoutRoute, } as any) const ClassicHelloIndexRoute = ClassicHelloIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => ClassicHelloRouteRoute, } as any) -const ClassicHelloWorldRoute = ClassicHelloWorldRouteImport.update({ - id: '/world', - path: '/world', - getParentRoute: () => ClassicHelloRouteRoute, -} as any) const ClassicHelloUniverseRoute = ClassicHelloUniverseRouteImport.update({ id: '/universe', path: '/universe', getParentRoute: () => ClassicHelloRouteRoute, } as any) -const bRoute = bRouteImport.update({ - id: '/layout-b', - path: '/layout-b', - getParentRoute: () => layoutSecondLayoutRoute, -} as any) -const aRoute = aRouteImport.update({ - id: '/layout-a', - path: '/layout-a', - getParentRoute: () => layoutSecondLayoutRoute, +const ClassicHelloWorldRoute = ClassicHelloWorldRouteImport.update({ + id: '/world', + path: '/world', + getParentRoute: () => ClassicHelloRouteRoute, } as any) export interface FileRoutesByFullPath { @@ -166,11 +166,11 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof postsPostsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof homeRouteImport parentRoute: typeof rootRouteImport } '/_first': { @@ -180,20 +180,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof layoutFirstLayoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof homeRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof postsPostsRouteImport parentRoute: typeof rootRouteImport } - '/posts/$postId': { - id: '/posts/$postId' - path: '/$postId' - fullPath: '/posts/$postId' - preLoaderRoute: typeof postsPostsDetailRouteImport - parentRoute: typeof postsPostsRoute - } '/_first/_second': { id: '/_first/_second' path: '' @@ -201,6 +194,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof layoutSecondLayoutRouteImport parentRoute: typeof layoutFirstLayoutRoute } + '/classic/hello': { + id: '/classic/hello' + path: '/classic/hello' + fullPath: '/classic/hello' + preLoaderRoute: typeof ClassicHelloRouteRouteImport + parentRoute: typeof rootRouteImport + } '/posts/': { id: '/posts/' path: '/' @@ -208,12 +208,26 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof postsPostsHomeRouteImport parentRoute: typeof postsPostsRoute } - '/classic/hello': { - id: '/classic/hello' - path: '/classic/hello' - fullPath: '/classic/hello' - preLoaderRoute: typeof ClassicHelloRouteRouteImport - parentRoute: typeof rootRouteImport + '/posts/$postId': { + id: '/posts/$postId' + path: '/$postId' + fullPath: '/posts/$postId' + preLoaderRoute: typeof postsPostsDetailRouteImport + parentRoute: typeof postsPostsRoute + } + '/_first/_second/layout-a': { + id: '/_first/_second/layout-a' + path: '/layout-a' + fullPath: '/layout-a' + preLoaderRoute: typeof aRouteImport + parentRoute: typeof layoutSecondLayoutRoute + } + '/_first/_second/layout-b': { + id: '/_first/_second/layout-b' + path: '/layout-b' + fullPath: '/layout-b' + preLoaderRoute: typeof bRouteImport + parentRoute: typeof layoutSecondLayoutRoute } '/classic/hello/': { id: '/classic/hello/' @@ -222,13 +236,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof ClassicHelloIndexRouteImport parentRoute: typeof ClassicHelloRouteRoute } - '/classic/hello/world': { - id: '/classic/hello/world' - path: '/world' - fullPath: '/classic/hello/world' - preLoaderRoute: typeof ClassicHelloWorldRouteImport - parentRoute: typeof ClassicHelloRouteRoute - } '/classic/hello/universe': { id: '/classic/hello/universe' path: '/universe' @@ -236,19 +243,12 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof ClassicHelloUniverseRouteImport parentRoute: typeof ClassicHelloRouteRoute } - '/_first/_second/layout-b': { - id: '/_first/_second/layout-b' - path: '/layout-b' - fullPath: '/layout-b' - preLoaderRoute: typeof bRouteImport - parentRoute: typeof layoutSecondLayoutRoute - } - '/_first/_second/layout-a': { - id: '/_first/_second/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: typeof aRouteImport - parentRoute: typeof layoutSecondLayoutRoute + '/classic/hello/world': { + id: '/classic/hello/world' + path: '/world' + fullPath: '/classic/hello/world' + preLoaderRoute: typeof ClassicHelloWorldRouteImport + parentRoute: typeof ClassicHelloRouteRoute } } } diff --git a/e2e/solid-router/generator-cli-only/src/routeTree.gen.ts b/e2e/solid-router/generator-cli-only/src/routeTree.gen.ts index eb6d9fe975..c0f9fbabe4 100644 --- a/e2e/solid-router/generator-cli-only/src/routeTree.gen.ts +++ b/e2e/solid-router/generator-cli-only/src/routeTree.gen.ts @@ -9,15 +9,20 @@ // 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 IndexRouteImport } from './routes/index' import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as PostsRouteRouteImport } from './routes/posts.route' -import { Route as IndexRouteImport } from './routes/index' +import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' -import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' +import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, @@ -27,11 +32,11 @@ const PostsRouteRoute = PostsRouteRouteImport.update({ path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) +const PathlessLayoutNestedLayoutRoute = + PathlessLayoutNestedLayoutRouteImport.update({ + id: '/_nested-layout', + getParentRoute: () => PathlessLayoutRoute, + } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -42,10 +47,11 @@ const PostsPostIdRoute = PostsPostIdRouteImport.update({ path: '/$postId', getParentRoute: () => PostsRouteRoute, } as any) -const PathlessLayoutNestedLayoutRoute = - PathlessLayoutNestedLayoutRouteImport.update({ - id: '/_nested-layout', - getParentRoute: () => PathlessLayoutRoute, +const PathlessLayoutNestedLayoutRouteARoute = + PathlessLayoutNestedLayoutRouteARouteImport.update({ + id: '/route-a', + path: '/route-a', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) const PathlessLayoutNestedLayoutRouteBRoute = PathlessLayoutNestedLayoutRouteBRouteImport.update({ @@ -53,12 +59,6 @@ const PathlessLayoutNestedLayoutRouteBRoute = path: '/route-b', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) -const PathlessLayoutNestedLayoutRouteARoute = - PathlessLayoutNestedLayoutRouteARouteImport.update({ - id: '/route-a', - path: '/route-a', - getParentRoute: () => PathlessLayoutNestedLayoutRoute, - } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -117,6 +117,13 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } '/_pathlessLayout': { id: '/_pathlessLayout' path: '' @@ -131,12 +138,12 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' + '/_pathlessLayout/_nested-layout': { + id: '/_pathlessLayout/_nested-layout' + path: '' fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport + parentRoute: typeof PathlessLayoutRoute } '/posts/': { id: '/posts/' @@ -152,12 +159,12 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRouteRoute } - '/_pathlessLayout/_nested-layout': { - id: '/_pathlessLayout/_nested-layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport - parentRoute: typeof PathlessLayoutRoute + '/_pathlessLayout/_nested-layout/route-a': { + id: '/_pathlessLayout/_nested-layout/route-a' + path: '/route-a' + fullPath: '/route-a' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport + parentRoute: typeof PathlessLayoutNestedLayoutRoute } '/_pathlessLayout/_nested-layout/route-b': { id: '/_pathlessLayout/_nested-layout/route-b' @@ -166,13 +173,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } - '/_pathlessLayout/_nested-layout/route-a': { - id: '/_pathlessLayout/_nested-layout/route-a' - path: '/route-a' - fullPath: '/route-a' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport - parentRoute: typeof PathlessLayoutNestedLayoutRoute - } } } diff --git a/e2e/solid-router/js-only-file-based/src/routeTree.gen.js b/e2e/solid-router/js-only-file-based/src/routeTree.gen.js index f2d42e5a6c..80683abb30 100644 --- a/e2e/solid-router/js-only-file-based/src/routeTree.gen.js +++ b/e2e/solid-router/js-only-file-based/src/routeTree.gen.js @@ -9,15 +9,20 @@ // 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 IndexRouteImport } from './routes/index' import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as PostsRouteRouteImport } from './routes/posts.route' -import { Route as IndexRouteImport } from './routes/index' +import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' -import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' +import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +}) const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, @@ -27,11 +32,11 @@ const PostsRouteRoute = PostsRouteRouteImport.update({ path: '/posts', getParentRoute: () => rootRouteImport, }) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -}) +const PathlessLayoutNestedLayoutRoute = + PathlessLayoutNestedLayoutRouteImport.update({ + id: '/_nested-layout', + getParentRoute: () => PathlessLayoutRoute, + }) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -42,10 +47,11 @@ const PostsPostIdRoute = PostsPostIdRouteImport.update({ path: '/$postId', getParentRoute: () => PostsRouteRoute, }) -const PathlessLayoutNestedLayoutRoute = - PathlessLayoutNestedLayoutRouteImport.update({ - id: '/_nested-layout', - getParentRoute: () => PathlessLayoutRoute, +const PathlessLayoutNestedLayoutRouteARoute = + PathlessLayoutNestedLayoutRouteARouteImport.update({ + id: '/route-a', + path: '/route-a', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, }) const PathlessLayoutNestedLayoutRouteBRoute = PathlessLayoutNestedLayoutRouteBRouteImport.update({ @@ -53,12 +59,6 @@ const PathlessLayoutNestedLayoutRouteBRoute = path: '/route-b', getParentRoute: () => PathlessLayoutNestedLayoutRoute, }) -const PathlessLayoutNestedLayoutRouteARoute = - PathlessLayoutNestedLayoutRouteARouteImport.update({ - id: '/route-a', - path: '/route-a', - getParentRoute: () => PathlessLayoutNestedLayoutRoute, - }) const PostsRouteRouteChildren = { PostsPostIdRoute: PostsPostIdRoute, diff --git a/e2e/solid-router/scroll-restoration-sandbox-vite/src/routeTree.gen.ts b/e2e/solid-router/scroll-restoration-sandbox-vite/src/routeTree.gen.ts index 7108ee0743..bbd53dbfe6 100644 --- a/e2e/solid-router/scroll-restoration-sandbox-vite/src/routeTree.gen.ts +++ b/e2e/solid-router/scroll-restoration-sandbox-vite/src/routeTree.gen.ts @@ -12,10 +12,10 @@ import { createFileRoute } from '@tanstack/solid-router' import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' -import { Route as testsPageWithSearchRouteImport } from './routes/(tests)/page-with-search' -import { Route as testsNormalPageRouteImport } from './routes/(tests)/normal-page' -import { Route as testsLazyWithLoaderPageRouteImport } from './routes/(tests)/lazy-with-loader-page' import { Route as testsLazyPageRouteImport } from './routes/(tests)/lazy-page' +import { Route as testsLazyWithLoaderPageRouteImport } from './routes/(tests)/lazy-with-loader-page' +import { Route as testsNormalPageRouteImport } from './routes/(tests)/normal-page' +import { Route as testsPageWithSearchRouteImport } from './routes/(tests)/page-with-search' const testsVirtualPageLazyRouteImport = createFileRoute( '/(tests)/virtual-page', @@ -26,23 +26,13 @@ const IndexRoute = IndexRouteImport.update({ path: '/', getParentRoute: () => rootRouteImport, } as any) -const testsVirtualPageLazyRoute = testsVirtualPageLazyRouteImport +const testsLazyPageRoute = testsLazyPageRouteImport .update({ - id: '/(tests)/virtual-page', - path: '/virtual-page', + id: '/(tests)/lazy-page', + path: '/lazy-page', getParentRoute: () => rootRouteImport, } as any) - .lazy(() => import('./routes/(tests)/virtual-page.lazy').then((d) => d.Route)) -const testsPageWithSearchRoute = testsPageWithSearchRouteImport.update({ - id: '/(tests)/page-with-search', - path: '/page-with-search', - getParentRoute: () => rootRouteImport, -} as any) -const testsNormalPageRoute = testsNormalPageRouteImport.update({ - id: '/(tests)/normal-page', - path: '/normal-page', - getParentRoute: () => rootRouteImport, -} as any) + .lazy(() => import('./routes/(tests)/lazy-page.lazy').then((d) => d.Route)) const testsLazyWithLoaderPageRoute = testsLazyWithLoaderPageRouteImport .update({ id: '/(tests)/lazy-with-loader-page', @@ -52,13 +42,23 @@ const testsLazyWithLoaderPageRoute = testsLazyWithLoaderPageRouteImport .lazy(() => import('./routes/(tests)/lazy-with-loader-page.lazy').then((d) => d.Route), ) -const testsLazyPageRoute = testsLazyPageRouteImport +const testsNormalPageRoute = testsNormalPageRouteImport.update({ + id: '/(tests)/normal-page', + path: '/normal-page', + getParentRoute: () => rootRouteImport, +} as any) +const testsPageWithSearchRoute = testsPageWithSearchRouteImport.update({ + id: '/(tests)/page-with-search', + path: '/page-with-search', + getParentRoute: () => rootRouteImport, +} as any) +const testsVirtualPageLazyRoute = testsVirtualPageLazyRouteImport .update({ - id: '/(tests)/lazy-page', - path: '/lazy-page', + id: '/(tests)/virtual-page', + path: '/virtual-page', getParentRoute: () => rootRouteImport, } as any) - .lazy(() => import('./routes/(tests)/lazy-page.lazy').then((d) => d.Route)) + .lazy(() => import('./routes/(tests)/virtual-page.lazy').then((d) => d.Route)) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -130,18 +130,18 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/virtual-page': { - id: '/(tests)/virtual-page' - path: '/virtual-page' - fullPath: '/virtual-page' - preLoaderRoute: typeof testsVirtualPageLazyRouteImport + '/(tests)/lazy-page': { + id: '/(tests)/lazy-page' + path: '/lazy-page' + fullPath: '/lazy-page' + preLoaderRoute: typeof testsLazyPageRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/page-with-search': { - id: '/(tests)/page-with-search' - path: '/page-with-search' - fullPath: '/page-with-search' - preLoaderRoute: typeof testsPageWithSearchRouteImport + '/(tests)/lazy-with-loader-page': { + id: '/(tests)/lazy-with-loader-page' + path: '/lazy-with-loader-page' + fullPath: '/lazy-with-loader-page' + preLoaderRoute: typeof testsLazyWithLoaderPageRouteImport parentRoute: typeof rootRouteImport } '/(tests)/normal-page': { @@ -151,18 +151,18 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof testsNormalPageRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/lazy-with-loader-page': { - id: '/(tests)/lazy-with-loader-page' - path: '/lazy-with-loader-page' - fullPath: '/lazy-with-loader-page' - preLoaderRoute: typeof testsLazyWithLoaderPageRouteImport + '/(tests)/page-with-search': { + id: '/(tests)/page-with-search' + path: '/page-with-search' + fullPath: '/page-with-search' + preLoaderRoute: typeof testsPageWithSearchRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/lazy-page': { - id: '/(tests)/lazy-page' - path: '/lazy-page' - fullPath: '/lazy-page' - preLoaderRoute: typeof testsLazyPageRouteImport + '/(tests)/virtual-page': { + id: '/(tests)/virtual-page' + path: '/virtual-page' + fullPath: '/virtual-page' + preLoaderRoute: typeof testsVirtualPageLazyRouteImport parentRoute: typeof rootRouteImport } } diff --git a/e2e/solid-router/view-transitions/src/routeTree.gen.ts b/e2e/solid-router/view-transitions/src/routeTree.gen.ts index 114e34ed9d..01843027dc 100644 --- a/e2e/solid-router/view-transitions/src/routeTree.gen.ts +++ b/e2e/solid-router/view-transitions/src/routeTree.gen.ts @@ -9,16 +9,16 @@ // 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 HowItWorksRouteImport } from './routes/how-it-works' +import { Route as IndexRouteImport } from './routes/index' import { Route as ExploreRouteImport } from './routes/explore' +import { Route as HowItWorksRouteImport } from './routes/how-it-works' import { Route as PostsRouteRouteImport } from './routes/posts.route' -import { Route as IndexRouteImport } from './routes/index' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -const HowItWorksRoute = HowItWorksRouteImport.update({ - id: '/how-it-works', - path: '/how-it-works', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const ExploreRoute = ExploreRouteImport.update({ @@ -26,16 +26,16 @@ const ExploreRoute = ExploreRouteImport.update({ path: '/explore', getParentRoute: () => rootRouteImport, } as any) +const HowItWorksRoute = HowItWorksRouteImport.update({ + id: '/how-it-works', + path: '/how-it-works', + getParentRoute: () => rootRouteImport, +} as any) const PostsRouteRoute = PostsRouteRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -101,11 +101,11 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/how-it-works': { - id: '/how-it-works' - path: '/how-it-works' - fullPath: '/how-it-works' - preLoaderRoute: typeof HowItWorksRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/explore': { @@ -115,6 +115,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof ExploreRouteImport parentRoute: typeof rootRouteImport } + '/how-it-works': { + id: '/how-it-works' + path: '/how-it-works' + fullPath: '/how-it-works' + preLoaderRoute: typeof HowItWorksRouteImport + parentRoute: typeof rootRouteImport + } '/posts': { id: '/posts' path: '/posts' @@ -122,13 +129,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport - } '/posts/': { id: '/posts/' path: '/' diff --git a/e2e/solid-start/basic-auth/src/routeTree.gen.ts b/e2e/solid-start/basic-auth/src/routeTree.gen.ts index 7019a6c21a..bbebb19df3 100644 --- a/e2e/solid-start/basic-auth/src/routeTree.gen.ts +++ b/e2e/solid-start/basic-auth/src/routeTree.gen.ts @@ -9,23 +9,22 @@ // 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 SignupRouteImport } from './routes/signup' -import { Route as LogoutRouteImport } from './routes/logout' -import { Route as LoginRouteImport } from './routes/login' -import { Route as AuthedRouteImport } from './routes/_authed' import { Route as IndexRouteImport } from './routes/index' +import { Route as AuthedRouteImport } from './routes/_authed' +import { Route as LoginRouteImport } from './routes/login' +import { Route as LogoutRouteImport } from './routes/logout' +import { Route as SignupRouteImport } from './routes/signup' import { Route as AuthedPostsRouteImport } from './routes/_authed/posts' import { Route as AuthedPostsIndexRouteImport } from './routes/_authed/posts.index' import { Route as AuthedPostsPostIdRouteImport } from './routes/_authed/posts.$postId' -const SignupRoute = SignupRouteImport.update({ - id: '/signup', - path: '/signup', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const LogoutRoute = LogoutRouteImport.update({ - id: '/logout', - path: '/logout', +const AuthedRoute = AuthedRouteImport.update({ + id: '/_authed', getParentRoute: () => rootRouteImport, } as any) const LoginRoute = LoginRouteImport.update({ @@ -33,13 +32,14 @@ const LoginRoute = LoginRouteImport.update({ path: '/login', getParentRoute: () => rootRouteImport, } as any) -const AuthedRoute = AuthedRouteImport.update({ - id: '/_authed', +const LogoutRoute = LogoutRouteImport.update({ + id: '/logout', + path: '/logout', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const SignupRoute = SignupRouteImport.update({ + id: '/signup', + path: '/signup', getParentRoute: () => rootRouteImport, } as any) const AuthedPostsRoute = AuthedPostsRouteImport.update({ @@ -120,18 +120,18 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/signup': { - id: '/signup' - path: '/signup' - fullPath: '/signup' - preLoaderRoute: typeof SignupRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/logout': { - id: '/logout' - path: '/logout' - fullPath: '/logout' - preLoaderRoute: typeof LogoutRouteImport + '/_authed': { + id: '/_authed' + path: '' + fullPath: '/' + preLoaderRoute: typeof AuthedRouteImport parentRoute: typeof rootRouteImport } '/login': { @@ -141,18 +141,18 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof LoginRouteImport parentRoute: typeof rootRouteImport } - '/_authed': { - id: '/_authed' - path: '' - fullPath: '/' - preLoaderRoute: typeof AuthedRouteImport + '/logout': { + id: '/logout' + path: '/logout' + fullPath: '/logout' + preLoaderRoute: typeof LogoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/signup': { + id: '/signup' + path: '/signup' + fullPath: '/signup' + preLoaderRoute: typeof SignupRouteImport parentRoute: typeof rootRouteImport } '/_authed/posts': { diff --git a/e2e/solid-start/basic-cloudflare/src/routeTree.gen.ts b/e2e/solid-start/basic-cloudflare/src/routeTree.gen.ts index 2bd11546dd..4e698ade2c 100644 --- a/e2e/solid-start/basic-cloudflare/src/routeTree.gen.ts +++ b/e2e/solid-start/basic-cloudflare/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 StaticRouteImport } from './routes/static' import { Route as IndexRouteImport } from './routes/index' +import { Route as StaticRouteImport } from './routes/static' -const StaticRoute = StaticRouteImport.update({ - id: '/static', - path: '/static', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const StaticRoute = StaticRouteImport.update({ + id: '/static', + path: '/static', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/static': { - id: '/static' - path: '/static' - fullPath: '/static' - preLoaderRoute: typeof StaticRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/static': { + id: '/static' + path: '/static' + fullPath: '/static' + preLoaderRoute: typeof StaticRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/e2e/solid-start/basic-solid-query/src/routeTree.gen.ts b/e2e/solid-start/basic-solid-query/src/routeTree.gen.ts index ee02d13d55..a79d1a5880 100644 --- a/e2e/solid-start/basic-solid-query/src/routeTree.gen.ts +++ b/e2e/solid-start/basic-solid-query/src/routeTree.gen.ts @@ -9,32 +9,36 @@ // 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 UsersRouteImport } from './routes/users' -import { Route as SuspenseTransitionRouteImport } from './routes/suspense-transition' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as DeferredRouteImport } from './routes/deferred' -import { Route as LayoutRouteImport } from './routes/_layout' import { Route as IndexRouteImport } from './routes/index' -import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as LayoutRouteImport } from './routes/_layout' +import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as SuspenseTransitionRouteImport } from './routes/suspense-transition' +import { Route as UsersRouteImport } from './routes/users' +import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' +import { Route as ApiUsersRouteImport } from './routes/api.users' import { Route as PostsIndexRouteImport } from './routes/posts.index' -import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as ApiUsersRouteImport } from './routes/api.users' -import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' -import { Route as TransitionCountQueryRouteImport } from './routes/transition/count/query' -import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -import { Route as ApiUsersIdRouteImport } from './routes/api/users.$id' -import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' +import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' +import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' +import { Route as ApiUsersIdRouteImport } from './routes/api/users.$id' +import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' +import { Route as TransitionCountQueryRouteImport } from './routes/transition/count/query' -const UsersRoute = UsersRouteImport.update({ - id: '/users', - path: '/users', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const SuspenseTransitionRoute = SuspenseTransitionRouteImport.update({ - id: '/suspense-transition', - path: '/suspense-transition', +const LayoutRoute = LayoutRouteImport.update({ + id: '/_layout', + getParentRoute: () => rootRouteImport, +} as any) +const DeferredRoute = DeferredRouteImport.update({ + id: '/deferred', + path: '/deferred', getParentRoute: () => rootRouteImport, } as any) const PostsRoute = PostsRouteImport.update({ @@ -42,73 +46,69 @@ const PostsRoute = PostsRouteImport.update({ path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const DeferredRoute = DeferredRouteImport.update({ - id: '/deferred', - path: '/deferred', +const SuspenseTransitionRoute = SuspenseTransitionRouteImport.update({ + id: '/suspense-transition', + path: '/suspense-transition', getParentRoute: () => rootRouteImport, } as any) -const LayoutRoute = LayoutRouteImport.update({ - id: '/_layout', +const UsersRoute = UsersRouteImport.update({ + id: '/users', + path: '/users', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, +const LayoutLayout2Route = LayoutLayout2RouteImport.update({ + id: '/_layout-2', + getParentRoute: () => LayoutRoute, } as any) -const UsersIndexRoute = UsersIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => UsersRoute, +const ApiUsersRoute = ApiUsersRouteImport.update({ + id: '/api/users', + path: '/api/users', + getParentRoute: () => rootRouteImport, } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRoute, } as any) -const UsersUserIdRoute = UsersUserIdRouteImport.update({ - id: '/$userId', - path: '/$userId', - getParentRoute: () => UsersRoute, -} as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRoute, } as any) -const ApiUsersRoute = ApiUsersRouteImport.update({ - id: '/api/users', - path: '/api/users', - getParentRoute: () => rootRouteImport, +const UsersIndexRoute = UsersIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => UsersRoute, } as any) -const LayoutLayout2Route = LayoutLayout2RouteImport.update({ - id: '/_layout-2', - getParentRoute: () => LayoutRoute, +const UsersUserIdRoute = UsersUserIdRouteImport.update({ + id: '/$userId', + path: '/$userId', + getParentRoute: () => UsersRoute, } as any) -const TransitionCountQueryRoute = TransitionCountQueryRouteImport.update({ - id: '/transition/count/query', - path: '/transition/count/query', - getParentRoute: () => rootRouteImport, +const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ + id: '/layout-a', + path: '/layout-a', + getParentRoute: () => LayoutLayout2Route, } as any) -const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ - id: '/posts_/$postId/deep', - path: '/posts/$postId/deep', - getParentRoute: () => rootRouteImport, +const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ + id: '/layout-b', + path: '/layout-b', + getParentRoute: () => LayoutLayout2Route, } as any) const ApiUsersIdRoute = ApiUsersIdRouteImport.update({ id: '/$id', path: '/$id', getParentRoute: () => ApiUsersRoute, } as any) -const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ - id: '/layout-b', - path: '/layout-b', - getParentRoute: () => LayoutLayout2Route, +const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ + id: '/posts_/$postId/deep', + path: '/posts/$postId/deep', + getParentRoute: () => rootRouteImport, } as any) -const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ - id: '/layout-a', - path: '/layout-a', - getParentRoute: () => LayoutLayout2Route, +const TransitionCountQueryRoute = TransitionCountQueryRouteImport.update({ + id: '/transition/count/query', + path: '/transition/count/query', + getParentRoute: () => rootRouteImport, } as any) export interface FileRoutesByFullPath { @@ -231,18 +231,25 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/users': { - id: '/users' - path: '/users' - fullPath: '/users' - preLoaderRoute: typeof UsersRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/suspense-transition': { - id: '/suspense-transition' - path: '/suspense-transition' - fullPath: '/suspense-transition' - preLoaderRoute: typeof SuspenseTransitionRouteImport + '/_layout': { + id: '/_layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutRouteImport + parentRoute: typeof rootRouteImport + } + '/deferred': { + id: '/deferred' + path: '/deferred' + fullPath: '/deferred' + preLoaderRoute: typeof DeferredRouteImport parentRoute: typeof rootRouteImport } '/posts': { @@ -252,33 +259,33 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/deferred': { - id: '/deferred' - path: '/deferred' - fullPath: '/deferred' - preLoaderRoute: typeof DeferredRouteImport + '/suspense-transition': { + id: '/suspense-transition' + path: '/suspense-transition' + fullPath: '/suspense-transition' + preLoaderRoute: typeof SuspenseTransitionRouteImport parentRoute: typeof rootRouteImport } - '/_layout': { - id: '/_layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutRouteImport + '/users': { + id: '/users' + path: '/users' + fullPath: '/users' + preLoaderRoute: typeof UsersRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' + '/_layout/_layout-2': { + id: '/_layout/_layout-2' + path: '' fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport + preLoaderRoute: typeof LayoutLayout2RouteImport + parentRoute: typeof LayoutRoute } - '/users/': { - id: '/users/' - path: '/' - fullPath: '/users/' - preLoaderRoute: typeof UsersIndexRouteImport - parentRoute: typeof UsersRoute + '/api/users': { + id: '/api/users' + path: '/api/users' + fullPath: '/api/users' + preLoaderRoute: typeof ApiUsersRouteImport + parentRoute: typeof rootRouteImport } '/posts/': { id: '/posts/' @@ -287,13 +294,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRoute } - '/users/$userId': { - id: '/users/$userId' - path: '/$userId' - fullPath: '/users/$userId' - preLoaderRoute: typeof UsersUserIdRouteImport - parentRoute: typeof UsersRoute - } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' @@ -301,33 +301,33 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/api/users': { - id: '/api/users' - path: '/api/users' - fullPath: '/api/users' - preLoaderRoute: typeof ApiUsersRouteImport - parentRoute: typeof rootRouteImport + '/users/': { + id: '/users/' + path: '/' + fullPath: '/users/' + preLoaderRoute: typeof UsersIndexRouteImport + parentRoute: typeof UsersRoute } - '/_layout/_layout-2': { - id: '/_layout/_layout-2' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutLayout2RouteImport - parentRoute: typeof LayoutRoute + '/users/$userId': { + id: '/users/$userId' + path: '/$userId' + fullPath: '/users/$userId' + preLoaderRoute: typeof UsersUserIdRouteImport + parentRoute: typeof UsersRoute } - '/transition/count/query': { - id: '/transition/count/query' - path: '/transition/count/query' - fullPath: '/transition/count/query' - preLoaderRoute: typeof TransitionCountQueryRouteImport - parentRoute: typeof rootRouteImport + '/_layout/_layout-2/layout-a': { + id: '/_layout/_layout-2/layout-a' + path: '/layout-a' + fullPath: '/layout-a' + preLoaderRoute: typeof LayoutLayout2LayoutARouteImport + parentRoute: typeof LayoutLayout2Route } - '/posts_/$postId/deep': { - id: '/posts_/$postId/deep' - path: '/posts/$postId/deep' - fullPath: '/posts/$postId/deep' - preLoaderRoute: typeof PostsPostIdDeepRouteImport - parentRoute: typeof rootRouteImport + '/_layout/_layout-2/layout-b': { + id: '/_layout/_layout-2/layout-b' + path: '/layout-b' + fullPath: '/layout-b' + preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport + parentRoute: typeof LayoutLayout2Route } '/api/users/$id': { id: '/api/users/$id' @@ -336,19 +336,19 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof ApiUsersIdRouteImport parentRoute: typeof ApiUsersRoute } - '/_layout/_layout-2/layout-b': { - id: '/_layout/_layout-2/layout-b' - path: '/layout-b' - fullPath: '/layout-b' - preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport - parentRoute: typeof LayoutLayout2Route + '/posts_/$postId/deep': { + id: '/posts_/$postId/deep' + path: '/posts/$postId/deep' + fullPath: '/posts/$postId/deep' + preLoaderRoute: typeof PostsPostIdDeepRouteImport + parentRoute: typeof rootRouteImport } - '/_layout/_layout-2/layout-a': { - id: '/_layout/_layout-2/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: typeof LayoutLayout2LayoutARouteImport - parentRoute: typeof LayoutLayout2Route + '/transition/count/query': { + id: '/transition/count/query' + path: '/transition/count/query' + fullPath: '/transition/count/query' + preLoaderRoute: typeof TransitionCountQueryRouteImport + parentRoute: typeof rootRouteImport } } } diff --git a/e2e/solid-start/basic/src/routeTree.gen.ts b/e2e/solid-start/basic/src/routeTree.gen.ts index c9fba32156..dc8390f6b4 100644 --- a/e2e/solid-start/basic/src/routeTree.gen.ts +++ b/e2e/solid-start/basic/src/routeTree.gen.ts @@ -9,90 +9,89 @@ // 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 UsersRouteImport } from './routes/users' -import { Route as StreamRouteImport } from './routes/stream' -import { Route as ScriptsRouteImport } from './routes/scripts' -import { Route as RawStreamRouteImport } from './routes/raw-stream' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as LinksRouteImport } from './routes/links' -import { Route as InlineScriptsRouteImport } from './routes/inline-scripts' -import { Route as DeferredWithoutSuspenseRouteImport } from './routes/deferred-without-suspense' -import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as IndexRouteImport } from './routes/index' import { Route as LayoutRouteImport } from './routes/_layout' -import { Route as SpecialCharsRouteRouteImport } from './routes/specialChars/route' -import { Route as SearchParamsRouteRouteImport } from './routes/search-params/route' +import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as DeferredWithoutSuspenseRouteImport } from './routes/deferred-without-suspense' +import { Route as InlineScriptsRouteImport } from './routes/inline-scripts' +import { Route as LinksRouteImport } from './routes/links' import { Route as NotFoundRouteRouteImport } from './routes/not-found/route' -import { Route as IndexRouteImport } from './routes/index' -import { Route as UsersIndexRouteImport } from './routes/users.index' -import { Route as SearchParamsIndexRouteImport } from './routes/search-params/index' -import { Route as RedirectIndexRouteImport } from './routes/redirect/index' -import { Route as RawStreamIndexRouteImport } from './routes/raw-stream/index' -import { Route as PostsIndexRouteImport } from './routes/posts.index' -import { Route as NotFoundIndexRouteImport } from './routes/not-found/index' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as RawStreamRouteImport } from './routes/raw-stream' +import { Route as ScriptsRouteImport } from './routes/scripts' +import { Route as SearchParamsRouteRouteImport } from './routes/search-params/route' +import { Route as SpecialCharsRouteRouteImport } from './routes/specialChars/route' +import { Route as StreamRouteImport } from './routes/stream' +import { Route as UsersRouteImport } from './routes/users' +import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' +import { Route as ApiUsersRouteImport } from './routes/api/users' import { Route as MultiCookieRedirectIndexRouteImport } from './routes/multi-cookie-redirect/index' -import { Route as UsersUserIdRouteImport } from './routes/users.$userId' -import { Route as SpecialCharsChar45824Char54620Char48124Char44397RouteImport } from './routes/specialChars/대한민국' -import { Route as SpecialCharsSearchRouteImport } from './routes/specialChars/search' -import { Route as SpecialCharsHashRouteImport } from './routes/specialChars/hash' -import { Route as SpecialCharsParamRouteImport } from './routes/specialChars/$param' -import { Route as SearchParamsLoaderThrowsRedirectRouteImport } from './routes/search-params/loader-throws-redirect' -import { Route as SearchParamsDefaultRouteImport } from './routes/search-params/default' -import { Route as RedirectTargetRouteImport } from './routes/redirect/$target' -import { Route as RawStreamSsrTextHintRouteImport } from './routes/raw-stream/ssr-text-hint' -import { Route as RawStreamSsrSingleRouteImport } from './routes/raw-stream/ssr-single' -import { Route as RawStreamSsrMultipleRouteImport } from './routes/raw-stream/ssr-multiple' -import { Route as RawStreamSsrMixedRouteImport } from './routes/raw-stream/ssr-mixed' -import { Route as RawStreamSsrBinaryHintRouteImport } from './routes/raw-stream/ssr-binary-hint' -import { Route as RawStreamClientCallRouteImport } from './routes/raw-stream/client-call' -import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as NotFoundViaLoaderRouteImport } from './routes/not-found/via-loader' -import { Route as NotFoundViaBeforeLoadTargetRootRouteImport } from './routes/not-found/via-beforeLoad-target-root' -import { Route as NotFoundViaBeforeLoadRouteImport } from './routes/not-found/via-beforeLoad' import { Route as MultiCookieRedirectTargetRouteImport } from './routes/multi-cookie-redirect/target' -import { Route as ApiUsersRouteImport } from './routes/api/users' -import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' -import { Route as SpecialCharsMalformedRouteRouteImport } from './routes/specialChars/malformed/route' +import { Route as NotFoundIndexRouteImport } from './routes/not-found/index' import { Route as NotFoundParentBoundaryRouteRouteImport } from './routes/not-found/parent-boundary/route' +import { Route as NotFoundViaBeforeLoadRouteImport } from './routes/not-found/via-beforeLoad' +import { Route as NotFoundViaBeforeLoadTargetRootRouteImport } from './routes/not-found/via-beforeLoad-target-root' +import { Route as NotFoundViaLoaderRouteImport } from './routes/not-found/via-loader' +import { Route as PostsIndexRouteImport } from './routes/posts.index' +import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' +import { Route as RawStreamIndexRouteImport } from './routes/raw-stream/index' +import { Route as RawStreamClientCallRouteImport } from './routes/raw-stream/client-call' +import { Route as RawStreamSsrBinaryHintRouteImport } from './routes/raw-stream/ssr-binary-hint' +import { Route as RawStreamSsrMixedRouteImport } from './routes/raw-stream/ssr-mixed' +import { Route as RawStreamSsrMultipleRouteImport } from './routes/raw-stream/ssr-multiple' +import { Route as RawStreamSsrSingleRouteImport } from './routes/raw-stream/ssr-single' +import { Route as RawStreamSsrTextHintRouteImport } from './routes/raw-stream/ssr-text-hint' +import { Route as RedirectIndexRouteImport } from './routes/redirect/index' +import { Route as RedirectTargetRouteImport } from './routes/redirect/$target' +import { Route as SearchParamsIndexRouteImport } from './routes/search-params/index' +import { Route as SearchParamsDefaultRouteImport } from './routes/search-params/default' +import { Route as SearchParamsLoaderThrowsRedirectRouteImport } from './routes/search-params/loader-throws-redirect' +import { Route as SpecialCharsParamRouteImport } from './routes/specialChars/$param' +import { Route as SpecialCharsHashRouteImport } from './routes/specialChars/hash' +import { Route as SpecialCharsMalformedRouteRouteImport } from './routes/specialChars/malformed/route' +import { Route as SpecialCharsSearchRouteImport } from './routes/specialChars/search' +import { Route as SpecialCharsChar45824Char54620Char48124Char44397RouteImport } from './routes/specialChars/대한민국' +import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as UsersUserIdRouteImport } from './routes/users.$userId' +import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' +import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' +import { Route as ApiUsersUserIdRouteImport } from './routes/api/users.$userId' +import { Route as NotFoundParentBoundaryViaBeforeLoadRouteImport } from './routes/not-found/parent-boundary/via-beforeLoad' +import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' import { Route as RedirectTargetIndexRouteImport } from './routes/redirect/$target/index' -import { Route as TransitionTypingCreateResourceRouteImport } from './routes/transition/typing/create-resource' -import { Route as TransitionCountCreateResourceRouteImport } from './routes/transition/count/create-resource' -import { Route as SpecialCharsMalformedSearchRouteImport } from './routes/specialChars/malformed/search' -import { Route as SpecialCharsMalformedParamRouteImport } from './routes/specialChars/malformed/$param' -import { Route as RedirectTargetViaLoaderRouteImport } from './routes/redirect/$target/via-loader' import { Route as RedirectTargetViaBeforeLoadRouteImport } from './routes/redirect/$target/via-beforeLoad' -import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -import { Route as NotFoundParentBoundaryViaBeforeLoadRouteImport } from './routes/not-found/parent-boundary/via-beforeLoad' -import { Route as ApiUsersUserIdRouteImport } from './routes/api/users.$userId' -import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' -import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' +import { Route as RedirectTargetViaLoaderRouteImport } from './routes/redirect/$target/via-loader' +import { Route as SpecialCharsMalformedParamRouteImport } from './routes/specialChars/malformed/$param' +import { Route as SpecialCharsMalformedSearchRouteImport } from './routes/specialChars/malformed/search' +import { Route as TransitionCountCreateResourceRouteImport } from './routes/transition/count/create-resource' +import { Route as TransitionTypingCreateResourceRouteImport } from './routes/transition/typing/create-resource' import { Route as RedirectTargetServerFnIndexRouteImport } from './routes/redirect/$target/serverFn/index' -import { Route as RedirectTargetServerFnViaUseServerFnRouteImport } from './routes/redirect/$target/serverFn/via-useServerFn' -import { Route as RedirectTargetServerFnViaLoaderRouteImport } from './routes/redirect/$target/serverFn/via-loader' import { Route as RedirectTargetServerFnViaBeforeLoadRouteImport } from './routes/redirect/$target/serverFn/via-beforeLoad' +import { Route as RedirectTargetServerFnViaLoaderRouteImport } from './routes/redirect/$target/serverFn/via-loader' +import { Route as RedirectTargetServerFnViaUseServerFnRouteImport } from './routes/redirect/$target/serverFn/via-useServerFn' -const UsersRoute = UsersRouteImport.update({ - id: '/users', - path: '/users', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const StreamRoute = StreamRouteImport.update({ - id: '/stream', - path: '/stream', +const LayoutRoute = LayoutRouteImport.update({ + id: '/_layout', getParentRoute: () => rootRouteImport, } as any) -const ScriptsRoute = ScriptsRouteImport.update({ - id: '/scripts', - path: '/scripts', +const DeferredRoute = DeferredRouteImport.update({ + id: '/deferred', + path: '/deferred', getParentRoute: () => rootRouteImport, } as any) -const RawStreamRoute = RawStreamRouteImport.update({ - id: '/raw-stream', - path: '/raw-stream', +const DeferredWithoutSuspenseRoute = DeferredWithoutSuspenseRouteImport.update({ + id: '/deferred-without-suspense', + path: '/deferred-without-suspense', getParentRoute: () => rootRouteImport, } as any) -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const InlineScriptsRoute = InlineScriptsRouteImport.update({ + id: '/inline-scripts', + path: '/inline-scripts', getParentRoute: () => rootRouteImport, } as any) const LinksRoute = LinksRouteImport.update({ @@ -100,28 +99,24 @@ const LinksRoute = LinksRouteImport.update({ path: '/links', getParentRoute: () => rootRouteImport, } as any) -const InlineScriptsRoute = InlineScriptsRouteImport.update({ - id: '/inline-scripts', - path: '/inline-scripts', - getParentRoute: () => rootRouteImport, -} as any) -const DeferredWithoutSuspenseRoute = DeferredWithoutSuspenseRouteImport.update({ - id: '/deferred-without-suspense', - path: '/deferred-without-suspense', +const NotFoundRouteRoute = NotFoundRouteRouteImport.update({ + id: '/not-found', + path: '/not-found', getParentRoute: () => rootRouteImport, } as any) -const DeferredRoute = DeferredRouteImport.update({ - id: '/deferred', - path: '/deferred', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const LayoutRoute = LayoutRouteImport.update({ - id: '/_layout', +const RawStreamRoute = RawStreamRouteImport.update({ + id: '/raw-stream', + path: '/raw-stream', getParentRoute: () => rootRouteImport, } as any) -const SpecialCharsRouteRoute = SpecialCharsRouteRouteImport.update({ - id: '/specialChars', - path: '/specialChars', +const ScriptsRoute = ScriptsRouteImport.update({ + id: '/scripts', + path: '/scripts', getParentRoute: () => rootRouteImport, } as any) const SearchParamsRouteRoute = SearchParamsRouteRouteImport.update({ @@ -129,102 +124,97 @@ const SearchParamsRouteRoute = SearchParamsRouteRouteImport.update({ path: '/search-params', getParentRoute: () => rootRouteImport, } as any) -const NotFoundRouteRoute = NotFoundRouteRouteImport.update({ - id: '/not-found', - path: '/not-found', +const SpecialCharsRouteRoute = SpecialCharsRouteRouteImport.update({ + id: '/specialChars', + path: '/specialChars', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const StreamRoute = StreamRouteImport.update({ + id: '/stream', + path: '/stream', getParentRoute: () => rootRouteImport, } as any) -const UsersIndexRoute = UsersIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => UsersRoute, +const UsersRoute = UsersRouteImport.update({ + id: '/users', + path: '/users', + getParentRoute: () => rootRouteImport, } as any) -const SearchParamsIndexRoute = SearchParamsIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => SearchParamsRouteRoute, +const LayoutLayout2Route = LayoutLayout2RouteImport.update({ + id: '/_layout-2', + getParentRoute: () => LayoutRoute, } as any) -const RedirectIndexRoute = RedirectIndexRouteImport.update({ - id: '/redirect/', - path: '/redirect/', +const ApiUsersRoute = ApiUsersRouteImport.update({ + id: '/api/users', + path: '/api/users', getParentRoute: () => rootRouteImport, } as any) -const RawStreamIndexRoute = RawStreamIndexRouteImport.update({ +const MultiCookieRedirectIndexRoute = + MultiCookieRedirectIndexRouteImport.update({ + id: '/multi-cookie-redirect/', + path: '/multi-cookie-redirect/', + getParentRoute: () => rootRouteImport, + } as any) +const MultiCookieRedirectTargetRoute = + MultiCookieRedirectTargetRouteImport.update({ + id: '/multi-cookie-redirect/target', + path: '/multi-cookie-redirect/target', + getParentRoute: () => rootRouteImport, + } as any) +const NotFoundIndexRoute = NotFoundIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => RawStreamRoute, + getParentRoute: () => NotFoundRouteRoute, +} as any) +const NotFoundParentBoundaryRouteRoute = + NotFoundParentBoundaryRouteRouteImport.update({ + id: '/parent-boundary', + path: '/parent-boundary', + getParentRoute: () => NotFoundRouteRoute, + } as any) +const NotFoundViaBeforeLoadRoute = NotFoundViaBeforeLoadRouteImport.update({ + id: '/via-beforeLoad', + path: '/via-beforeLoad', + getParentRoute: () => NotFoundRouteRoute, +} as any) +const NotFoundViaBeforeLoadTargetRootRoute = + NotFoundViaBeforeLoadTargetRootRouteImport.update({ + id: '/via-beforeLoad-target-root', + path: '/via-beforeLoad-target-root', + getParentRoute: () => NotFoundRouteRoute, + } as any) +const NotFoundViaLoaderRoute = NotFoundViaLoaderRouteImport.update({ + id: '/via-loader', + path: '/via-loader', + getParentRoute: () => NotFoundRouteRoute, } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRoute, } as any) -const NotFoundIndexRoute = NotFoundIndexRouteImport.update({ +const PostsPostIdRoute = PostsPostIdRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => PostsRoute, +} as any) +const RawStreamIndexRoute = RawStreamIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => NotFoundRouteRoute, + getParentRoute: () => RawStreamRoute, } as any) -const MultiCookieRedirectIndexRoute = - MultiCookieRedirectIndexRouteImport.update({ - id: '/multi-cookie-redirect/', - path: '/multi-cookie-redirect/', - getParentRoute: () => rootRouteImport, - } as any) -const UsersUserIdRoute = UsersUserIdRouteImport.update({ - id: '/$userId', - path: '/$userId', - getParentRoute: () => UsersRoute, +const RawStreamClientCallRoute = RawStreamClientCallRouteImport.update({ + id: '/client-call', + path: '/client-call', + getParentRoute: () => RawStreamRoute, } as any) -const SpecialCharsChar45824Char54620Char48124Char44397Route = - SpecialCharsChar45824Char54620Char48124Char44397RouteImport.update({ - id: '/대한민국', - path: '/대한민국', - getParentRoute: () => SpecialCharsRouteRoute, - } as any) -const SpecialCharsSearchRoute = SpecialCharsSearchRouteImport.update({ - id: '/search', - path: '/search', - getParentRoute: () => SpecialCharsRouteRoute, -} as any) -const SpecialCharsHashRoute = SpecialCharsHashRouteImport.update({ - id: '/hash', - path: '/hash', - getParentRoute: () => SpecialCharsRouteRoute, -} as any) -const SpecialCharsParamRoute = SpecialCharsParamRouteImport.update({ - id: '/$param', - path: '/$param', - getParentRoute: () => SpecialCharsRouteRoute, -} as any) -const SearchParamsLoaderThrowsRedirectRoute = - SearchParamsLoaderThrowsRedirectRouteImport.update({ - id: '/loader-throws-redirect', - path: '/loader-throws-redirect', - getParentRoute: () => SearchParamsRouteRoute, - } as any) -const SearchParamsDefaultRoute = SearchParamsDefaultRouteImport.update({ - id: '/default', - path: '/default', - getParentRoute: () => SearchParamsRouteRoute, -} as any) -const RedirectTargetRoute = RedirectTargetRouteImport.update({ - id: '/redirect/$target', - path: '/redirect/$target', - getParentRoute: () => rootRouteImport, -} as any) -const RawStreamSsrTextHintRoute = RawStreamSsrTextHintRouteImport.update({ - id: '/ssr-text-hint', - path: '/ssr-text-hint', +const RawStreamSsrBinaryHintRoute = RawStreamSsrBinaryHintRouteImport.update({ + id: '/ssr-binary-hint', + path: '/ssr-binary-hint', getParentRoute: () => RawStreamRoute, } as any) -const RawStreamSsrSingleRoute = RawStreamSsrSingleRouteImport.update({ - id: '/ssr-single', - path: '/ssr-single', +const RawStreamSsrMixedRoute = RawStreamSsrMixedRouteImport.update({ + id: '/ssr-mixed', + path: '/ssr-mixed', getParentRoute: () => RawStreamRoute, } as any) const RawStreamSsrMultipleRoute = RawStreamSsrMultipleRouteImport.update({ @@ -232,56 +222,51 @@ const RawStreamSsrMultipleRoute = RawStreamSsrMultipleRouteImport.update({ path: '/ssr-multiple', getParentRoute: () => RawStreamRoute, } as any) -const RawStreamSsrMixedRoute = RawStreamSsrMixedRouteImport.update({ - id: '/ssr-mixed', - path: '/ssr-mixed', +const RawStreamSsrSingleRoute = RawStreamSsrSingleRouteImport.update({ + id: '/ssr-single', + path: '/ssr-single', getParentRoute: () => RawStreamRoute, } as any) -const RawStreamSsrBinaryHintRoute = RawStreamSsrBinaryHintRouteImport.update({ - id: '/ssr-binary-hint', - path: '/ssr-binary-hint', +const RawStreamSsrTextHintRoute = RawStreamSsrTextHintRouteImport.update({ + id: '/ssr-text-hint', + path: '/ssr-text-hint', getParentRoute: () => RawStreamRoute, } as any) -const RawStreamClientCallRoute = RawStreamClientCallRouteImport.update({ - id: '/client-call', - path: '/client-call', - getParentRoute: () => RawStreamRoute, +const RedirectIndexRoute = RedirectIndexRouteImport.update({ + id: '/redirect/', + path: '/redirect/', + getParentRoute: () => rootRouteImport, } as any) -const PostsPostIdRoute = PostsPostIdRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => PostsRoute, +const RedirectTargetRoute = RedirectTargetRouteImport.update({ + id: '/redirect/$target', + path: '/redirect/$target', + getParentRoute: () => rootRouteImport, } as any) -const NotFoundViaLoaderRoute = NotFoundViaLoaderRouteImport.update({ - id: '/via-loader', - path: '/via-loader', - getParentRoute: () => NotFoundRouteRoute, +const SearchParamsIndexRoute = SearchParamsIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => SearchParamsRouteRoute, } as any) -const NotFoundViaBeforeLoadTargetRootRoute = - NotFoundViaBeforeLoadTargetRootRouteImport.update({ - id: '/via-beforeLoad-target-root', - path: '/via-beforeLoad-target-root', - getParentRoute: () => NotFoundRouteRoute, - } as any) -const NotFoundViaBeforeLoadRoute = NotFoundViaBeforeLoadRouteImport.update({ - id: '/via-beforeLoad', - path: '/via-beforeLoad', - getParentRoute: () => NotFoundRouteRoute, +const SearchParamsDefaultRoute = SearchParamsDefaultRouteImport.update({ + id: '/default', + path: '/default', + getParentRoute: () => SearchParamsRouteRoute, } as any) -const MultiCookieRedirectTargetRoute = - MultiCookieRedirectTargetRouteImport.update({ - id: '/multi-cookie-redirect/target', - path: '/multi-cookie-redirect/target', - getParentRoute: () => rootRouteImport, +const SearchParamsLoaderThrowsRedirectRoute = + SearchParamsLoaderThrowsRedirectRouteImport.update({ + id: '/loader-throws-redirect', + path: '/loader-throws-redirect', + getParentRoute: () => SearchParamsRouteRoute, } as any) -const ApiUsersRoute = ApiUsersRouteImport.update({ - id: '/api/users', - path: '/api/users', - getParentRoute: () => rootRouteImport, +const SpecialCharsParamRoute = SpecialCharsParamRouteImport.update({ + id: '/$param', + path: '/$param', + getParentRoute: () => SpecialCharsRouteRoute, } as any) -const LayoutLayout2Route = LayoutLayout2RouteImport.update({ - id: '/_layout-2', - getParentRoute: () => LayoutRoute, +const SpecialCharsHashRoute = SpecialCharsHashRouteImport.update({ + id: '/hash', + path: '/hash', + getParentRoute: () => SpecialCharsRouteRoute, } as any) const SpecialCharsMalformedRouteRoute = SpecialCharsMalformedRouteRouteImport.update({ @@ -289,88 +274,103 @@ const SpecialCharsMalformedRouteRoute = path: '/malformed', getParentRoute: () => SpecialCharsRouteRoute, } as any) -const NotFoundParentBoundaryRouteRoute = - NotFoundParentBoundaryRouteRouteImport.update({ - id: '/parent-boundary', - path: '/parent-boundary', - getParentRoute: () => NotFoundRouteRoute, +const SpecialCharsSearchRoute = SpecialCharsSearchRouteImport.update({ + id: '/search', + path: '/search', + getParentRoute: () => SpecialCharsRouteRoute, +} as any) +const SpecialCharsChar45824Char54620Char48124Char44397Route = + SpecialCharsChar45824Char54620Char48124Char44397RouteImport.update({ + id: '/대한민국', + path: '/대한민국', + getParentRoute: () => SpecialCharsRouteRoute, } as any) -const RedirectTargetIndexRoute = RedirectTargetIndexRouteImport.update({ +const UsersIndexRoute = UsersIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => RedirectTargetRoute, + getParentRoute: () => UsersRoute, } as any) -const TransitionTypingCreateResourceRoute = - TransitionTypingCreateResourceRouteImport.update({ - id: '/transition/typing/create-resource', - path: '/transition/typing/create-resource', - getParentRoute: () => rootRouteImport, - } as any) -const TransitionCountCreateResourceRoute = - TransitionCountCreateResourceRouteImport.update({ - id: '/transition/count/create-resource', - path: '/transition/count/create-resource', - getParentRoute: () => rootRouteImport, - } as any) -const SpecialCharsMalformedSearchRoute = - SpecialCharsMalformedSearchRouteImport.update({ - id: '/search', - path: '/search', - getParentRoute: () => SpecialCharsMalformedRouteRoute, - } as any) -const SpecialCharsMalformedParamRoute = - SpecialCharsMalformedParamRouteImport.update({ - id: '/$param', - path: '/$param', - getParentRoute: () => SpecialCharsMalformedRouteRoute, - } as any) -const RedirectTargetViaLoaderRoute = RedirectTargetViaLoaderRouteImport.update({ - id: '/via-loader', - path: '/via-loader', - getParentRoute: () => RedirectTargetRoute, +const UsersUserIdRoute = UsersUserIdRouteImport.update({ + id: '/$userId', + path: '/$userId', + getParentRoute: () => UsersRoute, } as any) -const RedirectTargetViaBeforeLoadRoute = - RedirectTargetViaBeforeLoadRouteImport.update({ +const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ + id: '/layout-a', + path: '/layout-a', + getParentRoute: () => LayoutLayout2Route, +} as any) +const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ + id: '/layout-b', + path: '/layout-b', + getParentRoute: () => LayoutLayout2Route, +} as any) +const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ + id: '/$userId', + path: '/$userId', + getParentRoute: () => ApiUsersRoute, +} as any) +const NotFoundParentBoundaryViaBeforeLoadRoute = + NotFoundParentBoundaryViaBeforeLoadRouteImport.update({ id: '/via-beforeLoad', path: '/via-beforeLoad', - getParentRoute: () => RedirectTargetRoute, + getParentRoute: () => NotFoundParentBoundaryRouteRoute, } as any) const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ id: '/posts_/$postId/deep', path: '/posts/$postId/deep', getParentRoute: () => rootRouteImport, } as any) -const NotFoundParentBoundaryViaBeforeLoadRoute = - NotFoundParentBoundaryViaBeforeLoadRouteImport.update({ +const RedirectTargetIndexRoute = RedirectTargetIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => RedirectTargetRoute, +} as any) +const RedirectTargetViaBeforeLoadRoute = + RedirectTargetViaBeforeLoadRouteImport.update({ id: '/via-beforeLoad', path: '/via-beforeLoad', - getParentRoute: () => NotFoundParentBoundaryRouteRoute, + getParentRoute: () => RedirectTargetRoute, } as any) -const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ - id: '/$userId', - path: '/$userId', - getParentRoute: () => ApiUsersRoute, -} as any) -const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ - id: '/layout-b', - path: '/layout-b', - getParentRoute: () => LayoutLayout2Route, -} as any) -const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ - id: '/layout-a', - path: '/layout-a', - getParentRoute: () => LayoutLayout2Route, +const RedirectTargetViaLoaderRoute = RedirectTargetViaLoaderRouteImport.update({ + id: '/via-loader', + path: '/via-loader', + getParentRoute: () => RedirectTargetRoute, } as any) +const SpecialCharsMalformedParamRoute = + SpecialCharsMalformedParamRouteImport.update({ + id: '/$param', + path: '/$param', + getParentRoute: () => SpecialCharsMalformedRouteRoute, + } as any) +const SpecialCharsMalformedSearchRoute = + SpecialCharsMalformedSearchRouteImport.update({ + id: '/search', + path: '/search', + getParentRoute: () => SpecialCharsMalformedRouteRoute, + } as any) +const TransitionCountCreateResourceRoute = + TransitionCountCreateResourceRouteImport.update({ + id: '/transition/count/create-resource', + path: '/transition/count/create-resource', + getParentRoute: () => rootRouteImport, + } as any) +const TransitionTypingCreateResourceRoute = + TransitionTypingCreateResourceRouteImport.update({ + id: '/transition/typing/create-resource', + path: '/transition/typing/create-resource', + getParentRoute: () => rootRouteImport, + } as any) const RedirectTargetServerFnIndexRoute = RedirectTargetServerFnIndexRouteImport.update({ id: '/serverFn/', path: '/serverFn/', getParentRoute: () => RedirectTargetRoute, } as any) -const RedirectTargetServerFnViaUseServerFnRoute = - RedirectTargetServerFnViaUseServerFnRouteImport.update({ - id: '/serverFn/via-useServerFn', - path: '/serverFn/via-useServerFn', +const RedirectTargetServerFnViaBeforeLoadRoute = + RedirectTargetServerFnViaBeforeLoadRouteImport.update({ + id: '/serverFn/via-beforeLoad', + path: '/serverFn/via-beforeLoad', getParentRoute: () => RedirectTargetRoute, } as any) const RedirectTargetServerFnViaLoaderRoute = @@ -379,10 +379,10 @@ const RedirectTargetServerFnViaLoaderRoute = path: '/serverFn/via-loader', getParentRoute: () => RedirectTargetRoute, } as any) -const RedirectTargetServerFnViaBeforeLoadRoute = - RedirectTargetServerFnViaBeforeLoadRouteImport.update({ - id: '/serverFn/via-beforeLoad', - path: '/serverFn/via-beforeLoad', +const RedirectTargetServerFnViaUseServerFnRoute = + RedirectTargetServerFnViaUseServerFnRouteImport.update({ + id: '/serverFn/via-useServerFn', + path: '/serverFn/via-useServerFn', getParentRoute: () => RedirectTargetRoute, } as any) @@ -769,39 +769,39 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/users': { - id: '/users' - path: '/users' - fullPath: '/users' - preLoaderRoute: typeof UsersRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/stream': { - id: '/stream' - path: '/stream' - fullPath: '/stream' - preLoaderRoute: typeof StreamRouteImport + '/_layout': { + id: '/_layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } - '/scripts': { - id: '/scripts' - path: '/scripts' - fullPath: '/scripts' - preLoaderRoute: typeof ScriptsRouteImport + '/deferred': { + id: '/deferred' + path: '/deferred' + fullPath: '/deferred' + preLoaderRoute: typeof DeferredRouteImport parentRoute: typeof rootRouteImport } - '/raw-stream': { - id: '/raw-stream' - path: '/raw-stream' - fullPath: '/raw-stream' - preLoaderRoute: typeof RawStreamRouteImport + '/deferred-without-suspense': { + id: '/deferred-without-suspense' + path: '/deferred-without-suspense' + fullPath: '/deferred-without-suspense' + preLoaderRoute: typeof DeferredWithoutSuspenseRouteImport parentRoute: typeof rootRouteImport } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/inline-scripts': { + id: '/inline-scripts' + path: '/inline-scripts' + fullPath: '/inline-scripts' + preLoaderRoute: typeof InlineScriptsRouteImport parentRoute: typeof rootRouteImport } '/links': { @@ -811,39 +811,32 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof LinksRouteImport parentRoute: typeof rootRouteImport } - '/inline-scripts': { - id: '/inline-scripts' - path: '/inline-scripts' - fullPath: '/inline-scripts' - preLoaderRoute: typeof InlineScriptsRouteImport - parentRoute: typeof rootRouteImport - } - '/deferred-without-suspense': { - id: '/deferred-without-suspense' - path: '/deferred-without-suspense' - fullPath: '/deferred-without-suspense' - preLoaderRoute: typeof DeferredWithoutSuspenseRouteImport + '/not-found': { + id: '/not-found' + path: '/not-found' + fullPath: '/not-found' + preLoaderRoute: typeof NotFoundRouteRouteImport parentRoute: typeof rootRouteImport } - '/deferred': { - id: '/deferred' - path: '/deferred' - fullPath: '/deferred' - preLoaderRoute: typeof DeferredRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/_layout': { - id: '/_layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutRouteImport + '/raw-stream': { + id: '/raw-stream' + path: '/raw-stream' + fullPath: '/raw-stream' + preLoaderRoute: typeof RawStreamRouteImport parentRoute: typeof rootRouteImport } - '/specialChars': { - id: '/specialChars' - path: '/specialChars' - fullPath: '/specialChars' - preLoaderRoute: typeof SpecialCharsRouteRouteImport + '/scripts': { + id: '/scripts' + path: '/scripts' + fullPath: '/scripts' + preLoaderRoute: typeof ScriptsRouteImport parentRoute: typeof rootRouteImport } '/search-params': { @@ -853,54 +846,54 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof SearchParamsRouteRouteImport parentRoute: typeof rootRouteImport } - '/not-found': { - id: '/not-found' - path: '/not-found' - fullPath: '/not-found' - preLoaderRoute: typeof NotFoundRouteRouteImport + '/specialChars': { + id: '/specialChars' + path: '/specialChars' + fullPath: '/specialChars' + preLoaderRoute: typeof SpecialCharsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/stream': { + id: '/stream' + path: '/stream' + fullPath: '/stream' + preLoaderRoute: typeof StreamRouteImport parentRoute: typeof rootRouteImport } - '/users/': { - id: '/users/' - path: '/' - fullPath: '/users/' - preLoaderRoute: typeof UsersIndexRouteImport - parentRoute: typeof UsersRoute + '/users': { + id: '/users' + path: '/users' + fullPath: '/users' + preLoaderRoute: typeof UsersRouteImport + parentRoute: typeof rootRouteImport } - '/search-params/': { - id: '/search-params/' - path: '/' - fullPath: '/search-params/' - preLoaderRoute: typeof SearchParamsIndexRouteImport - parentRoute: typeof SearchParamsRouteRoute + '/_layout/_layout-2': { + id: '/_layout/_layout-2' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutLayout2RouteImport + parentRoute: typeof LayoutRoute } - '/redirect/': { - id: '/redirect/' - path: '/redirect' - fullPath: '/redirect/' - preLoaderRoute: typeof RedirectIndexRouteImport + '/api/users': { + id: '/api/users' + path: '/api/users' + fullPath: '/api/users' + preLoaderRoute: typeof ApiUsersRouteImport parentRoute: typeof rootRouteImport } - '/raw-stream/': { - id: '/raw-stream/' - path: '/' - fullPath: '/raw-stream/' - preLoaderRoute: typeof RawStreamIndexRouteImport - parentRoute: typeof RawStreamRoute + '/multi-cookie-redirect/': { + id: '/multi-cookie-redirect/' + path: '/multi-cookie-redirect' + fullPath: '/multi-cookie-redirect/' + preLoaderRoute: typeof MultiCookieRedirectIndexRouteImport + parentRoute: typeof rootRouteImport } - '/posts/': { - id: '/posts/' - path: '/' - fullPath: '/posts/' - preLoaderRoute: typeof PostsIndexRouteImport - parentRoute: typeof PostsRoute + '/multi-cookie-redirect/target': { + id: '/multi-cookie-redirect/target' + path: '/multi-cookie-redirect/target' + fullPath: '/multi-cookie-redirect/target' + preLoaderRoute: typeof MultiCookieRedirectTargetRouteImport + parentRoute: typeof rootRouteImport } '/not-found/': { id: '/not-found/' @@ -909,88 +902,67 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof NotFoundIndexRouteImport parentRoute: typeof NotFoundRouteRoute } - '/multi-cookie-redirect/': { - id: '/multi-cookie-redirect/' - path: '/multi-cookie-redirect' - fullPath: '/multi-cookie-redirect/' - preLoaderRoute: typeof MultiCookieRedirectIndexRouteImport - parentRoute: typeof rootRouteImport + '/not-found/parent-boundary': { + id: '/not-found/parent-boundary' + path: '/parent-boundary' + fullPath: '/not-found/parent-boundary' + preLoaderRoute: typeof NotFoundParentBoundaryRouteRouteImport + parentRoute: typeof NotFoundRouteRoute } - '/users/$userId': { - id: '/users/$userId' - path: '/$userId' - fullPath: '/users/$userId' - preLoaderRoute: typeof UsersUserIdRouteImport - parentRoute: typeof UsersRoute + '/not-found/via-beforeLoad': { + id: '/not-found/via-beforeLoad' + path: '/via-beforeLoad' + fullPath: '/not-found/via-beforeLoad' + preLoaderRoute: typeof NotFoundViaBeforeLoadRouteImport + parentRoute: typeof NotFoundRouteRoute } - '/specialChars/대한민국': { - id: '/specialChars/대한민국' - path: '/대한민국' - fullPath: '/specialChars/대한민국' - preLoaderRoute: typeof SpecialCharsChar45824Char54620Char48124Char44397RouteImport - parentRoute: typeof SpecialCharsRouteRoute + '/not-found/via-beforeLoad-target-root': { + id: '/not-found/via-beforeLoad-target-root' + path: '/via-beforeLoad-target-root' + fullPath: '/not-found/via-beforeLoad-target-root' + preLoaderRoute: typeof NotFoundViaBeforeLoadTargetRootRouteImport + parentRoute: typeof NotFoundRouteRoute } - '/specialChars/search': { - id: '/specialChars/search' - path: '/search' - fullPath: '/specialChars/search' - preLoaderRoute: typeof SpecialCharsSearchRouteImport - parentRoute: typeof SpecialCharsRouteRoute + '/not-found/via-loader': { + id: '/not-found/via-loader' + path: '/via-loader' + fullPath: '/not-found/via-loader' + preLoaderRoute: typeof NotFoundViaLoaderRouteImport + parentRoute: typeof NotFoundRouteRoute } - '/specialChars/hash': { - id: '/specialChars/hash' - path: '/hash' - fullPath: '/specialChars/hash' - preLoaderRoute: typeof SpecialCharsHashRouteImport - parentRoute: typeof SpecialCharsRouteRoute + '/posts/': { + id: '/posts/' + path: '/' + fullPath: '/posts/' + preLoaderRoute: typeof PostsIndexRouteImport + parentRoute: typeof PostsRoute } - '/specialChars/$param': { - id: '/specialChars/$param' - path: '/$param' - fullPath: '/specialChars/$param' - preLoaderRoute: typeof SpecialCharsParamRouteImport - parentRoute: typeof SpecialCharsRouteRoute + '/posts/$postId': { + id: '/posts/$postId' + path: '/$postId' + fullPath: '/posts/$postId' + preLoaderRoute: typeof PostsPostIdRouteImport + parentRoute: typeof PostsRoute } - '/search-params/loader-throws-redirect': { - id: '/search-params/loader-throws-redirect' - path: '/loader-throws-redirect' - fullPath: '/search-params/loader-throws-redirect' - preLoaderRoute: typeof SearchParamsLoaderThrowsRedirectRouteImport - parentRoute: typeof SearchParamsRouteRoute - } - '/search-params/default': { - id: '/search-params/default' - path: '/default' - fullPath: '/search-params/default' - preLoaderRoute: typeof SearchParamsDefaultRouteImport - parentRoute: typeof SearchParamsRouteRoute - } - '/redirect/$target': { - id: '/redirect/$target' - path: '/redirect/$target' - fullPath: '/redirect/$target' - preLoaderRoute: typeof RedirectTargetRouteImport - parentRoute: typeof rootRouteImport - } - '/raw-stream/ssr-text-hint': { - id: '/raw-stream/ssr-text-hint' - path: '/ssr-text-hint' - fullPath: '/raw-stream/ssr-text-hint' - preLoaderRoute: typeof RawStreamSsrTextHintRouteImport + '/raw-stream/': { + id: '/raw-stream/' + path: '/' + fullPath: '/raw-stream/' + preLoaderRoute: typeof RawStreamIndexRouteImport parentRoute: typeof RawStreamRoute } - '/raw-stream/ssr-single': { - id: '/raw-stream/ssr-single' - path: '/ssr-single' - fullPath: '/raw-stream/ssr-single' - preLoaderRoute: typeof RawStreamSsrSingleRouteImport + '/raw-stream/client-call': { + id: '/raw-stream/client-call' + path: '/client-call' + fullPath: '/raw-stream/client-call' + preLoaderRoute: typeof RawStreamClientCallRouteImport parentRoute: typeof RawStreamRoute } - '/raw-stream/ssr-multiple': { - id: '/raw-stream/ssr-multiple' - path: '/ssr-multiple' - fullPath: '/raw-stream/ssr-multiple' - preLoaderRoute: typeof RawStreamSsrMultipleRouteImport + '/raw-stream/ssr-binary-hint': { + id: '/raw-stream/ssr-binary-hint' + path: '/ssr-binary-hint' + fullPath: '/raw-stream/ssr-binary-hint' + preLoaderRoute: typeof RawStreamSsrBinaryHintRouteImport parentRoute: typeof RawStreamRoute } '/raw-stream/ssr-mixed': { @@ -1000,68 +972,75 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof RawStreamSsrMixedRouteImport parentRoute: typeof RawStreamRoute } - '/raw-stream/ssr-binary-hint': { - id: '/raw-stream/ssr-binary-hint' - path: '/ssr-binary-hint' - fullPath: '/raw-stream/ssr-binary-hint' - preLoaderRoute: typeof RawStreamSsrBinaryHintRouteImport + '/raw-stream/ssr-multiple': { + id: '/raw-stream/ssr-multiple' + path: '/ssr-multiple' + fullPath: '/raw-stream/ssr-multiple' + preLoaderRoute: typeof RawStreamSsrMultipleRouteImport parentRoute: typeof RawStreamRoute } - '/raw-stream/client-call': { - id: '/raw-stream/client-call' - path: '/client-call' - fullPath: '/raw-stream/client-call' - preLoaderRoute: typeof RawStreamClientCallRouteImport + '/raw-stream/ssr-single': { + id: '/raw-stream/ssr-single' + path: '/ssr-single' + fullPath: '/raw-stream/ssr-single' + preLoaderRoute: typeof RawStreamSsrSingleRouteImport parentRoute: typeof RawStreamRoute } - '/posts/$postId': { - id: '/posts/$postId' - path: '/$postId' - fullPath: '/posts/$postId' - preLoaderRoute: typeof PostsPostIdRouteImport - parentRoute: typeof PostsRoute + '/raw-stream/ssr-text-hint': { + id: '/raw-stream/ssr-text-hint' + path: '/ssr-text-hint' + fullPath: '/raw-stream/ssr-text-hint' + preLoaderRoute: typeof RawStreamSsrTextHintRouteImport + parentRoute: typeof RawStreamRoute } - '/not-found/via-loader': { - id: '/not-found/via-loader' - path: '/via-loader' - fullPath: '/not-found/via-loader' - preLoaderRoute: typeof NotFoundViaLoaderRouteImport - parentRoute: typeof NotFoundRouteRoute + '/redirect/': { + id: '/redirect/' + path: '/redirect' + fullPath: '/redirect/' + preLoaderRoute: typeof RedirectIndexRouteImport + parentRoute: typeof rootRouteImport } - '/not-found/via-beforeLoad-target-root': { - id: '/not-found/via-beforeLoad-target-root' - path: '/via-beforeLoad-target-root' - fullPath: '/not-found/via-beforeLoad-target-root' - preLoaderRoute: typeof NotFoundViaBeforeLoadTargetRootRouteImport - parentRoute: typeof NotFoundRouteRoute + '/redirect/$target': { + id: '/redirect/$target' + path: '/redirect/$target' + fullPath: '/redirect/$target' + preLoaderRoute: typeof RedirectTargetRouteImport + parentRoute: typeof rootRouteImport } - '/not-found/via-beforeLoad': { - id: '/not-found/via-beforeLoad' - path: '/via-beforeLoad' - fullPath: '/not-found/via-beforeLoad' - preLoaderRoute: typeof NotFoundViaBeforeLoadRouteImport - parentRoute: typeof NotFoundRouteRoute + '/search-params/': { + id: '/search-params/' + path: '/' + fullPath: '/search-params/' + preLoaderRoute: typeof SearchParamsIndexRouteImport + parentRoute: typeof SearchParamsRouteRoute } - '/multi-cookie-redirect/target': { - id: '/multi-cookie-redirect/target' - path: '/multi-cookie-redirect/target' - fullPath: '/multi-cookie-redirect/target' - preLoaderRoute: typeof MultiCookieRedirectTargetRouteImport - parentRoute: typeof rootRouteImport + '/search-params/default': { + id: '/search-params/default' + path: '/default' + fullPath: '/search-params/default' + preLoaderRoute: typeof SearchParamsDefaultRouteImport + parentRoute: typeof SearchParamsRouteRoute } - '/api/users': { - id: '/api/users' - path: '/api/users' - fullPath: '/api/users' - preLoaderRoute: typeof ApiUsersRouteImport - parentRoute: typeof rootRouteImport + '/search-params/loader-throws-redirect': { + id: '/search-params/loader-throws-redirect' + path: '/loader-throws-redirect' + fullPath: '/search-params/loader-throws-redirect' + preLoaderRoute: typeof SearchParamsLoaderThrowsRedirectRouteImport + parentRoute: typeof SearchParamsRouteRoute } - '/_layout/_layout-2': { - id: '/_layout/_layout-2' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutLayout2RouteImport - parentRoute: typeof LayoutRoute + '/specialChars/$param': { + id: '/specialChars/$param' + path: '/$param' + fullPath: '/specialChars/$param' + preLoaderRoute: typeof SpecialCharsParamRouteImport + parentRoute: typeof SpecialCharsRouteRoute + } + '/specialChars/hash': { + id: '/specialChars/hash' + path: '/hash' + fullPath: '/specialChars/hash' + preLoaderRoute: typeof SpecialCharsHashRouteImport + parentRoute: typeof SpecialCharsRouteRoute } '/specialChars/malformed': { id: '/specialChars/malformed' @@ -1070,61 +1049,61 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof SpecialCharsMalformedRouteRouteImport parentRoute: typeof SpecialCharsRouteRoute } - '/not-found/parent-boundary': { - id: '/not-found/parent-boundary' - path: '/parent-boundary' - fullPath: '/not-found/parent-boundary' - preLoaderRoute: typeof NotFoundParentBoundaryRouteRouteImport - parentRoute: typeof NotFoundRouteRoute + '/specialChars/search': { + id: '/specialChars/search' + path: '/search' + fullPath: '/specialChars/search' + preLoaderRoute: typeof SpecialCharsSearchRouteImport + parentRoute: typeof SpecialCharsRouteRoute } - '/redirect/$target/': { - id: '/redirect/$target/' - path: '/' - fullPath: '/redirect/$target/' - preLoaderRoute: typeof RedirectTargetIndexRouteImport - parentRoute: typeof RedirectTargetRoute + '/specialChars/대한민국': { + id: '/specialChars/대한민국' + path: '/대한민국' + fullPath: '/specialChars/대한민국' + preLoaderRoute: typeof SpecialCharsChar45824Char54620Char48124Char44397RouteImport + parentRoute: typeof SpecialCharsRouteRoute } - '/transition/typing/create-resource': { - id: '/transition/typing/create-resource' - path: '/transition/typing/create-resource' - fullPath: '/transition/typing/create-resource' - preLoaderRoute: typeof TransitionTypingCreateResourceRouteImport - parentRoute: typeof rootRouteImport + '/users/': { + id: '/users/' + path: '/' + fullPath: '/users/' + preLoaderRoute: typeof UsersIndexRouteImport + parentRoute: typeof UsersRoute } - '/transition/count/create-resource': { - id: '/transition/count/create-resource' - path: '/transition/count/create-resource' - fullPath: '/transition/count/create-resource' - preLoaderRoute: typeof TransitionCountCreateResourceRouteImport - parentRoute: typeof rootRouteImport + '/users/$userId': { + id: '/users/$userId' + path: '/$userId' + fullPath: '/users/$userId' + preLoaderRoute: typeof UsersUserIdRouteImport + parentRoute: typeof UsersRoute } - '/specialChars/malformed/search': { - id: '/specialChars/malformed/search' - path: '/search' - fullPath: '/specialChars/malformed/search' - preLoaderRoute: typeof SpecialCharsMalformedSearchRouteImport - parentRoute: typeof SpecialCharsMalformedRouteRoute + '/_layout/_layout-2/layout-a': { + id: '/_layout/_layout-2/layout-a' + path: '/layout-a' + fullPath: '/layout-a' + preLoaderRoute: typeof LayoutLayout2LayoutARouteImport + parentRoute: typeof LayoutLayout2Route } - '/specialChars/malformed/$param': { - id: '/specialChars/malformed/$param' - path: '/$param' - fullPath: '/specialChars/malformed/$param' - preLoaderRoute: typeof SpecialCharsMalformedParamRouteImport - parentRoute: typeof SpecialCharsMalformedRouteRoute + '/_layout/_layout-2/layout-b': { + id: '/_layout/_layout-2/layout-b' + path: '/layout-b' + fullPath: '/layout-b' + preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport + parentRoute: typeof LayoutLayout2Route } - '/redirect/$target/via-loader': { - id: '/redirect/$target/via-loader' - path: '/via-loader' - fullPath: '/redirect/$target/via-loader' - preLoaderRoute: typeof RedirectTargetViaLoaderRouteImport - parentRoute: typeof RedirectTargetRoute + '/api/users/$userId': { + id: '/api/users/$userId' + path: '/$userId' + fullPath: '/api/users/$userId' + preLoaderRoute: typeof ApiUsersUserIdRouteImport + parentRoute: typeof ApiUsersRoute } - '/redirect/$target/via-beforeLoad': { - id: '/redirect/$target/via-beforeLoad' + '/not-found/parent-boundary/via-beforeLoad': { + id: '/not-found/parent-boundary/via-beforeLoad' path: '/via-beforeLoad' - fullPath: '/redirect/$target/via-beforeLoad' - preLoaderRoute: typeof RedirectTargetViaBeforeLoadRouteImport - parentRoute: typeof RedirectTargetRoute + fullPath: '/not-found/parent-boundary/via-beforeLoad' + preLoaderRoute: typeof NotFoundParentBoundaryViaBeforeLoadRouteImport + parentRoute: typeof NotFoundParentBoundaryRouteRoute } '/posts_/$postId/deep': { id: '/posts_/$postId/deep' @@ -1133,33 +1112,54 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsPostIdDeepRouteImport parentRoute: typeof rootRouteImport } - '/not-found/parent-boundary/via-beforeLoad': { - id: '/not-found/parent-boundary/via-beforeLoad' + '/redirect/$target/': { + id: '/redirect/$target/' + path: '/' + fullPath: '/redirect/$target/' + preLoaderRoute: typeof RedirectTargetIndexRouteImport + parentRoute: typeof RedirectTargetRoute + } + '/redirect/$target/via-beforeLoad': { + id: '/redirect/$target/via-beforeLoad' path: '/via-beforeLoad' - fullPath: '/not-found/parent-boundary/via-beforeLoad' - preLoaderRoute: typeof NotFoundParentBoundaryViaBeforeLoadRouteImport - parentRoute: typeof NotFoundParentBoundaryRouteRoute + fullPath: '/redirect/$target/via-beforeLoad' + preLoaderRoute: typeof RedirectTargetViaBeforeLoadRouteImport + parentRoute: typeof RedirectTargetRoute } - '/api/users/$userId': { - id: '/api/users/$userId' - path: '/$userId' - fullPath: '/api/users/$userId' - preLoaderRoute: typeof ApiUsersUserIdRouteImport - parentRoute: typeof ApiUsersRoute + '/redirect/$target/via-loader': { + id: '/redirect/$target/via-loader' + path: '/via-loader' + fullPath: '/redirect/$target/via-loader' + preLoaderRoute: typeof RedirectTargetViaLoaderRouteImport + parentRoute: typeof RedirectTargetRoute } - '/_layout/_layout-2/layout-b': { - id: '/_layout/_layout-2/layout-b' - path: '/layout-b' - fullPath: '/layout-b' - preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport - parentRoute: typeof LayoutLayout2Route + '/specialChars/malformed/$param': { + id: '/specialChars/malformed/$param' + path: '/$param' + fullPath: '/specialChars/malformed/$param' + preLoaderRoute: typeof SpecialCharsMalformedParamRouteImport + parentRoute: typeof SpecialCharsMalformedRouteRoute } - '/_layout/_layout-2/layout-a': { - id: '/_layout/_layout-2/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: typeof LayoutLayout2LayoutARouteImport - parentRoute: typeof LayoutLayout2Route + '/specialChars/malformed/search': { + id: '/specialChars/malformed/search' + path: '/search' + fullPath: '/specialChars/malformed/search' + preLoaderRoute: typeof SpecialCharsMalformedSearchRouteImport + parentRoute: typeof SpecialCharsMalformedRouteRoute + } + '/transition/count/create-resource': { + id: '/transition/count/create-resource' + path: '/transition/count/create-resource' + fullPath: '/transition/count/create-resource' + preLoaderRoute: typeof TransitionCountCreateResourceRouteImport + parentRoute: typeof rootRouteImport + } + '/transition/typing/create-resource': { + id: '/transition/typing/create-resource' + path: '/transition/typing/create-resource' + fullPath: '/transition/typing/create-resource' + preLoaderRoute: typeof TransitionTypingCreateResourceRouteImport + parentRoute: typeof rootRouteImport } '/redirect/$target/serverFn/': { id: '/redirect/$target/serverFn/' @@ -1168,11 +1168,11 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof RedirectTargetServerFnIndexRouteImport parentRoute: typeof RedirectTargetRoute } - '/redirect/$target/serverFn/via-useServerFn': { - id: '/redirect/$target/serverFn/via-useServerFn' - path: '/serverFn/via-useServerFn' - fullPath: '/redirect/$target/serverFn/via-useServerFn' - preLoaderRoute: typeof RedirectTargetServerFnViaUseServerFnRouteImport + '/redirect/$target/serverFn/via-beforeLoad': { + id: '/redirect/$target/serverFn/via-beforeLoad' + path: '/serverFn/via-beforeLoad' + fullPath: '/redirect/$target/serverFn/via-beforeLoad' + preLoaderRoute: typeof RedirectTargetServerFnViaBeforeLoadRouteImport parentRoute: typeof RedirectTargetRoute } '/redirect/$target/serverFn/via-loader': { @@ -1182,11 +1182,11 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof RedirectTargetServerFnViaLoaderRouteImport parentRoute: typeof RedirectTargetRoute } - '/redirect/$target/serverFn/via-beforeLoad': { - id: '/redirect/$target/serverFn/via-beforeLoad' - path: '/serverFn/via-beforeLoad' - fullPath: '/redirect/$target/serverFn/via-beforeLoad' - preLoaderRoute: typeof RedirectTargetServerFnViaBeforeLoadRouteImport + '/redirect/$target/serverFn/via-useServerFn': { + id: '/redirect/$target/serverFn/via-useServerFn' + path: '/serverFn/via-useServerFn' + fullPath: '/redirect/$target/serverFn/via-useServerFn' + preLoaderRoute: typeof RedirectTargetServerFnViaUseServerFnRouteImport parentRoute: typeof RedirectTargetRoute } } diff --git a/e2e/solid-start/custom-basepath/src/routeTree.gen.ts b/e2e/solid-start/custom-basepath/src/routeTree.gen.ts index f87b2b3731..ddc9f81ab0 100644 --- a/e2e/solid-start/custom-basepath/src/routeTree.gen.ts +++ b/e2e/solid-start/custom-basepath/src/routeTree.gen.ts @@ -9,30 +9,35 @@ // 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 UsersRouteImport } from './routes/users' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as NavigateTestRouteImport } from './routes/navigate-test' -import { Route as LogoutRouteImport } from './routes/logout' -import { Route as DeferredRouteImport } from './routes/deferred' import { Route as IndexRouteImport } from './routes/index' -import { Route as UsersIndexRouteImport } from './routes/users.index' -import { Route as RedirectIndexRouteImport } from './routes/redirect.index' +import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as LogoutRouteImport } from './routes/logout' +import { Route as NavigateTestRouteImport } from './routes/navigate-test' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as UsersRouteImport } from './routes/users' +import { Route as ApiUsersRouteImport } from './routes/api/users' import { Route as PostsIndexRouteImport } from './routes/posts.index' -import { Route as UsersUserIdRouteImport } from './routes/users.$userId' -import { Route as RedirectThrowItRouteImport } from './routes/redirect.throw-it' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as ApiUsersRouteImport } from './routes/api/users' -import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' +import { Route as RedirectIndexRouteImport } from './routes/redirect.index' +import { Route as RedirectThrowItRouteImport } from './routes/redirect.throw-it' +import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as ApiUsersUserIdRouteImport } from './routes/api/users.$userId' +import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -const UsersRoute = UsersRouteImport.update({ - id: '/users', - path: '/users', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const DeferredRoute = DeferredRouteImport.update({ + id: '/deferred', + path: '/deferred', + getParentRoute: () => rootRouteImport, +} as any) +const LogoutRoute = LogoutRouteImport.update({ + id: '/logout', + path: '/logout', getParentRoute: () => rootRouteImport, } as any) const NavigateTestRoute = NavigateTestRouteImport.update({ @@ -40,66 +45,61 @@ const NavigateTestRoute = NavigateTestRouteImport.update({ path: '/navigate-test', getParentRoute: () => rootRouteImport, } as any) -const LogoutRoute = LogoutRouteImport.update({ - id: '/logout', - path: '/logout', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const DeferredRoute = DeferredRouteImport.update({ - id: '/deferred', - path: '/deferred', +const UsersRoute = UsersRouteImport.update({ + id: '/users', + path: '/users', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const ApiUsersRoute = ApiUsersRouteImport.update({ + id: '/api/users', + path: '/api/users', getParentRoute: () => rootRouteImport, } as any) -const UsersIndexRoute = UsersIndexRouteImport.update({ +const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => UsersRoute, + getParentRoute: () => PostsRoute, +} as any) +const PostsPostIdRoute = PostsPostIdRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => PostsRoute, } as any) const RedirectIndexRoute = RedirectIndexRouteImport.update({ id: '/redirect/', path: '/redirect/', getParentRoute: () => rootRouteImport, } as any) -const PostsIndexRoute = PostsIndexRouteImport.update({ +const RedirectThrowItRoute = RedirectThrowItRouteImport.update({ + id: '/redirect/throw-it', + path: '/redirect/throw-it', + getParentRoute: () => rootRouteImport, +} as any) +const UsersIndexRoute = UsersIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => PostsRoute, + getParentRoute: () => UsersRoute, } as any) const UsersUserIdRoute = UsersUserIdRouteImport.update({ id: '/$userId', path: '/$userId', getParentRoute: () => UsersRoute, } as any) -const RedirectThrowItRoute = RedirectThrowItRouteImport.update({ - id: '/redirect/throw-it', - path: '/redirect/throw-it', - getParentRoute: () => rootRouteImport, -} as any) -const PostsPostIdRoute = PostsPostIdRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => PostsRoute, -} as any) -const ApiUsersRoute = ApiUsersRouteImport.update({ - id: '/api/users', - path: '/api/users', - getParentRoute: () => rootRouteImport, +const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ + id: '/$userId', + path: '/$userId', + getParentRoute: () => ApiUsersRoute, } as any) const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ id: '/posts_/$postId/deep', path: '/posts/$postId/deep', getParentRoute: () => rootRouteImport, } as any) -const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ - id: '/$userId', - path: '/$userId', - getParentRoute: () => ApiUsersRoute, -} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -218,25 +218,18 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/users': { - id: '/users' - path: '/users' - fullPath: '/users' - preLoaderRoute: typeof UsersRouteImport - parentRoute: typeof rootRouteImport - } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/navigate-test': { - id: '/navigate-test' - path: '/navigate-test' - fullPath: '/navigate-test' - preLoaderRoute: typeof NavigateTestRouteImport + '/deferred': { + id: '/deferred' + path: '/deferred' + fullPath: '/deferred' + preLoaderRoute: typeof DeferredRouteImport parentRoute: typeof rootRouteImport } '/logout': { @@ -246,32 +239,32 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof LogoutRouteImport parentRoute: typeof rootRouteImport } - '/deferred': { - id: '/deferred' - path: '/deferred' - fullPath: '/deferred' - preLoaderRoute: typeof DeferredRouteImport + '/navigate-test': { + id: '/navigate-test' + path: '/navigate-test' + fullPath: '/navigate-test' + preLoaderRoute: typeof NavigateTestRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/users/': { - id: '/users/' - path: '/' - fullPath: '/users/' - preLoaderRoute: typeof UsersIndexRouteImport - parentRoute: typeof UsersRoute + '/users': { + id: '/users' + path: '/users' + fullPath: '/users' + preLoaderRoute: typeof UsersRouteImport + parentRoute: typeof rootRouteImport } - '/redirect/': { - id: '/redirect/' - path: '/redirect' - fullPath: '/redirect/' - preLoaderRoute: typeof RedirectIndexRouteImport + '/api/users': { + id: '/api/users' + path: '/api/users' + fullPath: '/api/users' + preLoaderRoute: typeof ApiUsersRouteImport parentRoute: typeof rootRouteImport } '/posts/': { @@ -281,20 +274,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRoute } - '/users/$userId': { - id: '/users/$userId' - path: '/$userId' - fullPath: '/users/$userId' - preLoaderRoute: typeof UsersUserIdRouteImport - parentRoute: typeof UsersRoute - } - '/redirect/throw-it': { - id: '/redirect/throw-it' - path: '/redirect/throw-it' - fullPath: '/redirect/throw-it' - preLoaderRoute: typeof RedirectThrowItRouteImport - parentRoute: typeof rootRouteImport - } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' @@ -302,20 +281,34 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/api/users': { - id: '/api/users' - path: '/api/users' - fullPath: '/api/users' - preLoaderRoute: typeof ApiUsersRouteImport + '/redirect/': { + id: '/redirect/' + path: '/redirect' + fullPath: '/redirect/' + preLoaderRoute: typeof RedirectIndexRouteImport parentRoute: typeof rootRouteImport } - '/posts_/$postId/deep': { - id: '/posts_/$postId/deep' - path: '/posts/$postId/deep' - fullPath: '/posts/$postId/deep' - preLoaderRoute: typeof PostsPostIdDeepRouteImport + '/redirect/throw-it': { + id: '/redirect/throw-it' + path: '/redirect/throw-it' + fullPath: '/redirect/throw-it' + preLoaderRoute: typeof RedirectThrowItRouteImport parentRoute: typeof rootRouteImport } + '/users/': { + id: '/users/' + path: '/' + fullPath: '/users/' + preLoaderRoute: typeof UsersIndexRouteImport + parentRoute: typeof UsersRoute + } + '/users/$userId': { + id: '/users/$userId' + path: '/$userId' + fullPath: '/users/$userId' + preLoaderRoute: typeof UsersUserIdRouteImport + parentRoute: typeof UsersRoute + } '/api/users/$userId': { id: '/api/users/$userId' path: '/$userId' @@ -323,6 +316,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof ApiUsersUserIdRouteImport parentRoute: typeof ApiUsersRoute } + '/posts_/$postId/deep': { + id: '/posts_/$postId/deep' + path: '/posts/$postId/deep' + fullPath: '/posts/$postId/deep' + preLoaderRoute: typeof PostsPostIdDeepRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/e2e/solid-start/deferred-hydration/src/routeTree.gen.ts b/e2e/solid-start/deferred-hydration/src/routeTree.gen.ts index e75225b500..e689dd19b8 100644 --- a/e2e/solid-start/deferred-hydration/src/routeTree.gen.ts +++ b/e2e/solid-start/deferred-hydration/src/routeTree.gen.ts @@ -9,20 +9,20 @@ // 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 ImportedRouteImport } from './routes/imported' -import { Route as EnhancedRouteImport } from './routes/enhanced' -import { Route as CssRouteImport } from './routes/css' -import { Route as ComponentsRouteImport } from './routes/components' import { Route as IndexRouteImport } from './routes/index' +import { Route as ComponentsRouteImport } from './routes/components' +import { Route as CssRouteImport } from './routes/css' +import { Route as EnhancedRouteImport } from './routes/enhanced' +import { Route as ImportedRouteImport } from './routes/imported' -const ImportedRoute = ImportedRouteImport.update({ - id: '/imported', - path: '/imported', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const EnhancedRoute = EnhancedRouteImport.update({ - id: '/enhanced', - path: '/enhanced', +const ComponentsRoute = ComponentsRouteImport.update({ + id: '/components', + path: '/components', getParentRoute: () => rootRouteImport, } as any) const CssRoute = CssRouteImport.update({ @@ -30,14 +30,14 @@ const CssRoute = CssRouteImport.update({ path: '/css', getParentRoute: () => rootRouteImport, } as any) -const ComponentsRoute = ComponentsRouteImport.update({ - id: '/components', - path: '/components', +const EnhancedRoute = EnhancedRouteImport.update({ + id: '/enhanced', + path: '/enhanced', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const ImportedRoute = ImportedRouteImport.update({ + id: '/imported', + path: '/imported', getParentRoute: () => rootRouteImport, } as any) @@ -81,18 +81,18 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/imported': { - id: '/imported' - path: '/imported' - fullPath: '/imported' - preLoaderRoute: typeof ImportedRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/enhanced': { - id: '/enhanced' - path: '/enhanced' - fullPath: '/enhanced' - preLoaderRoute: typeof EnhancedRouteImport + '/components': { + id: '/components' + path: '/components' + fullPath: '/components' + preLoaderRoute: typeof ComponentsRouteImport parentRoute: typeof rootRouteImport } '/css': { @@ -102,18 +102,18 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof CssRouteImport parentRoute: typeof rootRouteImport } - '/components': { - id: '/components' - path: '/components' - fullPath: '/components' - preLoaderRoute: typeof ComponentsRouteImport + '/enhanced': { + id: '/enhanced' + path: '/enhanced' + fullPath: '/enhanced' + preLoaderRoute: typeof EnhancedRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/imported': { + id: '/imported' + path: '/imported' + fullPath: '/imported' + preLoaderRoute: typeof ImportedRouteImport parentRoute: typeof rootRouteImport } } diff --git a/e2e/solid-start/query-integration/src/routeTree.gen.ts b/e2e/solid-start/query-integration/src/routeTree.gen.ts index bdd2163038..3d6686f67c 100644 --- a/e2e/solid-start/query-integration/src/routeTree.gen.ts +++ b/e2e/solid-start/query-integration/src/routeTree.gen.ts @@ -9,20 +9,20 @@ // 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 UseQueryRouteImport } from './routes/useQuery' import { Route as IndexRouteImport } from './routes/index' +import { Route as UseQueryRouteImport } from './routes/useQuery' import { Route as LoaderFetchQueryTypeRouteImport } from './routes/loader-fetchQuery/$type' -const UseQueryRoute = UseQueryRouteImport.update({ - id: '/useQuery', - path: '/useQuery', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const UseQueryRoute = UseQueryRouteImport.update({ + id: '/useQuery', + path: '/useQuery', + getParentRoute: () => rootRouteImport, +} as any) const LoaderFetchQueryTypeRoute = LoaderFetchQueryTypeRouteImport.update({ id: '/loader-fetchQuery/$type', path: '/loader-fetchQuery/$type', @@ -61,13 +61,6 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/useQuery': { - id: '/useQuery' - path: '/useQuery' - fullPath: '/useQuery' - preLoaderRoute: typeof UseQueryRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -75,6 +68,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/useQuery': { + id: '/useQuery' + path: '/useQuery' + fullPath: '/useQuery' + preLoaderRoute: typeof UseQueryRouteImport + parentRoute: typeof rootRouteImport + } '/loader-fetchQuery/$type': { id: '/loader-fetchQuery/$type' path: '/loader-fetchQuery/$type' diff --git a/e2e/solid-start/scroll-restoration/src/routeTree.gen.ts b/e2e/solid-start/scroll-restoration/src/routeTree.gen.ts index 4b8d19fbef..7668df5930 100644 --- a/e2e/solid-start/scroll-restoration/src/routeTree.gen.ts +++ b/e2e/solid-start/scroll-restoration/src/routeTree.gen.ts @@ -10,18 +10,18 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' -import { Route as testsWithSearchRouteImport } from './routes/(tests)/with-search' -import { Route as testsWithLoaderRouteImport } from './routes/(tests)/with-loader' import { Route as testsNormalPageRouteImport } from './routes/(tests)/normal-page' +import { Route as testsWithLoaderRouteImport } from './routes/(tests)/with-loader' +import { Route as testsWithSearchRouteImport } from './routes/(tests)/with-search' const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) -const testsWithSearchRoute = testsWithSearchRouteImport.update({ - id: '/(tests)/with-search', - path: '/with-search', +const testsNormalPageRoute = testsNormalPageRouteImport.update({ + id: '/(tests)/normal-page', + path: '/normal-page', getParentRoute: () => rootRouteImport, } as any) const testsWithLoaderRoute = testsWithLoaderRouteImport.update({ @@ -29,9 +29,9 @@ const testsWithLoaderRoute = testsWithLoaderRouteImport.update({ path: '/with-loader', getParentRoute: () => rootRouteImport, } as any) -const testsNormalPageRoute = testsNormalPageRouteImport.update({ - id: '/(tests)/normal-page', - path: '/normal-page', +const testsWithSearchRoute = testsWithSearchRouteImport.update({ + id: '/(tests)/with-search', + path: '/with-search', getParentRoute: () => rootRouteImport, } as any) @@ -83,11 +83,11 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/with-search': { - id: '/(tests)/with-search' - path: '/with-search' - fullPath: '/with-search' - preLoaderRoute: typeof testsWithSearchRouteImport + '/(tests)/normal-page': { + id: '/(tests)/normal-page' + path: '/normal-page' + fullPath: '/normal-page' + preLoaderRoute: typeof testsNormalPageRouteImport parentRoute: typeof rootRouteImport } '/(tests)/with-loader': { @@ -97,11 +97,11 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof testsWithLoaderRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/normal-page': { - id: '/(tests)/normal-page' - path: '/normal-page' - fullPath: '/normal-page' - preLoaderRoute: typeof testsNormalPageRouteImport + '/(tests)/with-search': { + id: '/(tests)/with-search' + path: '/with-search' + fullPath: '/with-search' + preLoaderRoute: typeof testsWithSearchRouteImport parentRoute: typeof rootRouteImport } } diff --git a/e2e/solid-start/selective-ssr/src/routeTree.gen.ts b/e2e/solid-start/selective-ssr/src/routeTree.gen.ts index df2b865e51..a6984d095e 100644 --- a/e2e/solid-start/selective-ssr/src/routeTree.gen.ts +++ b/e2e/solid-start/selective-ssr/src/routeTree.gen.ts @@ -9,14 +9,14 @@ // 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 PostsRouteImport } from './routes/posts' -import { Route as DataOnlyPendingComponentRouteImport } from './routes/data-only-pending-component' import { Route as IndexRouteImport } from './routes/index' +import { Route as DataOnlyPendingComponentRouteImport } from './routes/data-only-pending-component' +import { Route as PostsRouteImport } from './routes/posts' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const DataOnlyPendingComponentRoute = @@ -25,9 +25,9 @@ const DataOnlyPendingComponentRoute = path: '/data-only-pending-component', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ @@ -76,11 +76,11 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/data-only-pending-component': { @@ -90,11 +90,11 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof DataOnlyPendingComponentRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } '/posts/$postId': { diff --git a/e2e/solid-start/serialization-adapters/src/routeTree.gen.ts b/e2e/solid-start/serialization-adapters/src/routeTree.gen.ts index 09d0211154..94ebb49d16 100644 --- a/e2e/solid-start/serialization-adapters/src/routeTree.gen.ts +++ b/e2e/solid-start/serialization-adapters/src/routeTree.gen.ts @@ -10,25 +10,26 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' -import { Route as SsrStreamRouteImport } from './routes/ssr/stream' -import { Route as SsrNestedRouteImport } from './routes/ssr/nested' -import { Route as SsrDataOnlyRouteImport } from './routes/ssr/data-only' -import { Route as ServerFunctionNestedRouteImport } from './routes/server-function/nested' import { Route as ServerFunctionCustomErrorRouteImport } from './routes/server-function/custom-error' +import { Route as ServerFunctionNestedRouteImport } from './routes/server-function/nested' +import { Route as SsrDataOnlyRouteImport } from './routes/ssr/data-only' +import { Route as SsrNestedRouteImport } from './routes/ssr/nested' +import { Route as SsrStreamRouteImport } from './routes/ssr/stream' const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) -const SsrStreamRoute = SsrStreamRouteImport.update({ - id: '/ssr/stream', - path: '/ssr/stream', - getParentRoute: () => rootRouteImport, -} as any) -const SsrNestedRoute = SsrNestedRouteImport.update({ - id: '/ssr/nested', - path: '/ssr/nested', +const ServerFunctionCustomErrorRoute = + ServerFunctionCustomErrorRouteImport.update({ + id: '/server-function/custom-error', + path: '/server-function/custom-error', + getParentRoute: () => rootRouteImport, + } as any) +const ServerFunctionNestedRoute = ServerFunctionNestedRouteImport.update({ + id: '/server-function/nested', + path: '/server-function/nested', getParentRoute: () => rootRouteImport, } as any) const SsrDataOnlyRoute = SsrDataOnlyRouteImport.update({ @@ -36,17 +37,16 @@ const SsrDataOnlyRoute = SsrDataOnlyRouteImport.update({ path: '/ssr/data-only', getParentRoute: () => rootRouteImport, } as any) -const ServerFunctionNestedRoute = ServerFunctionNestedRouteImport.update({ - id: '/server-function/nested', - path: '/server-function/nested', +const SsrNestedRoute = SsrNestedRouteImport.update({ + id: '/ssr/nested', + path: '/ssr/nested', + getParentRoute: () => rootRouteImport, +} as any) +const SsrStreamRoute = SsrStreamRouteImport.update({ + id: '/ssr/stream', + path: '/ssr/stream', getParentRoute: () => rootRouteImport, } as any) -const ServerFunctionCustomErrorRoute = - ServerFunctionCustomErrorRouteImport.update({ - id: '/server-function/custom-error', - path: '/server-function/custom-error', - getParentRoute: () => rootRouteImport, - } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -118,18 +118,18 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/ssr/stream': { - id: '/ssr/stream' - path: '/ssr/stream' - fullPath: '/ssr/stream' - preLoaderRoute: typeof SsrStreamRouteImport + '/server-function/custom-error': { + id: '/server-function/custom-error' + path: '/server-function/custom-error' + fullPath: '/server-function/custom-error' + preLoaderRoute: typeof ServerFunctionCustomErrorRouteImport parentRoute: typeof rootRouteImport } - '/ssr/nested': { - id: '/ssr/nested' - path: '/ssr/nested' - fullPath: '/ssr/nested' - preLoaderRoute: typeof SsrNestedRouteImport + '/server-function/nested': { + id: '/server-function/nested' + path: '/server-function/nested' + fullPath: '/server-function/nested' + preLoaderRoute: typeof ServerFunctionNestedRouteImport parentRoute: typeof rootRouteImport } '/ssr/data-only': { @@ -139,18 +139,18 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof SsrDataOnlyRouteImport parentRoute: typeof rootRouteImport } - '/server-function/nested': { - id: '/server-function/nested' - path: '/server-function/nested' - fullPath: '/server-function/nested' - preLoaderRoute: typeof ServerFunctionNestedRouteImport + '/ssr/nested': { + id: '/ssr/nested' + path: '/ssr/nested' + fullPath: '/ssr/nested' + preLoaderRoute: typeof SsrNestedRouteImport parentRoute: typeof rootRouteImport } - '/server-function/custom-error': { - id: '/server-function/custom-error' - path: '/server-function/custom-error' - fullPath: '/server-function/custom-error' - preLoaderRoute: typeof ServerFunctionCustomErrorRouteImport + '/ssr/stream': { + id: '/ssr/stream' + path: '/ssr/stream' + fullPath: '/ssr/stream' + preLoaderRoute: typeof SsrStreamRouteImport parentRoute: typeof rootRouteImport } } diff --git a/e2e/solid-start/server-functions/src/routeTree.gen.ts b/e2e/solid-start/server-functions/src/routeTree.gen.ts index 329a70726e..2d3b197b77 100644 --- a/e2e/solid-start/server-functions/src/routeTree.gen.ts +++ b/e2e/solid-start/server-functions/src/routeTree.gen.ts @@ -9,60 +9,65 @@ // 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 SubmitPostFormdataRouteImport } from './routes/submit-post-formdata' -import { Route as StatusRouteImport } from './routes/status' -import { Route as SerializeFormDataRouteImport } from './routes/serialize-form-data' -import { Route as ReturnNullRouteImport } from './routes/return-null' -import { Route as RawResponseRouteImport } from './routes/raw-response' -import { Route as MultipartRouteImport } from './routes/multipart' -import { Route as IsomorphicFnsRouteImport } from './routes/isomorphic-fns' -import { Route as HeadersRouteImport } from './routes/headers' -import { Route as EnvOnlyRouteImport } from './routes/env-only' -import { Route as DeadCodePreserveRouteImport } from './routes/dead-code-preserve' -import { Route as ConsistentRouteImport } from './routes/consistent' import { Route as IndexRouteImport } from './routes/index' -import { Route as RedirectTestIndexRouteImport } from './routes/redirect-test/index' -import { Route as RedirectTestSsrIndexRouteImport } from './routes/redirect-test-ssr/index' -import { Route as PrimitivesIndexRouteImport } from './routes/primitives/index' -import { Route as MiddlewareIndexRouteImport } from './routes/middleware/index' -import { Route as FunctionMethodIndexRouteImport } from './routes/function-method/index' -import { Route as FunctionMetadataIndexRouteImport } from './routes/function-metadata/index' -import { Route as FormdataRedirectIndexRouteImport } from './routes/formdata-redirect/index' -import { Route as FactoryIndexRouteImport } from './routes/factory/index' -import { Route as CookiesIndexRouteImport } from './routes/cookies/index' +import { Route as ConsistentRouteImport } from './routes/consistent' +import { Route as DeadCodePreserveRouteImport } from './routes/dead-code-preserve' +import { Route as EnvOnlyRouteImport } from './routes/env-only' +import { Route as HeadersRouteImport } from './routes/headers' +import { Route as IsomorphicFnsRouteImport } from './routes/isomorphic-fns' +import { Route as MultipartRouteImport } from './routes/multipart' +import { Route as RawResponseRouteImport } from './routes/raw-response' +import { Route as ReturnNullRouteImport } from './routes/return-null' +import { Route as SerializeFormDataRouteImport } from './routes/serialize-form-data' +import { Route as StatusRouteImport } from './routes/status' +import { Route as SubmitPostFormdataRouteImport } from './routes/submit-post-formdata' import { Route as AbortSignalIndexRouteImport } from './routes/abort-signal/index' -import { Route as RedirectTestTargetRouteImport } from './routes/redirect-test/target' -import { Route as RedirectTestSsrTargetRouteImport } from './routes/redirect-test-ssr/target' -import { Route as MiddlewareSendServerFnRouteImport } from './routes/middleware/send-serverFn' -import { Route as MiddlewareRequestMiddlewareRouteImport } from './routes/middleware/request-middleware' -import { Route as MiddlewareClientMiddlewareRouterRouteImport } from './routes/middleware/client-middleware-router' -import { Route as CookiesSetRouteImport } from './routes/cookies/set' import { Route as AbortSignalMethodRouteImport } from './routes/abort-signal/$method' +import { Route as CookiesIndexRouteImport } from './routes/cookies/index' +import { Route as CookiesSetRouteImport } from './routes/cookies/set' +import { Route as FactoryIndexRouteImport } from './routes/factory/index' +import { Route as FormdataRedirectIndexRouteImport } from './routes/formdata-redirect/index' +import { Route as FunctionMetadataIndexRouteImport } from './routes/function-metadata/index' +import { Route as FunctionMethodIndexRouteImport } from './routes/function-method/index' +import { Route as MiddlewareIndexRouteImport } from './routes/middleware/index' +import { Route as MiddlewareClientMiddlewareRouterRouteImport } from './routes/middleware/client-middleware-router' +import { Route as MiddlewareRequestMiddlewareRouteImport } from './routes/middleware/request-middleware' +import { Route as MiddlewareSendServerFnRouteImport } from './routes/middleware/send-serverFn' +import { Route as PrimitivesIndexRouteImport } from './routes/primitives/index' +import { Route as RedirectTestSsrIndexRouteImport } from './routes/redirect-test-ssr/index' +import { Route as RedirectTestSsrTargetRouteImport } from './routes/redirect-test-ssr/target' +import { Route as RedirectTestIndexRouteImport } from './routes/redirect-test/index' +import { Route as RedirectTestTargetRouteImport } from './routes/redirect-test/target' import { Route as FormdataRedirectTargetNameRouteImport } from './routes/formdata-redirect/target.$name' -const SubmitPostFormdataRoute = SubmitPostFormdataRouteImport.update({ - id: '/submit-post-formdata', - path: '/submit-post-formdata', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const StatusRoute = StatusRouteImport.update({ - id: '/status', - path: '/status', +const ConsistentRoute = ConsistentRouteImport.update({ + id: '/consistent', + path: '/consistent', getParentRoute: () => rootRouteImport, } as any) -const SerializeFormDataRoute = SerializeFormDataRouteImport.update({ - id: '/serialize-form-data', - path: '/serialize-form-data', +const DeadCodePreserveRoute = DeadCodePreserveRouteImport.update({ + id: '/dead-code-preserve', + path: '/dead-code-preserve', getParentRoute: () => rootRouteImport, } as any) -const ReturnNullRoute = ReturnNullRouteImport.update({ - id: '/return-null', - path: '/return-null', +const EnvOnlyRoute = EnvOnlyRouteImport.update({ + id: '/env-only', + path: '/env-only', getParentRoute: () => rootRouteImport, } as any) -const RawResponseRoute = RawResponseRouteImport.update({ - id: '/raw-response', - path: '/raw-response', +const HeadersRoute = HeadersRouteImport.update({ + id: '/headers', + path: '/headers', + getParentRoute: () => rootRouteImport, +} as any) +const IsomorphicFnsRoute = IsomorphicFnsRouteImport.update({ + id: '/isomorphic-fns', + path: '/isomorphic-fns', getParentRoute: () => rootRouteImport, } as any) const MultipartRoute = MultipartRouteImport.update({ @@ -70,59 +75,59 @@ const MultipartRoute = MultipartRouteImport.update({ path: '/multipart', getParentRoute: () => rootRouteImport, } as any) -const IsomorphicFnsRoute = IsomorphicFnsRouteImport.update({ - id: '/isomorphic-fns', - path: '/isomorphic-fns', +const RawResponseRoute = RawResponseRouteImport.update({ + id: '/raw-response', + path: '/raw-response', getParentRoute: () => rootRouteImport, } as any) -const HeadersRoute = HeadersRouteImport.update({ - id: '/headers', - path: '/headers', +const ReturnNullRoute = ReturnNullRouteImport.update({ + id: '/return-null', + path: '/return-null', getParentRoute: () => rootRouteImport, } as any) -const EnvOnlyRoute = EnvOnlyRouteImport.update({ - id: '/env-only', - path: '/env-only', +const SerializeFormDataRoute = SerializeFormDataRouteImport.update({ + id: '/serialize-form-data', + path: '/serialize-form-data', getParentRoute: () => rootRouteImport, } as any) -const DeadCodePreserveRoute = DeadCodePreserveRouteImport.update({ - id: '/dead-code-preserve', - path: '/dead-code-preserve', +const StatusRoute = StatusRouteImport.update({ + id: '/status', + path: '/status', getParentRoute: () => rootRouteImport, } as any) -const ConsistentRoute = ConsistentRouteImport.update({ - id: '/consistent', - path: '/consistent', +const SubmitPostFormdataRoute = SubmitPostFormdataRouteImport.update({ + id: '/submit-post-formdata', + path: '/submit-post-formdata', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const AbortSignalIndexRoute = AbortSignalIndexRouteImport.update({ + id: '/abort-signal/', + path: '/abort-signal/', getParentRoute: () => rootRouteImport, } as any) -const RedirectTestIndexRoute = RedirectTestIndexRouteImport.update({ - id: '/redirect-test/', - path: '/redirect-test/', +const AbortSignalMethodRoute = AbortSignalMethodRouteImport.update({ + id: '/abort-signal/$method', + path: '/abort-signal/$method', getParentRoute: () => rootRouteImport, } as any) -const RedirectTestSsrIndexRoute = RedirectTestSsrIndexRouteImport.update({ - id: '/redirect-test-ssr/', - path: '/redirect-test-ssr/', +const CookiesIndexRoute = CookiesIndexRouteImport.update({ + id: '/cookies/', + path: '/cookies/', getParentRoute: () => rootRouteImport, } as any) -const PrimitivesIndexRoute = PrimitivesIndexRouteImport.update({ - id: '/primitives/', - path: '/primitives/', +const CookiesSetRoute = CookiesSetRouteImport.update({ + id: '/cookies/set', + path: '/cookies/set', getParentRoute: () => rootRouteImport, } as any) -const MiddlewareIndexRoute = MiddlewareIndexRouteImport.update({ - id: '/middleware/', - path: '/middleware/', +const FactoryIndexRoute = FactoryIndexRouteImport.update({ + id: '/factory/', + path: '/factory/', getParentRoute: () => rootRouteImport, } as any) -const FunctionMethodIndexRoute = FunctionMethodIndexRouteImport.update({ - id: '/function-method/', - path: '/function-method/', +const FormdataRedirectIndexRoute = FormdataRedirectIndexRouteImport.update({ + id: '/formdata-redirect/', + path: '/formdata-redirect/', getParentRoute: () => rootRouteImport, } as any) const FunctionMetadataIndexRoute = FunctionMetadataIndexRouteImport.update({ @@ -130,29 +135,41 @@ const FunctionMetadataIndexRoute = FunctionMetadataIndexRouteImport.update({ path: '/function-metadata/', getParentRoute: () => rootRouteImport, } as any) -const FormdataRedirectIndexRoute = FormdataRedirectIndexRouteImport.update({ - id: '/formdata-redirect/', - path: '/formdata-redirect/', +const FunctionMethodIndexRoute = FunctionMethodIndexRouteImport.update({ + id: '/function-method/', + path: '/function-method/', getParentRoute: () => rootRouteImport, } as any) -const FactoryIndexRoute = FactoryIndexRouteImport.update({ - id: '/factory/', - path: '/factory/', +const MiddlewareIndexRoute = MiddlewareIndexRouteImport.update({ + id: '/middleware/', + path: '/middleware/', getParentRoute: () => rootRouteImport, } as any) -const CookiesIndexRoute = CookiesIndexRouteImport.update({ - id: '/cookies/', - path: '/cookies/', +const MiddlewareClientMiddlewareRouterRoute = + MiddlewareClientMiddlewareRouterRouteImport.update({ + id: '/middleware/client-middleware-router', + path: '/middleware/client-middleware-router', + getParentRoute: () => rootRouteImport, + } as any) +const MiddlewareRequestMiddlewareRoute = + MiddlewareRequestMiddlewareRouteImport.update({ + id: '/middleware/request-middleware', + path: '/middleware/request-middleware', + getParentRoute: () => rootRouteImport, + } as any) +const MiddlewareSendServerFnRoute = MiddlewareSendServerFnRouteImport.update({ + id: '/middleware/send-serverFn', + path: '/middleware/send-serverFn', getParentRoute: () => rootRouteImport, } as any) -const AbortSignalIndexRoute = AbortSignalIndexRouteImport.update({ - id: '/abort-signal/', - path: '/abort-signal/', +const PrimitivesIndexRoute = PrimitivesIndexRouteImport.update({ + id: '/primitives/', + path: '/primitives/', getParentRoute: () => rootRouteImport, } as any) -const RedirectTestTargetRoute = RedirectTestTargetRouteImport.update({ - id: '/redirect-test/target', - path: '/redirect-test/target', +const RedirectTestSsrIndexRoute = RedirectTestSsrIndexRouteImport.update({ + id: '/redirect-test-ssr/', + path: '/redirect-test-ssr/', getParentRoute: () => rootRouteImport, } as any) const RedirectTestSsrTargetRoute = RedirectTestSsrTargetRouteImport.update({ @@ -160,31 +177,14 @@ const RedirectTestSsrTargetRoute = RedirectTestSsrTargetRouteImport.update({ path: '/redirect-test-ssr/target', getParentRoute: () => rootRouteImport, } as any) -const MiddlewareSendServerFnRoute = MiddlewareSendServerFnRouteImport.update({ - id: '/middleware/send-serverFn', - path: '/middleware/send-serverFn', - getParentRoute: () => rootRouteImport, -} as any) -const MiddlewareRequestMiddlewareRoute = - MiddlewareRequestMiddlewareRouteImport.update({ - id: '/middleware/request-middleware', - path: '/middleware/request-middleware', - getParentRoute: () => rootRouteImport, - } as any) -const MiddlewareClientMiddlewareRouterRoute = - MiddlewareClientMiddlewareRouterRouteImport.update({ - id: '/middleware/client-middleware-router', - path: '/middleware/client-middleware-router', - getParentRoute: () => rootRouteImport, - } as any) -const CookiesSetRoute = CookiesSetRouteImport.update({ - id: '/cookies/set', - path: '/cookies/set', +const RedirectTestIndexRoute = RedirectTestIndexRouteImport.update({ + id: '/redirect-test/', + path: '/redirect-test/', getParentRoute: () => rootRouteImport, } as any) -const AbortSignalMethodRoute = AbortSignalMethodRouteImport.update({ - id: '/abort-signal/$method', - path: '/abort-signal/$method', +const RedirectTestTargetRoute = RedirectTestTargetRouteImport.update({ + id: '/redirect-test/target', + path: '/redirect-test/target', getParentRoute: () => rootRouteImport, } as any) const FormdataRedirectTargetNameRoute = @@ -425,46 +425,39 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/submit-post-formdata': { - id: '/submit-post-formdata' - path: '/submit-post-formdata' - fullPath: '/submit-post-formdata' - preLoaderRoute: typeof SubmitPostFormdataRouteImport - parentRoute: typeof rootRouteImport - } - '/status': { - id: '/status' - path: '/status' - fullPath: '/status' - preLoaderRoute: typeof StatusRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/serialize-form-data': { - id: '/serialize-form-data' - path: '/serialize-form-data' - fullPath: '/serialize-form-data' - preLoaderRoute: typeof SerializeFormDataRouteImport + '/consistent': { + id: '/consistent' + path: '/consistent' + fullPath: '/consistent' + preLoaderRoute: typeof ConsistentRouteImport parentRoute: typeof rootRouteImport } - '/return-null': { - id: '/return-null' - path: '/return-null' - fullPath: '/return-null' - preLoaderRoute: typeof ReturnNullRouteImport + '/dead-code-preserve': { + id: '/dead-code-preserve' + path: '/dead-code-preserve' + fullPath: '/dead-code-preserve' + preLoaderRoute: typeof DeadCodePreserveRouteImport parentRoute: typeof rootRouteImport } - '/raw-response': { - id: '/raw-response' - path: '/raw-response' - fullPath: '/raw-response' - preLoaderRoute: typeof RawResponseRouteImport + '/env-only': { + id: '/env-only' + path: '/env-only' + fullPath: '/env-only' + preLoaderRoute: typeof EnvOnlyRouteImport parentRoute: typeof rootRouteImport } - '/multipart': { - id: '/multipart' - path: '/multipart' - fullPath: '/multipart' - preLoaderRoute: typeof MultipartRouteImport + '/headers': { + id: '/headers' + path: '/headers' + fullPath: '/headers' + preLoaderRoute: typeof HeadersRouteImport parentRoute: typeof rootRouteImport } '/isomorphic-fns': { @@ -474,81 +467,81 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof IsomorphicFnsRouteImport parentRoute: typeof rootRouteImport } - '/headers': { - id: '/headers' - path: '/headers' - fullPath: '/headers' - preLoaderRoute: typeof HeadersRouteImport + '/multipart': { + id: '/multipart' + path: '/multipart' + fullPath: '/multipart' + preLoaderRoute: typeof MultipartRouteImport parentRoute: typeof rootRouteImport } - '/env-only': { - id: '/env-only' - path: '/env-only' - fullPath: '/env-only' - preLoaderRoute: typeof EnvOnlyRouteImport + '/raw-response': { + id: '/raw-response' + path: '/raw-response' + fullPath: '/raw-response' + preLoaderRoute: typeof RawResponseRouteImport parentRoute: typeof rootRouteImport } - '/dead-code-preserve': { - id: '/dead-code-preserve' - path: '/dead-code-preserve' - fullPath: '/dead-code-preserve' - preLoaderRoute: typeof DeadCodePreserveRouteImport + '/return-null': { + id: '/return-null' + path: '/return-null' + fullPath: '/return-null' + preLoaderRoute: typeof ReturnNullRouteImport parentRoute: typeof rootRouteImport } - '/consistent': { - id: '/consistent' - path: '/consistent' - fullPath: '/consistent' - preLoaderRoute: typeof ConsistentRouteImport + '/serialize-form-data': { + id: '/serialize-form-data' + path: '/serialize-form-data' + fullPath: '/serialize-form-data' + preLoaderRoute: typeof SerializeFormDataRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/status': { + id: '/status' + path: '/status' + fullPath: '/status' + preLoaderRoute: typeof StatusRouteImport parentRoute: typeof rootRouteImport } - '/redirect-test/': { - id: '/redirect-test/' - path: '/redirect-test' - fullPath: '/redirect-test/' - preLoaderRoute: typeof RedirectTestIndexRouteImport + '/submit-post-formdata': { + id: '/submit-post-formdata' + path: '/submit-post-formdata' + fullPath: '/submit-post-formdata' + preLoaderRoute: typeof SubmitPostFormdataRouteImport parentRoute: typeof rootRouteImport } - '/redirect-test-ssr/': { - id: '/redirect-test-ssr/' - path: '/redirect-test-ssr' - fullPath: '/redirect-test-ssr/' - preLoaderRoute: typeof RedirectTestSsrIndexRouteImport + '/abort-signal/': { + id: '/abort-signal/' + path: '/abort-signal' + fullPath: '/abort-signal/' + preLoaderRoute: typeof AbortSignalIndexRouteImport parentRoute: typeof rootRouteImport } - '/primitives/': { - id: '/primitives/' - path: '/primitives' - fullPath: '/primitives/' - preLoaderRoute: typeof PrimitivesIndexRouteImport + '/abort-signal/$method': { + id: '/abort-signal/$method' + path: '/abort-signal/$method' + fullPath: '/abort-signal/$method' + preLoaderRoute: typeof AbortSignalMethodRouteImport parentRoute: typeof rootRouteImport } - '/middleware/': { - id: '/middleware/' - path: '/middleware' - fullPath: '/middleware/' - preLoaderRoute: typeof MiddlewareIndexRouteImport + '/cookies/': { + id: '/cookies/' + path: '/cookies' + fullPath: '/cookies/' + preLoaderRoute: typeof CookiesIndexRouteImport parentRoute: typeof rootRouteImport } - '/function-method/': { - id: '/function-method/' - path: '/function-method' - fullPath: '/function-method/' - preLoaderRoute: typeof FunctionMethodIndexRouteImport + '/cookies/set': { + id: '/cookies/set' + path: '/cookies/set' + fullPath: '/cookies/set' + preLoaderRoute: typeof CookiesSetRouteImport parentRoute: typeof rootRouteImport } - '/function-metadata/': { - id: '/function-metadata/' - path: '/function-metadata' - fullPath: '/function-metadata/' - preLoaderRoute: typeof FunctionMetadataIndexRouteImport + '/factory/': { + id: '/factory/' + path: '/factory' + fullPath: '/factory/' + preLoaderRoute: typeof FactoryIndexRouteImport parentRoute: typeof rootRouteImport } '/formdata-redirect/': { @@ -558,39 +551,39 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof FormdataRedirectIndexRouteImport parentRoute: typeof rootRouteImport } - '/factory/': { - id: '/factory/' - path: '/factory' - fullPath: '/factory/' - preLoaderRoute: typeof FactoryIndexRouteImport + '/function-metadata/': { + id: '/function-metadata/' + path: '/function-metadata' + fullPath: '/function-metadata/' + preLoaderRoute: typeof FunctionMetadataIndexRouteImport parentRoute: typeof rootRouteImport } - '/cookies/': { - id: '/cookies/' - path: '/cookies' - fullPath: '/cookies/' - preLoaderRoute: typeof CookiesIndexRouteImport + '/function-method/': { + id: '/function-method/' + path: '/function-method' + fullPath: '/function-method/' + preLoaderRoute: typeof FunctionMethodIndexRouteImport parentRoute: typeof rootRouteImport } - '/abort-signal/': { - id: '/abort-signal/' - path: '/abort-signal' - fullPath: '/abort-signal/' - preLoaderRoute: typeof AbortSignalIndexRouteImport + '/middleware/': { + id: '/middleware/' + path: '/middleware' + fullPath: '/middleware/' + preLoaderRoute: typeof MiddlewareIndexRouteImport parentRoute: typeof rootRouteImport } - '/redirect-test/target': { - id: '/redirect-test/target' - path: '/redirect-test/target' - fullPath: '/redirect-test/target' - preLoaderRoute: typeof RedirectTestTargetRouteImport + '/middleware/client-middleware-router': { + id: '/middleware/client-middleware-router' + path: '/middleware/client-middleware-router' + fullPath: '/middleware/client-middleware-router' + preLoaderRoute: typeof MiddlewareClientMiddlewareRouterRouteImport parentRoute: typeof rootRouteImport } - '/redirect-test-ssr/target': { - id: '/redirect-test-ssr/target' - path: '/redirect-test-ssr/target' - fullPath: '/redirect-test-ssr/target' - preLoaderRoute: typeof RedirectTestSsrTargetRouteImport + '/middleware/request-middleware': { + id: '/middleware/request-middleware' + path: '/middleware/request-middleware' + fullPath: '/middleware/request-middleware' + preLoaderRoute: typeof MiddlewareRequestMiddlewareRouteImport parentRoute: typeof rootRouteImport } '/middleware/send-serverFn': { @@ -600,32 +593,39 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof MiddlewareSendServerFnRouteImport parentRoute: typeof rootRouteImport } - '/middleware/request-middleware': { - id: '/middleware/request-middleware' - path: '/middleware/request-middleware' - fullPath: '/middleware/request-middleware' - preLoaderRoute: typeof MiddlewareRequestMiddlewareRouteImport + '/primitives/': { + id: '/primitives/' + path: '/primitives' + fullPath: '/primitives/' + preLoaderRoute: typeof PrimitivesIndexRouteImport parentRoute: typeof rootRouteImport } - '/middleware/client-middleware-router': { - id: '/middleware/client-middleware-router' - path: '/middleware/client-middleware-router' - fullPath: '/middleware/client-middleware-router' - preLoaderRoute: typeof MiddlewareClientMiddlewareRouterRouteImport + '/redirect-test-ssr/': { + id: '/redirect-test-ssr/' + path: '/redirect-test-ssr' + fullPath: '/redirect-test-ssr/' + preLoaderRoute: typeof RedirectTestSsrIndexRouteImport parentRoute: typeof rootRouteImport } - '/cookies/set': { - id: '/cookies/set' - path: '/cookies/set' - fullPath: '/cookies/set' - preLoaderRoute: typeof CookiesSetRouteImport + '/redirect-test-ssr/target': { + id: '/redirect-test-ssr/target' + path: '/redirect-test-ssr/target' + fullPath: '/redirect-test-ssr/target' + preLoaderRoute: typeof RedirectTestSsrTargetRouteImport parentRoute: typeof rootRouteImport } - '/abort-signal/$method': { - id: '/abort-signal/$method' - path: '/abort-signal/$method' - fullPath: '/abort-signal/$method' - preLoaderRoute: typeof AbortSignalMethodRouteImport + '/redirect-test/': { + id: '/redirect-test/' + path: '/redirect-test' + fullPath: '/redirect-test/' + preLoaderRoute: typeof RedirectTestIndexRouteImport + parentRoute: typeof rootRouteImport + } + '/redirect-test/target': { + id: '/redirect-test/target' + path: '/redirect-test/target' + fullPath: '/redirect-test/target' + preLoaderRoute: typeof RedirectTestTargetRouteImport parentRoute: typeof rootRouteImport } '/formdata-redirect/target/$name': { diff --git a/e2e/solid-start/server-routes/src/routeTree.gen.ts b/e2e/solid-start/server-routes/src/routeTree.gen.ts index 87cd58b0ea..75cee36b01 100644 --- a/e2e/solid-start/server-routes/src/routeTree.gen.ts +++ b/e2e/solid-start/server-routes/src/routeTree.gen.ts @@ -9,16 +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 IndexRouteImport } from './routes/index' import { Route as MergeMiddlewareContextRouteImport } from './routes/merge-middleware-context' import { Route as MethodsRouteRouteImport } from './routes/methods/route' -import { Route as IndexRouteImport } from './routes/index' +import { Route as ApiMiddlewareContextRouteImport } from './routes/api/middleware-context' +import { Route as ApiOnlyAnyRouteImport } from './routes/api/only-any' import { Route as MethodsIndexRouteImport } from './routes/methods/index' import { Route as MethodsOnlyAnyRouteImport } from './routes/methods/only-any' -import { Route as ApiOnlyAnyRouteImport } from './routes/api/only-any' -import { Route as ApiMiddlewareContextRouteImport } from './routes/api/middleware-context' import { Route as ApiParamsFooRouteRouteImport } from './routes/api/params/$foo/route' import { Route as ApiParamsFooBarRouteImport } from './routes/api/params/$foo/$bar' +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) const MergeMiddlewareContextRoute = MergeMiddlewareContextRouteImport.update({ id: '/merge-middleware-context', path: '/merge-middleware-context', @@ -29,9 +34,14 @@ const MethodsRouteRoute = MethodsRouteRouteImport.update({ path: '/methods', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const ApiMiddlewareContextRoute = ApiMiddlewareContextRouteImport.update({ + id: '/api/middleware-context', + path: '/api/middleware-context', + getParentRoute: () => rootRouteImport, +} as any) +const ApiOnlyAnyRoute = ApiOnlyAnyRouteImport.update({ + id: '/api/only-any', + path: '/api/only-any', getParentRoute: () => rootRouteImport, } as any) const MethodsIndexRoute = MethodsIndexRouteImport.update({ @@ -44,16 +54,6 @@ const MethodsOnlyAnyRoute = MethodsOnlyAnyRouteImport.update({ path: '/only-any', getParentRoute: () => MethodsRouteRoute, } as any) -const ApiOnlyAnyRoute = ApiOnlyAnyRouteImport.update({ - id: '/api/only-any', - path: '/api/only-any', - getParentRoute: () => rootRouteImport, -} as any) -const ApiMiddlewareContextRoute = ApiMiddlewareContextRouteImport.update({ - id: '/api/middleware-context', - path: '/api/middleware-context', - getParentRoute: () => rootRouteImport, -} as any) const ApiParamsFooRouteRoute = ApiParamsFooRouteRouteImport.update({ id: '/api/params/$foo', path: '/api/params/$foo', @@ -144,6 +144,13 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } '/merge-middleware-context': { id: '/merge-middleware-context' path: '/merge-middleware-context' @@ -158,11 +165,18 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof MethodsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/api/middleware-context': { + id: '/api/middleware-context' + path: '/api/middleware-context' + fullPath: '/api/middleware-context' + preLoaderRoute: typeof ApiMiddlewareContextRouteImport + parentRoute: typeof rootRouteImport + } + '/api/only-any': { + id: '/api/only-any' + path: '/api/only-any' + fullPath: '/api/only-any' + preLoaderRoute: typeof ApiOnlyAnyRouteImport parentRoute: typeof rootRouteImport } '/methods/': { @@ -179,20 +193,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof MethodsOnlyAnyRouteImport parentRoute: typeof MethodsRouteRoute } - '/api/only-any': { - id: '/api/only-any' - path: '/api/only-any' - fullPath: '/api/only-any' - preLoaderRoute: typeof ApiOnlyAnyRouteImport - parentRoute: typeof rootRouteImport - } - '/api/middleware-context': { - id: '/api/middleware-context' - path: '/api/middleware-context' - fullPath: '/api/middleware-context' - preLoaderRoute: typeof ApiMiddlewareContextRouteImport - parentRoute: typeof rootRouteImport - } '/api/params/$foo': { id: '/api/params/$foo' path: '/api/params/$foo' diff --git a/e2e/solid-start/solid-query-layout-suspense/src/routeTree.gen.ts b/e2e/solid-start/solid-query-layout-suspense/src/routeTree.gen.ts index e120a08bd4..4da4991cd2 100644 --- a/e2e/solid-start/solid-query-layout-suspense/src/routeTree.gen.ts +++ b/e2e/solid-start/solid-query-layout-suspense/src/routeTree.gen.ts @@ -9,20 +9,20 @@ // 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 LayoutRouteImport } from './routes/layout' import { Route as IndexRouteImport } from './routes/index' +import { Route as LayoutRouteImport } from './routes/layout' import { Route as LayoutPage2RouteImport } from './routes/layout.page2' -const LayoutRoute = LayoutRouteImport.update({ - id: '/layout', - path: '/layout', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const LayoutRoute = LayoutRouteImport.update({ + id: '/layout', + path: '/layout', + getParentRoute: () => rootRouteImport, +} as any) const LayoutPage2Route = LayoutPage2RouteImport.update({ id: '/page2', path: '/page2', @@ -60,13 +60,6 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/layout': { - id: '/layout' - path: '/layout' - fullPath: '/layout' - preLoaderRoute: typeof LayoutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -74,6 +67,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/layout': { + id: '/layout' + path: '/layout' + fullPath: '/layout' + preLoaderRoute: typeof LayoutRouteImport + parentRoute: typeof rootRouteImport + } '/layout/page2': { id: '/layout/page2' path: '/page2' diff --git a/e2e/solid-start/spa-mode/src/routeTree.gen.ts b/e2e/solid-start/spa-mode/src/routeTree.gen.ts index 423535df71..e65284f0e5 100644 --- a/e2e/solid-start/spa-mode/src/routeTree.gen.ts +++ b/e2e/solid-start/spa-mode/src/routeTree.gen.ts @@ -9,20 +9,20 @@ // 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 PostsRouteImport } from './routes/posts' import { Route as IndexRouteImport } from './routes/index' +import { Route as PostsRouteImport } from './routes/posts' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', + getParentRoute: () => rootRouteImport, +} as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', @@ -60,13 +60,6 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -74,6 +67,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport + parentRoute: typeof rootRouteImport + } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' diff --git a/e2e/solid-start/start-manifest/src/routeTree.gen.ts b/e2e/solid-start/start-manifest/src/routeTree.gen.ts index 982f80b150..5e0ea176d5 100644 --- a/e2e/solid-start/start-manifest/src/routeTree.gen.ts +++ b/e2e/solid-start/start-manifest/src/routeTree.gen.ts @@ -9,54 +9,49 @@ // 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 SharedCRouteImport } from './routes/shared-c' -import { Route as SharedBRouteImport } from './routes/shared-b' -import { Route as SharedARouteImport } from './routes/shared-a' -import { Route as R6RouteImport } from './routes/r6' -import { Route as R5RouteImport } from './routes/r5' -import { Route as R4RouteImport } from './routes/r4' -import { Route as R3RouteImport } from './routes/r3' -import { Route as R2RouteImport } from './routes/r2' -import { Route as R1RouteImport } from './routes/r1' -import { Route as LazyCssStaticRouteImport } from './routes/lazy-css-static' -import { Route as LazyCssLazyRouteImport } from './routes/lazy-css-lazy' -import { Route as BRouteImport } from './routes/b' -import { Route as ARouteImport } from './routes/a' import { Route as IndexRouteImport } from './routes/index' +import { Route as ARouteImport } from './routes/a' +import { Route as BRouteImport } from './routes/b' +import { Route as LazyCssLazyRouteImport } from './routes/lazy-css-lazy' +import { Route as LazyCssStaticRouteImport } from './routes/lazy-css-static' +import { Route as R1RouteImport } from './routes/r1' +import { Route as R2RouteImport } from './routes/r2' +import { Route as R3RouteImport } from './routes/r3' +import { Route as R4RouteImport } from './routes/r4' +import { Route as R5RouteImport } from './routes/r5' +import { Route as R6RouteImport } from './routes/r6' +import { Route as SharedARouteImport } from './routes/shared-a' +import { Route as SharedBRouteImport } from './routes/shared-b' +import { Route as SharedCRouteImport } from './routes/shared-c' -const SharedCRoute = SharedCRouteImport.update({ - id: '/shared-c', - path: '/shared-c', - getParentRoute: () => rootRouteImport, -} as any) -const SharedBRoute = SharedBRouteImport.update({ - id: '/shared-b', - path: '/shared-b', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const SharedARoute = SharedARouteImport.update({ - id: '/shared-a', - path: '/shared-a', +const ARoute = ARouteImport.update({ + id: '/a', + path: '/a', getParentRoute: () => rootRouteImport, } as any) -const R6Route = R6RouteImport.update({ - id: '/r6', - path: '/r6', +const BRoute = BRouteImport.update({ + id: '/b', + path: '/b', getParentRoute: () => rootRouteImport, } as any) -const R5Route = R5RouteImport.update({ - id: '/r5', - path: '/r5', +const LazyCssLazyRoute = LazyCssLazyRouteImport.update({ + id: '/lazy-css-lazy', + path: '/lazy-css-lazy', getParentRoute: () => rootRouteImport, } as any) -const R4Route = R4RouteImport.update({ - id: '/r4', - path: '/r4', +const LazyCssStaticRoute = LazyCssStaticRouteImport.update({ + id: '/lazy-css-static', + path: '/lazy-css-static', getParentRoute: () => rootRouteImport, } as any) -const R3Route = R3RouteImport.update({ - id: '/r3', - path: '/r3', +const R1Route = R1RouteImport.update({ + id: '/r1', + path: '/r1', getParentRoute: () => rootRouteImport, } as any) const R2Route = R2RouteImport.update({ @@ -64,34 +59,39 @@ const R2Route = R2RouteImport.update({ path: '/r2', getParentRoute: () => rootRouteImport, } as any) -const R1Route = R1RouteImport.update({ - id: '/r1', - path: '/r1', +const R3Route = R3RouteImport.update({ + id: '/r3', + path: '/r3', getParentRoute: () => rootRouteImport, } as any) -const LazyCssStaticRoute = LazyCssStaticRouteImport.update({ - id: '/lazy-css-static', - path: '/lazy-css-static', +const R4Route = R4RouteImport.update({ + id: '/r4', + path: '/r4', getParentRoute: () => rootRouteImport, } as any) -const LazyCssLazyRoute = LazyCssLazyRouteImport.update({ - id: '/lazy-css-lazy', - path: '/lazy-css-lazy', +const R5Route = R5RouteImport.update({ + id: '/r5', + path: '/r5', getParentRoute: () => rootRouteImport, } as any) -const BRoute = BRouteImport.update({ - id: '/b', - path: '/b', +const R6Route = R6RouteImport.update({ + id: '/r6', + path: '/r6', getParentRoute: () => rootRouteImport, } as any) -const ARoute = ARouteImport.update({ - id: '/a', - path: '/a', +const SharedARoute = SharedARouteImport.update({ + id: '/shared-a', + path: '/shared-a', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const SharedBRoute = SharedBRouteImport.update({ + id: '/shared-b', + path: '/shared-b', + getParentRoute: () => rootRouteImport, +} as any) +const SharedCRoute = SharedCRouteImport.update({ + id: '/shared-c', + path: '/shared-c', getParentRoute: () => rootRouteImport, } as any) @@ -214,53 +214,46 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/shared-c': { - id: '/shared-c' - path: '/shared-c' - fullPath: '/shared-c' - preLoaderRoute: typeof SharedCRouteImport - parentRoute: typeof rootRouteImport - } - '/shared-b': { - id: '/shared-b' - path: '/shared-b' - fullPath: '/shared-b' - preLoaderRoute: typeof SharedBRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/shared-a': { - id: '/shared-a' - path: '/shared-a' - fullPath: '/shared-a' - preLoaderRoute: typeof SharedARouteImport + '/a': { + id: '/a' + path: '/a' + fullPath: '/a' + preLoaderRoute: typeof ARouteImport parentRoute: typeof rootRouteImport } - '/r6': { - id: '/r6' - path: '/r6' - fullPath: '/r6' - preLoaderRoute: typeof R6RouteImport + '/b': { + id: '/b' + path: '/b' + fullPath: '/b' + preLoaderRoute: typeof BRouteImport parentRoute: typeof rootRouteImport } - '/r5': { - id: '/r5' - path: '/r5' - fullPath: '/r5' - preLoaderRoute: typeof R5RouteImport + '/lazy-css-lazy': { + id: '/lazy-css-lazy' + path: '/lazy-css-lazy' + fullPath: '/lazy-css-lazy' + preLoaderRoute: typeof LazyCssLazyRouteImport parentRoute: typeof rootRouteImport } - '/r4': { - id: '/r4' - path: '/r4' - fullPath: '/r4' - preLoaderRoute: typeof R4RouteImport + '/lazy-css-static': { + id: '/lazy-css-static' + path: '/lazy-css-static' + fullPath: '/lazy-css-static' + preLoaderRoute: typeof LazyCssStaticRouteImport parentRoute: typeof rootRouteImport } - '/r3': { - id: '/r3' - path: '/r3' - fullPath: '/r3' - preLoaderRoute: typeof R3RouteImport + '/r1': { + id: '/r1' + path: '/r1' + fullPath: '/r1' + preLoaderRoute: typeof R1RouteImport parentRoute: typeof rootRouteImport } '/r2': { @@ -270,46 +263,53 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof R2RouteImport parentRoute: typeof rootRouteImport } - '/r1': { - id: '/r1' - path: '/r1' - fullPath: '/r1' - preLoaderRoute: typeof R1RouteImport + '/r3': { + id: '/r3' + path: '/r3' + fullPath: '/r3' + preLoaderRoute: typeof R3RouteImport parentRoute: typeof rootRouteImport } - '/lazy-css-static': { - id: '/lazy-css-static' - path: '/lazy-css-static' - fullPath: '/lazy-css-static' - preLoaderRoute: typeof LazyCssStaticRouteImport + '/r4': { + id: '/r4' + path: '/r4' + fullPath: '/r4' + preLoaderRoute: typeof R4RouteImport parentRoute: typeof rootRouteImport } - '/lazy-css-lazy': { - id: '/lazy-css-lazy' - path: '/lazy-css-lazy' - fullPath: '/lazy-css-lazy' - preLoaderRoute: typeof LazyCssLazyRouteImport + '/r5': { + id: '/r5' + path: '/r5' + fullPath: '/r5' + preLoaderRoute: typeof R5RouteImport parentRoute: typeof rootRouteImport } - '/b': { - id: '/b' - path: '/b' - fullPath: '/b' - preLoaderRoute: typeof BRouteImport + '/r6': { + id: '/r6' + path: '/r6' + fullPath: '/r6' + preLoaderRoute: typeof R6RouteImport parentRoute: typeof rootRouteImport } - '/a': { - id: '/a' - path: '/a' - fullPath: '/a' - preLoaderRoute: typeof ARouteImport + '/shared-a': { + id: '/shared-a' + path: '/shared-a' + fullPath: '/shared-a' + preLoaderRoute: typeof SharedARouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/shared-b': { + id: '/shared-b' + path: '/shared-b' + fullPath: '/shared-b' + preLoaderRoute: typeof SharedBRouteImport + parentRoute: typeof rootRouteImport + } + '/shared-c': { + id: '/shared-c' + path: '/shared-c' + fullPath: '/shared-c' + preLoaderRoute: typeof SharedCRouteImport parentRoute: typeof rootRouteImport } } diff --git a/e2e/solid-start/virtual-routes/src/routeTree.gen.ts b/e2e/solid-start/virtual-routes/src/routeTree.gen.ts index 810468171a..39172d43b4 100644 --- a/e2e/solid-start/virtual-routes/src/routeTree.gen.ts +++ b/e2e/solid-start/virtual-routes/src/routeTree.gen.ts @@ -9,83 +9,83 @@ // 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 pipeRouteImport } from './routes/pipe' -import { Route as postsPostsRouteImport } from './routes/posts/posts' -import { Route as layoutFirstLayoutRouteImport } from './routes/layout/first-layout' import { Route as homeRouteImport } from './routes/home' -import { Route as postsPostsDetailRouteImport } from './routes/posts/posts-detail' +import { Route as layoutFirstLayoutRouteImport } from './routes/layout/first-layout' +import { Route as postsPostsRouteImport } from './routes/posts/posts' +import { Route as pipeRouteImport } from './routes/pipe' import { Route as layoutSecondLayoutRouteImport } from './routes/layout/second-layout' -import { Route as postsPostsHomeRouteImport } from './routes/posts/posts-home' import { Route as ClassicHelloRouteRouteImport } from './routes/file-based-subtree/hello/route' +import { Route as postsPostsHomeRouteImport } from './routes/posts/posts-home' +import { Route as postsPostsDetailRouteImport } from './routes/posts/posts-detail' import { Route as ClassicHelloIndexRouteImport } from './routes/file-based-subtree/hello/index' -import { Route as ClassicHelloWorldRouteImport } from './routes/file-based-subtree/hello/world' import { Route as ClassicHelloUniverseRouteImport } from './routes/file-based-subtree/hello/universe' -import { Route as bRouteImport } from './routes/b' +import { Route as ClassicHelloWorldRouteImport } from './routes/file-based-subtree/hello/world' import { Route as aRouteImport } from './routes/a' +import { Route as bRouteImport } from './routes/b' -const pipeRoute = pipeRouteImport.update({ - id: '/special|pipe', - path: '/special|pipe', - getParentRoute: () => rootRouteImport, -} as any) -const postsPostsRoute = postsPostsRouteImport.update({ - id: '/posts', - path: '/posts', +const homeRoute = homeRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const layoutFirstLayoutRoute = layoutFirstLayoutRouteImport.update({ id: '/_first', getParentRoute: () => rootRouteImport, } as any) -const homeRoute = homeRouteImport.update({ - id: '/', - path: '/', +const postsPostsRoute = postsPostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const postsPostsDetailRoute = postsPostsDetailRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => postsPostsRoute, +const pipeRoute = pipeRouteImport.update({ + id: '/special|pipe', + path: '/special|pipe', + getParentRoute: () => rootRouteImport, } as any) const layoutSecondLayoutRoute = layoutSecondLayoutRouteImport.update({ id: '/_second-layout', getParentRoute: () => layoutFirstLayoutRoute, } as any) +const ClassicHelloRouteRoute = ClassicHelloRouteRouteImport.update({ + id: '/classic/hello', + path: '/classic/hello', + getParentRoute: () => rootRouteImport, +} as any) const postsPostsHomeRoute = postsPostsHomeRouteImport.update({ id: '/', path: '/', getParentRoute: () => postsPostsRoute, } as any) -const ClassicHelloRouteRoute = ClassicHelloRouteRouteImport.update({ - id: '/classic/hello', - path: '/classic/hello', - getParentRoute: () => rootRouteImport, +const postsPostsDetailRoute = postsPostsDetailRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => postsPostsRoute, } as any) const ClassicHelloIndexRoute = ClassicHelloIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => ClassicHelloRouteRoute, } as any) -const ClassicHelloWorldRoute = ClassicHelloWorldRouteImport.update({ - id: '/world', - path: '/world', - getParentRoute: () => ClassicHelloRouteRoute, -} as any) const ClassicHelloUniverseRoute = ClassicHelloUniverseRouteImport.update({ id: '/universe', path: '/universe', getParentRoute: () => ClassicHelloRouteRoute, } as any) -const bRoute = bRouteImport.update({ - id: '/route-without-file/layout-b', - path: '/route-without-file/layout-b', - getParentRoute: () => layoutSecondLayoutRoute, +const ClassicHelloWorldRoute = ClassicHelloWorldRouteImport.update({ + id: '/world', + path: '/world', + getParentRoute: () => ClassicHelloRouteRoute, } as any) const aRoute = aRouteImport.update({ id: '/route-without-file/layout-a', path: '/route-without-file/layout-a', getParentRoute: () => layoutSecondLayoutRoute, } as any) +const bRoute = bRouteImport.update({ + id: '/route-without-file/layout-b', + path: '/route-without-file/layout-b', + getParentRoute: () => layoutSecondLayoutRoute, +} as any) export interface FileRoutesByFullPath { '/': typeof homeRoute @@ -179,18 +179,11 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/special|pipe': { - id: '/special|pipe' - path: '/special|pipe' - fullPath: '/special|pipe' - preLoaderRoute: typeof pipeRouteImport - parentRoute: typeof rootRouteImport - } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof postsPostsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof homeRouteImport parentRoute: typeof rootRouteImport } '/_first': { @@ -200,19 +193,19 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof layoutFirstLayoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof homeRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof postsPostsRouteImport parentRoute: typeof rootRouteImport } - '/posts/$postId': { - id: '/posts/$postId' - path: '/$postId' - fullPath: '/posts/$postId' - preLoaderRoute: typeof postsPostsDetailRouteImport - parentRoute: typeof postsPostsRoute + '/special|pipe': { + id: '/special|pipe' + path: '/special|pipe' + fullPath: '/special|pipe' + preLoaderRoute: typeof pipeRouteImport + parentRoute: typeof rootRouteImport } '/_first/_second-layout': { id: '/_first/_second-layout' @@ -221,6 +214,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof layoutSecondLayoutRouteImport parentRoute: typeof layoutFirstLayoutRoute } + '/classic/hello': { + id: '/classic/hello' + path: '/classic/hello' + fullPath: '/classic/hello' + preLoaderRoute: typeof ClassicHelloRouteRouteImport + parentRoute: typeof rootRouteImport + } '/posts/': { id: '/posts/' path: '/' @@ -228,12 +228,12 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof postsPostsHomeRouteImport parentRoute: typeof postsPostsRoute } - '/classic/hello': { - id: '/classic/hello' - path: '/classic/hello' - fullPath: '/classic/hello' - preLoaderRoute: typeof ClassicHelloRouteRouteImport - parentRoute: typeof rootRouteImport + '/posts/$postId': { + id: '/posts/$postId' + path: '/$postId' + fullPath: '/posts/$postId' + preLoaderRoute: typeof postsPostsDetailRouteImport + parentRoute: typeof postsPostsRoute } '/classic/hello/': { id: '/classic/hello/' @@ -242,13 +242,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof ClassicHelloIndexRouteImport parentRoute: typeof ClassicHelloRouteRoute } - '/classic/hello/world': { - id: '/classic/hello/world' - path: '/world' - fullPath: '/classic/hello/world' - preLoaderRoute: typeof ClassicHelloWorldRouteImport - parentRoute: typeof ClassicHelloRouteRoute - } '/classic/hello/universe': { id: '/classic/hello/universe' path: '/universe' @@ -256,12 +249,12 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof ClassicHelloUniverseRouteImport parentRoute: typeof ClassicHelloRouteRoute } - '/_first/_second-layout/route-without-file/layout-b': { - id: '/_first/_second-layout/route-without-file/layout-b' - path: '/route-without-file/layout-b' - fullPath: '/route-without-file/layout-b' - preLoaderRoute: typeof bRouteImport - parentRoute: typeof layoutSecondLayoutRoute + '/classic/hello/world': { + id: '/classic/hello/world' + path: '/world' + fullPath: '/classic/hello/world' + preLoaderRoute: typeof ClassicHelloWorldRouteImport + parentRoute: typeof ClassicHelloRouteRoute } '/_first/_second-layout/route-without-file/layout-a': { id: '/_first/_second-layout/route-without-file/layout-a' @@ -270,6 +263,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof aRouteImport parentRoute: typeof layoutSecondLayoutRoute } + '/_first/_second-layout/route-without-file/layout-b': { + id: '/_first/_second-layout/route-without-file/layout-b' + path: '/route-without-file/layout-b' + fullPath: '/route-without-file/layout-b' + preLoaderRoute: typeof bRouteImport + parentRoute: typeof layoutSecondLayoutRoute + } } } diff --git a/e2e/solid-start/website/src/routeTree.gen.ts b/e2e/solid-start/website/src/routeTree.gen.ts index c37e78d2f1..fcc677e13e 100644 --- a/e2e/solid-start/website/src/routeTree.gen.ts +++ b/e2e/solid-start/website/src/routeTree.gen.ts @@ -10,11 +10,11 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as LibraryRouteImport } from './routes/_library' -import { Route as LibraryIndexRouteImport } from './routes/_library.index' import { Route as ProjectIndexRouteImport } from './routes/$project.index' +import { Route as LibraryIndexRouteImport } from './routes/_library.index' import { Route as LibraryProjectRouteImport } from './routes/_library.$project' -import { Route as LibraryProjectVersionIndexRouteImport } from './routes/_library.$project.$version.index' import { Route as ProjectVersionDocsIndexRouteImport } from './routes/$project.$version.docs.index' +import { Route as LibraryProjectVersionIndexRouteImport } from './routes/_library.$project.$version.index' import { Route as ProjectVersionDocsFrameworkFrameworkRouteImport } from './routes/$project.$version.docs.framework.$framework' import { Route as ProjectVersionDocsFrameworkFrameworkIndexRouteImport } from './routes/$project.$version.docs.framework.$framework.index' import { Route as ProjectVersionDocsFrameworkFrameworkSplatRouteImport } from './routes/$project.$version.docs.framework.$framework.$' @@ -24,32 +24,32 @@ const LibraryRoute = LibraryRouteImport.update({ id: '/_library', getParentRoute: () => rootRouteImport, } as any) -const LibraryIndexRoute = LibraryIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => LibraryRoute, -} as any) const ProjectIndexRoute = ProjectIndexRouteImport.update({ id: '/$project/', path: '/$project/', getParentRoute: () => rootRouteImport, } as any) +const LibraryIndexRoute = LibraryIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => LibraryRoute, +} as any) const LibraryProjectRoute = LibraryProjectRouteImport.update({ id: '/$project', path: '/$project', getParentRoute: () => LibraryRoute, } as any) +const ProjectVersionDocsIndexRoute = ProjectVersionDocsIndexRouteImport.update({ + id: '/$project/$version/docs/', + path: '/$project/$version/docs/', + getParentRoute: () => rootRouteImport, +} as any) const LibraryProjectVersionIndexRoute = LibraryProjectVersionIndexRouteImport.update({ id: '/$version/', path: '/$version/', getParentRoute: () => LibraryProjectRoute, } as any) -const ProjectVersionDocsIndexRoute = ProjectVersionDocsIndexRouteImport.update({ - id: '/$project/$version/docs/', - path: '/$project/$version/docs/', - getParentRoute: () => rootRouteImport, -} as any) const ProjectVersionDocsFrameworkFrameworkRoute = ProjectVersionDocsFrameworkFrameworkRouteImport.update({ id: '/$project/$version/docs/framework/$framework', @@ -159,13 +159,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof LibraryRouteImport parentRoute: typeof rootRouteImport } - '/_library/': { - id: '/_library/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof LibraryIndexRouteImport - parentRoute: typeof LibraryRoute - } '/$project/': { id: '/$project/' path: '/$project' @@ -173,6 +166,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof ProjectIndexRouteImport parentRoute: typeof rootRouteImport } + '/_library/': { + id: '/_library/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof LibraryIndexRouteImport + parentRoute: typeof LibraryRoute + } '/_library/$project': { id: '/_library/$project' path: '/$project' @@ -180,13 +180,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof LibraryProjectRouteImport parentRoute: typeof LibraryRoute } - '/_library/$project/$version/': { - id: '/_library/$project/$version/' - path: '/$version' - fullPath: '/$project/$version/' - preLoaderRoute: typeof LibraryProjectVersionIndexRouteImport - parentRoute: typeof LibraryProjectRoute - } '/$project/$version/docs/': { id: '/$project/$version/docs/' path: '/$project/$version/docs' @@ -194,6 +187,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof ProjectVersionDocsIndexRouteImport parentRoute: typeof rootRouteImport } + '/_library/$project/$version/': { + id: '/_library/$project/$version/' + path: '/$version' + fullPath: '/$project/$version/' + preLoaderRoute: typeof LibraryProjectVersionIndexRouteImport + parentRoute: typeof LibraryProjectRoute + } '/$project/$version/docs/framework/$framework': { id: '/$project/$version/docs/framework/$framework' path: '/$project/$version/docs/framework/$framework' diff --git a/e2e/vue-router/basepath-file-based/src/routeTree.gen.ts b/e2e/vue-router/basepath-file-based/src/routeTree.gen.ts index 82b561baf1..75b8119285 100644 --- a/e2e/vue-router/basepath-file-based/src/routeTree.gen.ts +++ b/e2e/vue-router/basepath-file-based/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' +import { Route as AboutRouteImport } from './routes/about' -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/vue-router' { interface FileRoutesByPath { - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/e2e/vue-router/basic-file-based-jsx/src/routeTree.gen.ts b/e2e/vue-router/basic-file-based-jsx/src/routeTree.gen.ts index f5cd519d7f..3922025c82 100644 --- a/e2e/vue-router/basic-file-based-jsx/src/routeTree.gen.ts +++ b/e2e/vue-router/basic-file-based-jsx/src/routeTree.gen.ts @@ -11,52 +11,45 @@ import { lazyRouteComponent } from '@tanstack/vue-router' import { Route as rootRouteImport } from './routes/__root' -import { Route as Char45824Char54620Char48124Char44397RouteImport } from './routes/대한민국' -import { Route as SfcComponentRouteImport } from './routes/sfcComponent' -import { Route as RemountDepsRouteImport } from './routes/remountDeps' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as NotRemountDepsRouteImport } from './routes/notRemountDeps' -import { Route as EditingBRouteImport } from './routes/editing-b' -import { Route as EditingARouteImport } from './routes/editing-a' -import { Route as LayoutRouteImport } from './routes/_layout' import { Route as IndexRouteImport } from './routes/index' +import { Route as LayoutRouteImport } from './routes/_layout' +import { Route as EditingARouteImport } from './routes/editing-a' +import { Route as EditingBRouteImport } from './routes/editing-b' +import { Route as NotRemountDepsRouteImport } from './routes/notRemountDeps' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as RemountDepsRouteImport } from './routes/remountDeps' +import { Route as SfcComponentRouteImport } from './routes/sfcComponent' +import { Route as Char45824Char54620Char48124Char44397RouteImport } from './routes/대한민국' +import { Route as anotherGroupOnlyrouteinsideRouteImport } from './routes/(another-group)/onlyrouteinside' +import { Route as groupLayoutRouteImport } from './routes/(group)/_layout' +import { Route as groupInsideRouteImport } from './routes/(group)/inside' +import { Route as groupLazyinsideRouteImport } from './routes/(group)/lazyinside' +import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' -import { Route as groupLazyinsideRouteImport } from './routes/(group)/lazyinside' -import { Route as groupInsideRouteImport } from './routes/(group)/inside' -import { Route as groupLayoutRouteImport } from './routes/(group)/_layout' -import { Route as anotherGroupOnlyrouteinsideRouteImport } from './routes/(another-group)/onlyrouteinside' -import { Route as PostsPostIdEditRouteImport } from './routes/posts_.$postId.edit' -import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' -import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' -import { Route as groupSubfolderInsideRouteImport } from './routes/(group)/subfolder/inside' import { Route as groupLayoutInsidelayoutRouteImport } from './routes/(group)/_layout.insidelayout' +import { Route as groupSubfolderInsideRouteImport } from './routes/(group)/subfolder/inside' +import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' +import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' +import { Route as PostsPostIdEditRouteImport } from './routes/posts_.$postId.edit' -const Char45824Char54620Char48124Char44397Route = - Char45824Char54620Char48124Char44397RouteImport.update({ - id: '/대한민국', - path: '/대한민국', - getParentRoute: () => rootRouteImport, - } as any) -const SfcComponentRoute = SfcComponentRouteImport.update({ - id: '/sfcComponent', - path: '/sfcComponent', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, -} as any).update({ - component: lazyRouteComponent( - () => import('./routes/sfcComponent.component.vue'), - 'default', - ), -}) -const RemountDepsRoute = RemountDepsRouteImport.update({ - id: '/remountDeps', - path: '/remountDeps', +} as any) +const LayoutRoute = LayoutRouteImport.update({ + id: '/_layout', getParentRoute: () => rootRouteImport, } as any) -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const EditingARoute = EditingARouteImport.update({ + id: '/editing-a', + path: '/editing-a', + getParentRoute: () => rootRouteImport, +} as any) +const EditingBRoute = EditingBRouteImport.update({ + id: '/editing-b', + path: '/editing-b', getParentRoute: () => rootRouteImport, } as any) const NotRemountDepsRoute = NotRemountDepsRouteImport.update({ @@ -64,25 +57,56 @@ const NotRemountDepsRoute = NotRemountDepsRouteImport.update({ path: '/notRemountDeps', getParentRoute: () => rootRouteImport, } as any) -const EditingBRoute = EditingBRouteImport.update({ - id: '/editing-b', - path: '/editing-b', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const EditingARoute = EditingARouteImport.update({ - id: '/editing-a', - path: '/editing-a', +const RemountDepsRoute = RemountDepsRouteImport.update({ + id: '/remountDeps', + path: '/remountDeps', getParentRoute: () => rootRouteImport, } as any) -const LayoutRoute = LayoutRouteImport.update({ - id: '/_layout', +const SfcComponentRoute = SfcComponentRouteImport.update({ + id: '/sfcComponent', + path: '/sfcComponent', + getParentRoute: () => rootRouteImport, +} as any).update({ + component: lazyRouteComponent( + () => import('./routes/sfcComponent.component.vue'), + 'default', + ), +}) +const Char45824Char54620Char48124Char44397Route = + Char45824Char54620Char48124Char44397RouteImport.update({ + id: '/대한민국', + path: '/대한민국', + getParentRoute: () => rootRouteImport, + } as any) +const anotherGroupOnlyrouteinsideRoute = + anotherGroupOnlyrouteinsideRouteImport.update({ + id: '/(another-group)/onlyrouteinside', + path: '/onlyrouteinside', + getParentRoute: () => rootRouteImport, + } as any) +const groupLayoutRoute = groupLayoutRouteImport.update({ + id: '/(group)/_layout', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const groupInsideRoute = groupInsideRouteImport.update({ + id: '/(group)/inside', + path: '/inside', + getParentRoute: () => rootRouteImport, +} as any) +const groupLazyinsideRoute = groupLazyinsideRouteImport.update({ + id: '/(group)/lazyinside', + path: '/lazyinside', getParentRoute: () => rootRouteImport, } as any) +const LayoutLayout2Route = LayoutLayout2RouteImport.update({ + id: '/_layout-2', + getParentRoute: () => LayoutRoute, +} as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -93,55 +117,31 @@ const PostsPostIdRoute = PostsPostIdRouteImport.update({ path: '/$postId', getParentRoute: () => PostsRoute, } as any) -const LayoutLayout2Route = LayoutLayout2RouteImport.update({ - id: '/_layout-2', - getParentRoute: () => LayoutRoute, -} as any) -const groupLazyinsideRoute = groupLazyinsideRouteImport.update({ - id: '/(group)/lazyinside', - path: '/lazyinside', - getParentRoute: () => rootRouteImport, -} as any) -const groupInsideRoute = groupInsideRouteImport.update({ - id: '/(group)/inside', - path: '/inside', - getParentRoute: () => rootRouteImport, +const groupLayoutInsidelayoutRoute = groupLayoutInsidelayoutRouteImport.update({ + id: '/insidelayout', + path: '/insidelayout', + getParentRoute: () => groupLayoutRoute, } as any) -const groupLayoutRoute = groupLayoutRouteImport.update({ - id: '/(group)/_layout', +const groupSubfolderInsideRoute = groupSubfolderInsideRouteImport.update({ + id: '/(group)/subfolder/inside', + path: '/subfolder/inside', getParentRoute: () => rootRouteImport, } as any) -const anotherGroupOnlyrouteinsideRoute = - anotherGroupOnlyrouteinsideRouteImport.update({ - id: '/(another-group)/onlyrouteinside', - path: '/onlyrouteinside', - getParentRoute: () => rootRouteImport, - } as any) -const PostsPostIdEditRoute = PostsPostIdEditRouteImport.update({ - id: '/posts_/$postId/edit', - path: '/posts/$postId/edit', - getParentRoute: () => rootRouteImport, +const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ + id: '/layout-a', + path: '/layout-a', + getParentRoute: () => LayoutLayout2Route, } as any) const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ id: '/layout-b', path: '/layout-b', getParentRoute: () => LayoutLayout2Route, } as any) -const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ - id: '/layout-a', - path: '/layout-a', - getParentRoute: () => LayoutLayout2Route, -} as any) -const groupSubfolderInsideRoute = groupSubfolderInsideRouteImport.update({ - id: '/(group)/subfolder/inside', - path: '/subfolder/inside', +const PostsPostIdEditRoute = PostsPostIdEditRouteImport.update({ + id: '/posts_/$postId/edit', + path: '/posts/$postId/edit', getParentRoute: () => rootRouteImport, } as any) -const groupLayoutInsidelayoutRoute = groupLayoutInsidelayoutRouteImport.update({ - id: '/insidelayout', - path: '/insidelayout', - getParentRoute: () => groupLayoutRoute, -} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -291,32 +291,32 @@ export interface RootRouteChildren { declare module '@tanstack/vue-router' { interface FileRoutesByPath { - '/대한민국': { - id: '/대한민국' - path: '/대한민국' - fullPath: '/대한민국' - preLoaderRoute: typeof Char45824Char54620Char48124Char44397RouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/sfcComponent': { - id: '/sfcComponent' - path: '/sfcComponent' - fullPath: '/sfcComponent' - preLoaderRoute: typeof SfcComponentRouteImport + '/_layout': { + id: '/_layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } - '/remountDeps': { - id: '/remountDeps' - path: '/remountDeps' - fullPath: '/remountDeps' - preLoaderRoute: typeof RemountDepsRouteImport + '/editing-a': { + id: '/editing-a' + path: '/editing-a' + fullPath: '/editing-a' + preLoaderRoute: typeof EditingARouteImport parentRoute: typeof rootRouteImport } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/editing-b': { + id: '/editing-b' + path: '/editing-b' + fullPath: '/editing-b' + preLoaderRoute: typeof EditingBRouteImport parentRoute: typeof rootRouteImport } '/notRemountDeps': { @@ -326,34 +326,69 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof NotRemountDepsRouteImport parentRoute: typeof rootRouteImport } - '/editing-b': { - id: '/editing-b' - path: '/editing-b' - fullPath: '/editing-b' - preLoaderRoute: typeof EditingBRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/editing-a': { - id: '/editing-a' - path: '/editing-a' - fullPath: '/editing-a' - preLoaderRoute: typeof EditingARouteImport + '/remountDeps': { + id: '/remountDeps' + path: '/remountDeps' + fullPath: '/remountDeps' + preLoaderRoute: typeof RemountDepsRouteImport parentRoute: typeof rootRouteImport } - '/_layout': { - id: '/_layout' + '/sfcComponent': { + id: '/sfcComponent' + path: '/sfcComponent' + fullPath: '/sfcComponent' + preLoaderRoute: typeof SfcComponentRouteImport + parentRoute: typeof rootRouteImport + } + '/대한민국': { + id: '/대한민국' + path: '/대한민국' + fullPath: '/대한민국' + preLoaderRoute: typeof Char45824Char54620Char48124Char44397RouteImport + parentRoute: typeof rootRouteImport + } + '/(another-group)/onlyrouteinside': { + id: '/(another-group)/onlyrouteinside' + path: '/onlyrouteinside' + fullPath: '/onlyrouteinside' + preLoaderRoute: typeof anotherGroupOnlyrouteinsideRouteImport + parentRoute: typeof rootRouteImport + } + '/(group)/_layout': { + id: '/(group)/_layout' path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutRouteImport + fullPath: '' + preLoaderRoute: typeof groupLayoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/(group)/inside': { + id: '/(group)/inside' + path: '/inside' + fullPath: '/inside' + preLoaderRoute: typeof groupInsideRouteImport parentRoute: typeof rootRouteImport } + '/(group)/lazyinside': { + id: '/(group)/lazyinside' + path: '/lazyinside' + fullPath: '/lazyinside' + preLoaderRoute: typeof groupLazyinsideRouteImport + parentRoute: typeof rootRouteImport + } + '/_layout/_layout-2': { + id: '/_layout/_layout-2' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutLayout2RouteImport + parentRoute: typeof LayoutRoute + } '/posts/': { id: '/posts/' path: '/' @@ -368,47 +403,26 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/_layout/_layout-2': { - id: '/_layout/_layout-2' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutLayout2RouteImport - parentRoute: typeof LayoutRoute - } - '/(group)/lazyinside': { - id: '/(group)/lazyinside' - path: '/lazyinside' - fullPath: '/lazyinside' - preLoaderRoute: typeof groupLazyinsideRouteImport - parentRoute: typeof rootRouteImport - } - '/(group)/inside': { - id: '/(group)/inside' - path: '/inside' - fullPath: '/inside' - preLoaderRoute: typeof groupInsideRouteImport - parentRoute: typeof rootRouteImport - } - '/(group)/_layout': { - id: '/(group)/_layout' - path: '' - fullPath: '' - preLoaderRoute: typeof groupLayoutRouteImport - parentRoute: typeof rootRouteImport + '/(group)/_layout/insidelayout': { + id: '/(group)/_layout/insidelayout' + path: '/insidelayout' + fullPath: '/insidelayout' + preLoaderRoute: typeof groupLayoutInsidelayoutRouteImport + parentRoute: typeof groupLayoutRoute } - '/(another-group)/onlyrouteinside': { - id: '/(another-group)/onlyrouteinside' - path: '/onlyrouteinside' - fullPath: '/onlyrouteinside' - preLoaderRoute: typeof anotherGroupOnlyrouteinsideRouteImport + '/(group)/subfolder/inside': { + id: '/(group)/subfolder/inside' + path: '/subfolder/inside' + fullPath: '/subfolder/inside' + preLoaderRoute: typeof groupSubfolderInsideRouteImport parentRoute: typeof rootRouteImport } - '/posts_/$postId/edit': { - id: '/posts_/$postId/edit' - path: '/posts/$postId/edit' - fullPath: '/posts/$postId/edit' - preLoaderRoute: typeof PostsPostIdEditRouteImport - parentRoute: typeof rootRouteImport + '/_layout/_layout-2/layout-a': { + id: '/_layout/_layout-2/layout-a' + path: '/layout-a' + fullPath: '/layout-a' + preLoaderRoute: typeof LayoutLayout2LayoutARouteImport + parentRoute: typeof LayoutLayout2Route } '/_layout/_layout-2/layout-b': { id: '/_layout/_layout-2/layout-b' @@ -417,27 +431,13 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport parentRoute: typeof LayoutLayout2Route } - '/_layout/_layout-2/layout-a': { - id: '/_layout/_layout-2/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: typeof LayoutLayout2LayoutARouteImport - parentRoute: typeof LayoutLayout2Route - } - '/(group)/subfolder/inside': { - id: '/(group)/subfolder/inside' - path: '/subfolder/inside' - fullPath: '/subfolder/inside' - preLoaderRoute: typeof groupSubfolderInsideRouteImport + '/posts_/$postId/edit': { + id: '/posts_/$postId/edit' + path: '/posts/$postId/edit' + fullPath: '/posts/$postId/edit' + preLoaderRoute: typeof PostsPostIdEditRouteImport parentRoute: typeof rootRouteImport } - '/(group)/_layout/insidelayout': { - id: '/(group)/_layout/insidelayout' - path: '/insidelayout' - fullPath: '/insidelayout' - preLoaderRoute: typeof groupLayoutInsidelayoutRouteImport - parentRoute: typeof groupLayoutRoute - } } } diff --git a/e2e/vue-router/basic-file-based-sfc/src/routeTree.gen.ts b/e2e/vue-router/basic-file-based-sfc/src/routeTree.gen.ts index fa00eb1ce0..09d62b0706 100644 --- a/e2e/vue-router/basic-file-based-sfc/src/routeTree.gen.ts +++ b/e2e/vue-router/basic-file-based-sfc/src/routeTree.gen.ts @@ -11,65 +11,53 @@ import { lazyRouteComponent } from '@tanstack/vue-router' import { Route as rootRouteImport } from './routes/__root' -import { Route as Char45824Char54620Char48124Char44397RouteImport } from './routes/대한민국' -import { Route as RemountDepsRouteImport } from './routes/remountDeps' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as NotRemountDepsRouteImport } from './routes/notRemountDeps' -import { Route as EditingBRouteImport } from './routes/editing-b' -import { Route as EditingARouteImport } from './routes/editing-a' -import { Route as LayoutRouteImport } from './routes/_layout' import { Route as IndexRouteImport } from './routes/index' +import { Route as LayoutRouteImport } from './routes/_layout' +import { Route as EditingARouteImport } from './routes/editing-a' +import { Route as EditingBRouteImport } from './routes/editing-b' +import { Route as NotRemountDepsRouteImport } from './routes/notRemountDeps' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as RemountDepsRouteImport } from './routes/remountDeps' +import { Route as Char45824Char54620Char48124Char44397RouteImport } from './routes/대한민국' +import { Route as anotherGroupOnlyrouteinsideRouteImport } from './routes/(another-group)/onlyrouteinside' +import { Route as groupLayoutRouteImport } from './routes/(group)/_layout' +import { Route as groupInsideRouteImport } from './routes/(group)/inside' +import { Route as groupLazyinsideRouteImport } from './routes/(group)/lazyinside' +import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' -import { Route as groupLazyinsideRouteImport } from './routes/(group)/lazyinside' -import { Route as groupInsideRouteImport } from './routes/(group)/inside' -import { Route as groupLayoutRouteImport } from './routes/(group)/_layout' -import { Route as anotherGroupOnlyrouteinsideRouteImport } from './routes/(another-group)/onlyrouteinside' -import { Route as PostsPostIdEditRouteImport } from './routes/posts_.$postId.edit' -import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' -import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' -import { Route as groupSubfolderInsideRouteImport } from './routes/(group)/subfolder/inside' import { Route as groupLayoutInsidelayoutRouteImport } from './routes/(group)/_layout.insidelayout' +import { Route as groupSubfolderInsideRouteImport } from './routes/(group)/subfolder/inside' +import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' +import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' +import { Route as PostsPostIdEditRouteImport } from './routes/posts_.$postId.edit' -const Char45824Char54620Char48124Char44397Route = - Char45824Char54620Char48124Char44397RouteImport.update({ - id: '/대한민국', - path: '/대한민국', - getParentRoute: () => rootRouteImport, - } as any).update({ - component: lazyRouteComponent( - () => import('./routes/대한민국.component.vue'), - 'default', - ), - }) -const RemountDepsRoute = RemountDepsRouteImport.update({ - id: '/remountDeps', - path: '/remountDeps', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any).update({ component: lazyRouteComponent( - () => import('./routes/remountDeps.component.vue'), + () => import('./routes/index.component.vue'), 'default', ), }) -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const LayoutRoute = LayoutRouteImport.update({ + id: '/_layout', getParentRoute: () => rootRouteImport, } as any).update({ component: lazyRouteComponent( - () => import('./routes/posts.component.vue'), + () => import('./routes/_layout.component.vue'), 'default', ), }) -const NotRemountDepsRoute = NotRemountDepsRouteImport.update({ - id: '/notRemountDeps', - path: '/notRemountDeps', +const EditingARoute = EditingARouteImport.update({ + id: '/editing-a', + path: '/editing-a', getParentRoute: () => rootRouteImport, } as any).update({ component: lazyRouteComponent( - () => import('./routes/notRemountDeps.component.vue'), + () => import('./routes/editing-a.component.vue'), 'default', ), }) @@ -83,89 +71,56 @@ const EditingBRoute = EditingBRouteImport.update({ 'default', ), }) -const EditingARoute = EditingARouteImport.update({ - id: '/editing-a', - path: '/editing-a', +const NotRemountDepsRoute = NotRemountDepsRouteImport.update({ + id: '/notRemountDeps', + path: '/notRemountDeps', getParentRoute: () => rootRouteImport, } as any).update({ component: lazyRouteComponent( - () => import('./routes/editing-a.component.vue'), + () => import('./routes/notRemountDeps.component.vue'), 'default', ), }) -const LayoutRoute = LayoutRouteImport.update({ - id: '/_layout', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any).update({ component: lazyRouteComponent( - () => import('./routes/_layout.component.vue'), + () => import('./routes/posts.component.vue'), 'default', ), }) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const RemountDepsRoute = RemountDepsRouteImport.update({ + id: '/remountDeps', + path: '/remountDeps', getParentRoute: () => rootRouteImport, } as any).update({ component: lazyRouteComponent( - () => import('./routes/index.component.vue'), - 'default', - ), -}) -const PostsIndexRoute = PostsIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => PostsRoute, -} as any).update({ - component: lazyRouteComponent( - () => import('./routes/posts.index.component.vue'), - 'default', - ), -}) -const PostsPostIdRoute = PostsPostIdRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => PostsRoute, -} as any).update({ - component: lazyRouteComponent( - () => import('./routes/posts.$postId.component.vue'), - 'default', - ), - errorComponent: lazyRouteComponent( - () => import('./routes/posts.$postId.errorComponent.vue'), - 'default', - ), -}) -const LayoutLayout2Route = LayoutLayout2RouteImport.update({ - id: '/_layout-2', - getParentRoute: () => LayoutRoute, -} as any).update({ - component: lazyRouteComponent( - () => import('./routes/_layout/_layout-2.component.vue'), + () => import('./routes/remountDeps.component.vue'), 'default', ), }) -const groupLazyinsideRoute = groupLazyinsideRouteImport - .update({ - id: '/(group)/lazyinside', - path: '/lazyinside', +const Char45824Char54620Char48124Char44397Route = + Char45824Char54620Char48124Char44397RouteImport.update({ + id: '/대한민국', + path: '/대한민국', getParentRoute: () => rootRouteImport, - } as any) - .update({ + } as any).update({ component: lazyRouteComponent( - () => import('./routes/(group)/lazyinside.component.vue'), + () => import('./routes/대한민국.component.vue'), 'default', ), }) -const groupInsideRoute = groupInsideRouteImport +const anotherGroupOnlyrouteinsideRoute = anotherGroupOnlyrouteinsideRouteImport .update({ - id: '/(group)/inside', - path: '/inside', + id: '/(another-group)/onlyrouteinside', + path: '/onlyrouteinside', getParentRoute: () => rootRouteImport, } as any) .update({ component: lazyRouteComponent( - () => import('./routes/(group)/inside.component.vue'), + () => import('./routes/(another-group)/onlyrouteinside.component.vue'), 'default', ), }) @@ -180,72 +135,117 @@ const groupLayoutRoute = groupLayoutRouteImport 'default', ), }) -const anotherGroupOnlyrouteinsideRoute = anotherGroupOnlyrouteinsideRouteImport +const groupInsideRoute = groupInsideRouteImport .update({ - id: '/(another-group)/onlyrouteinside', - path: '/onlyrouteinside', + id: '/(group)/inside', + path: '/inside', getParentRoute: () => rootRouteImport, } as any) .update({ component: lazyRouteComponent( - () => import('./routes/(another-group)/onlyrouteinside.component.vue'), + () => import('./routes/(group)/inside.component.vue'), 'default', ), }) -const PostsPostIdEditRoute = PostsPostIdEditRouteImport.update({ - id: '/posts_/$postId/edit', - path: '/posts/$postId/edit', - getParentRoute: () => rootRouteImport, +const groupLazyinsideRoute = groupLazyinsideRouteImport + .update({ + id: '/(group)/lazyinside', + path: '/lazyinside', + getParentRoute: () => rootRouteImport, + } as any) + .update({ + component: lazyRouteComponent( + () => import('./routes/(group)/lazyinside.component.vue'), + 'default', + ), + }) +const LayoutLayout2Route = LayoutLayout2RouteImport.update({ + id: '/_layout-2', + getParentRoute: () => LayoutRoute, } as any).update({ component: lazyRouteComponent( - () => import('./routes/posts_.$postId.edit.component.vue'), + () => import('./routes/_layout/_layout-2.component.vue'), 'default', ), }) -const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ - id: '/layout-b', - path: '/layout-b', - getParentRoute: () => LayoutLayout2Route, +const PostsIndexRoute = PostsIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => PostsRoute, } as any).update({ component: lazyRouteComponent( - () => import('./routes/_layout/_layout-2/layout-b.component.vue'), + () => import('./routes/posts.index.component.vue'), 'default', ), }) -const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ - id: '/layout-a', - path: '/layout-a', - getParentRoute: () => LayoutLayout2Route, +const PostsPostIdRoute = PostsPostIdRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => PostsRoute, } as any).update({ component: lazyRouteComponent( - () => import('./routes/_layout/_layout-2/layout-a.component.vue'), + () => import('./routes/posts.$postId.component.vue'), + 'default', + ), + errorComponent: lazyRouteComponent( + () => import('./routes/posts.$postId.errorComponent.vue'), 'default', ), }) -const groupSubfolderInsideRoute = groupSubfolderInsideRouteImport +const groupLayoutInsidelayoutRoute = groupLayoutInsidelayoutRouteImport .update({ - id: '/(group)/subfolder/inside', - path: '/subfolder/inside', - getParentRoute: () => rootRouteImport, + id: '/insidelayout', + path: '/insidelayout', + getParentRoute: () => groupLayoutRoute, } as any) .update({ component: lazyRouteComponent( - () => import('./routes/(group)/subfolder/inside.component.vue'), + () => import('./routes/(group)/_layout.insidelayout.component.vue'), 'default', ), }) -const groupLayoutInsidelayoutRoute = groupLayoutInsidelayoutRouteImport +const groupSubfolderInsideRoute = groupSubfolderInsideRouteImport .update({ - id: '/insidelayout', - path: '/insidelayout', - getParentRoute: () => groupLayoutRoute, + id: '/(group)/subfolder/inside', + path: '/subfolder/inside', + getParentRoute: () => rootRouteImport, } as any) .update({ component: lazyRouteComponent( - () => import('./routes/(group)/_layout.insidelayout.component.vue'), + () => import('./routes/(group)/subfolder/inside.component.vue'), 'default', ), }) +const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ + id: '/layout-a', + path: '/layout-a', + getParentRoute: () => LayoutLayout2Route, +} as any).update({ + component: lazyRouteComponent( + () => import('./routes/_layout/_layout-2/layout-a.component.vue'), + 'default', + ), +}) +const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ + id: '/layout-b', + path: '/layout-b', + getParentRoute: () => LayoutLayout2Route, +} as any).update({ + component: lazyRouteComponent( + () => import('./routes/_layout/_layout-2/layout-b.component.vue'), + 'default', + ), +}) +const PostsPostIdEditRoute = PostsPostIdEditRouteImport.update({ + id: '/posts_/$postId/edit', + path: '/posts/$postId/edit', + getParentRoute: () => rootRouteImport, +} as any).update({ + component: lazyRouteComponent( + () => import('./routes/posts_.$postId.edit.component.vue'), + 'default', + ), +}) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -388,25 +388,32 @@ export interface RootRouteChildren { declare module '@tanstack/vue-router' { interface FileRoutesByPath { - '/대한민국': { - id: '/대한민국' - path: '/대한민국' - fullPath: '/대한민국' - preLoaderRoute: typeof Char45824Char54620Char48124Char44397RouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/remountDeps': { - id: '/remountDeps' - path: '/remountDeps' - fullPath: '/remountDeps' - preLoaderRoute: typeof RemountDepsRouteImport + '/_layout': { + id: '/_layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/editing-a': { + id: '/editing-a' + path: '/editing-a' + fullPath: '/editing-a' + preLoaderRoute: typeof EditingARouteImport + parentRoute: typeof rootRouteImport + } + '/editing-b': { + id: '/editing-b' + path: '/editing-b' + fullPath: '/editing-b' + preLoaderRoute: typeof EditingBRouteImport parentRoute: typeof rootRouteImport } '/notRemountDeps': { @@ -416,34 +423,62 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof NotRemountDepsRouteImport parentRoute: typeof rootRouteImport } - '/editing-b': { - id: '/editing-b' - path: '/editing-b' - fullPath: '/editing-b' - preLoaderRoute: typeof EditingBRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/editing-a': { - id: '/editing-a' - path: '/editing-a' - fullPath: '/editing-a' - preLoaderRoute: typeof EditingARouteImport + '/remountDeps': { + id: '/remountDeps' + path: '/remountDeps' + fullPath: '/remountDeps' + preLoaderRoute: typeof RemountDepsRouteImport parentRoute: typeof rootRouteImport } - '/_layout': { - id: '/_layout' + '/대한민국': { + id: '/대한민국' + path: '/대한민국' + fullPath: '/대한민국' + preLoaderRoute: typeof Char45824Char54620Char48124Char44397RouteImport + parentRoute: typeof rootRouteImport + } + '/(another-group)/onlyrouteinside': { + id: '/(another-group)/onlyrouteinside' + path: '/onlyrouteinside' + fullPath: '/onlyrouteinside' + preLoaderRoute: typeof anotherGroupOnlyrouteinsideRouteImport + parentRoute: typeof rootRouteImport + } + '/(group)/_layout': { + id: '/(group)/_layout' path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutRouteImport + fullPath: '' + preLoaderRoute: typeof groupLayoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/(group)/inside': { + id: '/(group)/inside' + path: '/inside' + fullPath: '/inside' + preLoaderRoute: typeof groupInsideRouteImport parentRoute: typeof rootRouteImport } + '/(group)/lazyinside': { + id: '/(group)/lazyinside' + path: '/lazyinside' + fullPath: '/lazyinside' + preLoaderRoute: typeof groupLazyinsideRouteImport + parentRoute: typeof rootRouteImport + } + '/_layout/_layout-2': { + id: '/_layout/_layout-2' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutLayout2RouteImport + parentRoute: typeof LayoutRoute + } '/posts/': { id: '/posts/' path: '/' @@ -458,47 +493,26 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/_layout/_layout-2': { - id: '/_layout/_layout-2' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutLayout2RouteImport - parentRoute: typeof LayoutRoute - } - '/(group)/lazyinside': { - id: '/(group)/lazyinside' - path: '/lazyinside' - fullPath: '/lazyinside' - preLoaderRoute: typeof groupLazyinsideRouteImport - parentRoute: typeof rootRouteImport - } - '/(group)/inside': { - id: '/(group)/inside' - path: '/inside' - fullPath: '/inside' - preLoaderRoute: typeof groupInsideRouteImport - parentRoute: typeof rootRouteImport - } - '/(group)/_layout': { - id: '/(group)/_layout' - path: '' - fullPath: '' - preLoaderRoute: typeof groupLayoutRouteImport - parentRoute: typeof rootRouteImport + '/(group)/_layout/insidelayout': { + id: '/(group)/_layout/insidelayout' + path: '/insidelayout' + fullPath: '/insidelayout' + preLoaderRoute: typeof groupLayoutInsidelayoutRouteImport + parentRoute: typeof groupLayoutRoute } - '/(another-group)/onlyrouteinside': { - id: '/(another-group)/onlyrouteinside' - path: '/onlyrouteinside' - fullPath: '/onlyrouteinside' - preLoaderRoute: typeof anotherGroupOnlyrouteinsideRouteImport + '/(group)/subfolder/inside': { + id: '/(group)/subfolder/inside' + path: '/subfolder/inside' + fullPath: '/subfolder/inside' + preLoaderRoute: typeof groupSubfolderInsideRouteImport parentRoute: typeof rootRouteImport } - '/posts_/$postId/edit': { - id: '/posts_/$postId/edit' - path: '/posts/$postId/edit' - fullPath: '/posts/$postId/edit' - preLoaderRoute: typeof PostsPostIdEditRouteImport - parentRoute: typeof rootRouteImport + '/_layout/_layout-2/layout-a': { + id: '/_layout/_layout-2/layout-a' + path: '/layout-a' + fullPath: '/layout-a' + preLoaderRoute: typeof LayoutLayout2LayoutARouteImport + parentRoute: typeof LayoutLayout2Route } '/_layout/_layout-2/layout-b': { id: '/_layout/_layout-2/layout-b' @@ -507,27 +521,13 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport parentRoute: typeof LayoutLayout2Route } - '/_layout/_layout-2/layout-a': { - id: '/_layout/_layout-2/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: typeof LayoutLayout2LayoutARouteImport - parentRoute: typeof LayoutLayout2Route - } - '/(group)/subfolder/inside': { - id: '/(group)/subfolder/inside' - path: '/subfolder/inside' - fullPath: '/subfolder/inside' - preLoaderRoute: typeof groupSubfolderInsideRouteImport + '/posts_/$postId/edit': { + id: '/posts_/$postId/edit' + path: '/posts/$postId/edit' + fullPath: '/posts/$postId/edit' + preLoaderRoute: typeof PostsPostIdEditRouteImport parentRoute: typeof rootRouteImport } - '/(group)/_layout/insidelayout': { - id: '/(group)/_layout/insidelayout' - path: '/insidelayout' - fullPath: '/insidelayout' - preLoaderRoute: typeof groupLayoutInsidelayoutRouteImport - parentRoute: typeof groupLayoutRoute - } } } diff --git a/e2e/vue-router/basic-virtual-file-based/src/routeTree.gen.ts b/e2e/vue-router/basic-virtual-file-based/src/routeTree.gen.ts index 3798afaa01..1bae3d69a1 100644 --- a/e2e/vue-router/basic-virtual-file-based/src/routeTree.gen.ts +++ b/e2e/vue-router/basic-virtual-file-based/src/routeTree.gen.ts @@ -9,76 +9,76 @@ // 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 postsPostsRouteImport } from './routes/posts/posts' -import { Route as layoutFirstLayoutRouteImport } from './routes/layout/first-layout' import { Route as homeRouteImport } from './routes/home' -import { Route as postsPostsDetailRouteImport } from './routes/posts/posts-detail' +import { Route as layoutFirstLayoutRouteImport } from './routes/layout/first-layout' +import { Route as postsPostsRouteImport } from './routes/posts/posts' import { Route as layoutSecondLayoutRouteImport } from './routes/layout/second-layout' -import { Route as postsPostsHomeRouteImport } from './routes/posts/posts-home' import { Route as ClassicHelloRouteRouteImport } from './routes/file-based-subtree/hello/route' +import { Route as postsPostsHomeRouteImport } from './routes/posts/posts-home' +import { Route as postsPostsDetailRouteImport } from './routes/posts/posts-detail' +import { Route as aRouteImport } from './routes/a' +import { Route as bRouteImport } from './routes/b' import { Route as ClassicHelloIndexRouteImport } from './routes/file-based-subtree/hello/index' -import { Route as ClassicHelloWorldRouteImport } from './routes/file-based-subtree/hello/world' import { Route as ClassicHelloUniverseRouteImport } from './routes/file-based-subtree/hello/universe' -import { Route as bRouteImport } from './routes/b' -import { Route as aRouteImport } from './routes/a' +import { Route as ClassicHelloWorldRouteImport } from './routes/file-based-subtree/hello/world' -const postsPostsRoute = postsPostsRouteImport.update({ - id: '/posts', - path: '/posts', +const homeRoute = homeRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const layoutFirstLayoutRoute = layoutFirstLayoutRouteImport.update({ id: '/_first', getParentRoute: () => rootRouteImport, } as any) -const homeRoute = homeRouteImport.update({ - id: '/', - path: '/', +const postsPostsRoute = postsPostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const postsPostsDetailRoute = postsPostsDetailRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => postsPostsRoute, -} as any) const layoutSecondLayoutRoute = layoutSecondLayoutRouteImport.update({ id: '/_second', getParentRoute: () => layoutFirstLayoutRoute, } as any) +const ClassicHelloRouteRoute = ClassicHelloRouteRouteImport.update({ + id: '/classic/hello', + path: '/classic/hello', + getParentRoute: () => rootRouteImport, +} as any) const postsPostsHomeRoute = postsPostsHomeRouteImport.update({ id: '/', path: '/', getParentRoute: () => postsPostsRoute, } as any) -const ClassicHelloRouteRoute = ClassicHelloRouteRouteImport.update({ - id: '/classic/hello', - path: '/classic/hello', - getParentRoute: () => rootRouteImport, +const postsPostsDetailRoute = postsPostsDetailRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => postsPostsRoute, +} as any) +const aRoute = aRouteImport.update({ + id: '/layout-a', + path: '/layout-a', + getParentRoute: () => layoutSecondLayoutRoute, +} as any) +const bRoute = bRouteImport.update({ + id: '/layout-b', + path: '/layout-b', + getParentRoute: () => layoutSecondLayoutRoute, } as any) const ClassicHelloIndexRoute = ClassicHelloIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => ClassicHelloRouteRoute, } as any) -const ClassicHelloWorldRoute = ClassicHelloWorldRouteImport.update({ - id: '/world', - path: '/world', - getParentRoute: () => ClassicHelloRouteRoute, -} as any) const ClassicHelloUniverseRoute = ClassicHelloUniverseRouteImport.update({ id: '/universe', path: '/universe', getParentRoute: () => ClassicHelloRouteRoute, } as any) -const bRoute = bRouteImport.update({ - id: '/layout-b', - path: '/layout-b', - getParentRoute: () => layoutSecondLayoutRoute, -} as any) -const aRoute = aRouteImport.update({ - id: '/layout-a', - path: '/layout-a', - getParentRoute: () => layoutSecondLayoutRoute, +const ClassicHelloWorldRoute = ClassicHelloWorldRouteImport.update({ + id: '/world', + path: '/world', + getParentRoute: () => ClassicHelloRouteRoute, } as any) export interface FileRoutesByFullPath { @@ -166,11 +166,11 @@ export interface RootRouteChildren { declare module '@tanstack/vue-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof postsPostsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof homeRouteImport parentRoute: typeof rootRouteImport } '/_first': { @@ -180,20 +180,13 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof layoutFirstLayoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof homeRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof postsPostsRouteImport parentRoute: typeof rootRouteImport } - '/posts/$postId': { - id: '/posts/$postId' - path: '/$postId' - fullPath: '/posts/$postId' - preLoaderRoute: typeof postsPostsDetailRouteImport - parentRoute: typeof postsPostsRoute - } '/_first/_second': { id: '/_first/_second' path: '' @@ -201,6 +194,13 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof layoutSecondLayoutRouteImport parentRoute: typeof layoutFirstLayoutRoute } + '/classic/hello': { + id: '/classic/hello' + path: '/classic/hello' + fullPath: '/classic/hello' + preLoaderRoute: typeof ClassicHelloRouteRouteImport + parentRoute: typeof rootRouteImport + } '/posts/': { id: '/posts/' path: '/' @@ -208,12 +208,26 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof postsPostsHomeRouteImport parentRoute: typeof postsPostsRoute } - '/classic/hello': { - id: '/classic/hello' - path: '/classic/hello' - fullPath: '/classic/hello' - preLoaderRoute: typeof ClassicHelloRouteRouteImport - parentRoute: typeof rootRouteImport + '/posts/$postId': { + id: '/posts/$postId' + path: '/$postId' + fullPath: '/posts/$postId' + preLoaderRoute: typeof postsPostsDetailRouteImport + parentRoute: typeof postsPostsRoute + } + '/_first/_second/layout-a': { + id: '/_first/_second/layout-a' + path: '/layout-a' + fullPath: '/layout-a' + preLoaderRoute: typeof aRouteImport + parentRoute: typeof layoutSecondLayoutRoute + } + '/_first/_second/layout-b': { + id: '/_first/_second/layout-b' + path: '/layout-b' + fullPath: '/layout-b' + preLoaderRoute: typeof bRouteImport + parentRoute: typeof layoutSecondLayoutRoute } '/classic/hello/': { id: '/classic/hello/' @@ -222,13 +236,6 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof ClassicHelloIndexRouteImport parentRoute: typeof ClassicHelloRouteRoute } - '/classic/hello/world': { - id: '/classic/hello/world' - path: '/world' - fullPath: '/classic/hello/world' - preLoaderRoute: typeof ClassicHelloWorldRouteImport - parentRoute: typeof ClassicHelloRouteRoute - } '/classic/hello/universe': { id: '/classic/hello/universe' path: '/universe' @@ -236,19 +243,12 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof ClassicHelloUniverseRouteImport parentRoute: typeof ClassicHelloRouteRoute } - '/_first/_second/layout-b': { - id: '/_first/_second/layout-b' - path: '/layout-b' - fullPath: '/layout-b' - preLoaderRoute: typeof bRouteImport - parentRoute: typeof layoutSecondLayoutRoute - } - '/_first/_second/layout-a': { - id: '/_first/_second/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: typeof aRouteImport - parentRoute: typeof layoutSecondLayoutRoute + '/classic/hello/world': { + id: '/classic/hello/world' + path: '/world' + fullPath: '/classic/hello/world' + preLoaderRoute: typeof ClassicHelloWorldRouteImport + parentRoute: typeof ClassicHelloRouteRoute } } } diff --git a/e2e/vue-router/basic-vue-query-file-based/src/routeTree.gen.ts b/e2e/vue-router/basic-vue-query-file-based/src/routeTree.gen.ts index 9dd7ada796..0af0e21dbd 100644 --- a/e2e/vue-router/basic-vue-query-file-based/src/routeTree.gen.ts +++ b/e2e/vue-router/basic-vue-query-file-based/src/routeTree.gen.ts @@ -9,29 +9,33 @@ // 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 PostsRouteImport } from './routes/posts' -import { Route as LayoutRouteImport } from './routes/_layout' import { Route as IndexRouteImport } from './routes/index' +import { Route as LayoutRouteImport } from './routes/_layout' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' -import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' +import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const LayoutRoute = LayoutRouteImport.update({ id: '/_layout', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) +const LayoutLayout2Route = LayoutLayout2RouteImport.update({ + id: '/_layout-2', + getParentRoute: () => LayoutRoute, +} as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -42,20 +46,16 @@ const PostsPostIdRoute = PostsPostIdRouteImport.update({ path: '/$postId', getParentRoute: () => PostsRoute, } as any) -const LayoutLayout2Route = LayoutLayout2RouteImport.update({ - id: '/_layout-2', - getParentRoute: () => LayoutRoute, +const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ + id: '/layout-a', + path: '/layout-a', + getParentRoute: () => LayoutLayout2Route, } as any) const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ id: '/layout-b', path: '/layout-b', getParentRoute: () => LayoutLayout2Route, } as any) -const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ - id: '/layout-a', - path: '/layout-a', - getParentRoute: () => LayoutLayout2Route, -} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -114,11 +114,11 @@ export interface RootRouteChildren { declare module '@tanstack/vue-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/_layout': { @@ -128,13 +128,20 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } + '/_layout/_layout-2': { + id: '/_layout/_layout-2' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutLayout2RouteImport + parentRoute: typeof LayoutRoute + } '/posts/': { id: '/posts/' path: '/' @@ -149,12 +156,12 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/_layout/_layout-2': { - id: '/_layout/_layout-2' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutLayout2RouteImport - parentRoute: typeof LayoutRoute + '/_layout/_layout-2/layout-a': { + id: '/_layout/_layout-2/layout-a' + path: '/layout-a' + fullPath: '/layout-a' + preLoaderRoute: typeof LayoutLayout2LayoutARouteImport + parentRoute: typeof LayoutLayout2Route } '/_layout/_layout-2/layout-b': { id: '/_layout/_layout-2/layout-b' @@ -163,13 +170,6 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport parentRoute: typeof LayoutLayout2Route } - '/_layout/_layout-2/layout-a': { - id: '/_layout/_layout-2/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: typeof LayoutLayout2LayoutARouteImport - parentRoute: typeof LayoutLayout2Route - } } } diff --git a/e2e/vue-router/generator-cli-only/src/routeTree.gen.ts b/e2e/vue-router/generator-cli-only/src/routeTree.gen.ts index c769c841f0..decb53754c 100644 --- a/e2e/vue-router/generator-cli-only/src/routeTree.gen.ts +++ b/e2e/vue-router/generator-cli-only/src/routeTree.gen.ts @@ -9,15 +9,20 @@ // 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 IndexRouteImport } from './routes/index' import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as PostsRouteRouteImport } from './routes/posts.route' -import { Route as IndexRouteImport } from './routes/index' +import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' -import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' +import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, @@ -27,11 +32,11 @@ const PostsRouteRoute = PostsRouteRouteImport.update({ path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) +const PathlessLayoutNestedLayoutRoute = + PathlessLayoutNestedLayoutRouteImport.update({ + id: '/_nested-layout', + getParentRoute: () => PathlessLayoutRoute, + } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -42,10 +47,11 @@ const PostsPostIdRoute = PostsPostIdRouteImport.update({ path: '/$postId', getParentRoute: () => PostsRouteRoute, } as any) -const PathlessLayoutNestedLayoutRoute = - PathlessLayoutNestedLayoutRouteImport.update({ - id: '/_nested-layout', - getParentRoute: () => PathlessLayoutRoute, +const PathlessLayoutNestedLayoutRouteARoute = + PathlessLayoutNestedLayoutRouteARouteImport.update({ + id: '/route-a', + path: '/route-a', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) const PathlessLayoutNestedLayoutRouteBRoute = PathlessLayoutNestedLayoutRouteBRouteImport.update({ @@ -53,12 +59,6 @@ const PathlessLayoutNestedLayoutRouteBRoute = path: '/route-b', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) -const PathlessLayoutNestedLayoutRouteARoute = - PathlessLayoutNestedLayoutRouteARouteImport.update({ - id: '/route-a', - path: '/route-a', - getParentRoute: () => PathlessLayoutNestedLayoutRoute, - } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -117,6 +117,13 @@ export interface RootRouteChildren { declare module '@tanstack/vue-router' { interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } '/_pathlessLayout': { id: '/_pathlessLayout' path: '' @@ -131,12 +138,12 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof PostsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' + '/_pathlessLayout/_nested-layout': { + id: '/_pathlessLayout/_nested-layout' + path: '' fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport + parentRoute: typeof PathlessLayoutRoute } '/posts/': { id: '/posts/' @@ -152,12 +159,12 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRouteRoute } - '/_pathlessLayout/_nested-layout': { - id: '/_pathlessLayout/_nested-layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport - parentRoute: typeof PathlessLayoutRoute + '/_pathlessLayout/_nested-layout/route-a': { + id: '/_pathlessLayout/_nested-layout/route-a' + path: '/route-a' + fullPath: '/route-a' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport + parentRoute: typeof PathlessLayoutNestedLayoutRoute } '/_pathlessLayout/_nested-layout/route-b': { id: '/_pathlessLayout/_nested-layout/route-b' @@ -166,13 +173,6 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } - '/_pathlessLayout/_nested-layout/route-a': { - id: '/_pathlessLayout/_nested-layout/route-a' - path: '/route-a' - fullPath: '/route-a' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport - parentRoute: typeof PathlessLayoutNestedLayoutRoute - } } } diff --git a/e2e/vue-router/js-only-file-based/src/routeTree.gen.ts b/e2e/vue-router/js-only-file-based/src/routeTree.gen.ts index c769c841f0..decb53754c 100644 --- a/e2e/vue-router/js-only-file-based/src/routeTree.gen.ts +++ b/e2e/vue-router/js-only-file-based/src/routeTree.gen.ts @@ -9,15 +9,20 @@ // 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 IndexRouteImport } from './routes/index' import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as PostsRouteRouteImport } from './routes/posts.route' -import { Route as IndexRouteImport } from './routes/index' +import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' -import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' +import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, @@ -27,11 +32,11 @@ const PostsRouteRoute = PostsRouteRouteImport.update({ path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) +const PathlessLayoutNestedLayoutRoute = + PathlessLayoutNestedLayoutRouteImport.update({ + id: '/_nested-layout', + getParentRoute: () => PathlessLayoutRoute, + } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -42,10 +47,11 @@ const PostsPostIdRoute = PostsPostIdRouteImport.update({ path: '/$postId', getParentRoute: () => PostsRouteRoute, } as any) -const PathlessLayoutNestedLayoutRoute = - PathlessLayoutNestedLayoutRouteImport.update({ - id: '/_nested-layout', - getParentRoute: () => PathlessLayoutRoute, +const PathlessLayoutNestedLayoutRouteARoute = + PathlessLayoutNestedLayoutRouteARouteImport.update({ + id: '/route-a', + path: '/route-a', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) const PathlessLayoutNestedLayoutRouteBRoute = PathlessLayoutNestedLayoutRouteBRouteImport.update({ @@ -53,12 +59,6 @@ const PathlessLayoutNestedLayoutRouteBRoute = path: '/route-b', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) -const PathlessLayoutNestedLayoutRouteARoute = - PathlessLayoutNestedLayoutRouteARouteImport.update({ - id: '/route-a', - path: '/route-a', - getParentRoute: () => PathlessLayoutNestedLayoutRoute, - } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -117,6 +117,13 @@ export interface RootRouteChildren { declare module '@tanstack/vue-router' { interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } '/_pathlessLayout': { id: '/_pathlessLayout' path: '' @@ -131,12 +138,12 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof PostsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' + '/_pathlessLayout/_nested-layout': { + id: '/_pathlessLayout/_nested-layout' + path: '' fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport + parentRoute: typeof PathlessLayoutRoute } '/posts/': { id: '/posts/' @@ -152,12 +159,12 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRouteRoute } - '/_pathlessLayout/_nested-layout': { - id: '/_pathlessLayout/_nested-layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport - parentRoute: typeof PathlessLayoutRoute + '/_pathlessLayout/_nested-layout/route-a': { + id: '/_pathlessLayout/_nested-layout/route-a' + path: '/route-a' + fullPath: '/route-a' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport + parentRoute: typeof PathlessLayoutNestedLayoutRoute } '/_pathlessLayout/_nested-layout/route-b': { id: '/_pathlessLayout/_nested-layout/route-b' @@ -166,13 +173,6 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } - '/_pathlessLayout/_nested-layout/route-a': { - id: '/_pathlessLayout/_nested-layout/route-a' - path: '/route-a' - fullPath: '/route-a' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport - parentRoute: typeof PathlessLayoutNestedLayoutRoute - } } } diff --git a/e2e/vue-router/scroll-restoration-sandbox-vite/src/routeTree.gen.ts b/e2e/vue-router/scroll-restoration-sandbox-vite/src/routeTree.gen.ts index 69dda324fe..e85be2bf37 100644 --- a/e2e/vue-router/scroll-restoration-sandbox-vite/src/routeTree.gen.ts +++ b/e2e/vue-router/scroll-restoration-sandbox-vite/src/routeTree.gen.ts @@ -12,10 +12,10 @@ import { createFileRoute } from '@tanstack/vue-router' import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' -import { Route as testsPageWithSearchRouteImport } from './routes/(tests)/page-with-search' -import { Route as testsNormalPageRouteImport } from './routes/(tests)/normal-page' -import { Route as testsLazyWithLoaderPageRouteImport } from './routes/(tests)/lazy-with-loader-page' import { Route as testsLazyPageRouteImport } from './routes/(tests)/lazy-page' +import { Route as testsLazyWithLoaderPageRouteImport } from './routes/(tests)/lazy-with-loader-page' +import { Route as testsNormalPageRouteImport } from './routes/(tests)/normal-page' +import { Route as testsPageWithSearchRouteImport } from './routes/(tests)/page-with-search' const testsVirtualPageLazyRouteImport = createFileRoute( '/(tests)/virtual-page', @@ -26,23 +26,13 @@ const IndexRoute = IndexRouteImport.update({ path: '/', getParentRoute: () => rootRouteImport, } as any) -const testsVirtualPageLazyRoute = testsVirtualPageLazyRouteImport +const testsLazyPageRoute = testsLazyPageRouteImport .update({ - id: '/(tests)/virtual-page', - path: '/virtual-page', + id: '/(tests)/lazy-page', + path: '/lazy-page', getParentRoute: () => rootRouteImport, } as any) - .lazy(() => import('./routes/(tests)/virtual-page.lazy').then((d) => d.Route)) -const testsPageWithSearchRoute = testsPageWithSearchRouteImport.update({ - id: '/(tests)/page-with-search', - path: '/page-with-search', - getParentRoute: () => rootRouteImport, -} as any) -const testsNormalPageRoute = testsNormalPageRouteImport.update({ - id: '/(tests)/normal-page', - path: '/normal-page', - getParentRoute: () => rootRouteImport, -} as any) + .lazy(() => import('./routes/(tests)/lazy-page.lazy').then((d) => d.Route)) const testsLazyWithLoaderPageRoute = testsLazyWithLoaderPageRouteImport .update({ id: '/(tests)/lazy-with-loader-page', @@ -52,13 +42,23 @@ const testsLazyWithLoaderPageRoute = testsLazyWithLoaderPageRouteImport .lazy(() => import('./routes/(tests)/lazy-with-loader-page.lazy').then((d) => d.Route), ) -const testsLazyPageRoute = testsLazyPageRouteImport +const testsNormalPageRoute = testsNormalPageRouteImport.update({ + id: '/(tests)/normal-page', + path: '/normal-page', + getParentRoute: () => rootRouteImport, +} as any) +const testsPageWithSearchRoute = testsPageWithSearchRouteImport.update({ + id: '/(tests)/page-with-search', + path: '/page-with-search', + getParentRoute: () => rootRouteImport, +} as any) +const testsVirtualPageLazyRoute = testsVirtualPageLazyRouteImport .update({ - id: '/(tests)/lazy-page', - path: '/lazy-page', + id: '/(tests)/virtual-page', + path: '/virtual-page', getParentRoute: () => rootRouteImport, } as any) - .lazy(() => import('./routes/(tests)/lazy-page.lazy').then((d) => d.Route)) + .lazy(() => import('./routes/(tests)/virtual-page.lazy').then((d) => d.Route)) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -130,18 +130,18 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/virtual-page': { - id: '/(tests)/virtual-page' - path: '/virtual-page' - fullPath: '/virtual-page' - preLoaderRoute: typeof testsVirtualPageLazyRouteImport + '/(tests)/lazy-page': { + id: '/(tests)/lazy-page' + path: '/lazy-page' + fullPath: '/lazy-page' + preLoaderRoute: typeof testsLazyPageRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/page-with-search': { - id: '/(tests)/page-with-search' - path: '/page-with-search' - fullPath: '/page-with-search' - preLoaderRoute: typeof testsPageWithSearchRouteImport + '/(tests)/lazy-with-loader-page': { + id: '/(tests)/lazy-with-loader-page' + path: '/lazy-with-loader-page' + fullPath: '/lazy-with-loader-page' + preLoaderRoute: typeof testsLazyWithLoaderPageRouteImport parentRoute: typeof rootRouteImport } '/(tests)/normal-page': { @@ -151,18 +151,18 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof testsNormalPageRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/lazy-with-loader-page': { - id: '/(tests)/lazy-with-loader-page' - path: '/lazy-with-loader-page' - fullPath: '/lazy-with-loader-page' - preLoaderRoute: typeof testsLazyWithLoaderPageRouteImport + '/(tests)/page-with-search': { + id: '/(tests)/page-with-search' + path: '/page-with-search' + fullPath: '/page-with-search' + preLoaderRoute: typeof testsPageWithSearchRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/lazy-page': { - id: '/(tests)/lazy-page' - path: '/lazy-page' - fullPath: '/lazy-page' - preLoaderRoute: typeof testsLazyPageRouteImport + '/(tests)/virtual-page': { + id: '/(tests)/virtual-page' + path: '/virtual-page' + fullPath: '/virtual-page' + preLoaderRoute: typeof testsVirtualPageLazyRouteImport parentRoute: typeof rootRouteImport } } diff --git a/e2e/vue-router/view-transitions/src/routeTree.gen.ts b/e2e/vue-router/view-transitions/src/routeTree.gen.ts index 465362a7a2..04708c29a8 100644 --- a/e2e/vue-router/view-transitions/src/routeTree.gen.ts +++ b/e2e/vue-router/view-transitions/src/routeTree.gen.ts @@ -9,16 +9,16 @@ // 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 HowItWorksRouteImport } from './routes/how-it-works' +import { Route as IndexRouteImport } from './routes/index' import { Route as ExploreRouteImport } from './routes/explore' +import { Route as HowItWorksRouteImport } from './routes/how-it-works' import { Route as PostsRouteRouteImport } from './routes/posts.route' -import { Route as IndexRouteImport } from './routes/index' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -const HowItWorksRoute = HowItWorksRouteImport.update({ - id: '/how-it-works', - path: '/how-it-works', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const ExploreRoute = ExploreRouteImport.update({ @@ -26,16 +26,16 @@ const ExploreRoute = ExploreRouteImport.update({ path: '/explore', getParentRoute: () => rootRouteImport, } as any) +const HowItWorksRoute = HowItWorksRouteImport.update({ + id: '/how-it-works', + path: '/how-it-works', + getParentRoute: () => rootRouteImport, +} as any) const PostsRouteRoute = PostsRouteRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -101,11 +101,11 @@ export interface RootRouteChildren { declare module '@tanstack/vue-router' { interface FileRoutesByPath { - '/how-it-works': { - id: '/how-it-works' - path: '/how-it-works' - fullPath: '/how-it-works' - preLoaderRoute: typeof HowItWorksRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/explore': { @@ -115,6 +115,13 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof ExploreRouteImport parentRoute: typeof rootRouteImport } + '/how-it-works': { + id: '/how-it-works' + path: '/how-it-works' + fullPath: '/how-it-works' + preLoaderRoute: typeof HowItWorksRouteImport + parentRoute: typeof rootRouteImport + } '/posts': { id: '/posts' path: '/posts' @@ -122,13 +129,6 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof PostsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport - } '/posts/': { id: '/posts/' path: '/' diff --git a/e2e/vue-start/basic-auth/src/routeTree.gen.ts b/e2e/vue-start/basic-auth/src/routeTree.gen.ts index a0a61b0279..058f8674ac 100644 --- a/e2e/vue-start/basic-auth/src/routeTree.gen.ts +++ b/e2e/vue-start/basic-auth/src/routeTree.gen.ts @@ -9,23 +9,22 @@ // 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 SignupRouteImport } from './routes/signup' -import { Route as LogoutRouteImport } from './routes/logout' -import { Route as LoginRouteImport } from './routes/login' -import { Route as AuthedRouteImport } from './routes/_authed' import { Route as IndexRouteImport } from './routes/index' +import { Route as AuthedRouteImport } from './routes/_authed' +import { Route as LoginRouteImport } from './routes/login' +import { Route as LogoutRouteImport } from './routes/logout' +import { Route as SignupRouteImport } from './routes/signup' import { Route as AuthedPostsRouteImport } from './routes/_authed/posts' import { Route as AuthedPostsIndexRouteImport } from './routes/_authed/posts.index' import { Route as AuthedPostsPostIdRouteImport } from './routes/_authed/posts.$postId' -const SignupRoute = SignupRouteImport.update({ - id: '/signup', - path: '/signup', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const LogoutRoute = LogoutRouteImport.update({ - id: '/logout', - path: '/logout', +const AuthedRoute = AuthedRouteImport.update({ + id: '/_authed', getParentRoute: () => rootRouteImport, } as any) const LoginRoute = LoginRouteImport.update({ @@ -33,13 +32,14 @@ const LoginRoute = LoginRouteImport.update({ path: '/login', getParentRoute: () => rootRouteImport, } as any) -const AuthedRoute = AuthedRouteImport.update({ - id: '/_authed', +const LogoutRoute = LogoutRouteImport.update({ + id: '/logout', + path: '/logout', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const SignupRoute = SignupRouteImport.update({ + id: '/signup', + path: '/signup', getParentRoute: () => rootRouteImport, } as any) const AuthedPostsRoute = AuthedPostsRouteImport.update({ @@ -120,18 +120,18 @@ export interface RootRouteChildren { declare module '@tanstack/vue-router' { interface FileRoutesByPath { - '/signup': { - id: '/signup' - path: '/signup' - fullPath: '/signup' - preLoaderRoute: typeof SignupRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/logout': { - id: '/logout' - path: '/logout' - fullPath: '/logout' - preLoaderRoute: typeof LogoutRouteImport + '/_authed': { + id: '/_authed' + path: '' + fullPath: '/' + preLoaderRoute: typeof AuthedRouteImport parentRoute: typeof rootRouteImport } '/login': { @@ -141,18 +141,18 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof LoginRouteImport parentRoute: typeof rootRouteImport } - '/_authed': { - id: '/_authed' - path: '' - fullPath: '/' - preLoaderRoute: typeof AuthedRouteImport + '/logout': { + id: '/logout' + path: '/logout' + fullPath: '/logout' + preLoaderRoute: typeof LogoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/signup': { + id: '/signup' + path: '/signup' + fullPath: '/signup' + preLoaderRoute: typeof SignupRouteImport parentRoute: typeof rootRouteImport } '/_authed/posts': { diff --git a/e2e/vue-start/basic-cloudflare/src/routeTree.gen.ts b/e2e/vue-start/basic-cloudflare/src/routeTree.gen.ts index 30451e7ab7..c52a88360b 100644 --- a/e2e/vue-start/basic-cloudflare/src/routeTree.gen.ts +++ b/e2e/vue-start/basic-cloudflare/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 StaticRouteImport } from './routes/static' import { Route as IndexRouteImport } from './routes/index' +import { Route as StaticRouteImport } from './routes/static' -const StaticRoute = StaticRouteImport.update({ - id: '/static', - path: '/static', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const StaticRoute = StaticRouteImport.update({ + id: '/static', + path: '/static', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/vue-router' { interface FileRoutesByPath { - '/static': { - id: '/static' - path: '/static' - fullPath: '/static' - preLoaderRoute: typeof StaticRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/static': { + id: '/static' + path: '/static' + fullPath: '/static' + preLoaderRoute: typeof StaticRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/e2e/vue-start/basic-vue-query/src/routeTree.gen.ts b/e2e/vue-start/basic-vue-query/src/routeTree.gen.ts index 2c514d54ea..10e8a56450 100644 --- a/e2e/vue-start/basic-vue-query/src/routeTree.gen.ts +++ b/e2e/vue-start/basic-vue-query/src/routeTree.gen.ts @@ -9,32 +9,36 @@ // 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 UsersRouteImport } from './routes/users' -import { Route as SuspenseTransitionRouteImport } from './routes/suspense-transition' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as DeferredRouteImport } from './routes/deferred' -import { Route as LayoutRouteImport } from './routes/_layout' import { Route as IndexRouteImport } from './routes/index' -import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as LayoutRouteImport } from './routes/_layout' +import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as SuspenseTransitionRouteImport } from './routes/suspense-transition' +import { Route as UsersRouteImport } from './routes/users' +import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' +import { Route as ApiUsersRouteImport } from './routes/api.users' import { Route as PostsIndexRouteImport } from './routes/posts.index' -import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as ApiUsersRouteImport } from './routes/api.users' -import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' -import { Route as TransitionCountQueryRouteImport } from './routes/transition/count/query' -import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -import { Route as ApiUsersIdRouteImport } from './routes/api/users.$id' -import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' +import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' +import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' +import { Route as ApiUsersIdRouteImport } from './routes/api/users.$id' +import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' +import { Route as TransitionCountQueryRouteImport } from './routes/transition/count/query' -const UsersRoute = UsersRouteImport.update({ - id: '/users', - path: '/users', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const SuspenseTransitionRoute = SuspenseTransitionRouteImport.update({ - id: '/suspense-transition', - path: '/suspense-transition', +const LayoutRoute = LayoutRouteImport.update({ + id: '/_layout', + getParentRoute: () => rootRouteImport, +} as any) +const DeferredRoute = DeferredRouteImport.update({ + id: '/deferred', + path: '/deferred', getParentRoute: () => rootRouteImport, } as any) const PostsRoute = PostsRouteImport.update({ @@ -42,73 +46,69 @@ const PostsRoute = PostsRouteImport.update({ path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const DeferredRoute = DeferredRouteImport.update({ - id: '/deferred', - path: '/deferred', +const SuspenseTransitionRoute = SuspenseTransitionRouteImport.update({ + id: '/suspense-transition', + path: '/suspense-transition', getParentRoute: () => rootRouteImport, } as any) -const LayoutRoute = LayoutRouteImport.update({ - id: '/_layout', +const UsersRoute = UsersRouteImport.update({ + id: '/users', + path: '/users', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, +const LayoutLayout2Route = LayoutLayout2RouteImport.update({ + id: '/_layout-2', + getParentRoute: () => LayoutRoute, } as any) -const UsersIndexRoute = UsersIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => UsersRoute, +const ApiUsersRoute = ApiUsersRouteImport.update({ + id: '/api/users', + path: '/api/users', + getParentRoute: () => rootRouteImport, } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRoute, } as any) -const UsersUserIdRoute = UsersUserIdRouteImport.update({ - id: '/$userId', - path: '/$userId', - getParentRoute: () => UsersRoute, -} as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRoute, } as any) -const ApiUsersRoute = ApiUsersRouteImport.update({ - id: '/api/users', - path: '/api/users', - getParentRoute: () => rootRouteImport, +const UsersIndexRoute = UsersIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => UsersRoute, } as any) -const LayoutLayout2Route = LayoutLayout2RouteImport.update({ - id: '/_layout-2', - getParentRoute: () => LayoutRoute, +const UsersUserIdRoute = UsersUserIdRouteImport.update({ + id: '/$userId', + path: '/$userId', + getParentRoute: () => UsersRoute, } as any) -const TransitionCountQueryRoute = TransitionCountQueryRouteImport.update({ - id: '/transition/count/query', - path: '/transition/count/query', - getParentRoute: () => rootRouteImport, +const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ + id: '/layout-a', + path: '/layout-a', + getParentRoute: () => LayoutLayout2Route, } as any) -const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ - id: '/posts_/$postId/deep', - path: '/posts/$postId/deep', - getParentRoute: () => rootRouteImport, +const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ + id: '/layout-b', + path: '/layout-b', + getParentRoute: () => LayoutLayout2Route, } as any) const ApiUsersIdRoute = ApiUsersIdRouteImport.update({ id: '/$id', path: '/$id', getParentRoute: () => ApiUsersRoute, } as any) -const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ - id: '/layout-b', - path: '/layout-b', - getParentRoute: () => LayoutLayout2Route, +const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ + id: '/posts_/$postId/deep', + path: '/posts/$postId/deep', + getParentRoute: () => rootRouteImport, } as any) -const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ - id: '/layout-a', - path: '/layout-a', - getParentRoute: () => LayoutLayout2Route, +const TransitionCountQueryRoute = TransitionCountQueryRouteImport.update({ + id: '/transition/count/query', + path: '/transition/count/query', + getParentRoute: () => rootRouteImport, } as any) export interface FileRoutesByFullPath { @@ -231,18 +231,25 @@ export interface RootRouteChildren { declare module '@tanstack/vue-router' { interface FileRoutesByPath { - '/users': { - id: '/users' - path: '/users' - fullPath: '/users' - preLoaderRoute: typeof UsersRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/suspense-transition': { - id: '/suspense-transition' - path: '/suspense-transition' - fullPath: '/suspense-transition' - preLoaderRoute: typeof SuspenseTransitionRouteImport + '/_layout': { + id: '/_layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutRouteImport + parentRoute: typeof rootRouteImport + } + '/deferred': { + id: '/deferred' + path: '/deferred' + fullPath: '/deferred' + preLoaderRoute: typeof DeferredRouteImport parentRoute: typeof rootRouteImport } '/posts': { @@ -252,33 +259,33 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/deferred': { - id: '/deferred' - path: '/deferred' - fullPath: '/deferred' - preLoaderRoute: typeof DeferredRouteImport + '/suspense-transition': { + id: '/suspense-transition' + path: '/suspense-transition' + fullPath: '/suspense-transition' + preLoaderRoute: typeof SuspenseTransitionRouteImport parentRoute: typeof rootRouteImport } - '/_layout': { - id: '/_layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutRouteImport + '/users': { + id: '/users' + path: '/users' + fullPath: '/users' + preLoaderRoute: typeof UsersRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' + '/_layout/_layout-2': { + id: '/_layout/_layout-2' + path: '' fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport + preLoaderRoute: typeof LayoutLayout2RouteImport + parentRoute: typeof LayoutRoute } - '/users/': { - id: '/users/' - path: '/' - fullPath: '/users/' - preLoaderRoute: typeof UsersIndexRouteImport - parentRoute: typeof UsersRoute + '/api/users': { + id: '/api/users' + path: '/api/users' + fullPath: '/api/users' + preLoaderRoute: typeof ApiUsersRouteImport + parentRoute: typeof rootRouteImport } '/posts/': { id: '/posts/' @@ -287,13 +294,6 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRoute } - '/users/$userId': { - id: '/users/$userId' - path: '/$userId' - fullPath: '/users/$userId' - preLoaderRoute: typeof UsersUserIdRouteImport - parentRoute: typeof UsersRoute - } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' @@ -301,33 +301,33 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/api/users': { - id: '/api/users' - path: '/api/users' - fullPath: '/api/users' - preLoaderRoute: typeof ApiUsersRouteImport - parentRoute: typeof rootRouteImport + '/users/': { + id: '/users/' + path: '/' + fullPath: '/users/' + preLoaderRoute: typeof UsersIndexRouteImport + parentRoute: typeof UsersRoute } - '/_layout/_layout-2': { - id: '/_layout/_layout-2' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutLayout2RouteImport - parentRoute: typeof LayoutRoute + '/users/$userId': { + id: '/users/$userId' + path: '/$userId' + fullPath: '/users/$userId' + preLoaderRoute: typeof UsersUserIdRouteImport + parentRoute: typeof UsersRoute } - '/transition/count/query': { - id: '/transition/count/query' - path: '/transition/count/query' - fullPath: '/transition/count/query' - preLoaderRoute: typeof TransitionCountQueryRouteImport - parentRoute: typeof rootRouteImport + '/_layout/_layout-2/layout-a': { + id: '/_layout/_layout-2/layout-a' + path: '/layout-a' + fullPath: '/layout-a' + preLoaderRoute: typeof LayoutLayout2LayoutARouteImport + parentRoute: typeof LayoutLayout2Route } - '/posts_/$postId/deep': { - id: '/posts_/$postId/deep' - path: '/posts/$postId/deep' - fullPath: '/posts/$postId/deep' - preLoaderRoute: typeof PostsPostIdDeepRouteImport - parentRoute: typeof rootRouteImport + '/_layout/_layout-2/layout-b': { + id: '/_layout/_layout-2/layout-b' + path: '/layout-b' + fullPath: '/layout-b' + preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport + parentRoute: typeof LayoutLayout2Route } '/api/users/$id': { id: '/api/users/$id' @@ -336,19 +336,19 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof ApiUsersIdRouteImport parentRoute: typeof ApiUsersRoute } - '/_layout/_layout-2/layout-b': { - id: '/_layout/_layout-2/layout-b' - path: '/layout-b' - fullPath: '/layout-b' - preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport - parentRoute: typeof LayoutLayout2Route + '/posts_/$postId/deep': { + id: '/posts_/$postId/deep' + path: '/posts/$postId/deep' + fullPath: '/posts/$postId/deep' + preLoaderRoute: typeof PostsPostIdDeepRouteImport + parentRoute: typeof rootRouteImport } - '/_layout/_layout-2/layout-a': { - id: '/_layout/_layout-2/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: typeof LayoutLayout2LayoutARouteImport - parentRoute: typeof LayoutLayout2Route + '/transition/count/query': { + id: '/transition/count/query' + path: '/transition/count/query' + fullPath: '/transition/count/query' + preLoaderRoute: typeof TransitionCountQueryRouteImport + parentRoute: typeof rootRouteImport } } } diff --git a/e2e/vue-start/basic/src/routeTree.gen.ts b/e2e/vue-start/basic/src/routeTree.gen.ts index 016a292835..7fbcd192b6 100644 --- a/e2e/vue-start/basic/src/routeTree.gen.ts +++ b/e2e/vue-start/basic/src/routeTree.gen.ts @@ -9,87 +9,81 @@ // 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 UsersRouteImport } from './routes/users' -import { Route as StreamRouteImport } from './routes/stream' -import { Route as ScriptsRouteImport } from './routes/scripts' -import { Route as RawStreamRouteImport } from './routes/raw-stream' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as LinksRouteImport } from './routes/links' -import { Route as InlineScriptsRouteImport } from './routes/inline-scripts' -import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as IndexRouteImport } from './routes/index' import { Route as LayoutRouteImport } from './routes/_layout' -import { Route as SpecialCharsRouteRouteImport } from './routes/specialChars/route' -import { Route as SearchParamsRouteRouteImport } from './routes/search-params/route' +import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as InlineScriptsRouteImport } from './routes/inline-scripts' +import { Route as LinksRouteImport } from './routes/links' import { Route as NotFoundRouteRouteImport } from './routes/not-found/route' -import { Route as IndexRouteImport } from './routes/index' -import { Route as UsersIndexRouteImport } from './routes/users.index' -import { Route as SearchParamsIndexRouteImport } from './routes/search-params/index' -import { Route as RedirectIndexRouteImport } from './routes/redirect/index' -import { Route as RawStreamIndexRouteImport } from './routes/raw-stream/index' -import { Route as PostsIndexRouteImport } from './routes/posts.index' -import { Route as NotFoundIndexRouteImport } from './routes/not-found/index' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as RawStreamRouteImport } from './routes/raw-stream' +import { Route as ScriptsRouteImport } from './routes/scripts' +import { Route as SearchParamsRouteRouteImport } from './routes/search-params/route' +import { Route as SpecialCharsRouteRouteImport } from './routes/specialChars/route' +import { Route as StreamRouteImport } from './routes/stream' +import { Route as UsersRouteImport } from './routes/users' +import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' +import { Route as ApiUsersRouteImport } from './routes/api/users' import { Route as MultiCookieRedirectIndexRouteImport } from './routes/multi-cookie-redirect/index' -import { Route as UsersUserIdRouteImport } from './routes/users.$userId' -import { Route as SpecialCharsChar45824Char54620Char48124Char44397RouteImport } from './routes/specialChars/대한민국' -import { Route as SpecialCharsSearchRouteImport } from './routes/specialChars/search' -import { Route as SpecialCharsHashRouteImport } from './routes/specialChars/hash' -import { Route as SpecialCharsParamRouteImport } from './routes/specialChars/$param' -import { Route as SearchParamsLoaderThrowsRedirectRouteImport } from './routes/search-params/loader-throws-redirect' -import { Route as SearchParamsDefaultRouteImport } from './routes/search-params/default' -import { Route as RedirectTargetRouteImport } from './routes/redirect/$target' -import { Route as RawStreamSsrTextHintRouteImport } from './routes/raw-stream/ssr-text-hint' -import { Route as RawStreamSsrSingleRouteImport } from './routes/raw-stream/ssr-single' -import { Route as RawStreamSsrMultipleRouteImport } from './routes/raw-stream/ssr-multiple' -import { Route as RawStreamSsrMixedRouteImport } from './routes/raw-stream/ssr-mixed' -import { Route as RawStreamSsrBinaryHintRouteImport } from './routes/raw-stream/ssr-binary-hint' -import { Route as RawStreamClientCallRouteImport } from './routes/raw-stream/client-call' -import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as NotFoundViaLoaderRouteImport } from './routes/not-found/via-loader' -import { Route as NotFoundViaBeforeLoadTargetRootRouteImport } from './routes/not-found/via-beforeLoad-target-root' -import { Route as NotFoundViaBeforeLoadRouteImport } from './routes/not-found/via-beforeLoad' import { Route as MultiCookieRedirectTargetRouteImport } from './routes/multi-cookie-redirect/target' -import { Route as ApiUsersRouteImport } from './routes/api/users' -import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' -import { Route as SpecialCharsMalformedRouteRouteImport } from './routes/specialChars/malformed/route' +import { Route as NotFoundIndexRouteImport } from './routes/not-found/index' import { Route as NotFoundParentBoundaryRouteRouteImport } from './routes/not-found/parent-boundary/route' +import { Route as NotFoundViaBeforeLoadRouteImport } from './routes/not-found/via-beforeLoad' +import { Route as NotFoundViaBeforeLoadTargetRootRouteImport } from './routes/not-found/via-beforeLoad-target-root' +import { Route as NotFoundViaLoaderRouteImport } from './routes/not-found/via-loader' +import { Route as PostsIndexRouteImport } from './routes/posts.index' +import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' +import { Route as RawStreamIndexRouteImport } from './routes/raw-stream/index' +import { Route as RawStreamClientCallRouteImport } from './routes/raw-stream/client-call' +import { Route as RawStreamSsrBinaryHintRouteImport } from './routes/raw-stream/ssr-binary-hint' +import { Route as RawStreamSsrMixedRouteImport } from './routes/raw-stream/ssr-mixed' +import { Route as RawStreamSsrMultipleRouteImport } from './routes/raw-stream/ssr-multiple' +import { Route as RawStreamSsrSingleRouteImport } from './routes/raw-stream/ssr-single' +import { Route as RawStreamSsrTextHintRouteImport } from './routes/raw-stream/ssr-text-hint' +import { Route as RedirectIndexRouteImport } from './routes/redirect/index' +import { Route as RedirectTargetRouteImport } from './routes/redirect/$target' +import { Route as SearchParamsIndexRouteImport } from './routes/search-params/index' +import { Route as SearchParamsDefaultRouteImport } from './routes/search-params/default' +import { Route as SearchParamsLoaderThrowsRedirectRouteImport } from './routes/search-params/loader-throws-redirect' +import { Route as SpecialCharsParamRouteImport } from './routes/specialChars/$param' +import { Route as SpecialCharsHashRouteImport } from './routes/specialChars/hash' +import { Route as SpecialCharsMalformedRouteRouteImport } from './routes/specialChars/malformed/route' +import { Route as SpecialCharsSearchRouteImport } from './routes/specialChars/search' +import { Route as SpecialCharsChar45824Char54620Char48124Char44397RouteImport } from './routes/specialChars/대한민국' +import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as UsersUserIdRouteImport } from './routes/users.$userId' +import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' +import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' +import { Route as ApiUsersUserIdRouteImport } from './routes/api/users.$userId' +import { Route as NotFoundParentBoundaryViaBeforeLoadRouteImport } from './routes/not-found/parent-boundary/via-beforeLoad' +import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' import { Route as RedirectTargetIndexRouteImport } from './routes/redirect/$target/index' -import { Route as SpecialCharsMalformedSearchRouteImport } from './routes/specialChars/malformed/search' -import { Route as SpecialCharsMalformedParamRouteImport } from './routes/specialChars/malformed/$param' -import { Route as RedirectTargetViaLoaderRouteImport } from './routes/redirect/$target/via-loader' import { Route as RedirectTargetViaBeforeLoadRouteImport } from './routes/redirect/$target/via-beforeLoad' -import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -import { Route as NotFoundParentBoundaryViaBeforeLoadRouteImport } from './routes/not-found/parent-boundary/via-beforeLoad' -import { Route as ApiUsersUserIdRouteImport } from './routes/api/users.$userId' -import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' -import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' +import { Route as RedirectTargetViaLoaderRouteImport } from './routes/redirect/$target/via-loader' +import { Route as SpecialCharsMalformedParamRouteImport } from './routes/specialChars/malformed/$param' +import { Route as SpecialCharsMalformedSearchRouteImport } from './routes/specialChars/malformed/search' import { Route as RedirectTargetServerFnIndexRouteImport } from './routes/redirect/$target/serverFn/index' -import { Route as RedirectTargetServerFnViaUseServerFnRouteImport } from './routes/redirect/$target/serverFn/via-useServerFn' -import { Route as RedirectTargetServerFnViaLoaderRouteImport } from './routes/redirect/$target/serverFn/via-loader' import { Route as RedirectTargetServerFnViaBeforeLoadRouteImport } from './routes/redirect/$target/serverFn/via-beforeLoad' +import { Route as RedirectTargetServerFnViaLoaderRouteImport } from './routes/redirect/$target/serverFn/via-loader' +import { Route as RedirectTargetServerFnViaUseServerFnRouteImport } from './routes/redirect/$target/serverFn/via-useServerFn' -const UsersRoute = UsersRouteImport.update({ - id: '/users', - path: '/users', - getParentRoute: () => rootRouteImport, -} as any) -const StreamRoute = StreamRouteImport.update({ - id: '/stream', - path: '/stream', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const ScriptsRoute = ScriptsRouteImport.update({ - id: '/scripts', - path: '/scripts', +const LayoutRoute = LayoutRouteImport.update({ + id: '/_layout', getParentRoute: () => rootRouteImport, } as any) -const RawStreamRoute = RawStreamRouteImport.update({ - id: '/raw-stream', - path: '/raw-stream', +const DeferredRoute = DeferredRouteImport.update({ + id: '/deferred', + path: '/deferred', getParentRoute: () => rootRouteImport, } as any) -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const InlineScriptsRoute = InlineScriptsRouteImport.update({ + id: '/inline-scripts', + path: '/inline-scripts', getParentRoute: () => rootRouteImport, } as any) const LinksRoute = LinksRouteImport.update({ @@ -97,23 +91,24 @@ const LinksRoute = LinksRouteImport.update({ path: '/links', getParentRoute: () => rootRouteImport, } as any) -const InlineScriptsRoute = InlineScriptsRouteImport.update({ - id: '/inline-scripts', - path: '/inline-scripts', +const NotFoundRouteRoute = NotFoundRouteRouteImport.update({ + id: '/not-found', + path: '/not-found', getParentRoute: () => rootRouteImport, } as any) -const DeferredRoute = DeferredRouteImport.update({ - id: '/deferred', - path: '/deferred', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const LayoutRoute = LayoutRouteImport.update({ - id: '/_layout', +const RawStreamRoute = RawStreamRouteImport.update({ + id: '/raw-stream', + path: '/raw-stream', getParentRoute: () => rootRouteImport, } as any) -const SpecialCharsRouteRoute = SpecialCharsRouteRouteImport.update({ - id: '/specialChars', - path: '/specialChars', +const ScriptsRoute = ScriptsRouteImport.update({ + id: '/scripts', + path: '/scripts', getParentRoute: () => rootRouteImport, } as any) const SearchParamsRouteRoute = SearchParamsRouteRouteImport.update({ @@ -121,102 +116,97 @@ const SearchParamsRouteRoute = SearchParamsRouteRouteImport.update({ path: '/search-params', getParentRoute: () => rootRouteImport, } as any) -const NotFoundRouteRoute = NotFoundRouteRouteImport.update({ - id: '/not-found', - path: '/not-found', +const SpecialCharsRouteRoute = SpecialCharsRouteRouteImport.update({ + id: '/specialChars', + path: '/specialChars', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const StreamRoute = StreamRouteImport.update({ + id: '/stream', + path: '/stream', getParentRoute: () => rootRouteImport, } as any) -const UsersIndexRoute = UsersIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => UsersRoute, +const UsersRoute = UsersRouteImport.update({ + id: '/users', + path: '/users', + getParentRoute: () => rootRouteImport, } as any) -const SearchParamsIndexRoute = SearchParamsIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => SearchParamsRouteRoute, +const LayoutLayout2Route = LayoutLayout2RouteImport.update({ + id: '/_layout-2', + getParentRoute: () => LayoutRoute, } as any) -const RedirectIndexRoute = RedirectIndexRouteImport.update({ - id: '/redirect/', - path: '/redirect/', +const ApiUsersRoute = ApiUsersRouteImport.update({ + id: '/api/users', + path: '/api/users', getParentRoute: () => rootRouteImport, } as any) -const RawStreamIndexRoute = RawStreamIndexRouteImport.update({ +const MultiCookieRedirectIndexRoute = + MultiCookieRedirectIndexRouteImport.update({ + id: '/multi-cookie-redirect/', + path: '/multi-cookie-redirect/', + getParentRoute: () => rootRouteImport, + } as any) +const MultiCookieRedirectTargetRoute = + MultiCookieRedirectTargetRouteImport.update({ + id: '/multi-cookie-redirect/target', + path: '/multi-cookie-redirect/target', + getParentRoute: () => rootRouteImport, + } as any) +const NotFoundIndexRoute = NotFoundIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => RawStreamRoute, + getParentRoute: () => NotFoundRouteRoute, +} as any) +const NotFoundParentBoundaryRouteRoute = + NotFoundParentBoundaryRouteRouteImport.update({ + id: '/parent-boundary', + path: '/parent-boundary', + getParentRoute: () => NotFoundRouteRoute, + } as any) +const NotFoundViaBeforeLoadRoute = NotFoundViaBeforeLoadRouteImport.update({ + id: '/via-beforeLoad', + path: '/via-beforeLoad', + getParentRoute: () => NotFoundRouteRoute, +} as any) +const NotFoundViaBeforeLoadTargetRootRoute = + NotFoundViaBeforeLoadTargetRootRouteImport.update({ + id: '/via-beforeLoad-target-root', + path: '/via-beforeLoad-target-root', + getParentRoute: () => NotFoundRouteRoute, + } as any) +const NotFoundViaLoaderRoute = NotFoundViaLoaderRouteImport.update({ + id: '/via-loader', + path: '/via-loader', + getParentRoute: () => NotFoundRouteRoute, } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRoute, } as any) -const NotFoundIndexRoute = NotFoundIndexRouteImport.update({ +const PostsPostIdRoute = PostsPostIdRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => PostsRoute, +} as any) +const RawStreamIndexRoute = RawStreamIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => NotFoundRouteRoute, + getParentRoute: () => RawStreamRoute, } as any) -const MultiCookieRedirectIndexRoute = - MultiCookieRedirectIndexRouteImport.update({ - id: '/multi-cookie-redirect/', - path: '/multi-cookie-redirect/', - getParentRoute: () => rootRouteImport, - } as any) -const UsersUserIdRoute = UsersUserIdRouteImport.update({ - id: '/$userId', - path: '/$userId', - getParentRoute: () => UsersRoute, +const RawStreamClientCallRoute = RawStreamClientCallRouteImport.update({ + id: '/client-call', + path: '/client-call', + getParentRoute: () => RawStreamRoute, } as any) -const SpecialCharsChar45824Char54620Char48124Char44397Route = - SpecialCharsChar45824Char54620Char48124Char44397RouteImport.update({ - id: '/대한민국', - path: '/대한민국', - getParentRoute: () => SpecialCharsRouteRoute, - } as any) -const SpecialCharsSearchRoute = SpecialCharsSearchRouteImport.update({ - id: '/search', - path: '/search', - getParentRoute: () => SpecialCharsRouteRoute, +const RawStreamSsrBinaryHintRoute = RawStreamSsrBinaryHintRouteImport.update({ + id: '/ssr-binary-hint', + path: '/ssr-binary-hint', + getParentRoute: () => RawStreamRoute, } as any) -const SpecialCharsHashRoute = SpecialCharsHashRouteImport.update({ - id: '/hash', - path: '/hash', - getParentRoute: () => SpecialCharsRouteRoute, -} as any) -const SpecialCharsParamRoute = SpecialCharsParamRouteImport.update({ - id: '/$param', - path: '/$param', - getParentRoute: () => SpecialCharsRouteRoute, -} as any) -const SearchParamsLoaderThrowsRedirectRoute = - SearchParamsLoaderThrowsRedirectRouteImport.update({ - id: '/loader-throws-redirect', - path: '/loader-throws-redirect', - getParentRoute: () => SearchParamsRouteRoute, - } as any) -const SearchParamsDefaultRoute = SearchParamsDefaultRouteImport.update({ - id: '/default', - path: '/default', - getParentRoute: () => SearchParamsRouteRoute, -} as any) -const RedirectTargetRoute = RedirectTargetRouteImport.update({ - id: '/redirect/$target', - path: '/redirect/$target', - getParentRoute: () => rootRouteImport, -} as any) -const RawStreamSsrTextHintRoute = RawStreamSsrTextHintRouteImport.update({ - id: '/ssr-text-hint', - path: '/ssr-text-hint', - getParentRoute: () => RawStreamRoute, -} as any) -const RawStreamSsrSingleRoute = RawStreamSsrSingleRouteImport.update({ - id: '/ssr-single', - path: '/ssr-single', +const RawStreamSsrMixedRoute = RawStreamSsrMixedRouteImport.update({ + id: '/ssr-mixed', + path: '/ssr-mixed', getParentRoute: () => RawStreamRoute, } as any) const RawStreamSsrMultipleRoute = RawStreamSsrMultipleRouteImport.update({ @@ -224,56 +214,51 @@ const RawStreamSsrMultipleRoute = RawStreamSsrMultipleRouteImport.update({ path: '/ssr-multiple', getParentRoute: () => RawStreamRoute, } as any) -const RawStreamSsrMixedRoute = RawStreamSsrMixedRouteImport.update({ - id: '/ssr-mixed', - path: '/ssr-mixed', +const RawStreamSsrSingleRoute = RawStreamSsrSingleRouteImport.update({ + id: '/ssr-single', + path: '/ssr-single', getParentRoute: () => RawStreamRoute, } as any) -const RawStreamSsrBinaryHintRoute = RawStreamSsrBinaryHintRouteImport.update({ - id: '/ssr-binary-hint', - path: '/ssr-binary-hint', +const RawStreamSsrTextHintRoute = RawStreamSsrTextHintRouteImport.update({ + id: '/ssr-text-hint', + path: '/ssr-text-hint', getParentRoute: () => RawStreamRoute, } as any) -const RawStreamClientCallRoute = RawStreamClientCallRouteImport.update({ - id: '/client-call', - path: '/client-call', - getParentRoute: () => RawStreamRoute, +const RedirectIndexRoute = RedirectIndexRouteImport.update({ + id: '/redirect/', + path: '/redirect/', + getParentRoute: () => rootRouteImport, } as any) -const PostsPostIdRoute = PostsPostIdRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => PostsRoute, +const RedirectTargetRoute = RedirectTargetRouteImport.update({ + id: '/redirect/$target', + path: '/redirect/$target', + getParentRoute: () => rootRouteImport, } as any) -const NotFoundViaLoaderRoute = NotFoundViaLoaderRouteImport.update({ - id: '/via-loader', - path: '/via-loader', - getParentRoute: () => NotFoundRouteRoute, +const SearchParamsIndexRoute = SearchParamsIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => SearchParamsRouteRoute, } as any) -const NotFoundViaBeforeLoadTargetRootRoute = - NotFoundViaBeforeLoadTargetRootRouteImport.update({ - id: '/via-beforeLoad-target-root', - path: '/via-beforeLoad-target-root', - getParentRoute: () => NotFoundRouteRoute, - } as any) -const NotFoundViaBeforeLoadRoute = NotFoundViaBeforeLoadRouteImport.update({ - id: '/via-beforeLoad', - path: '/via-beforeLoad', - getParentRoute: () => NotFoundRouteRoute, +const SearchParamsDefaultRoute = SearchParamsDefaultRouteImport.update({ + id: '/default', + path: '/default', + getParentRoute: () => SearchParamsRouteRoute, } as any) -const MultiCookieRedirectTargetRoute = - MultiCookieRedirectTargetRouteImport.update({ - id: '/multi-cookie-redirect/target', - path: '/multi-cookie-redirect/target', - getParentRoute: () => rootRouteImport, +const SearchParamsLoaderThrowsRedirectRoute = + SearchParamsLoaderThrowsRedirectRouteImport.update({ + id: '/loader-throws-redirect', + path: '/loader-throws-redirect', + getParentRoute: () => SearchParamsRouteRoute, } as any) -const ApiUsersRoute = ApiUsersRouteImport.update({ - id: '/api/users', - path: '/api/users', - getParentRoute: () => rootRouteImport, +const SpecialCharsParamRoute = SpecialCharsParamRouteImport.update({ + id: '/$param', + path: '/$param', + getParentRoute: () => SpecialCharsRouteRoute, } as any) -const LayoutLayout2Route = LayoutLayout2RouteImport.update({ - id: '/_layout-2', - getParentRoute: () => LayoutRoute, +const SpecialCharsHashRoute = SpecialCharsHashRouteImport.update({ + id: '/hash', + path: '/hash', + getParentRoute: () => SpecialCharsRouteRoute, } as any) const SpecialCharsMalformedRouteRoute = SpecialCharsMalformedRouteRouteImport.update({ @@ -281,76 +266,91 @@ const SpecialCharsMalformedRouteRoute = path: '/malformed', getParentRoute: () => SpecialCharsRouteRoute, } as any) -const NotFoundParentBoundaryRouteRoute = - NotFoundParentBoundaryRouteRouteImport.update({ - id: '/parent-boundary', - path: '/parent-boundary', - getParentRoute: () => NotFoundRouteRoute, +const SpecialCharsSearchRoute = SpecialCharsSearchRouteImport.update({ + id: '/search', + path: '/search', + getParentRoute: () => SpecialCharsRouteRoute, +} as any) +const SpecialCharsChar45824Char54620Char48124Char44397Route = + SpecialCharsChar45824Char54620Char48124Char44397RouteImport.update({ + id: '/대한민국', + path: '/대한민국', + getParentRoute: () => SpecialCharsRouteRoute, } as any) -const RedirectTargetIndexRoute = RedirectTargetIndexRouteImport.update({ +const UsersIndexRoute = UsersIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => RedirectTargetRoute, + getParentRoute: () => UsersRoute, } as any) -const SpecialCharsMalformedSearchRoute = - SpecialCharsMalformedSearchRouteImport.update({ - id: '/search', - path: '/search', - getParentRoute: () => SpecialCharsMalformedRouteRoute, - } as any) -const SpecialCharsMalformedParamRoute = - SpecialCharsMalformedParamRouteImport.update({ - id: '/$param', - path: '/$param', - getParentRoute: () => SpecialCharsMalformedRouteRoute, - } as any) -const RedirectTargetViaLoaderRoute = RedirectTargetViaLoaderRouteImport.update({ - id: '/via-loader', - path: '/via-loader', - getParentRoute: () => RedirectTargetRoute, +const UsersUserIdRoute = UsersUserIdRouteImport.update({ + id: '/$userId', + path: '/$userId', + getParentRoute: () => UsersRoute, } as any) -const RedirectTargetViaBeforeLoadRoute = - RedirectTargetViaBeforeLoadRouteImport.update({ +const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ + id: '/layout-a', + path: '/layout-a', + getParentRoute: () => LayoutLayout2Route, +} as any) +const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ + id: '/layout-b', + path: '/layout-b', + getParentRoute: () => LayoutLayout2Route, +} as any) +const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ + id: '/$userId', + path: '/$userId', + getParentRoute: () => ApiUsersRoute, +} as any) +const NotFoundParentBoundaryViaBeforeLoadRoute = + NotFoundParentBoundaryViaBeforeLoadRouteImport.update({ id: '/via-beforeLoad', path: '/via-beforeLoad', - getParentRoute: () => RedirectTargetRoute, + getParentRoute: () => NotFoundParentBoundaryRouteRoute, } as any) const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ id: '/posts_/$postId/deep', path: '/posts/$postId/deep', getParentRoute: () => rootRouteImport, } as any) -const NotFoundParentBoundaryViaBeforeLoadRoute = - NotFoundParentBoundaryViaBeforeLoadRouteImport.update({ +const RedirectTargetIndexRoute = RedirectTargetIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => RedirectTargetRoute, +} as any) +const RedirectTargetViaBeforeLoadRoute = + RedirectTargetViaBeforeLoadRouteImport.update({ id: '/via-beforeLoad', path: '/via-beforeLoad', - getParentRoute: () => NotFoundParentBoundaryRouteRoute, + getParentRoute: () => RedirectTargetRoute, } as any) -const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ - id: '/$userId', - path: '/$userId', - getParentRoute: () => ApiUsersRoute, -} as any) -const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ - id: '/layout-b', - path: '/layout-b', - getParentRoute: () => LayoutLayout2Route, -} as any) -const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ - id: '/layout-a', - path: '/layout-a', - getParentRoute: () => LayoutLayout2Route, +const RedirectTargetViaLoaderRoute = RedirectTargetViaLoaderRouteImport.update({ + id: '/via-loader', + path: '/via-loader', + getParentRoute: () => RedirectTargetRoute, } as any) +const SpecialCharsMalformedParamRoute = + SpecialCharsMalformedParamRouteImport.update({ + id: '/$param', + path: '/$param', + getParentRoute: () => SpecialCharsMalformedRouteRoute, + } as any) +const SpecialCharsMalformedSearchRoute = + SpecialCharsMalformedSearchRouteImport.update({ + id: '/search', + path: '/search', + getParentRoute: () => SpecialCharsMalformedRouteRoute, + } as any) const RedirectTargetServerFnIndexRoute = RedirectTargetServerFnIndexRouteImport.update({ id: '/serverFn/', path: '/serverFn/', getParentRoute: () => RedirectTargetRoute, } as any) -const RedirectTargetServerFnViaUseServerFnRoute = - RedirectTargetServerFnViaUseServerFnRouteImport.update({ - id: '/serverFn/via-useServerFn', - path: '/serverFn/via-useServerFn', +const RedirectTargetServerFnViaBeforeLoadRoute = + RedirectTargetServerFnViaBeforeLoadRouteImport.update({ + id: '/serverFn/via-beforeLoad', + path: '/serverFn/via-beforeLoad', getParentRoute: () => RedirectTargetRoute, } as any) const RedirectTargetServerFnViaLoaderRoute = @@ -359,10 +359,10 @@ const RedirectTargetServerFnViaLoaderRoute = path: '/serverFn/via-loader', getParentRoute: () => RedirectTargetRoute, } as any) -const RedirectTargetServerFnViaBeforeLoadRoute = - RedirectTargetServerFnViaBeforeLoadRouteImport.update({ - id: '/serverFn/via-beforeLoad', - path: '/serverFn/via-beforeLoad', +const RedirectTargetServerFnViaUseServerFnRoute = + RedirectTargetServerFnViaUseServerFnRouteImport.update({ + id: '/serverFn/via-useServerFn', + path: '/serverFn/via-useServerFn', getParentRoute: () => RedirectTargetRoute, } as any) @@ -728,39 +728,32 @@ export interface RootRouteChildren { declare module '@tanstack/vue-router' { interface FileRoutesByPath { - '/users': { - id: '/users' - path: '/users' - fullPath: '/users' - preLoaderRoute: typeof UsersRouteImport - parentRoute: typeof rootRouteImport - } - '/stream': { - id: '/stream' - path: '/stream' - fullPath: '/stream' - preLoaderRoute: typeof StreamRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/scripts': { - id: '/scripts' - path: '/scripts' - fullPath: '/scripts' - preLoaderRoute: typeof ScriptsRouteImport + '/_layout': { + id: '/_layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } - '/raw-stream': { - id: '/raw-stream' - path: '/raw-stream' - fullPath: '/raw-stream' - preLoaderRoute: typeof RawStreamRouteImport + '/deferred': { + id: '/deferred' + path: '/deferred' + fullPath: '/deferred' + preLoaderRoute: typeof DeferredRouteImport parentRoute: typeof rootRouteImport } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/inline-scripts': { + id: '/inline-scripts' + path: '/inline-scripts' + fullPath: '/inline-scripts' + preLoaderRoute: typeof InlineScriptsRouteImport parentRoute: typeof rootRouteImport } '/links': { @@ -770,32 +763,32 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof LinksRouteImport parentRoute: typeof rootRouteImport } - '/inline-scripts': { - id: '/inline-scripts' - path: '/inline-scripts' - fullPath: '/inline-scripts' - preLoaderRoute: typeof InlineScriptsRouteImport + '/not-found': { + id: '/not-found' + path: '/not-found' + fullPath: '/not-found' + preLoaderRoute: typeof NotFoundRouteRouteImport parentRoute: typeof rootRouteImport } - '/deferred': { - id: '/deferred' - path: '/deferred' - fullPath: '/deferred' - preLoaderRoute: typeof DeferredRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/_layout': { - id: '/_layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutRouteImport + '/raw-stream': { + id: '/raw-stream' + path: '/raw-stream' + fullPath: '/raw-stream' + preLoaderRoute: typeof RawStreamRouteImport parentRoute: typeof rootRouteImport } - '/specialChars': { - id: '/specialChars' - path: '/specialChars' - fullPath: '/specialChars' - preLoaderRoute: typeof SpecialCharsRouteRouteImport + '/scripts': { + id: '/scripts' + path: '/scripts' + fullPath: '/scripts' + preLoaderRoute: typeof ScriptsRouteImport parentRoute: typeof rootRouteImport } '/search-params': { @@ -805,61 +798,40 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof SearchParamsRouteRouteImport parentRoute: typeof rootRouteImport } - '/not-found': { - id: '/not-found' - path: '/not-found' - fullPath: '/not-found' - preLoaderRoute: typeof NotFoundRouteRouteImport + '/specialChars': { + id: '/specialChars' + path: '/specialChars' + fullPath: '/specialChars' + preLoaderRoute: typeof SpecialCharsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/stream': { + id: '/stream' + path: '/stream' + fullPath: '/stream' + preLoaderRoute: typeof StreamRouteImport parentRoute: typeof rootRouteImport } - '/users/': { - id: '/users/' - path: '/' - fullPath: '/users/' - preLoaderRoute: typeof UsersIndexRouteImport - parentRoute: typeof UsersRoute - } - '/search-params/': { - id: '/search-params/' - path: '/' - fullPath: '/search-params/' - preLoaderRoute: typeof SearchParamsIndexRouteImport - parentRoute: typeof SearchParamsRouteRoute - } - '/redirect/': { - id: '/redirect/' - path: '/redirect' - fullPath: '/redirect/' - preLoaderRoute: typeof RedirectIndexRouteImport + '/users': { + id: '/users' + path: '/users' + fullPath: '/users' + preLoaderRoute: typeof UsersRouteImport parentRoute: typeof rootRouteImport } - '/raw-stream/': { - id: '/raw-stream/' - path: '/' - fullPath: '/raw-stream/' - preLoaderRoute: typeof RawStreamIndexRouteImport - parentRoute: typeof RawStreamRoute - } - '/posts/': { - id: '/posts/' - path: '/' - fullPath: '/posts/' - preLoaderRoute: typeof PostsIndexRouteImport - parentRoute: typeof PostsRoute + '/_layout/_layout-2': { + id: '/_layout/_layout-2' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutLayout2RouteImport + parentRoute: typeof LayoutRoute } - '/not-found/': { - id: '/not-found/' - path: '/' - fullPath: '/not-found/' - preLoaderRoute: typeof NotFoundIndexRouteImport - parentRoute: typeof NotFoundRouteRoute + '/api/users': { + id: '/api/users' + path: '/api/users' + fullPath: '/api/users' + preLoaderRoute: typeof ApiUsersRouteImport + parentRoute: typeof rootRouteImport } '/multi-cookie-redirect/': { id: '/multi-cookie-redirect/' @@ -868,81 +840,81 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof MultiCookieRedirectIndexRouteImport parentRoute: typeof rootRouteImport } - '/users/$userId': { - id: '/users/$userId' - path: '/$userId' - fullPath: '/users/$userId' - preLoaderRoute: typeof UsersUserIdRouteImport - parentRoute: typeof UsersRoute + '/multi-cookie-redirect/target': { + id: '/multi-cookie-redirect/target' + path: '/multi-cookie-redirect/target' + fullPath: '/multi-cookie-redirect/target' + preLoaderRoute: typeof MultiCookieRedirectTargetRouteImport + parentRoute: typeof rootRouteImport } - '/specialChars/대한민국': { - id: '/specialChars/대한민국' - path: '/대한민국' - fullPath: '/specialChars/대한민국' - preLoaderRoute: typeof SpecialCharsChar45824Char54620Char48124Char44397RouteImport - parentRoute: typeof SpecialCharsRouteRoute + '/not-found/': { + id: '/not-found/' + path: '/' + fullPath: '/not-found/' + preLoaderRoute: typeof NotFoundIndexRouteImport + parentRoute: typeof NotFoundRouteRoute } - '/specialChars/search': { - id: '/specialChars/search' - path: '/search' - fullPath: '/specialChars/search' - preLoaderRoute: typeof SpecialCharsSearchRouteImport - parentRoute: typeof SpecialCharsRouteRoute + '/not-found/parent-boundary': { + id: '/not-found/parent-boundary' + path: '/parent-boundary' + fullPath: '/not-found/parent-boundary' + preLoaderRoute: typeof NotFoundParentBoundaryRouteRouteImport + parentRoute: typeof NotFoundRouteRoute } - '/specialChars/hash': { - id: '/specialChars/hash' - path: '/hash' - fullPath: '/specialChars/hash' - preLoaderRoute: typeof SpecialCharsHashRouteImport - parentRoute: typeof SpecialCharsRouteRoute + '/not-found/via-beforeLoad': { + id: '/not-found/via-beforeLoad' + path: '/via-beforeLoad' + fullPath: '/not-found/via-beforeLoad' + preLoaderRoute: typeof NotFoundViaBeforeLoadRouteImport + parentRoute: typeof NotFoundRouteRoute } - '/specialChars/$param': { - id: '/specialChars/$param' - path: '/$param' - fullPath: '/specialChars/$param' - preLoaderRoute: typeof SpecialCharsParamRouteImport - parentRoute: typeof SpecialCharsRouteRoute + '/not-found/via-beforeLoad-target-root': { + id: '/not-found/via-beforeLoad-target-root' + path: '/via-beforeLoad-target-root' + fullPath: '/not-found/via-beforeLoad-target-root' + preLoaderRoute: typeof NotFoundViaBeforeLoadTargetRootRouteImport + parentRoute: typeof NotFoundRouteRoute } - '/search-params/loader-throws-redirect': { - id: '/search-params/loader-throws-redirect' - path: '/loader-throws-redirect' - fullPath: '/search-params/loader-throws-redirect' - preLoaderRoute: typeof SearchParamsLoaderThrowsRedirectRouteImport - parentRoute: typeof SearchParamsRouteRoute + '/not-found/via-loader': { + id: '/not-found/via-loader' + path: '/via-loader' + fullPath: '/not-found/via-loader' + preLoaderRoute: typeof NotFoundViaLoaderRouteImport + parentRoute: typeof NotFoundRouteRoute } - '/search-params/default': { - id: '/search-params/default' - path: '/default' - fullPath: '/search-params/default' - preLoaderRoute: typeof SearchParamsDefaultRouteImport - parentRoute: typeof SearchParamsRouteRoute + '/posts/': { + id: '/posts/' + path: '/' + fullPath: '/posts/' + preLoaderRoute: typeof PostsIndexRouteImport + parentRoute: typeof PostsRoute } - '/redirect/$target': { - id: '/redirect/$target' - path: '/redirect/$target' - fullPath: '/redirect/$target' - preLoaderRoute: typeof RedirectTargetRouteImport - parentRoute: typeof rootRouteImport + '/posts/$postId': { + id: '/posts/$postId' + path: '/$postId' + fullPath: '/posts/$postId' + preLoaderRoute: typeof PostsPostIdRouteImport + parentRoute: typeof PostsRoute } - '/raw-stream/ssr-text-hint': { - id: '/raw-stream/ssr-text-hint' - path: '/ssr-text-hint' - fullPath: '/raw-stream/ssr-text-hint' - preLoaderRoute: typeof RawStreamSsrTextHintRouteImport + '/raw-stream/': { + id: '/raw-stream/' + path: '/' + fullPath: '/raw-stream/' + preLoaderRoute: typeof RawStreamIndexRouteImport parentRoute: typeof RawStreamRoute } - '/raw-stream/ssr-single': { - id: '/raw-stream/ssr-single' - path: '/ssr-single' - fullPath: '/raw-stream/ssr-single' - preLoaderRoute: typeof RawStreamSsrSingleRouteImport + '/raw-stream/client-call': { + id: '/raw-stream/client-call' + path: '/client-call' + fullPath: '/raw-stream/client-call' + preLoaderRoute: typeof RawStreamClientCallRouteImport parentRoute: typeof RawStreamRoute } - '/raw-stream/ssr-multiple': { - id: '/raw-stream/ssr-multiple' - path: '/ssr-multiple' - fullPath: '/raw-stream/ssr-multiple' - preLoaderRoute: typeof RawStreamSsrMultipleRouteImport + '/raw-stream/ssr-binary-hint': { + id: '/raw-stream/ssr-binary-hint' + path: '/ssr-binary-hint' + fullPath: '/raw-stream/ssr-binary-hint' + preLoaderRoute: typeof RawStreamSsrBinaryHintRouteImport parentRoute: typeof RawStreamRoute } '/raw-stream/ssr-mixed': { @@ -952,68 +924,75 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof RawStreamSsrMixedRouteImport parentRoute: typeof RawStreamRoute } - '/raw-stream/ssr-binary-hint': { - id: '/raw-stream/ssr-binary-hint' - path: '/ssr-binary-hint' - fullPath: '/raw-stream/ssr-binary-hint' - preLoaderRoute: typeof RawStreamSsrBinaryHintRouteImport + '/raw-stream/ssr-multiple': { + id: '/raw-stream/ssr-multiple' + path: '/ssr-multiple' + fullPath: '/raw-stream/ssr-multiple' + preLoaderRoute: typeof RawStreamSsrMultipleRouteImport parentRoute: typeof RawStreamRoute } - '/raw-stream/client-call': { - id: '/raw-stream/client-call' - path: '/client-call' - fullPath: '/raw-stream/client-call' - preLoaderRoute: typeof RawStreamClientCallRouteImport + '/raw-stream/ssr-single': { + id: '/raw-stream/ssr-single' + path: '/ssr-single' + fullPath: '/raw-stream/ssr-single' + preLoaderRoute: typeof RawStreamSsrSingleRouteImport parentRoute: typeof RawStreamRoute } - '/posts/$postId': { - id: '/posts/$postId' - path: '/$postId' - fullPath: '/posts/$postId' - preLoaderRoute: typeof PostsPostIdRouteImport - parentRoute: typeof PostsRoute + '/raw-stream/ssr-text-hint': { + id: '/raw-stream/ssr-text-hint' + path: '/ssr-text-hint' + fullPath: '/raw-stream/ssr-text-hint' + preLoaderRoute: typeof RawStreamSsrTextHintRouteImport + parentRoute: typeof RawStreamRoute } - '/not-found/via-loader': { - id: '/not-found/via-loader' - path: '/via-loader' - fullPath: '/not-found/via-loader' - preLoaderRoute: typeof NotFoundViaLoaderRouteImport - parentRoute: typeof NotFoundRouteRoute + '/redirect/': { + id: '/redirect/' + path: '/redirect' + fullPath: '/redirect/' + preLoaderRoute: typeof RedirectIndexRouteImport + parentRoute: typeof rootRouteImport } - '/not-found/via-beforeLoad-target-root': { - id: '/not-found/via-beforeLoad-target-root' - path: '/via-beforeLoad-target-root' - fullPath: '/not-found/via-beforeLoad-target-root' - preLoaderRoute: typeof NotFoundViaBeforeLoadTargetRootRouteImport - parentRoute: typeof NotFoundRouteRoute + '/redirect/$target': { + id: '/redirect/$target' + path: '/redirect/$target' + fullPath: '/redirect/$target' + preLoaderRoute: typeof RedirectTargetRouteImport + parentRoute: typeof rootRouteImport } - '/not-found/via-beforeLoad': { - id: '/not-found/via-beforeLoad' - path: '/via-beforeLoad' - fullPath: '/not-found/via-beforeLoad' - preLoaderRoute: typeof NotFoundViaBeforeLoadRouteImport - parentRoute: typeof NotFoundRouteRoute + '/search-params/': { + id: '/search-params/' + path: '/' + fullPath: '/search-params/' + preLoaderRoute: typeof SearchParamsIndexRouteImport + parentRoute: typeof SearchParamsRouteRoute } - '/multi-cookie-redirect/target': { - id: '/multi-cookie-redirect/target' - path: '/multi-cookie-redirect/target' - fullPath: '/multi-cookie-redirect/target' - preLoaderRoute: typeof MultiCookieRedirectTargetRouteImport - parentRoute: typeof rootRouteImport + '/search-params/default': { + id: '/search-params/default' + path: '/default' + fullPath: '/search-params/default' + preLoaderRoute: typeof SearchParamsDefaultRouteImport + parentRoute: typeof SearchParamsRouteRoute } - '/api/users': { - id: '/api/users' - path: '/api/users' - fullPath: '/api/users' - preLoaderRoute: typeof ApiUsersRouteImport - parentRoute: typeof rootRouteImport + '/search-params/loader-throws-redirect': { + id: '/search-params/loader-throws-redirect' + path: '/loader-throws-redirect' + fullPath: '/search-params/loader-throws-redirect' + preLoaderRoute: typeof SearchParamsLoaderThrowsRedirectRouteImport + parentRoute: typeof SearchParamsRouteRoute } - '/_layout/_layout-2': { - id: '/_layout/_layout-2' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutLayout2RouteImport - parentRoute: typeof LayoutRoute + '/specialChars/$param': { + id: '/specialChars/$param' + path: '/$param' + fullPath: '/specialChars/$param' + preLoaderRoute: typeof SpecialCharsParamRouteImport + parentRoute: typeof SpecialCharsRouteRoute + } + '/specialChars/hash': { + id: '/specialChars/hash' + path: '/hash' + fullPath: '/specialChars/hash' + preLoaderRoute: typeof SpecialCharsHashRouteImport + parentRoute: typeof SpecialCharsRouteRoute } '/specialChars/malformed': { id: '/specialChars/malformed' @@ -1022,47 +1001,61 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof SpecialCharsMalformedRouteRouteImport parentRoute: typeof SpecialCharsRouteRoute } - '/not-found/parent-boundary': { - id: '/not-found/parent-boundary' - path: '/parent-boundary' - fullPath: '/not-found/parent-boundary' - preLoaderRoute: typeof NotFoundParentBoundaryRouteRouteImport - parentRoute: typeof NotFoundRouteRoute + '/specialChars/search': { + id: '/specialChars/search' + path: '/search' + fullPath: '/specialChars/search' + preLoaderRoute: typeof SpecialCharsSearchRouteImport + parentRoute: typeof SpecialCharsRouteRoute } - '/redirect/$target/': { - id: '/redirect/$target/' + '/specialChars/대한민국': { + id: '/specialChars/대한민국' + path: '/대한민국' + fullPath: '/specialChars/대한민국' + preLoaderRoute: typeof SpecialCharsChar45824Char54620Char48124Char44397RouteImport + parentRoute: typeof SpecialCharsRouteRoute + } + '/users/': { + id: '/users/' path: '/' - fullPath: '/redirect/$target/' - preLoaderRoute: typeof RedirectTargetIndexRouteImport - parentRoute: typeof RedirectTargetRoute + fullPath: '/users/' + preLoaderRoute: typeof UsersIndexRouteImport + parentRoute: typeof UsersRoute } - '/specialChars/malformed/search': { - id: '/specialChars/malformed/search' - path: '/search' - fullPath: '/specialChars/malformed/search' - preLoaderRoute: typeof SpecialCharsMalformedSearchRouteImport - parentRoute: typeof SpecialCharsMalformedRouteRoute + '/users/$userId': { + id: '/users/$userId' + path: '/$userId' + fullPath: '/users/$userId' + preLoaderRoute: typeof UsersUserIdRouteImport + parentRoute: typeof UsersRoute } - '/specialChars/malformed/$param': { - id: '/specialChars/malformed/$param' - path: '/$param' - fullPath: '/specialChars/malformed/$param' - preLoaderRoute: typeof SpecialCharsMalformedParamRouteImport - parentRoute: typeof SpecialCharsMalformedRouteRoute + '/_layout/_layout-2/layout-a': { + id: '/_layout/_layout-2/layout-a' + path: '/layout-a' + fullPath: '/layout-a' + preLoaderRoute: typeof LayoutLayout2LayoutARouteImport + parentRoute: typeof LayoutLayout2Route } - '/redirect/$target/via-loader': { - id: '/redirect/$target/via-loader' - path: '/via-loader' - fullPath: '/redirect/$target/via-loader' - preLoaderRoute: typeof RedirectTargetViaLoaderRouteImport - parentRoute: typeof RedirectTargetRoute + '/_layout/_layout-2/layout-b': { + id: '/_layout/_layout-2/layout-b' + path: '/layout-b' + fullPath: '/layout-b' + preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport + parentRoute: typeof LayoutLayout2Route } - '/redirect/$target/via-beforeLoad': { - id: '/redirect/$target/via-beforeLoad' + '/api/users/$userId': { + id: '/api/users/$userId' + path: '/$userId' + fullPath: '/api/users/$userId' + preLoaderRoute: typeof ApiUsersUserIdRouteImport + parentRoute: typeof ApiUsersRoute + } + '/not-found/parent-boundary/via-beforeLoad': { + id: '/not-found/parent-boundary/via-beforeLoad' path: '/via-beforeLoad' - fullPath: '/redirect/$target/via-beforeLoad' - preLoaderRoute: typeof RedirectTargetViaBeforeLoadRouteImport - parentRoute: typeof RedirectTargetRoute + fullPath: '/not-found/parent-boundary/via-beforeLoad' + preLoaderRoute: typeof NotFoundParentBoundaryViaBeforeLoadRouteImport + parentRoute: typeof NotFoundParentBoundaryRouteRoute } '/posts_/$postId/deep': { id: '/posts_/$postId/deep' @@ -1071,33 +1064,40 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof PostsPostIdDeepRouteImport parentRoute: typeof rootRouteImport } - '/not-found/parent-boundary/via-beforeLoad': { - id: '/not-found/parent-boundary/via-beforeLoad' + '/redirect/$target/': { + id: '/redirect/$target/' + path: '/' + fullPath: '/redirect/$target/' + preLoaderRoute: typeof RedirectTargetIndexRouteImport + parentRoute: typeof RedirectTargetRoute + } + '/redirect/$target/via-beforeLoad': { + id: '/redirect/$target/via-beforeLoad' path: '/via-beforeLoad' - fullPath: '/not-found/parent-boundary/via-beforeLoad' - preLoaderRoute: typeof NotFoundParentBoundaryViaBeforeLoadRouteImport - parentRoute: typeof NotFoundParentBoundaryRouteRoute + fullPath: '/redirect/$target/via-beforeLoad' + preLoaderRoute: typeof RedirectTargetViaBeforeLoadRouteImport + parentRoute: typeof RedirectTargetRoute } - '/api/users/$userId': { - id: '/api/users/$userId' - path: '/$userId' - fullPath: '/api/users/$userId' - preLoaderRoute: typeof ApiUsersUserIdRouteImport - parentRoute: typeof ApiUsersRoute + '/redirect/$target/via-loader': { + id: '/redirect/$target/via-loader' + path: '/via-loader' + fullPath: '/redirect/$target/via-loader' + preLoaderRoute: typeof RedirectTargetViaLoaderRouteImport + parentRoute: typeof RedirectTargetRoute } - '/_layout/_layout-2/layout-b': { - id: '/_layout/_layout-2/layout-b' - path: '/layout-b' - fullPath: '/layout-b' - preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport - parentRoute: typeof LayoutLayout2Route + '/specialChars/malformed/$param': { + id: '/specialChars/malformed/$param' + path: '/$param' + fullPath: '/specialChars/malformed/$param' + preLoaderRoute: typeof SpecialCharsMalformedParamRouteImport + parentRoute: typeof SpecialCharsMalformedRouteRoute } - '/_layout/_layout-2/layout-a': { - id: '/_layout/_layout-2/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: typeof LayoutLayout2LayoutARouteImport - parentRoute: typeof LayoutLayout2Route + '/specialChars/malformed/search': { + id: '/specialChars/malformed/search' + path: '/search' + fullPath: '/specialChars/malformed/search' + preLoaderRoute: typeof SpecialCharsMalformedSearchRouteImport + parentRoute: typeof SpecialCharsMalformedRouteRoute } '/redirect/$target/serverFn/': { id: '/redirect/$target/serverFn/' @@ -1106,11 +1106,11 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof RedirectTargetServerFnIndexRouteImport parentRoute: typeof RedirectTargetRoute } - '/redirect/$target/serverFn/via-useServerFn': { - id: '/redirect/$target/serverFn/via-useServerFn' - path: '/serverFn/via-useServerFn' - fullPath: '/redirect/$target/serverFn/via-useServerFn' - preLoaderRoute: typeof RedirectTargetServerFnViaUseServerFnRouteImport + '/redirect/$target/serverFn/via-beforeLoad': { + id: '/redirect/$target/serverFn/via-beforeLoad' + path: '/serverFn/via-beforeLoad' + fullPath: '/redirect/$target/serverFn/via-beforeLoad' + preLoaderRoute: typeof RedirectTargetServerFnViaBeforeLoadRouteImport parentRoute: typeof RedirectTargetRoute } '/redirect/$target/serverFn/via-loader': { @@ -1120,11 +1120,11 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof RedirectTargetServerFnViaLoaderRouteImport parentRoute: typeof RedirectTargetRoute } - '/redirect/$target/serverFn/via-beforeLoad': { - id: '/redirect/$target/serverFn/via-beforeLoad' - path: '/serverFn/via-beforeLoad' - fullPath: '/redirect/$target/serverFn/via-beforeLoad' - preLoaderRoute: typeof RedirectTargetServerFnViaBeforeLoadRouteImport + '/redirect/$target/serverFn/via-useServerFn': { + id: '/redirect/$target/serverFn/via-useServerFn' + path: '/serverFn/via-useServerFn' + fullPath: '/redirect/$target/serverFn/via-useServerFn' + preLoaderRoute: typeof RedirectTargetServerFnViaUseServerFnRouteImport parentRoute: typeof RedirectTargetRoute } } diff --git a/e2e/vue-start/custom-basepath/src/routeTree.gen.ts b/e2e/vue-start/custom-basepath/src/routeTree.gen.ts index ea7e127d8e..605193fbec 100644 --- a/e2e/vue-start/custom-basepath/src/routeTree.gen.ts +++ b/e2e/vue-start/custom-basepath/src/routeTree.gen.ts @@ -9,29 +9,29 @@ // 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 UsersRouteImport } from './routes/users' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as LogoutRouteImport } from './routes/logout' -import { Route as DeferredRouteImport } from './routes/deferred' import { Route as IndexRouteImport } from './routes/index' -import { Route as UsersIndexRouteImport } from './routes/users.index' -import { Route as RedirectIndexRouteImport } from './routes/redirect.index' +import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as LogoutRouteImport } from './routes/logout' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as UsersRouteImport } from './routes/users' +import { Route as ApiUsersRouteImport } from './routes/api/users' import { Route as PostsIndexRouteImport } from './routes/posts.index' -import { Route as UsersUserIdRouteImport } from './routes/users.$userId' -import { Route as RedirectThrowItRouteImport } from './routes/redirect.throw-it' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as ApiUsersRouteImport } from './routes/api/users' -import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' +import { Route as RedirectIndexRouteImport } from './routes/redirect.index' +import { Route as RedirectThrowItRouteImport } from './routes/redirect.throw-it' +import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as ApiUsersUserIdRouteImport } from './routes/api/users.$userId' +import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -const UsersRoute = UsersRouteImport.update({ - id: '/users', - path: '/users', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const DeferredRoute = DeferredRouteImport.update({ + id: '/deferred', + path: '/deferred', getParentRoute: () => rootRouteImport, } as any) const LogoutRoute = LogoutRouteImport.update({ @@ -39,61 +39,61 @@ const LogoutRoute = LogoutRouteImport.update({ path: '/logout', getParentRoute: () => rootRouteImport, } as any) -const DeferredRoute = DeferredRouteImport.update({ - id: '/deferred', - path: '/deferred', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const UsersRoute = UsersRouteImport.update({ + id: '/users', + path: '/users', getParentRoute: () => rootRouteImport, } as any) -const UsersIndexRoute = UsersIndexRouteImport.update({ +const ApiUsersRoute = ApiUsersRouteImport.update({ + id: '/api/users', + path: '/api/users', + getParentRoute: () => rootRouteImport, +} as any) +const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => UsersRoute, + getParentRoute: () => PostsRoute, +} as any) +const PostsPostIdRoute = PostsPostIdRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => PostsRoute, } as any) const RedirectIndexRoute = RedirectIndexRouteImport.update({ id: '/redirect/', path: '/redirect/', getParentRoute: () => rootRouteImport, } as any) -const PostsIndexRoute = PostsIndexRouteImport.update({ +const RedirectThrowItRoute = RedirectThrowItRouteImport.update({ + id: '/redirect/throw-it', + path: '/redirect/throw-it', + getParentRoute: () => rootRouteImport, +} as any) +const UsersIndexRoute = UsersIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => PostsRoute, + getParentRoute: () => UsersRoute, } as any) const UsersUserIdRoute = UsersUserIdRouteImport.update({ id: '/$userId', path: '/$userId', getParentRoute: () => UsersRoute, } as any) -const RedirectThrowItRoute = RedirectThrowItRouteImport.update({ - id: '/redirect/throw-it', - path: '/redirect/throw-it', - getParentRoute: () => rootRouteImport, -} as any) -const PostsPostIdRoute = PostsPostIdRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => PostsRoute, -} as any) -const ApiUsersRoute = ApiUsersRouteImport.update({ - id: '/api/users', - path: '/api/users', - getParentRoute: () => rootRouteImport, +const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ + id: '/$userId', + path: '/$userId', + getParentRoute: () => ApiUsersRoute, } as any) const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ id: '/posts_/$postId/deep', path: '/posts/$postId/deep', getParentRoute: () => rootRouteImport, } as any) -const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ - id: '/$userId', - path: '/$userId', - getParentRoute: () => ApiUsersRoute, -} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -205,18 +205,18 @@ export interface RootRouteChildren { declare module '@tanstack/vue-router' { interface FileRoutesByPath { - '/users': { - id: '/users' - path: '/users' - fullPath: '/users' - preLoaderRoute: typeof UsersRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/deferred': { + id: '/deferred' + path: '/deferred' + fullPath: '/deferred' + preLoaderRoute: typeof DeferredRouteImport parentRoute: typeof rootRouteImport } '/logout': { @@ -226,32 +226,25 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof LogoutRouteImport parentRoute: typeof rootRouteImport } - '/deferred': { - id: '/deferred' - path: '/deferred' - fullPath: '/deferred' - preLoaderRoute: typeof DeferredRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/users': { + id: '/users' + path: '/users' + fullPath: '/users' + preLoaderRoute: typeof UsersRouteImport parentRoute: typeof rootRouteImport } - '/users/': { - id: '/users/' - path: '/' - fullPath: '/users/' - preLoaderRoute: typeof UsersIndexRouteImport - parentRoute: typeof UsersRoute - } - '/redirect/': { - id: '/redirect/' - path: '/redirect' - fullPath: '/redirect/' - preLoaderRoute: typeof RedirectIndexRouteImport + '/api/users': { + id: '/api/users' + path: '/api/users' + fullPath: '/api/users' + preLoaderRoute: typeof ApiUsersRouteImport parentRoute: typeof rootRouteImport } '/posts/': { @@ -261,20 +254,6 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRoute } - '/users/$userId': { - id: '/users/$userId' - path: '/$userId' - fullPath: '/users/$userId' - preLoaderRoute: typeof UsersUserIdRouteImport - parentRoute: typeof UsersRoute - } - '/redirect/throw-it': { - id: '/redirect/throw-it' - path: '/redirect/throw-it' - fullPath: '/redirect/throw-it' - preLoaderRoute: typeof RedirectThrowItRouteImport - parentRoute: typeof rootRouteImport - } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' @@ -282,20 +261,34 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/api/users': { - id: '/api/users' - path: '/api/users' - fullPath: '/api/users' - preLoaderRoute: typeof ApiUsersRouteImport + '/redirect/': { + id: '/redirect/' + path: '/redirect' + fullPath: '/redirect/' + preLoaderRoute: typeof RedirectIndexRouteImport parentRoute: typeof rootRouteImport } - '/posts_/$postId/deep': { - id: '/posts_/$postId/deep' - path: '/posts/$postId/deep' - fullPath: '/posts/$postId/deep' - preLoaderRoute: typeof PostsPostIdDeepRouteImport + '/redirect/throw-it': { + id: '/redirect/throw-it' + path: '/redirect/throw-it' + fullPath: '/redirect/throw-it' + preLoaderRoute: typeof RedirectThrowItRouteImport parentRoute: typeof rootRouteImport } + '/users/': { + id: '/users/' + path: '/' + fullPath: '/users/' + preLoaderRoute: typeof UsersIndexRouteImport + parentRoute: typeof UsersRoute + } + '/users/$userId': { + id: '/users/$userId' + path: '/$userId' + fullPath: '/users/$userId' + preLoaderRoute: typeof UsersUserIdRouteImport + parentRoute: typeof UsersRoute + } '/api/users/$userId': { id: '/api/users/$userId' path: '/$userId' @@ -303,6 +296,13 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof ApiUsersUserIdRouteImport parentRoute: typeof ApiUsersRoute } + '/posts_/$postId/deep': { + id: '/posts_/$postId/deep' + path: '/posts/$postId/deep' + fullPath: '/posts/$postId/deep' + preLoaderRoute: typeof PostsPostIdDeepRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/e2e/vue-start/query-integration/src/routeTree.gen.ts b/e2e/vue-start/query-integration/src/routeTree.gen.ts index d4d30083a0..c5a9f02a7a 100644 --- a/e2e/vue-start/query-integration/src/routeTree.gen.ts +++ b/e2e/vue-start/query-integration/src/routeTree.gen.ts @@ -9,20 +9,20 @@ // 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 UseQueryRouteImport } from './routes/useQuery' import { Route as IndexRouteImport } from './routes/index' +import { Route as UseQueryRouteImport } from './routes/useQuery' import { Route as LoaderFetchQueryTypeRouteImport } from './routes/loader-fetchQuery/$type' -const UseQueryRoute = UseQueryRouteImport.update({ - id: '/useQuery', - path: '/useQuery', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const UseQueryRoute = UseQueryRouteImport.update({ + id: '/useQuery', + path: '/useQuery', + getParentRoute: () => rootRouteImport, +} as any) const LoaderFetchQueryTypeRoute = LoaderFetchQueryTypeRouteImport.update({ id: '/loader-fetchQuery/$type', path: '/loader-fetchQuery/$type', @@ -61,13 +61,6 @@ export interface RootRouteChildren { declare module '@tanstack/vue-router' { interface FileRoutesByPath { - '/useQuery': { - id: '/useQuery' - path: '/useQuery' - fullPath: '/useQuery' - preLoaderRoute: typeof UseQueryRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -75,6 +68,13 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/useQuery': { + id: '/useQuery' + path: '/useQuery' + fullPath: '/useQuery' + preLoaderRoute: typeof UseQueryRouteImport + parentRoute: typeof rootRouteImport + } '/loader-fetchQuery/$type': { id: '/loader-fetchQuery/$type' path: '/loader-fetchQuery/$type' diff --git a/e2e/vue-start/scroll-restoration/src/routeTree.gen.ts b/e2e/vue-start/scroll-restoration/src/routeTree.gen.ts index 86dc966e78..49b34fc68f 100644 --- a/e2e/vue-start/scroll-restoration/src/routeTree.gen.ts +++ b/e2e/vue-start/scroll-restoration/src/routeTree.gen.ts @@ -10,18 +10,18 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' -import { Route as testsWithSearchRouteImport } from './routes/(tests)/with-search' -import { Route as testsWithLoaderRouteImport } from './routes/(tests)/with-loader' import { Route as testsNormalPageRouteImport } from './routes/(tests)/normal-page' +import { Route as testsWithLoaderRouteImport } from './routes/(tests)/with-loader' +import { Route as testsWithSearchRouteImport } from './routes/(tests)/with-search' const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) -const testsWithSearchRoute = testsWithSearchRouteImport.update({ - id: '/(tests)/with-search', - path: '/with-search', +const testsNormalPageRoute = testsNormalPageRouteImport.update({ + id: '/(tests)/normal-page', + path: '/normal-page', getParentRoute: () => rootRouteImport, } as any) const testsWithLoaderRoute = testsWithLoaderRouteImport.update({ @@ -29,9 +29,9 @@ const testsWithLoaderRoute = testsWithLoaderRouteImport.update({ path: '/with-loader', getParentRoute: () => rootRouteImport, } as any) -const testsNormalPageRoute = testsNormalPageRouteImport.update({ - id: '/(tests)/normal-page', - path: '/normal-page', +const testsWithSearchRoute = testsWithSearchRouteImport.update({ + id: '/(tests)/with-search', + path: '/with-search', getParentRoute: () => rootRouteImport, } as any) @@ -83,11 +83,11 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/with-search': { - id: '/(tests)/with-search' - path: '/with-search' - fullPath: '/with-search' - preLoaderRoute: typeof testsWithSearchRouteImport + '/(tests)/normal-page': { + id: '/(tests)/normal-page' + path: '/normal-page' + fullPath: '/normal-page' + preLoaderRoute: typeof testsNormalPageRouteImport parentRoute: typeof rootRouteImport } '/(tests)/with-loader': { @@ -97,11 +97,11 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof testsWithLoaderRouteImport parentRoute: typeof rootRouteImport } - '/(tests)/normal-page': { - id: '/(tests)/normal-page' - path: '/normal-page' - fullPath: '/normal-page' - preLoaderRoute: typeof testsNormalPageRouteImport + '/(tests)/with-search': { + id: '/(tests)/with-search' + path: '/with-search' + fullPath: '/with-search' + preLoaderRoute: typeof testsWithSearchRouteImport parentRoute: typeof rootRouteImport } } diff --git a/e2e/vue-start/selective-ssr/src/routeTree.gen.ts b/e2e/vue-start/selective-ssr/src/routeTree.gen.ts index 474705b7e7..0d44b648ee 100644 --- a/e2e/vue-start/selective-ssr/src/routeTree.gen.ts +++ b/e2e/vue-start/selective-ssr/src/routeTree.gen.ts @@ -9,20 +9,20 @@ // 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 PostsRouteImport } from './routes/posts' import { Route as IndexRouteImport } from './routes/index' +import { Route as PostsRouteImport } from './routes/posts' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', + getParentRoute: () => rootRouteImport, +} as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', @@ -60,13 +60,6 @@ export interface RootRouteChildren { declare module '@tanstack/vue-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -74,6 +67,13 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport + parentRoute: typeof rootRouteImport + } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' diff --git a/e2e/vue-start/serialization-adapters/src/routeTree.gen.ts b/e2e/vue-start/serialization-adapters/src/routeTree.gen.ts index 79b17ac82b..c684bbc82c 100644 --- a/e2e/vue-start/serialization-adapters/src/routeTree.gen.ts +++ b/e2e/vue-start/serialization-adapters/src/routeTree.gen.ts @@ -10,25 +10,26 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' -import { Route as SsrStreamRouteImport } from './routes/ssr/stream' -import { Route as SsrNestedRouteImport } from './routes/ssr/nested' -import { Route as SsrDataOnlyRouteImport } from './routes/ssr/data-only' -import { Route as ServerFunctionNestedRouteImport } from './routes/server-function/nested' import { Route as ServerFunctionCustomErrorRouteImport } from './routes/server-function/custom-error' +import { Route as ServerFunctionNestedRouteImport } from './routes/server-function/nested' +import { Route as SsrDataOnlyRouteImport } from './routes/ssr/data-only' +import { Route as SsrNestedRouteImport } from './routes/ssr/nested' +import { Route as SsrStreamRouteImport } from './routes/ssr/stream' const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) -const SsrStreamRoute = SsrStreamRouteImport.update({ - id: '/ssr/stream', - path: '/ssr/stream', - getParentRoute: () => rootRouteImport, -} as any) -const SsrNestedRoute = SsrNestedRouteImport.update({ - id: '/ssr/nested', - path: '/ssr/nested', +const ServerFunctionCustomErrorRoute = + ServerFunctionCustomErrorRouteImport.update({ + id: '/server-function/custom-error', + path: '/server-function/custom-error', + getParentRoute: () => rootRouteImport, + } as any) +const ServerFunctionNestedRoute = ServerFunctionNestedRouteImport.update({ + id: '/server-function/nested', + path: '/server-function/nested', getParentRoute: () => rootRouteImport, } as any) const SsrDataOnlyRoute = SsrDataOnlyRouteImport.update({ @@ -36,17 +37,16 @@ const SsrDataOnlyRoute = SsrDataOnlyRouteImport.update({ path: '/ssr/data-only', getParentRoute: () => rootRouteImport, } as any) -const ServerFunctionNestedRoute = ServerFunctionNestedRouteImport.update({ - id: '/server-function/nested', - path: '/server-function/nested', +const SsrNestedRoute = SsrNestedRouteImport.update({ + id: '/ssr/nested', + path: '/ssr/nested', + getParentRoute: () => rootRouteImport, +} as any) +const SsrStreamRoute = SsrStreamRouteImport.update({ + id: '/ssr/stream', + path: '/ssr/stream', getParentRoute: () => rootRouteImport, } as any) -const ServerFunctionCustomErrorRoute = - ServerFunctionCustomErrorRouteImport.update({ - id: '/server-function/custom-error', - path: '/server-function/custom-error', - getParentRoute: () => rootRouteImport, - } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -118,18 +118,18 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/ssr/stream': { - id: '/ssr/stream' - path: '/ssr/stream' - fullPath: '/ssr/stream' - preLoaderRoute: typeof SsrStreamRouteImport + '/server-function/custom-error': { + id: '/server-function/custom-error' + path: '/server-function/custom-error' + fullPath: '/server-function/custom-error' + preLoaderRoute: typeof ServerFunctionCustomErrorRouteImport parentRoute: typeof rootRouteImport } - '/ssr/nested': { - id: '/ssr/nested' - path: '/ssr/nested' - fullPath: '/ssr/nested' - preLoaderRoute: typeof SsrNestedRouteImport + '/server-function/nested': { + id: '/server-function/nested' + path: '/server-function/nested' + fullPath: '/server-function/nested' + preLoaderRoute: typeof ServerFunctionNestedRouteImport parentRoute: typeof rootRouteImport } '/ssr/data-only': { @@ -139,18 +139,18 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof SsrDataOnlyRouteImport parentRoute: typeof rootRouteImport } - '/server-function/nested': { - id: '/server-function/nested' - path: '/server-function/nested' - fullPath: '/server-function/nested' - preLoaderRoute: typeof ServerFunctionNestedRouteImport + '/ssr/nested': { + id: '/ssr/nested' + path: '/ssr/nested' + fullPath: '/ssr/nested' + preLoaderRoute: typeof SsrNestedRouteImport parentRoute: typeof rootRouteImport } - '/server-function/custom-error': { - id: '/server-function/custom-error' - path: '/server-function/custom-error' - fullPath: '/server-function/custom-error' - preLoaderRoute: typeof ServerFunctionCustomErrorRouteImport + '/ssr/stream': { + id: '/ssr/stream' + path: '/ssr/stream' + fullPath: '/ssr/stream' + preLoaderRoute: typeof SsrStreamRouteImport parentRoute: typeof rootRouteImport } } diff --git a/e2e/vue-start/server-functions/src/routeTree.gen.ts b/e2e/vue-start/server-functions/src/routeTree.gen.ts index 43a6eb4ae6..96e5b7ba29 100644 --- a/e2e/vue-start/server-functions/src/routeTree.gen.ts +++ b/e2e/vue-start/server-functions/src/routeTree.gen.ts @@ -9,64 +9,64 @@ // 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 SubmitPostFormdataRouteImport } from './routes/submit-post-formdata' -import { Route as StatusRouteImport } from './routes/status' -import { Route as SerializeFormDataRouteImport } from './routes/serialize-form-data' -import { Route as ReturnNullRouteImport } from './routes/return-null' -import { Route as RawResponseRouteImport } from './routes/raw-response' -import { Route as MultipartRouteImport } from './routes/multipart' -import { Route as IsomorphicFnsRouteImport } from './routes/isomorphic-fns' -import { Route as HeadersRouteImport } from './routes/headers' -import { Route as EnvOnlyRouteImport } from './routes/env-only' -import { Route as DeadCodePreserveRouteImport } from './routes/dead-code-preserve' -import { Route as ConsistentRouteImport } from './routes/consistent' -import { Route as AbortSignalRouteImport } from './routes/abort-signal' import { Route as IndexRouteImport } from './routes/index' -import { Route as RedirectTestIndexRouteImport } from './routes/redirect-test/index' -import { Route as RedirectTestSsrIndexRouteImport } from './routes/redirect-test-ssr/index' -import { Route as PrimitivesIndexRouteImport } from './routes/primitives/index' -import { Route as MiddlewareIndexRouteImport } from './routes/middleware/index' -import { Route as FunctionMethodIndexRouteImport } from './routes/function-method/index' -import { Route as FunctionMetadataIndexRouteImport } from './routes/function-metadata/index' -import { Route as FormdataRedirectIndexRouteImport } from './routes/formdata-redirect/index' -import { Route as FactoryIndexRouteImport } from './routes/factory/index' +import { Route as AbortSignalRouteImport } from './routes/abort-signal' +import { Route as ConsistentRouteImport } from './routes/consistent' +import { Route as DeadCodePreserveRouteImport } from './routes/dead-code-preserve' +import { Route as EnvOnlyRouteImport } from './routes/env-only' +import { Route as HeadersRouteImport } from './routes/headers' +import { Route as IsomorphicFnsRouteImport } from './routes/isomorphic-fns' +import { Route as MultipartRouteImport } from './routes/multipart' +import { Route as RawResponseRouteImport } from './routes/raw-response' +import { Route as ReturnNullRouteImport } from './routes/return-null' +import { Route as SerializeFormDataRouteImport } from './routes/serialize-form-data' +import { Route as StatusRouteImport } from './routes/status' +import { Route as SubmitPostFormdataRouteImport } from './routes/submit-post-formdata' import { Route as CookiesIndexRouteImport } from './routes/cookies/index' -import { Route as RedirectTestTargetRouteImport } from './routes/redirect-test/target' -import { Route as RedirectTestSsrTargetRouteImport } from './routes/redirect-test-ssr/target' -import { Route as MiddlewareSendServerFnRouteImport } from './routes/middleware/send-serverFn' -import { Route as MiddlewareRequestMiddlewareRouteImport } from './routes/middleware/request-middleware' -import { Route as MiddlewareClientMiddlewareRouterRouteImport } from './routes/middleware/client-middleware-router' import { Route as CookiesSetRouteImport } from './routes/cookies/set' +import { Route as FactoryIndexRouteImport } from './routes/factory/index' +import { Route as FormdataRedirectIndexRouteImport } from './routes/formdata-redirect/index' +import { Route as FunctionMetadataIndexRouteImport } from './routes/function-metadata/index' +import { Route as FunctionMethodIndexRouteImport } from './routes/function-method/index' +import { Route as MiddlewareIndexRouteImport } from './routes/middleware/index' +import { Route as MiddlewareClientMiddlewareRouterRouteImport } from './routes/middleware/client-middleware-router' +import { Route as MiddlewareRequestMiddlewareRouteImport } from './routes/middleware/request-middleware' +import { Route as MiddlewareSendServerFnRouteImport } from './routes/middleware/send-serverFn' +import { Route as PrimitivesIndexRouteImport } from './routes/primitives/index' +import { Route as RedirectTestSsrIndexRouteImport } from './routes/redirect-test-ssr/index' +import { Route as RedirectTestSsrTargetRouteImport } from './routes/redirect-test-ssr/target' +import { Route as RedirectTestIndexRouteImport } from './routes/redirect-test/index' +import { Route as RedirectTestTargetRouteImport } from './routes/redirect-test/target' import { Route as FormdataRedirectTargetNameRouteImport } from './routes/formdata-redirect/target.$name' -const SubmitPostFormdataRoute = SubmitPostFormdataRouteImport.update({ - id: '/submit-post-formdata', - path: '/submit-post-formdata', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const StatusRoute = StatusRouteImport.update({ - id: '/status', - path: '/status', +const AbortSignalRoute = AbortSignalRouteImport.update({ + id: '/abort-signal', + path: '/abort-signal', getParentRoute: () => rootRouteImport, } as any) -const SerializeFormDataRoute = SerializeFormDataRouteImport.update({ - id: '/serialize-form-data', - path: '/serialize-form-data', +const ConsistentRoute = ConsistentRouteImport.update({ + id: '/consistent', + path: '/consistent', getParentRoute: () => rootRouteImport, } as any) -const ReturnNullRoute = ReturnNullRouteImport.update({ - id: '/return-null', - path: '/return-null', +const DeadCodePreserveRoute = DeadCodePreserveRouteImport.update({ + id: '/dead-code-preserve', + path: '/dead-code-preserve', getParentRoute: () => rootRouteImport, } as any) -const RawResponseRoute = RawResponseRouteImport.update({ - id: '/raw-response', - path: '/raw-response', +const EnvOnlyRoute = EnvOnlyRouteImport.update({ + id: '/env-only', + path: '/env-only', getParentRoute: () => rootRouteImport, } as any) -const MultipartRoute = MultipartRouteImport.update({ - id: '/multipart', - path: '/multipart', +const HeadersRoute = HeadersRouteImport.update({ + id: '/headers', + path: '/headers', getParentRoute: () => rootRouteImport, } as any) const IsomorphicFnsRoute = IsomorphicFnsRouteImport.update({ @@ -74,59 +74,54 @@ const IsomorphicFnsRoute = IsomorphicFnsRouteImport.update({ path: '/isomorphic-fns', getParentRoute: () => rootRouteImport, } as any) -const HeadersRoute = HeadersRouteImport.update({ - id: '/headers', - path: '/headers', - getParentRoute: () => rootRouteImport, -} as any) -const EnvOnlyRoute = EnvOnlyRouteImport.update({ - id: '/env-only', - path: '/env-only', +const MultipartRoute = MultipartRouteImport.update({ + id: '/multipart', + path: '/multipart', getParentRoute: () => rootRouteImport, } as any) -const DeadCodePreserveRoute = DeadCodePreserveRouteImport.update({ - id: '/dead-code-preserve', - path: '/dead-code-preserve', +const RawResponseRoute = RawResponseRouteImport.update({ + id: '/raw-response', + path: '/raw-response', getParentRoute: () => rootRouteImport, } as any) -const ConsistentRoute = ConsistentRouteImport.update({ - id: '/consistent', - path: '/consistent', +const ReturnNullRoute = ReturnNullRouteImport.update({ + id: '/return-null', + path: '/return-null', getParentRoute: () => rootRouteImport, } as any) -const AbortSignalRoute = AbortSignalRouteImport.update({ - id: '/abort-signal', - path: '/abort-signal', +const SerializeFormDataRoute = SerializeFormDataRouteImport.update({ + id: '/serialize-form-data', + path: '/serialize-form-data', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const StatusRoute = StatusRouteImport.update({ + id: '/status', + path: '/status', getParentRoute: () => rootRouteImport, } as any) -const RedirectTestIndexRoute = RedirectTestIndexRouteImport.update({ - id: '/redirect-test/', - path: '/redirect-test/', +const SubmitPostFormdataRoute = SubmitPostFormdataRouteImport.update({ + id: '/submit-post-formdata', + path: '/submit-post-formdata', getParentRoute: () => rootRouteImport, } as any) -const RedirectTestSsrIndexRoute = RedirectTestSsrIndexRouteImport.update({ - id: '/redirect-test-ssr/', - path: '/redirect-test-ssr/', +const CookiesIndexRoute = CookiesIndexRouteImport.update({ + id: '/cookies/', + path: '/cookies/', getParentRoute: () => rootRouteImport, } as any) -const PrimitivesIndexRoute = PrimitivesIndexRouteImport.update({ - id: '/primitives/', - path: '/primitives/', +const CookiesSetRoute = CookiesSetRouteImport.update({ + id: '/cookies/set', + path: '/cookies/set', getParentRoute: () => rootRouteImport, } as any) -const MiddlewareIndexRoute = MiddlewareIndexRouteImport.update({ - id: '/middleware/', - path: '/middleware/', +const FactoryIndexRoute = FactoryIndexRouteImport.update({ + id: '/factory/', + path: '/factory/', getParentRoute: () => rootRouteImport, } as any) -const FunctionMethodIndexRoute = FunctionMethodIndexRouteImport.update({ - id: '/function-method/', - path: '/function-method/', +const FormdataRedirectIndexRoute = FormdataRedirectIndexRouteImport.update({ + id: '/formdata-redirect/', + path: '/formdata-redirect/', getParentRoute: () => rootRouteImport, } as any) const FunctionMetadataIndexRoute = FunctionMetadataIndexRouteImport.update({ @@ -134,24 +129,41 @@ const FunctionMetadataIndexRoute = FunctionMetadataIndexRouteImport.update({ path: '/function-metadata/', getParentRoute: () => rootRouteImport, } as any) -const FormdataRedirectIndexRoute = FormdataRedirectIndexRouteImport.update({ - id: '/formdata-redirect/', - path: '/formdata-redirect/', +const FunctionMethodIndexRoute = FunctionMethodIndexRouteImport.update({ + id: '/function-method/', + path: '/function-method/', getParentRoute: () => rootRouteImport, } as any) -const FactoryIndexRoute = FactoryIndexRouteImport.update({ - id: '/factory/', - path: '/factory/', +const MiddlewareIndexRoute = MiddlewareIndexRouteImport.update({ + id: '/middleware/', + path: '/middleware/', getParentRoute: () => rootRouteImport, } as any) -const CookiesIndexRoute = CookiesIndexRouteImport.update({ - id: '/cookies/', - path: '/cookies/', +const MiddlewareClientMiddlewareRouterRoute = + MiddlewareClientMiddlewareRouterRouteImport.update({ + id: '/middleware/client-middleware-router', + path: '/middleware/client-middleware-router', + getParentRoute: () => rootRouteImport, + } as any) +const MiddlewareRequestMiddlewareRoute = + MiddlewareRequestMiddlewareRouteImport.update({ + id: '/middleware/request-middleware', + path: '/middleware/request-middleware', + getParentRoute: () => rootRouteImport, + } as any) +const MiddlewareSendServerFnRoute = MiddlewareSendServerFnRouteImport.update({ + id: '/middleware/send-serverFn', + path: '/middleware/send-serverFn', getParentRoute: () => rootRouteImport, } as any) -const RedirectTestTargetRoute = RedirectTestTargetRouteImport.update({ - id: '/redirect-test/target', - path: '/redirect-test/target', +const PrimitivesIndexRoute = PrimitivesIndexRouteImport.update({ + id: '/primitives/', + path: '/primitives/', + getParentRoute: () => rootRouteImport, +} as any) +const RedirectTestSsrIndexRoute = RedirectTestSsrIndexRouteImport.update({ + id: '/redirect-test-ssr/', + path: '/redirect-test-ssr/', getParentRoute: () => rootRouteImport, } as any) const RedirectTestSsrTargetRoute = RedirectTestSsrTargetRouteImport.update({ @@ -159,26 +171,14 @@ const RedirectTestSsrTargetRoute = RedirectTestSsrTargetRouteImport.update({ path: '/redirect-test-ssr/target', getParentRoute: () => rootRouteImport, } as any) -const MiddlewareSendServerFnRoute = MiddlewareSendServerFnRouteImport.update({ - id: '/middleware/send-serverFn', - path: '/middleware/send-serverFn', +const RedirectTestIndexRoute = RedirectTestIndexRouteImport.update({ + id: '/redirect-test/', + path: '/redirect-test/', getParentRoute: () => rootRouteImport, } as any) -const MiddlewareRequestMiddlewareRoute = - MiddlewareRequestMiddlewareRouteImport.update({ - id: '/middleware/request-middleware', - path: '/middleware/request-middleware', - getParentRoute: () => rootRouteImport, - } as any) -const MiddlewareClientMiddlewareRouterRoute = - MiddlewareClientMiddlewareRouterRouteImport.update({ - id: '/middleware/client-middleware-router', - path: '/middleware/client-middleware-router', - getParentRoute: () => rootRouteImport, - } as any) -const CookiesSetRoute = CookiesSetRouteImport.update({ - id: '/cookies/set', - path: '/cookies/set', +const RedirectTestTargetRoute = RedirectTestTargetRouteImport.update({ + id: '/redirect-test/target', + path: '/redirect-test/target', getParentRoute: () => rootRouteImport, } as any) const FormdataRedirectTargetNameRoute = @@ -412,46 +412,46 @@ export interface RootRouteChildren { declare module '@tanstack/vue-router' { interface FileRoutesByPath { - '/submit-post-formdata': { - id: '/submit-post-formdata' - path: '/submit-post-formdata' - fullPath: '/submit-post-formdata' - preLoaderRoute: typeof SubmitPostFormdataRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/status': { - id: '/status' - path: '/status' - fullPath: '/status' - preLoaderRoute: typeof StatusRouteImport + '/abort-signal': { + id: '/abort-signal' + path: '/abort-signal' + fullPath: '/abort-signal' + preLoaderRoute: typeof AbortSignalRouteImport parentRoute: typeof rootRouteImport } - '/serialize-form-data': { - id: '/serialize-form-data' - path: '/serialize-form-data' - fullPath: '/serialize-form-data' - preLoaderRoute: typeof SerializeFormDataRouteImport + '/consistent': { + id: '/consistent' + path: '/consistent' + fullPath: '/consistent' + preLoaderRoute: typeof ConsistentRouteImport parentRoute: typeof rootRouteImport } - '/return-null': { - id: '/return-null' - path: '/return-null' - fullPath: '/return-null' - preLoaderRoute: typeof ReturnNullRouteImport + '/dead-code-preserve': { + id: '/dead-code-preserve' + path: '/dead-code-preserve' + fullPath: '/dead-code-preserve' + preLoaderRoute: typeof DeadCodePreserveRouteImport parentRoute: typeof rootRouteImport } - '/raw-response': { - id: '/raw-response' - path: '/raw-response' - fullPath: '/raw-response' - preLoaderRoute: typeof RawResponseRouteImport + '/env-only': { + id: '/env-only' + path: '/env-only' + fullPath: '/env-only' + preLoaderRoute: typeof EnvOnlyRouteImport parentRoute: typeof rootRouteImport } - '/multipart': { - id: '/multipart' - path: '/multipart' - fullPath: '/multipart' - preLoaderRoute: typeof MultipartRouteImport + '/headers': { + id: '/headers' + path: '/headers' + fullPath: '/headers' + preLoaderRoute: typeof HeadersRouteImport parentRoute: typeof rootRouteImport } '/isomorphic-fns': { @@ -461,81 +461,74 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof IsomorphicFnsRouteImport parentRoute: typeof rootRouteImport } - '/headers': { - id: '/headers' - path: '/headers' - fullPath: '/headers' - preLoaderRoute: typeof HeadersRouteImport - parentRoute: typeof rootRouteImport - } - '/env-only': { - id: '/env-only' - path: '/env-only' - fullPath: '/env-only' - preLoaderRoute: typeof EnvOnlyRouteImport + '/multipart': { + id: '/multipart' + path: '/multipart' + fullPath: '/multipart' + preLoaderRoute: typeof MultipartRouteImport parentRoute: typeof rootRouteImport } - '/dead-code-preserve': { - id: '/dead-code-preserve' - path: '/dead-code-preserve' - fullPath: '/dead-code-preserve' - preLoaderRoute: typeof DeadCodePreserveRouteImport + '/raw-response': { + id: '/raw-response' + path: '/raw-response' + fullPath: '/raw-response' + preLoaderRoute: typeof RawResponseRouteImport parentRoute: typeof rootRouteImport } - '/consistent': { - id: '/consistent' - path: '/consistent' - fullPath: '/consistent' - preLoaderRoute: typeof ConsistentRouteImport + '/return-null': { + id: '/return-null' + path: '/return-null' + fullPath: '/return-null' + preLoaderRoute: typeof ReturnNullRouteImport parentRoute: typeof rootRouteImport } - '/abort-signal': { - id: '/abort-signal' - path: '/abort-signal' - fullPath: '/abort-signal' - preLoaderRoute: typeof AbortSignalRouteImport + '/serialize-form-data': { + id: '/serialize-form-data' + path: '/serialize-form-data' + fullPath: '/serialize-form-data' + preLoaderRoute: typeof SerializeFormDataRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/status': { + id: '/status' + path: '/status' + fullPath: '/status' + preLoaderRoute: typeof StatusRouteImport parentRoute: typeof rootRouteImport } - '/redirect-test/': { - id: '/redirect-test/' - path: '/redirect-test' - fullPath: '/redirect-test/' - preLoaderRoute: typeof RedirectTestIndexRouteImport + '/submit-post-formdata': { + id: '/submit-post-formdata' + path: '/submit-post-formdata' + fullPath: '/submit-post-formdata' + preLoaderRoute: typeof SubmitPostFormdataRouteImport parentRoute: typeof rootRouteImport } - '/redirect-test-ssr/': { - id: '/redirect-test-ssr/' - path: '/redirect-test-ssr' - fullPath: '/redirect-test-ssr/' - preLoaderRoute: typeof RedirectTestSsrIndexRouteImport + '/cookies/': { + id: '/cookies/' + path: '/cookies' + fullPath: '/cookies/' + preLoaderRoute: typeof CookiesIndexRouteImport parentRoute: typeof rootRouteImport } - '/primitives/': { - id: '/primitives/' - path: '/primitives' - fullPath: '/primitives/' - preLoaderRoute: typeof PrimitivesIndexRouteImport + '/cookies/set': { + id: '/cookies/set' + path: '/cookies/set' + fullPath: '/cookies/set' + preLoaderRoute: typeof CookiesSetRouteImport parentRoute: typeof rootRouteImport } - '/middleware/': { - id: '/middleware/' - path: '/middleware' - fullPath: '/middleware/' - preLoaderRoute: typeof MiddlewareIndexRouteImport + '/factory/': { + id: '/factory/' + path: '/factory' + fullPath: '/factory/' + preLoaderRoute: typeof FactoryIndexRouteImport parentRoute: typeof rootRouteImport } - '/function-method/': { - id: '/function-method/' - path: '/function-method' - fullPath: '/function-method/' - preLoaderRoute: typeof FunctionMethodIndexRouteImport + '/formdata-redirect/': { + id: '/formdata-redirect/' + path: '/formdata-redirect' + fullPath: '/formdata-redirect/' + preLoaderRoute: typeof FormdataRedirectIndexRouteImport parentRoute: typeof rootRouteImport } '/function-metadata/': { @@ -545,39 +538,32 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof FunctionMetadataIndexRouteImport parentRoute: typeof rootRouteImport } - '/formdata-redirect/': { - id: '/formdata-redirect/' - path: '/formdata-redirect' - fullPath: '/formdata-redirect/' - preLoaderRoute: typeof FormdataRedirectIndexRouteImport - parentRoute: typeof rootRouteImport - } - '/factory/': { - id: '/factory/' - path: '/factory' - fullPath: '/factory/' - preLoaderRoute: typeof FactoryIndexRouteImport + '/function-method/': { + id: '/function-method/' + path: '/function-method' + fullPath: '/function-method/' + preLoaderRoute: typeof FunctionMethodIndexRouteImport parentRoute: typeof rootRouteImport } - '/cookies/': { - id: '/cookies/' - path: '/cookies' - fullPath: '/cookies/' - preLoaderRoute: typeof CookiesIndexRouteImport + '/middleware/': { + id: '/middleware/' + path: '/middleware' + fullPath: '/middleware/' + preLoaderRoute: typeof MiddlewareIndexRouteImport parentRoute: typeof rootRouteImport } - '/redirect-test/target': { - id: '/redirect-test/target' - path: '/redirect-test/target' - fullPath: '/redirect-test/target' - preLoaderRoute: typeof RedirectTestTargetRouteImport + '/middleware/client-middleware-router': { + id: '/middleware/client-middleware-router' + path: '/middleware/client-middleware-router' + fullPath: '/middleware/client-middleware-router' + preLoaderRoute: typeof MiddlewareClientMiddlewareRouterRouteImport parentRoute: typeof rootRouteImport } - '/redirect-test-ssr/target': { - id: '/redirect-test-ssr/target' - path: '/redirect-test-ssr/target' - fullPath: '/redirect-test-ssr/target' - preLoaderRoute: typeof RedirectTestSsrTargetRouteImport + '/middleware/request-middleware': { + id: '/middleware/request-middleware' + path: '/middleware/request-middleware' + fullPath: '/middleware/request-middleware' + preLoaderRoute: typeof MiddlewareRequestMiddlewareRouteImport parentRoute: typeof rootRouteImport } '/middleware/send-serverFn': { @@ -587,25 +573,39 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof MiddlewareSendServerFnRouteImport parentRoute: typeof rootRouteImport } - '/middleware/request-middleware': { - id: '/middleware/request-middleware' - path: '/middleware/request-middleware' - fullPath: '/middleware/request-middleware' - preLoaderRoute: typeof MiddlewareRequestMiddlewareRouteImport + '/primitives/': { + id: '/primitives/' + path: '/primitives' + fullPath: '/primitives/' + preLoaderRoute: typeof PrimitivesIndexRouteImport parentRoute: typeof rootRouteImport } - '/middleware/client-middleware-router': { - id: '/middleware/client-middleware-router' - path: '/middleware/client-middleware-router' - fullPath: '/middleware/client-middleware-router' - preLoaderRoute: typeof MiddlewareClientMiddlewareRouterRouteImport + '/redirect-test-ssr/': { + id: '/redirect-test-ssr/' + path: '/redirect-test-ssr' + fullPath: '/redirect-test-ssr/' + preLoaderRoute: typeof RedirectTestSsrIndexRouteImport parentRoute: typeof rootRouteImport } - '/cookies/set': { - id: '/cookies/set' - path: '/cookies/set' - fullPath: '/cookies/set' - preLoaderRoute: typeof CookiesSetRouteImport + '/redirect-test-ssr/target': { + id: '/redirect-test-ssr/target' + path: '/redirect-test-ssr/target' + fullPath: '/redirect-test-ssr/target' + preLoaderRoute: typeof RedirectTestSsrTargetRouteImport + parentRoute: typeof rootRouteImport + } + '/redirect-test/': { + id: '/redirect-test/' + path: '/redirect-test' + fullPath: '/redirect-test/' + preLoaderRoute: typeof RedirectTestIndexRouteImport + parentRoute: typeof rootRouteImport + } + '/redirect-test/target': { + id: '/redirect-test/target' + path: '/redirect-test/target' + fullPath: '/redirect-test/target' + preLoaderRoute: typeof RedirectTestTargetRouteImport parentRoute: typeof rootRouteImport } '/formdata-redirect/target/$name': { diff --git a/e2e/vue-start/server-routes/src/routeTree.gen.ts b/e2e/vue-start/server-routes/src/routeTree.gen.ts index 7d9d463772..603560da2a 100644 --- a/e2e/vue-start/server-routes/src/routeTree.gen.ts +++ b/e2e/vue-start/server-routes/src/routeTree.gen.ts @@ -9,16 +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 IndexRouteImport } from './routes/index' import { Route as MergeMiddlewareContextRouteImport } from './routes/merge-middleware-context' import { Route as MethodsRouteRouteImport } from './routes/methods/route' -import { Route as IndexRouteImport } from './routes/index' +import { Route as ApiMiddlewareContextRouteImport } from './routes/api/middleware-context' +import { Route as ApiOnlyAnyRouteImport } from './routes/api/only-any' import { Route as MethodsIndexRouteImport } from './routes/methods/index' import { Route as MethodsOnlyAnyRouteImport } from './routes/methods/only-any' -import { Route as ApiOnlyAnyRouteImport } from './routes/api/only-any' -import { Route as ApiMiddlewareContextRouteImport } from './routes/api/middleware-context' import { Route as ApiParamsFooRouteRouteImport } from './routes/api/params/$foo/route' import { Route as ApiParamsFooBarRouteImport } from './routes/api/params/$foo/$bar' +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) const MergeMiddlewareContextRoute = MergeMiddlewareContextRouteImport.update({ id: '/merge-middleware-context', path: '/merge-middleware-context', @@ -29,9 +34,14 @@ const MethodsRouteRoute = MethodsRouteRouteImport.update({ path: '/methods', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const ApiMiddlewareContextRoute = ApiMiddlewareContextRouteImport.update({ + id: '/api/middleware-context', + path: '/api/middleware-context', + getParentRoute: () => rootRouteImport, +} as any) +const ApiOnlyAnyRoute = ApiOnlyAnyRouteImport.update({ + id: '/api/only-any', + path: '/api/only-any', getParentRoute: () => rootRouteImport, } as any) const MethodsIndexRoute = MethodsIndexRouteImport.update({ @@ -44,16 +54,6 @@ const MethodsOnlyAnyRoute = MethodsOnlyAnyRouteImport.update({ path: '/only-any', getParentRoute: () => MethodsRouteRoute, } as any) -const ApiOnlyAnyRoute = ApiOnlyAnyRouteImport.update({ - id: '/api/only-any', - path: '/api/only-any', - getParentRoute: () => rootRouteImport, -} as any) -const ApiMiddlewareContextRoute = ApiMiddlewareContextRouteImport.update({ - id: '/api/middleware-context', - path: '/api/middleware-context', - getParentRoute: () => rootRouteImport, -} as any) const ApiParamsFooRouteRoute = ApiParamsFooRouteRouteImport.update({ id: '/api/params/$foo', path: '/api/params/$foo', @@ -144,6 +144,13 @@ export interface RootRouteChildren { declare module '@tanstack/vue-router' { interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } '/merge-middleware-context': { id: '/merge-middleware-context' path: '/merge-middleware-context' @@ -158,11 +165,18 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof MethodsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/api/middleware-context': { + id: '/api/middleware-context' + path: '/api/middleware-context' + fullPath: '/api/middleware-context' + preLoaderRoute: typeof ApiMiddlewareContextRouteImport + parentRoute: typeof rootRouteImport + } + '/api/only-any': { + id: '/api/only-any' + path: '/api/only-any' + fullPath: '/api/only-any' + preLoaderRoute: typeof ApiOnlyAnyRouteImport parentRoute: typeof rootRouteImport } '/methods/': { @@ -179,20 +193,6 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof MethodsOnlyAnyRouteImport parentRoute: typeof MethodsRouteRoute } - '/api/only-any': { - id: '/api/only-any' - path: '/api/only-any' - fullPath: '/api/only-any' - preLoaderRoute: typeof ApiOnlyAnyRouteImport - parentRoute: typeof rootRouteImport - } - '/api/middleware-context': { - id: '/api/middleware-context' - path: '/api/middleware-context' - fullPath: '/api/middleware-context' - preLoaderRoute: typeof ApiMiddlewareContextRouteImport - parentRoute: typeof rootRouteImport - } '/api/params/$foo': { id: '/api/params/$foo' path: '/api/params/$foo' diff --git a/e2e/vue-start/spa-mode/src/routeTree.gen.ts b/e2e/vue-start/spa-mode/src/routeTree.gen.ts index 474705b7e7..0d44b648ee 100644 --- a/e2e/vue-start/spa-mode/src/routeTree.gen.ts +++ b/e2e/vue-start/spa-mode/src/routeTree.gen.ts @@ -9,20 +9,20 @@ // 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 PostsRouteImport } from './routes/posts' import { Route as IndexRouteImport } from './routes/index' +import { Route as PostsRouteImport } from './routes/posts' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', + getParentRoute: () => rootRouteImport, +} as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', @@ -60,13 +60,6 @@ export interface RootRouteChildren { declare module '@tanstack/vue-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -74,6 +67,13 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport + parentRoute: typeof rootRouteImport + } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' diff --git a/e2e/vue-start/start-manifest/src/routeTree.gen.ts b/e2e/vue-start/start-manifest/src/routeTree.gen.ts index 357d3be62b..fc42a1e404 100644 --- a/e2e/vue-start/start-manifest/src/routeTree.gen.ts +++ b/e2e/vue-start/start-manifest/src/routeTree.gen.ts @@ -9,54 +9,49 @@ // 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 SharedCRouteImport } from './routes/shared-c' -import { Route as SharedBRouteImport } from './routes/shared-b' -import { Route as SharedARouteImport } from './routes/shared-a' -import { Route as R6RouteImport } from './routes/r6' -import { Route as R5RouteImport } from './routes/r5' -import { Route as R4RouteImport } from './routes/r4' -import { Route as R3RouteImport } from './routes/r3' -import { Route as R2RouteImport } from './routes/r2' -import { Route as R1RouteImport } from './routes/r1' -import { Route as LazyCssStaticRouteImport } from './routes/lazy-css-static' -import { Route as LazyCssLazyRouteImport } from './routes/lazy-css-lazy' -import { Route as BRouteImport } from './routes/b' -import { Route as ARouteImport } from './routes/a' import { Route as IndexRouteImport } from './routes/index' +import { Route as ARouteImport } from './routes/a' +import { Route as BRouteImport } from './routes/b' +import { Route as LazyCssLazyRouteImport } from './routes/lazy-css-lazy' +import { Route as LazyCssStaticRouteImport } from './routes/lazy-css-static' +import { Route as R1RouteImport } from './routes/r1' +import { Route as R2RouteImport } from './routes/r2' +import { Route as R3RouteImport } from './routes/r3' +import { Route as R4RouteImport } from './routes/r4' +import { Route as R5RouteImport } from './routes/r5' +import { Route as R6RouteImport } from './routes/r6' +import { Route as SharedARouteImport } from './routes/shared-a' +import { Route as SharedBRouteImport } from './routes/shared-b' +import { Route as SharedCRouteImport } from './routes/shared-c' -const SharedCRoute = SharedCRouteImport.update({ - id: '/shared-c', - path: '/shared-c', - getParentRoute: () => rootRouteImport, -} as any) -const SharedBRoute = SharedBRouteImport.update({ - id: '/shared-b', - path: '/shared-b', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const SharedARoute = SharedARouteImport.update({ - id: '/shared-a', - path: '/shared-a', +const ARoute = ARouteImport.update({ + id: '/a', + path: '/a', getParentRoute: () => rootRouteImport, } as any) -const R6Route = R6RouteImport.update({ - id: '/r6', - path: '/r6', +const BRoute = BRouteImport.update({ + id: '/b', + path: '/b', getParentRoute: () => rootRouteImport, } as any) -const R5Route = R5RouteImport.update({ - id: '/r5', - path: '/r5', +const LazyCssLazyRoute = LazyCssLazyRouteImport.update({ + id: '/lazy-css-lazy', + path: '/lazy-css-lazy', getParentRoute: () => rootRouteImport, } as any) -const R4Route = R4RouteImport.update({ - id: '/r4', - path: '/r4', +const LazyCssStaticRoute = LazyCssStaticRouteImport.update({ + id: '/lazy-css-static', + path: '/lazy-css-static', getParentRoute: () => rootRouteImport, } as any) -const R3Route = R3RouteImport.update({ - id: '/r3', - path: '/r3', +const R1Route = R1RouteImport.update({ + id: '/r1', + path: '/r1', getParentRoute: () => rootRouteImport, } as any) const R2Route = R2RouteImport.update({ @@ -64,34 +59,39 @@ const R2Route = R2RouteImport.update({ path: '/r2', getParentRoute: () => rootRouteImport, } as any) -const R1Route = R1RouteImport.update({ - id: '/r1', - path: '/r1', +const R3Route = R3RouteImport.update({ + id: '/r3', + path: '/r3', getParentRoute: () => rootRouteImport, } as any) -const LazyCssStaticRoute = LazyCssStaticRouteImport.update({ - id: '/lazy-css-static', - path: '/lazy-css-static', +const R4Route = R4RouteImport.update({ + id: '/r4', + path: '/r4', getParentRoute: () => rootRouteImport, } as any) -const LazyCssLazyRoute = LazyCssLazyRouteImport.update({ - id: '/lazy-css-lazy', - path: '/lazy-css-lazy', +const R5Route = R5RouteImport.update({ + id: '/r5', + path: '/r5', getParentRoute: () => rootRouteImport, } as any) -const BRoute = BRouteImport.update({ - id: '/b', - path: '/b', +const R6Route = R6RouteImport.update({ + id: '/r6', + path: '/r6', getParentRoute: () => rootRouteImport, } as any) -const ARoute = ARouteImport.update({ - id: '/a', - path: '/a', +const SharedARoute = SharedARouteImport.update({ + id: '/shared-a', + path: '/shared-a', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const SharedBRoute = SharedBRouteImport.update({ + id: '/shared-b', + path: '/shared-b', + getParentRoute: () => rootRouteImport, +} as any) +const SharedCRoute = SharedCRouteImport.update({ + id: '/shared-c', + path: '/shared-c', getParentRoute: () => rootRouteImport, } as any) @@ -214,53 +214,46 @@ export interface RootRouteChildren { declare module '@tanstack/vue-router' { interface FileRoutesByPath { - '/shared-c': { - id: '/shared-c' - path: '/shared-c' - fullPath: '/shared-c' - preLoaderRoute: typeof SharedCRouteImport - parentRoute: typeof rootRouteImport - } - '/shared-b': { - id: '/shared-b' - path: '/shared-b' - fullPath: '/shared-b' - preLoaderRoute: typeof SharedBRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/shared-a': { - id: '/shared-a' - path: '/shared-a' - fullPath: '/shared-a' - preLoaderRoute: typeof SharedARouteImport + '/a': { + id: '/a' + path: '/a' + fullPath: '/a' + preLoaderRoute: typeof ARouteImport parentRoute: typeof rootRouteImport } - '/r6': { - id: '/r6' - path: '/r6' - fullPath: '/r6' - preLoaderRoute: typeof R6RouteImport + '/b': { + id: '/b' + path: '/b' + fullPath: '/b' + preLoaderRoute: typeof BRouteImport parentRoute: typeof rootRouteImport } - '/r5': { - id: '/r5' - path: '/r5' - fullPath: '/r5' - preLoaderRoute: typeof R5RouteImport + '/lazy-css-lazy': { + id: '/lazy-css-lazy' + path: '/lazy-css-lazy' + fullPath: '/lazy-css-lazy' + preLoaderRoute: typeof LazyCssLazyRouteImport parentRoute: typeof rootRouteImport } - '/r4': { - id: '/r4' - path: '/r4' - fullPath: '/r4' - preLoaderRoute: typeof R4RouteImport + '/lazy-css-static': { + id: '/lazy-css-static' + path: '/lazy-css-static' + fullPath: '/lazy-css-static' + preLoaderRoute: typeof LazyCssStaticRouteImport parentRoute: typeof rootRouteImport } - '/r3': { - id: '/r3' - path: '/r3' - fullPath: '/r3' - preLoaderRoute: typeof R3RouteImport + '/r1': { + id: '/r1' + path: '/r1' + fullPath: '/r1' + preLoaderRoute: typeof R1RouteImport parentRoute: typeof rootRouteImport } '/r2': { @@ -270,46 +263,53 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof R2RouteImport parentRoute: typeof rootRouteImport } - '/r1': { - id: '/r1' - path: '/r1' - fullPath: '/r1' - preLoaderRoute: typeof R1RouteImport + '/r3': { + id: '/r3' + path: '/r3' + fullPath: '/r3' + preLoaderRoute: typeof R3RouteImport parentRoute: typeof rootRouteImport } - '/lazy-css-static': { - id: '/lazy-css-static' - path: '/lazy-css-static' - fullPath: '/lazy-css-static' - preLoaderRoute: typeof LazyCssStaticRouteImport + '/r4': { + id: '/r4' + path: '/r4' + fullPath: '/r4' + preLoaderRoute: typeof R4RouteImport parentRoute: typeof rootRouteImport } - '/lazy-css-lazy': { - id: '/lazy-css-lazy' - path: '/lazy-css-lazy' - fullPath: '/lazy-css-lazy' - preLoaderRoute: typeof LazyCssLazyRouteImport + '/r5': { + id: '/r5' + path: '/r5' + fullPath: '/r5' + preLoaderRoute: typeof R5RouteImport parentRoute: typeof rootRouteImport } - '/b': { - id: '/b' - path: '/b' - fullPath: '/b' - preLoaderRoute: typeof BRouteImport + '/r6': { + id: '/r6' + path: '/r6' + fullPath: '/r6' + preLoaderRoute: typeof R6RouteImport parentRoute: typeof rootRouteImport } - '/a': { - id: '/a' - path: '/a' - fullPath: '/a' - preLoaderRoute: typeof ARouteImport + '/shared-a': { + id: '/shared-a' + path: '/shared-a' + fullPath: '/shared-a' + preLoaderRoute: typeof SharedARouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/shared-b': { + id: '/shared-b' + path: '/shared-b' + fullPath: '/shared-b' + preLoaderRoute: typeof SharedBRouteImport + parentRoute: typeof rootRouteImport + } + '/shared-c': { + id: '/shared-c' + path: '/shared-c' + fullPath: '/shared-c' + preLoaderRoute: typeof SharedCRouteImport parentRoute: typeof rootRouteImport } } diff --git a/e2e/vue-start/virtual-routes/src/routeTree.gen.ts b/e2e/vue-start/virtual-routes/src/routeTree.gen.ts index 94664b2e5b..1a1bc2d27b 100644 --- a/e2e/vue-start/virtual-routes/src/routeTree.gen.ts +++ b/e2e/vue-start/virtual-routes/src/routeTree.gen.ts @@ -9,83 +9,83 @@ // 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 pipeRouteImport } from './routes/pipe' -import { Route as postsPostsRouteImport } from './routes/posts/posts' -import { Route as layoutFirstLayoutRouteImport } from './routes/layout/first-layout' import { Route as homeRouteImport } from './routes/home' -import { Route as postsPostsDetailRouteImport } from './routes/posts/posts-detail' +import { Route as layoutFirstLayoutRouteImport } from './routes/layout/first-layout' +import { Route as postsPostsRouteImport } from './routes/posts/posts' +import { Route as pipeRouteImport } from './routes/pipe' import { Route as layoutSecondLayoutRouteImport } from './routes/layout/second-layout' -import { Route as postsPostsHomeRouteImport } from './routes/posts/posts-home' import { Route as ClassicHelloRouteRouteImport } from './routes/file-based-subtree/hello/route' +import { Route as postsPostsHomeRouteImport } from './routes/posts/posts-home' +import { Route as postsPostsDetailRouteImport } from './routes/posts/posts-detail' import { Route as ClassicHelloIndexRouteImport } from './routes/file-based-subtree/hello/index' -import { Route as ClassicHelloWorldRouteImport } from './routes/file-based-subtree/hello/world' import { Route as ClassicHelloUniverseRouteImport } from './routes/file-based-subtree/hello/universe' -import { Route as bRouteImport } from './routes/b' +import { Route as ClassicHelloWorldRouteImport } from './routes/file-based-subtree/hello/world' import { Route as aRouteImport } from './routes/a' +import { Route as bRouteImport } from './routes/b' -const pipeRoute = pipeRouteImport.update({ - id: '/special|pipe', - path: '/special|pipe', - getParentRoute: () => rootRouteImport, -} as any) -const postsPostsRoute = postsPostsRouteImport.update({ - id: '/posts', - path: '/posts', +const homeRoute = homeRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const layoutFirstLayoutRoute = layoutFirstLayoutRouteImport.update({ id: '/_first', getParentRoute: () => rootRouteImport, } as any) -const homeRoute = homeRouteImport.update({ - id: '/', - path: '/', +const postsPostsRoute = postsPostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const postsPostsDetailRoute = postsPostsDetailRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => postsPostsRoute, +const pipeRoute = pipeRouteImport.update({ + id: '/special|pipe', + path: '/special|pipe', + getParentRoute: () => rootRouteImport, } as any) const layoutSecondLayoutRoute = layoutSecondLayoutRouteImport.update({ id: '/_second-layout', getParentRoute: () => layoutFirstLayoutRoute, } as any) +const ClassicHelloRouteRoute = ClassicHelloRouteRouteImport.update({ + id: '/classic/hello', + path: '/classic/hello', + getParentRoute: () => rootRouteImport, +} as any) const postsPostsHomeRoute = postsPostsHomeRouteImport.update({ id: '/', path: '/', getParentRoute: () => postsPostsRoute, } as any) -const ClassicHelloRouteRoute = ClassicHelloRouteRouteImport.update({ - id: '/classic/hello', - path: '/classic/hello', - getParentRoute: () => rootRouteImport, +const postsPostsDetailRoute = postsPostsDetailRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => postsPostsRoute, } as any) const ClassicHelloIndexRoute = ClassicHelloIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => ClassicHelloRouteRoute, } as any) -const ClassicHelloWorldRoute = ClassicHelloWorldRouteImport.update({ - id: '/world', - path: '/world', - getParentRoute: () => ClassicHelloRouteRoute, -} as any) const ClassicHelloUniverseRoute = ClassicHelloUniverseRouteImport.update({ id: '/universe', path: '/universe', getParentRoute: () => ClassicHelloRouteRoute, } as any) -const bRoute = bRouteImport.update({ - id: '/route-without-file/layout-b', - path: '/route-without-file/layout-b', - getParentRoute: () => layoutSecondLayoutRoute, +const ClassicHelloWorldRoute = ClassicHelloWorldRouteImport.update({ + id: '/world', + path: '/world', + getParentRoute: () => ClassicHelloRouteRoute, } as any) const aRoute = aRouteImport.update({ id: '/route-without-file/layout-a', path: '/route-without-file/layout-a', getParentRoute: () => layoutSecondLayoutRoute, } as any) +const bRoute = bRouteImport.update({ + id: '/route-without-file/layout-b', + path: '/route-without-file/layout-b', + getParentRoute: () => layoutSecondLayoutRoute, +} as any) export interface FileRoutesByFullPath { '/': typeof homeRoute @@ -179,18 +179,11 @@ export interface RootRouteChildren { declare module '@tanstack/vue-router' { interface FileRoutesByPath { - '/special|pipe': { - id: '/special|pipe' - path: '/special|pipe' - fullPath: '/special|pipe' - preLoaderRoute: typeof pipeRouteImport - parentRoute: typeof rootRouteImport - } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof postsPostsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof homeRouteImport parentRoute: typeof rootRouteImport } '/_first': { @@ -200,19 +193,19 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof layoutFirstLayoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof homeRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof postsPostsRouteImport parentRoute: typeof rootRouteImport } - '/posts/$postId': { - id: '/posts/$postId' - path: '/$postId' - fullPath: '/posts/$postId' - preLoaderRoute: typeof postsPostsDetailRouteImport - parentRoute: typeof postsPostsRoute + '/special|pipe': { + id: '/special|pipe' + path: '/special|pipe' + fullPath: '/special|pipe' + preLoaderRoute: typeof pipeRouteImport + parentRoute: typeof rootRouteImport } '/_first/_second-layout': { id: '/_first/_second-layout' @@ -221,6 +214,13 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof layoutSecondLayoutRouteImport parentRoute: typeof layoutFirstLayoutRoute } + '/classic/hello': { + id: '/classic/hello' + path: '/classic/hello' + fullPath: '/classic/hello' + preLoaderRoute: typeof ClassicHelloRouteRouteImport + parentRoute: typeof rootRouteImport + } '/posts/': { id: '/posts/' path: '/' @@ -228,12 +228,12 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof postsPostsHomeRouteImport parentRoute: typeof postsPostsRoute } - '/classic/hello': { - id: '/classic/hello' - path: '/classic/hello' - fullPath: '/classic/hello' - preLoaderRoute: typeof ClassicHelloRouteRouteImport - parentRoute: typeof rootRouteImport + '/posts/$postId': { + id: '/posts/$postId' + path: '/$postId' + fullPath: '/posts/$postId' + preLoaderRoute: typeof postsPostsDetailRouteImport + parentRoute: typeof postsPostsRoute } '/classic/hello/': { id: '/classic/hello/' @@ -242,13 +242,6 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof ClassicHelloIndexRouteImport parentRoute: typeof ClassicHelloRouteRoute } - '/classic/hello/world': { - id: '/classic/hello/world' - path: '/world' - fullPath: '/classic/hello/world' - preLoaderRoute: typeof ClassicHelloWorldRouteImport - parentRoute: typeof ClassicHelloRouteRoute - } '/classic/hello/universe': { id: '/classic/hello/universe' path: '/universe' @@ -256,12 +249,12 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof ClassicHelloUniverseRouteImport parentRoute: typeof ClassicHelloRouteRoute } - '/_first/_second-layout/route-without-file/layout-b': { - id: '/_first/_second-layout/route-without-file/layout-b' - path: '/route-without-file/layout-b' - fullPath: '/route-without-file/layout-b' - preLoaderRoute: typeof bRouteImport - parentRoute: typeof layoutSecondLayoutRoute + '/classic/hello/world': { + id: '/classic/hello/world' + path: '/world' + fullPath: '/classic/hello/world' + preLoaderRoute: typeof ClassicHelloWorldRouteImport + parentRoute: typeof ClassicHelloRouteRoute } '/_first/_second-layout/route-without-file/layout-a': { id: '/_first/_second-layout/route-without-file/layout-a' @@ -270,6 +263,13 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof aRouteImport parentRoute: typeof layoutSecondLayoutRoute } + '/_first/_second-layout/route-without-file/layout-b': { + id: '/_first/_second-layout/route-without-file/layout-b' + path: '/route-without-file/layout-b' + fullPath: '/route-without-file/layout-b' + preLoaderRoute: typeof bRouteImport + parentRoute: typeof layoutSecondLayoutRoute + } } } diff --git a/e2e/vue-start/website/src/routeTree.gen.ts b/e2e/vue-start/website/src/routeTree.gen.ts index e7b58aae2a..0909725a4b 100644 --- a/e2e/vue-start/website/src/routeTree.gen.ts +++ b/e2e/vue-start/website/src/routeTree.gen.ts @@ -10,11 +10,11 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as LibraryRouteImport } from './routes/_library' -import { Route as LibraryIndexRouteImport } from './routes/_library.index' import { Route as ProjectIndexRouteImport } from './routes/$project.index' +import { Route as LibraryIndexRouteImport } from './routes/_library.index' import { Route as LibraryProjectRouteImport } from './routes/_library.$project' -import { Route as LibraryProjectVersionIndexRouteImport } from './routes/_library.$project.$version.index' import { Route as ProjectVersionDocsIndexRouteImport } from './routes/$project.$version.docs.index' +import { Route as LibraryProjectVersionIndexRouteImport } from './routes/_library.$project.$version.index' import { Route as ProjectVersionDocsFrameworkFrameworkRouteImport } from './routes/$project.$version.docs.framework.$framework' import { Route as ProjectVersionDocsFrameworkFrameworkIndexRouteImport } from './routes/$project.$version.docs.framework.$framework.index' import { Route as ProjectVersionDocsFrameworkFrameworkSplatRouteImport } from './routes/$project.$version.docs.framework.$framework.$' @@ -24,32 +24,32 @@ const LibraryRoute = LibraryRouteImport.update({ id: '/_library', getParentRoute: () => rootRouteImport, } as any) -const LibraryIndexRoute = LibraryIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => LibraryRoute, -} as any) const ProjectIndexRoute = ProjectIndexRouteImport.update({ id: '/$project/', path: '/$project/', getParentRoute: () => rootRouteImport, } as any) +const LibraryIndexRoute = LibraryIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => LibraryRoute, +} as any) const LibraryProjectRoute = LibraryProjectRouteImport.update({ id: '/$project', path: '/$project', getParentRoute: () => LibraryRoute, } as any) +const ProjectVersionDocsIndexRoute = ProjectVersionDocsIndexRouteImport.update({ + id: '/$project/$version/docs/', + path: '/$project/$version/docs/', + getParentRoute: () => rootRouteImport, +} as any) const LibraryProjectVersionIndexRoute = LibraryProjectVersionIndexRouteImport.update({ id: '/$version/', path: '/$version/', getParentRoute: () => LibraryProjectRoute, } as any) -const ProjectVersionDocsIndexRoute = ProjectVersionDocsIndexRouteImport.update({ - id: '/$project/$version/docs/', - path: '/$project/$version/docs/', - getParentRoute: () => rootRouteImport, -} as any) const ProjectVersionDocsFrameworkFrameworkRoute = ProjectVersionDocsFrameworkFrameworkRouteImport.update({ id: '/$project/$version/docs/framework/$framework', @@ -159,13 +159,6 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof LibraryRouteImport parentRoute: typeof rootRouteImport } - '/_library/': { - id: '/_library/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof LibraryIndexRouteImport - parentRoute: typeof LibraryRoute - } '/$project/': { id: '/$project/' path: '/$project' @@ -173,6 +166,13 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof ProjectIndexRouteImport parentRoute: typeof rootRouteImport } + '/_library/': { + id: '/_library/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof LibraryIndexRouteImport + parentRoute: typeof LibraryRoute + } '/_library/$project': { id: '/_library/$project' path: '/$project' @@ -180,13 +180,6 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof LibraryProjectRouteImport parentRoute: typeof LibraryRoute } - '/_library/$project/$version/': { - id: '/_library/$project/$version/' - path: '/$version' - fullPath: '/$project/$version/' - preLoaderRoute: typeof LibraryProjectVersionIndexRouteImport - parentRoute: typeof LibraryProjectRoute - } '/$project/$version/docs/': { id: '/$project/$version/docs/' path: '/$project/$version/docs' @@ -194,6 +187,13 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof ProjectVersionDocsIndexRouteImport parentRoute: typeof rootRouteImport } + '/_library/$project/$version/': { + id: '/_library/$project/$version/' + path: '/$version' + fullPath: '/$project/$version/' + preLoaderRoute: typeof LibraryProjectVersionIndexRouteImport + parentRoute: typeof LibraryProjectRoute + } '/$project/$version/docs/framework/$framework': { id: '/$project/$version/docs/framework/$framework' path: '/$project/$version/docs/framework/$framework' diff --git a/examples/react/authenticated-routes-firebase/src/routeTree.gen.ts b/examples/react/authenticated-routes-firebase/src/routeTree.gen.ts index 0e81c27c1c..bcc454ab03 100644 --- a/examples/react/authenticated-routes-firebase/src/routeTree.gen.ts +++ b/examples/react/authenticated-routes-firebase/src/routeTree.gen.ts @@ -9,38 +9,38 @@ // 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 LoginRouteImport } from './routes/login' -import { Route as AuthRouteImport } from './routes/_auth' import { Route as IndexRouteImport } from './routes/index' -import { Route as AuthInvoicesRouteImport } from './routes/_auth.invoices' +import { Route as AuthRouteImport } from './routes/_auth' +import { Route as LoginRouteImport } from './routes/login' import { Route as AuthDashboardRouteImport } from './routes/_auth.dashboard' +import { Route as AuthInvoicesRouteImport } from './routes/_auth.invoices' import { Route as AuthInvoicesIndexRouteImport } from './routes/_auth.invoices.index' import { Route as AuthInvoicesInvoiceIdRouteImport } from './routes/_auth.invoices.$invoiceId' -const LoginRoute = LoginRouteImport.update({ - id: '/login', - path: '/login', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const AuthRoute = AuthRouteImport.update({ id: '/_auth', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const LoginRoute = LoginRouteImport.update({ + id: '/login', + path: '/login', getParentRoute: () => rootRouteImport, } as any) -const AuthInvoicesRoute = AuthInvoicesRouteImport.update({ - id: '/invoices', - path: '/invoices', - getParentRoute: () => AuthRoute, -} as any) const AuthDashboardRoute = AuthDashboardRouteImport.update({ id: '/dashboard', path: '/dashboard', getParentRoute: () => AuthRoute, } as any) +const AuthInvoicesRoute = AuthInvoicesRouteImport.update({ + id: '/invoices', + path: '/invoices', + getParentRoute: () => AuthRoute, +} as any) const AuthInvoicesIndexRoute = AuthInvoicesIndexRouteImport.update({ id: '/', path: '/', @@ -107,11 +107,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/login': { - id: '/login' - path: '/login' - fullPath: '/login' - preLoaderRoute: typeof LoginRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/_auth': { @@ -121,20 +121,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AuthRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/login': { + id: '/login' + path: '/login' + fullPath: '/login' + preLoaderRoute: typeof LoginRouteImport parentRoute: typeof rootRouteImport } - '/_auth/invoices': { - id: '/_auth/invoices' - path: '/invoices' - fullPath: '/invoices' - preLoaderRoute: typeof AuthInvoicesRouteImport - parentRoute: typeof AuthRoute - } '/_auth/dashboard': { id: '/_auth/dashboard' path: '/dashboard' @@ -142,6 +135,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AuthDashboardRouteImport parentRoute: typeof AuthRoute } + '/_auth/invoices': { + id: '/_auth/invoices' + path: '/invoices' + fullPath: '/invoices' + preLoaderRoute: typeof AuthInvoicesRouteImport + parentRoute: typeof AuthRoute + } '/_auth/invoices/': { id: '/_auth/invoices/' path: '/' diff --git a/examples/react/authenticated-routes/src/routeTree.gen.ts b/examples/react/authenticated-routes/src/routeTree.gen.ts index 0e81c27c1c..bcc454ab03 100644 --- a/examples/react/authenticated-routes/src/routeTree.gen.ts +++ b/examples/react/authenticated-routes/src/routeTree.gen.ts @@ -9,38 +9,38 @@ // 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 LoginRouteImport } from './routes/login' -import { Route as AuthRouteImport } from './routes/_auth' import { Route as IndexRouteImport } from './routes/index' -import { Route as AuthInvoicesRouteImport } from './routes/_auth.invoices' +import { Route as AuthRouteImport } from './routes/_auth' +import { Route as LoginRouteImport } from './routes/login' import { Route as AuthDashboardRouteImport } from './routes/_auth.dashboard' +import { Route as AuthInvoicesRouteImport } from './routes/_auth.invoices' import { Route as AuthInvoicesIndexRouteImport } from './routes/_auth.invoices.index' import { Route as AuthInvoicesInvoiceIdRouteImport } from './routes/_auth.invoices.$invoiceId' -const LoginRoute = LoginRouteImport.update({ - id: '/login', - path: '/login', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const AuthRoute = AuthRouteImport.update({ id: '/_auth', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const LoginRoute = LoginRouteImport.update({ + id: '/login', + path: '/login', getParentRoute: () => rootRouteImport, } as any) -const AuthInvoicesRoute = AuthInvoicesRouteImport.update({ - id: '/invoices', - path: '/invoices', - getParentRoute: () => AuthRoute, -} as any) const AuthDashboardRoute = AuthDashboardRouteImport.update({ id: '/dashboard', path: '/dashboard', getParentRoute: () => AuthRoute, } as any) +const AuthInvoicesRoute = AuthInvoicesRouteImport.update({ + id: '/invoices', + path: '/invoices', + getParentRoute: () => AuthRoute, +} as any) const AuthInvoicesIndexRoute = AuthInvoicesIndexRouteImport.update({ id: '/', path: '/', @@ -107,11 +107,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/login': { - id: '/login' - path: '/login' - fullPath: '/login' - preLoaderRoute: typeof LoginRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/_auth': { @@ -121,20 +121,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AuthRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/login': { + id: '/login' + path: '/login' + fullPath: '/login' + preLoaderRoute: typeof LoginRouteImport parentRoute: typeof rootRouteImport } - '/_auth/invoices': { - id: '/_auth/invoices' - path: '/invoices' - fullPath: '/invoices' - preLoaderRoute: typeof AuthInvoicesRouteImport - parentRoute: typeof AuthRoute - } '/_auth/dashboard': { id: '/_auth/dashboard' path: '/dashboard' @@ -142,6 +135,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AuthDashboardRouteImport parentRoute: typeof AuthRoute } + '/_auth/invoices': { + id: '/_auth/invoices' + path: '/invoices' + fullPath: '/invoices' + preLoaderRoute: typeof AuthInvoicesRouteImport + parentRoute: typeof AuthRoute + } '/_auth/invoices/': { id: '/_auth/invoices/' path: '/' diff --git a/examples/react/basic-file-based/src/routeTree.gen.ts b/examples/react/basic-file-based/src/routeTree.gen.ts index 9d28e9a236..f370ad5d03 100644 --- a/examples/react/basic-file-based/src/routeTree.gen.ts +++ b/examples/react/basic-file-based/src/routeTree.gen.ts @@ -9,35 +9,40 @@ // 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 AnchorRouteImport } from './routes/anchor' +import { Route as IndexRouteImport } from './routes/index' import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' +import { Route as AnchorRouteImport } from './routes/anchor' import { Route as PostsRouteRouteImport } from './routes/posts.route' -import { Route as IndexRouteImport } from './routes/index' +import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' -import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' +import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' -const AnchorRoute = AnchorRouteImport.update({ - id: '/anchor', - path: '/anchor', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, } as any) +const AnchorRoute = AnchorRouteImport.update({ + id: '/anchor', + path: '/anchor', + getParentRoute: () => rootRouteImport, +} as any) const PostsRouteRoute = PostsRouteRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) +const PathlessLayoutNestedLayoutRoute = + PathlessLayoutNestedLayoutRouteImport.update({ + id: '/_nested-layout', + getParentRoute: () => PathlessLayoutRoute, + } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -48,10 +53,11 @@ const PostsPostIdRoute = PostsPostIdRouteImport.update({ path: '/$postId', getParentRoute: () => PostsRouteRoute, } as any) -const PathlessLayoutNestedLayoutRoute = - PathlessLayoutNestedLayoutRouteImport.update({ - id: '/_nested-layout', - getParentRoute: () => PathlessLayoutRoute, +const PathlessLayoutNestedLayoutRouteARoute = + PathlessLayoutNestedLayoutRouteARouteImport.update({ + id: '/route-a', + path: '/route-a', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) const PathlessLayoutNestedLayoutRouteBRoute = PathlessLayoutNestedLayoutRouteBRouteImport.update({ @@ -59,12 +65,6 @@ const PathlessLayoutNestedLayoutRouteBRoute = path: '/route-b', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) -const PathlessLayoutNestedLayoutRouteARoute = - PathlessLayoutNestedLayoutRouteARouteImport.update({ - id: '/route-a', - path: '/route-a', - getParentRoute: () => PathlessLayoutNestedLayoutRoute, - } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -129,11 +129,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/anchor': { - id: '/anchor' - path: '/anchor' - fullPath: '/anchor' - preLoaderRoute: typeof AnchorRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/_pathlessLayout': { @@ -143,6 +143,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PathlessLayoutRouteImport parentRoute: typeof rootRouteImport } + '/anchor': { + id: '/anchor' + path: '/anchor' + fullPath: '/anchor' + preLoaderRoute: typeof AnchorRouteImport + parentRoute: typeof rootRouteImport + } '/posts': { id: '/posts' path: '/posts' @@ -150,12 +157,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' + '/_pathlessLayout/_nested-layout': { + id: '/_pathlessLayout/_nested-layout' + path: '' fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport + parentRoute: typeof PathlessLayoutRoute } '/posts/': { id: '/posts/' @@ -171,12 +178,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRouteRoute } - '/_pathlessLayout/_nested-layout': { - id: '/_pathlessLayout/_nested-layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport - parentRoute: typeof PathlessLayoutRoute + '/_pathlessLayout/_nested-layout/route-a': { + id: '/_pathlessLayout/_nested-layout/route-a' + path: '/route-a' + fullPath: '/route-a' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport + parentRoute: typeof PathlessLayoutNestedLayoutRoute } '/_pathlessLayout/_nested-layout/route-b': { id: '/_pathlessLayout/_nested-layout/route-b' @@ -185,13 +192,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } - '/_pathlessLayout/_nested-layout/route-a': { - id: '/_pathlessLayout/_nested-layout/route-a' - path: '/route-a' - fullPath: '/route-a' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport - parentRoute: typeof PathlessLayoutNestedLayoutRoute - } } } diff --git a/examples/react/basic-react-query-file-based/src/routeTree.gen.ts b/examples/react/basic-react-query-file-based/src/routeTree.gen.ts index 28cfbec8ed..9676858c7c 100644 --- a/examples/react/basic-react-query-file-based/src/routeTree.gen.ts +++ b/examples/react/basic-react-query-file-based/src/routeTree.gen.ts @@ -9,15 +9,20 @@ // 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 IndexRouteImport } from './routes/index' import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as PostsRouteRouteImport } from './routes/posts.route' -import { Route as IndexRouteImport } from './routes/index' +import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' -import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' +import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, @@ -27,11 +32,11 @@ const PostsRouteRoute = PostsRouteRouteImport.update({ path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) +const PathlessLayoutNestedLayoutRoute = + PathlessLayoutNestedLayoutRouteImport.update({ + id: '/_nested-layout', + getParentRoute: () => PathlessLayoutRoute, + } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -42,10 +47,11 @@ const PostsPostIdRoute = PostsPostIdRouteImport.update({ path: '/$postId', getParentRoute: () => PostsRouteRoute, } as any) -const PathlessLayoutNestedLayoutRoute = - PathlessLayoutNestedLayoutRouteImport.update({ - id: '/_nested-layout', - getParentRoute: () => PathlessLayoutRoute, +const PathlessLayoutNestedLayoutRouteARoute = + PathlessLayoutNestedLayoutRouteARouteImport.update({ + id: '/route-a', + path: '/route-a', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) const PathlessLayoutNestedLayoutRouteBRoute = PathlessLayoutNestedLayoutRouteBRouteImport.update({ @@ -53,12 +59,6 @@ const PathlessLayoutNestedLayoutRouteBRoute = path: '/route-b', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) -const PathlessLayoutNestedLayoutRouteARoute = - PathlessLayoutNestedLayoutRouteARouteImport.update({ - id: '/route-a', - path: '/route-a', - getParentRoute: () => PathlessLayoutNestedLayoutRoute, - } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -117,6 +117,13 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } '/_pathlessLayout': { id: '/_pathlessLayout' path: '' @@ -131,12 +138,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' + '/_pathlessLayout/_nested-layout': { + id: '/_pathlessLayout/_nested-layout' + path: '' fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport + parentRoute: typeof PathlessLayoutRoute } '/posts/': { id: '/posts/' @@ -152,12 +159,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRouteRoute } - '/_pathlessLayout/_nested-layout': { - id: '/_pathlessLayout/_nested-layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport - parentRoute: typeof PathlessLayoutRoute + '/_pathlessLayout/_nested-layout/route-a': { + id: '/_pathlessLayout/_nested-layout/route-a' + path: '/route-a' + fullPath: '/route-a' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport + parentRoute: typeof PathlessLayoutNestedLayoutRoute } '/_pathlessLayout/_nested-layout/route-b': { id: '/_pathlessLayout/_nested-layout/route-b' @@ -166,13 +173,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } - '/_pathlessLayout/_nested-layout/route-a': { - id: '/_pathlessLayout/_nested-layout/route-a' - path: '/route-a' - fullPath: '/route-a' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport - parentRoute: typeof PathlessLayoutNestedLayoutRoute - } } } diff --git a/examples/react/basic-ssr-file-based/src/routeTree.gen.ts b/examples/react/basic-ssr-file-based/src/routeTree.gen.ts index 4e61620e43..6f9f3f0f5e 100644 --- a/examples/react/basic-ssr-file-based/src/routeTree.gen.ts +++ b/examples/react/basic-ssr-file-based/src/routeTree.gen.ts @@ -9,12 +9,17 @@ // 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 IndexRouteImport } from './routes/index' import { Route as ErrorRouteImport } from './routes/error' import { Route as PostsRouteRouteImport } from './routes/posts/route' -import { Route as IndexRouteImport } from './routes/index' import { Route as PostsIndexRouteImport } from './routes/posts/index' import { Route as PostsPostIdRouteImport } from './routes/posts/$postId' +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) const ErrorRoute = ErrorRouteImport.update({ id: '/error', path: '/error', @@ -25,11 +30,6 @@ const PostsRouteRoute = PostsRouteRouteImport.update({ path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -78,6 +78,13 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } '/error': { id: '/error' path: '/error' @@ -92,13 +99,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport - } '/posts/': { id: '/posts/' path: '/' diff --git a/examples/react/basic-ssr-streaming-file-based/src/routeTree.gen.ts b/examples/react/basic-ssr-streaming-file-based/src/routeTree.gen.ts index 4e61620e43..6f9f3f0f5e 100644 --- a/examples/react/basic-ssr-streaming-file-based/src/routeTree.gen.ts +++ b/examples/react/basic-ssr-streaming-file-based/src/routeTree.gen.ts @@ -9,12 +9,17 @@ // 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 IndexRouteImport } from './routes/index' import { Route as ErrorRouteImport } from './routes/error' import { Route as PostsRouteRouteImport } from './routes/posts/route' -import { Route as IndexRouteImport } from './routes/index' import { Route as PostsIndexRouteImport } from './routes/posts/index' import { Route as PostsPostIdRouteImport } from './routes/posts/$postId' +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) const ErrorRoute = ErrorRouteImport.update({ id: '/error', path: '/error', @@ -25,11 +30,6 @@ const PostsRouteRoute = PostsRouteRouteImport.update({ path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -78,6 +78,13 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } '/error': { id: '/error' path: '/error' @@ -92,13 +99,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport - } '/posts/': { id: '/posts/' path: '/' diff --git a/examples/react/basic-virtual-file-based/src/routeTree.gen.ts b/examples/react/basic-virtual-file-based/src/routeTree.gen.ts index 2fe7840f19..6745e373aa 100644 --- a/examples/react/basic-virtual-file-based/src/routeTree.gen.ts +++ b/examples/react/basic-virtual-file-based/src/routeTree.gen.ts @@ -9,77 +9,77 @@ // 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 postsPostsRouteImport } from './routes/posts/posts' -import { Route as layoutFirstLayoutRouteImport } from './routes/layout/first-layout' import { Route as homeRouteImport } from './routes/home' -import { Route as postsPostsDetailRouteImport } from './routes/posts/posts-detail' +import { Route as layoutFirstLayoutRouteImport } from './routes/layout/first-layout' +import { Route as postsPostsRouteImport } from './routes/posts/posts' import { Route as layoutSecondLayoutRouteImport } from './routes/layout/second-layout' -import { Route as postsPostsHomeRouteImport } from './routes/posts/posts-home' import { Route as ClassicHelloRouteRouteImport } from './routes/file-based-subtree/hello/route' +import { Route as postsPostsHomeRouteImport } from './routes/posts/posts-home' +import { Route as postsPostsDetailRouteImport } from './routes/posts/posts-detail' import { Route as ClassicHelloIndexRouteImport } from './routes/file-based-subtree/hello/index' -import { Route as ClassicHelloWorldRouteImport } from './routes/file-based-subtree/hello/world' import { Route as ClassicHelloUniverseRouteImport } from './routes/file-based-subtree/hello/universe' -import { Route as bRouteImport } from './routes/b' +import { Route as ClassicHelloWorldRouteImport } from './routes/file-based-subtree/hello/world' import { Route as aRouteImport } from './routes/a' +import { Route as bRouteImport } from './routes/b' -const postsPostsRoute = postsPostsRouteImport.update({ - id: '/posts', - path: '/posts', +const homeRoute = homeRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const layoutFirstLayoutRoute = layoutFirstLayoutRouteImport.update({ id: '/_first', getParentRoute: () => rootRouteImport, } as any) -const homeRoute = homeRouteImport.update({ - id: '/', - path: '/', +const postsPostsRoute = postsPostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const postsPostsDetailRoute = postsPostsDetailRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => postsPostsRoute, -} as any) const layoutSecondLayoutRoute = layoutSecondLayoutRouteImport.update({ id: '/_second-layout', getParentRoute: () => layoutFirstLayoutRoute, } as any) +const ClassicHelloRouteRoute = ClassicHelloRouteRouteImport.update({ + id: '/classic/hello', + path: '/classic/hello', + getParentRoute: () => rootRouteImport, +} as any) const postsPostsHomeRoute = postsPostsHomeRouteImport.update({ id: '/', path: '/', getParentRoute: () => postsPostsRoute, } as any) -const ClassicHelloRouteRoute = ClassicHelloRouteRouteImport.update({ - id: '/classic/hello', - path: '/classic/hello', - getParentRoute: () => rootRouteImport, +const postsPostsDetailRoute = postsPostsDetailRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => postsPostsRoute, } as any) const ClassicHelloIndexRoute = ClassicHelloIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => ClassicHelloRouteRoute, } as any) -const ClassicHelloWorldRoute = ClassicHelloWorldRouteImport.update({ - id: '/world', - path: '/world', - getParentRoute: () => ClassicHelloRouteRoute, -} as any) const ClassicHelloUniverseRoute = ClassicHelloUniverseRouteImport.update({ id: '/universe', path: '/universe', getParentRoute: () => ClassicHelloRouteRoute, } as any) -const bRoute = bRouteImport.update({ - id: '/route-without-file/layout-b', - path: '/route-without-file/layout-b', - getParentRoute: () => layoutSecondLayoutRoute, +const ClassicHelloWorldRoute = ClassicHelloWorldRouteImport.update({ + id: '/world', + path: '/world', + getParentRoute: () => ClassicHelloRouteRoute, } as any) const aRoute = aRouteImport.update({ id: '/route-without-file/layout-a', path: '/route-without-file/layout-a', getParentRoute: () => layoutSecondLayoutRoute, } as any) +const bRoute = bRouteImport.update({ + id: '/route-without-file/layout-b', + path: '/route-without-file/layout-b', + getParentRoute: () => layoutSecondLayoutRoute, +} as any) export interface FileRoutesByFullPath { '/': typeof homeRoute @@ -166,11 +166,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof postsPostsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof homeRouteImport parentRoute: typeof rootRouteImport } '/_first': { @@ -180,20 +180,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof layoutFirstLayoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof homeRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof postsPostsRouteImport parentRoute: typeof rootRouteImport } - '/posts/$postId': { - id: '/posts/$postId' - path: '/$postId' - fullPath: '/posts/$postId' - preLoaderRoute: typeof postsPostsDetailRouteImport - parentRoute: typeof postsPostsRoute - } '/_first/_second-layout': { id: '/_first/_second-layout' path: '' @@ -201,6 +194,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof layoutSecondLayoutRouteImport parentRoute: typeof layoutFirstLayoutRoute } + '/classic/hello': { + id: '/classic/hello' + path: '/classic/hello' + fullPath: '/classic/hello' + preLoaderRoute: typeof ClassicHelloRouteRouteImport + parentRoute: typeof rootRouteImport + } '/posts/': { id: '/posts/' path: '/' @@ -208,12 +208,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof postsPostsHomeRouteImport parentRoute: typeof postsPostsRoute } - '/classic/hello': { - id: '/classic/hello' - path: '/classic/hello' - fullPath: '/classic/hello' - preLoaderRoute: typeof ClassicHelloRouteRouteImport - parentRoute: typeof rootRouteImport + '/posts/$postId': { + id: '/posts/$postId' + path: '/$postId' + fullPath: '/posts/$postId' + preLoaderRoute: typeof postsPostsDetailRouteImport + parentRoute: typeof postsPostsRoute } '/classic/hello/': { id: '/classic/hello/' @@ -222,13 +222,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ClassicHelloIndexRouteImport parentRoute: typeof ClassicHelloRouteRoute } - '/classic/hello/world': { - id: '/classic/hello/world' - path: '/world' - fullPath: '/classic/hello/world' - preLoaderRoute: typeof ClassicHelloWorldRouteImport - parentRoute: typeof ClassicHelloRouteRoute - } '/classic/hello/universe': { id: '/classic/hello/universe' path: '/universe' @@ -236,12 +229,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ClassicHelloUniverseRouteImport parentRoute: typeof ClassicHelloRouteRoute } - '/_first/_second-layout/route-without-file/layout-b': { - id: '/_first/_second-layout/route-without-file/layout-b' - path: '/route-without-file/layout-b' - fullPath: '/route-without-file/layout-b' - preLoaderRoute: typeof bRouteImport - parentRoute: typeof layoutSecondLayoutRoute + '/classic/hello/world': { + id: '/classic/hello/world' + path: '/world' + fullPath: '/classic/hello/world' + preLoaderRoute: typeof ClassicHelloWorldRouteImport + parentRoute: typeof ClassicHelloRouteRoute } '/_first/_second-layout/route-without-file/layout-a': { id: '/_first/_second-layout/route-without-file/layout-a' @@ -250,6 +243,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof aRouteImport parentRoute: typeof layoutSecondLayoutRoute } + '/_first/_second-layout/route-without-file/layout-b': { + id: '/_first/_second-layout/route-without-file/layout-b' + path: '/route-without-file/layout-b' + fullPath: '/route-without-file/layout-b' + preLoaderRoute: typeof bRouteImport + parentRoute: typeof layoutSecondLayoutRoute + } } } diff --git a/examples/react/basic-virtual-inside-file-based/src/routeTree.gen.ts b/examples/react/basic-virtual-inside-file-based/src/routeTree.gen.ts index 947fd037d9..cda1f85f15 100644 --- a/examples/react/basic-virtual-inside-file-based/src/routeTree.gen.ts +++ b/examples/react/basic-virtual-inside-file-based/src/routeTree.gen.ts @@ -9,36 +9,31 @@ // 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 PostsRouteImport } from './routes/posts' -import { Route as LayoutRouteImport } from './routes/_layout' import { Route as IndexRouteImport } from './routes/index' -import { Route as PostsDetailsRouteImport } from './routes/posts/details' +import { Route as LayoutRouteImport } from './routes/_layout' +import { Route as PostsRouteImport } from './routes/posts' import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' import { Route as PostsHomeRouteImport } from './routes/posts/home' -import { Route as PostsLetsGoIndexRouteImport } from './routes/posts/lets-go/index' -import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' +import { Route as PostsDetailsRouteImport } from './routes/posts/details' import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' +import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' +import { Route as PostsLetsGoIndexRouteImport } from './routes/posts/lets-go/index' import { Route as PostsLetsGoDeeperHomeRouteImport } from './routes/posts/lets-go/deeper/home' -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const LayoutRoute = LayoutRouteImport.update({ id: '/_layout', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const PostsDetailsRoute = PostsDetailsRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => PostsRoute, -} as any) const LayoutLayout2Route = LayoutLayout2RouteImport.update({ id: '/_layout-2', getParentRoute: () => LayoutRoute, @@ -48,20 +43,25 @@ const PostsHomeRoute = PostsHomeRouteImport.update({ path: '/', getParentRoute: () => PostsRoute, } as any) -const PostsLetsGoIndexRoute = PostsLetsGoIndexRouteImport.update({ - id: '/inception/', - path: '/inception/', +const PostsDetailsRoute = PostsDetailsRouteImport.update({ + id: '/$postId', + path: '/$postId', getParentRoute: () => PostsRoute, } as any) +const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ + id: '/layout-a', + path: '/layout-a', + getParentRoute: () => LayoutLayout2Route, +} as any) const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ id: '/layout-b', path: '/layout-b', getParentRoute: () => LayoutLayout2Route, } as any) -const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ - id: '/layout-a', - path: '/layout-a', - getParentRoute: () => LayoutLayout2Route, +const PostsLetsGoIndexRoute = PostsLetsGoIndexRouteImport.update({ + id: '/inception/', + path: '/inception/', + getParentRoute: () => PostsRoute, } as any) const PostsLetsGoDeeperHomeRoute = PostsLetsGoDeeperHomeRouteImport.update({ id: '/inception/deeper/', @@ -143,11 +143,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/_layout': { @@ -157,20 +157,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/posts/$postId': { - id: '/posts/$postId' - path: '/$postId' - fullPath: '/posts/$postId' - preLoaderRoute: typeof PostsDetailsRouteImport - parentRoute: typeof PostsRoute - } '/_layout/_layout-2': { id: '/_layout/_layout-2' path: '' @@ -185,13 +178,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsHomeRouteImport parentRoute: typeof PostsRoute } - '/posts/inception/': { - id: '/posts/inception/' - path: '/inception' - fullPath: '/posts/inception/' - preLoaderRoute: typeof PostsLetsGoIndexRouteImport + '/posts/$postId': { + id: '/posts/$postId' + path: '/$postId' + fullPath: '/posts/$postId' + preLoaderRoute: typeof PostsDetailsRouteImport parentRoute: typeof PostsRoute } + '/_layout/_layout-2/layout-a': { + id: '/_layout/_layout-2/layout-a' + path: '/layout-a' + fullPath: '/layout-a' + preLoaderRoute: typeof LayoutLayout2LayoutARouteImport + parentRoute: typeof LayoutLayout2Route + } '/_layout/_layout-2/layout-b': { id: '/_layout/_layout-2/layout-b' path: '/layout-b' @@ -199,12 +199,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport parentRoute: typeof LayoutLayout2Route } - '/_layout/_layout-2/layout-a': { - id: '/_layout/_layout-2/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: typeof LayoutLayout2LayoutARouteImport - parentRoute: typeof LayoutLayout2Route + '/posts/inception/': { + id: '/posts/inception/' + path: '/inception' + fullPath: '/posts/inception/' + preLoaderRoute: typeof PostsLetsGoIndexRouteImport + parentRoute: typeof PostsRoute } '/posts/inception/deeper/': { id: '/posts/inception/deeper/' diff --git a/examples/react/i18n-paraglide/src/routeTree.gen.ts b/examples/react/i18n-paraglide/src/routeTree.gen.ts index 59499d9fbf..12bc916ae4 100644 --- a/examples/react/i18n-paraglide/src/routeTree.gen.ts +++ b/examples/react/i18n-paraglide/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' +import { Route as AboutRouteImport } from './routes/about' -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/examples/react/kitchen-sink-file-based/src/routeTree.gen.ts b/examples/react/kitchen-sink-file-based/src/routeTree.gen.ts index 1e29ba5794..03af4e1e58 100644 --- a/examples/react/kitchen-sink-file-based/src/routeTree.gen.ts +++ b/examples/react/kitchen-sink-file-based/src/routeTree.gen.ts @@ -9,81 +9,71 @@ // 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 LoginRouteImport } from './routes/login' -import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' +import { Route as IndexRouteImport } from './routes/index' import { Route as AuthRouteImport } from './routes/_auth' +import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as DashboardRouteRouteImport } from './routes/dashboard.route' -import { Route as IndexRouteImport } from './routes/index' -import { Route as ExpensiveIndexRouteImport } from './routes/expensive/index' -import { Route as DashboardIndexRouteImport } from './routes/dashboard.index' -import { Route as PathlessLayoutRouteBRouteImport } from './routes/_pathlessLayout.route-b' -import { Route as PathlessLayoutRouteARouteImport } from './routes/_pathlessLayout.route-a' -import { Route as AuthProfileRouteImport } from './routes/_auth.profile' +import { Route as LoginRouteImport } from './routes/login' import { Route as thisFolderIsNotInTheUrlRouteGroupRouteImport } from './routes/(this-folder-is-not-in-the-url)/route-group' -import { Route as DashboardUsersRouteRouteImport } from './routes/dashboard.users.route' +import { Route as AuthProfileRouteImport } from './routes/_auth.profile' +import { Route as PathlessLayoutRouteARouteImport } from './routes/_pathlessLayout.route-a' +import { Route as PathlessLayoutRouteBRouteImport } from './routes/_pathlessLayout.route-b' +import { Route as DashboardIndexRouteImport } from './routes/dashboard.index' import { Route as DashboardInvoicesRouteRouteImport } from './routes/dashboard.invoices.route' -import { Route as DashboardUsersIndexRouteImport } from './routes/dashboard.users.index' +import { Route as DashboardUsersRouteRouteImport } from './routes/dashboard.users.route' +import { Route as ExpensiveIndexRouteImport } from './routes/expensive/index' import { Route as DashboardInvoicesIndexRouteImport } from './routes/dashboard.invoices.index' -import { Route as DashboardUsersUserRouteImport } from './routes/dashboard.users.user' import { Route as DashboardInvoicesInvoiceIdRouteImport } from './routes/dashboard.invoices.$invoiceId' +import { Route as DashboardUsersIndexRouteImport } from './routes/dashboard.users.index' +import { Route as DashboardUsersUserRouteImport } from './routes/dashboard.users.user' -const LoginRoute = LoginRouteImport.update({ - id: '/login', - path: '/login', - getParentRoute: () => rootRouteImport, -} as any) -const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ - id: '/_pathlessLayout', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const AuthRoute = AuthRouteImport.update({ id: '/_auth', getParentRoute: () => rootRouteImport, } as any) +const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ + id: '/_pathlessLayout', + getParentRoute: () => rootRouteImport, +} as any) const DashboardRouteRoute = DashboardRouteRouteImport.update({ id: '/dashboard', path: '/dashboard', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) -const ExpensiveIndexRoute = ExpensiveIndexRouteImport.update({ - id: '/expensive/', - path: '/expensive/', +const LoginRoute = LoginRouteImport.update({ + id: '/login', + path: '/login', getParentRoute: () => rootRouteImport, } as any) -const DashboardIndexRoute = DashboardIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => DashboardRouteRoute, -} as any) -const PathlessLayoutRouteBRoute = PathlessLayoutRouteBRouteImport.update({ - id: '/route-b', - path: '/route-b', - getParentRoute: () => PathlessLayoutRoute, +const thisFolderIsNotInTheUrlRouteGroupRoute = + thisFolderIsNotInTheUrlRouteGroupRouteImport.update({ + id: '/(this-folder-is-not-in-the-url)/route-group', + path: '/route-group', + getParentRoute: () => rootRouteImport, + } as any) +const AuthProfileRoute = AuthProfileRouteImport.update({ + id: '/profile', + path: '/profile', + getParentRoute: () => AuthRoute, } as any) const PathlessLayoutRouteARoute = PathlessLayoutRouteARouteImport.update({ id: '/route-a', path: '/route-a', getParentRoute: () => PathlessLayoutRoute, } as any) -const AuthProfileRoute = AuthProfileRouteImport.update({ - id: '/profile', - path: '/profile', - getParentRoute: () => AuthRoute, +const PathlessLayoutRouteBRoute = PathlessLayoutRouteBRouteImport.update({ + id: '/route-b', + path: '/route-b', + getParentRoute: () => PathlessLayoutRoute, } as any) -const thisFolderIsNotInTheUrlRouteGroupRoute = - thisFolderIsNotInTheUrlRouteGroupRouteImport.update({ - id: '/(this-folder-is-not-in-the-url)/route-group', - path: '/route-group', - getParentRoute: () => rootRouteImport, - } as any) -const DashboardUsersRouteRoute = DashboardUsersRouteRouteImport.update({ - id: '/users', - path: '/users', +const DashboardIndexRoute = DashboardIndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => DashboardRouteRoute, } as any) const DashboardInvoicesRouteRoute = DashboardInvoicesRouteRouteImport.update({ @@ -91,27 +81,37 @@ const DashboardInvoicesRouteRoute = DashboardInvoicesRouteRouteImport.update({ path: '/invoices', getParentRoute: () => DashboardRouteRoute, } as any) -const DashboardUsersIndexRoute = DashboardUsersIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => DashboardUsersRouteRoute, +const DashboardUsersRouteRoute = DashboardUsersRouteRouteImport.update({ + id: '/users', + path: '/users', + getParentRoute: () => DashboardRouteRoute, +} as any) +const ExpensiveIndexRoute = ExpensiveIndexRouteImport.update({ + id: '/expensive/', + path: '/expensive/', + getParentRoute: () => rootRouteImport, } as any) const DashboardInvoicesIndexRoute = DashboardInvoicesIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => DashboardInvoicesRouteRoute, } as any) -const DashboardUsersUserRoute = DashboardUsersUserRouteImport.update({ - id: '/user', - path: '/user', - getParentRoute: () => DashboardUsersRouteRoute, -} as any) const DashboardInvoicesInvoiceIdRoute = DashboardInvoicesInvoiceIdRouteImport.update({ id: '/$invoiceId', path: '/$invoiceId', getParentRoute: () => DashboardInvoicesRouteRoute, } as any) +const DashboardUsersIndexRoute = DashboardUsersIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => DashboardUsersRouteRoute, +} as any) +const DashboardUsersUserRoute = DashboardUsersUserRouteImport.update({ + id: '/user', + path: '/user', + getParentRoute: () => DashboardUsersRouteRoute, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -229,18 +229,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/login': { - id: '/login' - path: '/login' - fullPath: '/login' - preLoaderRoute: typeof LoginRouteImport - parentRoute: typeof rootRouteImport - } - '/_pathlessLayout': { - id: '/_pathlessLayout' - path: '' + '/': { + id: '/' + path: '/' fullPath: '/' - preLoaderRoute: typeof PathlessLayoutRouteImport + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/_auth': { @@ -250,6 +243,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AuthRouteImport parentRoute: typeof rootRouteImport } + '/_pathlessLayout': { + id: '/_pathlessLayout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutRouteImport + parentRoute: typeof rootRouteImport + } '/dashboard': { id: '/dashboard' path: '/dashboard' @@ -257,33 +257,26 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DashboardRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/login': { + id: '/login' + path: '/login' + fullPath: '/login' + preLoaderRoute: typeof LoginRouteImport parentRoute: typeof rootRouteImport } - '/expensive/': { - id: '/expensive/' - path: '/expensive' - fullPath: '/expensive/' - preLoaderRoute: typeof ExpensiveIndexRouteImport + '/(this-folder-is-not-in-the-url)/route-group': { + id: '/(this-folder-is-not-in-the-url)/route-group' + path: '/route-group' + fullPath: '/route-group' + preLoaderRoute: typeof thisFolderIsNotInTheUrlRouteGroupRouteImport parentRoute: typeof rootRouteImport } - '/dashboard/': { - id: '/dashboard/' - path: '/' - fullPath: '/dashboard/' - preLoaderRoute: typeof DashboardIndexRouteImport - parentRoute: typeof DashboardRouteRoute - } - '/_pathlessLayout/route-b': { - id: '/_pathlessLayout/route-b' - path: '/route-b' - fullPath: '/route-b' - preLoaderRoute: typeof PathlessLayoutRouteBRouteImport - parentRoute: typeof PathlessLayoutRoute + '/_auth/profile': { + id: '/_auth/profile' + path: '/profile' + fullPath: '/profile' + preLoaderRoute: typeof AuthProfileRouteImport + parentRoute: typeof AuthRoute } '/_pathlessLayout/route-a': { id: '/_pathlessLayout/route-a' @@ -292,25 +285,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PathlessLayoutRouteARouteImport parentRoute: typeof PathlessLayoutRoute } - '/_auth/profile': { - id: '/_auth/profile' - path: '/profile' - fullPath: '/profile' - preLoaderRoute: typeof AuthProfileRouteImport - parentRoute: typeof AuthRoute - } - '/(this-folder-is-not-in-the-url)/route-group': { - id: '/(this-folder-is-not-in-the-url)/route-group' - path: '/route-group' - fullPath: '/route-group' - preLoaderRoute: typeof thisFolderIsNotInTheUrlRouteGroupRouteImport - parentRoute: typeof rootRouteImport + '/_pathlessLayout/route-b': { + id: '/_pathlessLayout/route-b' + path: '/route-b' + fullPath: '/route-b' + preLoaderRoute: typeof PathlessLayoutRouteBRouteImport + parentRoute: typeof PathlessLayoutRoute } - '/dashboard/users': { - id: '/dashboard/users' - path: '/users' - fullPath: '/dashboard/users' - preLoaderRoute: typeof DashboardUsersRouteRouteImport + '/dashboard/': { + id: '/dashboard/' + path: '/' + fullPath: '/dashboard/' + preLoaderRoute: typeof DashboardIndexRouteImport parentRoute: typeof DashboardRouteRoute } '/dashboard/invoices': { @@ -320,12 +306,19 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DashboardInvoicesRouteRouteImport parentRoute: typeof DashboardRouteRoute } - '/dashboard/users/': { - id: '/dashboard/users/' - path: '/' - fullPath: '/dashboard/users/' - preLoaderRoute: typeof DashboardUsersIndexRouteImport - parentRoute: typeof DashboardUsersRouteRoute + '/dashboard/users': { + id: '/dashboard/users' + path: '/users' + fullPath: '/dashboard/users' + preLoaderRoute: typeof DashboardUsersRouteRouteImport + parentRoute: typeof DashboardRouteRoute + } + '/expensive/': { + id: '/expensive/' + path: '/expensive' + fullPath: '/expensive/' + preLoaderRoute: typeof ExpensiveIndexRouteImport + parentRoute: typeof rootRouteImport } '/dashboard/invoices/': { id: '/dashboard/invoices/' @@ -334,13 +327,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DashboardInvoicesIndexRouteImport parentRoute: typeof DashboardInvoicesRouteRoute } - '/dashboard/users/user': { - id: '/dashboard/users/user' - path: '/user' - fullPath: '/dashboard/users/user' - preLoaderRoute: typeof DashboardUsersUserRouteImport - parentRoute: typeof DashboardUsersRouteRoute - } '/dashboard/invoices/$invoiceId': { id: '/dashboard/invoices/$invoiceId' path: '/$invoiceId' @@ -348,6 +334,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DashboardInvoicesInvoiceIdRouteImport parentRoute: typeof DashboardInvoicesRouteRoute } + '/dashboard/users/': { + id: '/dashboard/users/' + path: '/' + fullPath: '/dashboard/users/' + preLoaderRoute: typeof DashboardUsersIndexRouteImport + parentRoute: typeof DashboardUsersRouteRoute + } + '/dashboard/users/user': { + id: '/dashboard/users/user' + path: '/user' + fullPath: '/dashboard/users/user' + preLoaderRoute: typeof DashboardUsersUserRouteImport + parentRoute: typeof DashboardUsersRouteRoute + } } } diff --git a/examples/react/kitchen-sink-react-query-file-based/src/routeTree.gen.ts b/examples/react/kitchen-sink-react-query-file-based/src/routeTree.gen.ts index 66180a8554..dbce8cf604 100644 --- a/examples/react/kitchen-sink-react-query-file-based/src/routeTree.gen.ts +++ b/examples/react/kitchen-sink-react-query-file-based/src/routeTree.gen.ts @@ -9,108 +9,108 @@ // 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 LoginRouteImport } from './routes/login' -import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' +import { Route as IndexRouteImport } from './routes/index' import { Route as AuthRouteImport } from './routes/_auth' +import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as DashboardRouteRouteImport } from './routes/dashboard.route' -import { Route as IndexRouteImport } from './routes/index' -import { Route as ExpensiveIndexRouteImport } from './routes/expensive/index' -import { Route as DashboardIndexRouteImport } from './routes/dashboard.index' -import { Route as FooBarRouteImport } from './routes/foo/bar' -import { Route as PathlessLayoutRouteBRouteImport } from './routes/_pathlessLayout.route-b' -import { Route as PathlessLayoutRouteARouteImport } from './routes/_pathlessLayout.route-a' +import { Route as LoginRouteImport } from './routes/login' import { Route as AuthProfileRouteImport } from './routes/_auth.profile' -import { Route as DashboardUsersRouteRouteImport } from './routes/dashboard.users.route' +import { Route as PathlessLayoutRouteARouteImport } from './routes/_pathlessLayout.route-a' +import { Route as PathlessLayoutRouteBRouteImport } from './routes/_pathlessLayout.route-b' +import { Route as DashboardIndexRouteImport } from './routes/dashboard.index' import { Route as DashboardInvoicesRouteRouteImport } from './routes/dashboard.invoices.route' -import { Route as DashboardUsersIndexRouteImport } from './routes/dashboard.users.index' +import { Route as DashboardUsersRouteRouteImport } from './routes/dashboard.users.route' +import { Route as ExpensiveIndexRouteImport } from './routes/expensive/index' +import { Route as FooBarRouteImport } from './routes/foo/bar' import { Route as DashboardInvoicesIndexRouteImport } from './routes/dashboard.invoices.index' -import { Route as DashboardUsersUserRouteImport } from './routes/dashboard.users.user' import { Route as DashboardInvoicesInvoiceIdRouteImport } from './routes/dashboard.invoices.$invoiceId' +import { Route as DashboardUsersIndexRouteImport } from './routes/dashboard.users.index' +import { Route as DashboardUsersUserRouteImport } from './routes/dashboard.users.user' -const LoginRoute = LoginRouteImport.update({ - id: '/login', - path: '/login', - getParentRoute: () => rootRouteImport, -} as any) -const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ - id: '/_pathlessLayout', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const AuthRoute = AuthRouteImport.update({ id: '/_auth', getParentRoute: () => rootRouteImport, } as any) +const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ + id: '/_pathlessLayout', + getParentRoute: () => rootRouteImport, +} as any) const DashboardRouteRoute = DashboardRouteRouteImport.update({ id: '/dashboard', path: '/dashboard', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) -const ExpensiveIndexRoute = ExpensiveIndexRouteImport.update({ - id: '/expensive/', - path: '/expensive/', +const LoginRoute = LoginRouteImport.update({ + id: '/login', + path: '/login', getParentRoute: () => rootRouteImport, } as any) -const DashboardIndexRoute = DashboardIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => DashboardRouteRoute, +const AuthProfileRoute = AuthProfileRouteImport.update({ + id: '/profile', + path: '/profile', + getParentRoute: () => AuthRoute, } as any) -const FooBarRoute = FooBarRouteImport.update({ - id: '/foo/bar', - path: '/foo/bar', - getParentRoute: () => rootRouteImport, +const PathlessLayoutRouteARoute = PathlessLayoutRouteARouteImport.update({ + id: '/route-a', + path: '/route-a', + getParentRoute: () => PathlessLayoutRoute, } as any) const PathlessLayoutRouteBRoute = PathlessLayoutRouteBRouteImport.update({ id: '/route-b', path: '/route-b', getParentRoute: () => PathlessLayoutRoute, } as any) -const PathlessLayoutRouteARoute = PathlessLayoutRouteARouteImport.update({ - id: '/route-a', - path: '/route-a', - getParentRoute: () => PathlessLayoutRoute, +const DashboardIndexRoute = DashboardIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => DashboardRouteRoute, } as any) -const AuthProfileRoute = AuthProfileRouteImport.update({ - id: '/profile', - path: '/profile', - getParentRoute: () => AuthRoute, +const DashboardInvoicesRouteRoute = DashboardInvoicesRouteRouteImport.update({ + id: '/invoices', + path: '/invoices', + getParentRoute: () => DashboardRouteRoute, } as any) const DashboardUsersRouteRoute = DashboardUsersRouteRouteImport.update({ id: '/users', path: '/users', getParentRoute: () => DashboardRouteRoute, } as any) -const DashboardInvoicesRouteRoute = DashboardInvoicesRouteRouteImport.update({ - id: '/invoices', - path: '/invoices', - getParentRoute: () => DashboardRouteRoute, +const ExpensiveIndexRoute = ExpensiveIndexRouteImport.update({ + id: '/expensive/', + path: '/expensive/', + getParentRoute: () => rootRouteImport, } as any) -const DashboardUsersIndexRoute = DashboardUsersIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => DashboardUsersRouteRoute, +const FooBarRoute = FooBarRouteImport.update({ + id: '/foo/bar', + path: '/foo/bar', + getParentRoute: () => rootRouteImport, } as any) const DashboardInvoicesIndexRoute = DashboardInvoicesIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => DashboardInvoicesRouteRoute, } as any) -const DashboardUsersUserRoute = DashboardUsersUserRouteImport.update({ - id: '/user', - path: '/user', - getParentRoute: () => DashboardUsersRouteRoute, -} as any) const DashboardInvoicesInvoiceIdRoute = DashboardInvoicesInvoiceIdRouteImport.update({ id: '/$invoiceId', path: '/$invoiceId', getParentRoute: () => DashboardInvoicesRouteRoute, } as any) +const DashboardUsersIndexRoute = DashboardUsersIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => DashboardUsersRouteRoute, +} as any) +const DashboardUsersUserRoute = DashboardUsersUserRouteImport.update({ + id: '/user', + path: '/user', + getParentRoute: () => DashboardUsersRouteRoute, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -228,18 +228,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/login': { - id: '/login' - path: '/login' - fullPath: '/login' - preLoaderRoute: typeof LoginRouteImport - parentRoute: typeof rootRouteImport - } - '/_pathlessLayout': { - id: '/_pathlessLayout' - path: '' + '/': { + id: '/' + path: '/' fullPath: '/' - preLoaderRoute: typeof PathlessLayoutRouteImport + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/_auth': { @@ -249,6 +242,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AuthRouteImport parentRoute: typeof rootRouteImport } + '/_pathlessLayout': { + id: '/_pathlessLayout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutRouteImport + parentRoute: typeof rootRouteImport + } '/dashboard': { id: '/dashboard' path: '/dashboard' @@ -256,33 +256,26 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DashboardRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport - } - '/expensive/': { - id: '/expensive/' - path: '/expensive' - fullPath: '/expensive/' - preLoaderRoute: typeof ExpensiveIndexRouteImport + '/login': { + id: '/login' + path: '/login' + fullPath: '/login' + preLoaderRoute: typeof LoginRouteImport parentRoute: typeof rootRouteImport } - '/dashboard/': { - id: '/dashboard/' - path: '/' - fullPath: '/dashboard/' - preLoaderRoute: typeof DashboardIndexRouteImport - parentRoute: typeof DashboardRouteRoute + '/_auth/profile': { + id: '/_auth/profile' + path: '/profile' + fullPath: '/profile' + preLoaderRoute: typeof AuthProfileRouteImport + parentRoute: typeof AuthRoute } - '/foo/bar': { - id: '/foo/bar' - path: '/foo/bar' - fullPath: '/foo/bar' - preLoaderRoute: typeof FooBarRouteImport - parentRoute: typeof rootRouteImport + '/_pathlessLayout/route-a': { + id: '/_pathlessLayout/route-a' + path: '/route-a' + fullPath: '/route-a' + preLoaderRoute: typeof PathlessLayoutRouteARouteImport + parentRoute: typeof PathlessLayoutRoute } '/_pathlessLayout/route-b': { id: '/_pathlessLayout/route-b' @@ -291,19 +284,19 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PathlessLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutRoute } - '/_pathlessLayout/route-a': { - id: '/_pathlessLayout/route-a' - path: '/route-a' - fullPath: '/route-a' - preLoaderRoute: typeof PathlessLayoutRouteARouteImport - parentRoute: typeof PathlessLayoutRoute + '/dashboard/': { + id: '/dashboard/' + path: '/' + fullPath: '/dashboard/' + preLoaderRoute: typeof DashboardIndexRouteImport + parentRoute: typeof DashboardRouteRoute } - '/_auth/profile': { - id: '/_auth/profile' - path: '/profile' - fullPath: '/profile' - preLoaderRoute: typeof AuthProfileRouteImport - parentRoute: typeof AuthRoute + '/dashboard/invoices': { + id: '/dashboard/invoices' + path: '/invoices' + fullPath: '/dashboard/invoices' + preLoaderRoute: typeof DashboardInvoicesRouteRouteImport + parentRoute: typeof DashboardRouteRoute } '/dashboard/users': { id: '/dashboard/users' @@ -312,19 +305,19 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DashboardUsersRouteRouteImport parentRoute: typeof DashboardRouteRoute } - '/dashboard/invoices': { - id: '/dashboard/invoices' - path: '/invoices' - fullPath: '/dashboard/invoices' - preLoaderRoute: typeof DashboardInvoicesRouteRouteImport - parentRoute: typeof DashboardRouteRoute + '/expensive/': { + id: '/expensive/' + path: '/expensive' + fullPath: '/expensive/' + preLoaderRoute: typeof ExpensiveIndexRouteImport + parentRoute: typeof rootRouteImport } - '/dashboard/users/': { - id: '/dashboard/users/' - path: '/' - fullPath: '/dashboard/users/' - preLoaderRoute: typeof DashboardUsersIndexRouteImport - parentRoute: typeof DashboardUsersRouteRoute + '/foo/bar': { + id: '/foo/bar' + path: '/foo/bar' + fullPath: '/foo/bar' + preLoaderRoute: typeof FooBarRouteImport + parentRoute: typeof rootRouteImport } '/dashboard/invoices/': { id: '/dashboard/invoices/' @@ -333,13 +326,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DashboardInvoicesIndexRouteImport parentRoute: typeof DashboardInvoicesRouteRoute } - '/dashboard/users/user': { - id: '/dashboard/users/user' - path: '/user' - fullPath: '/dashboard/users/user' - preLoaderRoute: typeof DashboardUsersUserRouteImport - parentRoute: typeof DashboardUsersRouteRoute - } '/dashboard/invoices/$invoiceId': { id: '/dashboard/invoices/$invoiceId' path: '/$invoiceId' @@ -347,6 +333,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DashboardInvoicesInvoiceIdRouteImport parentRoute: typeof DashboardInvoicesRouteRoute } + '/dashboard/users/': { + id: '/dashboard/users/' + path: '/' + fullPath: '/dashboard/users/' + preLoaderRoute: typeof DashboardUsersIndexRouteImport + parentRoute: typeof DashboardUsersRouteRoute + } + '/dashboard/users/user': { + id: '/dashboard/users/user' + path: '/user' + fullPath: '/dashboard/users/user' + preLoaderRoute: typeof DashboardUsersUserRouteImport + parentRoute: typeof DashboardUsersRouteRoute + } } } diff --git a/examples/react/large-file-based/src/routeTree.gen.ts b/examples/react/large-file-based/src/routeTree.gen.ts index 93a5be975c..8494f51d73 100644 --- a/examples/react/large-file-based/src/routeTree.gen.ts +++ b/examples/react/large-file-based/src/routeTree.gen.ts @@ -9,23 +9,18 @@ // 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 RelativeRouteImport } from './routes/relative' -import { Route as LinkPropsRouteImport } from './routes/linkProps' +import { Route as IndexRouteImport } from './routes/index' import { Route as AbsoluteRouteImport } from './routes/absolute' -import { Route as SearchRouteRouteImport } from './routes/search/route' +import { Route as LinkPropsRouteImport } from './routes/linkProps' import { Route as ParamsRouteRouteImport } from './routes/params/route' -import { Route as IndexRouteImport } from './routes/index' -import { Route as SearchSearchPlaceholderRouteImport } from './routes/search/searchPlaceholder' +import { Route as RelativeRouteImport } from './routes/relative' +import { Route as SearchRouteRouteImport } from './routes/search/route' import { Route as ParamsParamsPlaceholderRouteImport } from './routes/params/$paramsPlaceholder' +import { Route as SearchSearchPlaceholderRouteImport } from './routes/search/searchPlaceholder' -const RelativeRoute = RelativeRouteImport.update({ - id: '/relative', - path: '/relative', - getParentRoute: () => rootRouteImport, -} as any) -const LinkPropsRoute = LinkPropsRouteImport.update({ - id: '/linkProps', - path: '/linkProps', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const AbsoluteRoute = AbsoluteRouteImport.update({ @@ -33,9 +28,9 @@ const AbsoluteRoute = AbsoluteRouteImport.update({ path: '/absolute', getParentRoute: () => rootRouteImport, } as any) -const SearchRouteRoute = SearchRouteRouteImport.update({ - id: '/search', - path: '/search', +const LinkPropsRoute = LinkPropsRouteImport.update({ + id: '/linkProps', + path: '/linkProps', getParentRoute: () => rootRouteImport, } as any) const ParamsRouteRoute = ParamsRouteRouteImport.update({ @@ -43,21 +38,26 @@ const ParamsRouteRoute = ParamsRouteRouteImport.update({ path: '/params', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const RelativeRoute = RelativeRouteImport.update({ + id: '/relative', + path: '/relative', getParentRoute: () => rootRouteImport, } as any) -const SearchSearchPlaceholderRoute = SearchSearchPlaceholderRouteImport.update({ - id: '/searchPlaceholder', - path: '/searchPlaceholder', - getParentRoute: () => SearchRouteRoute, +const SearchRouteRoute = SearchRouteRouteImport.update({ + id: '/search', + path: '/search', + getParentRoute: () => rootRouteImport, } as any) const ParamsParamsPlaceholderRoute = ParamsParamsPlaceholderRouteImport.update({ id: '/$paramsPlaceholder', path: '/$paramsPlaceholder', getParentRoute: () => ParamsRouteRoute, } as any) +const SearchSearchPlaceholderRoute = SearchSearchPlaceholderRouteImport.update({ + id: '/searchPlaceholder', + path: '/searchPlaceholder', + getParentRoute: () => SearchRouteRoute, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -134,18 +134,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/relative': { - id: '/relative' - path: '/relative' - fullPath: '/relative' - preLoaderRoute: typeof RelativeRouteImport - parentRoute: typeof rootRouteImport - } - '/linkProps': { - id: '/linkProps' - path: '/linkProps' - fullPath: '/linkProps' - preLoaderRoute: typeof LinkPropsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/absolute': { @@ -155,11 +148,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AbsoluteRouteImport parentRoute: typeof rootRouteImport } - '/search': { - id: '/search' - path: '/search' - fullPath: '/search' - preLoaderRoute: typeof SearchRouteRouteImport + '/linkProps': { + id: '/linkProps' + path: '/linkProps' + fullPath: '/linkProps' + preLoaderRoute: typeof LinkPropsRouteImport parentRoute: typeof rootRouteImport } '/params': { @@ -169,19 +162,19 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ParamsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/relative': { + id: '/relative' + path: '/relative' + fullPath: '/relative' + preLoaderRoute: typeof RelativeRouteImport parentRoute: typeof rootRouteImport } - '/search/searchPlaceholder': { - id: '/search/searchPlaceholder' - path: '/searchPlaceholder' - fullPath: '/search/searchPlaceholder' - preLoaderRoute: typeof SearchSearchPlaceholderRouteImport - parentRoute: typeof SearchRouteRoute + '/search': { + id: '/search' + path: '/search' + fullPath: '/search' + preLoaderRoute: typeof SearchRouteRouteImport + parentRoute: typeof rootRouteImport } '/params/$paramsPlaceholder': { id: '/params/$paramsPlaceholder' @@ -190,6 +183,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ParamsParamsPlaceholderRouteImport parentRoute: typeof ParamsRouteRoute } + '/search/searchPlaceholder': { + id: '/search/searchPlaceholder' + path: '/searchPlaceholder' + fullPath: '/search/searchPlaceholder' + preLoaderRoute: typeof SearchSearchPlaceholderRouteImport + parentRoute: typeof SearchRouteRoute + } } } diff --git a/examples/react/quickstart-file-based/src/routeTree.gen.ts b/examples/react/quickstart-file-based/src/routeTree.gen.ts index 59499d9fbf..12bc916ae4 100644 --- a/examples/react/quickstart-file-based/src/routeTree.gen.ts +++ b/examples/react/quickstart-file-based/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' +import { Route as AboutRouteImport } from './routes/about' -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/examples/react/quickstart-rspack-file-based/src/routeTree.gen.ts b/examples/react/quickstart-rspack-file-based/src/routeTree.gen.ts index 59499d9fbf..12bc916ae4 100644 --- a/examples/react/quickstart-rspack-file-based/src/routeTree.gen.ts +++ b/examples/react/quickstart-rspack-file-based/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' +import { Route as AboutRouteImport } from './routes/about' -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/examples/react/quickstart-webpack-file-based/src/routeTree.gen.ts b/examples/react/quickstart-webpack-file-based/src/routeTree.gen.ts index 59499d9fbf..12bc916ae4 100644 --- a/examples/react/quickstart-webpack-file-based/src/routeTree.gen.ts +++ b/examples/react/quickstart-webpack-file-based/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' +import { Route as AboutRouteImport } from './routes/about' -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/examples/react/router-monorepo-react-query/packages/router/src/routeTree.gen.ts b/examples/react/router-monorepo-react-query/packages/router/src/routeTree.gen.ts index 0820fc5f30..a4ed448d2f 100644 --- a/examples/react/router-monorepo-react-query/packages/router/src/routeTree.gen.ts +++ b/examples/react/router-monorepo-react-query/packages/router/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 PostIdRouteImport } from './routes/$postId' import { Route as IndexRouteImport } from './routes/index' +import { Route as PostIdRouteImport } from './routes/$postId' -const PostIdRoute = PostIdRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const PostIdRoute = PostIdRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/$postId': { - id: '/$postId' - path: '/$postId' - fullPath: '/$postId' - preLoaderRoute: typeof PostIdRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/$postId': { + id: '/$postId' + path: '/$postId' + fullPath: '/$postId' + preLoaderRoute: typeof PostIdRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/examples/react/router-monorepo-simple-lazy/packages/router/src/routeTree.gen.ts b/examples/react/router-monorepo-simple-lazy/packages/router/src/routeTree.gen.ts index 0820fc5f30..a4ed448d2f 100644 --- a/examples/react/router-monorepo-simple-lazy/packages/router/src/routeTree.gen.ts +++ b/examples/react/router-monorepo-simple-lazy/packages/router/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 PostIdRouteImport } from './routes/$postId' import { Route as IndexRouteImport } from './routes/index' +import { Route as PostIdRouteImport } from './routes/$postId' -const PostIdRoute = PostIdRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const PostIdRoute = PostIdRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/$postId': { - id: '/$postId' - path: '/$postId' - fullPath: '/$postId' - preLoaderRoute: typeof PostIdRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/$postId': { + id: '/$postId' + path: '/$postId' + fullPath: '/$postId' + preLoaderRoute: typeof PostIdRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/examples/react/router-monorepo-simple/packages/router/src/routeTree.gen.ts b/examples/react/router-monorepo-simple/packages/router/src/routeTree.gen.ts index 0820fc5f30..a4ed448d2f 100644 --- a/examples/react/router-monorepo-simple/packages/router/src/routeTree.gen.ts +++ b/examples/react/router-monorepo-simple/packages/router/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 PostIdRouteImport } from './routes/$postId' import { Route as IndexRouteImport } from './routes/index' +import { Route as PostIdRouteImport } from './routes/$postId' -const PostIdRoute = PostIdRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const PostIdRoute = PostIdRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/$postId': { - id: '/$postId' - path: '/$postId' - fullPath: '/$postId' - preLoaderRoute: typeof PostIdRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/$postId': { + id: '/$postId' + path: '/$postId' + fullPath: '/$postId' + preLoaderRoute: typeof PostIdRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/examples/react/search-validator-adapters/src/routeTree.gen.ts b/examples/react/search-validator-adapters/src/routeTree.gen.ts index 4a973e1b29..d58c1ddfa1 100644 --- a/examples/react/search-validator-adapters/src/routeTree.gen.ts +++ b/examples/react/search-validator-adapters/src/routeTree.gen.ts @@ -10,18 +10,18 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' -import { Route as UsersZodIndexRouteImport } from './routes/users/zod.index' -import { Route as UsersValibotIndexRouteImport } from './routes/users/valibot.index' import { Route as UsersArktypeIndexRouteImport } from './routes/users/arktype.index' +import { Route as UsersValibotIndexRouteImport } from './routes/users/valibot.index' +import { Route as UsersZodIndexRouteImport } from './routes/users/zod.index' const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) -const UsersZodIndexRoute = UsersZodIndexRouteImport.update({ - id: '/users/zod/', - path: '/users/zod/', +const UsersArktypeIndexRoute = UsersArktypeIndexRouteImport.update({ + id: '/users/arktype/', + path: '/users/arktype/', getParentRoute: () => rootRouteImport, } as any) const UsersValibotIndexRoute = UsersValibotIndexRouteImport.update({ @@ -29,9 +29,9 @@ const UsersValibotIndexRoute = UsersValibotIndexRouteImport.update({ path: '/users/valibot/', getParentRoute: () => rootRouteImport, } as any) -const UsersArktypeIndexRoute = UsersArktypeIndexRouteImport.update({ - id: '/users/arktype/', - path: '/users/arktype/', +const UsersZodIndexRoute = UsersZodIndexRouteImport.update({ + id: '/users/zod/', + path: '/users/zod/', getParentRoute: () => rootRouteImport, } as any) @@ -78,11 +78,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/users/zod/': { - id: '/users/zod/' - path: '/users/zod' - fullPath: '/users/zod/' - preLoaderRoute: typeof UsersZodIndexRouteImport + '/users/arktype/': { + id: '/users/arktype/' + path: '/users/arktype' + fullPath: '/users/arktype/' + preLoaderRoute: typeof UsersArktypeIndexRouteImport parentRoute: typeof rootRouteImport } '/users/valibot/': { @@ -92,11 +92,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof UsersValibotIndexRouteImport parentRoute: typeof rootRouteImport } - '/users/arktype/': { - id: '/users/arktype/' - path: '/users/arktype' - fullPath: '/users/arktype/' - preLoaderRoute: typeof UsersArktypeIndexRouteImport + '/users/zod/': { + id: '/users/zod/' + path: '/users/zod' + fullPath: '/users/zod/' + preLoaderRoute: typeof UsersZodIndexRouteImport parentRoute: typeof rootRouteImport } } diff --git a/examples/react/start-bare/src/routeTree.gen.ts b/examples/react/start-bare/src/routeTree.gen.ts index 421daf2790..d89758e684 100644 --- a/examples/react/start-bare/src/routeTree.gen.ts +++ b/examples/react/start-bare/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' +import { Route as AboutRouteImport } from './routes/about' -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/examples/react/start-basic-auth/src/routeTree.gen.ts b/examples/react/start-basic-auth/src/routeTree.gen.ts index 389f656645..a74e753aec 100644 --- a/examples/react/start-basic-auth/src/routeTree.gen.ts +++ b/examples/react/start-basic-auth/src/routeTree.gen.ts @@ -9,23 +9,22 @@ // 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 SignupRouteImport } from './routes/signup' -import { Route as LogoutRouteImport } from './routes/logout' -import { Route as LoginRouteImport } from './routes/login' -import { Route as AuthedRouteImport } from './routes/_authed' import { Route as IndexRouteImport } from './routes/index' +import { Route as AuthedRouteImport } from './routes/_authed' +import { Route as LoginRouteImport } from './routes/login' +import { Route as LogoutRouteImport } from './routes/logout' +import { Route as SignupRouteImport } from './routes/signup' import { Route as AuthedPostsRouteRouteImport } from './routes/_authed/posts.route' import { Route as AuthedPostsIndexRouteImport } from './routes/_authed/posts.index' import { Route as AuthedPostsPostIdRouteImport } from './routes/_authed/posts.$postId' -const SignupRoute = SignupRouteImport.update({ - id: '/signup', - path: '/signup', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const LogoutRoute = LogoutRouteImport.update({ - id: '/logout', - path: '/logout', +const AuthedRoute = AuthedRouteImport.update({ + id: '/_authed', getParentRoute: () => rootRouteImport, } as any) const LoginRoute = LoginRouteImport.update({ @@ -33,13 +32,14 @@ const LoginRoute = LoginRouteImport.update({ path: '/login', getParentRoute: () => rootRouteImport, } as any) -const AuthedRoute = AuthedRouteImport.update({ - id: '/_authed', +const LogoutRoute = LogoutRouteImport.update({ + id: '/logout', + path: '/logout', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const SignupRoute = SignupRouteImport.update({ + id: '/signup', + path: '/signup', getParentRoute: () => rootRouteImport, } as any) const AuthedPostsRouteRoute = AuthedPostsRouteRouteImport.update({ @@ -120,18 +120,18 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/signup': { - id: '/signup' - path: '/signup' - fullPath: '/signup' - preLoaderRoute: typeof SignupRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/logout': { - id: '/logout' - path: '/logout' - fullPath: '/logout' - preLoaderRoute: typeof LogoutRouteImport + '/_authed': { + id: '/_authed' + path: '' + fullPath: '/' + preLoaderRoute: typeof AuthedRouteImport parentRoute: typeof rootRouteImport } '/login': { @@ -141,18 +141,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LoginRouteImport parentRoute: typeof rootRouteImport } - '/_authed': { - id: '/_authed' - path: '' - fullPath: '/' - preLoaderRoute: typeof AuthedRouteImport + '/logout': { + id: '/logout' + path: '/logout' + fullPath: '/logout' + preLoaderRoute: typeof LogoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/signup': { + id: '/signup' + path: '/signup' + fullPath: '/signup' + preLoaderRoute: typeof SignupRouteImport parentRoute: typeof rootRouteImport } '/_authed/posts': { diff --git a/examples/react/start-basic-authjs/src/routeTree.gen.ts b/examples/react/start-basic-authjs/src/routeTree.gen.ts index 2f59e4ba33..4abcbc8048 100644 --- a/examples/react/start-basic-authjs/src/routeTree.gen.ts +++ b/examples/react/start-basic-authjs/src/routeTree.gen.ts @@ -9,14 +9,14 @@ // 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 ProtectedRouteImport } from './routes/protected' -import { Route as LoginRouteImport } from './routes/login' import { Route as IndexRouteImport } from './routes/index' +import { Route as LoginRouteImport } from './routes/login' +import { Route as ProtectedRouteImport } from './routes/protected' import { Route as ApiAuthSplatRouteImport } from './routes/api/auth/$' -const ProtectedRoute = ProtectedRouteImport.update({ - id: '/protected', - path: '/protected', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const LoginRoute = LoginRouteImport.update({ @@ -24,9 +24,9 @@ const LoginRoute = LoginRouteImport.update({ path: '/login', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const ProtectedRoute = ProtectedRouteImport.update({ + id: '/protected', + path: '/protected', getParentRoute: () => rootRouteImport, } as any) const ApiAuthSplatRoute = ApiAuthSplatRouteImport.update({ @@ -71,11 +71,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/protected': { - id: '/protected' - path: '/protected' - fullPath: '/protected' - preLoaderRoute: typeof ProtectedRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/login': { @@ -85,11 +85,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LoginRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/protected': { + id: '/protected' + path: '/protected' + fullPath: '/protected' + preLoaderRoute: typeof ProtectedRouteImport parentRoute: typeof rootRouteImport } '/api/auth/$': { diff --git a/examples/react/start-basic-cloudflare/src/routeTree.gen.ts b/examples/react/start-basic-cloudflare/src/routeTree.gen.ts index 0bd698a666..57b849492d 100644 --- a/examples/react/start-basic-cloudflare/src/routeTree.gen.ts +++ b/examples/react/start-basic-cloudflare/src/routeTree.gen.ts @@ -9,37 +9,36 @@ // 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 UsersRouteImport } from './routes/users' -import { Route as RedirectRouteImport } from './routes/redirect' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as DeferredRouteImport } from './routes/deferred' -import { Route as CustomScriptDotjsRouteImport } from './routes/customScript[.]js' -import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as IndexRouteImport } from './routes/index' -import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' +import { Route as CustomScriptDotjsRouteImport } from './routes/customScript[.]js' +import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as RedirectRouteImport } from './routes/redirect' +import { Route as UsersRouteImport } from './routes/users' +import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' +import { Route as ApiUsersRouteImport } from './routes/api/users' import { Route as PostsIndexRouteImport } from './routes/posts.index' -import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as ApiUsersRouteImport } from './routes/api/users' -import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' -import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -import { Route as ApiUsersUserIdRouteImport } from './routes/api/users.$userId' -import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' +import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +import { Route as ApiUsersUserIdRouteImport } from './routes/api/users.$userId' +import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -const UsersRoute = UsersRouteImport.update({ - id: '/users', - path: '/users', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const RedirectRoute = RedirectRouteImport.update({ - id: '/redirect', - path: '/redirect', +const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ + id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, } as any) -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const CustomScriptDotjsRoute = CustomScriptDotjsRouteImport.update({ + id: '/customScript.js', + path: '/customScript.js', getParentRoute: () => rootRouteImport, } as any) const DeferredRoute = DeferredRouteImport.update({ @@ -47,72 +46,73 @@ const DeferredRoute = DeferredRouteImport.update({ path: '/deferred', getParentRoute: () => rootRouteImport, } as any) -const CustomScriptDotjsRoute = CustomScriptDotjsRouteImport.update({ - id: '/customScript.js', - path: '/customScript.js', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ - id: '/_pathlessLayout', +const RedirectRoute = RedirectRouteImport.update({ + id: '/redirect', + path: '/redirect', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const UsersRoute = UsersRouteImport.update({ + id: '/users', + path: '/users', getParentRoute: () => rootRouteImport, } as any) -const UsersIndexRoute = UsersIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => UsersRoute, +const PathlessLayoutNestedLayoutRoute = + PathlessLayoutNestedLayoutRouteImport.update({ + id: '/_nested-layout', + getParentRoute: () => PathlessLayoutRoute, + } as any) +const ApiUsersRoute = ApiUsersRouteImport.update({ + id: '/api/users', + path: '/api/users', + getParentRoute: () => rootRouteImport, } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRoute, } as any) -const UsersUserIdRoute = UsersUserIdRouteImport.update({ - id: '/$userId', - path: '/$userId', - getParentRoute: () => UsersRoute, -} as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRoute, } as any) -const ApiUsersRoute = ApiUsersRouteImport.update({ - id: '/api/users', - path: '/api/users', - getParentRoute: () => rootRouteImport, -} as any) -const PathlessLayoutNestedLayoutRoute = - PathlessLayoutNestedLayoutRouteImport.update({ - id: '/_nested-layout', - getParentRoute: () => PathlessLayoutRoute, - } as any) -const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ - id: '/posts_/$postId/deep', - path: '/posts/$postId/deep', - getParentRoute: () => rootRouteImport, +const UsersIndexRoute = UsersIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => UsersRoute, } as any) -const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ +const UsersUserIdRoute = UsersUserIdRouteImport.update({ id: '/$userId', path: '/$userId', - getParentRoute: () => ApiUsersRoute, + getParentRoute: () => UsersRoute, } as any) -const PathlessLayoutNestedLayoutRouteBRoute = - PathlessLayoutNestedLayoutRouteBRouteImport.update({ - id: '/route-b', - path: '/route-b', - getParentRoute: () => PathlessLayoutNestedLayoutRoute, - } as any) const PathlessLayoutNestedLayoutRouteARoute = PathlessLayoutNestedLayoutRouteARouteImport.update({ id: '/route-a', path: '/route-a', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) +const PathlessLayoutNestedLayoutRouteBRoute = + PathlessLayoutNestedLayoutRouteBRouteImport.update({ + id: '/route-b', + path: '/route-b', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, + } as any) +const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ + id: '/$userId', + path: '/$userId', + getParentRoute: () => ApiUsersRoute, +} as any) +const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ + id: '/posts_/$postId/deep', + path: '/posts/$postId/deep', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -234,25 +234,25 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/users': { - id: '/users' - path: '/users' - fullPath: '/users' - preLoaderRoute: typeof UsersRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/redirect': { - id: '/redirect' - path: '/redirect' - fullPath: '/redirect' - preLoaderRoute: typeof RedirectRouteImport + '/_pathlessLayout': { + id: '/_pathlessLayout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutRouteImport parentRoute: typeof rootRouteImport } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/customScript.js': { + id: '/customScript.js' + path: '/customScript.js' + fullPath: '/customScript.js' + preLoaderRoute: typeof CustomScriptDotjsRouteImport parentRoute: typeof rootRouteImport } '/deferred': { @@ -262,33 +262,40 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DeferredRouteImport parentRoute: typeof rootRouteImport } - '/customScript.js': { - id: '/customScript.js' - path: '/customScript.js' - fullPath: '/customScript.js' - preLoaderRoute: typeof CustomScriptDotjsRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/_pathlessLayout': { - id: '/_pathlessLayout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutRouteImport + '/redirect': { + id: '/redirect' + path: '/redirect' + fullPath: '/redirect' + preLoaderRoute: typeof RedirectRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/users': { + id: '/users' + path: '/users' + fullPath: '/users' + preLoaderRoute: typeof UsersRouteImport parentRoute: typeof rootRouteImport } - '/users/': { - id: '/users/' - path: '/' - fullPath: '/users/' - preLoaderRoute: typeof UsersIndexRouteImport - parentRoute: typeof UsersRoute + '/_pathlessLayout/_nested-layout': { + id: '/_pathlessLayout/_nested-layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport + parentRoute: typeof PathlessLayoutRoute + } + '/api/users': { + id: '/api/users' + path: '/api/users' + fullPath: '/api/users' + preLoaderRoute: typeof ApiUsersRouteImport + parentRoute: typeof rootRouteImport } '/posts/': { id: '/posts/' @@ -297,13 +304,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRoute } - '/users/$userId': { - id: '/users/$userId' - path: '/$userId' - fullPath: '/users/$userId' - preLoaderRoute: typeof UsersUserIdRouteImport - parentRoute: typeof UsersRoute - } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' @@ -311,33 +311,26 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/api/users': { - id: '/api/users' - path: '/api/users' - fullPath: '/api/users' - preLoaderRoute: typeof ApiUsersRouteImport - parentRoute: typeof rootRouteImport - } - '/_pathlessLayout/_nested-layout': { - id: '/_pathlessLayout/_nested-layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport - parentRoute: typeof PathlessLayoutRoute - } - '/posts_/$postId/deep': { - id: '/posts_/$postId/deep' - path: '/posts/$postId/deep' - fullPath: '/posts/$postId/deep' - preLoaderRoute: typeof PostsPostIdDeepRouteImport - parentRoute: typeof rootRouteImport + '/users/': { + id: '/users/' + path: '/' + fullPath: '/users/' + preLoaderRoute: typeof UsersIndexRouteImport + parentRoute: typeof UsersRoute } - '/api/users/$userId': { - id: '/api/users/$userId' + '/users/$userId': { + id: '/users/$userId' path: '/$userId' - fullPath: '/api/users/$userId' - preLoaderRoute: typeof ApiUsersUserIdRouteImport - parentRoute: typeof ApiUsersRoute + fullPath: '/users/$userId' + preLoaderRoute: typeof UsersUserIdRouteImport + parentRoute: typeof UsersRoute + } + '/_pathlessLayout/_nested-layout/route-a': { + id: '/_pathlessLayout/_nested-layout/route-a' + path: '/route-a' + fullPath: '/route-a' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport + parentRoute: typeof PathlessLayoutNestedLayoutRoute } '/_pathlessLayout/_nested-layout/route-b': { id: '/_pathlessLayout/_nested-layout/route-b' @@ -346,12 +339,19 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } - '/_pathlessLayout/_nested-layout/route-a': { - id: '/_pathlessLayout/_nested-layout/route-a' - path: '/route-a' - fullPath: '/route-a' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport - parentRoute: typeof PathlessLayoutNestedLayoutRoute + '/api/users/$userId': { + id: '/api/users/$userId' + path: '/$userId' + fullPath: '/api/users/$userId' + preLoaderRoute: typeof ApiUsersUserIdRouteImport + parentRoute: typeof ApiUsersRoute + } + '/posts_/$postId/deep': { + id: '/posts_/$postId/deep' + path: '/posts/$postId/deep' + fullPath: '/posts/$postId/deep' + preLoaderRoute: typeof PostsPostIdDeepRouteImport + parentRoute: typeof rootRouteImport } } } diff --git a/examples/react/start-basic-react-query/src/routeTree.gen.ts b/examples/react/start-basic-react-query/src/routeTree.gen.ts index a3a4ddf49f..e010af19fe 100644 --- a/examples/react/start-basic-react-query/src/routeTree.gen.ts +++ b/examples/react/start-basic-react-query/src/routeTree.gen.ts @@ -9,26 +9,30 @@ // 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 RedirectRouteImport } from './routes/redirect' -import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as IndexRouteImport } from './routes/index' import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' -import { Route as UsersRouteRouteImport } from './routes/users.route' +import { Route as DeferredRouteImport } from './routes/deferred' import { Route as PostsRouteRouteImport } from './routes/posts.route' -import { Route as IndexRouteImport } from './routes/index' -import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as RedirectRouteImport } from './routes/redirect' +import { Route as UsersRouteRouteImport } from './routes/users.route' +import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' +import { Route as ApiUsersRouteImport } from './routes/api/users' import { Route as PostsIndexRouteImport } from './routes/posts.index' -import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as ApiUsersRouteImport } from './routes/api/users' -import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' -import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -import { Route as ApiUsersIdRouteImport } from './routes/api/users.$id' -import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' +import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +import { Route as ApiUsersIdRouteImport } from './routes/api/users.$id' +import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -const RedirectRoute = RedirectRouteImport.update({ - id: '/redirect', - path: '/redirect', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) +const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ + id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, } as any) const DeferredRoute = DeferredRouteImport.update({ @@ -36,8 +40,14 @@ const DeferredRoute = DeferredRouteImport.update({ path: '/deferred', getParentRoute: () => rootRouteImport, } as any) -const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ - id: '/_pathlessLayout', +const PostsRouteRoute = PostsRouteRouteImport.update({ + id: '/posts', + path: '/posts', + getParentRoute: () => rootRouteImport, +} as any) +const RedirectRoute = RedirectRouteImport.update({ + id: '/redirect', + path: '/redirect', getParentRoute: () => rootRouteImport, } as any) const UsersRouteRoute = UsersRouteRouteImport.update({ @@ -45,68 +55,58 @@ const UsersRouteRoute = UsersRouteRouteImport.update({ path: '/users', getParentRoute: () => rootRouteImport, } as any) -const PostsRouteRoute = PostsRouteRouteImport.update({ - id: '/posts', - path: '/posts', +const PathlessLayoutNestedLayoutRoute = + PathlessLayoutNestedLayoutRouteImport.update({ + id: '/_nested-layout', + getParentRoute: () => PathlessLayoutRoute, + } as any) +const ApiUsersRoute = ApiUsersRouteImport.update({ + id: '/api/users', + path: '/api/users', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ +const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => rootRouteImport, + getParentRoute: () => PostsRouteRoute, +} as any) +const PostsPostIdRoute = PostsPostIdRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => PostsRouteRoute, } as any) const UsersIndexRoute = UsersIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => UsersRouteRoute, } as any) -const PostsIndexRoute = PostsIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => PostsRouteRoute, -} as any) const UsersUserIdRoute = UsersUserIdRouteImport.update({ id: '/$userId', path: '/$userId', getParentRoute: () => UsersRouteRoute, } as any) -const PostsPostIdRoute = PostsPostIdRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => PostsRouteRoute, -} as any) -const ApiUsersRoute = ApiUsersRouteImport.update({ - id: '/api/users', - path: '/api/users', - getParentRoute: () => rootRouteImport, -} as any) -const PathlessLayoutNestedLayoutRoute = - PathlessLayoutNestedLayoutRouteImport.update({ - id: '/_nested-layout', - getParentRoute: () => PathlessLayoutRoute, +const PathlessLayoutNestedLayoutRouteARoute = + PathlessLayoutNestedLayoutRouteARouteImport.update({ + id: '/route-a', + path: '/route-a', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) -const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ - id: '/posts_/$postId/deep', - path: '/posts/$postId/deep', - getParentRoute: () => rootRouteImport, -} as any) -const ApiUsersIdRoute = ApiUsersIdRouteImport.update({ - id: '/$id', - path: '/$id', - getParentRoute: () => ApiUsersRoute, -} as any) const PathlessLayoutNestedLayoutRouteBRoute = PathlessLayoutNestedLayoutRouteBRouteImport.update({ id: '/route-b', path: '/route-b', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) -const PathlessLayoutNestedLayoutRouteARoute = - PathlessLayoutNestedLayoutRouteARouteImport.update({ - id: '/route-a', - path: '/route-a', - getParentRoute: () => PathlessLayoutNestedLayoutRoute, - } as any) +const ApiUsersIdRoute = ApiUsersIdRouteImport.update({ + id: '/$id', + path: '/$id', + getParentRoute: () => ApiUsersRoute, +} as any) +const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ + id: '/posts_/$postId/deep', + path: '/posts/$postId/deep', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -221,18 +221,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/redirect': { - id: '/redirect' - path: '/redirect' - fullPath: '/redirect' - preLoaderRoute: typeof RedirectRouteImport - parentRoute: typeof rootRouteImport - } - '/deferred': { - id: '/deferred' - path: '/deferred' - fullPath: '/deferred' - preLoaderRoute: typeof DeferredRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/_pathlessLayout': { @@ -242,11 +235,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PathlessLayoutRouteImport parentRoute: typeof rootRouteImport } - '/users': { - id: '/users' - path: '/users' - fullPath: '/users' - preLoaderRoute: typeof UsersRouteRouteImport + '/deferred': { + id: '/deferred' + path: '/deferred' + fullPath: '/deferred' + preLoaderRoute: typeof DeferredRouteImport parentRoute: typeof rootRouteImport } '/posts': { @@ -256,19 +249,33 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/redirect': { + id: '/redirect' + path: '/redirect' + fullPath: '/redirect' + preLoaderRoute: typeof RedirectRouteImport parentRoute: typeof rootRouteImport } - '/users/': { - id: '/users/' - path: '/' - fullPath: '/users/' - preLoaderRoute: typeof UsersIndexRouteImport - parentRoute: typeof UsersRouteRoute + '/users': { + id: '/users' + path: '/users' + fullPath: '/users' + preLoaderRoute: typeof UsersRouteRouteImport + parentRoute: typeof rootRouteImport + } + '/_pathlessLayout/_nested-layout': { + id: '/_pathlessLayout/_nested-layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport + parentRoute: typeof PathlessLayoutRoute + } + '/api/users': { + id: '/api/users' + path: '/api/users' + fullPath: '/api/users' + preLoaderRoute: typeof ApiUsersRouteImport + parentRoute: typeof rootRouteImport } '/posts/': { id: '/posts/' @@ -277,13 +284,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRouteRoute } - '/users/$userId': { - id: '/users/$userId' - path: '/$userId' - fullPath: '/users/$userId' - preLoaderRoute: typeof UsersUserIdRouteImport - parentRoute: typeof UsersRouteRoute - } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' @@ -291,33 +291,26 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRouteRoute } - '/api/users': { - id: '/api/users' - path: '/api/users' - fullPath: '/api/users' - preLoaderRoute: typeof ApiUsersRouteImport - parentRoute: typeof rootRouteImport - } - '/_pathlessLayout/_nested-layout': { - id: '/_pathlessLayout/_nested-layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport - parentRoute: typeof PathlessLayoutRoute + '/users/': { + id: '/users/' + path: '/' + fullPath: '/users/' + preLoaderRoute: typeof UsersIndexRouteImport + parentRoute: typeof UsersRouteRoute } - '/posts_/$postId/deep': { - id: '/posts_/$postId/deep' - path: '/posts/$postId/deep' - fullPath: '/posts/$postId/deep' - preLoaderRoute: typeof PostsPostIdDeepRouteImport - parentRoute: typeof rootRouteImport + '/users/$userId': { + id: '/users/$userId' + path: '/$userId' + fullPath: '/users/$userId' + preLoaderRoute: typeof UsersUserIdRouteImport + parentRoute: typeof UsersRouteRoute } - '/api/users/$id': { - id: '/api/users/$id' - path: '/$id' - fullPath: '/api/users/$id' - preLoaderRoute: typeof ApiUsersIdRouteImport - parentRoute: typeof ApiUsersRoute + '/_pathlessLayout/_nested-layout/route-a': { + id: '/_pathlessLayout/_nested-layout/route-a' + path: '/route-a' + fullPath: '/route-a' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport + parentRoute: typeof PathlessLayoutNestedLayoutRoute } '/_pathlessLayout/_nested-layout/route-b': { id: '/_pathlessLayout/_nested-layout/route-b' @@ -326,12 +319,19 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } - '/_pathlessLayout/_nested-layout/route-a': { - id: '/_pathlessLayout/_nested-layout/route-a' - path: '/route-a' - fullPath: '/route-a' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport - parentRoute: typeof PathlessLayoutNestedLayoutRoute + '/api/users/$id': { + id: '/api/users/$id' + path: '/$id' + fullPath: '/api/users/$id' + preLoaderRoute: typeof ApiUsersIdRouteImport + parentRoute: typeof ApiUsersRoute + } + '/posts_/$postId/deep': { + id: '/posts_/$postId/deep' + path: '/posts/$postId/deep' + fullPath: '/posts/$postId/deep' + preLoaderRoute: typeof PostsPostIdDeepRouteImport + parentRoute: typeof rootRouteImport } } } diff --git a/examples/react/start-basic-static/src/routeTree.gen.ts b/examples/react/start-basic-static/src/routeTree.gen.ts index ddc8fc320a..d4a8e72343 100644 --- a/examples/react/start-basic-static/src/routeTree.gen.ts +++ b/examples/react/start-basic-static/src/routeTree.gen.ts @@ -9,29 +9,33 @@ // 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 UsersRouteImport } from './routes/users' -import { Route as RedirectRouteImport } from './routes/redirect' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as DeferredRouteImport } from './routes/deferred' -import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as IndexRouteImport } from './routes/index' -import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' +import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as RedirectRouteImport } from './routes/redirect' +import { Route as UsersRouteImport } from './routes/users' +import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' import { Route as PostsIndexRouteImport } from './routes/posts.index' -import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' -import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' +import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -const UsersRoute = UsersRouteImport.update({ - id: '/users', - path: '/users', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const RedirectRoute = RedirectRouteImport.update({ - id: '/redirect', - path: '/redirect', +const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ + id: '/_pathlessLayout', + getParentRoute: () => rootRouteImport, +} as any) +const DeferredRoute = DeferredRouteImport.update({ + id: '/deferred', + path: '/deferred', getParentRoute: () => rootRouteImport, } as any) const PostsRoute = PostsRouteImport.update({ @@ -39,62 +43,58 @@ const PostsRoute = PostsRouteImport.update({ path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const DeferredRoute = DeferredRouteImport.update({ - id: '/deferred', - path: '/deferred', +const RedirectRoute = RedirectRouteImport.update({ + id: '/redirect', + path: '/redirect', getParentRoute: () => rootRouteImport, } as any) -const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ - id: '/_pathlessLayout', +const UsersRoute = UsersRouteImport.update({ + id: '/users', + path: '/users', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ +const PathlessLayoutNestedLayoutRoute = + PathlessLayoutNestedLayoutRouteImport.update({ + id: '/_nested-layout', + getParentRoute: () => PathlessLayoutRoute, + } as any) +const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => rootRouteImport, + getParentRoute: () => PostsRoute, +} as any) +const PostsPostIdRoute = PostsPostIdRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => PostsRoute, } as any) const UsersIndexRoute = UsersIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => UsersRoute, } as any) -const PostsIndexRoute = PostsIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => PostsRoute, -} as any) const UsersUserIdRoute = UsersUserIdRouteImport.update({ id: '/$userId', path: '/$userId', getParentRoute: () => UsersRoute, } as any) -const PostsPostIdRoute = PostsPostIdRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => PostsRoute, -} as any) -const PathlessLayoutNestedLayoutRoute = - PathlessLayoutNestedLayoutRouteImport.update({ - id: '/_nested-layout', - getParentRoute: () => PathlessLayoutRoute, +const PathlessLayoutNestedLayoutRouteARoute = + PathlessLayoutNestedLayoutRouteARouteImport.update({ + id: '/route-a', + path: '/route-a', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) -const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ - id: '/posts_/$postId/deep', - path: '/posts/$postId/deep', - getParentRoute: () => rootRouteImport, -} as any) const PathlessLayoutNestedLayoutRouteBRoute = PathlessLayoutNestedLayoutRouteBRouteImport.update({ id: '/route-b', path: '/route-b', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) -const PathlessLayoutNestedLayoutRouteARoute = - PathlessLayoutNestedLayoutRouteARouteImport.update({ - id: '/route-a', - path: '/route-a', - getParentRoute: () => PathlessLayoutNestedLayoutRoute, - } as any) +const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ + id: '/posts_/$postId/deep', + path: '/posts/$postId/deep', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -196,18 +196,25 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/users': { - id: '/users' - path: '/users' - fullPath: '/users' - preLoaderRoute: typeof UsersRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/redirect': { - id: '/redirect' - path: '/redirect' - fullPath: '/redirect' - preLoaderRoute: typeof RedirectRouteImport + '/_pathlessLayout': { + id: '/_pathlessLayout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutRouteImport + parentRoute: typeof rootRouteImport + } + '/deferred': { + id: '/deferred' + path: '/deferred' + fullPath: '/deferred' + preLoaderRoute: typeof DeferredRouteImport parentRoute: typeof rootRouteImport } '/posts': { @@ -217,33 +224,26 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/deferred': { - id: '/deferred' - path: '/deferred' - fullPath: '/deferred' - preLoaderRoute: typeof DeferredRouteImport + '/redirect': { + id: '/redirect' + path: '/redirect' + fullPath: '/redirect' + preLoaderRoute: typeof RedirectRouteImport parentRoute: typeof rootRouteImport } - '/_pathlessLayout': { - id: '/_pathlessLayout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutRouteImport + '/users': { + id: '/users' + path: '/users' + fullPath: '/users' + preLoaderRoute: typeof UsersRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' + '/_pathlessLayout/_nested-layout': { + id: '/_pathlessLayout/_nested-layout' + path: '' fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport - } - '/users/': { - id: '/users/' - path: '/' - fullPath: '/users/' - preLoaderRoute: typeof UsersIndexRouteImport - parentRoute: typeof UsersRoute + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport + parentRoute: typeof PathlessLayoutRoute } '/posts/': { id: '/posts/' @@ -252,13 +252,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRoute } - '/users/$userId': { - id: '/users/$userId' - path: '/$userId' - fullPath: '/users/$userId' - preLoaderRoute: typeof UsersUserIdRouteImport - parentRoute: typeof UsersRoute - } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' @@ -266,19 +259,26 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/_pathlessLayout/_nested-layout': { - id: '/_pathlessLayout/_nested-layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport - parentRoute: typeof PathlessLayoutRoute + '/users/': { + id: '/users/' + path: '/' + fullPath: '/users/' + preLoaderRoute: typeof UsersIndexRouteImport + parentRoute: typeof UsersRoute } - '/posts_/$postId/deep': { - id: '/posts_/$postId/deep' - path: '/posts/$postId/deep' - fullPath: '/posts/$postId/deep' - preLoaderRoute: typeof PostsPostIdDeepRouteImport - parentRoute: typeof rootRouteImport + '/users/$userId': { + id: '/users/$userId' + path: '/$userId' + fullPath: '/users/$userId' + preLoaderRoute: typeof UsersUserIdRouteImport + parentRoute: typeof UsersRoute + } + '/_pathlessLayout/_nested-layout/route-a': { + id: '/_pathlessLayout/_nested-layout/route-a' + path: '/route-a' + fullPath: '/route-a' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport + parentRoute: typeof PathlessLayoutNestedLayoutRoute } '/_pathlessLayout/_nested-layout/route-b': { id: '/_pathlessLayout/_nested-layout/route-b' @@ -287,12 +287,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } - '/_pathlessLayout/_nested-layout/route-a': { - id: '/_pathlessLayout/_nested-layout/route-a' - path: '/route-a' - fullPath: '/route-a' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport - parentRoute: typeof PathlessLayoutNestedLayoutRoute + '/posts_/$postId/deep': { + id: '/posts_/$postId/deep' + path: '/posts/$postId/deep' + fullPath: '/posts/$postId/deep' + preLoaderRoute: typeof PostsPostIdDeepRouteImport + parentRoute: typeof rootRouteImport } } } diff --git a/examples/react/start-basic/src/routeTree.gen.ts b/examples/react/start-basic/src/routeTree.gen.ts index 0bd698a666..57b849492d 100644 --- a/examples/react/start-basic/src/routeTree.gen.ts +++ b/examples/react/start-basic/src/routeTree.gen.ts @@ -9,37 +9,36 @@ // 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 UsersRouteImport } from './routes/users' -import { Route as RedirectRouteImport } from './routes/redirect' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as DeferredRouteImport } from './routes/deferred' -import { Route as CustomScriptDotjsRouteImport } from './routes/customScript[.]js' -import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as IndexRouteImport } from './routes/index' -import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' +import { Route as CustomScriptDotjsRouteImport } from './routes/customScript[.]js' +import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as RedirectRouteImport } from './routes/redirect' +import { Route as UsersRouteImport } from './routes/users' +import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' +import { Route as ApiUsersRouteImport } from './routes/api/users' import { Route as PostsIndexRouteImport } from './routes/posts.index' -import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as ApiUsersRouteImport } from './routes/api/users' -import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' -import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -import { Route as ApiUsersUserIdRouteImport } from './routes/api/users.$userId' -import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' +import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +import { Route as ApiUsersUserIdRouteImport } from './routes/api/users.$userId' +import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -const UsersRoute = UsersRouteImport.update({ - id: '/users', - path: '/users', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const RedirectRoute = RedirectRouteImport.update({ - id: '/redirect', - path: '/redirect', +const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ + id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, } as any) -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const CustomScriptDotjsRoute = CustomScriptDotjsRouteImport.update({ + id: '/customScript.js', + path: '/customScript.js', getParentRoute: () => rootRouteImport, } as any) const DeferredRoute = DeferredRouteImport.update({ @@ -47,72 +46,73 @@ const DeferredRoute = DeferredRouteImport.update({ path: '/deferred', getParentRoute: () => rootRouteImport, } as any) -const CustomScriptDotjsRoute = CustomScriptDotjsRouteImport.update({ - id: '/customScript.js', - path: '/customScript.js', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ - id: '/_pathlessLayout', +const RedirectRoute = RedirectRouteImport.update({ + id: '/redirect', + path: '/redirect', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const UsersRoute = UsersRouteImport.update({ + id: '/users', + path: '/users', getParentRoute: () => rootRouteImport, } as any) -const UsersIndexRoute = UsersIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => UsersRoute, +const PathlessLayoutNestedLayoutRoute = + PathlessLayoutNestedLayoutRouteImport.update({ + id: '/_nested-layout', + getParentRoute: () => PathlessLayoutRoute, + } as any) +const ApiUsersRoute = ApiUsersRouteImport.update({ + id: '/api/users', + path: '/api/users', + getParentRoute: () => rootRouteImport, } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRoute, } as any) -const UsersUserIdRoute = UsersUserIdRouteImport.update({ - id: '/$userId', - path: '/$userId', - getParentRoute: () => UsersRoute, -} as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRoute, } as any) -const ApiUsersRoute = ApiUsersRouteImport.update({ - id: '/api/users', - path: '/api/users', - getParentRoute: () => rootRouteImport, -} as any) -const PathlessLayoutNestedLayoutRoute = - PathlessLayoutNestedLayoutRouteImport.update({ - id: '/_nested-layout', - getParentRoute: () => PathlessLayoutRoute, - } as any) -const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ - id: '/posts_/$postId/deep', - path: '/posts/$postId/deep', - getParentRoute: () => rootRouteImport, +const UsersIndexRoute = UsersIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => UsersRoute, } as any) -const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ +const UsersUserIdRoute = UsersUserIdRouteImport.update({ id: '/$userId', path: '/$userId', - getParentRoute: () => ApiUsersRoute, + getParentRoute: () => UsersRoute, } as any) -const PathlessLayoutNestedLayoutRouteBRoute = - PathlessLayoutNestedLayoutRouteBRouteImport.update({ - id: '/route-b', - path: '/route-b', - getParentRoute: () => PathlessLayoutNestedLayoutRoute, - } as any) const PathlessLayoutNestedLayoutRouteARoute = PathlessLayoutNestedLayoutRouteARouteImport.update({ id: '/route-a', path: '/route-a', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) +const PathlessLayoutNestedLayoutRouteBRoute = + PathlessLayoutNestedLayoutRouteBRouteImport.update({ + id: '/route-b', + path: '/route-b', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, + } as any) +const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ + id: '/$userId', + path: '/$userId', + getParentRoute: () => ApiUsersRoute, +} as any) +const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ + id: '/posts_/$postId/deep', + path: '/posts/$postId/deep', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -234,25 +234,25 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/users': { - id: '/users' - path: '/users' - fullPath: '/users' - preLoaderRoute: typeof UsersRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/redirect': { - id: '/redirect' - path: '/redirect' - fullPath: '/redirect' - preLoaderRoute: typeof RedirectRouteImport + '/_pathlessLayout': { + id: '/_pathlessLayout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutRouteImport parentRoute: typeof rootRouteImport } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/customScript.js': { + id: '/customScript.js' + path: '/customScript.js' + fullPath: '/customScript.js' + preLoaderRoute: typeof CustomScriptDotjsRouteImport parentRoute: typeof rootRouteImport } '/deferred': { @@ -262,33 +262,40 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DeferredRouteImport parentRoute: typeof rootRouteImport } - '/customScript.js': { - id: '/customScript.js' - path: '/customScript.js' - fullPath: '/customScript.js' - preLoaderRoute: typeof CustomScriptDotjsRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/_pathlessLayout': { - id: '/_pathlessLayout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutRouteImport + '/redirect': { + id: '/redirect' + path: '/redirect' + fullPath: '/redirect' + preLoaderRoute: typeof RedirectRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/users': { + id: '/users' + path: '/users' + fullPath: '/users' + preLoaderRoute: typeof UsersRouteImport parentRoute: typeof rootRouteImport } - '/users/': { - id: '/users/' - path: '/' - fullPath: '/users/' - preLoaderRoute: typeof UsersIndexRouteImport - parentRoute: typeof UsersRoute + '/_pathlessLayout/_nested-layout': { + id: '/_pathlessLayout/_nested-layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport + parentRoute: typeof PathlessLayoutRoute + } + '/api/users': { + id: '/api/users' + path: '/api/users' + fullPath: '/api/users' + preLoaderRoute: typeof ApiUsersRouteImport + parentRoute: typeof rootRouteImport } '/posts/': { id: '/posts/' @@ -297,13 +304,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRoute } - '/users/$userId': { - id: '/users/$userId' - path: '/$userId' - fullPath: '/users/$userId' - preLoaderRoute: typeof UsersUserIdRouteImport - parentRoute: typeof UsersRoute - } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' @@ -311,33 +311,26 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/api/users': { - id: '/api/users' - path: '/api/users' - fullPath: '/api/users' - preLoaderRoute: typeof ApiUsersRouteImport - parentRoute: typeof rootRouteImport - } - '/_pathlessLayout/_nested-layout': { - id: '/_pathlessLayout/_nested-layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport - parentRoute: typeof PathlessLayoutRoute - } - '/posts_/$postId/deep': { - id: '/posts_/$postId/deep' - path: '/posts/$postId/deep' - fullPath: '/posts/$postId/deep' - preLoaderRoute: typeof PostsPostIdDeepRouteImport - parentRoute: typeof rootRouteImport + '/users/': { + id: '/users/' + path: '/' + fullPath: '/users/' + preLoaderRoute: typeof UsersIndexRouteImport + parentRoute: typeof UsersRoute } - '/api/users/$userId': { - id: '/api/users/$userId' + '/users/$userId': { + id: '/users/$userId' path: '/$userId' - fullPath: '/api/users/$userId' - preLoaderRoute: typeof ApiUsersUserIdRouteImport - parentRoute: typeof ApiUsersRoute + fullPath: '/users/$userId' + preLoaderRoute: typeof UsersUserIdRouteImport + parentRoute: typeof UsersRoute + } + '/_pathlessLayout/_nested-layout/route-a': { + id: '/_pathlessLayout/_nested-layout/route-a' + path: '/route-a' + fullPath: '/route-a' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport + parentRoute: typeof PathlessLayoutNestedLayoutRoute } '/_pathlessLayout/_nested-layout/route-b': { id: '/_pathlessLayout/_nested-layout/route-b' @@ -346,12 +339,19 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } - '/_pathlessLayout/_nested-layout/route-a': { - id: '/_pathlessLayout/_nested-layout/route-a' - path: '/route-a' - fullPath: '/route-a' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport - parentRoute: typeof PathlessLayoutNestedLayoutRoute + '/api/users/$userId': { + id: '/api/users/$userId' + path: '/$userId' + fullPath: '/api/users/$userId' + preLoaderRoute: typeof ApiUsersUserIdRouteImport + parentRoute: typeof ApiUsersRoute + } + '/posts_/$postId/deep': { + id: '/posts_/$postId/deep' + path: '/posts/$postId/deep' + fullPath: '/posts/$postId/deep' + preLoaderRoute: typeof PostsPostIdDeepRouteImport + parentRoute: typeof rootRouteImport } } } diff --git a/examples/react/start-bun/src/routeTree.gen.ts b/examples/react/start-bun/src/routeTree.gen.ts index 18fbc9b60e..77415f9088 100644 --- a/examples/react/start-bun/src/routeTree.gen.ts +++ b/examples/react/start-bun/src/routeTree.gen.ts @@ -11,8 +11,8 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' import { Route as ApiDemoNamesRouteImport } from './routes/api.demo-names' -import { Route as DemoStartServerFuncsRouteImport } from './routes/demo.start.server-funcs' import { Route as DemoStartApiRequestRouteImport } from './routes/demo.start.api-request' +import { Route as DemoStartServerFuncsRouteImport } from './routes/demo.start.server-funcs' const IndexRoute = IndexRouteImport.update({ id: '/', @@ -24,16 +24,16 @@ const ApiDemoNamesRoute = ApiDemoNamesRouteImport.update({ path: '/api/demo-names', getParentRoute: () => rootRouteImport, } as any) -const DemoStartServerFuncsRoute = DemoStartServerFuncsRouteImport.update({ - id: '/demo/start/server-funcs', - path: '/demo/start/server-funcs', - getParentRoute: () => rootRouteImport, -} as any) const DemoStartApiRequestRoute = DemoStartApiRequestRouteImport.update({ id: '/demo/start/api-request', path: '/demo/start/api-request', getParentRoute: () => rootRouteImport, } as any) +const DemoStartServerFuncsRoute = DemoStartServerFuncsRouteImport.update({ + id: '/demo/start/server-funcs', + path: '/demo/start/server-funcs', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -98,13 +98,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ApiDemoNamesRouteImport parentRoute: typeof rootRouteImport } - '/demo/start/server-funcs': { - id: '/demo/start/server-funcs' - path: '/demo/start/server-funcs' - fullPath: '/demo/start/server-funcs' - preLoaderRoute: typeof DemoStartServerFuncsRouteImport - parentRoute: typeof rootRouteImport - } '/demo/start/api-request': { id: '/demo/start/api-request' path: '/demo/start/api-request' @@ -112,6 +105,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DemoStartApiRequestRouteImport parentRoute: typeof rootRouteImport } + '/demo/start/server-funcs': { + id: '/demo/start/server-funcs' + path: '/demo/start/server-funcs' + fullPath: '/demo/start/server-funcs' + preLoaderRoute: typeof DemoStartServerFuncsRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/examples/react/start-clerk-basic/src/routeTree.gen.ts b/examples/react/start-clerk-basic/src/routeTree.gen.ts index 04f57442e2..87269f2faf 100644 --- a/examples/react/start-clerk-basic/src/routeTree.gen.ts +++ b/examples/react/start-clerk-basic/src/routeTree.gen.ts @@ -9,22 +9,22 @@ // 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 AuthedRouteImport } from './routes/_authed' import { Route as IndexRouteImport } from './routes/index' +import { Route as AuthedRouteImport } from './routes/_authed' import { Route as AuthedPostsRouteImport } from './routes/_authed/posts' import { Route as AuthedPostsIndexRouteImport } from './routes/_authed/posts.index' -import { Route as AuthedProfileSplatRouteImport } from './routes/_authed/profile.$' import { Route as AuthedPostsPostIdRouteImport } from './routes/_authed/posts.$postId' +import { Route as AuthedProfileSplatRouteImport } from './routes/_authed/profile.$' -const AuthedRoute = AuthedRouteImport.update({ - id: '/_authed', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AuthedRoute = AuthedRouteImport.update({ + id: '/_authed', + getParentRoute: () => rootRouteImport, +} as any) const AuthedPostsRoute = AuthedPostsRouteImport.update({ id: '/posts', path: '/posts', @@ -35,16 +35,16 @@ const AuthedPostsIndexRoute = AuthedPostsIndexRouteImport.update({ path: '/', getParentRoute: () => AuthedPostsRoute, } as any) -const AuthedProfileSplatRoute = AuthedProfileSplatRouteImport.update({ - id: '/profile/$', - path: '/profile/$', - getParentRoute: () => AuthedRoute, -} as any) const AuthedPostsPostIdRoute = AuthedPostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => AuthedPostsRoute, } as any) +const AuthedProfileSplatRoute = AuthedProfileSplatRouteImport.update({ + id: '/profile/$', + path: '/profile/$', + getParentRoute: () => AuthedRoute, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -90,13 +90,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/_authed': { - id: '/_authed' - path: '' - fullPath: '/' - preLoaderRoute: typeof AuthedRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -104,6 +97,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/_authed': { + id: '/_authed' + path: '' + fullPath: '/' + preLoaderRoute: typeof AuthedRouteImport + parentRoute: typeof rootRouteImport + } '/_authed/posts': { id: '/_authed/posts' path: '/posts' @@ -118,13 +118,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AuthedPostsIndexRouteImport parentRoute: typeof AuthedPostsRoute } - '/_authed/profile/$': { - id: '/_authed/profile/$' - path: '/profile/$' - fullPath: '/profile/$' - preLoaderRoute: typeof AuthedProfileSplatRouteImport - parentRoute: typeof AuthedRoute - } '/_authed/posts/$postId': { id: '/_authed/posts/$postId' path: '/$postId' @@ -132,6 +125,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AuthedPostsPostIdRouteImport parentRoute: typeof AuthedPostsRoute } + '/_authed/profile/$': { + id: '/_authed/profile/$' + path: '/profile/$' + fullPath: '/profile/$' + preLoaderRoute: typeof AuthedProfileSplatRouteImport + parentRoute: typeof AuthedRoute + } } } diff --git a/examples/react/start-i18n-paraglide/src/routeTree.gen.ts b/examples/react/start-i18n-paraglide/src/routeTree.gen.ts index 421daf2790..d89758e684 100644 --- a/examples/react/start-i18n-paraglide/src/routeTree.gen.ts +++ b/examples/react/start-i18n-paraglide/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' +import { Route as AboutRouteImport } from './routes/about' -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/examples/react/start-large/src/routeTree.gen.ts b/examples/react/start-large/src/routeTree.gen.ts index 03cc2b9080..4f1551f71d 100644 --- a/examples/react/start-large/src/routeTree.gen.ts +++ b/examples/react/start-large/src/routeTree.gen.ts @@ -9,23 +9,18 @@ // 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 RelativeRouteImport } from './routes/relative' -import { Route as LinkPropsRouteImport } from './routes/linkProps' +import { Route as IndexRouteImport } from './routes/index' import { Route as AbsoluteRouteImport } from './routes/absolute' -import { Route as SearchRouteRouteImport } from './routes/search/route' +import { Route as LinkPropsRouteImport } from './routes/linkProps' import { Route as ParamsRouteRouteImport } from './routes/params/route' -import { Route as IndexRouteImport } from './routes/index' -import { Route as SearchSearchPlaceholderRouteImport } from './routes/search/searchPlaceholder' +import { Route as RelativeRouteImport } from './routes/relative' +import { Route as SearchRouteRouteImport } from './routes/search/route' import { Route as ParamsParamsPlaceholderRouteImport } from './routes/params/$paramsPlaceholder' +import { Route as SearchSearchPlaceholderRouteImport } from './routes/search/searchPlaceholder' -const RelativeRoute = RelativeRouteImport.update({ - id: '/relative', - path: '/relative', - getParentRoute: () => rootRouteImport, -} as any) -const LinkPropsRoute = LinkPropsRouteImport.update({ - id: '/linkProps', - path: '/linkProps', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const AbsoluteRoute = AbsoluteRouteImport.update({ @@ -33,9 +28,9 @@ const AbsoluteRoute = AbsoluteRouteImport.update({ path: '/absolute', getParentRoute: () => rootRouteImport, } as any) -const SearchRouteRoute = SearchRouteRouteImport.update({ - id: '/search', - path: '/search', +const LinkPropsRoute = LinkPropsRouteImport.update({ + id: '/linkProps', + path: '/linkProps', getParentRoute: () => rootRouteImport, } as any) const ParamsRouteRoute = ParamsRouteRouteImport.update({ @@ -43,21 +38,26 @@ const ParamsRouteRoute = ParamsRouteRouteImport.update({ path: '/params', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const RelativeRoute = RelativeRouteImport.update({ + id: '/relative', + path: '/relative', getParentRoute: () => rootRouteImport, } as any) -const SearchSearchPlaceholderRoute = SearchSearchPlaceholderRouteImport.update({ - id: '/searchPlaceholder', - path: '/searchPlaceholder', - getParentRoute: () => SearchRouteRoute, +const SearchRouteRoute = SearchRouteRouteImport.update({ + id: '/search', + path: '/search', + getParentRoute: () => rootRouteImport, } as any) const ParamsParamsPlaceholderRoute = ParamsParamsPlaceholderRouteImport.update({ id: '/$paramsPlaceholder', path: '/$paramsPlaceholder', getParentRoute: () => ParamsRouteRoute, } as any) +const SearchSearchPlaceholderRoute = SearchSearchPlaceholderRouteImport.update({ + id: '/searchPlaceholder', + path: '/searchPlaceholder', + getParentRoute: () => SearchRouteRoute, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -134,18 +134,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/relative': { - id: '/relative' - path: '/relative' - fullPath: '/relative' - preLoaderRoute: typeof RelativeRouteImport - parentRoute: typeof rootRouteImport - } - '/linkProps': { - id: '/linkProps' - path: '/linkProps' - fullPath: '/linkProps' - preLoaderRoute: typeof LinkPropsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/absolute': { @@ -155,11 +148,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AbsoluteRouteImport parentRoute: typeof rootRouteImport } - '/search': { - id: '/search' - path: '/search' - fullPath: '/search' - preLoaderRoute: typeof SearchRouteRouteImport + '/linkProps': { + id: '/linkProps' + path: '/linkProps' + fullPath: '/linkProps' + preLoaderRoute: typeof LinkPropsRouteImport parentRoute: typeof rootRouteImport } '/params': { @@ -169,19 +162,19 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ParamsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/relative': { + id: '/relative' + path: '/relative' + fullPath: '/relative' + preLoaderRoute: typeof RelativeRouteImport parentRoute: typeof rootRouteImport } - '/search/searchPlaceholder': { - id: '/search/searchPlaceholder' - path: '/searchPlaceholder' - fullPath: '/search/searchPlaceholder' - preLoaderRoute: typeof SearchSearchPlaceholderRouteImport - parentRoute: typeof SearchRouteRoute + '/search': { + id: '/search' + path: '/search' + fullPath: '/search' + preLoaderRoute: typeof SearchRouteRouteImport + parentRoute: typeof rootRouteImport } '/params/$paramsPlaceholder': { id: '/params/$paramsPlaceholder' @@ -190,6 +183,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ParamsParamsPlaceholderRouteImport parentRoute: typeof ParamsRouteRoute } + '/search/searchPlaceholder': { + id: '/search/searchPlaceholder' + path: '/searchPlaceholder' + fullPath: '/search/searchPlaceholder' + preLoaderRoute: typeof SearchSearchPlaceholderRouteImport + parentRoute: typeof SearchRouteRoute + } } } diff --git a/examples/react/start-material-ui/src/routeTree.gen.ts b/examples/react/start-material-ui/src/routeTree.gen.ts index 421daf2790..d89758e684 100644 --- a/examples/react/start-material-ui/src/routeTree.gen.ts +++ b/examples/react/start-material-ui/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' +import { Route as AboutRouteImport } from './routes/about' -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/examples/react/start-rscs/src/routeTree.gen.ts b/examples/react/start-rscs/src/routeTree.gen.ts index ca4cef3864..d89ab3d3e7 100644 --- a/examples/react/start-rscs/src/routeTree.gen.ts +++ b/examples/react/start-rscs/src/routeTree.gen.ts @@ -9,21 +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 PokemonRscRouteImport } from './routes/pokemon-rsc' -import { Route as PokemonRouteImport } from './routes/pokemon' -import { Route as LowLevelApiRouteImport } from './routes/low-level-api' -import { Route as ECommerceRouteImport } from './routes/e-commerce' import { Route as IndexRouteImport } from './routes/index' +import { Route as ECommerceRouteImport } from './routes/e-commerce' +import { Route as LowLevelApiRouteImport } from './routes/low-level-api' +import { Route as PokemonRouteImport } from './routes/pokemon' +import { Route as PokemonRscRouteImport } from './routes/pokemon-rsc' import { Route as ApiRscRouteImport } from './routes/api/rsc' -const PokemonRscRoute = PokemonRscRouteImport.update({ - id: '/pokemon-rsc', - path: '/pokemon-rsc', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const PokemonRoute = PokemonRouteImport.update({ - id: '/pokemon', - path: '/pokemon', +const ECommerceRoute = ECommerceRouteImport.update({ + id: '/e-commerce', + path: '/e-commerce', getParentRoute: () => rootRouteImport, } as any) const LowLevelApiRoute = LowLevelApiRouteImport.update({ @@ -31,14 +31,14 @@ const LowLevelApiRoute = LowLevelApiRouteImport.update({ path: '/low-level-api', getParentRoute: () => rootRouteImport, } as any) -const ECommerceRoute = ECommerceRouteImport.update({ - id: '/e-commerce', - path: '/e-commerce', +const PokemonRoute = PokemonRouteImport.update({ + id: '/pokemon', + path: '/pokemon', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const PokemonRscRoute = PokemonRscRouteImport.update({ + id: '/pokemon-rsc', + path: '/pokemon-rsc', getParentRoute: () => rootRouteImport, } as any) const ApiRscRoute = ApiRscRouteImport.update({ @@ -110,18 +110,18 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/pokemon-rsc': { - id: '/pokemon-rsc' - path: '/pokemon-rsc' - fullPath: '/pokemon-rsc' - preLoaderRoute: typeof PokemonRscRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/pokemon': { - id: '/pokemon' - path: '/pokemon' - fullPath: '/pokemon' - preLoaderRoute: typeof PokemonRouteImport + '/e-commerce': { + id: '/e-commerce' + path: '/e-commerce' + fullPath: '/e-commerce' + preLoaderRoute: typeof ECommerceRouteImport parentRoute: typeof rootRouteImport } '/low-level-api': { @@ -131,18 +131,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LowLevelApiRouteImport parentRoute: typeof rootRouteImport } - '/e-commerce': { - id: '/e-commerce' - path: '/e-commerce' - fullPath: '/e-commerce' - preLoaderRoute: typeof ECommerceRouteImport + '/pokemon': { + id: '/pokemon' + path: '/pokemon' + fullPath: '/pokemon' + preLoaderRoute: typeof PokemonRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/pokemon-rsc': { + id: '/pokemon-rsc' + path: '/pokemon-rsc' + fullPath: '/pokemon-rsc' + preLoaderRoute: typeof PokemonRscRouteImport parentRoute: typeof rootRouteImport } '/api/rsc': { diff --git a/examples/react/start-supabase-basic/src/routeTree.gen.ts b/examples/react/start-supabase-basic/src/routeTree.gen.ts index 6be84444eb..998d34a709 100644 --- a/examples/react/start-supabase-basic/src/routeTree.gen.ts +++ b/examples/react/start-supabase-basic/src/routeTree.gen.ts @@ -9,23 +9,22 @@ // 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 SignupRouteImport } from './routes/signup' -import { Route as LogoutRouteImport } from './routes/logout' -import { Route as LoginRouteImport } from './routes/login' -import { Route as AuthedRouteImport } from './routes/_authed' import { Route as IndexRouteImport } from './routes/index' +import { Route as AuthedRouteImport } from './routes/_authed' +import { Route as LoginRouteImport } from './routes/login' +import { Route as LogoutRouteImport } from './routes/logout' +import { Route as SignupRouteImport } from './routes/signup' import { Route as AuthedPostsRouteImport } from './routes/_authed/posts' import { Route as AuthedPostsIndexRouteImport } from './routes/_authed/posts.index' import { Route as AuthedPostsPostIdRouteImport } from './routes/_authed/posts.$postId' -const SignupRoute = SignupRouteImport.update({ - id: '/signup', - path: '/signup', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const LogoutRoute = LogoutRouteImport.update({ - id: '/logout', - path: '/logout', +const AuthedRoute = AuthedRouteImport.update({ + id: '/_authed', getParentRoute: () => rootRouteImport, } as any) const LoginRoute = LoginRouteImport.update({ @@ -33,13 +32,14 @@ const LoginRoute = LoginRouteImport.update({ path: '/login', getParentRoute: () => rootRouteImport, } as any) -const AuthedRoute = AuthedRouteImport.update({ - id: '/_authed', +const LogoutRoute = LogoutRouteImport.update({ + id: '/logout', + path: '/logout', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const SignupRoute = SignupRouteImport.update({ + id: '/signup', + path: '/signup', getParentRoute: () => rootRouteImport, } as any) const AuthedPostsRoute = AuthedPostsRouteImport.update({ @@ -120,18 +120,18 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/signup': { - id: '/signup' - path: '/signup' - fullPath: '/signup' - preLoaderRoute: typeof SignupRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/logout': { - id: '/logout' - path: '/logout' - fullPath: '/logout' - preLoaderRoute: typeof LogoutRouteImport + '/_authed': { + id: '/_authed' + path: '' + fullPath: '/' + preLoaderRoute: typeof AuthedRouteImport parentRoute: typeof rootRouteImport } '/login': { @@ -141,18 +141,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LoginRouteImport parentRoute: typeof rootRouteImport } - '/_authed': { - id: '/_authed' - path: '' - fullPath: '/' - preLoaderRoute: typeof AuthedRouteImport + '/logout': { + id: '/logout' + path: '/logout' + fullPath: '/logout' + preLoaderRoute: typeof LogoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/signup': { + id: '/signup' + path: '/signup' + fullPath: '/signup' + preLoaderRoute: typeof SignupRouteImport parentRoute: typeof rootRouteImport } '/_authed/posts': { diff --git a/examples/react/start-workos/src/routeTree.gen.ts b/examples/react/start-workos/src/routeTree.gen.ts index 49009fc848..559db280de 100644 --- a/examples/react/start-workos/src/routeTree.gen.ts +++ b/examples/react/start-workos/src/routeTree.gen.ts @@ -9,16 +9,20 @@ // 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 LogoutRouteImport } from './routes/logout' -import { Route as ClientRouteImport } from './routes/client' -import { Route as AuthenticatedRouteImport } from './routes/_authenticated' import { Route as IndexRouteImport } from './routes/index' +import { Route as AuthenticatedRouteImport } from './routes/_authenticated' +import { Route as ClientRouteImport } from './routes/client' +import { Route as LogoutRouteImport } from './routes/logout' import { Route as AuthenticatedAccountRouteImport } from './routes/_authenticated/account' import { Route as ApiAuthCallbackRouteImport } from './routes/api/auth/callback' -const LogoutRoute = LogoutRouteImport.update({ - id: '/logout', - path: '/logout', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) +const AuthenticatedRoute = AuthenticatedRouteImport.update({ + id: '/_authenticated', getParentRoute: () => rootRouteImport, } as any) const ClientRoute = ClientRouteImport.update({ @@ -26,13 +30,9 @@ const ClientRoute = ClientRouteImport.update({ path: '/client', getParentRoute: () => rootRouteImport, } as any) -const AuthenticatedRoute = AuthenticatedRouteImport.update({ - id: '/_authenticated', - getParentRoute: () => rootRouteImport, -} as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const LogoutRoute = LogoutRouteImport.update({ + id: '/logout', + path: '/logout', getParentRoute: () => rootRouteImport, } as any) const AuthenticatedAccountRoute = AuthenticatedAccountRouteImport.update({ @@ -94,18 +94,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/logout': { - id: '/logout' - path: '/logout' - fullPath: '/logout' - preLoaderRoute: typeof LogoutRouteImport - parentRoute: typeof rootRouteImport - } - '/client': { - id: '/client' - path: '/client' - fullPath: '/client' - preLoaderRoute: typeof ClientRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/_authenticated': { @@ -115,11 +108,18 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AuthenticatedRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/client': { + id: '/client' + path: '/client' + fullPath: '/client' + preLoaderRoute: typeof ClientRouteImport + parentRoute: typeof rootRouteImport + } + '/logout': { + id: '/logout' + path: '/logout' + fullPath: '/logout' + preLoaderRoute: typeof LogoutRouteImport parentRoute: typeof rootRouteImport } '/_authenticated/account': { diff --git a/examples/react/view-transitions/src/routeTree.gen.ts b/examples/react/view-transitions/src/routeTree.gen.ts index 20ee76a1a5..8982fca68f 100644 --- a/examples/react/view-transitions/src/routeTree.gen.ts +++ b/examples/react/view-transitions/src/routeTree.gen.ts @@ -9,16 +9,16 @@ // 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 HowItWorksRouteImport } from './routes/how-it-works' +import { Route as IndexRouteImport } from './routes/index' import { Route as ExploreRouteImport } from './routes/explore' +import { Route as HowItWorksRouteImport } from './routes/how-it-works' import { Route as PostsRouteRouteImport } from './routes/posts.route' -import { Route as IndexRouteImport } from './routes/index' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -const HowItWorksRoute = HowItWorksRouteImport.update({ - id: '/how-it-works', - path: '/how-it-works', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const ExploreRoute = ExploreRouteImport.update({ @@ -26,16 +26,16 @@ const ExploreRoute = ExploreRouteImport.update({ path: '/explore', getParentRoute: () => rootRouteImport, } as any) +const HowItWorksRoute = HowItWorksRouteImport.update({ + id: '/how-it-works', + path: '/how-it-works', + getParentRoute: () => rootRouteImport, +} as any) const PostsRouteRoute = PostsRouteRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -101,11 +101,11 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/how-it-works': { - id: '/how-it-works' - path: '/how-it-works' - fullPath: '/how-it-works' - preLoaderRoute: typeof HowItWorksRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/explore': { @@ -115,6 +115,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ExploreRouteImport parentRoute: typeof rootRouteImport } + '/how-it-works': { + id: '/how-it-works' + path: '/how-it-works' + fullPath: '/how-it-works' + preLoaderRoute: typeof HowItWorksRouteImport + parentRoute: typeof rootRouteImport + } '/posts': { id: '/posts' path: '/posts' @@ -122,13 +129,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport - } '/posts/': { id: '/posts/' path: '/' diff --git a/examples/react/with-trpc-react-query/src/routeTree.gen.ts b/examples/react/with-trpc-react-query/src/routeTree.gen.ts index b407b58e4c..548a4b9e88 100644 --- a/examples/react/with-trpc-react-query/src/routeTree.gen.ts +++ b/examples/react/with-trpc-react-query/src/routeTree.gen.ts @@ -9,23 +9,23 @@ // 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 DashboardRouteImport } from './routes/dashboard' import { Route as IndexRouteImport } from './routes/index' +import { Route as DashboardRouteImport } from './routes/dashboard' import { Route as DashboardIndexRouteImport } from './routes/dashboard.index' import { Route as DashboardPostsRouteImport } from './routes/dashboard.posts' import { Route as DashboardPostsIndexRouteImport } from './routes/dashboard.posts.index' import { Route as DashboardPostsPostIdRouteImport } from './routes/dashboard.posts.$postId' -const DashboardRoute = DashboardRouteImport.update({ - id: '/dashboard', - path: '/dashboard', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const DashboardRoute = DashboardRouteImport.update({ + id: '/dashboard', + path: '/dashboard', + getParentRoute: () => rootRouteImport, +} as any) const DashboardIndexRoute = DashboardIndexRouteImport.update({ id: '/', path: '/', @@ -98,13 +98,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/dashboard': { - id: '/dashboard' - path: '/dashboard' - fullPath: '/dashboard' - preLoaderRoute: typeof DashboardRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -112,6 +105,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/dashboard': { + id: '/dashboard' + path: '/dashboard' + fullPath: '/dashboard' + preLoaderRoute: typeof DashboardRouteImport + parentRoute: typeof rootRouteImport + } '/dashboard/': { id: '/dashboard/' path: '/' diff --git a/examples/react/with-trpc/src/routeTree.gen.ts b/examples/react/with-trpc/src/routeTree.gen.ts index b407b58e4c..548a4b9e88 100644 --- a/examples/react/with-trpc/src/routeTree.gen.ts +++ b/examples/react/with-trpc/src/routeTree.gen.ts @@ -9,23 +9,23 @@ // 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 DashboardRouteImport } from './routes/dashboard' import { Route as IndexRouteImport } from './routes/index' +import { Route as DashboardRouteImport } from './routes/dashboard' import { Route as DashboardIndexRouteImport } from './routes/dashboard.index' import { Route as DashboardPostsRouteImport } from './routes/dashboard.posts' import { Route as DashboardPostsIndexRouteImport } from './routes/dashboard.posts.index' import { Route as DashboardPostsPostIdRouteImport } from './routes/dashboard.posts.$postId' -const DashboardRoute = DashboardRouteImport.update({ - id: '/dashboard', - path: '/dashboard', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const DashboardRoute = DashboardRouteImport.update({ + id: '/dashboard', + path: '/dashboard', + getParentRoute: () => rootRouteImport, +} as any) const DashboardIndexRoute = DashboardIndexRouteImport.update({ id: '/', path: '/', @@ -98,13 +98,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/dashboard': { - id: '/dashboard' - path: '/dashboard' - fullPath: '/dashboard' - preLoaderRoute: typeof DashboardRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -112,6 +105,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/dashboard': { + id: '/dashboard' + path: '/dashboard' + fullPath: '/dashboard' + preLoaderRoute: typeof DashboardRouteImport + parentRoute: typeof rootRouteImport + } '/dashboard/': { id: '/dashboard/' path: '/' diff --git a/examples/solid/authenticated-routes-firebase/src/routeTree.gen.ts b/examples/solid/authenticated-routes-firebase/src/routeTree.gen.ts index ff0ae58ad7..4b98258aea 100644 --- a/examples/solid/authenticated-routes-firebase/src/routeTree.gen.ts +++ b/examples/solid/authenticated-routes-firebase/src/routeTree.gen.ts @@ -9,38 +9,38 @@ // 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 LoginRouteImport } from './routes/login' -import { Route as AuthRouteImport } from './routes/_auth' import { Route as IndexRouteImport } from './routes/index' -import { Route as AuthInvoicesRouteImport } from './routes/_auth.invoices' +import { Route as AuthRouteImport } from './routes/_auth' +import { Route as LoginRouteImport } from './routes/login' import { Route as AuthDashboardRouteImport } from './routes/_auth.dashboard' +import { Route as AuthInvoicesRouteImport } from './routes/_auth.invoices' import { Route as AuthInvoicesIndexRouteImport } from './routes/_auth.invoices.index' import { Route as AuthInvoicesInvoiceIdRouteImport } from './routes/_auth.invoices.$invoiceId' -const LoginRoute = LoginRouteImport.update({ - id: '/login', - path: '/login', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const AuthRoute = AuthRouteImport.update({ id: '/_auth', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const LoginRoute = LoginRouteImport.update({ + id: '/login', + path: '/login', getParentRoute: () => rootRouteImport, } as any) -const AuthInvoicesRoute = AuthInvoicesRouteImport.update({ - id: '/invoices', - path: '/invoices', - getParentRoute: () => AuthRoute, -} as any) const AuthDashboardRoute = AuthDashboardRouteImport.update({ id: '/dashboard', path: '/dashboard', getParentRoute: () => AuthRoute, } as any) +const AuthInvoicesRoute = AuthInvoicesRouteImport.update({ + id: '/invoices', + path: '/invoices', + getParentRoute: () => AuthRoute, +} as any) const AuthInvoicesIndexRoute = AuthInvoicesIndexRouteImport.update({ id: '/', path: '/', @@ -107,11 +107,11 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/login': { - id: '/login' - path: '/login' - fullPath: '/login' - preLoaderRoute: typeof LoginRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/_auth': { @@ -121,20 +121,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof AuthRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/login': { + id: '/login' + path: '/login' + fullPath: '/login' + preLoaderRoute: typeof LoginRouteImport parentRoute: typeof rootRouteImport } - '/_auth/invoices': { - id: '/_auth/invoices' - path: '/invoices' - fullPath: '/invoices' - preLoaderRoute: typeof AuthInvoicesRouteImport - parentRoute: typeof AuthRoute - } '/_auth/dashboard': { id: '/_auth/dashboard' path: '/dashboard' @@ -142,6 +135,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof AuthDashboardRouteImport parentRoute: typeof AuthRoute } + '/_auth/invoices': { + id: '/_auth/invoices' + path: '/invoices' + fullPath: '/invoices' + preLoaderRoute: typeof AuthInvoicesRouteImport + parentRoute: typeof AuthRoute + } '/_auth/invoices/': { id: '/_auth/invoices/' path: '/' diff --git a/examples/solid/authenticated-routes/src/routeTree.gen.ts b/examples/solid/authenticated-routes/src/routeTree.gen.ts index ff0ae58ad7..4b98258aea 100644 --- a/examples/solid/authenticated-routes/src/routeTree.gen.ts +++ b/examples/solid/authenticated-routes/src/routeTree.gen.ts @@ -9,38 +9,38 @@ // 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 LoginRouteImport } from './routes/login' -import { Route as AuthRouteImport } from './routes/_auth' import { Route as IndexRouteImport } from './routes/index' -import { Route as AuthInvoicesRouteImport } from './routes/_auth.invoices' +import { Route as AuthRouteImport } from './routes/_auth' +import { Route as LoginRouteImport } from './routes/login' import { Route as AuthDashboardRouteImport } from './routes/_auth.dashboard' +import { Route as AuthInvoicesRouteImport } from './routes/_auth.invoices' import { Route as AuthInvoicesIndexRouteImport } from './routes/_auth.invoices.index' import { Route as AuthInvoicesInvoiceIdRouteImport } from './routes/_auth.invoices.$invoiceId' -const LoginRoute = LoginRouteImport.update({ - id: '/login', - path: '/login', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const AuthRoute = AuthRouteImport.update({ id: '/_auth', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const LoginRoute = LoginRouteImport.update({ + id: '/login', + path: '/login', getParentRoute: () => rootRouteImport, } as any) -const AuthInvoicesRoute = AuthInvoicesRouteImport.update({ - id: '/invoices', - path: '/invoices', - getParentRoute: () => AuthRoute, -} as any) const AuthDashboardRoute = AuthDashboardRouteImport.update({ id: '/dashboard', path: '/dashboard', getParentRoute: () => AuthRoute, } as any) +const AuthInvoicesRoute = AuthInvoicesRouteImport.update({ + id: '/invoices', + path: '/invoices', + getParentRoute: () => AuthRoute, +} as any) const AuthInvoicesIndexRoute = AuthInvoicesIndexRouteImport.update({ id: '/', path: '/', @@ -107,11 +107,11 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/login': { - id: '/login' - path: '/login' - fullPath: '/login' - preLoaderRoute: typeof LoginRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/_auth': { @@ -121,20 +121,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof AuthRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/login': { + id: '/login' + path: '/login' + fullPath: '/login' + preLoaderRoute: typeof LoginRouteImport parentRoute: typeof rootRouteImport } - '/_auth/invoices': { - id: '/_auth/invoices' - path: '/invoices' - fullPath: '/invoices' - preLoaderRoute: typeof AuthInvoicesRouteImport - parentRoute: typeof AuthRoute - } '/_auth/dashboard': { id: '/_auth/dashboard' path: '/dashboard' @@ -142,6 +135,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof AuthDashboardRouteImport parentRoute: typeof AuthRoute } + '/_auth/invoices': { + id: '/_auth/invoices' + path: '/invoices' + fullPath: '/invoices' + preLoaderRoute: typeof AuthInvoicesRouteImport + parentRoute: typeof AuthRoute + } '/_auth/invoices/': { id: '/_auth/invoices/' path: '/' diff --git a/examples/solid/basic-file-based/src/routeTree.gen.ts b/examples/solid/basic-file-based/src/routeTree.gen.ts index bde5d68567..5942acc907 100644 --- a/examples/solid/basic-file-based/src/routeTree.gen.ts +++ b/examples/solid/basic-file-based/src/routeTree.gen.ts @@ -9,29 +9,34 @@ // 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 PostsRouteImport } from './routes/posts' -import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as IndexRouteImport } from './routes/index' +import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' -import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' +import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) +const PathlessLayoutNestedLayoutRoute = + PathlessLayoutNestedLayoutRouteImport.update({ + id: '/_nested-layout', + getParentRoute: () => PathlessLayoutRoute, + } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -42,10 +47,11 @@ const PostsPostIdRoute = PostsPostIdRouteImport.update({ path: '/$postId', getParentRoute: () => PostsRoute, } as any) -const PathlessLayoutNestedLayoutRoute = - PathlessLayoutNestedLayoutRouteImport.update({ - id: '/_nested-layout', - getParentRoute: () => PathlessLayoutRoute, +const PathlessLayoutNestedLayoutRouteARoute = + PathlessLayoutNestedLayoutRouteARouteImport.update({ + id: '/route-a', + path: '/route-a', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) const PathlessLayoutNestedLayoutRouteBRoute = PathlessLayoutNestedLayoutRouteBRouteImport.update({ @@ -53,12 +59,6 @@ const PathlessLayoutNestedLayoutRouteBRoute = path: '/route-b', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) -const PathlessLayoutNestedLayoutRouteARoute = - PathlessLayoutNestedLayoutRouteARouteImport.update({ - id: '/route-a', - path: '/route-a', - getParentRoute: () => PathlessLayoutNestedLayoutRoute, - } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -117,11 +117,11 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/_pathlessLayout': { @@ -131,13 +131,20 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PathlessLayoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } + '/_pathlessLayout/_nested-layout': { + id: '/_pathlessLayout/_nested-layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport + parentRoute: typeof PathlessLayoutRoute + } '/posts/': { id: '/posts/' path: '/' @@ -152,12 +159,12 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/_pathlessLayout/_nested-layout': { - id: '/_pathlessLayout/_nested-layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport - parentRoute: typeof PathlessLayoutRoute + '/_pathlessLayout/_nested-layout/route-a': { + id: '/_pathlessLayout/_nested-layout/route-a' + path: '/route-a' + fullPath: '/route-a' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport + parentRoute: typeof PathlessLayoutNestedLayoutRoute } '/_pathlessLayout/_nested-layout/route-b': { id: '/_pathlessLayout/_nested-layout/route-b' @@ -166,13 +173,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } - '/_pathlessLayout/_nested-layout/route-a': { - id: '/_pathlessLayout/_nested-layout/route-a' - path: '/route-a' - fullPath: '/route-a' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport - parentRoute: typeof PathlessLayoutNestedLayoutRoute - } } } diff --git a/examples/solid/basic-solid-query-file-based/src/routeTree.gen.ts b/examples/solid/basic-solid-query-file-based/src/routeTree.gen.ts index bde5d68567..5942acc907 100644 --- a/examples/solid/basic-solid-query-file-based/src/routeTree.gen.ts +++ b/examples/solid/basic-solid-query-file-based/src/routeTree.gen.ts @@ -9,29 +9,34 @@ // 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 PostsRouteImport } from './routes/posts' -import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as IndexRouteImport } from './routes/index' +import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' -import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' +import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) +const PathlessLayoutNestedLayoutRoute = + PathlessLayoutNestedLayoutRouteImport.update({ + id: '/_nested-layout', + getParentRoute: () => PathlessLayoutRoute, + } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -42,10 +47,11 @@ const PostsPostIdRoute = PostsPostIdRouteImport.update({ path: '/$postId', getParentRoute: () => PostsRoute, } as any) -const PathlessLayoutNestedLayoutRoute = - PathlessLayoutNestedLayoutRouteImport.update({ - id: '/_nested-layout', - getParentRoute: () => PathlessLayoutRoute, +const PathlessLayoutNestedLayoutRouteARoute = + PathlessLayoutNestedLayoutRouteARouteImport.update({ + id: '/route-a', + path: '/route-a', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) const PathlessLayoutNestedLayoutRouteBRoute = PathlessLayoutNestedLayoutRouteBRouteImport.update({ @@ -53,12 +59,6 @@ const PathlessLayoutNestedLayoutRouteBRoute = path: '/route-b', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) -const PathlessLayoutNestedLayoutRouteARoute = - PathlessLayoutNestedLayoutRouteARouteImport.update({ - id: '/route-a', - path: '/route-a', - getParentRoute: () => PathlessLayoutNestedLayoutRoute, - } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -117,11 +117,11 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/_pathlessLayout': { @@ -131,13 +131,20 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PathlessLayoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } + '/_pathlessLayout/_nested-layout': { + id: '/_pathlessLayout/_nested-layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport + parentRoute: typeof PathlessLayoutRoute + } '/posts/': { id: '/posts/' path: '/' @@ -152,12 +159,12 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/_pathlessLayout/_nested-layout': { - id: '/_pathlessLayout/_nested-layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport - parentRoute: typeof PathlessLayoutRoute + '/_pathlessLayout/_nested-layout/route-a': { + id: '/_pathlessLayout/_nested-layout/route-a' + path: '/route-a' + fullPath: '/route-a' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport + parentRoute: typeof PathlessLayoutNestedLayoutRoute } '/_pathlessLayout/_nested-layout/route-b': { id: '/_pathlessLayout/_nested-layout/route-b' @@ -166,13 +173,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } - '/_pathlessLayout/_nested-layout/route-a': { - id: '/_pathlessLayout/_nested-layout/route-a' - path: '/route-a' - fullPath: '/route-a' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport - parentRoute: typeof PathlessLayoutNestedLayoutRoute - } } } diff --git a/examples/solid/basic-ssr-file-based/src/routeTree.gen.ts b/examples/solid/basic-ssr-file-based/src/routeTree.gen.ts index 26eb09e67f..fc6a3e4377 100644 --- a/examples/solid/basic-ssr-file-based/src/routeTree.gen.ts +++ b/examples/solid/basic-ssr-file-based/src/routeTree.gen.ts @@ -9,12 +9,17 @@ // 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 IndexRouteImport } from './routes/index' import { Route as ErrorRouteImport } from './routes/error' import { Route as PostsRouteRouteImport } from './routes/posts/route' -import { Route as IndexRouteImport } from './routes/index' import { Route as PostsIndexRouteImport } from './routes/posts/index' import { Route as PostsPostIdRouteImport } from './routes/posts/$postId' +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) const ErrorRoute = ErrorRouteImport.update({ id: '/error', path: '/error', @@ -25,11 +30,6 @@ const PostsRouteRoute = PostsRouteRouteImport.update({ path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -78,6 +78,13 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } '/error': { id: '/error' path: '/error' @@ -92,13 +99,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport - } '/posts/': { id: '/posts/' path: '/' diff --git a/examples/solid/basic-ssr-streaming-file-based/src/routeTree.gen.ts b/examples/solid/basic-ssr-streaming-file-based/src/routeTree.gen.ts index be2ed5b18d..0161f550f0 100644 --- a/examples/solid/basic-ssr-streaming-file-based/src/routeTree.gen.ts +++ b/examples/solid/basic-ssr-streaming-file-based/src/routeTree.gen.ts @@ -9,19 +9,23 @@ // 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 PostsRouteImport } from './routes/posts' -import { Route as CounterRouteImport } from './routes/counter' -import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as IndexRouteImport } from './routes/index' +import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' +import { Route as CounterRouteImport } from './routes/counter' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' -import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' +import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) +const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ + id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, } as any) const CounterRoute = CounterRouteImport.update({ @@ -29,15 +33,16 @@ const CounterRoute = CounterRouteImport.update({ path: '/counter', getParentRoute: () => rootRouteImport, } as any) -const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ - id: '/_pathlessLayout', - getParentRoute: () => rootRouteImport, -} as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) +const PathlessLayoutNestedLayoutRoute = + PathlessLayoutNestedLayoutRouteImport.update({ + id: '/_nested-layout', + getParentRoute: () => PathlessLayoutRoute, + } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -48,10 +53,11 @@ const PostsPostIdRoute = PostsPostIdRouteImport.update({ path: '/$postId', getParentRoute: () => PostsRoute, } as any) -const PathlessLayoutNestedLayoutRoute = - PathlessLayoutNestedLayoutRouteImport.update({ - id: '/_nested-layout', - getParentRoute: () => PathlessLayoutRoute, +const PathlessLayoutNestedLayoutRouteARoute = + PathlessLayoutNestedLayoutRouteARouteImport.update({ + id: '/route-a', + path: '/route-a', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) const PathlessLayoutNestedLayoutRouteBRoute = PathlessLayoutNestedLayoutRouteBRouteImport.update({ @@ -59,12 +65,6 @@ const PathlessLayoutNestedLayoutRouteBRoute = path: '/route-b', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) -const PathlessLayoutNestedLayoutRouteARoute = - PathlessLayoutNestedLayoutRouteARouteImport.update({ - id: '/route-a', - path: '/route-a', - getParentRoute: () => PathlessLayoutNestedLayoutRoute, - } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -129,11 +129,18 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + '/_pathlessLayout': { + id: '/_pathlessLayout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutRouteImport parentRoute: typeof rootRouteImport } '/counter': { @@ -143,19 +150,19 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof CounterRouteImport parentRoute: typeof rootRouteImport } - '/_pathlessLayout': { - id: '/_pathlessLayout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' + '/_pathlessLayout/_nested-layout': { + id: '/_pathlessLayout/_nested-layout' + path: '' fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport + parentRoute: typeof PathlessLayoutRoute } '/posts/': { id: '/posts/' @@ -171,12 +178,12 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/_pathlessLayout/_nested-layout': { - id: '/_pathlessLayout/_nested-layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport - parentRoute: typeof PathlessLayoutRoute + '/_pathlessLayout/_nested-layout/route-a': { + id: '/_pathlessLayout/_nested-layout/route-a' + path: '/route-a' + fullPath: '/route-a' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport + parentRoute: typeof PathlessLayoutNestedLayoutRoute } '/_pathlessLayout/_nested-layout/route-b': { id: '/_pathlessLayout/_nested-layout/route-b' @@ -185,13 +192,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } - '/_pathlessLayout/_nested-layout/route-a': { - id: '/_pathlessLayout/_nested-layout/route-a' - path: '/route-a' - fullPath: '/route-a' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport - parentRoute: typeof PathlessLayoutNestedLayoutRoute - } } } diff --git a/examples/solid/basic-virtual-file-based/src/routeTree.gen.ts b/examples/solid/basic-virtual-file-based/src/routeTree.gen.ts index cb96aee7ba..c41eda86bf 100644 --- a/examples/solid/basic-virtual-file-based/src/routeTree.gen.ts +++ b/examples/solid/basic-virtual-file-based/src/routeTree.gen.ts @@ -9,77 +9,77 @@ // 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 postsPostsRouteImport } from './routes/posts/posts' -import { Route as layoutFirstLayoutRouteImport } from './routes/layout/first-layout' import { Route as homeRouteImport } from './routes/home' -import { Route as postsPostsDetailRouteImport } from './routes/posts/posts-detail' +import { Route as layoutFirstLayoutRouteImport } from './routes/layout/first-layout' +import { Route as postsPostsRouteImport } from './routes/posts/posts' import { Route as layoutSecondLayoutRouteImport } from './routes/layout/second-layout' -import { Route as postsPostsHomeRouteImport } from './routes/posts/posts-home' import { Route as ClassicHelloRouteRouteImport } from './routes/file-based-subtree/hello/route' +import { Route as postsPostsHomeRouteImport } from './routes/posts/posts-home' +import { Route as postsPostsDetailRouteImport } from './routes/posts/posts-detail' import { Route as ClassicHelloIndexRouteImport } from './routes/file-based-subtree/hello/index' -import { Route as ClassicHelloWorldRouteImport } from './routes/file-based-subtree/hello/world' import { Route as ClassicHelloUniverseRouteImport } from './routes/file-based-subtree/hello/universe' -import { Route as bRouteImport } from './routes/b' +import { Route as ClassicHelloWorldRouteImport } from './routes/file-based-subtree/hello/world' import { Route as aRouteImport } from './routes/a' +import { Route as bRouteImport } from './routes/b' -const postsPostsRoute = postsPostsRouteImport.update({ - id: '/posts', - path: '/posts', +const homeRoute = homeRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const layoutFirstLayoutRoute = layoutFirstLayoutRouteImport.update({ id: '/_first', getParentRoute: () => rootRouteImport, } as any) -const homeRoute = homeRouteImport.update({ - id: '/', - path: '/', +const postsPostsRoute = postsPostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const postsPostsDetailRoute = postsPostsDetailRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => postsPostsRoute, -} as any) const layoutSecondLayoutRoute = layoutSecondLayoutRouteImport.update({ id: '/_second-layout', getParentRoute: () => layoutFirstLayoutRoute, } as any) +const ClassicHelloRouteRoute = ClassicHelloRouteRouteImport.update({ + id: '/classic/hello', + path: '/classic/hello', + getParentRoute: () => rootRouteImport, +} as any) const postsPostsHomeRoute = postsPostsHomeRouteImport.update({ id: '/', path: '/', getParentRoute: () => postsPostsRoute, } as any) -const ClassicHelloRouteRoute = ClassicHelloRouteRouteImport.update({ - id: '/classic/hello', - path: '/classic/hello', - getParentRoute: () => rootRouteImport, +const postsPostsDetailRoute = postsPostsDetailRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => postsPostsRoute, } as any) const ClassicHelloIndexRoute = ClassicHelloIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => ClassicHelloRouteRoute, } as any) -const ClassicHelloWorldRoute = ClassicHelloWorldRouteImport.update({ - id: '/world', - path: '/world', - getParentRoute: () => ClassicHelloRouteRoute, -} as any) const ClassicHelloUniverseRoute = ClassicHelloUniverseRouteImport.update({ id: '/universe', path: '/universe', getParentRoute: () => ClassicHelloRouteRoute, } as any) -const bRoute = bRouteImport.update({ - id: '/route-without-file/layout-b', - path: '/route-without-file/layout-b', - getParentRoute: () => layoutSecondLayoutRoute, +const ClassicHelloWorldRoute = ClassicHelloWorldRouteImport.update({ + id: '/world', + path: '/world', + getParentRoute: () => ClassicHelloRouteRoute, } as any) const aRoute = aRouteImport.update({ id: '/route-without-file/layout-a', path: '/route-without-file/layout-a', getParentRoute: () => layoutSecondLayoutRoute, } as any) +const bRoute = bRouteImport.update({ + id: '/route-without-file/layout-b', + path: '/route-without-file/layout-b', + getParentRoute: () => layoutSecondLayoutRoute, +} as any) export interface FileRoutesByFullPath { '/': typeof homeRoute @@ -166,11 +166,11 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof postsPostsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof homeRouteImport parentRoute: typeof rootRouteImport } '/_first': { @@ -180,20 +180,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof layoutFirstLayoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof homeRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof postsPostsRouteImport parentRoute: typeof rootRouteImport } - '/posts/$postId': { - id: '/posts/$postId' - path: '/$postId' - fullPath: '/posts/$postId' - preLoaderRoute: typeof postsPostsDetailRouteImport - parentRoute: typeof postsPostsRoute - } '/_first/_second-layout': { id: '/_first/_second-layout' path: '' @@ -201,6 +194,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof layoutSecondLayoutRouteImport parentRoute: typeof layoutFirstLayoutRoute } + '/classic/hello': { + id: '/classic/hello' + path: '/classic/hello' + fullPath: '/classic/hello' + preLoaderRoute: typeof ClassicHelloRouteRouteImport + parentRoute: typeof rootRouteImport + } '/posts/': { id: '/posts/' path: '/' @@ -208,12 +208,12 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof postsPostsHomeRouteImport parentRoute: typeof postsPostsRoute } - '/classic/hello': { - id: '/classic/hello' - path: '/classic/hello' - fullPath: '/classic/hello' - preLoaderRoute: typeof ClassicHelloRouteRouteImport - parentRoute: typeof rootRouteImport + '/posts/$postId': { + id: '/posts/$postId' + path: '/$postId' + fullPath: '/posts/$postId' + preLoaderRoute: typeof postsPostsDetailRouteImport + parentRoute: typeof postsPostsRoute } '/classic/hello/': { id: '/classic/hello/' @@ -222,13 +222,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof ClassicHelloIndexRouteImport parentRoute: typeof ClassicHelloRouteRoute } - '/classic/hello/world': { - id: '/classic/hello/world' - path: '/world' - fullPath: '/classic/hello/world' - preLoaderRoute: typeof ClassicHelloWorldRouteImport - parentRoute: typeof ClassicHelloRouteRoute - } '/classic/hello/universe': { id: '/classic/hello/universe' path: '/universe' @@ -236,12 +229,12 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof ClassicHelloUniverseRouteImport parentRoute: typeof ClassicHelloRouteRoute } - '/_first/_second-layout/route-without-file/layout-b': { - id: '/_first/_second-layout/route-without-file/layout-b' - path: '/route-without-file/layout-b' - fullPath: '/route-without-file/layout-b' - preLoaderRoute: typeof bRouteImport - parentRoute: typeof layoutSecondLayoutRoute + '/classic/hello/world': { + id: '/classic/hello/world' + path: '/world' + fullPath: '/classic/hello/world' + preLoaderRoute: typeof ClassicHelloWorldRouteImport + parentRoute: typeof ClassicHelloRouteRoute } '/_first/_second-layout/route-without-file/layout-a': { id: '/_first/_second-layout/route-without-file/layout-a' @@ -250,6 +243,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof aRouteImport parentRoute: typeof layoutSecondLayoutRoute } + '/_first/_second-layout/route-without-file/layout-b': { + id: '/_first/_second-layout/route-without-file/layout-b' + path: '/route-without-file/layout-b' + fullPath: '/route-without-file/layout-b' + preLoaderRoute: typeof bRouteImport + parentRoute: typeof layoutSecondLayoutRoute + } } } diff --git a/examples/solid/basic-virtual-inside-file-based/src/routeTree.gen.ts b/examples/solid/basic-virtual-inside-file-based/src/routeTree.gen.ts index 037dac78a3..6117330aa3 100644 --- a/examples/solid/basic-virtual-inside-file-based/src/routeTree.gen.ts +++ b/examples/solid/basic-virtual-inside-file-based/src/routeTree.gen.ts @@ -9,36 +9,31 @@ // 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 PostsRouteImport } from './routes/posts' -import { Route as LayoutRouteImport } from './routes/_layout' import { Route as IndexRouteImport } from './routes/index' -import { Route as PostsDetailsRouteImport } from './routes/posts/details' +import { Route as LayoutRouteImport } from './routes/_layout' +import { Route as PostsRouteImport } from './routes/posts' import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' import { Route as PostsHomeRouteImport } from './routes/posts/home' -import { Route as PostsLetsGoIndexRouteImport } from './routes/posts/lets-go/index' -import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' +import { Route as PostsDetailsRouteImport } from './routes/posts/details' import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' +import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' +import { Route as PostsLetsGoIndexRouteImport } from './routes/posts/lets-go/index' import { Route as PostsLetsGoDeeperHomeRouteImport } from './routes/posts/lets-go/deeper/home' -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const LayoutRoute = LayoutRouteImport.update({ id: '/_layout', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const PostsDetailsRoute = PostsDetailsRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => PostsRoute, -} as any) const LayoutLayout2Route = LayoutLayout2RouteImport.update({ id: '/_layout-2', getParentRoute: () => LayoutRoute, @@ -48,20 +43,25 @@ const PostsHomeRoute = PostsHomeRouteImport.update({ path: '/', getParentRoute: () => PostsRoute, } as any) -const PostsLetsGoIndexRoute = PostsLetsGoIndexRouteImport.update({ - id: '/inception/', - path: '/inception/', +const PostsDetailsRoute = PostsDetailsRouteImport.update({ + id: '/$postId', + path: '/$postId', getParentRoute: () => PostsRoute, } as any) +const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ + id: '/layout-a', + path: '/layout-a', + getParentRoute: () => LayoutLayout2Route, +} as any) const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ id: '/layout-b', path: '/layout-b', getParentRoute: () => LayoutLayout2Route, } as any) -const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ - id: '/layout-a', - path: '/layout-a', - getParentRoute: () => LayoutLayout2Route, +const PostsLetsGoIndexRoute = PostsLetsGoIndexRouteImport.update({ + id: '/inception/', + path: '/inception/', + getParentRoute: () => PostsRoute, } as any) const PostsLetsGoDeeperHomeRoute = PostsLetsGoDeeperHomeRouteImport.update({ id: '/inception/deeper/', @@ -143,11 +143,11 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/_layout': { @@ -157,20 +157,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/posts/$postId': { - id: '/posts/$postId' - path: '/$postId' - fullPath: '/posts/$postId' - preLoaderRoute: typeof PostsDetailsRouteImport - parentRoute: typeof PostsRoute - } '/_layout/_layout-2': { id: '/_layout/_layout-2' path: '' @@ -185,13 +178,20 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsHomeRouteImport parentRoute: typeof PostsRoute } - '/posts/inception/': { - id: '/posts/inception/' - path: '/inception' - fullPath: '/posts/inception/' - preLoaderRoute: typeof PostsLetsGoIndexRouteImport + '/posts/$postId': { + id: '/posts/$postId' + path: '/$postId' + fullPath: '/posts/$postId' + preLoaderRoute: typeof PostsDetailsRouteImport parentRoute: typeof PostsRoute } + '/_layout/_layout-2/layout-a': { + id: '/_layout/_layout-2/layout-a' + path: '/layout-a' + fullPath: '/layout-a' + preLoaderRoute: typeof LayoutLayout2LayoutARouteImport + parentRoute: typeof LayoutLayout2Route + } '/_layout/_layout-2/layout-b': { id: '/_layout/_layout-2/layout-b' path: '/layout-b' @@ -199,12 +199,12 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport parentRoute: typeof LayoutLayout2Route } - '/_layout/_layout-2/layout-a': { - id: '/_layout/_layout-2/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: typeof LayoutLayout2LayoutARouteImport - parentRoute: typeof LayoutLayout2Route + '/posts/inception/': { + id: '/posts/inception/' + path: '/inception' + fullPath: '/posts/inception/' + preLoaderRoute: typeof PostsLetsGoIndexRouteImport + parentRoute: typeof PostsRoute } '/posts/inception/deeper/': { id: '/posts/inception/deeper/' diff --git a/examples/solid/i18n-paraglide/src/routeTree.gen.ts b/examples/solid/i18n-paraglide/src/routeTree.gen.ts index 333a815c38..98df1e83d8 100644 --- a/examples/solid/i18n-paraglide/src/routeTree.gen.ts +++ b/examples/solid/i18n-paraglide/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' +import { Route as AboutRouteImport } from './routes/about' -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/examples/solid/kitchen-sink-file-based/src/routeTree.gen.ts b/examples/solid/kitchen-sink-file-based/src/routeTree.gen.ts index 0c6e04f63d..76bb314a71 100644 --- a/examples/solid/kitchen-sink-file-based/src/routeTree.gen.ts +++ b/examples/solid/kitchen-sink-file-based/src/routeTree.gen.ts @@ -9,81 +9,71 @@ // 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 LoginRouteImport } from './routes/login' -import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' +import { Route as IndexRouteImport } from './routes/index' import { Route as AuthRouteImport } from './routes/_auth' +import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as DashboardRouteRouteImport } from './routes/dashboard.route' -import { Route as IndexRouteImport } from './routes/index' -import { Route as ExpensiveIndexRouteImport } from './routes/expensive/index' -import { Route as DashboardIndexRouteImport } from './routes/dashboard.index' -import { Route as PathlessLayoutRouteBRouteImport } from './routes/_pathlessLayout.route-b' -import { Route as PathlessLayoutRouteARouteImport } from './routes/_pathlessLayout.route-a' -import { Route as AuthProfileRouteImport } from './routes/_auth.profile' +import { Route as LoginRouteImport } from './routes/login' import { Route as thisFolderIsNotInTheUrlRouteGroupRouteImport } from './routes/(this-folder-is-not-in-the-url)/route-group' -import { Route as DashboardUsersRouteRouteImport } from './routes/dashboard.users.route' +import { Route as AuthProfileRouteImport } from './routes/_auth.profile' +import { Route as PathlessLayoutRouteARouteImport } from './routes/_pathlessLayout.route-a' +import { Route as PathlessLayoutRouteBRouteImport } from './routes/_pathlessLayout.route-b' +import { Route as DashboardIndexRouteImport } from './routes/dashboard.index' import { Route as DashboardInvoicesRouteRouteImport } from './routes/dashboard.invoices.route' -import { Route as DashboardUsersIndexRouteImport } from './routes/dashboard.users.index' +import { Route as DashboardUsersRouteRouteImport } from './routes/dashboard.users.route' +import { Route as ExpensiveIndexRouteImport } from './routes/expensive/index' import { Route as DashboardInvoicesIndexRouteImport } from './routes/dashboard.invoices.index' -import { Route as DashboardUsersUserRouteImport } from './routes/dashboard.users.user' import { Route as DashboardInvoicesInvoiceIdRouteImport } from './routes/dashboard.invoices.$invoiceId' +import { Route as DashboardUsersIndexRouteImport } from './routes/dashboard.users.index' +import { Route as DashboardUsersUserRouteImport } from './routes/dashboard.users.user' -const LoginRoute = LoginRouteImport.update({ - id: '/login', - path: '/login', - getParentRoute: () => rootRouteImport, -} as any) -const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ - id: '/_pathlessLayout', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const AuthRoute = AuthRouteImport.update({ id: '/_auth', getParentRoute: () => rootRouteImport, } as any) +const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ + id: '/_pathlessLayout', + getParentRoute: () => rootRouteImport, +} as any) const DashboardRouteRoute = DashboardRouteRouteImport.update({ id: '/dashboard', path: '/dashboard', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) -const ExpensiveIndexRoute = ExpensiveIndexRouteImport.update({ - id: '/expensive/', - path: '/expensive/', +const LoginRoute = LoginRouteImport.update({ + id: '/login', + path: '/login', getParentRoute: () => rootRouteImport, } as any) -const DashboardIndexRoute = DashboardIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => DashboardRouteRoute, -} as any) -const PathlessLayoutRouteBRoute = PathlessLayoutRouteBRouteImport.update({ - id: '/route-b', - path: '/route-b', - getParentRoute: () => PathlessLayoutRoute, +const thisFolderIsNotInTheUrlRouteGroupRoute = + thisFolderIsNotInTheUrlRouteGroupRouteImport.update({ + id: '/(this-folder-is-not-in-the-url)/route-group', + path: '/route-group', + getParentRoute: () => rootRouteImport, + } as any) +const AuthProfileRoute = AuthProfileRouteImport.update({ + id: '/profile', + path: '/profile', + getParentRoute: () => AuthRoute, } as any) const PathlessLayoutRouteARoute = PathlessLayoutRouteARouteImport.update({ id: '/route-a', path: '/route-a', getParentRoute: () => PathlessLayoutRoute, } as any) -const AuthProfileRoute = AuthProfileRouteImport.update({ - id: '/profile', - path: '/profile', - getParentRoute: () => AuthRoute, +const PathlessLayoutRouteBRoute = PathlessLayoutRouteBRouteImport.update({ + id: '/route-b', + path: '/route-b', + getParentRoute: () => PathlessLayoutRoute, } as any) -const thisFolderIsNotInTheUrlRouteGroupRoute = - thisFolderIsNotInTheUrlRouteGroupRouteImport.update({ - id: '/(this-folder-is-not-in-the-url)/route-group', - path: '/route-group', - getParentRoute: () => rootRouteImport, - } as any) -const DashboardUsersRouteRoute = DashboardUsersRouteRouteImport.update({ - id: '/users', - path: '/users', +const DashboardIndexRoute = DashboardIndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => DashboardRouteRoute, } as any) const DashboardInvoicesRouteRoute = DashboardInvoicesRouteRouteImport.update({ @@ -91,27 +81,37 @@ const DashboardInvoicesRouteRoute = DashboardInvoicesRouteRouteImport.update({ path: '/invoices', getParentRoute: () => DashboardRouteRoute, } as any) -const DashboardUsersIndexRoute = DashboardUsersIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => DashboardUsersRouteRoute, +const DashboardUsersRouteRoute = DashboardUsersRouteRouteImport.update({ + id: '/users', + path: '/users', + getParentRoute: () => DashboardRouteRoute, +} as any) +const ExpensiveIndexRoute = ExpensiveIndexRouteImport.update({ + id: '/expensive/', + path: '/expensive/', + getParentRoute: () => rootRouteImport, } as any) const DashboardInvoicesIndexRoute = DashboardInvoicesIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => DashboardInvoicesRouteRoute, } as any) -const DashboardUsersUserRoute = DashboardUsersUserRouteImport.update({ - id: '/user', - path: '/user', - getParentRoute: () => DashboardUsersRouteRoute, -} as any) const DashboardInvoicesInvoiceIdRoute = DashboardInvoicesInvoiceIdRouteImport.update({ id: '/$invoiceId', path: '/$invoiceId', getParentRoute: () => DashboardInvoicesRouteRoute, } as any) +const DashboardUsersIndexRoute = DashboardUsersIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => DashboardUsersRouteRoute, +} as any) +const DashboardUsersUserRoute = DashboardUsersUserRouteImport.update({ + id: '/user', + path: '/user', + getParentRoute: () => DashboardUsersRouteRoute, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -229,18 +229,11 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/login': { - id: '/login' - path: '/login' - fullPath: '/login' - preLoaderRoute: typeof LoginRouteImport - parentRoute: typeof rootRouteImport - } - '/_pathlessLayout': { - id: '/_pathlessLayout' - path: '' + '/': { + id: '/' + path: '/' fullPath: '/' - preLoaderRoute: typeof PathlessLayoutRouteImport + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/_auth': { @@ -250,6 +243,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof AuthRouteImport parentRoute: typeof rootRouteImport } + '/_pathlessLayout': { + id: '/_pathlessLayout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutRouteImport + parentRoute: typeof rootRouteImport + } '/dashboard': { id: '/dashboard' path: '/dashboard' @@ -257,33 +257,26 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof DashboardRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/login': { + id: '/login' + path: '/login' + fullPath: '/login' + preLoaderRoute: typeof LoginRouteImport parentRoute: typeof rootRouteImport } - '/expensive/': { - id: '/expensive/' - path: '/expensive' - fullPath: '/expensive/' - preLoaderRoute: typeof ExpensiveIndexRouteImport + '/(this-folder-is-not-in-the-url)/route-group': { + id: '/(this-folder-is-not-in-the-url)/route-group' + path: '/route-group' + fullPath: '/route-group' + preLoaderRoute: typeof thisFolderIsNotInTheUrlRouteGroupRouteImport parentRoute: typeof rootRouteImport } - '/dashboard/': { - id: '/dashboard/' - path: '/' - fullPath: '/dashboard/' - preLoaderRoute: typeof DashboardIndexRouteImport - parentRoute: typeof DashboardRouteRoute - } - '/_pathlessLayout/route-b': { - id: '/_pathlessLayout/route-b' - path: '/route-b' - fullPath: '/route-b' - preLoaderRoute: typeof PathlessLayoutRouteBRouteImport - parentRoute: typeof PathlessLayoutRoute + '/_auth/profile': { + id: '/_auth/profile' + path: '/profile' + fullPath: '/profile' + preLoaderRoute: typeof AuthProfileRouteImport + parentRoute: typeof AuthRoute } '/_pathlessLayout/route-a': { id: '/_pathlessLayout/route-a' @@ -292,25 +285,18 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PathlessLayoutRouteARouteImport parentRoute: typeof PathlessLayoutRoute } - '/_auth/profile': { - id: '/_auth/profile' - path: '/profile' - fullPath: '/profile' - preLoaderRoute: typeof AuthProfileRouteImport - parentRoute: typeof AuthRoute - } - '/(this-folder-is-not-in-the-url)/route-group': { - id: '/(this-folder-is-not-in-the-url)/route-group' - path: '/route-group' - fullPath: '/route-group' - preLoaderRoute: typeof thisFolderIsNotInTheUrlRouteGroupRouteImport - parentRoute: typeof rootRouteImport + '/_pathlessLayout/route-b': { + id: '/_pathlessLayout/route-b' + path: '/route-b' + fullPath: '/route-b' + preLoaderRoute: typeof PathlessLayoutRouteBRouteImport + parentRoute: typeof PathlessLayoutRoute } - '/dashboard/users': { - id: '/dashboard/users' - path: '/users' - fullPath: '/dashboard/users' - preLoaderRoute: typeof DashboardUsersRouteRouteImport + '/dashboard/': { + id: '/dashboard/' + path: '/' + fullPath: '/dashboard/' + preLoaderRoute: typeof DashboardIndexRouteImport parentRoute: typeof DashboardRouteRoute } '/dashboard/invoices': { @@ -320,12 +306,19 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof DashboardInvoicesRouteRouteImport parentRoute: typeof DashboardRouteRoute } - '/dashboard/users/': { - id: '/dashboard/users/' - path: '/' - fullPath: '/dashboard/users/' - preLoaderRoute: typeof DashboardUsersIndexRouteImport - parentRoute: typeof DashboardUsersRouteRoute + '/dashboard/users': { + id: '/dashboard/users' + path: '/users' + fullPath: '/dashboard/users' + preLoaderRoute: typeof DashboardUsersRouteRouteImport + parentRoute: typeof DashboardRouteRoute + } + '/expensive/': { + id: '/expensive/' + path: '/expensive' + fullPath: '/expensive/' + preLoaderRoute: typeof ExpensiveIndexRouteImport + parentRoute: typeof rootRouteImport } '/dashboard/invoices/': { id: '/dashboard/invoices/' @@ -334,13 +327,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof DashboardInvoicesIndexRouteImport parentRoute: typeof DashboardInvoicesRouteRoute } - '/dashboard/users/user': { - id: '/dashboard/users/user' - path: '/user' - fullPath: '/dashboard/users/user' - preLoaderRoute: typeof DashboardUsersUserRouteImport - parentRoute: typeof DashboardUsersRouteRoute - } '/dashboard/invoices/$invoiceId': { id: '/dashboard/invoices/$invoiceId' path: '/$invoiceId' @@ -348,6 +334,20 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof DashboardInvoicesInvoiceIdRouteImport parentRoute: typeof DashboardInvoicesRouteRoute } + '/dashboard/users/': { + id: '/dashboard/users/' + path: '/' + fullPath: '/dashboard/users/' + preLoaderRoute: typeof DashboardUsersIndexRouteImport + parentRoute: typeof DashboardUsersRouteRoute + } + '/dashboard/users/user': { + id: '/dashboard/users/user' + path: '/user' + fullPath: '/dashboard/users/user' + preLoaderRoute: typeof DashboardUsersUserRouteImport + parentRoute: typeof DashboardUsersRouteRoute + } } } diff --git a/examples/solid/kitchen-sink-solid-query-file-based/src/routeTree.gen.ts b/examples/solid/kitchen-sink-solid-query-file-based/src/routeTree.gen.ts index e296846127..8235344782 100644 --- a/examples/solid/kitchen-sink-solid-query-file-based/src/routeTree.gen.ts +++ b/examples/solid/kitchen-sink-solid-query-file-based/src/routeTree.gen.ts @@ -9,108 +9,108 @@ // 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 LoginRouteImport } from './routes/login' -import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' +import { Route as IndexRouteImport } from './routes/index' import { Route as AuthRouteImport } from './routes/_auth' +import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as DashboardRouteRouteImport } from './routes/dashboard.route' -import { Route as IndexRouteImport } from './routes/index' -import { Route as ExpensiveIndexRouteImport } from './routes/expensive/index' -import { Route as DashboardIndexRouteImport } from './routes/dashboard.index' -import { Route as FooBarRouteImport } from './routes/foo/bar' -import { Route as PathlessLayoutRouteBRouteImport } from './routes/_pathlessLayout.route-b' -import { Route as PathlessLayoutRouteARouteImport } from './routes/_pathlessLayout.route-a' +import { Route as LoginRouteImport } from './routes/login' import { Route as AuthProfileRouteImport } from './routes/_auth.profile' -import { Route as DashboardUsersRouteRouteImport } from './routes/dashboard.users.route' +import { Route as PathlessLayoutRouteARouteImport } from './routes/_pathlessLayout.route-a' +import { Route as PathlessLayoutRouteBRouteImport } from './routes/_pathlessLayout.route-b' +import { Route as DashboardIndexRouteImport } from './routes/dashboard.index' import { Route as DashboardInvoicesRouteRouteImport } from './routes/dashboard.invoices.route' -import { Route as DashboardUsersIndexRouteImport } from './routes/dashboard.users.index' +import { Route as DashboardUsersRouteRouteImport } from './routes/dashboard.users.route' +import { Route as ExpensiveIndexRouteImport } from './routes/expensive/index' +import { Route as FooBarRouteImport } from './routes/foo/bar' import { Route as DashboardInvoicesIndexRouteImport } from './routes/dashboard.invoices.index' -import { Route as DashboardUsersUserRouteImport } from './routes/dashboard.users.user' import { Route as DashboardInvoicesInvoiceIdRouteImport } from './routes/dashboard.invoices.$invoiceId' +import { Route as DashboardUsersIndexRouteImport } from './routes/dashboard.users.index' +import { Route as DashboardUsersUserRouteImport } from './routes/dashboard.users.user' -const LoginRoute = LoginRouteImport.update({ - id: '/login', - path: '/login', - getParentRoute: () => rootRouteImport, -} as any) -const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ - id: '/_pathlessLayout', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const AuthRoute = AuthRouteImport.update({ id: '/_auth', getParentRoute: () => rootRouteImport, } as any) +const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ + id: '/_pathlessLayout', + getParentRoute: () => rootRouteImport, +} as any) const DashboardRouteRoute = DashboardRouteRouteImport.update({ id: '/dashboard', path: '/dashboard', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) -const ExpensiveIndexRoute = ExpensiveIndexRouteImport.update({ - id: '/expensive/', - path: '/expensive/', +const LoginRoute = LoginRouteImport.update({ + id: '/login', + path: '/login', getParentRoute: () => rootRouteImport, } as any) -const DashboardIndexRoute = DashboardIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => DashboardRouteRoute, +const AuthProfileRoute = AuthProfileRouteImport.update({ + id: '/profile', + path: '/profile', + getParentRoute: () => AuthRoute, } as any) -const FooBarRoute = FooBarRouteImport.update({ - id: '/foo/bar', - path: '/foo/bar', - getParentRoute: () => rootRouteImport, +const PathlessLayoutRouteARoute = PathlessLayoutRouteARouteImport.update({ + id: '/route-a', + path: '/route-a', + getParentRoute: () => PathlessLayoutRoute, } as any) const PathlessLayoutRouteBRoute = PathlessLayoutRouteBRouteImport.update({ id: '/route-b', path: '/route-b', getParentRoute: () => PathlessLayoutRoute, } as any) -const PathlessLayoutRouteARoute = PathlessLayoutRouteARouteImport.update({ - id: '/route-a', - path: '/route-a', - getParentRoute: () => PathlessLayoutRoute, +const DashboardIndexRoute = DashboardIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => DashboardRouteRoute, } as any) -const AuthProfileRoute = AuthProfileRouteImport.update({ - id: '/profile', - path: '/profile', - getParentRoute: () => AuthRoute, +const DashboardInvoicesRouteRoute = DashboardInvoicesRouteRouteImport.update({ + id: '/invoices', + path: '/invoices', + getParentRoute: () => DashboardRouteRoute, } as any) const DashboardUsersRouteRoute = DashboardUsersRouteRouteImport.update({ id: '/users', path: '/users', getParentRoute: () => DashboardRouteRoute, } as any) -const DashboardInvoicesRouteRoute = DashboardInvoicesRouteRouteImport.update({ - id: '/invoices', - path: '/invoices', - getParentRoute: () => DashboardRouteRoute, +const ExpensiveIndexRoute = ExpensiveIndexRouteImport.update({ + id: '/expensive/', + path: '/expensive/', + getParentRoute: () => rootRouteImport, } as any) -const DashboardUsersIndexRoute = DashboardUsersIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => DashboardUsersRouteRoute, +const FooBarRoute = FooBarRouteImport.update({ + id: '/foo/bar', + path: '/foo/bar', + getParentRoute: () => rootRouteImport, } as any) const DashboardInvoicesIndexRoute = DashboardInvoicesIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => DashboardInvoicesRouteRoute, } as any) -const DashboardUsersUserRoute = DashboardUsersUserRouteImport.update({ - id: '/user', - path: '/user', - getParentRoute: () => DashboardUsersRouteRoute, -} as any) const DashboardInvoicesInvoiceIdRoute = DashboardInvoicesInvoiceIdRouteImport.update({ id: '/$invoiceId', path: '/$invoiceId', getParentRoute: () => DashboardInvoicesRouteRoute, } as any) +const DashboardUsersIndexRoute = DashboardUsersIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => DashboardUsersRouteRoute, +} as any) +const DashboardUsersUserRoute = DashboardUsersUserRouteImport.update({ + id: '/user', + path: '/user', + getParentRoute: () => DashboardUsersRouteRoute, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -228,18 +228,11 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/login': { - id: '/login' - path: '/login' - fullPath: '/login' - preLoaderRoute: typeof LoginRouteImport - parentRoute: typeof rootRouteImport - } - '/_pathlessLayout': { - id: '/_pathlessLayout' - path: '' + '/': { + id: '/' + path: '/' fullPath: '/' - preLoaderRoute: typeof PathlessLayoutRouteImport + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/_auth': { @@ -249,6 +242,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof AuthRouteImport parentRoute: typeof rootRouteImport } + '/_pathlessLayout': { + id: '/_pathlessLayout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutRouteImport + parentRoute: typeof rootRouteImport + } '/dashboard': { id: '/dashboard' path: '/dashboard' @@ -256,33 +256,26 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof DashboardRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport - } - '/expensive/': { - id: '/expensive/' - path: '/expensive' - fullPath: '/expensive/' - preLoaderRoute: typeof ExpensiveIndexRouteImport + '/login': { + id: '/login' + path: '/login' + fullPath: '/login' + preLoaderRoute: typeof LoginRouteImport parentRoute: typeof rootRouteImport } - '/dashboard/': { - id: '/dashboard/' - path: '/' - fullPath: '/dashboard/' - preLoaderRoute: typeof DashboardIndexRouteImport - parentRoute: typeof DashboardRouteRoute + '/_auth/profile': { + id: '/_auth/profile' + path: '/profile' + fullPath: '/profile' + preLoaderRoute: typeof AuthProfileRouteImport + parentRoute: typeof AuthRoute } - '/foo/bar': { - id: '/foo/bar' - path: '/foo/bar' - fullPath: '/foo/bar' - preLoaderRoute: typeof FooBarRouteImport - parentRoute: typeof rootRouteImport + '/_pathlessLayout/route-a': { + id: '/_pathlessLayout/route-a' + path: '/route-a' + fullPath: '/route-a' + preLoaderRoute: typeof PathlessLayoutRouteARouteImport + parentRoute: typeof PathlessLayoutRoute } '/_pathlessLayout/route-b': { id: '/_pathlessLayout/route-b' @@ -291,19 +284,19 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PathlessLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutRoute } - '/_pathlessLayout/route-a': { - id: '/_pathlessLayout/route-a' - path: '/route-a' - fullPath: '/route-a' - preLoaderRoute: typeof PathlessLayoutRouteARouteImport - parentRoute: typeof PathlessLayoutRoute + '/dashboard/': { + id: '/dashboard/' + path: '/' + fullPath: '/dashboard/' + preLoaderRoute: typeof DashboardIndexRouteImport + parentRoute: typeof DashboardRouteRoute } - '/_auth/profile': { - id: '/_auth/profile' - path: '/profile' - fullPath: '/profile' - preLoaderRoute: typeof AuthProfileRouteImport - parentRoute: typeof AuthRoute + '/dashboard/invoices': { + id: '/dashboard/invoices' + path: '/invoices' + fullPath: '/dashboard/invoices' + preLoaderRoute: typeof DashboardInvoicesRouteRouteImport + parentRoute: typeof DashboardRouteRoute } '/dashboard/users': { id: '/dashboard/users' @@ -312,19 +305,19 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof DashboardUsersRouteRouteImport parentRoute: typeof DashboardRouteRoute } - '/dashboard/invoices': { - id: '/dashboard/invoices' - path: '/invoices' - fullPath: '/dashboard/invoices' - preLoaderRoute: typeof DashboardInvoicesRouteRouteImport - parentRoute: typeof DashboardRouteRoute + '/expensive/': { + id: '/expensive/' + path: '/expensive' + fullPath: '/expensive/' + preLoaderRoute: typeof ExpensiveIndexRouteImport + parentRoute: typeof rootRouteImport } - '/dashboard/users/': { - id: '/dashboard/users/' - path: '/' - fullPath: '/dashboard/users/' - preLoaderRoute: typeof DashboardUsersIndexRouteImport - parentRoute: typeof DashboardUsersRouteRoute + '/foo/bar': { + id: '/foo/bar' + path: '/foo/bar' + fullPath: '/foo/bar' + preLoaderRoute: typeof FooBarRouteImport + parentRoute: typeof rootRouteImport } '/dashboard/invoices/': { id: '/dashboard/invoices/' @@ -333,13 +326,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof DashboardInvoicesIndexRouteImport parentRoute: typeof DashboardInvoicesRouteRoute } - '/dashboard/users/user': { - id: '/dashboard/users/user' - path: '/user' - fullPath: '/dashboard/users/user' - preLoaderRoute: typeof DashboardUsersUserRouteImport - parentRoute: typeof DashboardUsersRouteRoute - } '/dashboard/invoices/$invoiceId': { id: '/dashboard/invoices/$invoiceId' path: '/$invoiceId' @@ -347,6 +333,20 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof DashboardInvoicesInvoiceIdRouteImport parentRoute: typeof DashboardInvoicesRouteRoute } + '/dashboard/users/': { + id: '/dashboard/users/' + path: '/' + fullPath: '/dashboard/users/' + preLoaderRoute: typeof DashboardUsersIndexRouteImport + parentRoute: typeof DashboardUsersRouteRoute + } + '/dashboard/users/user': { + id: '/dashboard/users/user' + path: '/user' + fullPath: '/dashboard/users/user' + preLoaderRoute: typeof DashboardUsersUserRouteImport + parentRoute: typeof DashboardUsersRouteRoute + } } } diff --git a/examples/solid/large-file-based/src/routeTree.gen.ts b/examples/solid/large-file-based/src/routeTree.gen.ts index d345b69acf..ea7329778c 100644 --- a/examples/solid/large-file-based/src/routeTree.gen.ts +++ b/examples/solid/large-file-based/src/routeTree.gen.ts @@ -9,23 +9,18 @@ // 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 RelativeRouteImport } from './routes/relative' -import { Route as LinkPropsRouteImport } from './routes/linkProps' +import { Route as IndexRouteImport } from './routes/index' import { Route as AbsoluteRouteImport } from './routes/absolute' -import { Route as SearchRouteRouteImport } from './routes/search/route' +import { Route as LinkPropsRouteImport } from './routes/linkProps' import { Route as ParamsRouteRouteImport } from './routes/params/route' -import { Route as IndexRouteImport } from './routes/index' -import { Route as SearchSearchPlaceholderRouteImport } from './routes/search/searchPlaceholder' +import { Route as RelativeRouteImport } from './routes/relative' +import { Route as SearchRouteRouteImport } from './routes/search/route' import { Route as ParamsParamsPlaceholderRouteImport } from './routes/params/$paramsPlaceholder' +import { Route as SearchSearchPlaceholderRouteImport } from './routes/search/searchPlaceholder' -const RelativeRoute = RelativeRouteImport.update({ - id: '/relative', - path: '/relative', - getParentRoute: () => rootRouteImport, -} as any) -const LinkPropsRoute = LinkPropsRouteImport.update({ - id: '/linkProps', - path: '/linkProps', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const AbsoluteRoute = AbsoluteRouteImport.update({ @@ -33,9 +28,9 @@ const AbsoluteRoute = AbsoluteRouteImport.update({ path: '/absolute', getParentRoute: () => rootRouteImport, } as any) -const SearchRouteRoute = SearchRouteRouteImport.update({ - id: '/search', - path: '/search', +const LinkPropsRoute = LinkPropsRouteImport.update({ + id: '/linkProps', + path: '/linkProps', getParentRoute: () => rootRouteImport, } as any) const ParamsRouteRoute = ParamsRouteRouteImport.update({ @@ -43,21 +38,26 @@ const ParamsRouteRoute = ParamsRouteRouteImport.update({ path: '/params', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const RelativeRoute = RelativeRouteImport.update({ + id: '/relative', + path: '/relative', getParentRoute: () => rootRouteImport, } as any) -const SearchSearchPlaceholderRoute = SearchSearchPlaceholderRouteImport.update({ - id: '/searchPlaceholder', - path: '/searchPlaceholder', - getParentRoute: () => SearchRouteRoute, +const SearchRouteRoute = SearchRouteRouteImport.update({ + id: '/search', + path: '/search', + getParentRoute: () => rootRouteImport, } as any) const ParamsParamsPlaceholderRoute = ParamsParamsPlaceholderRouteImport.update({ id: '/$paramsPlaceholder', path: '/$paramsPlaceholder', getParentRoute: () => ParamsRouteRoute, } as any) +const SearchSearchPlaceholderRoute = SearchSearchPlaceholderRouteImport.update({ + id: '/searchPlaceholder', + path: '/searchPlaceholder', + getParentRoute: () => SearchRouteRoute, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -134,18 +134,11 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/relative': { - id: '/relative' - path: '/relative' - fullPath: '/relative' - preLoaderRoute: typeof RelativeRouteImport - parentRoute: typeof rootRouteImport - } - '/linkProps': { - id: '/linkProps' - path: '/linkProps' - fullPath: '/linkProps' - preLoaderRoute: typeof LinkPropsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/absolute': { @@ -155,11 +148,11 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof AbsoluteRouteImport parentRoute: typeof rootRouteImport } - '/search': { - id: '/search' - path: '/search' - fullPath: '/search' - preLoaderRoute: typeof SearchRouteRouteImport + '/linkProps': { + id: '/linkProps' + path: '/linkProps' + fullPath: '/linkProps' + preLoaderRoute: typeof LinkPropsRouteImport parentRoute: typeof rootRouteImport } '/params': { @@ -169,19 +162,19 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof ParamsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/relative': { + id: '/relative' + path: '/relative' + fullPath: '/relative' + preLoaderRoute: typeof RelativeRouteImport parentRoute: typeof rootRouteImport } - '/search/searchPlaceholder': { - id: '/search/searchPlaceholder' - path: '/searchPlaceholder' - fullPath: '/search/searchPlaceholder' - preLoaderRoute: typeof SearchSearchPlaceholderRouteImport - parentRoute: typeof SearchRouteRoute + '/search': { + id: '/search' + path: '/search' + fullPath: '/search' + preLoaderRoute: typeof SearchRouteRouteImport + parentRoute: typeof rootRouteImport } '/params/$paramsPlaceholder': { id: '/params/$paramsPlaceholder' @@ -190,6 +183,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof ParamsParamsPlaceholderRouteImport parentRoute: typeof ParamsRouteRoute } + '/search/searchPlaceholder': { + id: '/search/searchPlaceholder' + path: '/searchPlaceholder' + fullPath: '/search/searchPlaceholder' + preLoaderRoute: typeof SearchSearchPlaceholderRouteImport + parentRoute: typeof SearchRouteRoute + } } } diff --git a/examples/solid/quickstart-file-based/src/routeTree.gen.ts b/examples/solid/quickstart-file-based/src/routeTree.gen.ts index 333a815c38..98df1e83d8 100644 --- a/examples/solid/quickstart-file-based/src/routeTree.gen.ts +++ b/examples/solid/quickstart-file-based/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' +import { Route as AboutRouteImport } from './routes/about' -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/examples/solid/quickstart-rspack-file-based/src/routeTree.gen.ts b/examples/solid/quickstart-rspack-file-based/src/routeTree.gen.ts index 333a815c38..98df1e83d8 100644 --- a/examples/solid/quickstart-rspack-file-based/src/routeTree.gen.ts +++ b/examples/solid/quickstart-rspack-file-based/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' +import { Route as AboutRouteImport } from './routes/about' -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/examples/solid/quickstart-webpack-file-based/src/routeTree.gen.ts b/examples/solid/quickstart-webpack-file-based/src/routeTree.gen.ts index 333a815c38..98df1e83d8 100644 --- a/examples/solid/quickstart-webpack-file-based/src/routeTree.gen.ts +++ b/examples/solid/quickstart-webpack-file-based/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' +import { Route as AboutRouteImport } from './routes/about' -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/examples/solid/router-monorepo-simple-lazy/packages/router/src/routeTree.gen.ts b/examples/solid/router-monorepo-simple-lazy/packages/router/src/routeTree.gen.ts index e8f1a5370b..8760312d24 100644 --- a/examples/solid/router-monorepo-simple-lazy/packages/router/src/routeTree.gen.ts +++ b/examples/solid/router-monorepo-simple-lazy/packages/router/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 PostIdRouteImport } from './routes/$postId' import { Route as IndexRouteImport } from './routes/index' +import { Route as PostIdRouteImport } from './routes/$postId' -const PostIdRoute = PostIdRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const PostIdRoute = PostIdRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/$postId': { - id: '/$postId' - path: '/$postId' - fullPath: '/$postId' - preLoaderRoute: typeof PostIdRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/$postId': { + id: '/$postId' + path: '/$postId' + fullPath: '/$postId' + preLoaderRoute: typeof PostIdRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/examples/solid/router-monorepo-simple/packages/router/src/routeTree.gen.ts b/examples/solid/router-monorepo-simple/packages/router/src/routeTree.gen.ts index e8f1a5370b..8760312d24 100644 --- a/examples/solid/router-monorepo-simple/packages/router/src/routeTree.gen.ts +++ b/examples/solid/router-monorepo-simple/packages/router/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 PostIdRouteImport } from './routes/$postId' import { Route as IndexRouteImport } from './routes/index' +import { Route as PostIdRouteImport } from './routes/$postId' -const PostIdRoute = PostIdRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const PostIdRoute = PostIdRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/$postId': { - id: '/$postId' - path: '/$postId' - fullPath: '/$postId' - preLoaderRoute: typeof PostIdRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/$postId': { + id: '/$postId' + path: '/$postId' + fullPath: '/$postId' + preLoaderRoute: typeof PostIdRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/examples/solid/router-monorepo-solid-query/packages/router/src/routeTree.gen.ts b/examples/solid/router-monorepo-solid-query/packages/router/src/routeTree.gen.ts index e8f1a5370b..8760312d24 100644 --- a/examples/solid/router-monorepo-solid-query/packages/router/src/routeTree.gen.ts +++ b/examples/solid/router-monorepo-solid-query/packages/router/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 PostIdRouteImport } from './routes/$postId' import { Route as IndexRouteImport } from './routes/index' +import { Route as PostIdRouteImport } from './routes/$postId' -const PostIdRoute = PostIdRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const PostIdRoute = PostIdRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/$postId': { - id: '/$postId' - path: '/$postId' - fullPath: '/$postId' - preLoaderRoute: typeof PostIdRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/$postId': { + id: '/$postId' + path: '/$postId' + fullPath: '/$postId' + preLoaderRoute: typeof PostIdRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/examples/solid/search-validator-adapters/src/routeTree.gen.ts b/examples/solid/search-validator-adapters/src/routeTree.gen.ts index 24089ece84..4cb97b08e9 100644 --- a/examples/solid/search-validator-adapters/src/routeTree.gen.ts +++ b/examples/solid/search-validator-adapters/src/routeTree.gen.ts @@ -10,18 +10,18 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' -import { Route as UsersZodIndexRouteImport } from './routes/users/zod.index' -import { Route as UsersValibotIndexRouteImport } from './routes/users/valibot.index' import { Route as UsersArktypeIndexRouteImport } from './routes/users/arktype.index' +import { Route as UsersValibotIndexRouteImport } from './routes/users/valibot.index' +import { Route as UsersZodIndexRouteImport } from './routes/users/zod.index' const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) -const UsersZodIndexRoute = UsersZodIndexRouteImport.update({ - id: '/users/zod/', - path: '/users/zod/', +const UsersArktypeIndexRoute = UsersArktypeIndexRouteImport.update({ + id: '/users/arktype/', + path: '/users/arktype/', getParentRoute: () => rootRouteImport, } as any) const UsersValibotIndexRoute = UsersValibotIndexRouteImport.update({ @@ -29,9 +29,9 @@ const UsersValibotIndexRoute = UsersValibotIndexRouteImport.update({ path: '/users/valibot/', getParentRoute: () => rootRouteImport, } as any) -const UsersArktypeIndexRoute = UsersArktypeIndexRouteImport.update({ - id: '/users/arktype/', - path: '/users/arktype/', +const UsersZodIndexRoute = UsersZodIndexRouteImport.update({ + id: '/users/zod/', + path: '/users/zod/', getParentRoute: () => rootRouteImport, } as any) @@ -78,11 +78,11 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/users/zod/': { - id: '/users/zod/' - path: '/users/zod' - fullPath: '/users/zod/' - preLoaderRoute: typeof UsersZodIndexRouteImport + '/users/arktype/': { + id: '/users/arktype/' + path: '/users/arktype' + fullPath: '/users/arktype/' + preLoaderRoute: typeof UsersArktypeIndexRouteImport parentRoute: typeof rootRouteImport } '/users/valibot/': { @@ -92,11 +92,11 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof UsersValibotIndexRouteImport parentRoute: typeof rootRouteImport } - '/users/arktype/': { - id: '/users/arktype/' - path: '/users/arktype' - fullPath: '/users/arktype/' - preLoaderRoute: typeof UsersArktypeIndexRouteImport + '/users/zod/': { + id: '/users/zod/' + path: '/users/zod' + fullPath: '/users/zod/' + preLoaderRoute: typeof UsersZodIndexRouteImport parentRoute: typeof rootRouteImport } } diff --git a/examples/solid/start-basic-auth/src/routeTree.gen.ts b/examples/solid/start-basic-auth/src/routeTree.gen.ts index bad0f1b074..07924d102a 100644 --- a/examples/solid/start-basic-auth/src/routeTree.gen.ts +++ b/examples/solid/start-basic-auth/src/routeTree.gen.ts @@ -9,23 +9,22 @@ // 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 SignupRouteImport } from './routes/signup' -import { Route as LogoutRouteImport } from './routes/logout' -import { Route as LoginRouteImport } from './routes/login' -import { Route as AuthedRouteImport } from './routes/_authed' import { Route as IndexRouteImport } from './routes/index' +import { Route as AuthedRouteImport } from './routes/_authed' +import { Route as LoginRouteImport } from './routes/login' +import { Route as LogoutRouteImport } from './routes/logout' +import { Route as SignupRouteImport } from './routes/signup' import { Route as AuthedPostsRouteRouteImport } from './routes/_authed/posts.route' import { Route as AuthedPostsIndexRouteImport } from './routes/_authed/posts.index' import { Route as AuthedPostsPostIdRouteImport } from './routes/_authed/posts.$postId' -const SignupRoute = SignupRouteImport.update({ - id: '/signup', - path: '/signup', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const LogoutRoute = LogoutRouteImport.update({ - id: '/logout', - path: '/logout', +const AuthedRoute = AuthedRouteImport.update({ + id: '/_authed', getParentRoute: () => rootRouteImport, } as any) const LoginRoute = LoginRouteImport.update({ @@ -33,13 +32,14 @@ const LoginRoute = LoginRouteImport.update({ path: '/login', getParentRoute: () => rootRouteImport, } as any) -const AuthedRoute = AuthedRouteImport.update({ - id: '/_authed', +const LogoutRoute = LogoutRouteImport.update({ + id: '/logout', + path: '/logout', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const SignupRoute = SignupRouteImport.update({ + id: '/signup', + path: '/signup', getParentRoute: () => rootRouteImport, } as any) const AuthedPostsRouteRoute = AuthedPostsRouteRouteImport.update({ @@ -120,18 +120,18 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/signup': { - id: '/signup' - path: '/signup' - fullPath: '/signup' - preLoaderRoute: typeof SignupRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/logout': { - id: '/logout' - path: '/logout' - fullPath: '/logout' - preLoaderRoute: typeof LogoutRouteImport + '/_authed': { + id: '/_authed' + path: '' + fullPath: '/' + preLoaderRoute: typeof AuthedRouteImport parentRoute: typeof rootRouteImport } '/login': { @@ -141,18 +141,18 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof LoginRouteImport parentRoute: typeof rootRouteImport } - '/_authed': { - id: '/_authed' - path: '' - fullPath: '/' - preLoaderRoute: typeof AuthedRouteImport + '/logout': { + id: '/logout' + path: '/logout' + fullPath: '/logout' + preLoaderRoute: typeof LogoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/signup': { + id: '/signup' + path: '/signup' + fullPath: '/signup' + preLoaderRoute: typeof SignupRouteImport parentRoute: typeof rootRouteImport } '/_authed/posts': { diff --git a/examples/solid/start-basic-authjs/src/routeTree.gen.ts b/examples/solid/start-basic-authjs/src/routeTree.gen.ts index e772ba9c3e..7cc6d28cf7 100644 --- a/examples/solid/start-basic-authjs/src/routeTree.gen.ts +++ b/examples/solid/start-basic-authjs/src/routeTree.gen.ts @@ -9,14 +9,14 @@ // 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 ProtectedRouteImport } from './routes/protected' -import { Route as LoginRouteImport } from './routes/login' import { Route as IndexRouteImport } from './routes/index' +import { Route as LoginRouteImport } from './routes/login' +import { Route as ProtectedRouteImport } from './routes/protected' import { Route as ApiAuthSplatRouteImport } from './routes/api/auth/$' -const ProtectedRoute = ProtectedRouteImport.update({ - id: '/protected', - path: '/protected', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const LoginRoute = LoginRouteImport.update({ @@ -24,9 +24,9 @@ const LoginRoute = LoginRouteImport.update({ path: '/login', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const ProtectedRoute = ProtectedRouteImport.update({ + id: '/protected', + path: '/protected', getParentRoute: () => rootRouteImport, } as any) const ApiAuthSplatRoute = ApiAuthSplatRouteImport.update({ @@ -71,11 +71,11 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/protected': { - id: '/protected' - path: '/protected' - fullPath: '/protected' - preLoaderRoute: typeof ProtectedRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/login': { @@ -85,11 +85,11 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof LoginRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/protected': { + id: '/protected' + path: '/protected' + fullPath: '/protected' + preLoaderRoute: typeof ProtectedRouteImport parentRoute: typeof rootRouteImport } '/api/auth/$': { diff --git a/examples/solid/start-basic-cloudflare/src/routeTree.gen.ts b/examples/solid/start-basic-cloudflare/src/routeTree.gen.ts index c3f6976d03..7cba7e8958 100644 --- a/examples/solid/start-basic-cloudflare/src/routeTree.gen.ts +++ b/examples/solid/start-basic-cloudflare/src/routeTree.gen.ts @@ -9,37 +9,36 @@ // 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 UsersRouteImport } from './routes/users' -import { Route as RedirectRouteImport } from './routes/redirect' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as DeferredRouteImport } from './routes/deferred' -import { Route as CustomScriptDotjsRouteImport } from './routes/customScript[.]js' -import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as IndexRouteImport } from './routes/index' -import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' +import { Route as CustomScriptDotjsRouteImport } from './routes/customScript[.]js' +import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as RedirectRouteImport } from './routes/redirect' +import { Route as UsersRouteImport } from './routes/users' +import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' +import { Route as ApiUsersRouteImport } from './routes/api/users' import { Route as PostsIndexRouteImport } from './routes/posts.index' -import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as ApiUsersRouteImport } from './routes/api/users' -import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' -import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -import { Route as ApiUsersUserIdRouteImport } from './routes/api/users.$userId' -import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' +import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +import { Route as ApiUsersUserIdRouteImport } from './routes/api/users.$userId' +import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -const UsersRoute = UsersRouteImport.update({ - id: '/users', - path: '/users', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const RedirectRoute = RedirectRouteImport.update({ - id: '/redirect', - path: '/redirect', +const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ + id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, } as any) -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const CustomScriptDotjsRoute = CustomScriptDotjsRouteImport.update({ + id: '/customScript.js', + path: '/customScript.js', getParentRoute: () => rootRouteImport, } as any) const DeferredRoute = DeferredRouteImport.update({ @@ -47,72 +46,73 @@ const DeferredRoute = DeferredRouteImport.update({ path: '/deferred', getParentRoute: () => rootRouteImport, } as any) -const CustomScriptDotjsRoute = CustomScriptDotjsRouteImport.update({ - id: '/customScript.js', - path: '/customScript.js', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ - id: '/_pathlessLayout', +const RedirectRoute = RedirectRouteImport.update({ + id: '/redirect', + path: '/redirect', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const UsersRoute = UsersRouteImport.update({ + id: '/users', + path: '/users', getParentRoute: () => rootRouteImport, } as any) -const UsersIndexRoute = UsersIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => UsersRoute, +const PathlessLayoutNestedLayoutRoute = + PathlessLayoutNestedLayoutRouteImport.update({ + id: '/_nested-layout', + getParentRoute: () => PathlessLayoutRoute, + } as any) +const ApiUsersRoute = ApiUsersRouteImport.update({ + id: '/api/users', + path: '/api/users', + getParentRoute: () => rootRouteImport, } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRoute, } as any) -const UsersUserIdRoute = UsersUserIdRouteImport.update({ - id: '/$userId', - path: '/$userId', - getParentRoute: () => UsersRoute, -} as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRoute, } as any) -const ApiUsersRoute = ApiUsersRouteImport.update({ - id: '/api/users', - path: '/api/users', - getParentRoute: () => rootRouteImport, -} as any) -const PathlessLayoutNestedLayoutRoute = - PathlessLayoutNestedLayoutRouteImport.update({ - id: '/_nested-layout', - getParentRoute: () => PathlessLayoutRoute, - } as any) -const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ - id: '/posts_/$postId/deep', - path: '/posts/$postId/deep', - getParentRoute: () => rootRouteImport, +const UsersIndexRoute = UsersIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => UsersRoute, } as any) -const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ +const UsersUserIdRoute = UsersUserIdRouteImport.update({ id: '/$userId', path: '/$userId', - getParentRoute: () => ApiUsersRoute, + getParentRoute: () => UsersRoute, } as any) -const PathlessLayoutNestedLayoutRouteBRoute = - PathlessLayoutNestedLayoutRouteBRouteImport.update({ - id: '/route-b', - path: '/route-b', - getParentRoute: () => PathlessLayoutNestedLayoutRoute, - } as any) const PathlessLayoutNestedLayoutRouteARoute = PathlessLayoutNestedLayoutRouteARouteImport.update({ id: '/route-a', path: '/route-a', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) +const PathlessLayoutNestedLayoutRouteBRoute = + PathlessLayoutNestedLayoutRouteBRouteImport.update({ + id: '/route-b', + path: '/route-b', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, + } as any) +const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ + id: '/$userId', + path: '/$userId', + getParentRoute: () => ApiUsersRoute, +} as any) +const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ + id: '/posts_/$postId/deep', + path: '/posts/$postId/deep', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -234,25 +234,25 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/users': { - id: '/users' - path: '/users' - fullPath: '/users' - preLoaderRoute: typeof UsersRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/redirect': { - id: '/redirect' - path: '/redirect' - fullPath: '/redirect' - preLoaderRoute: typeof RedirectRouteImport + '/_pathlessLayout': { + id: '/_pathlessLayout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutRouteImport parentRoute: typeof rootRouteImport } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/customScript.js': { + id: '/customScript.js' + path: '/customScript.js' + fullPath: '/customScript.js' + preLoaderRoute: typeof CustomScriptDotjsRouteImport parentRoute: typeof rootRouteImport } '/deferred': { @@ -262,33 +262,40 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof DeferredRouteImport parentRoute: typeof rootRouteImport } - '/customScript.js': { - id: '/customScript.js' - path: '/customScript.js' - fullPath: '/customScript.js' - preLoaderRoute: typeof CustomScriptDotjsRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/_pathlessLayout': { - id: '/_pathlessLayout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutRouteImport + '/redirect': { + id: '/redirect' + path: '/redirect' + fullPath: '/redirect' + preLoaderRoute: typeof RedirectRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/users': { + id: '/users' + path: '/users' + fullPath: '/users' + preLoaderRoute: typeof UsersRouteImport parentRoute: typeof rootRouteImport } - '/users/': { - id: '/users/' - path: '/' - fullPath: '/users/' - preLoaderRoute: typeof UsersIndexRouteImport - parentRoute: typeof UsersRoute + '/_pathlessLayout/_nested-layout': { + id: '/_pathlessLayout/_nested-layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport + parentRoute: typeof PathlessLayoutRoute + } + '/api/users': { + id: '/api/users' + path: '/api/users' + fullPath: '/api/users' + preLoaderRoute: typeof ApiUsersRouteImport + parentRoute: typeof rootRouteImport } '/posts/': { id: '/posts/' @@ -297,13 +304,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRoute } - '/users/$userId': { - id: '/users/$userId' - path: '/$userId' - fullPath: '/users/$userId' - preLoaderRoute: typeof UsersUserIdRouteImport - parentRoute: typeof UsersRoute - } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' @@ -311,33 +311,26 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/api/users': { - id: '/api/users' - path: '/api/users' - fullPath: '/api/users' - preLoaderRoute: typeof ApiUsersRouteImport - parentRoute: typeof rootRouteImport - } - '/_pathlessLayout/_nested-layout': { - id: '/_pathlessLayout/_nested-layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport - parentRoute: typeof PathlessLayoutRoute - } - '/posts_/$postId/deep': { - id: '/posts_/$postId/deep' - path: '/posts/$postId/deep' - fullPath: '/posts/$postId/deep' - preLoaderRoute: typeof PostsPostIdDeepRouteImport - parentRoute: typeof rootRouteImport + '/users/': { + id: '/users/' + path: '/' + fullPath: '/users/' + preLoaderRoute: typeof UsersIndexRouteImport + parentRoute: typeof UsersRoute } - '/api/users/$userId': { - id: '/api/users/$userId' + '/users/$userId': { + id: '/users/$userId' path: '/$userId' - fullPath: '/api/users/$userId' - preLoaderRoute: typeof ApiUsersUserIdRouteImport - parentRoute: typeof ApiUsersRoute + fullPath: '/users/$userId' + preLoaderRoute: typeof UsersUserIdRouteImport + parentRoute: typeof UsersRoute + } + '/_pathlessLayout/_nested-layout/route-a': { + id: '/_pathlessLayout/_nested-layout/route-a' + path: '/route-a' + fullPath: '/route-a' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport + parentRoute: typeof PathlessLayoutNestedLayoutRoute } '/_pathlessLayout/_nested-layout/route-b': { id: '/_pathlessLayout/_nested-layout/route-b' @@ -346,12 +339,19 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } - '/_pathlessLayout/_nested-layout/route-a': { - id: '/_pathlessLayout/_nested-layout/route-a' - path: '/route-a' - fullPath: '/route-a' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport - parentRoute: typeof PathlessLayoutNestedLayoutRoute + '/api/users/$userId': { + id: '/api/users/$userId' + path: '/$userId' + fullPath: '/api/users/$userId' + preLoaderRoute: typeof ApiUsersUserIdRouteImport + parentRoute: typeof ApiUsersRoute + } + '/posts_/$postId/deep': { + id: '/posts_/$postId/deep' + path: '/posts/$postId/deep' + fullPath: '/posts/$postId/deep' + preLoaderRoute: typeof PostsPostIdDeepRouteImport + parentRoute: typeof rootRouteImport } } } diff --git a/examples/solid/start-basic-netlify/src/routeTree.gen.ts b/examples/solid/start-basic-netlify/src/routeTree.gen.ts index c3f6976d03..7cba7e8958 100644 --- a/examples/solid/start-basic-netlify/src/routeTree.gen.ts +++ b/examples/solid/start-basic-netlify/src/routeTree.gen.ts @@ -9,37 +9,36 @@ // 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 UsersRouteImport } from './routes/users' -import { Route as RedirectRouteImport } from './routes/redirect' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as DeferredRouteImport } from './routes/deferred' -import { Route as CustomScriptDotjsRouteImport } from './routes/customScript[.]js' -import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as IndexRouteImport } from './routes/index' -import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' +import { Route as CustomScriptDotjsRouteImport } from './routes/customScript[.]js' +import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as RedirectRouteImport } from './routes/redirect' +import { Route as UsersRouteImport } from './routes/users' +import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' +import { Route as ApiUsersRouteImport } from './routes/api/users' import { Route as PostsIndexRouteImport } from './routes/posts.index' -import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as ApiUsersRouteImport } from './routes/api/users' -import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' -import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -import { Route as ApiUsersUserIdRouteImport } from './routes/api/users.$userId' -import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' +import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +import { Route as ApiUsersUserIdRouteImport } from './routes/api/users.$userId' +import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -const UsersRoute = UsersRouteImport.update({ - id: '/users', - path: '/users', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const RedirectRoute = RedirectRouteImport.update({ - id: '/redirect', - path: '/redirect', +const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ + id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, } as any) -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const CustomScriptDotjsRoute = CustomScriptDotjsRouteImport.update({ + id: '/customScript.js', + path: '/customScript.js', getParentRoute: () => rootRouteImport, } as any) const DeferredRoute = DeferredRouteImport.update({ @@ -47,72 +46,73 @@ const DeferredRoute = DeferredRouteImport.update({ path: '/deferred', getParentRoute: () => rootRouteImport, } as any) -const CustomScriptDotjsRoute = CustomScriptDotjsRouteImport.update({ - id: '/customScript.js', - path: '/customScript.js', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ - id: '/_pathlessLayout', +const RedirectRoute = RedirectRouteImport.update({ + id: '/redirect', + path: '/redirect', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const UsersRoute = UsersRouteImport.update({ + id: '/users', + path: '/users', getParentRoute: () => rootRouteImport, } as any) -const UsersIndexRoute = UsersIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => UsersRoute, +const PathlessLayoutNestedLayoutRoute = + PathlessLayoutNestedLayoutRouteImport.update({ + id: '/_nested-layout', + getParentRoute: () => PathlessLayoutRoute, + } as any) +const ApiUsersRoute = ApiUsersRouteImport.update({ + id: '/api/users', + path: '/api/users', + getParentRoute: () => rootRouteImport, } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRoute, } as any) -const UsersUserIdRoute = UsersUserIdRouteImport.update({ - id: '/$userId', - path: '/$userId', - getParentRoute: () => UsersRoute, -} as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRoute, } as any) -const ApiUsersRoute = ApiUsersRouteImport.update({ - id: '/api/users', - path: '/api/users', - getParentRoute: () => rootRouteImport, -} as any) -const PathlessLayoutNestedLayoutRoute = - PathlessLayoutNestedLayoutRouteImport.update({ - id: '/_nested-layout', - getParentRoute: () => PathlessLayoutRoute, - } as any) -const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ - id: '/posts_/$postId/deep', - path: '/posts/$postId/deep', - getParentRoute: () => rootRouteImport, +const UsersIndexRoute = UsersIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => UsersRoute, } as any) -const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ +const UsersUserIdRoute = UsersUserIdRouteImport.update({ id: '/$userId', path: '/$userId', - getParentRoute: () => ApiUsersRoute, + getParentRoute: () => UsersRoute, } as any) -const PathlessLayoutNestedLayoutRouteBRoute = - PathlessLayoutNestedLayoutRouteBRouteImport.update({ - id: '/route-b', - path: '/route-b', - getParentRoute: () => PathlessLayoutNestedLayoutRoute, - } as any) const PathlessLayoutNestedLayoutRouteARoute = PathlessLayoutNestedLayoutRouteARouteImport.update({ id: '/route-a', path: '/route-a', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) +const PathlessLayoutNestedLayoutRouteBRoute = + PathlessLayoutNestedLayoutRouteBRouteImport.update({ + id: '/route-b', + path: '/route-b', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, + } as any) +const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ + id: '/$userId', + path: '/$userId', + getParentRoute: () => ApiUsersRoute, +} as any) +const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ + id: '/posts_/$postId/deep', + path: '/posts/$postId/deep', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -234,25 +234,25 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/users': { - id: '/users' - path: '/users' - fullPath: '/users' - preLoaderRoute: typeof UsersRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/redirect': { - id: '/redirect' - path: '/redirect' - fullPath: '/redirect' - preLoaderRoute: typeof RedirectRouteImport + '/_pathlessLayout': { + id: '/_pathlessLayout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutRouteImport parentRoute: typeof rootRouteImport } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/customScript.js': { + id: '/customScript.js' + path: '/customScript.js' + fullPath: '/customScript.js' + preLoaderRoute: typeof CustomScriptDotjsRouteImport parentRoute: typeof rootRouteImport } '/deferred': { @@ -262,33 +262,40 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof DeferredRouteImport parentRoute: typeof rootRouteImport } - '/customScript.js': { - id: '/customScript.js' - path: '/customScript.js' - fullPath: '/customScript.js' - preLoaderRoute: typeof CustomScriptDotjsRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/_pathlessLayout': { - id: '/_pathlessLayout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutRouteImport + '/redirect': { + id: '/redirect' + path: '/redirect' + fullPath: '/redirect' + preLoaderRoute: typeof RedirectRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/users': { + id: '/users' + path: '/users' + fullPath: '/users' + preLoaderRoute: typeof UsersRouteImport parentRoute: typeof rootRouteImport } - '/users/': { - id: '/users/' - path: '/' - fullPath: '/users/' - preLoaderRoute: typeof UsersIndexRouteImport - parentRoute: typeof UsersRoute + '/_pathlessLayout/_nested-layout': { + id: '/_pathlessLayout/_nested-layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport + parentRoute: typeof PathlessLayoutRoute + } + '/api/users': { + id: '/api/users' + path: '/api/users' + fullPath: '/api/users' + preLoaderRoute: typeof ApiUsersRouteImport + parentRoute: typeof rootRouteImport } '/posts/': { id: '/posts/' @@ -297,13 +304,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRoute } - '/users/$userId': { - id: '/users/$userId' - path: '/$userId' - fullPath: '/users/$userId' - preLoaderRoute: typeof UsersUserIdRouteImport - parentRoute: typeof UsersRoute - } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' @@ -311,33 +311,26 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/api/users': { - id: '/api/users' - path: '/api/users' - fullPath: '/api/users' - preLoaderRoute: typeof ApiUsersRouteImport - parentRoute: typeof rootRouteImport - } - '/_pathlessLayout/_nested-layout': { - id: '/_pathlessLayout/_nested-layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport - parentRoute: typeof PathlessLayoutRoute - } - '/posts_/$postId/deep': { - id: '/posts_/$postId/deep' - path: '/posts/$postId/deep' - fullPath: '/posts/$postId/deep' - preLoaderRoute: typeof PostsPostIdDeepRouteImport - parentRoute: typeof rootRouteImport + '/users/': { + id: '/users/' + path: '/' + fullPath: '/users/' + preLoaderRoute: typeof UsersIndexRouteImport + parentRoute: typeof UsersRoute } - '/api/users/$userId': { - id: '/api/users/$userId' + '/users/$userId': { + id: '/users/$userId' path: '/$userId' - fullPath: '/api/users/$userId' - preLoaderRoute: typeof ApiUsersUserIdRouteImport - parentRoute: typeof ApiUsersRoute + fullPath: '/users/$userId' + preLoaderRoute: typeof UsersUserIdRouteImport + parentRoute: typeof UsersRoute + } + '/_pathlessLayout/_nested-layout/route-a': { + id: '/_pathlessLayout/_nested-layout/route-a' + path: '/route-a' + fullPath: '/route-a' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport + parentRoute: typeof PathlessLayoutNestedLayoutRoute } '/_pathlessLayout/_nested-layout/route-b': { id: '/_pathlessLayout/_nested-layout/route-b' @@ -346,12 +339,19 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } - '/_pathlessLayout/_nested-layout/route-a': { - id: '/_pathlessLayout/_nested-layout/route-a' - path: '/route-a' - fullPath: '/route-a' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport - parentRoute: typeof PathlessLayoutNestedLayoutRoute + '/api/users/$userId': { + id: '/api/users/$userId' + path: '/$userId' + fullPath: '/api/users/$userId' + preLoaderRoute: typeof ApiUsersUserIdRouteImport + parentRoute: typeof ApiUsersRoute + } + '/posts_/$postId/deep': { + id: '/posts_/$postId/deep' + path: '/posts/$postId/deep' + fullPath: '/posts/$postId/deep' + preLoaderRoute: typeof PostsPostIdDeepRouteImport + parentRoute: typeof rootRouteImport } } } diff --git a/examples/solid/start-basic-nitro/src/routeTree.gen.ts b/examples/solid/start-basic-nitro/src/routeTree.gen.ts index c3f6976d03..7cba7e8958 100644 --- a/examples/solid/start-basic-nitro/src/routeTree.gen.ts +++ b/examples/solid/start-basic-nitro/src/routeTree.gen.ts @@ -9,37 +9,36 @@ // 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 UsersRouteImport } from './routes/users' -import { Route as RedirectRouteImport } from './routes/redirect' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as DeferredRouteImport } from './routes/deferred' -import { Route as CustomScriptDotjsRouteImport } from './routes/customScript[.]js' -import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as IndexRouteImport } from './routes/index' -import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' +import { Route as CustomScriptDotjsRouteImport } from './routes/customScript[.]js' +import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as RedirectRouteImport } from './routes/redirect' +import { Route as UsersRouteImport } from './routes/users' +import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' +import { Route as ApiUsersRouteImport } from './routes/api/users' import { Route as PostsIndexRouteImport } from './routes/posts.index' -import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as ApiUsersRouteImport } from './routes/api/users' -import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' -import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -import { Route as ApiUsersUserIdRouteImport } from './routes/api/users.$userId' -import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' +import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +import { Route as ApiUsersUserIdRouteImport } from './routes/api/users.$userId' +import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -const UsersRoute = UsersRouteImport.update({ - id: '/users', - path: '/users', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const RedirectRoute = RedirectRouteImport.update({ - id: '/redirect', - path: '/redirect', +const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ + id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, } as any) -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const CustomScriptDotjsRoute = CustomScriptDotjsRouteImport.update({ + id: '/customScript.js', + path: '/customScript.js', getParentRoute: () => rootRouteImport, } as any) const DeferredRoute = DeferredRouteImport.update({ @@ -47,72 +46,73 @@ const DeferredRoute = DeferredRouteImport.update({ path: '/deferred', getParentRoute: () => rootRouteImport, } as any) -const CustomScriptDotjsRoute = CustomScriptDotjsRouteImport.update({ - id: '/customScript.js', - path: '/customScript.js', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ - id: '/_pathlessLayout', +const RedirectRoute = RedirectRouteImport.update({ + id: '/redirect', + path: '/redirect', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const UsersRoute = UsersRouteImport.update({ + id: '/users', + path: '/users', getParentRoute: () => rootRouteImport, } as any) -const UsersIndexRoute = UsersIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => UsersRoute, +const PathlessLayoutNestedLayoutRoute = + PathlessLayoutNestedLayoutRouteImport.update({ + id: '/_nested-layout', + getParentRoute: () => PathlessLayoutRoute, + } as any) +const ApiUsersRoute = ApiUsersRouteImport.update({ + id: '/api/users', + path: '/api/users', + getParentRoute: () => rootRouteImport, } as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRoute, } as any) -const UsersUserIdRoute = UsersUserIdRouteImport.update({ - id: '/$userId', - path: '/$userId', - getParentRoute: () => UsersRoute, -} as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRoute, } as any) -const ApiUsersRoute = ApiUsersRouteImport.update({ - id: '/api/users', - path: '/api/users', - getParentRoute: () => rootRouteImport, -} as any) -const PathlessLayoutNestedLayoutRoute = - PathlessLayoutNestedLayoutRouteImport.update({ - id: '/_nested-layout', - getParentRoute: () => PathlessLayoutRoute, - } as any) -const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ - id: '/posts_/$postId/deep', - path: '/posts/$postId/deep', - getParentRoute: () => rootRouteImport, +const UsersIndexRoute = UsersIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => UsersRoute, } as any) -const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ +const UsersUserIdRoute = UsersUserIdRouteImport.update({ id: '/$userId', path: '/$userId', - getParentRoute: () => ApiUsersRoute, + getParentRoute: () => UsersRoute, } as any) -const PathlessLayoutNestedLayoutRouteBRoute = - PathlessLayoutNestedLayoutRouteBRouteImport.update({ - id: '/route-b', - path: '/route-b', - getParentRoute: () => PathlessLayoutNestedLayoutRoute, - } as any) const PathlessLayoutNestedLayoutRouteARoute = PathlessLayoutNestedLayoutRouteARouteImport.update({ id: '/route-a', path: '/route-a', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) +const PathlessLayoutNestedLayoutRouteBRoute = + PathlessLayoutNestedLayoutRouteBRouteImport.update({ + id: '/route-b', + path: '/route-b', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, + } as any) +const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ + id: '/$userId', + path: '/$userId', + getParentRoute: () => ApiUsersRoute, +} as any) +const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ + id: '/posts_/$postId/deep', + path: '/posts/$postId/deep', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -234,25 +234,25 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/users': { - id: '/users' - path: '/users' - fullPath: '/users' - preLoaderRoute: typeof UsersRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/redirect': { - id: '/redirect' - path: '/redirect' - fullPath: '/redirect' - preLoaderRoute: typeof RedirectRouteImport + '/_pathlessLayout': { + id: '/_pathlessLayout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutRouteImport parentRoute: typeof rootRouteImport } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/customScript.js': { + id: '/customScript.js' + path: '/customScript.js' + fullPath: '/customScript.js' + preLoaderRoute: typeof CustomScriptDotjsRouteImport parentRoute: typeof rootRouteImport } '/deferred': { @@ -262,33 +262,40 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof DeferredRouteImport parentRoute: typeof rootRouteImport } - '/customScript.js': { - id: '/customScript.js' - path: '/customScript.js' - fullPath: '/customScript.js' - preLoaderRoute: typeof CustomScriptDotjsRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/_pathlessLayout': { - id: '/_pathlessLayout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutRouteImport + '/redirect': { + id: '/redirect' + path: '/redirect' + fullPath: '/redirect' + preLoaderRoute: typeof RedirectRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/users': { + id: '/users' + path: '/users' + fullPath: '/users' + preLoaderRoute: typeof UsersRouteImport parentRoute: typeof rootRouteImport } - '/users/': { - id: '/users/' - path: '/' - fullPath: '/users/' - preLoaderRoute: typeof UsersIndexRouteImport - parentRoute: typeof UsersRoute + '/_pathlessLayout/_nested-layout': { + id: '/_pathlessLayout/_nested-layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport + parentRoute: typeof PathlessLayoutRoute + } + '/api/users': { + id: '/api/users' + path: '/api/users' + fullPath: '/api/users' + preLoaderRoute: typeof ApiUsersRouteImport + parentRoute: typeof rootRouteImport } '/posts/': { id: '/posts/' @@ -297,13 +304,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRoute } - '/users/$userId': { - id: '/users/$userId' - path: '/$userId' - fullPath: '/users/$userId' - preLoaderRoute: typeof UsersUserIdRouteImport - parentRoute: typeof UsersRoute - } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' @@ -311,33 +311,26 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/api/users': { - id: '/api/users' - path: '/api/users' - fullPath: '/api/users' - preLoaderRoute: typeof ApiUsersRouteImport - parentRoute: typeof rootRouteImport - } - '/_pathlessLayout/_nested-layout': { - id: '/_pathlessLayout/_nested-layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport - parentRoute: typeof PathlessLayoutRoute - } - '/posts_/$postId/deep': { - id: '/posts_/$postId/deep' - path: '/posts/$postId/deep' - fullPath: '/posts/$postId/deep' - preLoaderRoute: typeof PostsPostIdDeepRouteImport - parentRoute: typeof rootRouteImport + '/users/': { + id: '/users/' + path: '/' + fullPath: '/users/' + preLoaderRoute: typeof UsersIndexRouteImport + parentRoute: typeof UsersRoute } - '/api/users/$userId': { - id: '/api/users/$userId' + '/users/$userId': { + id: '/users/$userId' path: '/$userId' - fullPath: '/api/users/$userId' - preLoaderRoute: typeof ApiUsersUserIdRouteImport - parentRoute: typeof ApiUsersRoute + fullPath: '/users/$userId' + preLoaderRoute: typeof UsersUserIdRouteImport + parentRoute: typeof UsersRoute + } + '/_pathlessLayout/_nested-layout/route-a': { + id: '/_pathlessLayout/_nested-layout/route-a' + path: '/route-a' + fullPath: '/route-a' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport + parentRoute: typeof PathlessLayoutNestedLayoutRoute } '/_pathlessLayout/_nested-layout/route-b': { id: '/_pathlessLayout/_nested-layout/route-b' @@ -346,12 +339,19 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } - '/_pathlessLayout/_nested-layout/route-a': { - id: '/_pathlessLayout/_nested-layout/route-a' - path: '/route-a' - fullPath: '/route-a' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport - parentRoute: typeof PathlessLayoutNestedLayoutRoute + '/api/users/$userId': { + id: '/api/users/$userId' + path: '/$userId' + fullPath: '/api/users/$userId' + preLoaderRoute: typeof ApiUsersUserIdRouteImport + parentRoute: typeof ApiUsersRoute + } + '/posts_/$postId/deep': { + id: '/posts_/$postId/deep' + path: '/posts/$postId/deep' + fullPath: '/posts/$postId/deep' + preLoaderRoute: typeof PostsPostIdDeepRouteImport + parentRoute: typeof rootRouteImport } } } diff --git a/examples/solid/start-basic-solid-query/src/routeTree.gen.ts b/examples/solid/start-basic-solid-query/src/routeTree.gen.ts index 226b599b8c..4a1747ae36 100644 --- a/examples/solid/start-basic-solid-query/src/routeTree.gen.ts +++ b/examples/solid/start-basic-solid-query/src/routeTree.gen.ts @@ -9,26 +9,30 @@ // 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 RedirectRouteImport } from './routes/redirect' -import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as IndexRouteImport } from './routes/index' import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' -import { Route as UsersRouteRouteImport } from './routes/users.route' +import { Route as DeferredRouteImport } from './routes/deferred' import { Route as PostsRouteRouteImport } from './routes/posts.route' -import { Route as IndexRouteImport } from './routes/index' -import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as RedirectRouteImport } from './routes/redirect' +import { Route as UsersRouteRouteImport } from './routes/users.route' +import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' +import { Route as ApiUsersRouteImport } from './routes/api/users' import { Route as PostsIndexRouteImport } from './routes/posts.index' -import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as ApiUsersRouteImport } from './routes/api/users' -import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' -import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -import { Route as ApiUsersIdRouteImport } from './routes/api/users.$id' -import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' +import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +import { Route as ApiUsersIdRouteImport } from './routes/api/users.$id' +import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -const RedirectRoute = RedirectRouteImport.update({ - id: '/redirect', - path: '/redirect', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) +const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ + id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, } as any) const DeferredRoute = DeferredRouteImport.update({ @@ -36,8 +40,14 @@ const DeferredRoute = DeferredRouteImport.update({ path: '/deferred', getParentRoute: () => rootRouteImport, } as any) -const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ - id: '/_pathlessLayout', +const PostsRouteRoute = PostsRouteRouteImport.update({ + id: '/posts', + path: '/posts', + getParentRoute: () => rootRouteImport, +} as any) +const RedirectRoute = RedirectRouteImport.update({ + id: '/redirect', + path: '/redirect', getParentRoute: () => rootRouteImport, } as any) const UsersRouteRoute = UsersRouteRouteImport.update({ @@ -45,68 +55,58 @@ const UsersRouteRoute = UsersRouteRouteImport.update({ path: '/users', getParentRoute: () => rootRouteImport, } as any) -const PostsRouteRoute = PostsRouteRouteImport.update({ - id: '/posts', - path: '/posts', +const PathlessLayoutNestedLayoutRoute = + PathlessLayoutNestedLayoutRouteImport.update({ + id: '/_nested-layout', + getParentRoute: () => PathlessLayoutRoute, + } as any) +const ApiUsersRoute = ApiUsersRouteImport.update({ + id: '/api/users', + path: '/api/users', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ +const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => rootRouteImport, + getParentRoute: () => PostsRouteRoute, +} as any) +const PostsPostIdRoute = PostsPostIdRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => PostsRouteRoute, } as any) const UsersIndexRoute = UsersIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => UsersRouteRoute, } as any) -const PostsIndexRoute = PostsIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => PostsRouteRoute, -} as any) const UsersUserIdRoute = UsersUserIdRouteImport.update({ id: '/$userId', path: '/$userId', getParentRoute: () => UsersRouteRoute, } as any) -const PostsPostIdRoute = PostsPostIdRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => PostsRouteRoute, -} as any) -const ApiUsersRoute = ApiUsersRouteImport.update({ - id: '/api/users', - path: '/api/users', - getParentRoute: () => rootRouteImport, -} as any) -const PathlessLayoutNestedLayoutRoute = - PathlessLayoutNestedLayoutRouteImport.update({ - id: '/_nested-layout', - getParentRoute: () => PathlessLayoutRoute, +const PathlessLayoutNestedLayoutRouteARoute = + PathlessLayoutNestedLayoutRouteARouteImport.update({ + id: '/route-a', + path: '/route-a', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) -const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ - id: '/posts_/$postId/deep', - path: '/posts/$postId/deep', - getParentRoute: () => rootRouteImport, -} as any) -const ApiUsersIdRoute = ApiUsersIdRouteImport.update({ - id: '/$id', - path: '/$id', - getParentRoute: () => ApiUsersRoute, -} as any) const PathlessLayoutNestedLayoutRouteBRoute = PathlessLayoutNestedLayoutRouteBRouteImport.update({ id: '/route-b', path: '/route-b', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) -const PathlessLayoutNestedLayoutRouteARoute = - PathlessLayoutNestedLayoutRouteARouteImport.update({ - id: '/route-a', - path: '/route-a', - getParentRoute: () => PathlessLayoutNestedLayoutRoute, - } as any) +const ApiUsersIdRoute = ApiUsersIdRouteImport.update({ + id: '/$id', + path: '/$id', + getParentRoute: () => ApiUsersRoute, +} as any) +const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ + id: '/posts_/$postId/deep', + path: '/posts/$postId/deep', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -221,18 +221,11 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/redirect': { - id: '/redirect' - path: '/redirect' - fullPath: '/redirect' - preLoaderRoute: typeof RedirectRouteImport - parentRoute: typeof rootRouteImport - } - '/deferred': { - id: '/deferred' - path: '/deferred' - fullPath: '/deferred' - preLoaderRoute: typeof DeferredRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/_pathlessLayout': { @@ -242,11 +235,11 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PathlessLayoutRouteImport parentRoute: typeof rootRouteImport } - '/users': { - id: '/users' - path: '/users' - fullPath: '/users' - preLoaderRoute: typeof UsersRouteRouteImport + '/deferred': { + id: '/deferred' + path: '/deferred' + fullPath: '/deferred' + preLoaderRoute: typeof DeferredRouteImport parentRoute: typeof rootRouteImport } '/posts': { @@ -256,19 +249,33 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/redirect': { + id: '/redirect' + path: '/redirect' + fullPath: '/redirect' + preLoaderRoute: typeof RedirectRouteImport parentRoute: typeof rootRouteImport } - '/users/': { - id: '/users/' - path: '/' - fullPath: '/users/' - preLoaderRoute: typeof UsersIndexRouteImport - parentRoute: typeof UsersRouteRoute + '/users': { + id: '/users' + path: '/users' + fullPath: '/users' + preLoaderRoute: typeof UsersRouteRouteImport + parentRoute: typeof rootRouteImport + } + '/_pathlessLayout/_nested-layout': { + id: '/_pathlessLayout/_nested-layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport + parentRoute: typeof PathlessLayoutRoute + } + '/api/users': { + id: '/api/users' + path: '/api/users' + fullPath: '/api/users' + preLoaderRoute: typeof ApiUsersRouteImport + parentRoute: typeof rootRouteImport } '/posts/': { id: '/posts/' @@ -277,13 +284,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRouteRoute } - '/users/$userId': { - id: '/users/$userId' - path: '/$userId' - fullPath: '/users/$userId' - preLoaderRoute: typeof UsersUserIdRouteImport - parentRoute: typeof UsersRouteRoute - } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' @@ -291,33 +291,26 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRouteRoute } - '/api/users': { - id: '/api/users' - path: '/api/users' - fullPath: '/api/users' - preLoaderRoute: typeof ApiUsersRouteImport - parentRoute: typeof rootRouteImport - } - '/_pathlessLayout/_nested-layout': { - id: '/_pathlessLayout/_nested-layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport - parentRoute: typeof PathlessLayoutRoute + '/users/': { + id: '/users/' + path: '/' + fullPath: '/users/' + preLoaderRoute: typeof UsersIndexRouteImport + parentRoute: typeof UsersRouteRoute } - '/posts_/$postId/deep': { - id: '/posts_/$postId/deep' - path: '/posts/$postId/deep' - fullPath: '/posts/$postId/deep' - preLoaderRoute: typeof PostsPostIdDeepRouteImport - parentRoute: typeof rootRouteImport + '/users/$userId': { + id: '/users/$userId' + path: '/$userId' + fullPath: '/users/$userId' + preLoaderRoute: typeof UsersUserIdRouteImport + parentRoute: typeof UsersRouteRoute } - '/api/users/$id': { - id: '/api/users/$id' - path: '/$id' - fullPath: '/api/users/$id' - preLoaderRoute: typeof ApiUsersIdRouteImport - parentRoute: typeof ApiUsersRoute + '/_pathlessLayout/_nested-layout/route-a': { + id: '/_pathlessLayout/_nested-layout/route-a' + path: '/route-a' + fullPath: '/route-a' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport + parentRoute: typeof PathlessLayoutNestedLayoutRoute } '/_pathlessLayout/_nested-layout/route-b': { id: '/_pathlessLayout/_nested-layout/route-b' @@ -326,12 +319,19 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } - '/_pathlessLayout/_nested-layout/route-a': { - id: '/_pathlessLayout/_nested-layout/route-a' - path: '/route-a' - fullPath: '/route-a' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport - parentRoute: typeof PathlessLayoutNestedLayoutRoute + '/api/users/$id': { + id: '/api/users/$id' + path: '/$id' + fullPath: '/api/users/$id' + preLoaderRoute: typeof ApiUsersIdRouteImport + parentRoute: typeof ApiUsersRoute + } + '/posts_/$postId/deep': { + id: '/posts_/$postId/deep' + path: '/posts/$postId/deep' + fullPath: '/posts/$postId/deep' + preLoaderRoute: typeof PostsPostIdDeepRouteImport + parentRoute: typeof rootRouteImport } } } diff --git a/examples/solid/start-basic-static/src/routeTree.gen.ts b/examples/solid/start-basic-static/src/routeTree.gen.ts index 7cad5523aa..a94e0fcf2e 100644 --- a/examples/solid/start-basic-static/src/routeTree.gen.ts +++ b/examples/solid/start-basic-static/src/routeTree.gen.ts @@ -9,29 +9,33 @@ // 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 UsersRouteImport } from './routes/users' -import { Route as RedirectRouteImport } from './routes/redirect' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as DeferredRouteImport } from './routes/deferred' -import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as IndexRouteImport } from './routes/index' -import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' +import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as RedirectRouteImport } from './routes/redirect' +import { Route as UsersRouteImport } from './routes/users' +import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' import { Route as PostsIndexRouteImport } from './routes/posts.index' -import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' -import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' +import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -const UsersRoute = UsersRouteImport.update({ - id: '/users', - path: '/users', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const RedirectRoute = RedirectRouteImport.update({ - id: '/redirect', - path: '/redirect', +const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ + id: '/_pathlessLayout', + getParentRoute: () => rootRouteImport, +} as any) +const DeferredRoute = DeferredRouteImport.update({ + id: '/deferred', + path: '/deferred', getParentRoute: () => rootRouteImport, } as any) const PostsRoute = PostsRouteImport.update({ @@ -39,62 +43,58 @@ const PostsRoute = PostsRouteImport.update({ path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const DeferredRoute = DeferredRouteImport.update({ - id: '/deferred', - path: '/deferred', +const RedirectRoute = RedirectRouteImport.update({ + id: '/redirect', + path: '/redirect', getParentRoute: () => rootRouteImport, } as any) -const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ - id: '/_pathlessLayout', +const UsersRoute = UsersRouteImport.update({ + id: '/users', + path: '/users', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ +const PathlessLayoutNestedLayoutRoute = + PathlessLayoutNestedLayoutRouteImport.update({ + id: '/_nested-layout', + getParentRoute: () => PathlessLayoutRoute, + } as any) +const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', - getParentRoute: () => rootRouteImport, + getParentRoute: () => PostsRoute, +} as any) +const PostsPostIdRoute = PostsPostIdRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => PostsRoute, } as any) const UsersIndexRoute = UsersIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => UsersRoute, } as any) -const PostsIndexRoute = PostsIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => PostsRoute, -} as any) const UsersUserIdRoute = UsersUserIdRouteImport.update({ id: '/$userId', path: '/$userId', getParentRoute: () => UsersRoute, } as any) -const PostsPostIdRoute = PostsPostIdRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => PostsRoute, -} as any) -const PathlessLayoutNestedLayoutRoute = - PathlessLayoutNestedLayoutRouteImport.update({ - id: '/_nested-layout', - getParentRoute: () => PathlessLayoutRoute, +const PathlessLayoutNestedLayoutRouteARoute = + PathlessLayoutNestedLayoutRouteARouteImport.update({ + id: '/route-a', + path: '/route-a', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) -const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ - id: '/posts_/$postId/deep', - path: '/posts/$postId/deep', - getParentRoute: () => rootRouteImport, -} as any) const PathlessLayoutNestedLayoutRouteBRoute = PathlessLayoutNestedLayoutRouteBRouteImport.update({ id: '/route-b', path: '/route-b', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) -const PathlessLayoutNestedLayoutRouteARoute = - PathlessLayoutNestedLayoutRouteARouteImport.update({ - id: '/route-a', - path: '/route-a', - getParentRoute: () => PathlessLayoutNestedLayoutRoute, - } as any) +const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ + id: '/posts_/$postId/deep', + path: '/posts/$postId/deep', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -196,18 +196,25 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/users': { - id: '/users' - path: '/users' - fullPath: '/users' - preLoaderRoute: typeof UsersRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/redirect': { - id: '/redirect' - path: '/redirect' - fullPath: '/redirect' - preLoaderRoute: typeof RedirectRouteImport + '/_pathlessLayout': { + id: '/_pathlessLayout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutRouteImport + parentRoute: typeof rootRouteImport + } + '/deferred': { + id: '/deferred' + path: '/deferred' + fullPath: '/deferred' + preLoaderRoute: typeof DeferredRouteImport parentRoute: typeof rootRouteImport } '/posts': { @@ -217,33 +224,26 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/deferred': { - id: '/deferred' - path: '/deferred' - fullPath: '/deferred' - preLoaderRoute: typeof DeferredRouteImport + '/redirect': { + id: '/redirect' + path: '/redirect' + fullPath: '/redirect' + preLoaderRoute: typeof RedirectRouteImport parentRoute: typeof rootRouteImport } - '/_pathlessLayout': { - id: '/_pathlessLayout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutRouteImport + '/users': { + id: '/users' + path: '/users' + fullPath: '/users' + preLoaderRoute: typeof UsersRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' + '/_pathlessLayout/_nested-layout': { + id: '/_pathlessLayout/_nested-layout' + path: '' fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport - } - '/users/': { - id: '/users/' - path: '/' - fullPath: '/users/' - preLoaderRoute: typeof UsersIndexRouteImport - parentRoute: typeof UsersRoute + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport + parentRoute: typeof PathlessLayoutRoute } '/posts/': { id: '/posts/' @@ -252,13 +252,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRoute } - '/users/$userId': { - id: '/users/$userId' - path: '/$userId' - fullPath: '/users/$userId' - preLoaderRoute: typeof UsersUserIdRouteImport - parentRoute: typeof UsersRoute - } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' @@ -266,19 +259,26 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/_pathlessLayout/_nested-layout': { - id: '/_pathlessLayout/_nested-layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport - parentRoute: typeof PathlessLayoutRoute + '/users/': { + id: '/users/' + path: '/' + fullPath: '/users/' + preLoaderRoute: typeof UsersIndexRouteImport + parentRoute: typeof UsersRoute } - '/posts_/$postId/deep': { - id: '/posts_/$postId/deep' - path: '/posts/$postId/deep' - fullPath: '/posts/$postId/deep' - preLoaderRoute: typeof PostsPostIdDeepRouteImport - parentRoute: typeof rootRouteImport + '/users/$userId': { + id: '/users/$userId' + path: '/$userId' + fullPath: '/users/$userId' + preLoaderRoute: typeof UsersUserIdRouteImport + parentRoute: typeof UsersRoute + } + '/_pathlessLayout/_nested-layout/route-a': { + id: '/_pathlessLayout/_nested-layout/route-a' + path: '/route-a' + fullPath: '/route-a' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport + parentRoute: typeof PathlessLayoutNestedLayoutRoute } '/_pathlessLayout/_nested-layout/route-b': { id: '/_pathlessLayout/_nested-layout/route-b' @@ -287,12 +287,12 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } - '/_pathlessLayout/_nested-layout/route-a': { - id: '/_pathlessLayout/_nested-layout/route-a' - path: '/route-a' - fullPath: '/route-a' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport - parentRoute: typeof PathlessLayoutNestedLayoutRoute + '/posts_/$postId/deep': { + id: '/posts_/$postId/deep' + path: '/posts/$postId/deep' + fullPath: '/posts/$postId/deep' + preLoaderRoute: typeof PostsPostIdDeepRouteImport + parentRoute: typeof rootRouteImport } } } diff --git a/examples/solid/start-basic/src/routeTree.gen.ts b/examples/solid/start-basic/src/routeTree.gen.ts index 722133b41e..8d6c8ffc2b 100644 --- a/examples/solid/start-basic/src/routeTree.gen.ts +++ b/examples/solid/start-basic/src/routeTree.gen.ts @@ -9,31 +9,35 @@ // 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 UsersRouteImport } from './routes/users' -import { Route as RedirectRouteImport } from './routes/redirect' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as DeferredRouteImport } from './routes/deferred' -import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as IndexRouteImport } from './routes/index' -import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' +import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as RedirectRouteImport } from './routes/redirect' +import { Route as UsersRouteImport } from './routes/users' +import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' +import { Route as ApiUsersRouteImport } from './routes/api/users' import { Route as PostsIndexRouteImport } from './routes/posts.index' -import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as ApiUsersRouteImport } from './routes/api/users' -import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' -import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -import { Route as ApiUsersUserIdRouteImport } from './routes/api/users.$userId' -import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' +import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +import { Route as ApiUsersUserIdRouteImport } from './routes/api/users.$userId' +import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' -const UsersRoute = UsersRouteImport.update({ - id: '/users', - path: '/users', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const RedirectRoute = RedirectRouteImport.update({ - id: '/redirect', - path: '/redirect', +const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ + id: '/_pathlessLayout', + getParentRoute: () => rootRouteImport, +} as any) +const DeferredRoute = DeferredRouteImport.update({ + id: '/deferred', + path: '/deferred', getParentRoute: () => rootRouteImport, } as any) const PostsRoute = PostsRouteImport.update({ @@ -41,72 +45,68 @@ const PostsRoute = PostsRouteImport.update({ path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const DeferredRoute = DeferredRouteImport.update({ - id: '/deferred', - path: '/deferred', +const RedirectRoute = RedirectRouteImport.update({ + id: '/redirect', + path: '/redirect', getParentRoute: () => rootRouteImport, } as any) -const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ - id: '/_pathlessLayout', +const UsersRoute = UsersRouteImport.update({ + id: '/users', + path: '/users', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const PathlessLayoutNestedLayoutRoute = + PathlessLayoutNestedLayoutRouteImport.update({ + id: '/_nested-layout', + getParentRoute: () => PathlessLayoutRoute, + } as any) +const ApiUsersRoute = ApiUsersRouteImport.update({ + id: '/api/users', + path: '/api/users', getParentRoute: () => rootRouteImport, } as any) -const UsersIndexRoute = UsersIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => UsersRoute, -} as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => PostsRoute, } as any) -const UsersUserIdRoute = UsersUserIdRouteImport.update({ - id: '/$userId', - path: '/$userId', - getParentRoute: () => UsersRoute, -} as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', getParentRoute: () => PostsRoute, } as any) -const ApiUsersRoute = ApiUsersRouteImport.update({ - id: '/api/users', - path: '/api/users', - getParentRoute: () => rootRouteImport, -} as any) -const PathlessLayoutNestedLayoutRoute = - PathlessLayoutNestedLayoutRouteImport.update({ - id: '/_nested-layout', - getParentRoute: () => PathlessLayoutRoute, - } as any) -const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ - id: '/posts_/$postId/deep', - path: '/posts/$postId/deep', - getParentRoute: () => rootRouteImport, +const UsersIndexRoute = UsersIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => UsersRoute, } as any) -const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ +const UsersUserIdRoute = UsersUserIdRouteImport.update({ id: '/$userId', path: '/$userId', - getParentRoute: () => ApiUsersRoute, + getParentRoute: () => UsersRoute, } as any) -const PathlessLayoutNestedLayoutRouteBRoute = - PathlessLayoutNestedLayoutRouteBRouteImport.update({ - id: '/route-b', - path: '/route-b', - getParentRoute: () => PathlessLayoutNestedLayoutRoute, - } as any) const PathlessLayoutNestedLayoutRouteARoute = PathlessLayoutNestedLayoutRouteARouteImport.update({ id: '/route-a', path: '/route-a', getParentRoute: () => PathlessLayoutNestedLayoutRoute, } as any) +const PathlessLayoutNestedLayoutRouteBRoute = + PathlessLayoutNestedLayoutRouteBRouteImport.update({ + id: '/route-b', + path: '/route-b', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, + } as any) +const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ + id: '/$userId', + path: '/$userId', + getParentRoute: () => ApiUsersRoute, +} as any) +const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ + id: '/posts_/$postId/deep', + path: '/posts/$postId/deep', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -221,18 +221,25 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/users': { - id: '/users' - path: '/users' - fullPath: '/users' - preLoaderRoute: typeof UsersRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/redirect': { - id: '/redirect' - path: '/redirect' - fullPath: '/redirect' - preLoaderRoute: typeof RedirectRouteImport + '/_pathlessLayout': { + id: '/_pathlessLayout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutRouteImport + parentRoute: typeof rootRouteImport + } + '/deferred': { + id: '/deferred' + path: '/deferred' + fullPath: '/deferred' + preLoaderRoute: typeof DeferredRouteImport parentRoute: typeof rootRouteImport } '/posts': { @@ -242,33 +249,33 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/deferred': { - id: '/deferred' - path: '/deferred' - fullPath: '/deferred' - preLoaderRoute: typeof DeferredRouteImport + '/redirect': { + id: '/redirect' + path: '/redirect' + fullPath: '/redirect' + preLoaderRoute: typeof RedirectRouteImport parentRoute: typeof rootRouteImport } - '/_pathlessLayout': { - id: '/_pathlessLayout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutRouteImport + '/users': { + id: '/users' + path: '/users' + fullPath: '/users' + preLoaderRoute: typeof UsersRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' + '/_pathlessLayout/_nested-layout': { + id: '/_pathlessLayout/_nested-layout' + path: '' fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport + parentRoute: typeof PathlessLayoutRoute } - '/users/': { - id: '/users/' - path: '/' - fullPath: '/users/' - preLoaderRoute: typeof UsersIndexRouteImport - parentRoute: typeof UsersRoute + '/api/users': { + id: '/api/users' + path: '/api/users' + fullPath: '/api/users' + preLoaderRoute: typeof ApiUsersRouteImport + parentRoute: typeof rootRouteImport } '/posts/': { id: '/posts/' @@ -277,13 +284,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsIndexRouteImport parentRoute: typeof PostsRoute } - '/users/$userId': { - id: '/users/$userId' - path: '/$userId' - fullPath: '/users/$userId' - preLoaderRoute: typeof UsersUserIdRouteImport - parentRoute: typeof UsersRoute - } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' @@ -291,33 +291,26 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/api/users': { - id: '/api/users' - path: '/api/users' - fullPath: '/api/users' - preLoaderRoute: typeof ApiUsersRouteImport - parentRoute: typeof rootRouteImport - } - '/_pathlessLayout/_nested-layout': { - id: '/_pathlessLayout/_nested-layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport - parentRoute: typeof PathlessLayoutRoute - } - '/posts_/$postId/deep': { - id: '/posts_/$postId/deep' - path: '/posts/$postId/deep' - fullPath: '/posts/$postId/deep' - preLoaderRoute: typeof PostsPostIdDeepRouteImport - parentRoute: typeof rootRouteImport + '/users/': { + id: '/users/' + path: '/' + fullPath: '/users/' + preLoaderRoute: typeof UsersIndexRouteImport + parentRoute: typeof UsersRoute } - '/api/users/$userId': { - id: '/api/users/$userId' + '/users/$userId': { + id: '/users/$userId' path: '/$userId' - fullPath: '/api/users/$userId' - preLoaderRoute: typeof ApiUsersUserIdRouteImport - parentRoute: typeof ApiUsersRoute + fullPath: '/users/$userId' + preLoaderRoute: typeof UsersUserIdRouteImport + parentRoute: typeof UsersRoute + } + '/_pathlessLayout/_nested-layout/route-a': { + id: '/_pathlessLayout/_nested-layout/route-a' + path: '/route-a' + fullPath: '/route-a' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport + parentRoute: typeof PathlessLayoutNestedLayoutRoute } '/_pathlessLayout/_nested-layout/route-b': { id: '/_pathlessLayout/_nested-layout/route-b' @@ -326,12 +319,19 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport parentRoute: typeof PathlessLayoutNestedLayoutRoute } - '/_pathlessLayout/_nested-layout/route-a': { - id: '/_pathlessLayout/_nested-layout/route-a' - path: '/route-a' - fullPath: '/route-a' - preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport - parentRoute: typeof PathlessLayoutNestedLayoutRoute + '/api/users/$userId': { + id: '/api/users/$userId' + path: '/$userId' + fullPath: '/api/users/$userId' + preLoaderRoute: typeof ApiUsersUserIdRouteImport + parentRoute: typeof ApiUsersRoute + } + '/posts_/$postId/deep': { + id: '/posts_/$postId/deep' + path: '/posts/$postId/deep' + fullPath: '/posts/$postId/deep' + preLoaderRoute: typeof PostsPostIdDeepRouteImport + parentRoute: typeof rootRouteImport } } } diff --git a/examples/solid/start-bun/src/routeTree.gen.ts b/examples/solid/start-bun/src/routeTree.gen.ts index 19009b2f5b..353db2cd02 100644 --- a/examples/solid/start-bun/src/routeTree.gen.ts +++ b/examples/solid/start-bun/src/routeTree.gen.ts @@ -11,8 +11,8 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' import { Route as ApiDemoNamesRouteImport } from './routes/api.demo-names' -import { Route as DemoStartServerFuncsRouteImport } from './routes/demo.start.server-funcs' import { Route as DemoStartApiRequestRouteImport } from './routes/demo.start.api-request' +import { Route as DemoStartServerFuncsRouteImport } from './routes/demo.start.server-funcs' const IndexRoute = IndexRouteImport.update({ id: '/', @@ -24,16 +24,16 @@ const ApiDemoNamesRoute = ApiDemoNamesRouteImport.update({ path: '/api/demo-names', getParentRoute: () => rootRouteImport, } as any) -const DemoStartServerFuncsRoute = DemoStartServerFuncsRouteImport.update({ - id: '/demo/start/server-funcs', - path: '/demo/start/server-funcs', - getParentRoute: () => rootRouteImport, -} as any) const DemoStartApiRequestRoute = DemoStartApiRequestRouteImport.update({ id: '/demo/start/api-request', path: '/demo/start/api-request', getParentRoute: () => rootRouteImport, } as any) +const DemoStartServerFuncsRoute = DemoStartServerFuncsRouteImport.update({ + id: '/demo/start/server-funcs', + path: '/demo/start/server-funcs', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -98,13 +98,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof ApiDemoNamesRouteImport parentRoute: typeof rootRouteImport } - '/demo/start/server-funcs': { - id: '/demo/start/server-funcs' - path: '/demo/start/server-funcs' - fullPath: '/demo/start/server-funcs' - preLoaderRoute: typeof DemoStartServerFuncsRouteImport - parentRoute: typeof rootRouteImport - } '/demo/start/api-request': { id: '/demo/start/api-request' path: '/demo/start/api-request' @@ -112,6 +105,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof DemoStartApiRequestRouteImport parentRoute: typeof rootRouteImport } + '/demo/start/server-funcs': { + id: '/demo/start/server-funcs' + path: '/demo/start/server-funcs' + fullPath: '/demo/start/server-funcs' + preLoaderRoute: typeof DemoStartServerFuncsRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/examples/solid/start-convex-better-auth/src/routeTree.gen.ts b/examples/solid/start-convex-better-auth/src/routeTree.gen.ts index 27dc0ca88c..d2307d1277 100644 --- a/examples/solid/start-convex-better-auth/src/routeTree.gen.ts +++ b/examples/solid/start-convex-better-auth/src/routeTree.gen.ts @@ -9,24 +9,24 @@ // 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 AboutRouteImport } from './routes/about' -import { Route as AuthedRouteImport } from './routes/_authed' import { Route as IndexRouteImport } from './routes/index' +import { Route as AuthedRouteImport } from './routes/_authed' +import { Route as AboutRouteImport } from './routes/about' import { Route as AuthedDashboardRouteImport } from './routes/_authed/dashboard' import { Route as ApiAuthSplatRouteImport } from './routes/api/auth/$' -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const AuthedRoute = AuthedRouteImport.update({ id: '/_authed', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', getParentRoute: () => rootRouteImport, } as any) const AuthedDashboardRoute = AuthedDashboardRouteImport.update({ @@ -83,11 +83,11 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/_authed': { @@ -97,11 +97,11 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof AuthedRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport parentRoute: typeof rootRouteImport } '/_authed/dashboard': { diff --git a/examples/solid/start-counter/src/routeTree.gen.ts b/examples/solid/start-counter/src/routeTree.gen.ts index 3cc45c071e..7495244535 100644 --- a/examples/solid/start-counter/src/routeTree.gen.ts +++ b/examples/solid/start-counter/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' +import { Route as AboutRouteImport } from './routes/about' -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/examples/solid/start-i18n-paraglide/src/routeTree.gen.ts b/examples/solid/start-i18n-paraglide/src/routeTree.gen.ts index 3cc45c071e..7495244535 100644 --- a/examples/solid/start-i18n-paraglide/src/routeTree.gen.ts +++ b/examples/solid/start-i18n-paraglide/src/routeTree.gen.ts @@ -9,19 +9,19 @@ // 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 AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' +import { Route as AboutRouteImport } from './routes/about' -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -51,13 +51,6 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -65,6 +58,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } } } diff --git a/examples/solid/start-large/src/routeTree.gen.ts b/examples/solid/start-large/src/routeTree.gen.ts index 3fb99e1aca..1febe90621 100644 --- a/examples/solid/start-large/src/routeTree.gen.ts +++ b/examples/solid/start-large/src/routeTree.gen.ts @@ -9,23 +9,18 @@ // 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 RelativeRouteImport } from './routes/relative' -import { Route as LinkPropsRouteImport } from './routes/linkProps' +import { Route as IndexRouteImport } from './routes/index' import { Route as AbsoluteRouteImport } from './routes/absolute' -import { Route as SearchRouteRouteImport } from './routes/search/route' +import { Route as LinkPropsRouteImport } from './routes/linkProps' import { Route as ParamsRouteRouteImport } from './routes/params/route' -import { Route as IndexRouteImport } from './routes/index' -import { Route as SearchSearchPlaceholderRouteImport } from './routes/search/searchPlaceholder' +import { Route as RelativeRouteImport } from './routes/relative' +import { Route as SearchRouteRouteImport } from './routes/search/route' import { Route as ParamsParamsPlaceholderRouteImport } from './routes/params/$paramsPlaceholder' +import { Route as SearchSearchPlaceholderRouteImport } from './routes/search/searchPlaceholder' -const RelativeRoute = RelativeRouteImport.update({ - id: '/relative', - path: '/relative', - getParentRoute: () => rootRouteImport, -} as any) -const LinkPropsRoute = LinkPropsRouteImport.update({ - id: '/linkProps', - path: '/linkProps', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const AbsoluteRoute = AbsoluteRouteImport.update({ @@ -33,9 +28,9 @@ const AbsoluteRoute = AbsoluteRouteImport.update({ path: '/absolute', getParentRoute: () => rootRouteImport, } as any) -const SearchRouteRoute = SearchRouteRouteImport.update({ - id: '/search', - path: '/search', +const LinkPropsRoute = LinkPropsRouteImport.update({ + id: '/linkProps', + path: '/linkProps', getParentRoute: () => rootRouteImport, } as any) const ParamsRouteRoute = ParamsRouteRouteImport.update({ @@ -43,21 +38,26 @@ const ParamsRouteRoute = ParamsRouteRouteImport.update({ path: '/params', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const RelativeRoute = RelativeRouteImport.update({ + id: '/relative', + path: '/relative', getParentRoute: () => rootRouteImport, } as any) -const SearchSearchPlaceholderRoute = SearchSearchPlaceholderRouteImport.update({ - id: '/searchPlaceholder', - path: '/searchPlaceholder', - getParentRoute: () => SearchRouteRoute, +const SearchRouteRoute = SearchRouteRouteImport.update({ + id: '/search', + path: '/search', + getParentRoute: () => rootRouteImport, } as any) const ParamsParamsPlaceholderRoute = ParamsParamsPlaceholderRouteImport.update({ id: '/$paramsPlaceholder', path: '/$paramsPlaceholder', getParentRoute: () => ParamsRouteRoute, } as any) +const SearchSearchPlaceholderRoute = SearchSearchPlaceholderRouteImport.update({ + id: '/searchPlaceholder', + path: '/searchPlaceholder', + getParentRoute: () => SearchRouteRoute, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -134,18 +134,11 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/relative': { - id: '/relative' - path: '/relative' - fullPath: '/relative' - preLoaderRoute: typeof RelativeRouteImport - parentRoute: typeof rootRouteImport - } - '/linkProps': { - id: '/linkProps' - path: '/linkProps' - fullPath: '/linkProps' - preLoaderRoute: typeof LinkPropsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/absolute': { @@ -155,11 +148,11 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof AbsoluteRouteImport parentRoute: typeof rootRouteImport } - '/search': { - id: '/search' - path: '/search' - fullPath: '/search' - preLoaderRoute: typeof SearchRouteRouteImport + '/linkProps': { + id: '/linkProps' + path: '/linkProps' + fullPath: '/linkProps' + preLoaderRoute: typeof LinkPropsRouteImport parentRoute: typeof rootRouteImport } '/params': { @@ -169,19 +162,19 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof ParamsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/relative': { + id: '/relative' + path: '/relative' + fullPath: '/relative' + preLoaderRoute: typeof RelativeRouteImport parentRoute: typeof rootRouteImport } - '/search/searchPlaceholder': { - id: '/search/searchPlaceholder' - path: '/searchPlaceholder' - fullPath: '/search/searchPlaceholder' - preLoaderRoute: typeof SearchSearchPlaceholderRouteImport - parentRoute: typeof SearchRouteRoute + '/search': { + id: '/search' + path: '/search' + fullPath: '/search' + preLoaderRoute: typeof SearchRouteRouteImport + parentRoute: typeof rootRouteImport } '/params/$paramsPlaceholder': { id: '/params/$paramsPlaceholder' @@ -190,6 +183,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof ParamsParamsPlaceholderRouteImport parentRoute: typeof ParamsRouteRoute } + '/search/searchPlaceholder': { + id: '/search/searchPlaceholder' + path: '/searchPlaceholder' + fullPath: '/search/searchPlaceholder' + preLoaderRoute: typeof SearchSearchPlaceholderRouteImport + parentRoute: typeof SearchRouteRoute + } } } diff --git a/examples/solid/start-supabase-basic/src/routeTree.gen.ts b/examples/solid/start-supabase-basic/src/routeTree.gen.ts index 7019a6c21a..bbebb19df3 100644 --- a/examples/solid/start-supabase-basic/src/routeTree.gen.ts +++ b/examples/solid/start-supabase-basic/src/routeTree.gen.ts @@ -9,23 +9,22 @@ // 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 SignupRouteImport } from './routes/signup' -import { Route as LogoutRouteImport } from './routes/logout' -import { Route as LoginRouteImport } from './routes/login' -import { Route as AuthedRouteImport } from './routes/_authed' import { Route as IndexRouteImport } from './routes/index' +import { Route as AuthedRouteImport } from './routes/_authed' +import { Route as LoginRouteImport } from './routes/login' +import { Route as LogoutRouteImport } from './routes/logout' +import { Route as SignupRouteImport } from './routes/signup' import { Route as AuthedPostsRouteImport } from './routes/_authed/posts' import { Route as AuthedPostsIndexRouteImport } from './routes/_authed/posts.index' import { Route as AuthedPostsPostIdRouteImport } from './routes/_authed/posts.$postId' -const SignupRoute = SignupRouteImport.update({ - id: '/signup', - path: '/signup', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) -const LogoutRoute = LogoutRouteImport.update({ - id: '/logout', - path: '/logout', +const AuthedRoute = AuthedRouteImport.update({ + id: '/_authed', getParentRoute: () => rootRouteImport, } as any) const LoginRoute = LoginRouteImport.update({ @@ -33,13 +32,14 @@ const LoginRoute = LoginRouteImport.update({ path: '/login', getParentRoute: () => rootRouteImport, } as any) -const AuthedRoute = AuthedRouteImport.update({ - id: '/_authed', +const LogoutRoute = LogoutRouteImport.update({ + id: '/logout', + path: '/logout', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const SignupRoute = SignupRouteImport.update({ + id: '/signup', + path: '/signup', getParentRoute: () => rootRouteImport, } as any) const AuthedPostsRoute = AuthedPostsRouteImport.update({ @@ -120,18 +120,18 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/signup': { - id: '/signup' - path: '/signup' - fullPath: '/signup' - preLoaderRoute: typeof SignupRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/logout': { - id: '/logout' - path: '/logout' - fullPath: '/logout' - preLoaderRoute: typeof LogoutRouteImport + '/_authed': { + id: '/_authed' + path: '' + fullPath: '/' + preLoaderRoute: typeof AuthedRouteImport parentRoute: typeof rootRouteImport } '/login': { @@ -141,18 +141,18 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof LoginRouteImport parentRoute: typeof rootRouteImport } - '/_authed': { - id: '/_authed' - path: '' - fullPath: '/' - preLoaderRoute: typeof AuthedRouteImport + '/logout': { + id: '/logout' + path: '/logout' + fullPath: '/logout' + preLoaderRoute: typeof LogoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/signup': { + id: '/signup' + path: '/signup' + fullPath: '/signup' + preLoaderRoute: typeof SignupRouteImport parentRoute: typeof rootRouteImport } '/_authed/posts': { diff --git a/examples/solid/view-transitions/src/routeTree.gen.ts b/examples/solid/view-transitions/src/routeTree.gen.ts index 114e34ed9d..01843027dc 100644 --- a/examples/solid/view-transitions/src/routeTree.gen.ts +++ b/examples/solid/view-transitions/src/routeTree.gen.ts @@ -9,16 +9,16 @@ // 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 HowItWorksRouteImport } from './routes/how-it-works' +import { Route as IndexRouteImport } from './routes/index' import { Route as ExploreRouteImport } from './routes/explore' +import { Route as HowItWorksRouteImport } from './routes/how-it-works' import { Route as PostsRouteRouteImport } from './routes/posts.route' -import { Route as IndexRouteImport } from './routes/index' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -const HowItWorksRoute = HowItWorksRouteImport.update({ - id: '/how-it-works', - path: '/how-it-works', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const ExploreRoute = ExploreRouteImport.update({ @@ -26,16 +26,16 @@ const ExploreRoute = ExploreRouteImport.update({ path: '/explore', getParentRoute: () => rootRouteImport, } as any) +const HowItWorksRoute = HowItWorksRouteImport.update({ + id: '/how-it-works', + path: '/how-it-works', + getParentRoute: () => rootRouteImport, +} as any) const PostsRouteRoute = PostsRouteRouteImport.update({ id: '/posts', path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -101,11 +101,11 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/how-it-works': { - id: '/how-it-works' - path: '/how-it-works' - fullPath: '/how-it-works' - preLoaderRoute: typeof HowItWorksRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/explore': { @@ -115,6 +115,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof ExploreRouteImport parentRoute: typeof rootRouteImport } + '/how-it-works': { + id: '/how-it-works' + path: '/how-it-works' + fullPath: '/how-it-works' + preLoaderRoute: typeof HowItWorksRouteImport + parentRoute: typeof rootRouteImport + } '/posts': { id: '/posts' path: '/posts' @@ -122,13 +129,6 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsRouteRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport - } '/posts/': { id: '/posts/' path: '/' diff --git a/examples/solid/with-trpc/src/routeTree.gen.ts b/examples/solid/with-trpc/src/routeTree.gen.ts index fd93507694..aa08570aca 100644 --- a/examples/solid/with-trpc/src/routeTree.gen.ts +++ b/examples/solid/with-trpc/src/routeTree.gen.ts @@ -9,23 +9,23 @@ // 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 DashboardRouteImport } from './routes/dashboard' import { Route as IndexRouteImport } from './routes/index' +import { Route as DashboardRouteImport } from './routes/dashboard' import { Route as DashboardIndexRouteImport } from './routes/dashboard.index' import { Route as DashboardPostsRouteImport } from './routes/dashboard.posts' import { Route as DashboardPostsIndexRouteImport } from './routes/dashboard.posts.index' import { Route as DashboardPostsPostIdRouteImport } from './routes/dashboard.posts.$postId' -const DashboardRoute = DashboardRouteImport.update({ - id: '/dashboard', - path: '/dashboard', - getParentRoute: () => rootRouteImport, -} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const DashboardRoute = DashboardRouteImport.update({ + id: '/dashboard', + path: '/dashboard', + getParentRoute: () => rootRouteImport, +} as any) const DashboardIndexRoute = DashboardIndexRouteImport.update({ id: '/', path: '/', @@ -98,13 +98,6 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/dashboard': { - id: '/dashboard' - path: '/dashboard' - fullPath: '/dashboard' - preLoaderRoute: typeof DashboardRouteImport - parentRoute: typeof rootRouteImport - } '/': { id: '/' path: '/' @@ -112,6 +105,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/dashboard': { + id: '/dashboard' + path: '/dashboard' + fullPath: '/dashboard' + preLoaderRoute: typeof DashboardRouteImport + parentRoute: typeof rootRouteImport + } '/dashboard/': { id: '/dashboard/' path: '/' diff --git a/examples/vue/basic-file-based-jsx/src/routeTree.gen.ts b/examples/vue/basic-file-based-jsx/src/routeTree.gen.ts index f5cd519d7f..3922025c82 100644 --- a/examples/vue/basic-file-based-jsx/src/routeTree.gen.ts +++ b/examples/vue/basic-file-based-jsx/src/routeTree.gen.ts @@ -11,52 +11,45 @@ import { lazyRouteComponent } from '@tanstack/vue-router' import { Route as rootRouteImport } from './routes/__root' -import { Route as Char45824Char54620Char48124Char44397RouteImport } from './routes/대한민국' -import { Route as SfcComponentRouteImport } from './routes/sfcComponent' -import { Route as RemountDepsRouteImport } from './routes/remountDeps' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as NotRemountDepsRouteImport } from './routes/notRemountDeps' -import { Route as EditingBRouteImport } from './routes/editing-b' -import { Route as EditingARouteImport } from './routes/editing-a' -import { Route as LayoutRouteImport } from './routes/_layout' import { Route as IndexRouteImport } from './routes/index' +import { Route as LayoutRouteImport } from './routes/_layout' +import { Route as EditingARouteImport } from './routes/editing-a' +import { Route as EditingBRouteImport } from './routes/editing-b' +import { Route as NotRemountDepsRouteImport } from './routes/notRemountDeps' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as RemountDepsRouteImport } from './routes/remountDeps' +import { Route as SfcComponentRouteImport } from './routes/sfcComponent' +import { Route as Char45824Char54620Char48124Char44397RouteImport } from './routes/대한민국' +import { Route as anotherGroupOnlyrouteinsideRouteImport } from './routes/(another-group)/onlyrouteinside' +import { Route as groupLayoutRouteImport } from './routes/(group)/_layout' +import { Route as groupInsideRouteImport } from './routes/(group)/inside' +import { Route as groupLazyinsideRouteImport } from './routes/(group)/lazyinside' +import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' -import { Route as groupLazyinsideRouteImport } from './routes/(group)/lazyinside' -import { Route as groupInsideRouteImport } from './routes/(group)/inside' -import { Route as groupLayoutRouteImport } from './routes/(group)/_layout' -import { Route as anotherGroupOnlyrouteinsideRouteImport } from './routes/(another-group)/onlyrouteinside' -import { Route as PostsPostIdEditRouteImport } from './routes/posts_.$postId.edit' -import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' -import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' -import { Route as groupSubfolderInsideRouteImport } from './routes/(group)/subfolder/inside' import { Route as groupLayoutInsidelayoutRouteImport } from './routes/(group)/_layout.insidelayout' +import { Route as groupSubfolderInsideRouteImport } from './routes/(group)/subfolder/inside' +import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' +import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' +import { Route as PostsPostIdEditRouteImport } from './routes/posts_.$postId.edit' -const Char45824Char54620Char48124Char44397Route = - Char45824Char54620Char48124Char44397RouteImport.update({ - id: '/대한민국', - path: '/대한민국', - getParentRoute: () => rootRouteImport, - } as any) -const SfcComponentRoute = SfcComponentRouteImport.update({ - id: '/sfcComponent', - path: '/sfcComponent', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, -} as any).update({ - component: lazyRouteComponent( - () => import('./routes/sfcComponent.component.vue'), - 'default', - ), -}) -const RemountDepsRoute = RemountDepsRouteImport.update({ - id: '/remountDeps', - path: '/remountDeps', +} as any) +const LayoutRoute = LayoutRouteImport.update({ + id: '/_layout', getParentRoute: () => rootRouteImport, } as any) -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const EditingARoute = EditingARouteImport.update({ + id: '/editing-a', + path: '/editing-a', + getParentRoute: () => rootRouteImport, +} as any) +const EditingBRoute = EditingBRouteImport.update({ + id: '/editing-b', + path: '/editing-b', getParentRoute: () => rootRouteImport, } as any) const NotRemountDepsRoute = NotRemountDepsRouteImport.update({ @@ -64,25 +57,56 @@ const NotRemountDepsRoute = NotRemountDepsRouteImport.update({ path: '/notRemountDeps', getParentRoute: () => rootRouteImport, } as any) -const EditingBRoute = EditingBRouteImport.update({ - id: '/editing-b', - path: '/editing-b', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) -const EditingARoute = EditingARouteImport.update({ - id: '/editing-a', - path: '/editing-a', +const RemountDepsRoute = RemountDepsRouteImport.update({ + id: '/remountDeps', + path: '/remountDeps', getParentRoute: () => rootRouteImport, } as any) -const LayoutRoute = LayoutRouteImport.update({ - id: '/_layout', +const SfcComponentRoute = SfcComponentRouteImport.update({ + id: '/sfcComponent', + path: '/sfcComponent', + getParentRoute: () => rootRouteImport, +} as any).update({ + component: lazyRouteComponent( + () => import('./routes/sfcComponent.component.vue'), + 'default', + ), +}) +const Char45824Char54620Char48124Char44397Route = + Char45824Char54620Char48124Char44397RouteImport.update({ + id: '/대한민국', + path: '/대한민국', + getParentRoute: () => rootRouteImport, + } as any) +const anotherGroupOnlyrouteinsideRoute = + anotherGroupOnlyrouteinsideRouteImport.update({ + id: '/(another-group)/onlyrouteinside', + path: '/onlyrouteinside', + getParentRoute: () => rootRouteImport, + } as any) +const groupLayoutRoute = groupLayoutRouteImport.update({ + id: '/(group)/_layout', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const groupInsideRoute = groupInsideRouteImport.update({ + id: '/(group)/inside', + path: '/inside', + getParentRoute: () => rootRouteImport, +} as any) +const groupLazyinsideRoute = groupLazyinsideRouteImport.update({ + id: '/(group)/lazyinside', + path: '/lazyinside', getParentRoute: () => rootRouteImport, } as any) +const LayoutLayout2Route = LayoutLayout2RouteImport.update({ + id: '/_layout-2', + getParentRoute: () => LayoutRoute, +} as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -93,55 +117,31 @@ const PostsPostIdRoute = PostsPostIdRouteImport.update({ path: '/$postId', getParentRoute: () => PostsRoute, } as any) -const LayoutLayout2Route = LayoutLayout2RouteImport.update({ - id: '/_layout-2', - getParentRoute: () => LayoutRoute, -} as any) -const groupLazyinsideRoute = groupLazyinsideRouteImport.update({ - id: '/(group)/lazyinside', - path: '/lazyinside', - getParentRoute: () => rootRouteImport, -} as any) -const groupInsideRoute = groupInsideRouteImport.update({ - id: '/(group)/inside', - path: '/inside', - getParentRoute: () => rootRouteImport, +const groupLayoutInsidelayoutRoute = groupLayoutInsidelayoutRouteImport.update({ + id: '/insidelayout', + path: '/insidelayout', + getParentRoute: () => groupLayoutRoute, } as any) -const groupLayoutRoute = groupLayoutRouteImport.update({ - id: '/(group)/_layout', +const groupSubfolderInsideRoute = groupSubfolderInsideRouteImport.update({ + id: '/(group)/subfolder/inside', + path: '/subfolder/inside', getParentRoute: () => rootRouteImport, } as any) -const anotherGroupOnlyrouteinsideRoute = - anotherGroupOnlyrouteinsideRouteImport.update({ - id: '/(another-group)/onlyrouteinside', - path: '/onlyrouteinside', - getParentRoute: () => rootRouteImport, - } as any) -const PostsPostIdEditRoute = PostsPostIdEditRouteImport.update({ - id: '/posts_/$postId/edit', - path: '/posts/$postId/edit', - getParentRoute: () => rootRouteImport, +const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ + id: '/layout-a', + path: '/layout-a', + getParentRoute: () => LayoutLayout2Route, } as any) const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ id: '/layout-b', path: '/layout-b', getParentRoute: () => LayoutLayout2Route, } as any) -const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ - id: '/layout-a', - path: '/layout-a', - getParentRoute: () => LayoutLayout2Route, -} as any) -const groupSubfolderInsideRoute = groupSubfolderInsideRouteImport.update({ - id: '/(group)/subfolder/inside', - path: '/subfolder/inside', +const PostsPostIdEditRoute = PostsPostIdEditRouteImport.update({ + id: '/posts_/$postId/edit', + path: '/posts/$postId/edit', getParentRoute: () => rootRouteImport, } as any) -const groupLayoutInsidelayoutRoute = groupLayoutInsidelayoutRouteImport.update({ - id: '/insidelayout', - path: '/insidelayout', - getParentRoute: () => groupLayoutRoute, -} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -291,32 +291,32 @@ export interface RootRouteChildren { declare module '@tanstack/vue-router' { interface FileRoutesByPath { - '/대한민국': { - id: '/대한민국' - path: '/대한민국' - fullPath: '/대한민국' - preLoaderRoute: typeof Char45824Char54620Char48124Char44397RouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/sfcComponent': { - id: '/sfcComponent' - path: '/sfcComponent' - fullPath: '/sfcComponent' - preLoaderRoute: typeof SfcComponentRouteImport + '/_layout': { + id: '/_layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } - '/remountDeps': { - id: '/remountDeps' - path: '/remountDeps' - fullPath: '/remountDeps' - preLoaderRoute: typeof RemountDepsRouteImport + '/editing-a': { + id: '/editing-a' + path: '/editing-a' + fullPath: '/editing-a' + preLoaderRoute: typeof EditingARouteImport parentRoute: typeof rootRouteImport } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/editing-b': { + id: '/editing-b' + path: '/editing-b' + fullPath: '/editing-b' + preLoaderRoute: typeof EditingBRouteImport parentRoute: typeof rootRouteImport } '/notRemountDeps': { @@ -326,34 +326,69 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof NotRemountDepsRouteImport parentRoute: typeof rootRouteImport } - '/editing-b': { - id: '/editing-b' - path: '/editing-b' - fullPath: '/editing-b' - preLoaderRoute: typeof EditingBRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/editing-a': { - id: '/editing-a' - path: '/editing-a' - fullPath: '/editing-a' - preLoaderRoute: typeof EditingARouteImport + '/remountDeps': { + id: '/remountDeps' + path: '/remountDeps' + fullPath: '/remountDeps' + preLoaderRoute: typeof RemountDepsRouteImport parentRoute: typeof rootRouteImport } - '/_layout': { - id: '/_layout' + '/sfcComponent': { + id: '/sfcComponent' + path: '/sfcComponent' + fullPath: '/sfcComponent' + preLoaderRoute: typeof SfcComponentRouteImport + parentRoute: typeof rootRouteImport + } + '/대한민국': { + id: '/대한민국' + path: '/대한민국' + fullPath: '/대한민국' + preLoaderRoute: typeof Char45824Char54620Char48124Char44397RouteImport + parentRoute: typeof rootRouteImport + } + '/(another-group)/onlyrouteinside': { + id: '/(another-group)/onlyrouteinside' + path: '/onlyrouteinside' + fullPath: '/onlyrouteinside' + preLoaderRoute: typeof anotherGroupOnlyrouteinsideRouteImport + parentRoute: typeof rootRouteImport + } + '/(group)/_layout': { + id: '/(group)/_layout' path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutRouteImport + fullPath: '' + preLoaderRoute: typeof groupLayoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/(group)/inside': { + id: '/(group)/inside' + path: '/inside' + fullPath: '/inside' + preLoaderRoute: typeof groupInsideRouteImport parentRoute: typeof rootRouteImport } + '/(group)/lazyinside': { + id: '/(group)/lazyinside' + path: '/lazyinside' + fullPath: '/lazyinside' + preLoaderRoute: typeof groupLazyinsideRouteImport + parentRoute: typeof rootRouteImport + } + '/_layout/_layout-2': { + id: '/_layout/_layout-2' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutLayout2RouteImport + parentRoute: typeof LayoutRoute + } '/posts/': { id: '/posts/' path: '/' @@ -368,47 +403,26 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/_layout/_layout-2': { - id: '/_layout/_layout-2' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutLayout2RouteImport - parentRoute: typeof LayoutRoute - } - '/(group)/lazyinside': { - id: '/(group)/lazyinside' - path: '/lazyinside' - fullPath: '/lazyinside' - preLoaderRoute: typeof groupLazyinsideRouteImport - parentRoute: typeof rootRouteImport - } - '/(group)/inside': { - id: '/(group)/inside' - path: '/inside' - fullPath: '/inside' - preLoaderRoute: typeof groupInsideRouteImport - parentRoute: typeof rootRouteImport - } - '/(group)/_layout': { - id: '/(group)/_layout' - path: '' - fullPath: '' - preLoaderRoute: typeof groupLayoutRouteImport - parentRoute: typeof rootRouteImport + '/(group)/_layout/insidelayout': { + id: '/(group)/_layout/insidelayout' + path: '/insidelayout' + fullPath: '/insidelayout' + preLoaderRoute: typeof groupLayoutInsidelayoutRouteImport + parentRoute: typeof groupLayoutRoute } - '/(another-group)/onlyrouteinside': { - id: '/(another-group)/onlyrouteinside' - path: '/onlyrouteinside' - fullPath: '/onlyrouteinside' - preLoaderRoute: typeof anotherGroupOnlyrouteinsideRouteImport + '/(group)/subfolder/inside': { + id: '/(group)/subfolder/inside' + path: '/subfolder/inside' + fullPath: '/subfolder/inside' + preLoaderRoute: typeof groupSubfolderInsideRouteImport parentRoute: typeof rootRouteImport } - '/posts_/$postId/edit': { - id: '/posts_/$postId/edit' - path: '/posts/$postId/edit' - fullPath: '/posts/$postId/edit' - preLoaderRoute: typeof PostsPostIdEditRouteImport - parentRoute: typeof rootRouteImport + '/_layout/_layout-2/layout-a': { + id: '/_layout/_layout-2/layout-a' + path: '/layout-a' + fullPath: '/layout-a' + preLoaderRoute: typeof LayoutLayout2LayoutARouteImport + parentRoute: typeof LayoutLayout2Route } '/_layout/_layout-2/layout-b': { id: '/_layout/_layout-2/layout-b' @@ -417,27 +431,13 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport parentRoute: typeof LayoutLayout2Route } - '/_layout/_layout-2/layout-a': { - id: '/_layout/_layout-2/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: typeof LayoutLayout2LayoutARouteImport - parentRoute: typeof LayoutLayout2Route - } - '/(group)/subfolder/inside': { - id: '/(group)/subfolder/inside' - path: '/subfolder/inside' - fullPath: '/subfolder/inside' - preLoaderRoute: typeof groupSubfolderInsideRouteImport + '/posts_/$postId/edit': { + id: '/posts_/$postId/edit' + path: '/posts/$postId/edit' + fullPath: '/posts/$postId/edit' + preLoaderRoute: typeof PostsPostIdEditRouteImport parentRoute: typeof rootRouteImport } - '/(group)/_layout/insidelayout': { - id: '/(group)/_layout/insidelayout' - path: '/insidelayout' - fullPath: '/insidelayout' - preLoaderRoute: typeof groupLayoutInsidelayoutRouteImport - parentRoute: typeof groupLayoutRoute - } } } diff --git a/examples/vue/basic-file-based-sfc/src/routeTree.gen.ts b/examples/vue/basic-file-based-sfc/src/routeTree.gen.ts index fa00eb1ce0..09d62b0706 100644 --- a/examples/vue/basic-file-based-sfc/src/routeTree.gen.ts +++ b/examples/vue/basic-file-based-sfc/src/routeTree.gen.ts @@ -11,65 +11,53 @@ import { lazyRouteComponent } from '@tanstack/vue-router' import { Route as rootRouteImport } from './routes/__root' -import { Route as Char45824Char54620Char48124Char44397RouteImport } from './routes/대한민국' -import { Route as RemountDepsRouteImport } from './routes/remountDeps' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as NotRemountDepsRouteImport } from './routes/notRemountDeps' -import { Route as EditingBRouteImport } from './routes/editing-b' -import { Route as EditingARouteImport } from './routes/editing-a' -import { Route as LayoutRouteImport } from './routes/_layout' import { Route as IndexRouteImport } from './routes/index' +import { Route as LayoutRouteImport } from './routes/_layout' +import { Route as EditingARouteImport } from './routes/editing-a' +import { Route as EditingBRouteImport } from './routes/editing-b' +import { Route as NotRemountDepsRouteImport } from './routes/notRemountDeps' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as RemountDepsRouteImport } from './routes/remountDeps' +import { Route as Char45824Char54620Char48124Char44397RouteImport } from './routes/대한민국' +import { Route as anotherGroupOnlyrouteinsideRouteImport } from './routes/(another-group)/onlyrouteinside' +import { Route as groupLayoutRouteImport } from './routes/(group)/_layout' +import { Route as groupInsideRouteImport } from './routes/(group)/inside' +import { Route as groupLazyinsideRouteImport } from './routes/(group)/lazyinside' +import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' -import { Route as groupLazyinsideRouteImport } from './routes/(group)/lazyinside' -import { Route as groupInsideRouteImport } from './routes/(group)/inside' -import { Route as groupLayoutRouteImport } from './routes/(group)/_layout' -import { Route as anotherGroupOnlyrouteinsideRouteImport } from './routes/(another-group)/onlyrouteinside' -import { Route as PostsPostIdEditRouteImport } from './routes/posts_.$postId.edit' -import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' -import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' -import { Route as groupSubfolderInsideRouteImport } from './routes/(group)/subfolder/inside' import { Route as groupLayoutInsidelayoutRouteImport } from './routes/(group)/_layout.insidelayout' +import { Route as groupSubfolderInsideRouteImport } from './routes/(group)/subfolder/inside' +import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' +import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' +import { Route as PostsPostIdEditRouteImport } from './routes/posts_.$postId.edit' -const Char45824Char54620Char48124Char44397Route = - Char45824Char54620Char48124Char44397RouteImport.update({ - id: '/대한민국', - path: '/대한민국', - getParentRoute: () => rootRouteImport, - } as any).update({ - component: lazyRouteComponent( - () => import('./routes/대한민국.component.vue'), - 'default', - ), - }) -const RemountDepsRoute = RemountDepsRouteImport.update({ - id: '/remountDeps', - path: '/remountDeps', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any).update({ component: lazyRouteComponent( - () => import('./routes/remountDeps.component.vue'), + () => import('./routes/index.component.vue'), 'default', ), }) -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const LayoutRoute = LayoutRouteImport.update({ + id: '/_layout', getParentRoute: () => rootRouteImport, } as any).update({ component: lazyRouteComponent( - () => import('./routes/posts.component.vue'), + () => import('./routes/_layout.component.vue'), 'default', ), }) -const NotRemountDepsRoute = NotRemountDepsRouteImport.update({ - id: '/notRemountDeps', - path: '/notRemountDeps', +const EditingARoute = EditingARouteImport.update({ + id: '/editing-a', + path: '/editing-a', getParentRoute: () => rootRouteImport, } as any).update({ component: lazyRouteComponent( - () => import('./routes/notRemountDeps.component.vue'), + () => import('./routes/editing-a.component.vue'), 'default', ), }) @@ -83,89 +71,56 @@ const EditingBRoute = EditingBRouteImport.update({ 'default', ), }) -const EditingARoute = EditingARouteImport.update({ - id: '/editing-a', - path: '/editing-a', +const NotRemountDepsRoute = NotRemountDepsRouteImport.update({ + id: '/notRemountDeps', + path: '/notRemountDeps', getParentRoute: () => rootRouteImport, } as any).update({ component: lazyRouteComponent( - () => import('./routes/editing-a.component.vue'), + () => import('./routes/notRemountDeps.component.vue'), 'default', ), }) -const LayoutRoute = LayoutRouteImport.update({ - id: '/_layout', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any).update({ component: lazyRouteComponent( - () => import('./routes/_layout.component.vue'), + () => import('./routes/posts.component.vue'), 'default', ), }) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const RemountDepsRoute = RemountDepsRouteImport.update({ + id: '/remountDeps', + path: '/remountDeps', getParentRoute: () => rootRouteImport, } as any).update({ component: lazyRouteComponent( - () => import('./routes/index.component.vue'), - 'default', - ), -}) -const PostsIndexRoute = PostsIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => PostsRoute, -} as any).update({ - component: lazyRouteComponent( - () => import('./routes/posts.index.component.vue'), - 'default', - ), -}) -const PostsPostIdRoute = PostsPostIdRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => PostsRoute, -} as any).update({ - component: lazyRouteComponent( - () => import('./routes/posts.$postId.component.vue'), - 'default', - ), - errorComponent: lazyRouteComponent( - () => import('./routes/posts.$postId.errorComponent.vue'), - 'default', - ), -}) -const LayoutLayout2Route = LayoutLayout2RouteImport.update({ - id: '/_layout-2', - getParentRoute: () => LayoutRoute, -} as any).update({ - component: lazyRouteComponent( - () => import('./routes/_layout/_layout-2.component.vue'), + () => import('./routes/remountDeps.component.vue'), 'default', ), }) -const groupLazyinsideRoute = groupLazyinsideRouteImport - .update({ - id: '/(group)/lazyinside', - path: '/lazyinside', +const Char45824Char54620Char48124Char44397Route = + Char45824Char54620Char48124Char44397RouteImport.update({ + id: '/대한민국', + path: '/대한민국', getParentRoute: () => rootRouteImport, - } as any) - .update({ + } as any).update({ component: lazyRouteComponent( - () => import('./routes/(group)/lazyinside.component.vue'), + () => import('./routes/대한민국.component.vue'), 'default', ), }) -const groupInsideRoute = groupInsideRouteImport +const anotherGroupOnlyrouteinsideRoute = anotherGroupOnlyrouteinsideRouteImport .update({ - id: '/(group)/inside', - path: '/inside', + id: '/(another-group)/onlyrouteinside', + path: '/onlyrouteinside', getParentRoute: () => rootRouteImport, } as any) .update({ component: lazyRouteComponent( - () => import('./routes/(group)/inside.component.vue'), + () => import('./routes/(another-group)/onlyrouteinside.component.vue'), 'default', ), }) @@ -180,72 +135,117 @@ const groupLayoutRoute = groupLayoutRouteImport 'default', ), }) -const anotherGroupOnlyrouteinsideRoute = anotherGroupOnlyrouteinsideRouteImport +const groupInsideRoute = groupInsideRouteImport .update({ - id: '/(another-group)/onlyrouteinside', - path: '/onlyrouteinside', + id: '/(group)/inside', + path: '/inside', getParentRoute: () => rootRouteImport, } as any) .update({ component: lazyRouteComponent( - () => import('./routes/(another-group)/onlyrouteinside.component.vue'), + () => import('./routes/(group)/inside.component.vue'), 'default', ), }) -const PostsPostIdEditRoute = PostsPostIdEditRouteImport.update({ - id: '/posts_/$postId/edit', - path: '/posts/$postId/edit', - getParentRoute: () => rootRouteImport, +const groupLazyinsideRoute = groupLazyinsideRouteImport + .update({ + id: '/(group)/lazyinside', + path: '/lazyinside', + getParentRoute: () => rootRouteImport, + } as any) + .update({ + component: lazyRouteComponent( + () => import('./routes/(group)/lazyinside.component.vue'), + 'default', + ), + }) +const LayoutLayout2Route = LayoutLayout2RouteImport.update({ + id: '/_layout-2', + getParentRoute: () => LayoutRoute, } as any).update({ component: lazyRouteComponent( - () => import('./routes/posts_.$postId.edit.component.vue'), + () => import('./routes/_layout/_layout-2.component.vue'), 'default', ), }) -const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ - id: '/layout-b', - path: '/layout-b', - getParentRoute: () => LayoutLayout2Route, +const PostsIndexRoute = PostsIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => PostsRoute, } as any).update({ component: lazyRouteComponent( - () => import('./routes/_layout/_layout-2/layout-b.component.vue'), + () => import('./routes/posts.index.component.vue'), 'default', ), }) -const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ - id: '/layout-a', - path: '/layout-a', - getParentRoute: () => LayoutLayout2Route, +const PostsPostIdRoute = PostsPostIdRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => PostsRoute, } as any).update({ component: lazyRouteComponent( - () => import('./routes/_layout/_layout-2/layout-a.component.vue'), + () => import('./routes/posts.$postId.component.vue'), + 'default', + ), + errorComponent: lazyRouteComponent( + () => import('./routes/posts.$postId.errorComponent.vue'), 'default', ), }) -const groupSubfolderInsideRoute = groupSubfolderInsideRouteImport +const groupLayoutInsidelayoutRoute = groupLayoutInsidelayoutRouteImport .update({ - id: '/(group)/subfolder/inside', - path: '/subfolder/inside', - getParentRoute: () => rootRouteImport, + id: '/insidelayout', + path: '/insidelayout', + getParentRoute: () => groupLayoutRoute, } as any) .update({ component: lazyRouteComponent( - () => import('./routes/(group)/subfolder/inside.component.vue'), + () => import('./routes/(group)/_layout.insidelayout.component.vue'), 'default', ), }) -const groupLayoutInsidelayoutRoute = groupLayoutInsidelayoutRouteImport +const groupSubfolderInsideRoute = groupSubfolderInsideRouteImport .update({ - id: '/insidelayout', - path: '/insidelayout', - getParentRoute: () => groupLayoutRoute, + id: '/(group)/subfolder/inside', + path: '/subfolder/inside', + getParentRoute: () => rootRouteImport, } as any) .update({ component: lazyRouteComponent( - () => import('./routes/(group)/_layout.insidelayout.component.vue'), + () => import('./routes/(group)/subfolder/inside.component.vue'), 'default', ), }) +const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ + id: '/layout-a', + path: '/layout-a', + getParentRoute: () => LayoutLayout2Route, +} as any).update({ + component: lazyRouteComponent( + () => import('./routes/_layout/_layout-2/layout-a.component.vue'), + 'default', + ), +}) +const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ + id: '/layout-b', + path: '/layout-b', + getParentRoute: () => LayoutLayout2Route, +} as any).update({ + component: lazyRouteComponent( + () => import('./routes/_layout/_layout-2/layout-b.component.vue'), + 'default', + ), +}) +const PostsPostIdEditRoute = PostsPostIdEditRouteImport.update({ + id: '/posts_/$postId/edit', + path: '/posts/$postId/edit', + getParentRoute: () => rootRouteImport, +} as any).update({ + component: lazyRouteComponent( + () => import('./routes/posts_.$postId.edit.component.vue'), + 'default', + ), +}) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -388,25 +388,32 @@ export interface RootRouteChildren { declare module '@tanstack/vue-router' { interface FileRoutesByPath { - '/대한민국': { - id: '/대한민국' - path: '/대한민국' - fullPath: '/대한민국' - preLoaderRoute: typeof Char45824Char54620Char48124Char44397RouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/remountDeps': { - id: '/remountDeps' - path: '/remountDeps' - fullPath: '/remountDeps' - preLoaderRoute: typeof RemountDepsRouteImport + '/_layout': { + id: '/_layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/editing-a': { + id: '/editing-a' + path: '/editing-a' + fullPath: '/editing-a' + preLoaderRoute: typeof EditingARouteImport + parentRoute: typeof rootRouteImport + } + '/editing-b': { + id: '/editing-b' + path: '/editing-b' + fullPath: '/editing-b' + preLoaderRoute: typeof EditingBRouteImport parentRoute: typeof rootRouteImport } '/notRemountDeps': { @@ -416,34 +423,62 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof NotRemountDepsRouteImport parentRoute: typeof rootRouteImport } - '/editing-b': { - id: '/editing-b' - path: '/editing-b' - fullPath: '/editing-b' - preLoaderRoute: typeof EditingBRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/editing-a': { - id: '/editing-a' - path: '/editing-a' - fullPath: '/editing-a' - preLoaderRoute: typeof EditingARouteImport + '/remountDeps': { + id: '/remountDeps' + path: '/remountDeps' + fullPath: '/remountDeps' + preLoaderRoute: typeof RemountDepsRouteImport parentRoute: typeof rootRouteImport } - '/_layout': { - id: '/_layout' + '/대한민국': { + id: '/대한민국' + path: '/대한민국' + fullPath: '/대한민국' + preLoaderRoute: typeof Char45824Char54620Char48124Char44397RouteImport + parentRoute: typeof rootRouteImport + } + '/(another-group)/onlyrouteinside': { + id: '/(another-group)/onlyrouteinside' + path: '/onlyrouteinside' + fullPath: '/onlyrouteinside' + preLoaderRoute: typeof anotherGroupOnlyrouteinsideRouteImport + parentRoute: typeof rootRouteImport + } + '/(group)/_layout': { + id: '/(group)/_layout' path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutRouteImport + fullPath: '' + preLoaderRoute: typeof groupLayoutRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/(group)/inside': { + id: '/(group)/inside' + path: '/inside' + fullPath: '/inside' + preLoaderRoute: typeof groupInsideRouteImport parentRoute: typeof rootRouteImport } + '/(group)/lazyinside': { + id: '/(group)/lazyinside' + path: '/lazyinside' + fullPath: '/lazyinside' + preLoaderRoute: typeof groupLazyinsideRouteImport + parentRoute: typeof rootRouteImport + } + '/_layout/_layout-2': { + id: '/_layout/_layout-2' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutLayout2RouteImport + parentRoute: typeof LayoutRoute + } '/posts/': { id: '/posts/' path: '/' @@ -458,47 +493,26 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof PostsPostIdRouteImport parentRoute: typeof PostsRoute } - '/_layout/_layout-2': { - id: '/_layout/_layout-2' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutLayout2RouteImport - parentRoute: typeof LayoutRoute - } - '/(group)/lazyinside': { - id: '/(group)/lazyinside' - path: '/lazyinside' - fullPath: '/lazyinside' - preLoaderRoute: typeof groupLazyinsideRouteImport - parentRoute: typeof rootRouteImport - } - '/(group)/inside': { - id: '/(group)/inside' - path: '/inside' - fullPath: '/inside' - preLoaderRoute: typeof groupInsideRouteImport - parentRoute: typeof rootRouteImport - } - '/(group)/_layout': { - id: '/(group)/_layout' - path: '' - fullPath: '' - preLoaderRoute: typeof groupLayoutRouteImport - parentRoute: typeof rootRouteImport + '/(group)/_layout/insidelayout': { + id: '/(group)/_layout/insidelayout' + path: '/insidelayout' + fullPath: '/insidelayout' + preLoaderRoute: typeof groupLayoutInsidelayoutRouteImport + parentRoute: typeof groupLayoutRoute } - '/(another-group)/onlyrouteinside': { - id: '/(another-group)/onlyrouteinside' - path: '/onlyrouteinside' - fullPath: '/onlyrouteinside' - preLoaderRoute: typeof anotherGroupOnlyrouteinsideRouteImport + '/(group)/subfolder/inside': { + id: '/(group)/subfolder/inside' + path: '/subfolder/inside' + fullPath: '/subfolder/inside' + preLoaderRoute: typeof groupSubfolderInsideRouteImport parentRoute: typeof rootRouteImport } - '/posts_/$postId/edit': { - id: '/posts_/$postId/edit' - path: '/posts/$postId/edit' - fullPath: '/posts/$postId/edit' - preLoaderRoute: typeof PostsPostIdEditRouteImport - parentRoute: typeof rootRouteImport + '/_layout/_layout-2/layout-a': { + id: '/_layout/_layout-2/layout-a' + path: '/layout-a' + fullPath: '/layout-a' + preLoaderRoute: typeof LayoutLayout2LayoutARouteImport + parentRoute: typeof LayoutLayout2Route } '/_layout/_layout-2/layout-b': { id: '/_layout/_layout-2/layout-b' @@ -507,27 +521,13 @@ declare module '@tanstack/vue-router' { preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport parentRoute: typeof LayoutLayout2Route } - '/_layout/_layout-2/layout-a': { - id: '/_layout/_layout-2/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: typeof LayoutLayout2LayoutARouteImport - parentRoute: typeof LayoutLayout2Route - } - '/(group)/subfolder/inside': { - id: '/(group)/subfolder/inside' - path: '/subfolder/inside' - fullPath: '/subfolder/inside' - preLoaderRoute: typeof groupSubfolderInsideRouteImport + '/posts_/$postId/edit': { + id: '/posts_/$postId/edit' + path: '/posts/$postId/edit' + fullPath: '/posts/$postId/edit' + preLoaderRoute: typeof PostsPostIdEditRouteImport parentRoute: typeof rootRouteImport } - '/(group)/_layout/insidelayout': { - id: '/(group)/_layout/insidelayout' - path: '/insidelayout' - fullPath: '/insidelayout' - preLoaderRoute: typeof groupLayoutInsidelayoutRouteImport - parentRoute: typeof groupLayoutRoute - } } } From 49f686324574ea65851317cbba084eb386a911c6 Mon Sep 17 00:00:00 2001 From: Manuel Schiller <6340397+schiller-manuel@users.noreply.github.com> Date: Sun, 19 Jul 2026 12:09:25 +0200 Subject: [PATCH 22/51] fix(router-plugin): preserve HMR state for lowercase split components (#7855) --- .changeset/fresh-routes-refresh.md | 5 + e2e/react-start/hmr/src/routeTree.gen.ts | 22 ++ e2e/react-start/hmr/src/routes/__root.tsx | 6 + .../component-hmr-lowercase-named-split.tsx | 26 +++ e2e/react-start/hmr/tests/app.spec.ts | 145 +++++++++++++ .../src/core/code-splitter/compilers.ts | 14 +- .../src/core/code-splitter/plugins.ts | 13 +- .../plugins/framework-plugins.ts | 26 +-- .../plugins/react-refresh-route-components.ts | 100 ++++++--- packages/router-plugin/src/core/config.ts | 4 +- .../src/core/router-code-splitter-plugin.ts | 48 +++-- .../src/core/router-hmr-plugin.ts | 5 +- packages/router-plugin/src/index.ts | 2 + packages/router-plugin/tests/add-hmr.test.ts | 12 +- ...teFileRoute-lowercase-components@false.tsx | 12 ++ ...ateFileRoute-lowercase-components@true.tsx | 204 ++++++++++++++++++ ...teRootRoute-lowercase-components@false.tsx | 25 +++ ...ateRootRoute-lowercase-components@true.tsx | 200 +++++++++++++++++ .../createFileRoute-lowercase-components.tsx | 19 ++ .../createRootRoute-lowercase-components.tsx | 25 +++ .../router-plugin/tests/code-splitter.test.ts | 57 +++++ .../lowercase-function-declaration.tsx | 12 ++ ...wercase-function-declaration@component.tsx | 4 + ...se-function-declaration@errorComponent.tsx | 4 + ...function-declaration@notFoundComponent.tsx | 0 .../lowercase-function-declaration@shared.tsx | 0 .../lowercase-function-declaration.tsx | 10 + ...--notFoundComponent---pendingComponent.tsx | 12 ++ .../lowercase-function-declaration@loader.tsx | 0 .../lowercase-function-declaration@shared.tsx | 0 .../lowercase-function-declaration.tsx | 10 + ...--notFoundComponent---pendingComponent.tsx | 8 + ...se-function-declaration@errorComponent.tsx | 4 + .../lowercase-function-declaration@shared.tsx | 0 .../react/lowercase-function-declaration.tsx | 19 ++ .../tests/router-plugin-context.test.ts | 104 ++++++++- 36 files changed, 1078 insertions(+), 79 deletions(-) create mode 100644 .changeset/fresh-routes-refresh.md create mode 100644 e2e/react-start/hmr/src/routes/component-hmr-lowercase-named-split.tsx create mode 100644 packages/router-plugin/tests/add-hmr/snapshots/react/createFileRoute-lowercase-components@false.tsx create mode 100644 packages/router-plugin/tests/add-hmr/snapshots/react/createFileRoute-lowercase-components@true.tsx create mode 100644 packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute-lowercase-components@false.tsx create mode 100644 packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute-lowercase-components@true.tsx create mode 100644 packages/router-plugin/tests/add-hmr/test-files/react/createFileRoute-lowercase-components.tsx create mode 100644 packages/router-plugin/tests/add-hmr/test-files/react/createRootRoute-lowercase-components.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/1-default/lowercase-function-declaration.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/1-default/lowercase-function-declaration@component.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/1-default/lowercase-function-declaration@errorComponent.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/1-default/lowercase-function-declaration@notFoundComponent.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/1-default/lowercase-function-declaration@shared.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/lowercase-function-declaration.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/lowercase-function-declaration@component---errorComponent---notFoundComponent---pendingComponent.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/lowercase-function-declaration@loader.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/lowercase-function-declaration@shared.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/lowercase-function-declaration.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/lowercase-function-declaration@component---loader---notFoundComponent---pendingComponent.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/lowercase-function-declaration@errorComponent.tsx create mode 100644 packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/lowercase-function-declaration@shared.tsx create mode 100644 packages/router-plugin/tests/code-splitter/test-files/react/lowercase-function-declaration.tsx diff --git a/.changeset/fresh-routes-refresh.md b/.changeset/fresh-routes-refresh.md new file mode 100644 index 0000000000..3c23631cd4 --- /dev/null +++ b/.changeset/fresh-routes-refresh.md @@ -0,0 +1,5 @@ +--- +'@tanstack/router-plugin': patch +--- + +Preserve React component state during HMR when route components are declared with lowercase function names. The development-only React transforms cover split route component groups and unsplit root route component options, including shell, pending, and error components, without changing production output. diff --git a/e2e/react-start/hmr/src/routeTree.gen.ts b/e2e/react-start/hmr/src/routeTree.gen.ts index 8de7587b66..73d8bd14f5 100644 --- a/e2e/react-start/hmr/src/routeTree.gen.ts +++ b/e2e/react-start/hmr/src/routeTree.gen.ts @@ -15,6 +15,7 @@ import { Route as ComponentHmrRouteImport } from './routes/component-hmr' import { Route as ComponentHmrInlineErrorSplitRouteImport } from './routes/component-hmr-inline-error-split' import { Route as ComponentHmrInlineNosplitRouteImport } from './routes/component-hmr-inline-nosplit' import { Route as ComponentHmrInlineSplitRouteImport } from './routes/component-hmr-inline-split' +import { Route as ComponentHmrLowercaseNamedSplitRouteImport } from './routes/component-hmr-lowercase-named-split' import { Route as ComponentHmrNamedErrorSplitRouteImport } from './routes/component-hmr-named-error-split' import { Route as ComponentHmrNamedNosplitRouteImport } from './routes/component-hmr-named-nosplit' import { Route as ComponentHmrNamedSplitRouteImport } from './routes/component-hmr-named-split' @@ -55,6 +56,12 @@ const ComponentHmrInlineSplitRoute = ComponentHmrInlineSplitRouteImport.update({ path: '/component-hmr-inline-split', getParentRoute: () => rootRouteImport, } as any) +const ComponentHmrLowercaseNamedSplitRoute = + ComponentHmrLowercaseNamedSplitRouteImport.update({ + id: '/component-hmr-lowercase-named-split', + path: '/component-hmr-lowercase-named-split', + getParentRoute: () => rootRouteImport, + } as any) const ComponentHmrNamedErrorSplitRoute = ComponentHmrNamedErrorSplitRouteImport.update({ id: '/component-hmr-named-error-split', @@ -101,6 +108,7 @@ export interface FileRoutesByFullPath { '/component-hmr-inline-error-split': typeof ComponentHmrInlineErrorSplitRoute '/component-hmr-inline-nosplit': typeof ComponentHmrInlineNosplitRoute '/component-hmr-inline-split': typeof ComponentHmrInlineSplitRoute + '/component-hmr-lowercase-named-split': typeof ComponentHmrLowercaseNamedSplitRoute '/component-hmr-named-error-split': typeof ComponentHmrNamedErrorSplitRoute '/component-hmr-named-nosplit': typeof ComponentHmrNamedNosplitRoute '/component-hmr-named-split': typeof ComponentHmrNamedSplitRoute @@ -116,6 +124,7 @@ export interface FileRoutesByTo { '/component-hmr-inline-error-split': typeof ComponentHmrInlineErrorSplitRoute '/component-hmr-inline-nosplit': typeof ComponentHmrInlineNosplitRoute '/component-hmr-inline-split': typeof ComponentHmrInlineSplitRoute + '/component-hmr-lowercase-named-split': typeof ComponentHmrLowercaseNamedSplitRoute '/component-hmr-named-error-split': typeof ComponentHmrNamedErrorSplitRoute '/component-hmr-named-nosplit': typeof ComponentHmrNamedNosplitRoute '/component-hmr-named-split': typeof ComponentHmrNamedSplitRoute @@ -132,6 +141,7 @@ export interface FileRoutesById { '/component-hmr-inline-error-split': typeof ComponentHmrInlineErrorSplitRoute '/component-hmr-inline-nosplit': typeof ComponentHmrInlineNosplitRoute '/component-hmr-inline-split': typeof ComponentHmrInlineSplitRoute + '/component-hmr-lowercase-named-split': typeof ComponentHmrLowercaseNamedSplitRoute '/component-hmr-named-error-split': typeof ComponentHmrNamedErrorSplitRoute '/component-hmr-named-nosplit': typeof ComponentHmrNamedNosplitRoute '/component-hmr-named-split': typeof ComponentHmrNamedSplitRoute @@ -149,6 +159,7 @@ export interface FileRouteTypes { | '/component-hmr-inline-error-split' | '/component-hmr-inline-nosplit' | '/component-hmr-inline-split' + | '/component-hmr-lowercase-named-split' | '/component-hmr-named-error-split' | '/component-hmr-named-nosplit' | '/component-hmr-named-split' @@ -164,6 +175,7 @@ export interface FileRouteTypes { | '/component-hmr-inline-error-split' | '/component-hmr-inline-nosplit' | '/component-hmr-inline-split' + | '/component-hmr-lowercase-named-split' | '/component-hmr-named-error-split' | '/component-hmr-named-nosplit' | '/component-hmr-named-split' @@ -179,6 +191,7 @@ export interface FileRouteTypes { | '/component-hmr-inline-error-split' | '/component-hmr-inline-nosplit' | '/component-hmr-inline-split' + | '/component-hmr-lowercase-named-split' | '/component-hmr-named-error-split' | '/component-hmr-named-nosplit' | '/component-hmr-named-split' @@ -195,6 +208,7 @@ export interface RootRouteChildren { ComponentHmrInlineErrorSplitRoute: typeof ComponentHmrInlineErrorSplitRoute ComponentHmrInlineNosplitRoute: typeof ComponentHmrInlineNosplitRoute ComponentHmrInlineSplitRoute: typeof ComponentHmrInlineSplitRoute + ComponentHmrLowercaseNamedSplitRoute: typeof ComponentHmrLowercaseNamedSplitRoute ComponentHmrNamedErrorSplitRoute: typeof ComponentHmrNamedErrorSplitRoute ComponentHmrNamedNosplitRoute: typeof ComponentHmrNamedNosplitRoute ComponentHmrNamedSplitRoute: typeof ComponentHmrNamedSplitRoute @@ -247,6 +261,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ComponentHmrInlineSplitRouteImport parentRoute: typeof rootRouteImport } + '/component-hmr-lowercase-named-split': { + id: '/component-hmr-lowercase-named-split' + path: '/component-hmr-lowercase-named-split' + fullPath: '/component-hmr-lowercase-named-split' + preLoaderRoute: typeof ComponentHmrLowercaseNamedSplitRouteImport + parentRoute: typeof rootRouteImport + } '/component-hmr-named-error-split': { id: '/component-hmr-named-error-split' path: '/component-hmr-named-error-split' @@ -319,6 +340,7 @@ const rootRouteChildren: RootRouteChildren = { ComponentHmrInlineErrorSplitRoute: ComponentHmrInlineErrorSplitRoute, ComponentHmrInlineNosplitRoute: ComponentHmrInlineNosplitRoute, ComponentHmrInlineSplitRoute: ComponentHmrInlineSplitRoute, + ComponentHmrLowercaseNamedSplitRoute: ComponentHmrLowercaseNamedSplitRoute, ComponentHmrNamedErrorSplitRoute: ComponentHmrNamedErrorSplitRoute, ComponentHmrNamedNosplitRoute: ComponentHmrNamedNosplitRoute, ComponentHmrNamedSplitRoute: ComponentHmrNamedSplitRoute, diff --git a/e2e/react-start/hmr/src/routes/__root.tsx b/e2e/react-start/hmr/src/routes/__root.tsx index b2ec48fb70..22dddf1b3c 100644 --- a/e2e/react-start/hmr/src/routes/__root.tsx +++ b/e2e/react-start/hmr/src/routes/__root.tsx @@ -164,6 +164,12 @@ function RootContent() { label: 'Named Split', detail: 'Named component with default splitting', }, + { + testId: 'component-hmr-lowercase-named-split-link', + to: '/component-hmr-lowercase-named-split', + label: 'Lowercase Named Split', + detail: 'Lowercase named component with default splitting', + }, { testId: 'component-hmr-named-nosplit-link', to: '/component-hmr-named-nosplit', diff --git a/e2e/react-start/hmr/src/routes/component-hmr-lowercase-named-split.tsx b/e2e/react-start/hmr/src/routes/component-hmr-lowercase-named-split.tsx new file mode 100644 index 0000000000..957e14d7a4 --- /dev/null +++ b/e2e/react-start/hmr/src/routes/component-hmr-lowercase-named-split.tsx @@ -0,0 +1,26 @@ +import { createFileRoute } from '@tanstack/react-router' +import { useState } from 'react' + +export const Route = createFileRoute('/component-hmr-lowercase-named-split')({ + component, +}) + +function component() { + const [count, setCount] = useState(0) + + return ( +
+

+ component-hmr-lowercase-named-split-baseline +

+

Count: {count}

+ + +
+ ) +} diff --git a/e2e/react-start/hmr/tests/app.spec.ts b/e2e/react-start/hmr/tests/app.spec.ts index 10499e4b88..6c70843c04 100644 --- a/e2e/react-start/hmr/tests/app.spec.ts +++ b/e2e/react-start/hmr/tests/app.spec.ts @@ -11,6 +11,7 @@ import type { Page } from '@playwright/test' const whitelistErrors = [ 'Failed to load resource: net::ERR_NAME_NOT_RESOLVED', 'Failed to load resource: the server responded with a status of 504', + 'root loader error', ] const hmrExpect = expect.configure({ timeout: 20_000 }) @@ -24,6 +25,8 @@ const routeFilePaths = { componentHmrInlineSplit: 'routes/component-hmr-inline-split.tsx', componentHmrInlineNosplit: 'routes/component-hmr-inline-nosplit.tsx', componentHmrNamedSplit: 'routes/component-hmr-named-split.tsx', + componentHmrLowercaseNamedSplit: + 'routes/component-hmr-lowercase-named-split.tsx', componentHmrNamedNosplit: 'routes/component-hmr-named-nosplit.tsx', componentHmrInlineErrorSplit: 'routes/component-hmr-inline-error-split.tsx', componentHmrNamedErrorSplit: 'routes/component-hmr-named-error-split.tsx', @@ -99,6 +102,11 @@ const routeFileRestoreChecks: Partial< testId: 'component-hmr-marker', text: 'component-hmr-named-split-baseline', }, + componentHmrLowercaseNamedSplit: { + url: '/component-hmr-lowercase-named-split', + testId: 'component-hmr-marker', + text: 'component-hmr-lowercase-named-split-baseline', + }, componentHmrNamedNosplit: { url: '/component-hmr-named-nosplit', testId: 'component-hmr-marker', @@ -158,6 +166,16 @@ function normalizeRouteSource(routeFileKey: RouteFileKey, source: string) { ' component: RootComponent,', ) + next = next.replace( + ' shellComponent: shellComponent,\n component: RootContent,', + ' component: RootComponent,', + ) + + next = next.replace( + " loader: () => {\n throw new Error('root loader error')\n },\n errorComponent: errorComponent,\n component: RootComponent,", + " loader: () => ({\n crumb: 'Home',\n }),\n component: RootComponent,", + ) + for (const marker of ['root-shell-baseline', 'root-shell-updated']) { next = next.replace( `function RootShell({ children }: { children: ReactNode }) {\n return {children}\n}\n\nfunction Breadcrumbs() {`, @@ -165,6 +183,26 @@ function normalizeRouteSource(routeFileKey: RouteFileKey, source: string) { ) } + for (const marker of [ + 'root-shell-lowercase-baseline', + 'root-shell-lowercase-updated', + ]) { + next = next.replace( + `function shellComponent({ children }: { children: ReactNode }) {\n return {children}\n}\n\nfunction Breadcrumbs() {`, + 'function Breadcrumbs() {', + ) + } + + for (const marker of [ + 'root-error-lowercase-baseline', + 'root-error-lowercase-updated', + ]) { + next = next.replace( + `function errorComponent() {\n return (\n \n

${marker}

\n
\n )\n}\n\nfunction Breadcrumbs() {`, + 'function Breadcrumbs() {', + ) + } + next = replaceAll(next, "crumb: 'Home Updated'", "crumb: 'Home'") next = replaceAll(next, 'root-component-updated', 'root-component-baseline') next = replaceAll(next, 'root-shell-updated', 'root-shell-baseline') @@ -214,6 +252,10 @@ function normalizeRouteSource(routeFileKey: RouteFileKey, source: string) { 'component-hmr-named-split-updated', 'component-hmr-named-split-baseline', ], + componentHmrLowercaseNamedSplit: [ + 'component-hmr-lowercase-named-split-updated', + 'component-hmr-lowercase-named-split-baseline', + ], componentHmrNamedNosplit: [ 'component-hmr-named-nosplit-updated', 'component-hmr-named-nosplit-baseline', @@ -1032,6 +1074,89 @@ test.describe('react-start hmr', () => { ) }) + test('updates root route shellComponent during HMR when its function name is lowercase', async ({ + page, + }) => { + await rewriteRouteFile( + page, + 'root', + (source) => + source + .replace( + ' component: RootComponent,', + ' shellComponent: shellComponent,\n component: RootContent,', + ) + .replace( + 'function Breadcrumbs() {', + 'function shellComponent({ children }: { children: ReactNode }) {\n return {children}\n}\n\nfunction Breadcrumbs() {', + ), + async () => {}, + ) + await page.waitForTimeout(300) + + await reloadPageAndWaitForText( + page, + '/', + 'root-shell-marker', + 'root-shell-lowercase-baseline', + ) + await page.getByTestId('increment').click() + await page.getByTestId('message').fill('index preserved') + await page.getByTestId('root-message').fill('root preserved') + + await replaceRouteTextAndWait( + page, + 'root', + 'root-shell-lowercase-baseline', + 'root-shell-lowercase-updated', + async () => { + await hmrExpect(page.getByTestId('root-shell-marker')).toHaveText( + 'root-shell-lowercase-updated', + ) + }, + ) + + await expectHomeStatePreserved(page) + }) + + test('updates root route errorComponent during HMR when its function name is lowercase', async ({ + page, + }) => { + await seedHomeState(page) + + await rewriteRouteFile( + page, + 'root', + (source) => + source + .replace( + " loader: () => ({\n crumb: 'Home',\n }),\n component: RootComponent,", + " loader: () => {\n throw new Error('root loader error')\n },\n errorComponent: errorComponent,\n component: RootComponent,", + ) + .replace( + 'function Breadcrumbs() {', + 'function errorComponent() {\n return (\n \n

root-error-lowercase-baseline

\n
\n )\n}\n\nfunction Breadcrumbs() {', + ), + async () => { + await hmrExpect(page.getByTestId('root-error-marker')).toHaveText( + 'root-error-lowercase-baseline', + ) + }, + ) + + await replaceRouteTextAndWait( + page, + 'root', + 'root-error-lowercase-baseline', + 'root-error-lowercase-updated', + async () => { + await hmrExpect(page.getByTestId('root-error-marker')).toHaveText( + 'root-error-lowercase-updated', + ) + }, + ) + }) + test('updates non-root route component during HMR when component is defined inline and codeSplitGroupings is undefined', async ({ page, }) => { @@ -1092,6 +1217,26 @@ test.describe('react-start hmr', () => { await expectComponentHmrStatePreserved(page) }) + test('updates a code-split route component whose function name is lowercase', async ({ + page, + }) => { + await seedComponentHmrState(page, '/component-hmr-lowercase-named-split') + + await replaceRouteTextAndWait( + page, + 'componentHmrLowercaseNamedSplit', + 'component-hmr-lowercase-named-split-baseline', + 'component-hmr-lowercase-named-split-updated', + async () => { + await hmrExpect(page.getByTestId('component-hmr-marker')).toHaveText( + 'component-hmr-lowercase-named-split-updated', + ) + }, + ) + + await expectComponentHmrStatePreserved(page) + }) + test('updates non-root route component during HMR when component is not defined inline and codeSplitGroupings disables code splitting', async ({ page, }) => { diff --git a/packages/router-plugin/src/core/code-splitter/compilers.ts b/packages/router-plugin/src/core/code-splitter/compilers.ts index 9411165630..89cdae41ad 100644 --- a/packages/router-plugin/src/core/code-splitter/compilers.ts +++ b/packages/router-plugin/src/core/code-splitter/compilers.ts @@ -25,8 +25,8 @@ import { createRouteHmrStatement } from '../hmr' import { getObjectPropertyKeyName } from '../utils' import { getFrameworkOptions } from './framework-options' import type { + CodeSplitCompilerPlugin, CompileCodeSplitReferenceRouteOptions, - ReferenceRouteCompilerPlugin, } from './plugins' import type { GeneratorResult, ParseAstOptions } from '@tanstack/router-utils' import type { CodeSplitGroupings, SplitRouteIdentNodes } from '../constants' @@ -364,7 +364,7 @@ function removeSharedDeclarations(ast: t.File, sharedBindings: Set) { export function compileCodeSplitReferenceRoute( opts: ParseAstOptions & CompileCodeSplitReferenceRouteOptions & { - compilerPlugins?: Array + compilerPlugins?: Array }, ): GeneratorResult | null { const ast = parseAst(opts) @@ -879,6 +879,7 @@ export function compileCodeSplitVirtualRoute( splitTargets: Array filename: string sharedBindings?: Set + compilerPlugins?: Array }, ): GeneratorResult { const ast = parseAst(opts) @@ -1015,6 +1016,14 @@ export function compileCodeSplitVirtualRoute( // Add the node to the program if (splitNode) { + for (const plugin of opts.compilerPlugins ?? []) { + plugin.onVirtualRouteSplitNode?.({ + programPath, + splitNode, + splitNodeMeta: splitMeta, + }) + } + if (t.isFunctionDeclaration(splitNode)) { // an anonymous function declaration should only happen for `export default function() {...}` // so we should never get here @@ -1023,6 +1032,7 @@ export function compileCodeSplitVirtualRoute( `Function declaration for "${SPLIT_TYPE}" must have an identifier.`, ) } + splitMeta.shouldRemoveNode = false splitMeta.localExporterIdent = splitNode.id.name } else if ( diff --git a/packages/router-plugin/src/core/code-splitter/plugins.ts b/packages/router-plugin/src/core/code-splitter/plugins.ts index ebf1f4aa30..1e8ada32b5 100644 --- a/packages/router-plugin/src/core/code-splitter/plugins.ts +++ b/packages/router-plugin/src/core/code-splitter/plugins.ts @@ -40,7 +40,13 @@ export type ReferenceRouteCompilerPluginResult = { modified?: boolean } -export type ReferenceRouteCompilerPlugin = { +export type VirtualRouteSplitNodeCompilerPluginContext = { + programPath: babel.NodePath + splitNode: t.Node + splitNodeMeta: SplitNodeMeta +} + +export type CodeSplitCompilerPlugin = { name: string getStableRouteOptionKeys?: () => Array onRouteOptions?: ( @@ -55,4 +61,9 @@ export type ReferenceRouteCompilerPlugin = { onSplitRouteProperty?: ( ctx: ReferenceRouteSplitPropertyCompilerPluginContext, ) => void | t.Expression + onVirtualRouteSplitNode?: ( + ctx: VirtualRouteSplitNodeCompilerPluginContext, + ) => void } + +export type ReferenceRouteCompilerPlugin = CodeSplitCompilerPlugin diff --git a/packages/router-plugin/src/core/code-splitter/plugins/framework-plugins.ts b/packages/router-plugin/src/core/code-splitter/plugins/framework-plugins.ts index de8195853a..a0351ad210 100644 --- a/packages/router-plugin/src/core/code-splitter/plugins/framework-plugins.ts +++ b/packages/router-plugin/src/core/code-splitter/plugins/framework-plugins.ts @@ -1,27 +1,23 @@ import { createReactRefreshIgnoredRouteExportsPlugin } from './react-refresh-ignored-route-exports' import { createReactRefreshRouteComponentsPlugin } from './react-refresh-route-components' import { createReactStableHmrSplitRouteComponentsPlugin } from './react-stable-hmr-split-route-components' -import type { ReferenceRouteCompilerPlugin } from '../plugins' +import type { CodeSplitCompilerPlugin } from '../plugins' import type { Config, HmrStyle } from '../../config' -export function getReferenceRouteCompilerPlugins(opts: { +export function getFrameworkHmrCompilerPlugins(opts: { targetFramework: Config['target'] - addHmr?: boolean hmrStyle?: HmrStyle -}): Array | undefined { +}): Array | undefined { switch (opts.targetFramework) { case 'react': { - if (opts.addHmr) { - const hmrStyle = opts.hmrStyle ?? 'vite' - return [ - ...(hmrStyle === 'vite' - ? [createReactRefreshIgnoredRouteExportsPlugin()] - : []), - createReactRefreshRouteComponentsPlugin(), - createReactStableHmrSplitRouteComponentsPlugin({ hmrStyle }), - ] - } - return undefined + const hmrStyle = opts.hmrStyle ?? 'vite' + return [ + ...(hmrStyle === 'vite' + ? [createReactRefreshIgnoredRouteExportsPlugin()] + : []), + createReactRefreshRouteComponentsPlugin(), + createReactStableHmrSplitRouteComponentsPlugin({ hmrStyle }), + ] } default: return undefined diff --git a/packages/router-plugin/src/core/code-splitter/plugins/react-refresh-route-components.ts b/packages/router-plugin/src/core/code-splitter/plugins/react-refresh-route-components.ts index f7c4bca338..44041671e0 100644 --- a/packages/router-plugin/src/core/code-splitter/plugins/react-refresh-route-components.ts +++ b/packages/router-plugin/src/core/code-splitter/plugins/react-refresh-route-components.ts @@ -13,29 +13,74 @@ const REACT_REFRESH_ROUTE_COMPONENT_IDENTS = new Set([ 'notFoundComponent', ]) -function hoistInlineRouteComponents(ctx: { +type RouteComponentContext = { programPath: Parameters[0] insertionPath: { insertBefore: (nodes: Array) => void } routeOptions: t.ObjectExpression -}) { +} + +function isReactComponentName(name: string) { + const firstCharacter = name[0] + + return ( + firstCharacter !== undefined && + firstCharacter >= 'A' && + firstCharacter <= 'Z' + ) +} + +function getRouteComponentKey(prop: t.ObjectProperty) { + const key = getObjectPropertyKeyName(prop) + + return key && REACT_REFRESH_ROUTE_COMPONENT_IDENTS.has(key) ? key : undefined +} + +function prepareRouteComponentsForReactRefresh(ctx: RouteComponentContext) { const hoistedDeclarations: Array = [] + let modified = false - ctx.routeOptions.properties.forEach((prop) => { + for (const prop of ctx.routeOptions.properties) { if (!t.isObjectProperty(prop)) { - return + continue } - const key = getObjectPropertyKeyName(prop) + const key = getRouteComponentKey(prop) - if (!key || !REACT_REFRESH_ROUTE_COMPONENT_IDENTS.has(key)) { - return + if (!key) { + continue + } + + if (t.isIdentifier(prop.value)) { + if (isReactComponentName(prop.value.name)) { + continue + } + + const bindingNode = ctx.programPath.scope.getBinding(prop.value.name) + ?.path.node + const isLocalComponentBinding = + t.isFunctionDeclaration(bindingNode) || + t.isClassDeclaration(bindingNode) || + t.isVariableDeclarator(bindingNode) + + if (!isLocalComponentBinding) { + continue + } + + const componentIdentifier = getUniqueProgramIdentifier( + ctx.programPath, + `TSR${key[0]!.toUpperCase()}${key.slice(1)}`, + ) + + ctx.programPath.scope.rename(prop.value.name, componentIdentifier.name) + modified = true + continue } if ( !t.isArrowFunctionExpression(prop.value) && !t.isFunctionExpression(prop.value) ) { - return + continue } const hoistedIdentifier = getUniqueProgramIdentifier( @@ -50,14 +95,14 @@ function hoistInlineRouteComponents(ctx: { ) prop.value = t.cloneNode(hoistedIdentifier) - }) + modified = true + } - if (hoistedDeclarations.length === 0) { - return false + if (hoistedDeclarations.length > 0) { + ctx.insertionPath.insertBefore(hoistedDeclarations) } - ctx.insertionPath.insertBefore(hoistedDeclarations) - return true + return modified } export function createReactRefreshRouteComponentsPlugin(): ReferenceRouteCompilerPlugin { @@ -66,27 +111,32 @@ export function createReactRefreshRouteComponentsPlugin(): ReferenceRouteCompile getStableRouteOptionKeys() { return [...REACT_REFRESH_ROUTE_COMPONENT_IDENTS] }, - onUnsplittableRoute(ctx) { - if (!ctx.opts.addHmr) { - return - } - - if (hoistInlineRouteComponents(ctx)) { + onAddHmr(ctx) { + if (prepareRouteComponentsForReactRefresh(ctx)) { return { modified: true } } return }, - onAddHmr(ctx) { - if (!ctx.opts.addHmr) { + onVirtualRouteSplitNode(ctx) { + if ( + ctx.splitNodeMeta.splitStrategy !== 'lazyRouteComponent' || + !t.isFunctionDeclaration(ctx.splitNode) || + !ctx.splitNode.id || + isReactComponentName(ctx.splitNode.id.name) + ) { return } - if (hoistInlineRouteComponents(ctx)) { - return { modified: true } - } + const componentIdentifier = getUniqueProgramIdentifier( + ctx.programPath, + ctx.splitNodeMeta.localExporterIdent, + ) - return + ctx.programPath.scope.rename( + ctx.splitNode.id.name, + componentIdentifier.name, + ) }, } } diff --git a/packages/router-plugin/src/core/config.ts b/packages/router-plugin/src/core/config.ts index c0b47b9ef5..17c0dd08da 100644 --- a/packages/router-plugin/src/core/config.ts +++ b/packages/router-plugin/src/core/config.ts @@ -9,7 +9,7 @@ import type { RouteIds, } from '@tanstack/router-core' import type { CodeSplitGroupings } from './constants' -import type { ReferenceRouteCompilerPlugin } from './code-splitter/plugins' +import type { CodeSplitCompilerPlugin } from './code-splitter/plugins' export const splitGroupingsSchema = z .array( @@ -76,7 +76,7 @@ export type CodeSplittingOptions = { * Internal compiler plugins used by framework integrations. * @internal */ - compilerPlugins?: Array + compilerPlugins?: Array } export type HmrStyle = 'vite' | 'webpack' diff --git a/packages/router-plugin/src/core/router-code-splitter-plugin.ts b/packages/router-plugin/src/core/router-code-splitter-plugin.ts index 4b6af091cc..f1f75d4f3c 100644 --- a/packages/router-plugin/src/core/router-code-splitter-plugin.ts +++ b/packages/router-plugin/src/core/router-code-splitter-plugin.ts @@ -13,7 +13,7 @@ import { computeSharedBindings, detectCodeSplitGroupingsFromRoute, } from './code-splitter/compilers' -import { getReferenceRouteCompilerPlugins } from './code-splitter/plugins/framework-plugins' +import { getFrameworkHmrCompilerPlugins } from './code-splitter/plugins/framework-plugins' import { defaultCodeSplitGroupings, splitRouteIdentNodes, @@ -24,7 +24,8 @@ import { debug, normalizePath, routeFactoryCallCodeFilter } from './utils' import { createRouterPluginContext } from './router-plugin-context' import type { CodeSplitGroupings, SplitRouteIdentNodes } from './constants' import type { GetRoutesByFileMapResultValue } from '@tanstack/router-generator' -import type { Config } from './config' +import type { CodeSplitCompilerPlugin } from './code-splitter/plugins' +import type { Config, HmrStyle } from './config' import type { RouterPluginContext } from './router-plugin-context' import type { UnpluginFactory, @@ -83,6 +84,12 @@ export function createRouterCodeSplitterPlugin( ): ReturnType Config)> | undefined>> { let ROOT: string = process.cwd() let userConfig: Config + let addHmr: boolean + let hmrStyle: HmrStyle + let compilerPlugins: Array + let virtualRouteCompilerPlugins: Array + + let isProduction = process.env.NODE_ENV === 'production' function initUserConfig() { if (typeof options === 'function') { @@ -90,8 +97,22 @@ export function createRouterCodeSplitterPlugin( } else { userConfig = getConfig(options, ROOT) } + + addHmr = (userConfig.codeSplittingOptions?.addHmr ?? true) && !isProduction + hmrStyle = userConfig.plugin?.hmr?.style ?? 'vite' + compilerPlugins = [ + ...(addHmr + ? (getFrameworkHmrCompilerPlugins({ + targetFramework: userConfig.target, + hmrStyle, + }) ?? []) + : []), + ...(userConfig.codeSplittingOptions?.compilerPlugins ?? []), + ] + virtualRouteCompilerPlugins = compilerPlugins.filter( + (plugin) => plugin.onVirtualRouteSplitNode, + ) } - const isProduction = process.env.NODE_ENV === 'production' // Map from normalized route file path → set of shared binding names. // Populated by the reference compiler, consumed by virtual and shared compilers. const sharedBindingsMap = new Map>() @@ -159,10 +180,6 @@ export function createRouterCodeSplitterPlugin( sharedBindingsMap.delete(id) } - const addHmr = - (userConfig.codeSplittingOptions?.addHmr ?? true) && !isProduction - const hmrStyle = userConfig.plugin?.hmr?.style ?? 'vite' - const compiledReferenceRoute = compileCodeSplitReferenceRoute({ code, codeSplitGroupings: splitGroupings, @@ -176,14 +193,7 @@ export function createRouterCodeSplitterPlugin( hmrStyle, hmrRouteId: generatorNodeInfo.routeId, sharedBindings: sharedBindings.size > 0 ? sharedBindings : undefined, - compilerPlugins: [ - ...(getReferenceRouteCompilerPlugins({ - targetFramework: userConfig.target, - addHmr, - hmrStyle, - }) ?? []), - ...(userConfig.codeSplittingOptions?.compilerPlugins ?? []), - ], + compilerPlugins, }) if (compiledReferenceRoute === null) { @@ -232,6 +242,7 @@ export function createRouterCodeSplitterPlugin( filename: id, splitTargets: grouping, sharedBindings: resolvedSharedBindings, + compilerPlugins: virtualRouteCompilerPlugins, }) if (debug) { @@ -276,6 +287,7 @@ export function createRouterCodeSplitterPlugin( vite: { configResolved(config) { + isProduction = config.command === 'build' ROOT = config.root initUserConfig() @@ -319,12 +331,14 @@ export function createRouterCodeSplitterPlugin( }, }, - rspack() { + rspack(compiler) { + isProduction = compiler.options.mode === 'production' ROOT = process.cwd() initUserConfig() }, - webpack() { + webpack(compiler) { + isProduction = compiler.options.mode === 'production' ROOT = process.cwd() initUserConfig() }, diff --git a/packages/router-plugin/src/core/router-hmr-plugin.ts b/packages/router-plugin/src/core/router-hmr-plugin.ts index 75dfde11f8..e1dc01a43b 100644 --- a/packages/router-plugin/src/core/router-hmr-plugin.ts +++ b/packages/router-plugin/src/core/router-hmr-plugin.ts @@ -1,6 +1,6 @@ import { generateFromAst, logDiff, parseAst } from '@tanstack/router-utils' import { compileCodeSplitReferenceRoute } from './code-splitter/compilers' -import { getReferenceRouteCompilerPlugins } from './code-splitter/plugins/framework-plugins' +import { getFrameworkHmrCompilerPlugins } from './code-splitter/plugins/framework-plugins' import { createRouteHmrStatement } from './hmr' import { debug, normalizePath, routeFactoryCallCodeFilter } from './utils' import { getConfig } from './config' @@ -50,9 +50,8 @@ export function createRouterHmrPlugin( const hmrStyle = userConfig.plugin?.hmr?.style ?? 'vite' if (userConfig.target === 'react') { - const compilerPlugins = getReferenceRouteCompilerPlugins({ + const compilerPlugins = getFrameworkHmrCompilerPlugins({ targetFramework: 'react', - addHmr: true, hmrStyle, }) const compiled = compileCodeSplitReferenceRoute({ diff --git a/packages/router-plugin/src/index.ts b/packages/router-plugin/src/index.ts index e390c046a2..6a6ad158bb 100644 --- a/packages/router-plugin/src/index.ts +++ b/packages/router-plugin/src/index.ts @@ -13,8 +13,10 @@ export type { export type { RouterPluginContext } from './core/router-plugin-context' export { getObjectPropertyKeyName } from './core/utils' export type { + CodeSplitCompilerPlugin, ReferenceRouteCompilerPlugin, ReferenceRouteCompilerPluginContext, + VirtualRouteSplitNodeCompilerPluginContext, } from './core/code-splitter/plugins' export { tsrSplit, diff --git a/packages/router-plugin/tests/add-hmr.test.ts b/packages/router-plugin/tests/add-hmr.test.ts index 4430cef74c..52c2645409 100644 --- a/packages/router-plugin/tests/add-hmr.test.ts +++ b/packages/router-plugin/tests/add-hmr.test.ts @@ -4,7 +4,7 @@ import { describe, expect, it } from 'vitest' import { compileCodeSplitReferenceRoute } from '../src/core/code-splitter/compilers' import { defaultCodeSplitGroupings } from '../src/core/constants' -import { getReferenceRouteCompilerPlugins } from '../src/core/code-splitter/plugins/framework-plugins' +import { getFrameworkHmrCompilerPlugins } from '../src/core/code-splitter/plugins/framework-plugins' import { createRouteHmrStatement } from '../src/core/hmr' import { frameworks } from './constants' @@ -32,9 +32,8 @@ describe('add-hmr works', () => { addHmr: true, codeSplitGroupings: defaultCodeSplitGroupings, targetFramework: framework, - compilerPlugins: getReferenceRouteCompilerPlugins({ + compilerPlugins: getFrameworkHmrCompilerPlugins({ targetFramework: framework, - addHmr: true, }), }) @@ -57,10 +56,6 @@ describe('add-hmr works', () => { addHmr: false, codeSplitGroupings: defaultCodeSplitGroupings, targetFramework: framework, - compilerPlugins: getReferenceRouteCompilerPlugins({ - targetFramework: framework, - addHmr: false, - }), }) await expect(compileResult?.code || code).toMatchFileSnapshot( @@ -86,9 +81,8 @@ describe('add-hmr works', () => { hmrStyle: 'webpack', codeSplitGroupings: defaultCodeSplitGroupings, targetFramework: framework, - compilerPlugins: getReferenceRouteCompilerPlugins({ + compilerPlugins: getFrameworkHmrCompilerPlugins({ targetFramework: framework, - addHmr: true, hmrStyle: 'webpack', }), }) diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/createFileRoute-lowercase-components@false.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/createFileRoute-lowercase-components@false.tsx new file mode 100644 index 0000000000..a2983e31bd --- /dev/null +++ b/packages/router-plugin/tests/add-hmr/snapshots/react/createFileRoute-lowercase-components@false.tsx @@ -0,0 +1,12 @@ +const $$splitComponentImporter = () => import('createFileRoute-lowercase-components.tsx?tsr-split=component'); +const $$splitErrorComponentImporter = () => import('createFileRoute-lowercase-components.tsx?tsr-split=errorComponent'); +import { lazyRouteComponent } from '@tanstack/react-router'; +import { createFileRoute } from '@tanstack/react-router'; +export const Route = createFileRoute('/lowercase-components')({ + pendingComponent: pendingComponent, + errorComponent: lazyRouteComponent($$splitErrorComponentImporter, 'errorComponent'), + component: lazyRouteComponent($$splitComponentImporter, 'component') +}); +function pendingComponent() { + return
Pending
; +} \ No newline at end of file diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/createFileRoute-lowercase-components@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/createFileRoute-lowercase-components@true.tsx new file mode 100644 index 0000000000..b04afe83f4 --- /dev/null +++ b/packages/router-plugin/tests/add-hmr/snapshots/react/createFileRoute-lowercase-components@true.tsx @@ -0,0 +1,204 @@ +const $$splitComponentImporter = () => import('createFileRoute-lowercase-components.tsx?tsr-split=component'); +const $$splitErrorComponentImporter = () => import('createFileRoute-lowercase-components.tsx?tsr-split=errorComponent'); +import { lazyRouteComponent } from '@tanstack/react-router'; +import { createFileRoute } from '@tanstack/react-router'; +const TSRSplitErrorComponent = (() => { + const hot = import.meta.hot; + const hotData = hot ? hot.data ??= {} : undefined; + return hotData?.["tsr-split-component:errorComponent"] ?? lazyRouteComponent($$splitErrorComponentImporter, "errorComponent"); +})(); +if (import.meta.hot) { + (import.meta.hot.data ??= {})["tsr-split-component:errorComponent"] = TSRSplitErrorComponent; +} +const TSRSplitComponent = (() => { + const hot = import.meta.hot; + const hotData = hot ? hot.data ??= {} : undefined; + return hotData?.["tsr-split-component:component"] ?? lazyRouteComponent($$splitComponentImporter, "component"); +})(); +if (import.meta.hot) { + (import.meta.hot.data ??= {})["tsr-split-component:component"] = TSRSplitComponent; +} +export const Route = createFileRoute('/lowercase-components')({ + pendingComponent: TSRPendingComponent, + errorComponent: TSRSplitErrorComponent, + component: TSRSplitComponent +}); +function TSRPendingComponent() { + return
Pending
; +} +const hot = import.meta.hot; +if (hot && typeof window !== 'undefined') { + hot.data ??= {}; + const tsrReactRefresh = window.__TSR_REACT_REFRESH__ ??= (() => { + const ignoredExportsById = new Map(); + const previousGetIgnoredExports = window.__getReactRefreshIgnoredExports; + window.__getReactRefreshIgnoredExports = ctx => { + const ignoredExports = previousGetIgnoredExports?.(ctx) ?? []; + const moduleIgnored = ignoredExportsById.get(ctx.id) ?? []; + return [...ignoredExports, ...moduleIgnored]; + }; + return { + ignoredExportsById + }; + })(); + tsrReactRefresh.ignoredExportsById.set("createFileRoute-lowercase-components.tsx", ['Route']); +} +export function TSRFastRefreshAnchor() { + return null; +} +if (import.meta.hot) { + const hot = import.meta.hot; + const hotData = hot.data ??= {}; + const handleRouteUpdate = function handleRouteUpdate(routeId, newRoute) { + const router = window.__TSR_ROUTER__; + const oldRoute = router.routesById[routeId]; + if (!oldRoute) { + return; + } + ; + const generatedRouteOptionKeys = new Set(["id", "path", "getParentRoute"]); + const generatedRouteOptions = {}; + generatedRouteOptionKeys.forEach(key => { + if (key in oldRoute.options) { + generatedRouteOptions[key] = oldRoute.options[key]; + } + }); + const removedKeys = new Set(); + Object.keys(oldRoute.options).forEach(key => { + if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) { + removedKeys.add(key); + delete oldRoute.options[key]; + } + }); + const oldHasShellComponent = "shellComponent" in oldRoute.options; + const newHasShellComponent = "shellComponent" in newRoute.options; + const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent; + const componentKeys = ["component", "shellComponent", "pendingComponent", "errorComponent", "notFoundComponent"]; + if (preserveComponentIdentity) { + componentKeys.forEach(key => { + if (key in oldRoute.options && key in newRoute.options) { + newRoute.options[key] = oldRoute.options[key]; + } + }); + } + ; + const nextOptions = { + ...newRoute.options, + ...generatedRouteOptions + }; + oldRoute.options = nextOptions; + oldRoute.update(nextOptions); + oldRoute._componentsPromise = undefined; + oldRoute._lazyPromise = undefined; + router.setRoutes(router.buildRouteTree()); + syncHotRouteExport(oldRoute); + router.resolvePathCache.clear(); + const filter = m => m.routeId === oldRoute.id; + const activeMatch = router.stores.matches.get().find(filter); + const pendingMatch = router.stores.pendingMatches.get().find(filter); + const cachedMatches = router.stores.cachedMatches.get().filter(filter); + if (activeMatch || pendingMatch || cachedMatches.length > 0) { + if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) { + const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean); + router.batch(() => { + for (const matchId of matchIds) { + const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId); + if (store) { + store.set(prev => { + const next = { + ...prev + }; + if (removedKeys.has("loader")) { + next.loaderData = undefined; + } + ; + if (removedKeys.has("beforeLoad")) { + next.__beforeLoadContext = undefined; + next.context = rebuildMatchContextWithoutBeforeLoad(next); + } + ; + return next; + }); + } + } + }); + } + ; + router.invalidate({ + filter, + sync: true + }); + } + ; + function syncHotRouteExport(liveRoute) { + newRoute.options = liveRoute.options; + newRoute.parentRoute = liveRoute.parentRoute; + newRoute._path = liveRoute._path; + newRoute._id = liveRoute._id; + newRoute._fullPath = liveRoute._fullPath; + newRoute._to = liveRoute._to; + } + function getStoreMatch(matchId) { + return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get(); + } + function getMatchList(matchId) { + const pendingMatches = router.stores.pendingMatches.get(); + if (pendingMatches.some(match => match.id === matchId)) { + return pendingMatches; + } + ; + const activeMatches = router.stores.matches.get(); + if (activeMatches.some(match => match.id === matchId)) { + return activeMatches; + } + ; + const cachedMatches = router.stores.cachedMatches.get(); + if (cachedMatches.some(match => match.id === matchId)) { + return cachedMatches; + } + ; + return []; + } + function getParentMatch(match) { + const matchList = getMatchList(match.id); + const matchIndex = matchList.findIndex(item => item.id === match.id); + if (matchIndex <= 0) { + return undefined; + } + ; + const parentMatch = matchList[matchIndex - 1]; + return getStoreMatch(parentMatch.id) || parentMatch; + } + function rebuildMatchContextWithoutBeforeLoad(match) { + const parentMatch = getParentMatch(match); + const getParentContext = router.getParentContext; + const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context; + return { + ...(parentContext ?? {}), + ...(match.__routeContext ?? {}) + }; + } + }; + const initialRouteId = Route.id ?? hotData['tsr-route-id']; + if (initialRouteId) { + hotData['tsr-route-id'] = initialRouteId; + } + const existingRoute = typeof window !== 'undefined' && initialRouteId ? window.__TSR_ROUTER__?.routesById?.[initialRouteId] : undefined; + if (initialRouteId && existingRoute && existingRoute !== Route) { + handleRouteUpdate(initialRouteId, Route); + hotData['tsr-route-update-handled'] = Route; + } + hot.accept(newModule => { + if (Route && newModule && newModule.Route) { + const routeId = hotData['tsr-route-id'] ?? Route.id; + if (routeId) { + hotData['tsr-route-id'] = routeId; + } + if (hotData['tsr-route-update-handled'] === newModule.Route) { + delete hotData['tsr-route-update-handled']; + return; + } + handleRouteUpdate(routeId, newModule.Route); + } + }); +} \ No newline at end of file diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute-lowercase-components@false.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute-lowercase-components@false.tsx new file mode 100644 index 0000000000..20c9a07798 --- /dev/null +++ b/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute-lowercase-components@false.tsx @@ -0,0 +1,25 @@ +import * as React from 'react' +import { createRootRoute } from '@tanstack/react-router' + +export const Route = createRootRoute({ + shellComponent: shellComponent, + pendingComponent: pendingComponent, + errorComponent: errorComponent, + component: component, +}) + +function shellComponent({ children }: { children: React.ReactNode }) { + return {children} +} + +function pendingComponent() { + return
Pending
+} + +function errorComponent() { + return
Error
+} + +function component() { + return
Hello
+} diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute-lowercase-components@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute-lowercase-components@true.tsx new file mode 100644 index 0000000000..d3eae5cb36 --- /dev/null +++ b/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute-lowercase-components@true.tsx @@ -0,0 +1,200 @@ +import * as React from 'react'; +import { createRootRoute } from '@tanstack/react-router'; +export const Route = createRootRoute({ + shellComponent: TSRShellComponent, + pendingComponent: TSRPendingComponent, + errorComponent: TSRErrorComponent, + component: TSRComponent +}); +function TSRShellComponent({ + children +}: { + children: React.ReactNode; +}) { + return {children}; +} +function TSRPendingComponent() { + return
Pending
; +} +function TSRErrorComponent() { + return
Error
; +} +function TSRComponent() { + return
Hello
; +} +const hot = import.meta.hot; +if (hot && typeof window !== 'undefined') { + hot.data ??= {}; + const tsrReactRefresh = window.__TSR_REACT_REFRESH__ ??= (() => { + const ignoredExportsById = new Map(); + const previousGetIgnoredExports = window.__getReactRefreshIgnoredExports; + window.__getReactRefreshIgnoredExports = ctx => { + const ignoredExports = previousGetIgnoredExports?.(ctx) ?? []; + const moduleIgnored = ignoredExportsById.get(ctx.id) ?? []; + return [...ignoredExports, ...moduleIgnored]; + }; + return { + ignoredExportsById + }; + })(); + tsrReactRefresh.ignoredExportsById.set("createRootRoute-lowercase-components.tsx", ['Route']); +} +export function TSRFastRefreshAnchor() { + return null; +} +if (import.meta.hot) { + const hot = import.meta.hot; + const hotData = hot.data ??= {}; + const handleRouteUpdate = function handleRouteUpdate(routeId, newRoute) { + const router = window.__TSR_ROUTER__; + const oldRoute = router.routesById[routeId]; + if (!oldRoute) { + return; + } + ; + const generatedRouteOptionKeys = new Set(["id", "path", "getParentRoute"]); + const generatedRouteOptions = {}; + generatedRouteOptionKeys.forEach(key => { + if (key in oldRoute.options) { + generatedRouteOptions[key] = oldRoute.options[key]; + } + }); + const removedKeys = new Set(); + Object.keys(oldRoute.options).forEach(key => { + if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) { + removedKeys.add(key); + delete oldRoute.options[key]; + } + }); + const oldHasShellComponent = "shellComponent" in oldRoute.options; + const newHasShellComponent = "shellComponent" in newRoute.options; + const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent; + const componentKeys = ["component", "shellComponent", "pendingComponent", "errorComponent", "notFoundComponent"]; + if (preserveComponentIdentity) { + componentKeys.forEach(key => { + if (key in oldRoute.options && key in newRoute.options) { + newRoute.options[key] = oldRoute.options[key]; + } + }); + } + ; + const nextOptions = { + ...newRoute.options, + ...generatedRouteOptions + }; + oldRoute.options = nextOptions; + oldRoute.update(nextOptions); + oldRoute._componentsPromise = undefined; + oldRoute._lazyPromise = undefined; + router.setRoutes(router.buildRouteTree()); + syncHotRouteExport(oldRoute); + router.resolvePathCache.clear(); + const filter = m => m.routeId === oldRoute.id; + const activeMatch = router.stores.matches.get().find(filter); + const pendingMatch = router.stores.pendingMatches.get().find(filter); + const cachedMatches = router.stores.cachedMatches.get().filter(filter); + if (activeMatch || pendingMatch || cachedMatches.length > 0) { + if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) { + const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean); + router.batch(() => { + for (const matchId of matchIds) { + const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId); + if (store) { + store.set(prev => { + const next = { + ...prev + }; + if (removedKeys.has("loader")) { + next.loaderData = undefined; + } + ; + if (removedKeys.has("beforeLoad")) { + next.__beforeLoadContext = undefined; + next.context = rebuildMatchContextWithoutBeforeLoad(next); + } + ; + return next; + }); + } + } + }); + } + ; + router.invalidate({ + filter, + sync: true + }); + } + ; + function syncHotRouteExport(liveRoute) { + newRoute.options = liveRoute.options; + newRoute.parentRoute = liveRoute.parentRoute; + newRoute._path = liveRoute._path; + newRoute._id = liveRoute._id; + newRoute._fullPath = liveRoute._fullPath; + newRoute._to = liveRoute._to; + } + function getStoreMatch(matchId) { + return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get(); + } + function getMatchList(matchId) { + const pendingMatches = router.stores.pendingMatches.get(); + if (pendingMatches.some(match => match.id === matchId)) { + return pendingMatches; + } + ; + const activeMatches = router.stores.matches.get(); + if (activeMatches.some(match => match.id === matchId)) { + return activeMatches; + } + ; + const cachedMatches = router.stores.cachedMatches.get(); + if (cachedMatches.some(match => match.id === matchId)) { + return cachedMatches; + } + ; + return []; + } + function getParentMatch(match) { + const matchList = getMatchList(match.id); + const matchIndex = matchList.findIndex(item => item.id === match.id); + if (matchIndex <= 0) { + return undefined; + } + ; + const parentMatch = matchList[matchIndex - 1]; + return getStoreMatch(parentMatch.id) || parentMatch; + } + function rebuildMatchContextWithoutBeforeLoad(match) { + const parentMatch = getParentMatch(match); + const getParentContext = router.getParentContext; + const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context; + return { + ...(parentContext ?? {}), + ...(match.__routeContext ?? {}) + }; + } + }; + const initialRouteId = Route.id ?? hotData['tsr-route-id']; + if (initialRouteId) { + hotData['tsr-route-id'] = initialRouteId; + } + const existingRoute = typeof window !== 'undefined' && initialRouteId ? window.__TSR_ROUTER__?.routesById?.[initialRouteId] : undefined; + if (initialRouteId && existingRoute && existingRoute !== Route) { + handleRouteUpdate(initialRouteId, Route); + hotData['tsr-route-update-handled'] = Route; + } + hot.accept(newModule => { + if (Route && newModule && newModule.Route) { + const routeId = hotData['tsr-route-id'] ?? Route.id; + if (routeId) { + hotData['tsr-route-id'] = routeId; + } + if (hotData['tsr-route-update-handled'] === newModule.Route) { + delete hotData['tsr-route-update-handled']; + return; + } + handleRouteUpdate(routeId, newModule.Route); + } + }); +} \ No newline at end of file diff --git a/packages/router-plugin/tests/add-hmr/test-files/react/createFileRoute-lowercase-components.tsx b/packages/router-plugin/tests/add-hmr/test-files/react/createFileRoute-lowercase-components.tsx new file mode 100644 index 0000000000..a09f8d24e2 --- /dev/null +++ b/packages/router-plugin/tests/add-hmr/test-files/react/createFileRoute-lowercase-components.tsx @@ -0,0 +1,19 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/lowercase-components')({ + pendingComponent: pendingComponent, + errorComponent: errorComponent, + component: component, +}) + +function pendingComponent() { + return
Pending
+} + +function errorComponent() { + return
Error
+} + +function component() { + return
Hello
+} diff --git a/packages/router-plugin/tests/add-hmr/test-files/react/createRootRoute-lowercase-components.tsx b/packages/router-plugin/tests/add-hmr/test-files/react/createRootRoute-lowercase-components.tsx new file mode 100644 index 0000000000..20c9a07798 --- /dev/null +++ b/packages/router-plugin/tests/add-hmr/test-files/react/createRootRoute-lowercase-components.tsx @@ -0,0 +1,25 @@ +import * as React from 'react' +import { createRootRoute } from '@tanstack/react-router' + +export const Route = createRootRoute({ + shellComponent: shellComponent, + pendingComponent: pendingComponent, + errorComponent: errorComponent, + component: component, +}) + +function shellComponent({ children }: { children: React.ReactNode }) { + return {children} +} + +function pendingComponent() { + return
Pending
+} + +function errorComponent() { + return
Error
+} + +function component() { + return
Hello
+} diff --git a/packages/router-plugin/tests/code-splitter.test.ts b/packages/router-plugin/tests/code-splitter.test.ts index ae64e2778b..967f102e4d 100644 --- a/packages/router-plugin/tests/code-splitter.test.ts +++ b/packages/router-plugin/tests/code-splitter.test.ts @@ -20,6 +20,7 @@ import { } from '../src/core/code-splitter/compilers' import { createIdentifier } from '@tanstack/router-utils' import { defaultCodeSplitGroupings } from '../src/core/constants' +import { getFrameworkHmrCompilerPlugins } from '../src/core/code-splitter/plugins/framework-plugins' import { frameworks } from './constants' import type { CodeSplitGroupings } from '../src/core/constants' @@ -165,6 +166,62 @@ describe('code-splitter works', () => { }) }) +describe('React Refresh virtual route compiler plugin', () => { + const splitComponentCases = [ + { splitType: 'component', splitIdentifier: 'SplitComponent' }, + { + splitType: 'pendingComponent', + splitIdentifier: 'SplitPendingComponent', + }, + { splitType: 'errorComponent', splitIdentifier: 'SplitErrorComponent' }, + ] as const + + function createRouteCode(splitType: string) { + return ` +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/')({ ${splitType} }) + +function ${splitType}() { + return
lowercase function declaration
+} +` + } + + it.each(splitComponentCases)( + 'renames a lowercase $splitType binding when HMR is enabled', + ({ splitType, splitIdentifier }) => { + const result = compileCodeSplitVirtualRoute({ + code: createRouteCode(splitType), + filename: `lowercase-function-declaration.tsx?tsr-split=${splitType}`, + splitTargets: [splitType], + compilerPlugins: getFrameworkHmrCompilerPlugins({ + targetFramework: 'react', + }), + }) + + expect(result.code).toContain(`function ${splitIdentifier}()`) + expect(result.code).toContain( + `export { ${splitIdentifier} as ${splitType} }`, + ) + }, + ) + + it.each(splitComponentCases)( + 'keeps a lowercase $splitType binding when HMR is disabled', + ({ splitType }) => { + const result = compileCodeSplitVirtualRoute({ + code: createRouteCode(splitType), + filename: `lowercase-function-declaration.tsx?tsr-split=${splitType}`, + splitTargets: [splitType], + }) + + expect(result.code).toContain(`function ${splitType}()`) + expect(result.code).toContain(`export { ${splitType} }`) + }, + ) +}) + describe('computeSharedBindings fast paths', () => { it('returns empty when only one split group is present (default groupings)', () => { const code = ` diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/lowercase-function-declaration.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/lowercase-function-declaration.tsx new file mode 100644 index 0000000000..b2f908ab45 --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/lowercase-function-declaration.tsx @@ -0,0 +1,12 @@ +const $$splitErrorComponentImporter = () => import('lowercase-function-declaration.tsx?tsr-split=errorComponent'); +const $$splitComponentImporter = () => import('lowercase-function-declaration.tsx?tsr-split=component'); +import { lazyRouteComponent } from '@tanstack/react-router'; +import { createFileRoute } from '@tanstack/react-router'; +export const Route = createFileRoute('/')({ + component: lazyRouteComponent($$splitComponentImporter, 'component'), + pendingComponent, + errorComponent: lazyRouteComponent($$splitErrorComponentImporter, 'errorComponent') +}); +function pendingComponent() { + return
lowercase pending function declaration
; +} \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/lowercase-function-declaration@component.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/lowercase-function-declaration@component.tsx new file mode 100644 index 0000000000..65735a0f20 --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/lowercase-function-declaration@component.tsx @@ -0,0 +1,4 @@ +function component() { + return
lowercase function declaration
; +} +export { component }; \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/lowercase-function-declaration@errorComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/lowercase-function-declaration@errorComponent.tsx new file mode 100644 index 0000000000..acc1c0be8d --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/lowercase-function-declaration@errorComponent.tsx @@ -0,0 +1,4 @@ +function errorComponent() { + return
lowercase error function declaration
; +} +export { errorComponent }; \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/lowercase-function-declaration@notFoundComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/lowercase-function-declaration@notFoundComponent.tsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/lowercase-function-declaration@shared.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/1-default/lowercase-function-declaration@shared.tsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/lowercase-function-declaration.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/lowercase-function-declaration.tsx new file mode 100644 index 0000000000..75657cb44a --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/lowercase-function-declaration.tsx @@ -0,0 +1,10 @@ +const $$splitErrorComponentImporter = () => import('lowercase-function-declaration.tsx?tsr-split=component---errorComponent---notFoundComponent---pendingComponent'); +const $$splitPendingComponentImporter = () => import('lowercase-function-declaration.tsx?tsr-split=component---errorComponent---notFoundComponent---pendingComponent'); +const $$splitComponentImporter = () => import('lowercase-function-declaration.tsx?tsr-split=component---errorComponent---notFoundComponent---pendingComponent'); +import { lazyRouteComponent } from '@tanstack/react-router'; +import { createFileRoute } from '@tanstack/react-router'; +export const Route = createFileRoute('/')({ + component: lazyRouteComponent($$splitComponentImporter, 'component'), + pendingComponent: lazyRouteComponent($$splitPendingComponentImporter, 'pendingComponent'), + errorComponent: lazyRouteComponent($$splitErrorComponentImporter, 'errorComponent') +}); \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/lowercase-function-declaration@component---errorComponent---notFoundComponent---pendingComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/lowercase-function-declaration@component---errorComponent---notFoundComponent---pendingComponent.tsx new file mode 100644 index 0000000000..86876d616e --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/lowercase-function-declaration@component---errorComponent---notFoundComponent---pendingComponent.tsx @@ -0,0 +1,12 @@ +function component() { + return
lowercase function declaration
; +} +function pendingComponent() { + return
lowercase pending function declaration
; +} +function errorComponent() { + return
lowercase error function declaration
; +} +export { component }; +export { pendingComponent }; +export { errorComponent }; \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/lowercase-function-declaration@loader.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/lowercase-function-declaration@loader.tsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/lowercase-function-declaration@shared.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/2-components-combined-loader-separate/lowercase-function-declaration@shared.tsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/lowercase-function-declaration.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/lowercase-function-declaration.tsx new file mode 100644 index 0000000000..574c6d4c2b --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/lowercase-function-declaration.tsx @@ -0,0 +1,10 @@ +const $$splitErrorComponentImporter = () => import('lowercase-function-declaration.tsx?tsr-split=errorComponent'); +const $$splitPendingComponentImporter = () => import('lowercase-function-declaration.tsx?tsr-split=component---loader---notFoundComponent---pendingComponent'); +const $$splitComponentImporter = () => import('lowercase-function-declaration.tsx?tsr-split=component---loader---notFoundComponent---pendingComponent'); +import { lazyRouteComponent } from '@tanstack/react-router'; +import { createFileRoute } from '@tanstack/react-router'; +export const Route = createFileRoute('/')({ + component: lazyRouteComponent($$splitComponentImporter, 'component'), + pendingComponent: lazyRouteComponent($$splitPendingComponentImporter, 'pendingComponent'), + errorComponent: lazyRouteComponent($$splitErrorComponentImporter, 'errorComponent') +}); \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/lowercase-function-declaration@component---loader---notFoundComponent---pendingComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/lowercase-function-declaration@component---loader---notFoundComponent---pendingComponent.tsx new file mode 100644 index 0000000000..382621ea9a --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/lowercase-function-declaration@component---loader---notFoundComponent---pendingComponent.tsx @@ -0,0 +1,8 @@ +function component() { + return
lowercase function declaration
; +} +function pendingComponent() { + return
lowercase pending function declaration
; +} +export { component }; +export { pendingComponent }; \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/lowercase-function-declaration@errorComponent.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/lowercase-function-declaration@errorComponent.tsx new file mode 100644 index 0000000000..acc1c0be8d --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/lowercase-function-declaration@errorComponent.tsx @@ -0,0 +1,4 @@ +function errorComponent() { + return
lowercase error function declaration
; +} +export { errorComponent }; \ No newline at end of file diff --git a/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/lowercase-function-declaration@shared.tsx b/packages/router-plugin/tests/code-splitter/snapshots/react/3-all-combined-errorComponent-separate/lowercase-function-declaration@shared.tsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/router-plugin/tests/code-splitter/test-files/react/lowercase-function-declaration.tsx b/packages/router-plugin/tests/code-splitter/test-files/react/lowercase-function-declaration.tsx new file mode 100644 index 0000000000..bfc37b1d6a --- /dev/null +++ b/packages/router-plugin/tests/code-splitter/test-files/react/lowercase-function-declaration.tsx @@ -0,0 +1,19 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/')({ + component, + pendingComponent, + errorComponent, +}) + +function component() { + return
lowercase function declaration
+} + +function pendingComponent() { + return
lowercase pending function declaration
+} + +function errorComponent() { + return
lowercase error function declaration
+} diff --git a/packages/router-plugin/tests/router-plugin-context.test.ts b/packages/router-plugin/tests/router-plugin-context.test.ts index 40664c50a8..ecb6e7e167 100644 --- a/packages/router-plugin/tests/router-plugin-context.test.ts +++ b/packages/router-plugin/tests/router-plugin-context.test.ts @@ -1,8 +1,9 @@ import path from 'node:path' import * as t from '@babel/types' -import { describe, expect, it } from 'vitest' +import { describe, expect, it, vi } from 'vitest' import { parseAst } from '@tanstack/router-utils' import { createRouterCodeSplitterPlugin } from '../src/core/router-code-splitter-plugin' +import { unpluginRouterComposedFactory } from '../src/core/router-composed-plugin' import { createRouterHmrPlugin } from '../src/core/router-hmr-plugin' import { createRouterPluginContext } from '../src/core/router-plugin-context' import { normalizePath } from '../src/core/utils' @@ -11,19 +12,30 @@ import type { UnpluginOptions, TransformResult } from 'unplugin' const referencePluginName = 'tanstack-router:code-splitter:compile-reference-file' +const virtualPluginName = 'tanstack-router:code-splitter:compile-virtual-file' -function getReferencePlugin( +function getCodeSplitterPlugin( plugins: ReturnType, + pluginName: string, ): UnpluginOptions { const pluginArray = Array.isArray(plugins) ? plugins : [plugins] - const plugin = pluginArray.find((item) => item.name === referencePluginName) + const plugin = pluginArray.find((item) => item.name === pluginName) if (!plugin) { - throw new Error('Reference code-splitter plugin not found') + throw new Error(`Code-splitter plugin "${pluginName}" not found`) } return plugin } -async function configurePlugin(plugin: UnpluginOptions) { +function getReferencePlugin( + plugins: ReturnType, +) { + return getCodeSplitterPlugin(plugins, referencePluginName) +} + +async function configurePlugin( + plugin: UnpluginOptions, + command: 'serve' | 'build' = 'serve', +) { const hook = plugin.vite?.configResolved if (!hook) { return @@ -31,6 +43,7 @@ async function configurePlugin(plugin: UnpluginOptions) { const config = { root: process.cwd(), + command, plugins: [{ name: referencePluginName }], } as never @@ -77,6 +90,87 @@ function countProgramHotDeclarations(code: string) { } describe('router plugin context', () => { + it.each([ + { mode: 'production', production: true }, + { mode: 'development with addHmr disabled', production: false }, + ])( + 'does not run React HMR compiler plugins in $mode', + async ({ production }) => { + const routeFile = normalizePath( + path.join(process.cwd(), 'src/routes/lowercase.tsx'), + ) + const routeCode = ` +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/lowercase')({ component }) + +function component() { + return
Hello
+} +` + + const context = createRouterPluginContext() + context.routesByFile.set(routeFile, { routeId: '/lowercase' }) + + if (production) { + vi.stubEnv('NODE_ENV', 'production') + } + + try { + const plugins = createRouterCodeSplitterPlugin( + { + target: 'react', + autoCodeSplitting: true, + codeSplittingOptions: production ? undefined : { addHmr: false }, + }, + context, + ) + const referencePlugin = getReferencePlugin(plugins) + const virtualPlugin = getCodeSplitterPlugin(plugins, virtualPluginName) + + await configurePlugin(referencePlugin, production ? 'build' : 'serve') + + const referenceCode = getCode( + await transformReferenceRoute(referencePlugin, routeCode, routeFile), + ) + const virtualCode = getCode( + await transformReferenceRoute( + virtualPlugin, + routeCode, + `${routeFile}?tsr-split=component`, + ), + ) + + expect(referenceCode).not.toContain('TSRFastRefreshAnchor') + expect(virtualCode).toContain('function component()') + expect(virtualCode).toContain('export { component }') + expect(virtualCode).not.toContain('SplitComponent') + } finally { + if (production) { + vi.unstubAllEnvs() + } + } + }, + ) + + it('does not install the standalone route HMR plugin in production', () => { + vi.stubEnv('NODE_ENV', 'production') + + try { + const plugins = unpluginRouterComposedFactory( + { target: 'react', autoCodeSplitting: false }, + { framework: 'vite' }, + ) + + const pluginArray = Array.isArray(plugins) ? plugins : [plugins] + expect( + pluginArray.some((plugin) => plugin.name === 'tanstack-router:hmr'), + ).toBe(false) + } finally { + vi.unstubAllEnvs() + } + }) + it('keeps multiple code-splitter instances isolated by explicit context', async () => { const routeFile = normalizePath( path.join(process.cwd(), 'src/routes-a/owned.tsx'), From 49bd9fa4471ad5bbc6c2e629b29de66ae43d421a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 19 Jul 2026 16:29:16 +0200 Subject: [PATCH 23/51] ci: Version Packages (#7857) --- .changeset/fresh-routes-refresh.md | 5 ----- examples/react/authenticated-routes-firebase/package.json | 2 +- examples/react/authenticated-routes/package.json | 2 +- examples/react/basic-file-based/package.json | 2 +- examples/react/basic-react-query-file-based/package.json | 2 +- examples/react/basic-ssr-file-based/package.json | 2 +- .../react/basic-ssr-streaming-file-based/package.json | 2 +- examples/react/basic-virtual-file-based/package.json | 2 +- .../react/basic-virtual-inside-file-based/package.json | 2 +- examples/react/i18n-paraglide/package.json | 2 +- examples/react/kitchen-sink-file-based/package.json | 2 +- .../kitchen-sink-react-query-file-based/package.json | 2 +- examples/react/large-file-based/package.json | 2 +- examples/react/quickstart-esbuild-file-based/package.json | 2 +- examples/react/quickstart-file-based/package.json | 2 +- examples/react/quickstart-rspack-file-based/package.json | 2 +- examples/react/quickstart-webpack-file-based/package.json | 2 +- examples/react/router-monorepo-react-query/package.json | 2 +- .../packages/router/package.json | 2 +- examples/react/router-monorepo-simple-lazy/package.json | 2 +- .../packages/router/package.json | 2 +- examples/react/router-monorepo-simple/package.json | 2 +- .../router-monorepo-simple/packages/router/package.json | 2 +- examples/react/search-validator-adapters/package.json | 2 +- examples/react/start-bare/package.json | 2 +- examples/react/start-basic-auth/package.json | 2 +- examples/react/start-basic-authjs/package.json | 2 +- examples/react/start-basic-cloudflare/package.json | 2 +- examples/react/start-basic-react-query/package.json | 2 +- examples/react/start-basic-static/package.json | 2 +- examples/react/start-basic/package.json | 2 +- examples/react/start-bun/package.json | 4 ++-- examples/react/start-clerk-basic/package.json | 2 +- examples/react/start-convex-trellaux/package.json | 2 +- examples/react/start-counter/package.json | 2 +- examples/react/start-i18n-paraglide/package.json | 2 +- examples/react/start-large/package.json | 2 +- examples/react/start-material-ui/package.json | 2 +- examples/react/start-rscs/package.json | 2 +- .../package.json | 2 +- examples/react/start-supabase-basic/package.json | 2 +- examples/react/start-tailwind-v4/package.json | 2 +- examples/react/start-trellaux/package.json | 2 +- examples/react/start-workos/package.json | 2 +- examples/react/view-transitions/package.json | 2 +- examples/react/with-trpc-react-query/package.json | 2 +- examples/react/with-trpc/package.json | 2 +- examples/solid/authenticated-routes-firebase/package.json | 2 +- examples/solid/authenticated-routes/package.json | 2 +- examples/solid/basic-file-based/package.json | 2 +- examples/solid/basic-solid-query-file-based/package.json | 2 +- examples/solid/basic-solid-query/package.json | 2 +- examples/solid/basic-ssr-file-based/package.json | 2 +- .../solid/basic-ssr-streaming-file-based/package.json | 2 +- examples/solid/basic-virtual-file-based/package.json | 2 +- .../solid/basic-virtual-inside-file-based/package.json | 2 +- examples/solid/i18n-paraglide/package.json | 2 +- examples/solid/kitchen-sink-file-based/package.json | 2 +- .../kitchen-sink-solid-query-file-based/package.json | 2 +- examples/solid/large-file-based/package.json | 2 +- examples/solid/quickstart-esbuild-file-based/package.json | 2 +- examples/solid/quickstart-file-based/package.json | 2 +- examples/solid/quickstart-rspack-file-based/package.json | 2 +- examples/solid/quickstart-webpack-file-based/package.json | 2 +- examples/solid/router-monorepo-simple-lazy/package.json | 2 +- .../packages/router/package.json | 2 +- examples/solid/router-monorepo-simple/package.json | 2 +- .../router-monorepo-simple/packages/router/package.json | 2 +- examples/solid/router-monorepo-solid-query/package.json | 2 +- .../packages/router/package.json | 2 +- examples/solid/search-validator-adapters/package.json | 2 +- examples/solid/start-basic-auth/package.json | 2 +- examples/solid/start-basic-authjs/package.json | 2 +- examples/solid/start-basic-cloudflare/package.json | 2 +- examples/solid/start-basic-netlify/package.json | 2 +- examples/solid/start-basic-nitro/package.json | 2 +- examples/solid/start-basic-solid-query/package.json | 2 +- examples/solid/start-basic-static/package.json | 2 +- examples/solid/start-basic/package.json | 2 +- examples/solid/start-bun/package.json | 4 ++-- examples/solid/start-convex-better-auth/package.json | 2 +- examples/solid/start-counter/package.json | 2 +- examples/solid/start-i18n-paraglide/package.json | 2 +- examples/solid/start-large/package.json | 2 +- .../package.json | 2 +- examples/solid/start-supabase-basic/package.json | 2 +- examples/solid/start-tailwind-v4/package.json | 2 +- examples/solid/view-transitions/package.json | 2 +- examples/solid/with-trpc/package.json | 2 +- examples/vue/basic-file-based-jsx/package.json | 2 +- examples/vue/basic-file-based-sfc/package.json | 2 +- packages/react-start-rsc/CHANGELOG.md | 7 +++++++ packages/react-start-rsc/package.json | 2 +- packages/react-start/CHANGELOG.md | 8 ++++++++ packages/react-start/package.json | 2 +- packages/router-plugin/CHANGELOG.md | 6 ++++++ packages/router-plugin/package.json | 2 +- packages/router-vite-plugin/CHANGELOG.md | 7 +++++++ packages/router-vite-plugin/package.json | 2 +- packages/solid-start/CHANGELOG.md | 7 +++++++ packages/solid-start/package.json | 2 +- packages/start-plugin-core/CHANGELOG.md | 7 +++++++ packages/start-plugin-core/package.json | 2 +- packages/vue-start/CHANGELOG.md | 7 +++++++ packages/vue-start/package.json | 2 +- 105 files changed, 148 insertions(+), 104 deletions(-) delete mode 100644 .changeset/fresh-routes-refresh.md diff --git a/.changeset/fresh-routes-refresh.md b/.changeset/fresh-routes-refresh.md deleted file mode 100644 index 3c23631cd4..0000000000 --- a/.changeset/fresh-routes-refresh.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@tanstack/router-plugin': patch ---- - -Preserve React component state during HMR when route components are declared with lowercase function names. The development-only React transforms cover split route component groups and unsplit root route component options, including shell, pending, and error components, without changing production output. diff --git a/examples/react/authenticated-routes-firebase/package.json b/examples/react/authenticated-routes-firebase/package.json index c11599bf0b..b1b0b0fd54 100644 --- a/examples/react/authenticated-routes-firebase/package.json +++ b/examples/react/authenticated-routes-firebase/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "firebase": "^11.4.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/authenticated-routes/package.json b/examples/react/authenticated-routes/package.json index 89b8f7842c..c32706b8ad 100644 --- a/examples/react/authenticated-routes/package.json +++ b/examples/react/authenticated-routes/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-file-based/package.json b/examples/react/basic-file-based/package.json index 68b451b310..2e335dd749 100644 --- a/examples/react/basic-file-based/package.json +++ b/examples/react/basic-file-based/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-react-query-file-based/package.json b/examples/react/basic-react-query-file-based/package.json index c85ea7b083..762c842956 100644 --- a/examples/react/basic-react-query-file-based/package.json +++ b/examples/react/basic-react-query-file-based/package.json @@ -14,7 +14,7 @@ "@tanstack/react-query-devtools": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-ssr-file-based/package.json b/examples/react/basic-ssr-file-based/package.json index 20bdd7f437..2ec67334f6 100644 --- a/examples/react/basic-ssr-file-based/package.json +++ b/examples/react/basic-ssr-file-based/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "compression": "^1.8.0", "express": "^5.2.1", "get-port": "^7.1.0", diff --git a/examples/react/basic-ssr-streaming-file-based/package.json b/examples/react/basic-ssr-streaming-file-based/package.json index de2cfab761..1c138ed636 100644 --- a/examples/react/basic-ssr-streaming-file-based/package.json +++ b/examples/react/basic-ssr-streaming-file-based/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "compression": "^1.8.0", "express": "^5.2.1", "get-port": "^7.1.0", diff --git a/examples/react/basic-virtual-file-based/package.json b/examples/react/basic-virtual-file-based/package.json index b2009e135a..5e8e43e9af 100644 --- a/examples/react/basic-virtual-file-based/package.json +++ b/examples/react/basic-virtual-file-based/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "@tanstack/virtual-file-routes": "^1.162.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/basic-virtual-inside-file-based/package.json b/examples/react/basic-virtual-inside-file-based/package.json index 591551fd44..d53491b801 100644 --- a/examples/react/basic-virtual-inside-file-based/package.json +++ b/examples/react/basic-virtual-inside-file-based/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "@tanstack/virtual-file-routes": "^1.162.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/i18n-paraglide/package.json b/examples/react/i18n-paraglide/package.json index db9634854c..81565f3543 100644 --- a/examples/react/i18n-paraglide/package.json +++ b/examples/react/i18n-paraglide/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "react": "^19.1.1", "react-dom": "^19.1.1", "tailwindcss": "^4.2.2" diff --git a/examples/react/kitchen-sink-file-based/package.json b/examples/react/kitchen-sink-file-based/package.json index f256824590..3437d8efdf 100644 --- a/examples/react/kitchen-sink-file-based/package.json +++ b/examples/react/kitchen-sink-file-based/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "immer": "^10.1.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/kitchen-sink-react-query-file-based/package.json b/examples/react/kitchen-sink-react-query-file-based/package.json index 62bec46301..d10164584b 100644 --- a/examples/react/kitchen-sink-react-query-file-based/package.json +++ b/examples/react/kitchen-sink-react-query-file-based/package.json @@ -14,7 +14,7 @@ "@tanstack/react-query-devtools": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "immer": "^10.1.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/large-file-based/package.json b/examples/react/large-file-based/package.json index 07c6eb78e3..10fc569856 100644 --- a/examples/react/large-file-based/package.json +++ b/examples/react/large-file-based/package.json @@ -15,7 +15,7 @@ "@tanstack/react-query": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/quickstart-esbuild-file-based/package.json b/examples/react/quickstart-esbuild-file-based/package.json index 7e9f68e0a4..7ce6a0d7c2 100644 --- a/examples/react/quickstart-esbuild-file-based/package.json +++ b/examples/react/quickstart-esbuild-file-based/package.json @@ -11,7 +11,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/quickstart-file-based/package.json b/examples/react/quickstart-file-based/package.json index ee81b51092..bc66af46cc 100644 --- a/examples/react/quickstart-file-based/package.json +++ b/examples/react/quickstart-file-based/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/quickstart-rspack-file-based/package.json b/examples/react/quickstart-rspack-file-based/package.json index 8b3dc38116..263dda63d3 100644 --- a/examples/react/quickstart-rspack-file-based/package.json +++ b/examples/react/quickstart-rspack-file-based/package.json @@ -19,7 +19,7 @@ "devDependencies": { "@rsbuild/core": "^2.0.11", "@rsbuild/plugin-react": "^2.0.0", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "typescript": "^6.0.2" diff --git a/examples/react/quickstart-webpack-file-based/package.json b/examples/react/quickstart-webpack-file-based/package.json index c352ab4a88..ce712ac9f3 100644 --- a/examples/react/quickstart-webpack-file-based/package.json +++ b/examples/react/quickstart-webpack-file-based/package.json @@ -14,7 +14,7 @@ }, "devDependencies": { "@swc/core": "^1.15.33", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "html-webpack-plugin": "^5.6.3", diff --git a/examples/react/router-monorepo-react-query/package.json b/examples/react/router-monorepo-react-query/package.json index 913a60975b..2a9e2a606c 100644 --- a/examples/react/router-monorepo-react-query/package.json +++ b/examples/react/router-monorepo-react-query/package.json @@ -14,7 +14,7 @@ "@tanstack/react-query-devtools": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/router-monorepo-react-query/packages/router/package.json b/examples/react/router-monorepo-react-query/packages/router/package.json index af2fb7288c..88f21fec59 100644 --- a/examples/react/router-monorepo-react-query/packages/router/package.json +++ b/examples/react/router-monorepo-react-query/packages/router/package.json @@ -12,7 +12,7 @@ "@tanstack/history": "^1.162.0", "@tanstack/react-query": "^5.90.0", "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/router-monorepo-simple-lazy/package.json b/examples/react/router-monorepo-simple-lazy/package.json index a52d7b172e..a2144b4439 100644 --- a/examples/react/router-monorepo-simple-lazy/package.json +++ b/examples/react/router-monorepo-simple-lazy/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/router-monorepo-simple-lazy/packages/router/package.json b/examples/react/router-monorepo-simple-lazy/packages/router/package.json index 544a7d8ce6..67cfed8cd6 100644 --- a/examples/react/router-monorepo-simple-lazy/packages/router/package.json +++ b/examples/react/router-monorepo-simple-lazy/packages/router/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/history": "^1.162.0", "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/router-monorepo-simple/package.json b/examples/react/router-monorepo-simple/package.json index 7192cfa977..95b01f06a5 100644 --- a/examples/react/router-monorepo-simple/package.json +++ b/examples/react/router-monorepo-simple/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/router-monorepo-simple/packages/router/package.json b/examples/react/router-monorepo-simple/packages/router/package.json index b762f00016..015d01de61 100644 --- a/examples/react/router-monorepo-simple/packages/router/package.json +++ b/examples/react/router-monorepo-simple/packages/router/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/history": "^1.162.0", "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/search-validator-adapters/package.json b/examples/react/search-validator-adapters/package.json index 057205072d..251d39d596 100644 --- a/examples/react/search-validator-adapters/package.json +++ b/examples/react/search-validator-adapters/package.json @@ -15,7 +15,7 @@ "@tanstack/react-query": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "@tanstack/valibot-adapter": "^1.167.0", "@tanstack/zod-adapter": "^1.167.0", "arktype": "^2.1.7", diff --git a/examples/react/start-bare/package.json b/examples/react/start-bare/package.json index 4b408ddc2f..5a3d43e3c7 100644 --- a/examples/react/start-bare/package.json +++ b/examples/react/start-bare/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.31", + "@tanstack/react-start": "^1.168.32", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-basic-auth/package.json b/examples/react/start-basic-auth/package.json index 4de0348958..e98871304e 100644 --- a/examples/react/start-basic-auth/package.json +++ b/examples/react/start-basic-auth/package.json @@ -16,7 +16,7 @@ "@prisma/client": "^7.0.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.31", + "@tanstack/react-start": "^1.168.32", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-basic-authjs/package.json b/examples/react/start-basic-authjs/package.json index f278e98af9..2d150d2a13 100644 --- a/examples/react/start-basic-authjs/package.json +++ b/examples/react/start-basic-authjs/package.json @@ -13,7 +13,7 @@ "@auth/core": "^0.41.1", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.31", + "@tanstack/react-start": "^1.168.32", "react": "^19.0.0", "react-dom": "^19.0.0", "start-authjs": "^1.0.0", diff --git a/examples/react/start-basic-cloudflare/package.json b/examples/react/start-basic-cloudflare/package.json index df280a633f..664f449996 100644 --- a/examples/react/start-basic-cloudflare/package.json +++ b/examples/react/start-basic-cloudflare/package.json @@ -14,7 +14,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.31", + "@tanstack/react-start": "^1.168.32", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/examples/react/start-basic-react-query/package.json b/examples/react/start-basic-react-query/package.json index 5da0dccc61..51006733fb 100644 --- a/examples/react/start-basic-react-query/package.json +++ b/examples/react/start-basic-react-query/package.json @@ -15,7 +15,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.31", + "@tanstack/react-start": "^1.168.32", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-basic-static/package.json b/examples/react/start-basic-static/package.json index cf48965e48..9bb5be6c25 100644 --- a/examples/react/start-basic-static/package.json +++ b/examples/react/start-basic-static/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.31", + "@tanstack/react-start": "^1.168.32", "@tanstack/start-static-server-functions": "^1.167.19", "@vitejs/plugin-react": "^6.0.1", "react": "^19.0.0", diff --git a/examples/react/start-basic/package.json b/examples/react/start-basic/package.json index 789d9139d5..86d047a7cf 100644 --- a/examples/react/start-basic/package.json +++ b/examples/react/start-basic/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.31", + "@tanstack/react-start": "^1.168.32", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.6.0", diff --git a/examples/react/start-bun/package.json b/examples/react/start-bun/package.json index 38ad3bce44..c2463684e4 100644 --- a/examples/react/start-bun/package.json +++ b/examples/react/start-bun/package.json @@ -18,8 +18,8 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.31", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/react-start": "^1.168.32", + "@tanstack/router-plugin": "^1.168.23", "react": "^19.1.1", "react-dom": "^19.1.1", "tailwindcss": "^4.2.2" diff --git a/examples/react/start-clerk-basic/package.json b/examples/react/start-clerk-basic/package.json index 405391cc61..3b55631b43 100644 --- a/examples/react/start-clerk-basic/package.json +++ b/examples/react/start-clerk-basic/package.json @@ -13,7 +13,7 @@ "@clerk/tanstack-react-start": "^0.27.14", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.31", + "@tanstack/react-start": "^1.168.32", "@vitejs/plugin-react": "^6.0.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/start-convex-trellaux/package.json b/examples/react/start-convex-trellaux/package.json index ec8a0c2fa2..1292a1ab84 100644 --- a/examples/react/start-convex-trellaux/package.json +++ b/examples/react/start-convex-trellaux/package.json @@ -18,7 +18,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.31", + "@tanstack/react-start": "^1.168.32", "concurrently": "^8.2.2", "convex": "^1.19.0", "ky": "^1.7.4", diff --git a/examples/react/start-counter/package.json b/examples/react/start-counter/package.json index 1b47b9852b..4e47d36b1d 100644 --- a/examples/react/start-counter/package.json +++ b/examples/react/start-counter/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.31", + "@tanstack/react-start": "^1.168.32", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/examples/react/start-i18n-paraglide/package.json b/examples/react/start-i18n-paraglide/package.json index 2b90afc1a9..f4df4b1c73 100644 --- a/examples/react/start-i18n-paraglide/package.json +++ b/examples/react/start-i18n-paraglide/package.json @@ -12,7 +12,7 @@ "@tanstack/react-devtools": "^0.7.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.31", + "@tanstack/react-start": "^1.168.32", "react": "^19.1.1", "react-dom": "^19.1.1" }, diff --git a/examples/react/start-large/package.json b/examples/react/start-large/package.json index f373745dcc..3a074bcdc9 100644 --- a/examples/react/start-large/package.json +++ b/examples/react/start-large/package.json @@ -15,7 +15,7 @@ "@tanstack/react-query": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.31", + "@tanstack/react-start": "^1.168.32", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-material-ui/package.json b/examples/react/start-material-ui/package.json index e528e0fb8f..ffc392e847 100644 --- a/examples/react/start-material-ui/package.json +++ b/examples/react/start-material-ui/package.json @@ -17,7 +17,7 @@ "@mui/material": "6.4.7", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.31", + "@tanstack/react-start": "^1.168.32", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-rscs/package.json b/examples/react/start-rscs/package.json index 1b9a90d1f9..e27ccb6aea 100644 --- a/examples/react/start-rscs/package.json +++ b/examples/react/start-rscs/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.31", + "@tanstack/react-start": "^1.168.32", "dexie": "^4.0.10", "react": "^19.2.0", "react-dom": "^19.2.0", diff --git a/examples/react/start-streaming-data-from-server-functions/package.json b/examples/react/start-streaming-data-from-server-functions/package.json index c00048fd10..7f4ec5c9a6 100644 --- a/examples/react/start-streaming-data-from-server-functions/package.json +++ b/examples/react/start-streaming-data-from-server-functions/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.31", + "@tanstack/react-start": "^1.168.32", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-supabase-basic/package.json b/examples/react/start-supabase-basic/package.json index c358363110..578efbab92 100644 --- a/examples/react/start-supabase-basic/package.json +++ b/examples/react/start-supabase-basic/package.json @@ -17,7 +17,7 @@ "@supabase/supabase-js": "^2.48.1", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.31", + "@tanstack/react-start": "^1.168.32", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/start-tailwind-v4/package.json b/examples/react/start-tailwind-v4/package.json index 1d370b2c8c..f032589511 100644 --- a/examples/react/start-tailwind-v4/package.json +++ b/examples/react/start-tailwind-v4/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.31", + "@tanstack/react-start": "^1.168.32", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.6.0", diff --git a/examples/react/start-trellaux/package.json b/examples/react/start-trellaux/package.json index 67a76ae325..d63357d985 100644 --- a/examples/react/start-trellaux/package.json +++ b/examples/react/start-trellaux/package.json @@ -15,7 +15,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.31", + "@tanstack/react-start": "^1.168.32", "ky": "^1.7.4", "msw": "^2.7.0", "react": "^19.0.0", diff --git a/examples/react/start-workos/package.json b/examples/react/start-workos/package.json index ec99b81c50..91f966cb78 100644 --- a/examples/react/start-workos/package.json +++ b/examples/react/start-workos/package.json @@ -16,7 +16,7 @@ "@radix-ui/themes": "^3.3.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.31", + "@tanstack/react-start": "^1.168.32", "@workos/authkit-tanstack-react-start": "^0.5.0", "react": "^19.0.0", "react-dom": "^19.0.0" diff --git a/examples/react/view-transitions/package.json b/examples/react/view-transitions/package.json index 5ff7d5b04c..04c3a5dc81 100644 --- a/examples/react/view-transitions/package.json +++ b/examples/react/view-transitions/package.json @@ -12,7 +12,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/with-trpc-react-query/package.json b/examples/react/with-trpc-react-query/package.json index cae3ef999c..c230435543 100644 --- a/examples/react/with-trpc-react-query/package.json +++ b/examples/react/with-trpc-react-query/package.json @@ -15,7 +15,7 @@ "@tanstack/react-query-devtools": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "@trpc/client": "^11.4.3", "@trpc/server": "^11.4.3", "@trpc/tanstack-react-query": "^11.4.3", diff --git a/examples/react/with-trpc/package.json b/examples/react/with-trpc/package.json index 14f95fe1f3..3281bd02d4 100644 --- a/examples/react/with-trpc/package.json +++ b/examples/react/with-trpc/package.json @@ -13,7 +13,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "@trpc/client": "^11.4.3", "@trpc/server": "^11.4.3", "express": "^5.2.1", diff --git a/examples/solid/authenticated-routes-firebase/package.json b/examples/solid/authenticated-routes-firebase/package.json index d96020f657..39cbab3677 100644 --- a/examples/solid/authenticated-routes-firebase/package.json +++ b/examples/solid/authenticated-routes-firebase/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "firebase": "^11.4.0", diff --git a/examples/solid/authenticated-routes/package.json b/examples/solid/authenticated-routes/package.json index d6991e2b8b..4b0f71d795 100644 --- a/examples/solid/authenticated-routes/package.json +++ b/examples/solid/authenticated-routes/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "redaxios": "^0.5.1", diff --git a/examples/solid/basic-file-based/package.json b/examples/solid/basic-file-based/package.json index d3aa4ab8c3..5287319ec0 100644 --- a/examples/solid/basic-file-based/package.json +++ b/examples/solid/basic-file-based/package.json @@ -18,7 +18,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "typescript": "^6.0.2", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/examples/solid/basic-solid-query-file-based/package.json b/examples/solid/basic-solid-query-file-based/package.json index c41942917b..d2923c2d56 100644 --- a/examples/solid/basic-solid-query-file-based/package.json +++ b/examples/solid/basic-solid-query-file-based/package.json @@ -21,7 +21,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "typescript": "^6.0.2", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/examples/solid/basic-solid-query/package.json b/examples/solid/basic-solid-query/package.json index 756747e412..b344e5778e 100644 --- a/examples/solid/basic-solid-query/package.json +++ b/examples/solid/basic-solid-query/package.json @@ -19,7 +19,7 @@ "tailwindcss": "^4.2.2" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "typescript": "^6.0.2", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/examples/solid/basic-ssr-file-based/package.json b/examples/solid/basic-ssr-file-based/package.json index 22058c1745..53cbc0b6eb 100644 --- a/examples/solid/basic-ssr-file-based/package.json +++ b/examples/solid/basic-ssr-file-based/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@tanstack/solid-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "compression": "^1.8.0", "express": "^5.2.1", "get-port": "^7.1.0", diff --git a/examples/solid/basic-ssr-streaming-file-based/package.json b/examples/solid/basic-ssr-streaming-file-based/package.json index 51d14f543e..e991502566 100644 --- a/examples/solid/basic-ssr-streaming-file-based/package.json +++ b/examples/solid/basic-ssr-streaming-file-based/package.json @@ -24,7 +24,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "@types/express": "^5.0.6", "typescript": "^6.0.2", "vite": "^8.0.14", diff --git a/examples/solid/basic-virtual-file-based/package.json b/examples/solid/basic-virtual-file-based/package.json index dfcd178efe..e735eec2f4 100644 --- a/examples/solid/basic-virtual-file-based/package.json +++ b/examples/solid/basic-virtual-file-based/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@tanstack/virtual-file-routes": "^1.162.0", diff --git a/examples/solid/basic-virtual-inside-file-based/package.json b/examples/solid/basic-virtual-inside-file-based/package.json index d590916c05..b5f149c152 100644 --- a/examples/solid/basic-virtual-inside-file-based/package.json +++ b/examples/solid/basic-virtual-inside-file-based/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@tanstack/virtual-file-routes": "^1.162.0", diff --git a/examples/solid/i18n-paraglide/package.json b/examples/solid/i18n-paraglide/package.json index bfec40a2ec..b10932c863 100644 --- a/examples/solid/i18n-paraglide/package.json +++ b/examples/solid/i18n-paraglide/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/solid-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" }, diff --git a/examples/solid/kitchen-sink-file-based/package.json b/examples/solid/kitchen-sink-file-based/package.json index 6c57a27ab6..953c3432a5 100644 --- a/examples/solid/kitchen-sink-file-based/package.json +++ b/examples/solid/kitchen-sink-file-based/package.json @@ -19,7 +19,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "typescript": "^6.0.2", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/examples/solid/kitchen-sink-solid-query-file-based/package.json b/examples/solid/kitchen-sink-solid-query-file-based/package.json index e10ead122b..07e59de122 100644 --- a/examples/solid/kitchen-sink-solid-query-file-based/package.json +++ b/examples/solid/kitchen-sink-solid-query-file-based/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-query-devtools": "^5.90.0", "@tanstack/solid-router": "^1.170.18", diff --git a/examples/solid/large-file-based/package.json b/examples/solid/large-file-based/package.json index a1d240c37e..cb5e474a03 100644 --- a/examples/solid/large-file-based/package.json +++ b/examples/solid/large-file-based/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", diff --git a/examples/solid/quickstart-esbuild-file-based/package.json b/examples/solid/quickstart-esbuild-file-based/package.json index 8849f4a443..118afd0bab 100644 --- a/examples/solid/quickstart-esbuild-file-based/package.json +++ b/examples/solid/quickstart-esbuild-file-based/package.json @@ -9,7 +9,7 @@ "start": "dev" }, "dependencies": { - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "redaxios": "^0.5.1", diff --git a/examples/solid/quickstart-file-based/package.json b/examples/solid/quickstart-file-based/package.json index c2308fcf0c..4792ee2010 100644 --- a/examples/solid/quickstart-file-based/package.json +++ b/examples/solid/quickstart-file-based/package.json @@ -18,7 +18,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "typescript": "^6.0.2", "vite": "^8.0.14", "vite-plugin-solid": "^2.11.11" diff --git a/examples/solid/quickstart-rspack-file-based/package.json b/examples/solid/quickstart-rspack-file-based/package.json index 0290e9c77d..df94063b32 100644 --- a/examples/solid/quickstart-rspack-file-based/package.json +++ b/examples/solid/quickstart-rspack-file-based/package.json @@ -19,7 +19,7 @@ "@rsbuild/core": "^2.0.11", "@rsbuild/plugin-babel": "^1.1.2", "@rsbuild/plugin-solid": "^1.1.1", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "typescript": "^6.0.2" } } diff --git a/examples/solid/quickstart-webpack-file-based/package.json b/examples/solid/quickstart-webpack-file-based/package.json index a519e80916..661a7ceece 100644 --- a/examples/solid/quickstart-webpack-file-based/package.json +++ b/examples/solid/quickstart-webpack-file-based/package.json @@ -15,7 +15,7 @@ "devDependencies": { "@babel/core": "^7.28.5", "@babel/preset-typescript": "^7.27.1", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "babel-loader": "^10.0.0", "babel-preset-solid": "^1.9.10", "css-loader": "^7.1.2", diff --git a/examples/solid/router-monorepo-simple-lazy/package.json b/examples/solid/router-monorepo-simple-lazy/package.json index 130b94021e..8bae80e6c3 100644 --- a/examples/solid/router-monorepo-simple-lazy/package.json +++ b/examples/solid/router-monorepo-simple-lazy/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "solid-js": "^1.9.10", "redaxios": "^0.5.1" }, diff --git a/examples/solid/router-monorepo-simple-lazy/packages/router/package.json b/examples/solid/router-monorepo-simple-lazy/packages/router/package.json index f102e265cc..f2b477f0a0 100644 --- a/examples/solid/router-monorepo-simple-lazy/packages/router/package.json +++ b/examples/solid/router-monorepo-simple-lazy/packages/router/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/history": "^1.162.0", "@tanstack/solid-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "redaxios": "^0.5.1", "zod": "^4.4.3", "solid-js": "^1.9.10" diff --git a/examples/solid/router-monorepo-simple/package.json b/examples/solid/router-monorepo-simple/package.json index 494e9fb7f6..d45c47b06a 100644 --- a/examples/solid/router-monorepo-simple/package.json +++ b/examples/solid/router-monorepo-simple/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "solid-js": "^1.9.10", "redaxios": "^0.5.1" }, diff --git a/examples/solid/router-monorepo-simple/packages/router/package.json b/examples/solid/router-monorepo-simple/packages/router/package.json index afbb05b627..9df7595ae8 100644 --- a/examples/solid/router-monorepo-simple/packages/router/package.json +++ b/examples/solid/router-monorepo-simple/packages/router/package.json @@ -10,7 +10,7 @@ "dependencies": { "@tanstack/history": "^1.162.0", "@tanstack/solid-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "redaxios": "^0.5.1", "zod": "^4.4.3", "solid-js": "^1.9.10" diff --git a/examples/solid/router-monorepo-solid-query/package.json b/examples/solid/router-monorepo-solid-query/package.json index 63ab851a79..82be9960af 100644 --- a/examples/solid/router-monorepo-solid-query/package.json +++ b/examples/solid/router-monorepo-solid-query/package.json @@ -14,7 +14,7 @@ "@tanstack/solid-query-devtools": "^5.90.0", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "solid-js": "^1.9.10", "redaxios": "^0.5.1" }, diff --git a/examples/solid/router-monorepo-solid-query/packages/router/package.json b/examples/solid/router-monorepo-solid-query/packages/router/package.json index 0b0212a2d9..5511587ac4 100644 --- a/examples/solid/router-monorepo-solid-query/packages/router/package.json +++ b/examples/solid/router-monorepo-solid-query/packages/router/package.json @@ -11,7 +11,7 @@ "@tanstack/history": "^1.162.0", "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "@router-solid-mono-solid-query/post-query": "workspace:*", "redaxios": "^0.5.1", "zod": "^4.4.3", diff --git a/examples/solid/search-validator-adapters/package.json b/examples/solid/search-validator-adapters/package.json index 3c2f179f5d..3c63435630 100644 --- a/examples/solid/search-validator-adapters/package.json +++ b/examples/solid/search-validator-adapters/package.json @@ -15,7 +15,7 @@ "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "@tanstack/valibot-adapter": "^1.167.0", "@tanstack/zod-adapter": "^1.167.0", "arktype": "^2.1.7", diff --git a/examples/solid/start-basic-auth/package.json b/examples/solid/start-basic-auth/package.json index 4153fa4418..1cf7e6ca8e 100644 --- a/examples/solid/start-basic-auth/package.json +++ b/examples/solid/start-basic-auth/package.json @@ -16,7 +16,7 @@ "@prisma/client": "^7.0.0", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.31", + "@tanstack/solid-start": "^1.168.32", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-authjs/package.json b/examples/solid/start-basic-authjs/package.json index e530b96fcb..20dfc7b4c8 100644 --- a/examples/solid/start-basic-authjs/package.json +++ b/examples/solid/start-basic-authjs/package.json @@ -13,7 +13,7 @@ "@auth/core": "^0.41.1", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.31", + "@tanstack/solid-start": "^1.168.32", "solid-js": "^1.9.10", "start-authjs": "^1.0.0", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-cloudflare/package.json b/examples/solid/start-basic-cloudflare/package.json index 37301e7006..61ec997672 100644 --- a/examples/solid/start-basic-cloudflare/package.json +++ b/examples/solid/start-basic-cloudflare/package.json @@ -14,7 +14,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.31", + "@tanstack/solid-start": "^1.168.32", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-netlify/package.json b/examples/solid/start-basic-netlify/package.json index 26843023e5..300731993a 100644 --- a/examples/solid/start-basic-netlify/package.json +++ b/examples/solid/start-basic-netlify/package.json @@ -11,7 +11,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.31", + "@tanstack/solid-start": "^1.168.32", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-nitro/package.json b/examples/solid/start-basic-nitro/package.json index d852359c69..eb242e0cdc 100644 --- a/examples/solid/start-basic-nitro/package.json +++ b/examples/solid/start-basic-nitro/package.json @@ -11,7 +11,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.31", + "@tanstack/solid-start": "^1.168.32", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-solid-query/package.json b/examples/solid/start-basic-solid-query/package.json index fdd069531a..2ed9b408a0 100644 --- a/examples/solid/start-basic-solid-query/package.json +++ b/examples/solid/start-basic-solid-query/package.json @@ -15,7 +15,7 @@ "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@tanstack/solid-router-ssr-query": "^1.167.1", - "@tanstack/solid-start": "^1.168.31", + "@tanstack/solid-start": "^1.168.32", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-static/package.json b/examples/solid/start-basic-static/package.json index a8bb95890f..e378294c01 100644 --- a/examples/solid/start-basic-static/package.json +++ b/examples/solid/start-basic-static/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.31", + "@tanstack/solid-start": "^1.168.32", "@tanstack/start-static-server-functions": "^1.167.19", "redaxios": "^0.5.1", "solid-js": "^1.9.10", diff --git a/examples/solid/start-basic/package.json b/examples/solid/start-basic/package.json index 4970f83523..2edd8c3478 100644 --- a/examples/solid/start-basic/package.json +++ b/examples/solid/start-basic/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.31", + "@tanstack/solid-start": "^1.168.32", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-bun/package.json b/examples/solid/start-bun/package.json index 84736bc627..c04beb9bd4 100644 --- a/examples/solid/start-bun/package.json +++ b/examples/solid/start-bun/package.json @@ -18,8 +18,8 @@ "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@tanstack/solid-router-ssr-query": "^1.167.1", - "@tanstack/solid-start": "^1.168.31", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/solid-start": "^1.168.32", + "@tanstack/router-plugin": "^1.168.23", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" }, diff --git a/examples/solid/start-convex-better-auth/package.json b/examples/solid/start-convex-better-auth/package.json index 96ecf8b933..7d0d3b748c 100644 --- a/examples/solid/start-convex-better-auth/package.json +++ b/examples/solid/start-convex-better-auth/package.json @@ -15,7 +15,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.31", + "@tanstack/solid-start": "^1.168.32", "better-auth": "^1.6.19", "clsx": "^2.1.1", "convex": "^1.28.2", diff --git a/examples/solid/start-counter/package.json b/examples/solid/start-counter/package.json index 375cdd1a0d..55f0def296 100644 --- a/examples/solid/start-counter/package.json +++ b/examples/solid/start-counter/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.31", + "@tanstack/solid-start": "^1.168.32", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", diff --git a/examples/solid/start-i18n-paraglide/package.json b/examples/solid/start-i18n-paraglide/package.json index 601677de98..afa9366193 100644 --- a/examples/solid/start-i18n-paraglide/package.json +++ b/examples/solid/start-i18n-paraglide/package.json @@ -12,7 +12,7 @@ "@tanstack/solid-devtools": "^0.7.0", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.31", + "@tanstack/solid-start": "^1.168.32", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-large/package.json b/examples/solid/start-large/package.json index 882312e4ac..f5e06de43e 100644 --- a/examples/solid/start-large/package.json +++ b/examples/solid/start-large/package.json @@ -15,7 +15,7 @@ "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.31", + "@tanstack/solid-start": "^1.168.32", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", diff --git a/examples/solid/start-streaming-data-from-server-functions/package.json b/examples/solid/start-streaming-data-from-server-functions/package.json index 55245c5e12..c72208a3f3 100644 --- a/examples/solid/start-streaming-data-from-server-functions/package.json +++ b/examples/solid/start-streaming-data-from-server-functions/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.31", + "@tanstack/solid-start": "^1.168.32", "solid-js": "^1.9.10", "zod": "^4.4.3" }, diff --git a/examples/solid/start-supabase-basic/package.json b/examples/solid/start-supabase-basic/package.json index 6c54d45673..3e8c7c063a 100644 --- a/examples/solid/start-supabase-basic/package.json +++ b/examples/solid/start-supabase-basic/package.json @@ -16,7 +16,7 @@ "@supabase/supabase-js": "^2.48.1", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.31", + "@tanstack/solid-start": "^1.168.32", "solid-js": "^1.9.9", "redaxios": "^0.5.1" }, diff --git a/examples/solid/start-tailwind-v4/package.json b/examples/solid/start-tailwind-v4/package.json index ce261b3ff7..323ff0ecaf 100644 --- a/examples/solid/start-tailwind-v4/package.json +++ b/examples/solid/start-tailwind-v4/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.31", + "@tanstack/solid-start": "^1.168.32", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", "zod": "^4.4.3" diff --git a/examples/solid/view-transitions/package.json b/examples/solid/view-transitions/package.json index 87fb50ffd3..0ea0691e97 100644 --- a/examples/solid/view-transitions/package.json +++ b/examples/solid/view-transitions/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "redaxios": "^0.5.1", diff --git a/examples/solid/with-trpc/package.json b/examples/solid/with-trpc/package.json index 3ea2028de1..9bcca25387 100644 --- a/examples/solid/with-trpc/package.json +++ b/examples/solid/with-trpc/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@trpc/client": "^11.4.3", diff --git a/examples/vue/basic-file-based-jsx/package.json b/examples/vue/basic-file-based-jsx/package.json index 147ff341ee..d2de76f46f 100644 --- a/examples/vue/basic-file-based-jsx/package.json +++ b/examples/vue/basic-file-based-jsx/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "@tanstack/vue-router": "^1.170.17", "@tanstack/vue-router-devtools": "^1.167.0", "redaxios": "^0.5.1", diff --git a/examples/vue/basic-file-based-sfc/package.json b/examples/vue/basic-file-based-sfc/package.json index 9f6eda684e..3fba023a59 100644 --- a/examples/vue/basic-file-based-sfc/package.json +++ b/examples/vue/basic-file-based-sfc/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.22", + "@tanstack/router-plugin": "^1.168.23", "@tanstack/vue-router": "^1.170.17", "@tanstack/vue-router-devtools": "^1.167.0", "redaxios": "^0.5.1", diff --git a/packages/react-start-rsc/CHANGELOG.md b/packages/react-start-rsc/CHANGELOG.md index 3178fb4940..4a84a4e80d 100644 --- a/packages/react-start-rsc/CHANGELOG.md +++ b/packages/react-start-rsc/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/react-start-rsc +## 0.1.31 + +### Patch Changes + +- Updated dependencies []: + - @tanstack/start-plugin-core@1.171.24 + ## 0.1.30 ### Patch Changes diff --git a/packages/react-start-rsc/package.json b/packages/react-start-rsc/package.json index 44a3f16c6b..3bb45bd6c4 100644 --- a/packages/react-start-rsc/package.json +++ b/packages/react-start-rsc/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start-rsc", - "version": "0.1.30", + "version": "0.1.31", "description": "React Server Components support for TanStack Start", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-start/CHANGELOG.md b/packages/react-start/CHANGELOG.md index e63bb1209e..4ade979ee4 100644 --- a/packages/react-start/CHANGELOG.md +++ b/packages/react-start/CHANGELOG.md @@ -1,5 +1,13 @@ # @tanstack/react-start +## 1.168.32 + +### Patch Changes + +- Updated dependencies []: + - @tanstack/start-plugin-core@1.171.24 + - @tanstack/react-start-rsc@0.1.31 + ## 1.168.31 ### Patch Changes diff --git a/packages/react-start/package.json b/packages/react-start/package.json index 24a7c5b58d..954c64597e 100644 --- a/packages/react-start/package.json +++ b/packages/react-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start", - "version": "1.168.31", + "version": "1.168.32", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-plugin/CHANGELOG.md b/packages/router-plugin/CHANGELOG.md index 3cbf45a5dc..ba440a0ff0 100644 --- a/packages/router-plugin/CHANGELOG.md +++ b/packages/router-plugin/CHANGELOG.md @@ -1,5 +1,11 @@ # @tanstack/router-plugin +## 1.168.23 + +### Patch Changes + +- [#7855](https://github.com/TanStack/router/pull/7855) [`49f6863`](https://github.com/TanStack/router/commit/49f686324574ea65851317cbba084eb386a911c6) - Preserve React component state during HMR when route components are declared with lowercase function names. The development-only React transforms cover split route component groups and unsplit root route component options, including shell, pending, and error components, without changing production output. + ## 1.168.22 ### Patch Changes diff --git a/packages/router-plugin/package.json b/packages/router-plugin/package.json index 36a92be67b..dfefe55a45 100644 --- a/packages/router-plugin/package.json +++ b/packages/router-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-plugin", - "version": "1.168.22", + "version": "1.168.23", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-vite-plugin/CHANGELOG.md b/packages/router-vite-plugin/CHANGELOG.md index 79eb4fb014..2cbfc55ef4 100644 --- a/packages/router-vite-plugin/CHANGELOG.md +++ b/packages/router-vite-plugin/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/router-vite-plugin +## 1.167.23 + +### Patch Changes + +- Updated dependencies [[`49f6863`](https://github.com/TanStack/router/commit/49f686324574ea65851317cbba084eb386a911c6)]: + - @tanstack/router-plugin@1.168.23 + ## 1.167.22 ### Patch Changes diff --git a/packages/router-vite-plugin/package.json b/packages/router-vite-plugin/package.json index c6f3ef2b77..5fa57a8cc2 100644 --- a/packages/router-vite-plugin/package.json +++ b/packages/router-vite-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-vite-plugin", - "version": "1.167.22", + "version": "1.167.23", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/solid-start/CHANGELOG.md b/packages/solid-start/CHANGELOG.md index 218f86a421..8a43a6c5f9 100644 --- a/packages/solid-start/CHANGELOG.md +++ b/packages/solid-start/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/solid-start +## 1.168.32 + +### Patch Changes + +- Updated dependencies []: + - @tanstack/start-plugin-core@1.171.24 + ## 1.168.31 ### Patch Changes diff --git a/packages/solid-start/package.json b/packages/solid-start/package.json index 70b265ae94..2a8c1dafc2 100644 --- a/packages/solid-start/package.json +++ b/packages/solid-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-start", - "version": "1.168.31", + "version": "1.168.32", "description": "Modern and scalable routing for Solid applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-plugin-core/CHANGELOG.md b/packages/start-plugin-core/CHANGELOG.md index 6fb57bccc7..3cc5088ed3 100644 --- a/packages/start-plugin-core/CHANGELOG.md +++ b/packages/start-plugin-core/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/start-plugin-core +## 1.171.24 + +### Patch Changes + +- Updated dependencies [[`49f6863`](https://github.com/TanStack/router/commit/49f686324574ea65851317cbba084eb386a911c6)]: + - @tanstack/router-plugin@1.168.23 + ## 1.171.23 ### Patch Changes diff --git a/packages/start-plugin-core/package.json b/packages/start-plugin-core/package.json index 8479116586..30f2e700e0 100644 --- a/packages/start-plugin-core/package.json +++ b/packages/start-plugin-core/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-plugin-core", - "version": "1.171.23", + "version": "1.171.24", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/vue-start/CHANGELOG.md b/packages/vue-start/CHANGELOG.md index eb1db3fd04..dd4a7e7233 100644 --- a/packages/vue-start/CHANGELOG.md +++ b/packages/vue-start/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/vue-start +## 1.168.31 + +### Patch Changes + +- Updated dependencies []: + - @tanstack/start-plugin-core@1.171.24 + ## 1.168.30 ### Patch Changes diff --git a/packages/vue-start/package.json b/packages/vue-start/package.json index 10215ef5d9..9c9c59b245 100644 --- a/packages/vue-start/package.json +++ b/packages/vue-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/vue-start", - "version": "1.168.30", + "version": "1.168.31", "description": "Modern and scalable routing for Vue applications", "author": "Tanner Linsley", "license": "MIT", From edf55759d8e9e5e7954945fdda2ee81c48e8c713 Mon Sep 17 00:00:00 2001 From: Manuel Schiller <6340397+schiller-manuel@users.noreply.github.com> Date: Sun, 19 Jul 2026 22:34:06 +0200 Subject: [PATCH 24/51] ci: omit unchanged bundle size scenarios (#7856) --- benchmarks/bundle-size/package.json | 11 +- scripts/benchmarks/bundle-size/pr-report.mjs | 64 ++++++--- .../benchmarks/bundle-size/pr-report.test.mjs | 131 ++++++++++++++++++ 3 files changed, 184 insertions(+), 22 deletions(-) create mode 100644 scripts/benchmarks/bundle-size/pr-report.test.mjs diff --git a/benchmarks/bundle-size/package.json b/benchmarks/bundle-size/package.json index 8cd8f73f41..65d8d7483b 100644 --- a/benchmarks/bundle-size/package.json +++ b/benchmarks/bundle-size/package.json @@ -3,7 +3,8 @@ "private": true, "type": "module", "scripts": { - "build": "node ../../scripts/benchmarks/bundle-size/measure.mjs --skip-package-builds" + "build": "node ../../scripts/benchmarks/bundle-size/measure.mjs --skip-package-builds", + "test:unit": "node --test ../../scripts/benchmarks/bundle-size/pr-report.test.mjs" }, "nx": { "targets": { @@ -23,6 +24,14 @@ "target": "build" } ] + }, + "test:unit": { + "inputs": [ + "default", + "^production", + "{workspaceRoot}/scripts/benchmarks/bundle-size/pr-report.mjs", + "{workspaceRoot}/scripts/benchmarks/bundle-size/pr-report.test.mjs" + ] } } }, diff --git a/scripts/benchmarks/bundle-size/pr-report.mjs b/scripts/benchmarks/bundle-size/pr-report.mjs index 5118d877ec..8c58563230 100644 --- a/scripts/benchmarks/bundle-size/pr-report.mjs +++ b/scripts/benchmarks/bundle-size/pr-report.mjs @@ -268,10 +268,16 @@ async function main() { ? resolveBaselineFromCurrentJson(baselineCurrent) : resolveBaselineFromHistory(historyEntries, args.baseSha) + const metrics = current.metrics || [] const rows = [] - for (const metric of current.metrics || []) { + for (const metric of metrics) { const baselineValue = baseline.benchesByName.get(metric.id) + + if (Number.isFinite(baselineValue) && metric.gzipBytes === baselineValue) { + continue + } + const historySeries = (seriesByScenario.get(metric.id) || []).slice( // Reserve one slot for the current metric so the sparkline stays at trendPoints. -args.trendPoints + 1, @@ -290,6 +296,7 @@ async function main() { raw: metric.rawBytes, brotli: metric.brotliBytes, initial: metric.initialGzipBytes, + hasBaseline: Number.isFinite(baselineValue), deltaCell: formatDelta(metric.gzipBytes, baselineValue), trendCell: sparkline(historySeries.slice(-args.trendPoints)), }) @@ -299,30 +306,45 @@ async function main() { lines.push(args.marker) lines.push('## Bundle Size Benchmarks') lines.push('') - lines.push(`- Commit: \`${formatShortSha(current.sha)}\``) - lines.push( - `- Measured at: \`${current.measuredAt || current.generatedAt || 'unknown'}\``, - ) - lines.push(`- Baseline source: \`${baseline.source}\``) - if (args.dashboardUrl) { - lines.push(`- Dashboard: [bundle-size history](${args.dashboardUrl})`) - } - lines.push('') - lines.push( - '| Scenario | Current (gzip) | Delta vs baseline | Initial gzip | Raw | Brotli | Trend |', - ) - lines.push('| --- | ---: | ---: | ---: | ---: | ---: | --- |') - for (const row of rows) { + if (metrics.length > 0 && rows.length === 0) { lines.push( - `| \`${row.id}\` | ${formatBytes(row.current)} | ${row.deltaCell} | ${formatBytes(row.initial)} | ${formatBytes(row.raw)} | ${formatBytes(row.brotli)} | ${row.trendCell} |`, + 'This pull request does not affect bundle size in any measured scenario.', ) - } + } else if (metrics.length === 0) { + lines.push('No bundle-size scenarios were measured.') + } else { + lines.push(`- Commit: \`${formatShortSha(current.sha)}\``) + lines.push( + `- Measured at: \`${current.measuredAt || current.generatedAt || 'unknown'}\``, + ) + lines.push(`- Baseline source: \`${baseline.source}\``) + if (args.dashboardUrl) { + lines.push(`- Dashboard: [bundle-size history](${args.dashboardUrl})`) + } + lines.push('') + lines.push( + rows.some((row) => !row.hasBaseline) + ? 'The following scenarios have bundle-size changes or lack baseline data for comparison:' + : 'The following scenarios have bundle-size changes compared with the baseline:', + ) + lines.push('') + lines.push( + '| Scenario | Current (gzip) | Delta vs baseline | Initial gzip | Raw | Brotli | Trend |', + ) + lines.push('| --- | ---: | ---: | ---: | ---: | ---: | --- |') - lines.push('') - lines.push( - '_Current gzip tracks all emitted client JS chunks. Initial gzip tracks only the entry/import graph. Trend sparkline is historical current gzip ending with this PR measurement; lower is better._', - ) + for (const row of rows) { + lines.push( + `| \`${row.id}\` | ${formatBytes(row.current)} | ${row.deltaCell} | ${formatBytes(row.initial)} | ${formatBytes(row.raw)} | ${formatBytes(row.brotli)} | ${row.trendCell} |`, + ) + } + + lines.push('') + lines.push( + '_Current gzip tracks all emitted client JS chunks. Initial gzip tracks only the entry/import graph. Trend sparkline is historical current gzip ending with this PR measurement; lower is better._', + ) + } const markdown = lines.join('\n') + '\n' await fsp.mkdir(path.dirname(outputPath), { recursive: true }) diff --git a/scripts/benchmarks/bundle-size/pr-report.test.mjs b/scripts/benchmarks/bundle-size/pr-report.test.mjs new file mode 100644 index 0000000000..f70081452d --- /dev/null +++ b/scripts/benchmarks/bundle-size/pr-report.test.mjs @@ -0,0 +1,131 @@ +import assert from 'node:assert/strict' +import { execFile } from 'node:child_process' +import { promises as fs } from 'node:fs' +import os from 'node:os' +import path from 'node:path' +import { promisify } from 'node:util' +import test from 'node:test' + +const execFileAsync = promisify(execFile) +const reportScript = new URL('./pr-report.mjs', import.meta.url) + +function metric(id, gzipBytes) { + return { + id, + gzipBytes, + rawBytes: gzipBytes * 3, + brotliBytes: gzipBytes - 100, + initialGzipBytes: gzipBytes - 10, + } +} + +async function generateReport({ current, baseline, history, baseSha }) { + const fixtureDir = await fs.mkdtemp( + path.join(os.tmpdir(), 'bundle-size-pr-report-'), + ) + + try { + const currentPath = path.join(fixtureDir, 'current.json') + const outputPath = path.join(fixtureDir, 'report.md') + const args = [ + reportScript.pathname, + '--current', + currentPath, + '--output', + outputPath, + ] + + await fs.writeFile(currentPath, JSON.stringify(current)) + + if (baseline) { + const baselinePath = path.join(fixtureDir, 'baseline.json') + await fs.writeFile(baselinePath, JSON.stringify(baseline)) + args.push('--baseline', baselinePath) + } + + if (history) { + const historyPath = path.join(fixtureDir, 'history.json') + await fs.writeFile(historyPath, JSON.stringify(history)) + args.push('--history', historyPath) + } + + if (baseSha) { + args.push('--base-sha', baseSha) + } + + await execFileAsync(process.execPath, args) + return await fs.readFile(outputPath, 'utf8') + } finally { + await fs.rm(fixtureDir, { recursive: true, force: true }) + } +} + +function currentJson(metrics) { + return { + benchmarkName: 'Bundle Size (gzip)', + measuredAt: '2026-07-19T09:45:56.787Z', + sha: '126aa13f296b1234', + metrics, + } +} + +test('renders a concise message when no measured scenario changed', async () => { + const current = currentJson([metric('react-router.minimal', 1_000)]) + const report = await generateReport({ current, baseline: current }) + + assert.equal( + report, + '\n## Bundle Size Benchmarks\n\nThis pull request does not affect bundle size in any measured scenario.\n', + ) +}) + +test('renders only scenarios that changed against the historical baseline', async () => { + const current = currentJson([ + metric('react-router.minimal', 1_000), + metric('react-router.full', 1_200), + ]) + const history = { + entries: { + [current.benchmarkName]: [ + { + commit: { id: 'baseline-sha' }, + benches: [ + { name: 'react-router.minimal', value: 1_000 }, + { name: 'react-router.full', value: 1_100 }, + ], + }, + ], + }, + } + const report = await generateReport({ + current, + history, + baseSha: 'baseline-sha', + }) + + assert.match( + report, + /The following scenarios have bundle-size changes compared with the baseline:/, + ) + assert.doesNotMatch(report, /`react-router\.minimal`/) + assert.match( + report, + /\| `react-router\.full` \| 1\.17 KiB \| \+100 B \(\+9\.09%\) \|/, + ) +}) + +test('keeps scenarios that do not have baseline data visible', async () => { + const current = currentJson([ + metric('react-router.minimal', 1_000), + metric('new-scenario', 900), + ]) + const baseline = currentJson([metric('react-router.minimal', 1_000)]) + const report = await generateReport({ current, baseline }) + + assert.match( + report, + /The following scenarios have bundle-size changes or lack baseline data for comparison:/, + ) + assert.doesNotMatch(report, /`react-router\.minimal`/) + assert.match(report, /\| `new-scenario` \| 900 B \| n\/a \|/) +}) From 07ce90304dde21ac5304369752bcdb2573fcc55e Mon Sep 17 00:00:00 2001 From: Zakher Masri <46135573+zaaakher@users.noreply.github.com> Date: Tue, 21 Jul 2026 21:45:50 +0300 Subject: [PATCH 25/51] docs: remove stray semicolon in i18n page (#7870) fix: remove stray semicolon --- docs/router/guide/internationalization-i18n.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/router/guide/internationalization-i18n.md b/docs/router/guide/internationalization-i18n.md index f6fd04087e..d5147ba6bf 100644 --- a/docs/router/guide/internationalization-i18n.md +++ b/docs/router/guide/internationalization-i18n.md @@ -187,7 +187,8 @@ export default { ```tsx import { getLocale } from '../paraglide/runtime' -; + + ``` --- From 21a859795208d52ce69147e3a6ed3a3c33b1f8bd Mon Sep 17 00:00:00 2001 From: Cong-Cong Pan Date: Thu, 23 Jul 2026 04:09:03 +0800 Subject: [PATCH 26/51] chore: add React Start basic Rsbuild example (#7729) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- .../router/guide/internationalization-i18n.md | 3 +- docs/start/framework/react/getting-started.md | 1 + .../.devcontainer/devcontainer.json | 3 + examples/react/start-basic-rsbuild/.gitignore | 19 + .../react/start-basic-rsbuild/.prettierignore | 4 + .../start-basic-rsbuild/.vscode/settings.json | 11 + examples/react/start-basic-rsbuild/README.md | 36 ++ .../react/start-basic-rsbuild/package.json | 32 ++ .../public/android-chrome-192x192.png | Bin 0 -> 29964 bytes .../public/android-chrome-512x512.png | Bin 0 -> 109271 bytes .../public/apple-touch-icon.png | Bin 0 -> 27246 bytes .../public/favicon-16x16.png | Bin 0 -> 832 bytes .../public/favicon-32x32.png | Bin 0 -> 2115 bytes .../start-basic-rsbuild/public/favicon.ico | Bin 0 -> 15406 bytes .../start-basic-rsbuild/public/favicon.png | Bin 0 -> 1507 bytes .../public/site.webmanifest | 19 + .../start-basic-rsbuild/rsbuild.config.ts | 17 + .../src/components/DefaultCatchBoundary.tsx | 51 ++ .../src/components/NotFound.tsx | 25 + .../src/components/PostError.tsx | 6 + .../src/components/UserError.tsx | 6 + .../react/start-basic-rsbuild/src/env.d.ts | 1 + .../start-basic-rsbuild/src/routeTree.gen.ts | 447 ++++++++++++++++++ .../react/start-basic-rsbuild/src/router.tsx | 15 + .../start-basic-rsbuild/src/routes/__root.tsx | 130 +++++ .../src/routes/_pathlessLayout.tsx | 16 + .../routes/_pathlessLayout/_nested-layout.tsx | 34 ++ .../_nested-layout/route-a.tsx | 11 + .../_nested-layout/route-b.tsx | 11 + .../src/routes/api/users.$userId.ts | 31 ++ .../src/routes/api/users.ts | 63 +++ .../src/routes/customScript[.]js.ts | 15 + .../src/routes/deferred.tsx | 62 +++ .../start-basic-rsbuild/src/routes/index.tsx | 13 + .../src/routes/posts.$postId.tsx | 34 ++ .../src/routes/posts.index.tsx | 9 + .../start-basic-rsbuild/src/routes/posts.tsx | 38 ++ .../src/routes/posts_.$postId.deep.tsx | 29 ++ .../src/routes/redirect.tsx | 9 + .../src/routes/users.$userId.tsx | 45 ++ .../src/routes/users.index.tsx | 19 + .../start-basic-rsbuild/src/routes/users.tsx | 49 ++ .../start-basic-rsbuild/src/styles/app.css | 30 ++ .../src/utils/loggingMiddleware.tsx | 41 ++ .../start-basic-rsbuild/src/utils/posts.tsx | 40 ++ .../start-basic-rsbuild/src/utils/seo.ts | 33 ++ .../start-basic-rsbuild/src/utils/users.tsx | 5 + .../react/start-basic-rsbuild/tsconfig.json | 21 + pnpm-lock.yaml | 281 ++++++++++- 49 files changed, 1756 insertions(+), 9 deletions(-) create mode 100644 examples/react/start-basic-rsbuild/.devcontainer/devcontainer.json create mode 100644 examples/react/start-basic-rsbuild/.gitignore create mode 100644 examples/react/start-basic-rsbuild/.prettierignore create mode 100644 examples/react/start-basic-rsbuild/.vscode/settings.json create mode 100644 examples/react/start-basic-rsbuild/README.md create mode 100644 examples/react/start-basic-rsbuild/package.json create mode 100644 examples/react/start-basic-rsbuild/public/android-chrome-192x192.png create mode 100644 examples/react/start-basic-rsbuild/public/android-chrome-512x512.png create mode 100644 examples/react/start-basic-rsbuild/public/apple-touch-icon.png create mode 100644 examples/react/start-basic-rsbuild/public/favicon-16x16.png create mode 100644 examples/react/start-basic-rsbuild/public/favicon-32x32.png create mode 100644 examples/react/start-basic-rsbuild/public/favicon.ico create mode 100644 examples/react/start-basic-rsbuild/public/favicon.png create mode 100644 examples/react/start-basic-rsbuild/public/site.webmanifest create mode 100644 examples/react/start-basic-rsbuild/rsbuild.config.ts create mode 100644 examples/react/start-basic-rsbuild/src/components/DefaultCatchBoundary.tsx create mode 100644 examples/react/start-basic-rsbuild/src/components/NotFound.tsx create mode 100644 examples/react/start-basic-rsbuild/src/components/PostError.tsx create mode 100644 examples/react/start-basic-rsbuild/src/components/UserError.tsx create mode 100644 examples/react/start-basic-rsbuild/src/env.d.ts create mode 100644 examples/react/start-basic-rsbuild/src/routeTree.gen.ts create mode 100644 examples/react/start-basic-rsbuild/src/router.tsx create mode 100644 examples/react/start-basic-rsbuild/src/routes/__root.tsx create mode 100644 examples/react/start-basic-rsbuild/src/routes/_pathlessLayout.tsx create mode 100644 examples/react/start-basic-rsbuild/src/routes/_pathlessLayout/_nested-layout.tsx create mode 100644 examples/react/start-basic-rsbuild/src/routes/_pathlessLayout/_nested-layout/route-a.tsx create mode 100644 examples/react/start-basic-rsbuild/src/routes/_pathlessLayout/_nested-layout/route-b.tsx create mode 100644 examples/react/start-basic-rsbuild/src/routes/api/users.$userId.ts create mode 100644 examples/react/start-basic-rsbuild/src/routes/api/users.ts create mode 100644 examples/react/start-basic-rsbuild/src/routes/customScript[.]js.ts create mode 100644 examples/react/start-basic-rsbuild/src/routes/deferred.tsx create mode 100644 examples/react/start-basic-rsbuild/src/routes/index.tsx create mode 100644 examples/react/start-basic-rsbuild/src/routes/posts.$postId.tsx create mode 100644 examples/react/start-basic-rsbuild/src/routes/posts.index.tsx create mode 100644 examples/react/start-basic-rsbuild/src/routes/posts.tsx create mode 100644 examples/react/start-basic-rsbuild/src/routes/posts_.$postId.deep.tsx create mode 100644 examples/react/start-basic-rsbuild/src/routes/redirect.tsx create mode 100644 examples/react/start-basic-rsbuild/src/routes/users.$userId.tsx create mode 100644 examples/react/start-basic-rsbuild/src/routes/users.index.tsx create mode 100644 examples/react/start-basic-rsbuild/src/routes/users.tsx create mode 100644 examples/react/start-basic-rsbuild/src/styles/app.css create mode 100644 examples/react/start-basic-rsbuild/src/utils/loggingMiddleware.tsx create mode 100644 examples/react/start-basic-rsbuild/src/utils/posts.tsx create mode 100644 examples/react/start-basic-rsbuild/src/utils/seo.ts create mode 100644 examples/react/start-basic-rsbuild/src/utils/users.tsx create mode 100644 examples/react/start-basic-rsbuild/tsconfig.json diff --git a/docs/router/guide/internationalization-i18n.md b/docs/router/guide/internationalization-i18n.md index d5147ba6bf..f6fd04087e 100644 --- a/docs/router/guide/internationalization-i18n.md +++ b/docs/router/guide/internationalization-i18n.md @@ -187,8 +187,7 @@ export default { ```tsx import { getLocale } from '../paraglide/runtime' - - +; ``` --- diff --git a/docs/start/framework/react/getting-started.md b/docs/start/framework/react/getting-started.md index 565dc4b4cf..0320fdab4e 100644 --- a/docs/start/framework/react/getting-started.md +++ b/docs/start/framework/react/getting-started.md @@ -39,6 +39,7 @@ To use a different example, replace `start-basic` with one of the slugs below. #### Examples - [Basic](https://github.com/TanStack/router/tree/main/examples/react/start-basic) (`start-basic`) +- [Basic + Rsbuild](https://github.com/TanStack/router/tree/main/examples/react/start-basic-rsbuild) (`start-basic-rsbuild`) - [Basic + Auth](https://github.com/TanStack/router/tree/main/examples/react/start-basic-auth) (`start-basic-auth`) - [Counter](https://github.com/TanStack/router/tree/main/examples/react/start-counter) (`start-counter`) - [Basic + React Query](https://github.com/TanStack/router/tree/main/examples/react/start-basic-react-query) (`start-basic-react-query`) diff --git a/examples/react/start-basic-rsbuild/.devcontainer/devcontainer.json b/examples/react/start-basic-rsbuild/.devcontainer/devcontainer.json new file mode 100644 index 0000000000..e5dafce188 --- /dev/null +++ b/examples/react/start-basic-rsbuild/.devcontainer/devcontainer.json @@ -0,0 +1,3 @@ +{ + "image": "mcr.microsoft.com/devcontainers/typescript-node:24" +} diff --git a/examples/react/start-basic-rsbuild/.gitignore b/examples/react/start-basic-rsbuild/.gitignore new file mode 100644 index 0000000000..1f189d9bc0 --- /dev/null +++ b/examples/react/start-basic-rsbuild/.gitignore @@ -0,0 +1,19 @@ +node_modules +package-lock.json +yarn.lock + +.DS_Store +.cache +.env +.vercel +dist +/build/ +/api/ +/server/build +/public/build# Sentry Config File +.env.sentry-build-plugin +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ +.tanstack diff --git a/examples/react/start-basic-rsbuild/.prettierignore b/examples/react/start-basic-rsbuild/.prettierignore new file mode 100644 index 0000000000..2be5eaa6ec --- /dev/null +++ b/examples/react/start-basic-rsbuild/.prettierignore @@ -0,0 +1,4 @@ +**/build +**/public +pnpm-lock.yaml +routeTree.gen.ts \ No newline at end of file diff --git a/examples/react/start-basic-rsbuild/.vscode/settings.json b/examples/react/start-basic-rsbuild/.vscode/settings.json new file mode 100644 index 0000000000..00b5278e58 --- /dev/null +++ b/examples/react/start-basic-rsbuild/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "files.watcherExclude": { + "**/routeTree.gen.ts": true + }, + "search.exclude": { + "**/routeTree.gen.ts": true + }, + "files.readonlyInclude": { + "**/routeTree.gen.ts": true + } +} diff --git a/examples/react/start-basic-rsbuild/README.md b/examples/react/start-basic-rsbuild/README.md new file mode 100644 index 0000000000..64117c915b --- /dev/null +++ b/examples/react/start-basic-rsbuild/README.md @@ -0,0 +1,36 @@ +# TanStack Start - Basic Rsbuild Example + +This is the basic TanStack Start Rsbuild example, demonstrating the fundamentals of building applications with TanStack Router and TanStack Start. + +- [TanStack Router Docs](https://tanstack.com/router) + +It's deployed automagically with Netlify! + +- [Netlify](https://netlify.com/) + +## Start a new project based on this example + +To start a new project based on this example, run: + +```sh +npx gitpick TanStack/router/tree/main/examples/react/start-basic-rsbuild start-basic-rsbuild +``` + +## Getting Started + +From your terminal: + +```sh +pnpm install +pnpm dev +``` + +This starts your app in development mode, rebuilding assets on file changes. + +## Build + +To build the app for production: + +```sh +pnpm build +``` diff --git a/examples/react/start-basic-rsbuild/package.json b/examples/react/start-basic-rsbuild/package.json new file mode 100644 index 0000000000..2bbd7496e4 --- /dev/null +++ b/examples/react/start-basic-rsbuild/package.json @@ -0,0 +1,32 @@ +{ + "name": "tanstack-start-example-basic-rsbuild", + "private": true, + "sideEffects": false, + "type": "module", + "scripts": { + "dev": "rsbuild dev --port 3000", + "build": "rsbuild build && tsc --noEmit", + "preview": "rsbuild preview", + "start": "srvx --prod -s ../client dist/server/index.js" + }, + "dependencies": { + "@tanstack/react-router": "workspace:*", + "@tanstack/react-router-devtools": "workspace:^", + "@tanstack/react-start": "workspace:*", + "react": "^19.2.3", + "react-dom": "^19.2.3", + "tailwind-merge": "^3.6.0", + "zod": "^4.4.3" + }, + "devDependencies": { + "@rsbuild/core": "^2.0.11", + "@rsbuild/plugin-react": "^2.0.0", + "@rsbuild/plugin-tailwindcss": "^2.0.3", + "@types/node": "25.0.9", + "@types/react": "^19.2.8", + "@types/react-dom": "^19.2.3", + "srvx": "^0.11.18", + "tailwindcss": "^4.2.2", + "typescript": "^6.0.2" + } +} diff --git a/examples/react/start-basic-rsbuild/public/android-chrome-192x192.png b/examples/react/start-basic-rsbuild/public/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..09c8324f8c6781bc90fcf5dd38e0702bd5f171f3 GIT binary patch literal 29964 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 b=FG literal 0 HcmV?d00001 diff --git a/examples/react/start-basic-rsbuild/public/android-chrome-512x512.png b/examples/react/start-basic-rsbuild/public/android-chrome-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..11d626ea3d00fddd52861bf0af5554a92fc30d69 GIT binary patch literal 109271 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 Ef@mE(_W%F@ literal 0 HcmV?d00001 diff --git a/examples/react/start-basic-rsbuild/public/apple-touch-icon.png b/examples/react/start-basic-rsbuild/public/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5a9423cc02c40ea066d2a061cdcdf621c0718cb1 GIT binary patch literal 27246 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⪼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 N002ovPDHLkV1k;v<;Va4 literal 0 HcmV?d00001 diff --git a/examples/react/start-basic-rsbuild/public/favicon-16x16.png b/examples/react/start-basic-rsbuild/public/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..e3389b00443d602a249b48ae0e21e333971c9c1e GIT binary patch literal 832 zcmV-G1Hb%Px%`bk7VR5(v%lW%BSRTRK~=OuZ4FYhI3vvy^4t8t~UDHGBS#44K%hSiDal!9Nj zeb^RZ;3&cb72F3w6s4;uUA7>Cf>1=Z56;a$8wZ=NKHtq z33GDV=_|V0E(QPTEB1=edhG{{=-+C??RQi7*e9J7YBnZR+PppIKjA8EV+}Yt`zH<7 zGR%O5D3|Ef)99hUXu19=Cxaki@6toZIe&u-X0(oDFRl|x6P7Bwc^mB(i9|gOY)Jh6DaPkmt z8Wr!@TP1|RPlYRlJe^eRB0K(gn91{(=zi{HrkpHIflw8&q%;+U!V&dFPYDQAWJh@< zwcklN9VEX?vG;}75GqCepKC25M^xV%BcR%A8c!)S5k4P0$g}HS#lA6zy`~K@9&EX4 zOrvr%_1U2dp~gg6G*;&`Jag*?jZTJv+$S`*#)zme_SFWOCm*F4U&hJihqxAu;8z}x zWv7!u>YKRa3PUJQs55wC)i4QvFOAV0jW$?1cnUJJ7Vl?z_ge{9q!zpfCJxd8vJ>-ql@0&`Bttn1~4;s>8tax%hr(^q#A_*wqAv( zh(T&>hJD?OhiP$sxAzkd1W1^EHV1WL3xM&6$*HhG@|8}OjPpG{+!;GC>w;ha0000< KMNUMnLSTZGXp5cz literal 0 HcmV?d00001 diff --git a/examples/react/start-basic-rsbuild/public/favicon-32x32.png b/examples/react/start-basic-rsbuild/public/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..900c77d444c9b31e6144b6ac0bb1ab7bb7ca767f GIT binary patch literal 2115 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 literal 0 HcmV?d00001 diff --git a/examples/react/start-basic-rsbuild/public/favicon.ico b/examples/react/start-basic-rsbuild/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..1a1751676f7e22811b1070572093996c93c87617 GIT binary patch literal 15406 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 literal 0 HcmV?d00001 diff --git a/examples/react/start-basic-rsbuild/public/favicon.png b/examples/react/start-basic-rsbuild/public/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..1e77bc06091ade4496525a09d8900675afcf03f0 GIT binary patch literal 1507 zcmV<91swW`P)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91AfN*P1ONa40RR91AOHXW0IY^$^8f$^O-V#SR9Fd>S3Qp$MHK9ro!#4$ zEP@L_hX|b@f=!*_42h6mKu7{)4)_U*>1>0bCkUj;Z1X!7 zHe(Ew^Oi(|bW3J~xu+)XbtFF?4>!7TH$>(D_atUQVEj(8fGvYu2NF33#JZX>)(Vj8 zIi@z>Glt?6t~;Lf(|C8F>;WF^8F<^s7Scr!sZc01uB?HMHoL5+FZ>B(g+r-)?Sn)#3Zal#?G@GAwO5U27MpGOlC2+_saA)rl zP-<@-n~;PQOlm|Hi<+W;NdR;5+=zADzM&?!+CPD36=cGwHy6!D^vPEHG?rO`K>G|M z3FposX{yT132wuw1OR3Um_5JoKB#6?!QgBupIT;?YIr;WcpmuCE>S75mZid+ens#E zGPuYjiG0UNNVWu=f!Id^?9)34)eIpu-`j_~W0iAQzK(}XYc_!;87Tk~?4tq|h=2(! zuq0HCiNK)@+ocCKR3q1REdUju>HdYxd>JX@%oOibg+J~D+}rhz54D!NfC{h-OYk{M zkzmFtdrL@nL0bm8nF@pob1CeLC>12ef#in-Bzv2!wi)Iuwq24)`AH}|0QNQ^f$KHv z?5PBPo1*#GAuAk+Poe`?UJ>mP`@~d4a(103j0lwUx@_+$#B&VC%7r>#2$HIiD`KO8L|s3Yp%M}BT0;NJDzZtPnx=4%enhU zhW*pNN0t`^4%5MKAR+}=^Q?QeqQ`>bbK zf+-ji$Uz8V0?LpX@kh`k%DL)GCA2=@SJNKg56Wh>>pr=7{1PmHqG|~=AdLV3002ov JPDHLkV1ivgp)>#h literal 0 HcmV?d00001 diff --git a/examples/react/start-basic-rsbuild/public/site.webmanifest b/examples/react/start-basic-rsbuild/public/site.webmanifest new file mode 100644 index 0000000000..fa99de77db --- /dev/null +++ b/examples/react/start-basic-rsbuild/public/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/examples/react/start-basic-rsbuild/rsbuild.config.ts b/examples/react/start-basic-rsbuild/rsbuild.config.ts new file mode 100644 index 0000000000..3705696818 --- /dev/null +++ b/examples/react/start-basic-rsbuild/rsbuild.config.ts @@ -0,0 +1,17 @@ +import { tanstackStart } from '@tanstack/react-start/plugin/rsbuild' +import { defineConfig } from '@rsbuild/core' +import { pluginReact } from '@rsbuild/plugin-react' +import { pluginTailwindcss } from '@rsbuild/plugin-tailwindcss' + +export default defineConfig({ + server: { + port: 3000, + }, + plugins: [ + pluginTailwindcss(), + pluginReact(), + tanstackStart({ + srcDirectory: 'src', + }), + ], +}) diff --git a/examples/react/start-basic-rsbuild/src/components/DefaultCatchBoundary.tsx b/examples/react/start-basic-rsbuild/src/components/DefaultCatchBoundary.tsx new file mode 100644 index 0000000000..4a22649ed4 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/components/DefaultCatchBoundary.tsx @@ -0,0 +1,51 @@ +import { + ErrorComponent, + Link, + useLocation, + useRouter, +} from '@tanstack/react-router' +import type { ErrorComponentProps } from '@tanstack/react-router' + +export function DefaultCatchBoundary({ error }: ErrorComponentProps) { + const router = useRouter() + const isRoot = useLocation({ + select: (location) => location.pathname === '/', + }) + + console.error('DefaultCatchBoundary Error:', error) + + return ( +
+ +
+ + {isRoot ? ( + + Home + + ) : ( + { + e.preventDefault() + window.history.back() + }} + > + Go Back + + )} +
+
+ ) +} diff --git a/examples/react/start-basic-rsbuild/src/components/NotFound.tsx b/examples/react/start-basic-rsbuild/src/components/NotFound.tsx new file mode 100644 index 0000000000..fd4e75befe --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/components/NotFound.tsx @@ -0,0 +1,25 @@ +import { Link } from '@tanstack/react-router' + +export function NotFound({ children }: { children?: any }) { + return ( +
+
+ {children ||

The page you are looking for does not exist.

} +
+

+ + + Start Over + +

+
+ ) +} diff --git a/examples/react/start-basic-rsbuild/src/components/PostError.tsx b/examples/react/start-basic-rsbuild/src/components/PostError.tsx new file mode 100644 index 0000000000..271f2d1de1 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/components/PostError.tsx @@ -0,0 +1,6 @@ +import { ErrorComponent } from '@tanstack/react-router' +import type { ErrorComponentProps } from '@tanstack/react-router' + +export function PostErrorComponent({ error }: ErrorComponentProps) { + return +} diff --git a/examples/react/start-basic-rsbuild/src/components/UserError.tsx b/examples/react/start-basic-rsbuild/src/components/UserError.tsx new file mode 100644 index 0000000000..af5d0c6f16 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/components/UserError.tsx @@ -0,0 +1,6 @@ +import { ErrorComponent } from '@tanstack/react-router' +import type { ErrorComponentProps } from '@tanstack/react-router' + +export function UserErrorComponent({ error }: ErrorComponentProps) { + return +} diff --git a/examples/react/start-basic-rsbuild/src/env.d.ts b/examples/react/start-basic-rsbuild/src/env.d.ts new file mode 100644 index 0000000000..b0ac762b09 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/react/start-basic-rsbuild/src/routeTree.gen.ts b/examples/react/start-basic-rsbuild/src/routeTree.gen.ts new file mode 100644 index 0000000000..57b849492d --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routeTree.gen.ts @@ -0,0 +1,447 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// 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 IndexRouteImport } from './routes/index' +import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' +import { Route as CustomScriptDotjsRouteImport } from './routes/customScript[.]js' +import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as RedirectRouteImport } from './routes/redirect' +import { Route as UsersRouteImport } from './routes/users' +import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' +import { Route as ApiUsersRouteImport } from './routes/api/users' +import { Route as PostsIndexRouteImport } from './routes/posts.index' +import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' +import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as UsersUserIdRouteImport } from './routes/users.$userId' +import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' +import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +import { Route as ApiUsersUserIdRouteImport } from './routes/api/users.$userId' +import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' + +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) +const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ + id: '/_pathlessLayout', + getParentRoute: () => rootRouteImport, +} as any) +const CustomScriptDotjsRoute = CustomScriptDotjsRouteImport.update({ + id: '/customScript.js', + path: '/customScript.js', + getParentRoute: () => rootRouteImport, +} as any) +const DeferredRoute = DeferredRouteImport.update({ + id: '/deferred', + path: '/deferred', + getParentRoute: () => rootRouteImport, +} as any) +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', + getParentRoute: () => rootRouteImport, +} as any) +const RedirectRoute = RedirectRouteImport.update({ + id: '/redirect', + path: '/redirect', + getParentRoute: () => rootRouteImport, +} as any) +const UsersRoute = UsersRouteImport.update({ + id: '/users', + path: '/users', + getParentRoute: () => rootRouteImport, +} as any) +const PathlessLayoutNestedLayoutRoute = + PathlessLayoutNestedLayoutRouteImport.update({ + id: '/_nested-layout', + getParentRoute: () => PathlessLayoutRoute, + } as any) +const ApiUsersRoute = ApiUsersRouteImport.update({ + id: '/api/users', + path: '/api/users', + getParentRoute: () => rootRouteImport, +} as any) +const PostsIndexRoute = PostsIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => PostsRoute, +} as any) +const PostsPostIdRoute = PostsPostIdRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => PostsRoute, +} as any) +const UsersIndexRoute = UsersIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => UsersRoute, +} as any) +const UsersUserIdRoute = UsersUserIdRouteImport.update({ + id: '/$userId', + path: '/$userId', + getParentRoute: () => UsersRoute, +} as any) +const PathlessLayoutNestedLayoutRouteARoute = + PathlessLayoutNestedLayoutRouteARouteImport.update({ + id: '/route-a', + path: '/route-a', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, + } as any) +const PathlessLayoutNestedLayoutRouteBRoute = + PathlessLayoutNestedLayoutRouteBRouteImport.update({ + id: '/route-b', + path: '/route-b', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, + } as any) +const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ + id: '/$userId', + path: '/$userId', + getParentRoute: () => ApiUsersRoute, +} as any) +const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ + id: '/posts_/$postId/deep', + path: '/posts/$postId/deep', + getParentRoute: () => rootRouteImport, +} as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/customScript.js': typeof CustomScriptDotjsRoute + '/deferred': typeof DeferredRoute + '/posts': typeof PostsRouteWithChildren + '/redirect': typeof RedirectRoute + '/users': typeof UsersRouteWithChildren + '/api/users': typeof ApiUsersRouteWithChildren + '/posts/$postId': typeof PostsPostIdRoute + '/users/$userId': typeof UsersUserIdRoute + '/posts/': typeof PostsIndexRoute + '/users/': typeof UsersIndexRoute + '/route-a': typeof PathlessLayoutNestedLayoutRouteARoute + '/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute + '/api/users/$userId': typeof ApiUsersUserIdRoute + '/posts/$postId/deep': typeof PostsPostIdDeepRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/customScript.js': typeof CustomScriptDotjsRoute + '/deferred': typeof DeferredRoute + '/redirect': typeof RedirectRoute + '/api/users': typeof ApiUsersRouteWithChildren + '/posts/$postId': typeof PostsPostIdRoute + '/users/$userId': typeof UsersUserIdRoute + '/posts': typeof PostsIndexRoute + '/users': typeof UsersIndexRoute + '/route-a': typeof PathlessLayoutNestedLayoutRouteARoute + '/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute + '/api/users/$userId': typeof ApiUsersUserIdRoute + '/posts/$postId/deep': typeof PostsPostIdDeepRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute + '/_pathlessLayout': typeof PathlessLayoutRouteWithChildren + '/customScript.js': typeof CustomScriptDotjsRoute + '/deferred': typeof DeferredRoute + '/posts': typeof PostsRouteWithChildren + '/redirect': typeof RedirectRoute + '/users': typeof UsersRouteWithChildren + '/_pathlessLayout/_nested-layout': typeof PathlessLayoutNestedLayoutRouteWithChildren + '/api/users': typeof ApiUsersRouteWithChildren + '/posts/$postId': typeof PostsPostIdRoute + '/users/$userId': typeof UsersUserIdRoute + '/posts/': typeof PostsIndexRoute + '/users/': typeof UsersIndexRoute + '/_pathlessLayout/_nested-layout/route-a': typeof PathlessLayoutNestedLayoutRouteARoute + '/_pathlessLayout/_nested-layout/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute + '/api/users/$userId': typeof ApiUsersUserIdRoute + '/posts_/$postId/deep': typeof PostsPostIdDeepRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: + | '/' + | '/customScript.js' + | '/deferred' + | '/posts' + | '/redirect' + | '/users' + | '/api/users' + | '/posts/$postId' + | '/users/$userId' + | '/posts/' + | '/users/' + | '/route-a' + | '/route-b' + | '/api/users/$userId' + | '/posts/$postId/deep' + fileRoutesByTo: FileRoutesByTo + to: + | '/' + | '/customScript.js' + | '/deferred' + | '/redirect' + | '/api/users' + | '/posts/$postId' + | '/users/$userId' + | '/posts' + | '/users' + | '/route-a' + | '/route-b' + | '/api/users/$userId' + | '/posts/$postId/deep' + id: + | '__root__' + | '/' + | '/_pathlessLayout' + | '/customScript.js' + | '/deferred' + | '/posts' + | '/redirect' + | '/users' + | '/_pathlessLayout/_nested-layout' + | '/api/users' + | '/posts/$postId' + | '/users/$userId' + | '/posts/' + | '/users/' + | '/_pathlessLayout/_nested-layout/route-a' + | '/_pathlessLayout/_nested-layout/route-b' + | '/api/users/$userId' + | '/posts_/$postId/deep' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + PathlessLayoutRoute: typeof PathlessLayoutRouteWithChildren + CustomScriptDotjsRoute: typeof CustomScriptDotjsRoute + DeferredRoute: typeof DeferredRoute + PostsRoute: typeof PostsRouteWithChildren + RedirectRoute: typeof RedirectRoute + UsersRoute: typeof UsersRouteWithChildren + ApiUsersRoute: typeof ApiUsersRouteWithChildren + PostsPostIdDeepRoute: typeof PostsPostIdDeepRoute +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + '/_pathlessLayout': { + id: '/_pathlessLayout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutRouteImport + parentRoute: typeof rootRouteImport + } + '/customScript.js': { + id: '/customScript.js' + path: '/customScript.js' + fullPath: '/customScript.js' + preLoaderRoute: typeof CustomScriptDotjsRouteImport + parentRoute: typeof rootRouteImport + } + '/deferred': { + id: '/deferred' + path: '/deferred' + fullPath: '/deferred' + preLoaderRoute: typeof DeferredRouteImport + parentRoute: typeof rootRouteImport + } + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport + parentRoute: typeof rootRouteImport + } + '/redirect': { + id: '/redirect' + path: '/redirect' + fullPath: '/redirect' + preLoaderRoute: typeof RedirectRouteImport + parentRoute: typeof rootRouteImport + } + '/users': { + id: '/users' + path: '/users' + fullPath: '/users' + preLoaderRoute: typeof UsersRouteImport + parentRoute: typeof rootRouteImport + } + '/_pathlessLayout/_nested-layout': { + id: '/_pathlessLayout/_nested-layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport + parentRoute: typeof PathlessLayoutRoute + } + '/api/users': { + id: '/api/users' + path: '/api/users' + fullPath: '/api/users' + preLoaderRoute: typeof ApiUsersRouteImport + parentRoute: typeof rootRouteImport + } + '/posts/': { + id: '/posts/' + path: '/' + fullPath: '/posts/' + preLoaderRoute: typeof PostsIndexRouteImport + parentRoute: typeof PostsRoute + } + '/posts/$postId': { + id: '/posts/$postId' + path: '/$postId' + fullPath: '/posts/$postId' + preLoaderRoute: typeof PostsPostIdRouteImport + parentRoute: typeof PostsRoute + } + '/users/': { + id: '/users/' + path: '/' + fullPath: '/users/' + preLoaderRoute: typeof UsersIndexRouteImport + parentRoute: typeof UsersRoute + } + '/users/$userId': { + id: '/users/$userId' + path: '/$userId' + fullPath: '/users/$userId' + preLoaderRoute: typeof UsersUserIdRouteImport + parentRoute: typeof UsersRoute + } + '/_pathlessLayout/_nested-layout/route-a': { + id: '/_pathlessLayout/_nested-layout/route-a' + path: '/route-a' + fullPath: '/route-a' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport + parentRoute: typeof PathlessLayoutNestedLayoutRoute + } + '/_pathlessLayout/_nested-layout/route-b': { + id: '/_pathlessLayout/_nested-layout/route-b' + path: '/route-b' + fullPath: '/route-b' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport + parentRoute: typeof PathlessLayoutNestedLayoutRoute + } + '/api/users/$userId': { + id: '/api/users/$userId' + path: '/$userId' + fullPath: '/api/users/$userId' + preLoaderRoute: typeof ApiUsersUserIdRouteImport + parentRoute: typeof ApiUsersRoute + } + '/posts_/$postId/deep': { + id: '/posts_/$postId/deep' + path: '/posts/$postId/deep' + fullPath: '/posts/$postId/deep' + preLoaderRoute: typeof PostsPostIdDeepRouteImport + parentRoute: typeof rootRouteImport + } + } +} + +interface PathlessLayoutNestedLayoutRouteChildren { + PathlessLayoutNestedLayoutRouteARoute: typeof PathlessLayoutNestedLayoutRouteARoute + PathlessLayoutNestedLayoutRouteBRoute: typeof PathlessLayoutNestedLayoutRouteBRoute +} + +const PathlessLayoutNestedLayoutRouteChildren: PathlessLayoutNestedLayoutRouteChildren = + { + PathlessLayoutNestedLayoutRouteARoute: + PathlessLayoutNestedLayoutRouteARoute, + PathlessLayoutNestedLayoutRouteBRoute: + PathlessLayoutNestedLayoutRouteBRoute, + } + +const PathlessLayoutNestedLayoutRouteWithChildren = + PathlessLayoutNestedLayoutRoute._addFileChildren( + PathlessLayoutNestedLayoutRouteChildren, + ) + +interface PathlessLayoutRouteChildren { + PathlessLayoutNestedLayoutRoute: typeof PathlessLayoutNestedLayoutRouteWithChildren +} + +const PathlessLayoutRouteChildren: PathlessLayoutRouteChildren = { + PathlessLayoutNestedLayoutRoute: PathlessLayoutNestedLayoutRouteWithChildren, +} + +const PathlessLayoutRouteWithChildren = PathlessLayoutRoute._addFileChildren( + PathlessLayoutRouteChildren, +) + +interface PostsRouteChildren { + PostsPostIdRoute: typeof PostsPostIdRoute + PostsIndexRoute: typeof PostsIndexRoute +} + +const PostsRouteChildren: PostsRouteChildren = { + PostsPostIdRoute: PostsPostIdRoute, + PostsIndexRoute: PostsIndexRoute, +} + +const PostsRouteWithChildren = PostsRoute._addFileChildren(PostsRouteChildren) + +interface UsersRouteChildren { + UsersUserIdRoute: typeof UsersUserIdRoute + UsersIndexRoute: typeof UsersIndexRoute +} + +const UsersRouteChildren: UsersRouteChildren = { + UsersUserIdRoute: UsersUserIdRoute, + UsersIndexRoute: UsersIndexRoute, +} + +const UsersRouteWithChildren = UsersRoute._addFileChildren(UsersRouteChildren) + +interface ApiUsersRouteChildren { + ApiUsersUserIdRoute: typeof ApiUsersUserIdRoute +} + +const ApiUsersRouteChildren: ApiUsersRouteChildren = { + ApiUsersUserIdRoute: ApiUsersUserIdRoute, +} + +const ApiUsersRouteWithChildren = ApiUsersRoute._addFileChildren( + ApiUsersRouteChildren, +) + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + PathlessLayoutRoute: PathlessLayoutRouteWithChildren, + CustomScriptDotjsRoute: CustomScriptDotjsRoute, + DeferredRoute: DeferredRoute, + PostsRoute: PostsRouteWithChildren, + RedirectRoute: RedirectRoute, + UsersRoute: UsersRouteWithChildren, + ApiUsersRoute: ApiUsersRouteWithChildren, + PostsPostIdDeepRoute: PostsPostIdDeepRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() + +import type { getRouter } from './router.tsx' +import type { createStart } from '@tanstack/react-start' +declare module '@tanstack/react-start' { + interface Register { + ssr: true + router: Awaited> + } +} diff --git a/examples/react/start-basic-rsbuild/src/router.tsx b/examples/react/start-basic-rsbuild/src/router.tsx new file mode 100644 index 0000000000..f380f73be7 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/router.tsx @@ -0,0 +1,15 @@ +import { createRouter } from '@tanstack/react-router' +import { routeTree } from './routeTree.gen' +import { DefaultCatchBoundary } from './components/DefaultCatchBoundary' +import { NotFound } from './components/NotFound' + +export function getRouter() { + const router = createRouter({ + routeTree, + defaultPreload: 'intent', + defaultErrorComponent: DefaultCatchBoundary, + defaultNotFoundComponent: () => , + scrollRestoration: true, + }) + return router +} diff --git a/examples/react/start-basic-rsbuild/src/routes/__root.tsx b/examples/react/start-basic-rsbuild/src/routes/__root.tsx new file mode 100644 index 0000000000..6d4aed1fe0 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/__root.tsx @@ -0,0 +1,130 @@ +import { + HeadContent, + Link, + Scripts, + createRootRoute, +} from '@tanstack/react-router' +import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' +import * as React from 'react' +import { DefaultCatchBoundary } from '~/components/DefaultCatchBoundary' +import { NotFound } from '~/components/NotFound' +import appCss from '~/styles/app.css?url' +import { seo } from '~/utils/seo' + +export const Route = createRootRoute({ + head: () => ({ + meta: [ + { + charSet: 'utf-8', + }, + { + name: 'viewport', + content: 'width=device-width, initial-scale=1', + }, + ...seo({ + title: + 'TanStack Start | Type-Safe, Client-First, Full-Stack React Framework', + description: `TanStack Start is a type-safe, client-first, full-stack React framework. `, + }), + ], + links: [ + { rel: 'stylesheet', href: appCss }, + { + rel: 'apple-touch-icon', + sizes: '180x180', + href: '/apple-touch-icon.png', + }, + { + rel: 'icon', + type: 'image/png', + sizes: '32x32', + href: '/favicon-32x32.png', + }, + { + rel: 'icon', + type: 'image/png', + sizes: '16x16', + href: '/favicon-16x16.png', + }, + { rel: 'manifest', href: '/site.webmanifest', color: '#fffff' }, + { rel: 'icon', href: '/favicon.ico' }, + ], + scripts: [ + { + src: '/customScript.js', + type: 'text/javascript', + }, + ], + }), + errorComponent: DefaultCatchBoundary, + notFoundComponent: () => , + shellComponent: RootDocument, +}) + +function RootDocument({ children }: { children: React.ReactNode }) { + return ( + + + + + +
+ + Home + {' '} + + Posts + {' '} + + Users + {' '} + + Pathless Layout + {' '} + + Deferred + {' '} + + This Route Does Not Exist + +
+
+ {children} + + + + + ) +} diff --git a/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout.tsx b/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout.tsx new file mode 100644 index 0000000000..c3b12442b8 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout.tsx @@ -0,0 +1,16 @@ +import { Outlet, createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/_pathlessLayout')({ + component: LayoutComponent, +}) + +function LayoutComponent() { + return ( +
+
I'm a layout
+
+ +
+
+ ) +} diff --git a/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout/_nested-layout.tsx b/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout/_nested-layout.tsx new file mode 100644 index 0000000000..9a48b73a46 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout/_nested-layout.tsx @@ -0,0 +1,34 @@ +import { Link, Outlet, createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/_pathlessLayout/_nested-layout')({ + component: LayoutComponent, +}) + +function LayoutComponent() { + return ( +
+
I'm a nested layout
+
+ + Go to route A + + + Go to route B + +
+
+ +
+
+ ) +} diff --git a/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout/_nested-layout/route-a.tsx b/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout/_nested-layout/route-a.tsx new file mode 100644 index 0000000000..426a8fe486 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout/_nested-layout/route-a.tsx @@ -0,0 +1,11 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/_pathlessLayout/_nested-layout/route-a')( + { + component: LayoutAComponent, + }, +) + +function LayoutAComponent() { + return
I'm A!
+} diff --git a/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout/_nested-layout/route-b.tsx b/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout/_nested-layout/route-b.tsx new file mode 100644 index 0000000000..20facf2daf --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout/_nested-layout/route-b.tsx @@ -0,0 +1,11 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/_pathlessLayout/_nested-layout/route-b')( + { + component: LayoutBComponent, + }, +) + +function LayoutBComponent() { + return
I'm B!
+} diff --git a/examples/react/start-basic-rsbuild/src/routes/api/users.$userId.ts b/examples/react/start-basic-rsbuild/src/routes/api/users.$userId.ts new file mode 100644 index 0000000000..ee34d7a189 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/api/users.$userId.ts @@ -0,0 +1,31 @@ +import { createFileRoute } from '@tanstack/react-router' +import type { User } from '~/utils/users' + +export const Route = createFileRoute('/api/users/$userId')({ + server: { + handlers: { + GET: async ({ params, request }) => { + console.info(`Fetching users by id=${params.userId}... @`, request.url) + try { + const res = await fetch( + 'https://jsonplaceholder.typicode.com/users/' + params.userId, + ) + if (!res.ok) { + throw new Error('Failed to fetch user') + } + + const user = (await res.json()) as User + + return Response.json({ + id: user.id, + name: user.name, + email: user.email, + }) + } catch (e) { + console.error(e) + return Response.json({ error: 'User not found' }, { status: 404 }) + } + }, + }, + }, +}) diff --git a/examples/react/start-basic-rsbuild/src/routes/api/users.ts b/examples/react/start-basic-rsbuild/src/routes/api/users.ts new file mode 100644 index 0000000000..4cecd01852 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/api/users.ts @@ -0,0 +1,63 @@ +import { createFileRoute } from '@tanstack/react-router' +import { getRequestHeaders } from '@tanstack/react-start/server' +import { createMiddleware } from '@tanstack/react-start' +import type { User } from '~/utils/users' + +const userLoggerMiddleware = createMiddleware().server(async ({ next }) => { + console.info('In: /users') + console.info('Request Headers:', getRequestHeaders()) + const result = await next() + result.response.headers.set('x-users', 'true') + console.info('Out: /users') + return result +}) + +const testParentMiddleware = createMiddleware().server(async ({ next }) => { + console.info('In: testParentMiddleware') + const result = await next() + result.response.headers.set('x-test-parent', 'true') + console.info('Out: testParentMiddleware') + return result +}) + +const testMiddleware = createMiddleware() + .middleware([testParentMiddleware]) + .server(async ({ next }) => { + console.info('In: testMiddleware') + const result = await next() + result.response.headers.set('x-test', 'true') + + // if (Math.random() > 0.5) { + // throw new Response(null, { + // status: 302, + // headers: { Location: 'https://www.google.com' }, + // }) + // } + + console.info('Out: testMiddleware') + return result + }) + +export const Route = createFileRoute('/api/users')({ + server: { + middleware: [testMiddleware, userLoggerMiddleware], + handlers: { + GET: async ({ request }) => { + console.info('GET /api/users @', request.url) + console.info('Fetching users... @', request.url) + const res = await fetch('https://jsonplaceholder.typicode.com/users') + if (!res.ok) { + throw new Error('Failed to fetch users') + } + + const data = (await res.json()) as Array + + const list = data.slice(0, 10) + + return Response.json( + list.map((u) => ({ id: u.id, name: u.name, email: u.email })), + ) + }, + }, + }, +}) diff --git a/examples/react/start-basic-rsbuild/src/routes/customScript[.]js.ts b/examples/react/start-basic-rsbuild/src/routes/customScript[.]js.ts new file mode 100644 index 0000000000..811471ac15 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/customScript[.]js.ts @@ -0,0 +1,15 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/customScript.js')({ + server: { + handlers: { + GET: () => { + return new Response('console.log("Hello from customScript.js!")', { + headers: { + 'Content-Type': 'application/javascript', + }, + }) + }, + }, + }, +}) diff --git a/examples/react/start-basic-rsbuild/src/routes/deferred.tsx b/examples/react/start-basic-rsbuild/src/routes/deferred.tsx new file mode 100644 index 0000000000..f3e09d1d4e --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/deferred.tsx @@ -0,0 +1,62 @@ +import { Await, createFileRoute } from '@tanstack/react-router' +import { createServerFn } from '@tanstack/react-start' +import { Suspense, useState } from 'react' + +const personServerFn = createServerFn({ method: 'GET' }) + .validator((d: string) => d) + .handler(({ data: name }) => { + return { name, randomNumber: Math.floor(Math.random() * 100) } + }) + +const slowServerFn = createServerFn({ method: 'GET' }) + .validator((d: string) => d) + .handler(async ({ data: name }) => { + await new Promise((r) => setTimeout(r, 1000)) + return { name, randomNumber: Math.floor(Math.random() * 100) } + }) + +export const Route = createFileRoute('/deferred')({ + loader: async () => { + return { + deferredStuff: new Promise((r) => + setTimeout(() => r('Hello deferred!'), 2000), + ), + deferredPerson: slowServerFn({ data: 'Tanner Linsley' }), + person: await personServerFn({ data: 'John Doe' }), + } + }, + component: Deferred, +}) + +function Deferred() { + const [count, setCount] = useState(0) + const { deferredStuff, deferredPerson, person } = Route.useLoaderData() + + return ( +
+
+ {person.name} - {person.randomNumber} +
+ Loading person...
}> + ( +
+ {data.name} - {data.randomNumber} +
+ )} + /> + + Loading stuff...
}> +

{data}

} + /> + +
Count: {count}
+
+ +
+
+ ) +} diff --git a/examples/react/start-basic-rsbuild/src/routes/index.tsx b/examples/react/start-basic-rsbuild/src/routes/index.tsx new file mode 100644 index 0000000000..09a907cb18 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/index.tsx @@ -0,0 +1,13 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/')({ + component: Home, +}) + +function Home() { + return ( +
+

Welcome Home!!!

+
+ ) +} diff --git a/examples/react/start-basic-rsbuild/src/routes/posts.$postId.tsx b/examples/react/start-basic-rsbuild/src/routes/posts.$postId.tsx new file mode 100644 index 0000000000..65905bf8c4 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/posts.$postId.tsx @@ -0,0 +1,34 @@ +import { Link, createFileRoute } from '@tanstack/react-router' +import { fetchPost } from '../utils/posts' +import { NotFound } from '~/components/NotFound' +import { PostErrorComponent } from '~/components/PostError' + +export const Route = createFileRoute('/posts/$postId')({ + loader: ({ params: { postId } }) => fetchPost({ data: postId }), + errorComponent: PostErrorComponent, + component: PostComponent, + notFoundComponent: () => { + return Post not found + }, +}) + +function PostComponent() { + const post = Route.useLoaderData() + + return ( +
+

{post.title}

+
{post.body}
+ + Deep View + +
+ ) +} diff --git a/examples/react/start-basic-rsbuild/src/routes/posts.index.tsx b/examples/react/start-basic-rsbuild/src/routes/posts.index.tsx new file mode 100644 index 0000000000..5b5f08f95b --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/posts.index.tsx @@ -0,0 +1,9 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/posts/')({ + component: PostsIndexComponent, +}) + +function PostsIndexComponent() { + return
Select a post.
+} diff --git a/examples/react/start-basic-rsbuild/src/routes/posts.tsx b/examples/react/start-basic-rsbuild/src/routes/posts.tsx new file mode 100644 index 0000000000..9b9162be7c --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/posts.tsx @@ -0,0 +1,38 @@ +import { Link, Outlet, createFileRoute } from '@tanstack/react-router' +import { fetchPosts } from '../utils/posts' + +export const Route = createFileRoute('/posts')({ + loader: async () => fetchPosts(), + component: PostsComponent, +}) + +function PostsComponent() { + const posts = Route.useLoaderData() + + return ( +
+
    + {[...posts, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map( + (post) => { + return ( +
  • + +
    {post.title.substring(0, 20)}
    + +
  • + ) + }, + )} +
+
+ +
+ ) +} diff --git a/examples/react/start-basic-rsbuild/src/routes/posts_.$postId.deep.tsx b/examples/react/start-basic-rsbuild/src/routes/posts_.$postId.deep.tsx new file mode 100644 index 0000000000..29e6c39b5a --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/posts_.$postId.deep.tsx @@ -0,0 +1,29 @@ +import { Link, createFileRoute } from '@tanstack/react-router' +import { fetchPost } from '../utils/posts' +import { PostErrorComponent } from '~/components/PostError' + +export const Route = createFileRoute('/posts_/$postId/deep')({ + loader: async ({ params: { postId } }) => + fetchPost({ + data: postId, + }), + errorComponent: PostErrorComponent, + component: PostDeepComponent, +}) + +function PostDeepComponent() { + const post = Route.useLoaderData() + + return ( +
+ + ← All Posts + +

{post.title}

+
{post.body}
+
+ ) +} diff --git a/examples/react/start-basic-rsbuild/src/routes/redirect.tsx b/examples/react/start-basic-rsbuild/src/routes/redirect.tsx new file mode 100644 index 0000000000..ea34b6e420 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/redirect.tsx @@ -0,0 +1,9 @@ +import { createFileRoute, redirect } from '@tanstack/react-router' + +export const Route = createFileRoute('/redirect')({ + beforeLoad: () => { + throw redirect({ + to: '/posts', + }) + }, +}) diff --git a/examples/react/start-basic-rsbuild/src/routes/users.$userId.tsx b/examples/react/start-basic-rsbuild/src/routes/users.$userId.tsx new file mode 100644 index 0000000000..4e4d470075 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/users.$userId.tsx @@ -0,0 +1,45 @@ +import { createFileRoute } from '@tanstack/react-router' +import { NotFound } from '../components/NotFound' +import { UserErrorComponent } from '../components/UserError' +import type { User } from '../utils/users' + +export const Route = createFileRoute('/users/$userId')({ + loader: async ({ params: { userId } }) => { + try { + const res = await fetch('/api/users/' + userId) + if (!res.ok) { + throw new Error('Unexpected status code') + } + + const data = await res.json() + + return data as User + } catch { + throw new Error('Failed to fetch user') + } + }, + errorComponent: UserErrorComponent, + component: UserComponent, + notFoundComponent: () => { + return User not found + }, +}) + +function UserComponent() { + const user = Route.useLoaderData() + + return ( +
+ ) +} diff --git a/examples/react/start-basic-rsbuild/src/routes/users.index.tsx b/examples/react/start-basic-rsbuild/src/routes/users.index.tsx new file mode 100644 index 0000000000..27d136987e --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/users.index.tsx @@ -0,0 +1,19 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/users/')({ + component: UsersIndexComponent, +}) + +function UsersIndexComponent() { + return ( +
+ Select a user or{' '} + + view as JSON + +
+ ) +} diff --git a/examples/react/start-basic-rsbuild/src/routes/users.tsx b/examples/react/start-basic-rsbuild/src/routes/users.tsx new file mode 100644 index 0000000000..64ca3ce156 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/users.tsx @@ -0,0 +1,49 @@ +import { Link, Outlet, createFileRoute } from '@tanstack/react-router' +import type { User } from '../utils/users' + +export const Route = createFileRoute('/users')({ + loader: async () => { + const res = await fetch('/api/users') + + if (!res.ok) { + throw new Error('Unexpected status code') + } + + const data = await res.json() + + return data as Array + }, + component: UsersComponent, +}) + +function UsersComponent() { + const users = Route.useLoaderData() + + return ( +
+
    + {[ + ...users, + { id: 'i-do-not-exist', name: 'Non-existent User', email: '' }, + ].map((user) => { + return ( +
  • + +
    {user.name}
    + +
  • + ) + })} +
+
+ +
+ ) +} diff --git a/examples/react/start-basic-rsbuild/src/styles/app.css b/examples/react/start-basic-rsbuild/src/styles/app.css new file mode 100644 index 0000000000..37c1f5a6e2 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/styles/app.css @@ -0,0 +1,30 @@ +@import 'tailwindcss' source('../'); + +@layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentcolor); + } +} + +@layer base { + html { + color-scheme: light dark; + } + + * { + @apply border-gray-200 dark:border-gray-800; + } + + html, + body { + @apply text-gray-900 bg-gray-50 dark:bg-gray-950 dark:text-gray-200; + } + + .using-mouse * { + outline: none !important; + } +} diff --git a/examples/react/start-basic-rsbuild/src/utils/loggingMiddleware.tsx b/examples/react/start-basic-rsbuild/src/utils/loggingMiddleware.tsx new file mode 100644 index 0000000000..3ea9a06439 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/utils/loggingMiddleware.tsx @@ -0,0 +1,41 @@ +import { createMiddleware } from '@tanstack/react-start' + +const preLogMiddleware = createMiddleware({ type: 'function' }) + .client(async (ctx) => { + const clientTime = new Date() + + return ctx.next({ + context: { + clientTime, + }, + sendContext: { + clientTime, + }, + }) + }) + .server(async (ctx) => { + const serverTime = new Date() + + return ctx.next({ + sendContext: { + serverTime, + durationToServer: + serverTime.getTime() - ctx.context.clientTime.getTime(), + }, + }) + }) + +export const logMiddleware = createMiddleware({ type: 'function' }) + .middleware([preLogMiddleware]) + .client(async (ctx) => { + const res = await ctx.next() + + const now = new Date() + console.log('Client Req/Res:', { + duration: now.getTime() - res.context.clientTime.getTime(), + durationToServer: res.context.durationToServer, + durationFromServer: now.getTime() - res.context.serverTime.getTime(), + }) + + return res + }) diff --git a/examples/react/start-basic-rsbuild/src/utils/posts.tsx b/examples/react/start-basic-rsbuild/src/utils/posts.tsx new file mode 100644 index 0000000000..ea7dd0a721 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/utils/posts.tsx @@ -0,0 +1,40 @@ +import { notFound } from '@tanstack/react-router' +import { createServerFn } from '@tanstack/react-start' + +export type PostType = { + id: number + title: string + body: string +} + +export const fetchPost = createServerFn({ method: 'POST' }) + .validator((d: string) => d) + .handler(async ({ data }) => { + console.info(`Fetching post with id ${data}...`) + const res = await fetch( + `https://jsonplaceholder.typicode.com/posts/${data}`, + ) + if (!res.ok) { + if (res.status === 404) { + throw notFound() + } + + throw new Error('Failed to fetch post') + } + + const post = await res.json() + + return post as PostType + }) + +export const fetchPosts = createServerFn().handler(async () => { + console.info('Fetching posts...') + const res = await fetch('https://jsonplaceholder.typicode.com/posts') + if (!res.ok) { + throw new Error('Failed to fetch posts') + } + + const posts = await res.json() + + return (posts as Array).slice(0, 10) +}) diff --git a/examples/react/start-basic-rsbuild/src/utils/seo.ts b/examples/react/start-basic-rsbuild/src/utils/seo.ts new file mode 100644 index 0000000000..d18ad84b74 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/utils/seo.ts @@ -0,0 +1,33 @@ +export const seo = ({ + title, + description, + keywords, + image, +}: { + title: string + description?: string + image?: string + keywords?: string +}) => { + const tags = [ + { title }, + { name: 'description', content: description }, + { name: 'keywords', content: keywords }, + { name: 'twitter:title', content: title }, + { name: 'twitter:description', content: description }, + { name: 'twitter:creator', content: '@tannerlinsley' }, + { name: 'twitter:site', content: '@tannerlinsley' }, + { name: 'og:type', content: 'website' }, + { name: 'og:title', content: title }, + { name: 'og:description', content: description }, + ...(image + ? [ + { name: 'twitter:image', content: image }, + { name: 'twitter:card', content: 'summary_large_image' }, + { name: 'og:image', content: image }, + ] + : []), + ] + + return tags +} diff --git a/examples/react/start-basic-rsbuild/src/utils/users.tsx b/examples/react/start-basic-rsbuild/src/utils/users.tsx new file mode 100644 index 0000000000..7ba645b383 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/utils/users.tsx @@ -0,0 +1,5 @@ +export type User = { + id: number + name: string + email: string +} diff --git a/examples/react/start-basic-rsbuild/tsconfig.json b/examples/react/start-basic-rsbuild/tsconfig.json new file mode 100644 index 0000000000..cc479423a1 --- /dev/null +++ b/examples/react/start-basic-rsbuild/tsconfig.json @@ -0,0 +1,21 @@ +{ + "include": ["**/*.ts", "**/*.tsx", "**/*.d.ts"], + "compilerOptions": { + "strict": true, + "esModuleInterop": true, + "jsx": "react-jsx", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["DOM", "DOM.Iterable", "ES2024"], + "isolatedModules": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "target": "ES2024", + "allowJs": true, + "forceConsistentCasingInFileNames": true, + "paths": { + "~/*": ["./src/*"] + }, + "noEmit": true + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a91aaea20f..3468f4256c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9302,6 +9302,58 @@ importers: specifier: ^8.0.14 version: 8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0) + examples/react/start-basic-rsbuild: + dependencies: + '@tanstack/react-router': + specifier: workspace:* + version: link:../../../packages/react-router + '@tanstack/react-router-devtools': + specifier: workspace:^ + version: link:../../../packages/react-router-devtools + '@tanstack/react-start': + specifier: workspace:* + version: link:../../../packages/react-start + react: + specifier: ^19.2.3 + version: 19.2.3 + react-dom: + specifier: ^19.2.3 + version: 19.2.3(react@19.2.3) + tailwind-merge: + specifier: ^3.6.0 + version: 3.6.0 + zod: + specifier: ^4.4.3 + version: 4.4.3 + devDependencies: + '@rsbuild/core': + specifier: ^2.0.11 + version: 2.0.14 + '@rsbuild/plugin-react': + specifier: ^2.0.0 + version: 2.0.0(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23)) + '@rsbuild/plugin-tailwindcss': + specifier: ^2.0.3 + version: 2.0.3(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23))(webpack@5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4)) + '@types/node': + specifier: 25.0.9 + version: 25.0.9 + '@types/react': + specifier: ^19.2.8 + version: 19.2.9 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.9) + srvx: + specifier: ^0.11.18 + version: 0.11.18 + tailwindcss: + specifier: ^4.2.2 + version: 4.2.2 + typescript: + specifier: ^6.0.2 + version: 6.0.2 + examples/react/start-basic-static: dependencies: '@tanstack/react-router': @@ -13408,7 +13460,7 @@ importers: version: 0.1.7 h3-v2: specifier: npm:h3@2.0.1-rc.20 - version: h3@2.0.1-rc.20(crossws@0.4.5(srvx@0.11.15)) + version: h3@2.0.1-rc.20(crossws@0.4.5(srvx@0.11.18)) seroval: specifier: ^1.5.4 version: 1.5.4 @@ -18446,6 +18498,14 @@ packages: '@rsbuild/core': optional: true + '@rsbuild/plugin-tailwindcss@2.0.3': + resolution: {integrity: sha512-sl7mN0fyoP0W+zEyFR2xEMVAS2cZPlOAqsXwI8Ei9tUSvV7V4G8Xw2WBVCOQ/B4KEFfJ6H1+TEuPlAzHLEfc4g==} + peerDependencies: + '@rsbuild/core': ^2.0.0 + peerDependenciesMeta: + '@rsbuild/core': + optional: true + '@rsbuild/plugin-vue-jsx@1.1.1': resolution: {integrity: sha512-6QHm0dmx5cdgpvMXv7bSrTzuDi1uDBw7SaydmN8kQ/uhaxRPWMNMU3g6rGYKhu+6ExHKwtXOf8CI2YtXwDaP9Q==} peerDependencies: @@ -19132,36 +19192,69 @@ packages: '@tailwindcss/node@4.2.2': resolution: {integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==} + '@tailwindcss/node@4.3.1': + resolution: {integrity: sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==} + '@tailwindcss/oxide-android-arm64@4.2.2': resolution: {integrity: sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==} engines: {node: '>= 20'} cpu: [arm64] os: [android] + '@tailwindcss/oxide-android-arm64@4.3.1': + resolution: {integrity: sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + '@tailwindcss/oxide-darwin-arm64@4.2.2': resolution: {integrity: sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==} engines: {node: '>= 20'} cpu: [arm64] os: [darwin] + '@tailwindcss/oxide-darwin-arm64@4.3.1': + resolution: {integrity: sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + '@tailwindcss/oxide-darwin-x64@4.2.2': resolution: {integrity: sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==} engines: {node: '>= 20'} cpu: [x64] os: [darwin] + '@tailwindcss/oxide-darwin-x64@4.3.1': + resolution: {integrity: sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + '@tailwindcss/oxide-freebsd-x64@4.2.2': resolution: {integrity: sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==} engines: {node: '>= 20'} cpu: [x64] os: [freebsd] + '@tailwindcss/oxide-freebsd-x64@4.3.1': + resolution: {integrity: sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': resolution: {integrity: sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==} engines: {node: '>= 20'} cpu: [arm] os: [linux] + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1': + resolution: {integrity: sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': resolution: {integrity: sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==} engines: {node: '>= 20'} @@ -19169,6 +19262,13 @@ packages: os: [linux] libc: [glibc] + '@tailwindcss/oxide-linux-arm64-gnu@4.3.1': + resolution: {integrity: sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@tailwindcss/oxide-linux-arm64-musl@4.2.2': resolution: {integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==} engines: {node: '>= 20'} @@ -19176,6 +19276,13 @@ packages: os: [linux] libc: [musl] + '@tailwindcss/oxide-linux-arm64-musl@4.3.1': + resolution: {integrity: sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + '@tailwindcss/oxide-linux-x64-gnu@4.2.2': resolution: {integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==} engines: {node: '>= 20'} @@ -19183,6 +19290,13 @@ packages: os: [linux] libc: [glibc] + '@tailwindcss/oxide-linux-x64-gnu@4.3.1': + resolution: {integrity: sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + '@tailwindcss/oxide-linux-x64-musl@4.2.2': resolution: {integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==} engines: {node: '>= 20'} @@ -19190,6 +19304,13 @@ packages: os: [linux] libc: [musl] + '@tailwindcss/oxide-linux-x64-musl@4.3.1': + resolution: {integrity: sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + '@tailwindcss/oxide-wasm32-wasi@4.2.2': resolution: {integrity: sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==} engines: {node: '>=14.0.0'} @@ -19202,22 +19323,50 @@ packages: - '@emnapi/wasi-threads' - tslib + '@tailwindcss/oxide-wasm32-wasi@4.3.1': + resolution: {integrity: sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': resolution: {integrity: sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==} engines: {node: '>= 20'} cpu: [arm64] os: [win32] + '@tailwindcss/oxide-win32-arm64-msvc@4.3.1': + resolution: {integrity: sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + '@tailwindcss/oxide-win32-x64-msvc@4.2.2': resolution: {integrity: sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==} engines: {node: '>= 20'} cpu: [x64] os: [win32] + '@tailwindcss/oxide-win32-x64-msvc@4.3.1': + resolution: {integrity: sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + '@tailwindcss/oxide@4.2.2': resolution: {integrity: sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==} engines: {node: '>= 20'} + '@tailwindcss/oxide@4.3.1': + resolution: {integrity: sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA==} + engines: {node: '>= 20'} + '@tailwindcss/postcss@4.2.2': resolution: {integrity: sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==} @@ -19226,6 +19375,17 @@ packages: peerDependencies: vite: ^8.0.14 + '@tailwindcss/webpack@4.3.1': + resolution: {integrity: sha512-HM33EjYPbkMlBGcYoVA/pk/hML3wAn2JOnjF79eDWVLuktOhRczDwWsSBQBOWV6LLBVAgGZS/pZ519OHUF8vKg==} + peerDependencies: + '@rspack/core': ^1.0.0 || ^2.0.0 + webpack: ^5.0.0 + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true + '@tanstack/devtools-client@0.0.4': resolution: {integrity: sha512-LefnH9KE9uRDEWifc3QDcooskA8ikfs41bybDTgpYQpyTUspZnaEdUdya9Hry0KYxZ8nos0S3nNbsP79KHqr6Q==} engines: {node: '>=18'} @@ -21765,6 +21925,10 @@ packages: resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} engines: {node: '>=10.13.0'} + enhanced-resolve@5.21.6: + resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==} + engines: {node: '>=10.13.0'} + enquirer@2.3.6: resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==} engines: {node: '>=8.6'} @@ -25381,6 +25545,11 @@ packages: engines: {node: '>=20.16.0'} hasBin: true + srvx@0.11.18: + resolution: {integrity: sha512-7/EW5sPdC1bU7iq1tgTvCZqUQDkJdsqIVzYqBv7SuBfQQ10oWkKj4KYNOw0H4Ig26bXuUYDA7XTKxB+/HC5SRw==} + engines: {node: '>=20.16.0'} + hasBin: true + stable-hash-x@0.2.0: resolution: {integrity: sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==} engines: {node: '>=12.0.0'} @@ -25557,6 +25726,9 @@ packages: tailwindcss@4.2.2: resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==} + tailwindcss@4.3.1: + resolution: {integrity: sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q==} + tapable@2.2.1: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} @@ -25565,6 +25737,10 @@ packages: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + tar-stream@2.2.0: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} engines: {node: '>=6'} @@ -31743,6 +31919,15 @@ snapshots: - solid-js - supports-color + '@rsbuild/plugin-tailwindcss@2.0.3(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23))(webpack@5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4))': + dependencies: + '@tailwindcss/webpack': 4.3.1(@rspack/core@2.0.8(@swc/helpers@0.5.23))(webpack@5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4)) + optionalDependencies: + '@rsbuild/core': 2.0.14 + transitivePeerDependencies: + - '@rspack/core' + - webpack + '@rsbuild/plugin-vue-jsx@1.1.1(@babel/core@7.29.0)(@rsbuild/core@2.0.14)': dependencies: '@rsbuild/plugin-babel': 1.1.2(@rsbuild/core@2.0.14) @@ -32418,42 +32603,88 @@ snapshots: source-map-js: 1.2.1 tailwindcss: 4.2.2 + '@tailwindcss/node@4.3.1': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.21.6 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.3.1 + '@tailwindcss/oxide-android-arm64@4.2.2': optional: true + '@tailwindcss/oxide-android-arm64@4.3.1': + optional: true + '@tailwindcss/oxide-darwin-arm64@4.2.2': optional: true + '@tailwindcss/oxide-darwin-arm64@4.3.1': + optional: true + '@tailwindcss/oxide-darwin-x64@4.2.2': optional: true + '@tailwindcss/oxide-darwin-x64@4.3.1': + optional: true + '@tailwindcss/oxide-freebsd-x64@4.2.2': optional: true + '@tailwindcss/oxide-freebsd-x64@4.3.1': + optional: true + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': optional: true + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1': + optional: true + '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': optional: true + '@tailwindcss/oxide-linux-arm64-gnu@4.3.1': + optional: true + '@tailwindcss/oxide-linux-arm64-musl@4.2.2': optional: true + '@tailwindcss/oxide-linux-arm64-musl@4.3.1': + optional: true + '@tailwindcss/oxide-linux-x64-gnu@4.2.2': optional: true + '@tailwindcss/oxide-linux-x64-gnu@4.3.1': + optional: true + '@tailwindcss/oxide-linux-x64-musl@4.2.2': optional: true + '@tailwindcss/oxide-linux-x64-musl@4.3.1': + optional: true + '@tailwindcss/oxide-wasm32-wasi@4.2.2': optional: true + '@tailwindcss/oxide-wasm32-wasi@4.3.1': + optional: true + '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': optional: true + '@tailwindcss/oxide-win32-arm64-msvc@4.3.1': + optional: true + '@tailwindcss/oxide-win32-x64-msvc@4.2.2': optional: true + '@tailwindcss/oxide-win32-x64-msvc@4.3.1': + optional: true + '@tailwindcss/oxide@4.2.2': optionalDependencies: '@tailwindcss/oxide-android-arm64': 4.2.2 @@ -32469,6 +32700,21 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.2.2 '@tailwindcss/oxide-win32-x64-msvc': 4.2.2 + '@tailwindcss/oxide@4.3.1': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.3.1 + '@tailwindcss/oxide-darwin-arm64': 4.3.1 + '@tailwindcss/oxide-darwin-x64': 4.3.1 + '@tailwindcss/oxide-freebsd-x64': 4.3.1 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.1 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.1 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.1 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.1 + '@tailwindcss/oxide-linux-x64-musl': 4.3.1 + '@tailwindcss/oxide-wasm32-wasi': 4.3.1 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.1 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.1 + '@tailwindcss/postcss@4.2.2': dependencies: '@alloc/quick-lru': 5.2.0 @@ -32484,6 +32730,16 @@ snapshots: tailwindcss: 4.2.2 vite: 8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0) + '@tailwindcss/webpack@4.3.1(@rspack/core@2.0.8(@swc/helpers@0.5.23))(webpack@5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4))': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.3.1 + '@tailwindcss/oxide': 4.3.1 + tailwindcss: 4.3.1 + optionalDependencies: + '@rspack/core': 2.0.8(@swc/helpers@0.5.23) + webpack: 5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4) + '@tanstack/devtools-client@0.0.4': dependencies: '@tanstack/devtools-event-client': 0.3.4 @@ -35253,9 +35509,9 @@ snapshots: optionalDependencies: srvx: 0.11.15 - crossws@0.4.5(srvx@0.11.15): + crossws@0.4.5(srvx@0.11.18): optionalDependencies: - srvx: 0.11.15 + srvx: 0.11.18 optional: true css-loader@7.1.2(webpack@5.97.1): @@ -35688,6 +35944,11 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.0 + enhanced-resolve@5.21.6: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + enquirer@2.3.6: dependencies: ansi-colors: 4.1.3 @@ -36830,12 +37091,12 @@ snapshots: optionalDependencies: crossws: 0.4.4(srvx@0.11.15) - h3@2.0.1-rc.20(crossws@0.4.5(srvx@0.11.15)): + h3@2.0.1-rc.20(crossws@0.4.5(srvx@0.11.18)): dependencies: rou3: 0.8.1 srvx: 0.11.15 optionalDependencies: - crossws: 0.4.5(srvx@0.11.15) + crossws: 0.4.5(srvx@0.11.18) handle-thing@2.0.1: {} @@ -40148,6 +40409,8 @@ snapshots: srvx@0.11.15: {} + srvx@0.11.18: {} + stable-hash-x@0.2.0: {} stack-trace@0.0.10: {} @@ -40309,10 +40572,14 @@ snapshots: tailwindcss@4.2.2: {} + tailwindcss@4.3.1: {} + tapable@2.2.1: {} tapable@2.3.0: {} + tapable@2.3.3: {} + tar-stream@2.2.0: dependencies: bl: 4.1.0 @@ -41323,7 +41590,7 @@ snapshots: acorn-import-phases: 1.0.4(acorn@8.15.0) browserslist: 4.28.1 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.18.3 + enhanced-resolve: 5.21.6 es-module-lexer: 2.0.0 eslint-scope: 5.1.1 events: 3.3.0 @@ -41334,7 +41601,7 @@ snapshots: mime-types: 2.1.35 neo-async: 2.6.2 schema-utils: 4.3.3 - tapable: 2.3.0 + tapable: 2.3.3 terser-webpack-plugin: 5.3.16(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4)(webpack@5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4)) watchpack: 2.4.4 webpack-sources: 3.3.3 From 2b08d9ba4b92f913eb5f89eaae1b46f52b5770e1 Mon Sep 17 00:00:00 2001 From: Sajad Torkamani Date: Wed, 22 Jul 2026 21:12:34 +0100 Subject: [PATCH 27/51] docs: minor docs improvements (#7842) --- docs/router/guide/automatic-code-splitting.md | 6 +++--- docs/router/guide/code-splitting.md | 2 +- docs/router/guide/navigation.md | 6 +++--- docs/router/routing/code-based-routing.md | 2 +- docs/router/routing/file-based-routing.md | 4 ++-- docs/router/routing/file-naming-conventions.md | 4 ++-- docs/router/routing/routing-concepts.md | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/router/guide/automatic-code-splitting.md b/docs/router/guide/automatic-code-splitting.md index 4612c298c6..be599845bc 100644 --- a/docs/router/guide/automatic-code-splitting.md +++ b/docs/router/guide/automatic-code-splitting.md @@ -35,7 +35,7 @@ This happens seamlessly, without requiring you to manually split your code or ma ### The transformation process -When you enable automatic code splitting, the bundler plugin does this by using static code analysis look at your the code in your route files to transform them into optimized outputs. +When you enable automatic code splitting, the bundler plugin does this by using static code analysis to look at the code in your route files and to transform them into optimized outputs. This transformation process produces two key outputs when each of your route files are processed: @@ -48,7 +48,7 @@ This process ensures that your original code remains clean and readable, while t The decision of what to split into separate chunks is crucial for optimizing your application's performance. TanStack Router uses a concept called "**Split Groupings**" to determine how different parts of your route should be bundled together. -Split groupings are arrays of properties that tell TanStack Router how to bundle different parts of your route together. Each grouping is an list of property names that you want to bundle together into a single lazy-loaded chunk. +Split groupings are arrays of properties that tell TanStack Router how to bundle different parts of your route together. Each grouping is a list of property names that you want to bundle together into a single lazy-loaded chunk. The available properties to split are: @@ -76,7 +76,7 @@ This means that it creates three separate lazy-loaded chunks for each route. Res ### Rules of Splitting -For automatic code splitting to work, there are some rules in-place to make sure that this process can reliably and predictably happen. +For automatic code splitting to work, there are some rules in place to make sure that this process can happen reliably and predictably. #### Do not export route properties diff --git a/docs/router/guide/code-splitting.md b/docs/router/guide/code-splitting.md index 698bf262b5..b1423253be 100644 --- a/docs/router/guide/code-splitting.md +++ b/docs/router/guide/code-splitting.md @@ -115,7 +115,7 @@ These are the only options that `createLazyFileRoute` supports: ### Example code splitting with `.lazy.tsx` -When you are using `.lazy.tsx` you can split your route into two files to enable code splitting: +When you are using `.lazy.tsx`, you can split your route into two files to enable code splitting: **Before (Single File)** diff --git a/docs/router/guide/navigation.md b/docs/router/guide/navigation.md index 3dd09cba47..dbabc0906a 100644 --- a/docs/router/guide/navigation.md +++ b/docs/router/guide/navigation.md @@ -11,7 +11,7 @@ TanStack Router keeps this constant concept of relative navigation in mind for e - `from` - The origin route path - `to` - The destination route path -> ⚠️ If a `from` route path isn't provided the router will assume you are navigating from the root `/` route and only auto-complete absolute paths. After all, you need to know where you are from in order to know where you're going 😉. +> ⚠️ If a `from` route path isn't provided, the router will assume you are navigating from the root `/` route and only autocomplete absolute paths. After all, you need to know where you are from in order to know where you're going 😉. ## Shared Navigation API @@ -143,7 +143,7 @@ Since `LinkOptions` extends `NavigateOptions`, it also supports `mask`. With relative navigation and all of the interfaces in mind now, let's talk about the different flavors of navigation API at your disposal: - The `` component - - Generates an actual `` tag with a valid `href` which can be click or even cmd/ctrl + clicked to open in a new tab + - Generates an actual `` tag with a valid `href` which can be clicked or even cmd/ctrl + clicked to open in a new tab. - The `useNavigate()` hook - When possible, `Link` component should be used for navigation, but sometimes you need to navigate imperatively as a result of a side-effect. `useNavigate` returns a function that can be called to perform an immediate client-side navigation. - The `` component @@ -309,7 +309,7 @@ const link = ( ### Search Param Type Safety -Search params are a highly dynamic state management mechanism, so it's important to ensure that you are passing the correct types to your search params. We'll see in a later section in detail how to validate and ensure search params typesafety, among other great features! +Search params are a highly dynamic state management mechanism, so it's important to ensure that you are passing the correct types to your search params. We'll see in a later section in detail how to validate and ensure search params type safety, among other great features! ### Hash Links diff --git a/docs/router/routing/code-based-routing.md b/docs/router/routing/code-based-routing.md index d3102cafd6..b1457c6fbe 100644 --- a/docs/router/routing/code-based-routing.md +++ b/docs/router/routing/code-based-routing.md @@ -14,7 +14,7 @@ title: Code-Based Routing Code-based routing is no different from file-based routing in that it uses the same route tree concept to organize, match and compose matching routes into a component tree. The only difference is that instead of using the filesystem to organize your routes, you use code. -Let's consider the same route tree from the [Route Trees & Nesting](./route-trees.md#route-trees) guide, and convert it to code-based routing: +Let's consider the same route tree from the [Route Trees](./route-trees.md#route-trees) guide, and convert it to code-based routing: Here is the file-based version: diff --git a/docs/router/routing/file-based-routing.md b/docs/router/routing/file-based-routing.md index 6be11e55d6..2eea38bba2 100644 --- a/docs/router/routing/file-based-routing.md +++ b/docs/router/routing/file-based-routing.md @@ -12,7 +12,7 @@ File-based routing is a way to configure your routes using the filesystem. Inste - **Organization**: Routes are organized in a way that mirrors the URL structure of your application. - **Scalability**: As your application grows, file-based routing makes it easy to add new routes and maintain existing ones. - **Code-Splitting**: File-based routing allows TanStack Router to automatically code-split your routes for better performance. -- **Type-Safety**: File-based routing raises the ceiling on type-safety by generating managing type linkages for your routes, which can otherwise be a tedious process via code-based routing. +- **Type-Safety**: File-based routing raises the ceiling on type-safety by generating and managing type linkages for your routes, which can otherwise be a tedious process via code-based routing. - **Consistency**: File-based routing enforces a consistent structure for your routes, making it easier to maintain and update your application and move from one project to another. ## `/`s or `.`s? @@ -80,7 +80,7 @@ See the example below: ## Mixed Flat and Directory Routes -It's extremely likely that a 100% directory or flat route structure won't be the best fit for your project, which is why TanStack Router allows you to mix both flat and directory routes together to create a route tree that uses the best of both worlds where it makes sense: +It's extremely likely that a 100% directory or flat route structure won't be the best fit for your project, which is why TanStack Router allows you to mix both flat and directory routes together to create a route tree that uses the best of both worlds where it makes sense. See the example below: diff --git a/docs/router/routing/file-naming-conventions.md b/docs/router/routing/file-naming-conventions.md index 115310f5b3..896e7d3d9b 100644 --- a/docs/router/routing/file-naming-conventions.md +++ b/docs/router/routing/file-naming-conventions.md @@ -2,7 +2,7 @@ title: File Naming Conventions --- -File-based routing requires that you follow a few simple file naming conventions to ensure that your routes are generated correctly. The concepts these conventions enable are covered in detail in the [Route Trees & Nesting](./route-trees.md) guide. +File-based routing requires that you follow a few simple file naming conventions to ensure that your routes are generated correctly. The concepts these conventions enable are covered in detail in the [Route Trees](./route-trees.md) guide. | Feature | Description | | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -40,4 +40,4 @@ Pathless routes wrap child routes with either logic or a component without requi | ʦ `_app.a.tsx` | /a | `` | | ʦ `_app.b.tsx` | /b | `` | -To learn more about pathless routes, see the [Routing Concepts - Pathless Routes](./routing-concepts.md#pathless-layout-routes) guide. +To learn more about pathless routes, see the [Routing Concepts - Pathless Layout Routes](./routing-concepts.md#pathless-layout-routes) guide. diff --git a/docs/router/routing/routing-concepts.md b/docs/router/routing/routing-concepts.md index 88eebbd5f0..11674e69c2 100644 --- a/docs/router/routing/routing-concepts.md +++ b/docs/router/routing/routing-concepts.md @@ -564,7 +564,7 @@ routes/ │ ├── ... ``` -We can import from the excluded files into our posts route +We can import from the excluded files into our posts route: From a76c192f1c8e016e58944bd5b083e242a7538c4d Mon Sep 17 00:00:00 2001 From: Sarah Gerrard Date: Wed, 22 Jul 2026 13:13:55 -0700 Subject: [PATCH 28/51] docs: Update Guides section and add back in missing pages (#7864) --- docs/router/config.json | 141 +++++++++++++-------- docs/start/config.json | 271 +++++++++++++++++++++++++--------------- 2 files changed, 263 insertions(+), 149 deletions(-) diff --git a/docs/router/config.json b/docs/router/config.json index bb30fabc4a..1258ddd817 100644 --- a/docs/router/config.json +++ b/docs/router/config.json @@ -94,7 +94,7 @@ ] }, { - "label": "Routing", + "label": "Core Routing", "children": [ { "label": "Routing Concepts", @@ -123,6 +123,10 @@ { "label": "File Naming Conventions", "to": "routing/file-naming-conventions" + }, + { + "label": "URL Rewrites", + "to": "guide/url-rewrites" } ], "frameworks": [ @@ -137,47 +141,76 @@ ] }, { - "label": "Guides", + "label": "Navigation & URL State", + "collapsible": true, "children": [ { - "label": "Code Splitting", - "to": "guide/code-splitting" + "label": "Navigation", + "to": "guide/navigation" }, { - "label": "Automatic Code Splitting", - "to": "guide/automatic-code-splitting" + "label": "Link Options", + "to": "guide/link-options" }, { - "label": "Creating a Router", - "to": "guide/creating-a-router" + "label": "Custom Links", + "to": "guide/custom-link" }, { - "label": "Outlets", - "to": "guide/outlets" + "label": "Path Params", + "to": "guide/path-params" }, { - "label": "Navigation", - "to": "guide/navigation" + "label": "Search Params", + "to": "guide/search-params" }, { - "label": "Router Events", - "to": "guide/router-events" + "label": "Custom Search Param Serialization", + "to": "guide/custom-search-param-serialization" }, { - "label": "Path Params", - "to": "guide/path-params" + "label": "Route Masking", + "to": "guide/route-masking" }, { - "label": "Search Params", - "to": "guide/search-params" + "label": "Navigation Blocking", + "to": "guide/navigation-blocking" }, { - "label": "Link Options", - "to": "guide/link-options" + "label": "History Types", + "to": "guide/history-types" }, { - "label": "Custom Links", - "to": "guide/custom-link" + "label": "Scroll Restoration", + "to": "guide/scroll-restoration" + }, + { + "label": "Internationalization", + "to": "guide/internationalization-i18n" + } + ], + "frameworks": [ + { + "label": "react", + "children": [] + }, + { + "label": "solid", + "children": [] + } + ] + }, + { + "label": "Data & Rendering", + "collapsible": true, + "children": [ + { + "label": "Code Splitting", + "to": "guide/code-splitting" + }, + { + "label": "Automatic Code Splitting", + "to": "guide/automatic-code-splitting" }, { "label": "Data Loading", @@ -195,14 +228,6 @@ "label": "Data Mutations", "to": "guide/data-mutations" }, - { - "label": "Type Safety", - "to": "guide/type-safety" - }, - { - "label": "Type Utilities", - "to": "guide/type-utilities" - }, { "label": "Preloading", "to": "guide/preloading" @@ -212,20 +237,48 @@ "to": "guide/document-head-management" }, { - "label": "Route Masking", - "to": "guide/route-masking" + "label": "SSR", + "to": "guide/ssr" }, { - "label": "Navigation Blocking", - "to": "guide/navigation-blocking" + "label": "Render Optimizations", + "to": "guide/render-optimizations" + } + ], + "frameworks": [ + { + "label": "react", + "children": [] }, { - "label": "Custom Search Param Serialization", - "to": "guide/custom-search-param-serialization" + "label": "solid", + "children": [] + } + ] + }, + { + "label": "Router Configuration", + "collapsible": true, + "children": [ + { + "label": "Creating a Router", + "to": "guide/creating-a-router" }, { - "label": "History Types", - "to": "guide/history-types" + "label": "Outlets", + "to": "guide/outlets" + }, + { + "label": "Router Events", + "to": "guide/router-events" + }, + { + "label": "Type Safety", + "to": "guide/type-safety" + }, + { + "label": "Type Utilities", + "to": "guide/type-utilities" }, { "label": "Router Context", @@ -239,21 +292,9 @@ "label": "Authenticated Routes", "to": "guide/authenticated-routes" }, - { - "label": "Scroll Restoration", - "to": "guide/scroll-restoration" - }, { "label": "Static Route Data", "to": "guide/static-route-data" - }, - { - "label": "SSR", - "to": "guide/ssr" - }, - { - "label": "Render Optimizations", - "to": "guide/render-optimizations" } ], "frameworks": [ diff --git a/docs/start/config.json b/docs/start/config.json index e93688eb91..57703aad89 100644 --- a/docs/start/config.json +++ b/docs/start/config.json @@ -59,8 +59,9 @@ ] }, { - "label": "Guides", + "label": "Server & Execution", "children": [], + "collapsible": true, "frameworks": [ { "label": "react", @@ -81,6 +82,10 @@ "label": "Import Protection", "to": "framework/react/guide/import-protection" }, + { + "label": "Path Aliases", + "to": "framework/react/guide/path-aliases" + }, { "label": "Environment Variables", "to": "framework/react/guide/environment-variables" @@ -89,6 +94,10 @@ "label": "Server Functions", "to": "framework/react/guide/server-functions" }, + { + "label": "Streaming Data from Server Functions", + "to": "framework/react/guide/streaming-data-from-server-functions" + }, { "label": "Server Components", "to": "framework/react/guide/server-components" @@ -112,94 +121,6 @@ { "label": "Server Routes", "to": "framework/react/guide/server-routes" - }, - { - "label": "Hydration Errors", - "to": "framework/react/guide/hydration-errors" - }, - { - "label": "Deferred Hydration", - "to": "framework/react/guide/deferred-hydration" - }, - { - "label": "Selective SSR", - "to": "framework/react/guide/selective-ssr" - }, - { - "label": "SPA Mode", - "to": "framework/react/guide/spa-mode" - }, - { - "label": "Static Prerendering", - "to": "framework/react/guide/static-prerendering" - }, - { - "label": "Incremental Static Regeneration (ISR)", - "to": "framework/react/guide/isr" - }, - { - "label": "Server Entry Point", - "to": "framework/react/guide/server-entry-point" - }, - { - "label": "Early Hints", - "to": "framework/react/guide/early-hints" - }, - { - "label": "CDN Asset URLs", - "to": "framework/react/guide/cdn-asset-urls" - }, - { - "label": "CSS Styling", - "to": "framework/react/guide/css-styling" - }, - { - "label": "Client Entry Point", - "to": "framework/react/guide/client-entry-point" - }, - { - "label": "Hosting", - "to": "framework/react/guide/hosting" - }, - { - "label": "Authentication Overview", - "to": "framework/react/guide/authentication-overview" - }, - { - "label": "Authentication Server Primitives", - "to": "framework/react/guide/authentication-server-primitives" - }, - { - "label": "Authentication", - "to": "framework/react/guide/authentication" - }, - { - "label": "Databases", - "to": "framework/react/guide/databases" - }, - { - "label": "Observability", - "to": "framework/react/guide/observability" - }, - { - "label": "Path Aliases", - "to": "framework/react/guide/path-aliases" - }, - { - "label": "Tailwind CSS Integration", - "to": "framework/react/guide/tailwind-integration" - }, - { - "label": "Rendering Markdown", - "to": "framework/react/guide/rendering-markdown" - }, - { - "label": "SEO", - "to": "framework/react/guide/seo" - }, - { - "label": "Generative Engine Optimization (GEO)", - "to": "framework/react/guide/geo" } ] }, @@ -222,6 +143,10 @@ "label": "Import Protection", "to": "framework/solid/guide/import-protection" }, + { + "label": "Path Aliases", + "to": "framework/solid/guide/path-aliases" + }, { "label": "Environment Variables", "to": "framework/solid/guide/environment-variables" @@ -249,7 +174,64 @@ { "label": "Server Routes", "to": "framework/solid/guide/server-routes" + } + ] + } + ] + }, + { + "label": "Rendering", + "children": [], + "collapsible": true, + "frameworks": [ + { + "label": "react", + "children": [ + { + "label": "Hydration Errors", + "to": "framework/react/guide/hydration-errors" + }, + { + "label": "Deferred Hydration", + "to": "framework/react/guide/deferred-hydration" + }, + { + "label": "Selective SSR", + "to": "framework/react/guide/selective-ssr" + }, + { + "label": "SPA Mode", + "to": "framework/react/guide/spa-mode" + }, + { + "label": "Static Prerendering", + "to": "framework/react/guide/static-prerendering" + }, + { + "label": "Incremental Static Regeneration (ISR)", + "to": "framework/react/guide/isr" }, + { + "label": "Server Entry Point", + "to": "framework/react/guide/server-entry-point" + }, + { + "label": "Client Entry Point", + "to": "framework/react/guide/client-entry-point" + }, + { + "label": "Early Hints", + "to": "framework/react/guide/early-hints" + }, + { + "label": "CDN Asset URLs", + "to": "framework/react/guide/cdn-asset-urls" + } + ] + }, + { + "label": "solid", + "children": [ { "label": "Hydration Errors", "to": "framework/solid/guide/hydration-errors" @@ -274,22 +256,80 @@ "label": "Server Entry Point", "to": "framework/solid/guide/server-entry-point" }, + { + "label": "Client Entry Point", + "to": "framework/solid/guide/client-entry-point" + }, { "label": "Early Hints", "to": "framework/solid/guide/early-hints" - }, + } + ] + } + ] + }, + { + "label": "Deployment & Operations", + "children": [], + "collapsible": true, + "frameworks": [ + { + "label": "react", + "children": [ { - "label": "CSS Styling", - "to": "framework/solid/guide/css-styling" + "label": "Hosting", + "to": "framework/react/guide/hosting" }, { - "label": "Client Entry Point", - "to": "framework/solid/guide/client-entry-point" - }, + "label": "Observability", + "to": "framework/react/guide/observability" + } + ] + }, + { + "label": "solid", + "children": [ { "label": "Hosting", "to": "framework/solid/guide/hosting" }, + { + "label": "Observability", + "to": "framework/solid/guide/observability" + } + ] + } + ] + }, + { + "label": "Authentication & Data", + "children": [], + "collapsible": true, + "frameworks": [ + { + "label": "react", + "children": [ + { + "label": "Authentication Overview", + "to": "framework/react/guide/authentication-overview" + }, + { + "label": "Authentication Server Primitives", + "to": "framework/react/guide/authentication-server-primitives" + }, + { + "label": "Authentication", + "to": "framework/react/guide/authentication" + }, + { + "label": "Databases", + "to": "framework/react/guide/databases" + } + ] + }, + { + "label": "solid", + "children": [ { "label": "Authentication Overview", "to": "framework/solid/guide/authentication-overview" @@ -305,14 +345,47 @@ { "label": "Databases", "to": "framework/solid/guide/databases" + } + ] + } + ] + }, + { + "label": "Styling & Metadata", + "children": [], + "collapsible": true, + "frameworks": [ + { + "label": "react", + "children": [ + { + "label": "CSS Styling", + "to": "framework/react/guide/css-styling" }, { - "label": "Observability", - "to": "framework/solid/guide/observability" + "label": "Tailwind CSS Integration", + "to": "framework/react/guide/tailwind-integration" }, { - "label": "Path Aliases", - "to": "framework/solid/guide/path-aliases" + "label": "Rendering Markdown", + "to": "framework/react/guide/rendering-markdown" + }, + { + "label": "SEO", + "to": "framework/react/guide/seo" + }, + { + "label": "Generative Engine Optimization (GEO)", + "to": "framework/react/guide/geo" + } + ] + }, + { + "label": "solid", + "children": [ + { + "label": "CSS Styling", + "to": "framework/solid/guide/css-styling" }, { "label": "Tailwind CSS Integration", From 3a5575627d46e765f7fab2e5488657d2b739273c Mon Sep 17 00:00:00 2001 From: Flo Date: Thu, 23 Jul 2026 22:13:21 +0200 Subject: [PATCH 29/51] test: add regression coverage for existing router behavior (#7886) --- .../basic/src/routes/specialChars/search.tsx | 7 + .../basic/tests/special-characters.spec.ts | 5 + .../selective-ssr/src/routeTree.gen.ts | 24 +- e2e/react-start/selective-ssr/src/router.tsx | 3 + .../selective-ssr/src/routes/__root.tsx | 30 +- .../selective-ssr/src/routes/issue-4614.tsx | 15 + .../selective-ssr/tests/app.spec.ts | 31 + packages/react-router/tests/Matches.test.tsx | 142 ++++- packages/react-router/tests/Scripts.test.tsx | 13 +- ...mponent-preload-retry-pending-min.test.tsx | 40 ++ .../tests/errorComponent.test.tsx | 338 ++++++++++- .../tests/hydration-terminal-lane.test.tsx | 98 ++++ packages/react-router/tests/loaders.test.tsx | 41 +- .../react-router/tests/not-found.test.tsx | 41 +- ...public-presentation-lane-contract.test.tsx | 133 +++++ packages/react-router/tests/redirect.test.tsx | 42 +- .../tests/renderRouterToStream.test.tsx | 5 +- .../react-router/tests/routeContext.test.tsx | 32 +- packages/react-router/tests/router.test.tsx | 329 ++++++----- .../tests/transactional-loading.test.tsx | 97 ++++ .../tests/transitioner-remount.test.tsx | 91 +++ .../tests/transitioner-render-ack.test.tsx | 196 +++++++ packages/react-router/tests/useMatch.test.tsx | 153 ++++- .../react-router/tests/useNavigate.test.tsx | 54 +- .../src/tests/hydrateStart.test.ts | 23 + .../tests/background-trim-abort.test.ts | 128 ++++ .../blocked-navigation-current-load.test.ts | 75 +++ .../tests/boundary-component-chunk.test.ts | 75 +++ packages/router-core/tests/callbacks.test.ts | 6 - .../tests/client-lane-adversarial.test.ts | 79 +++ .../error-boundary-cache-generation.test.ts | 77 +++ .../router-core/tests/granular-stores.test.ts | 85 +-- packages/router-core/tests/hydrate.test.ts | 459 +++++++++------ ...ue-5106-hydrated-notfound-boundary.test.ts | 7 +- .../issue-6221-head-waits-for-loader.test.ts | 91 +++ packages/router-core/tests/load.test.ts | 141 ++--- .../loader-architecture-regressions.test.ts | 257 +++++++++ .../tests/loader-self-abort.test.ts | 59 ++ .../masked-location-state-commit.test.ts | 49 ++ .../tests/parent-match-promise.test.ts | 78 +++ .../tests/preload-adoption.test.ts | 133 +++++ ...reload-background-parent-coherence.test.ts | 88 +++ .../tests/preload-beforeload-reuse.test.ts | 443 ++++++++++++++ .../tests/preload-navigation-adoption.test.ts | 143 +++++ .../preload-public-cache-behavior.test.ts | 186 ++++++ .../public-client-loading-contract.test.ts | 115 ++++ .../public-preload-lane-contract.test.ts | 546 ++++++++++++++++++ .../same-destination-navigation-join.test.ts | 92 +++ .../serial-failure-foreground-prefix.test.ts | 71 +++ .../server-beforeload-preload-flag.test.ts | 25 + .../tests/server-loader-abort-error.test.ts | 33 ++ .../router-core/tests/ssr-match-id.test.ts | 7 +- .../tests/superseded-load-await.test.ts | 90 +++ packages/router-plugin/tests/add-hmr.test.ts | 1 - .../tests/handle-route-update.test.ts | 221 ++++++- packages/solid-router/tests/Matches.test.tsx | 69 +++ .../solid-router/tests/Transitioner.test.tsx | 1 - .../tests/component-preload-retry.test.tsx | 21 + .../tests/createLazyRoute.test.tsx | 63 ++ packages/solid-router/tests/link.test.tsx | 58 +- packages/solid-router/tests/loaders.test.tsx | 61 +- packages/solid-router/tests/redirect.test.tsx | 14 +- .../tests/renderRouterToStream.test.tsx | 3 +- .../solid-router/tests/routeContext.test.tsx | 32 +- packages/solid-router/tests/router.test.tsx | 110 ++-- .../tests/same-route-pending-blank.test.tsx | 100 ++++ .../tests/server/errorComponent.test.tsx | 36 +- .../tests/transitioner-remount.test.tsx | 91 +++ .../use-match-outgoing-transition.test.tsx | 126 ++++ packages/solid-router/tests/useMatch.test.tsx | 153 ++++- .../src/tests/hydrateStart.test.ts | 23 + .../tests/createStartHandler.test.ts | 2 + packages/vue-router/tests/Matches.test.tsx | 76 ++- .../vue-router/tests/Transitioner.test.tsx | 23 +- .../tests/disableGlobalCatchBoundary.test.tsx | 28 + .../vue-router/tests/errorComponent.test.tsx | 103 ++++ packages/vue-router/tests/link.test.tsx | 21 +- packages/vue-router/tests/loaders.test.tsx | 20 +- ...ding-fallback-promise-replacement.test.tsx | 78 +++ packages/vue-router/tests/redirect.test.tsx | 18 +- .../tests/renderRouterToStream.test.tsx | 3 +- packages/vue-router/tests/router.test.tsx | 42 +- .../transitioner-idle-after-render.test.tsx | 46 ++ .../transitioner-remount-rendered.test.tsx | 56 ++ .../tests/transitioner-remount.test.tsx | 92 +++ packages/vue-router/tests/useMatch.test.tsx | 163 +++++- 86 files changed, 6786 insertions(+), 789 deletions(-) create mode 100644 e2e/react-start/selective-ssr/src/routes/issue-4614.tsx create mode 100644 packages/react-router/tests/component-preload-retry-pending-min.test.tsx create mode 100644 packages/react-router/tests/hydration-terminal-lane.test.tsx create mode 100644 packages/react-router/tests/public-presentation-lane-contract.test.tsx create mode 100644 packages/react-router/tests/transactional-loading.test.tsx create mode 100644 packages/react-router/tests/transitioner-remount.test.tsx create mode 100644 packages/react-router/tests/transitioner-render-ack.test.tsx create mode 100644 packages/react-start-client/src/tests/hydrateStart.test.ts create mode 100644 packages/router-core/tests/background-trim-abort.test.ts create mode 100644 packages/router-core/tests/blocked-navigation-current-load.test.ts create mode 100644 packages/router-core/tests/boundary-component-chunk.test.ts create mode 100644 packages/router-core/tests/client-lane-adversarial.test.ts create mode 100644 packages/router-core/tests/error-boundary-cache-generation.test.ts create mode 100644 packages/router-core/tests/issue-6221-head-waits-for-loader.test.ts create mode 100644 packages/router-core/tests/loader-architecture-regressions.test.ts create mode 100644 packages/router-core/tests/loader-self-abort.test.ts create mode 100644 packages/router-core/tests/masked-location-state-commit.test.ts create mode 100644 packages/router-core/tests/parent-match-promise.test.ts create mode 100644 packages/router-core/tests/preload-adoption.test.ts create mode 100644 packages/router-core/tests/preload-background-parent-coherence.test.ts create mode 100644 packages/router-core/tests/preload-beforeload-reuse.test.ts create mode 100644 packages/router-core/tests/preload-navigation-adoption.test.ts create mode 100644 packages/router-core/tests/preload-public-cache-behavior.test.ts create mode 100644 packages/router-core/tests/public-client-loading-contract.test.ts create mode 100644 packages/router-core/tests/public-preload-lane-contract.test.ts create mode 100644 packages/router-core/tests/same-destination-navigation-join.test.ts create mode 100644 packages/router-core/tests/serial-failure-foreground-prefix.test.ts create mode 100644 packages/router-core/tests/server-beforeload-preload-flag.test.ts create mode 100644 packages/router-core/tests/server-loader-abort-error.test.ts create mode 100644 packages/router-core/tests/superseded-load-await.test.ts create mode 100644 packages/solid-router/tests/component-preload-retry.test.tsx create mode 100644 packages/solid-router/tests/same-route-pending-blank.test.tsx create mode 100644 packages/solid-router/tests/transitioner-remount.test.tsx create mode 100644 packages/solid-router/tests/use-match-outgoing-transition.test.tsx create mode 100644 packages/solid-start-client/src/tests/hydrateStart.test.ts create mode 100644 packages/vue-router/tests/pending-fallback-promise-replacement.test.tsx create mode 100644 packages/vue-router/tests/transitioner-idle-after-render.test.tsx create mode 100644 packages/vue-router/tests/transitioner-remount-rendered.test.tsx create mode 100644 packages/vue-router/tests/transitioner-remount.test.tsx diff --git a/e2e/react-start/basic/src/routes/specialChars/search.tsx b/e2e/react-start/basic/src/routes/specialChars/search.tsx index 9e4b42e888..5840d425ab 100644 --- a/e2e/react-start/basic/src/routes/specialChars/search.tsx +++ b/e2e/react-start/basic/src/routes/specialChars/search.tsx @@ -7,6 +7,9 @@ export const Route = createFileRoute('/specialChars/search')({ validateSearch: z.object({ searchParam: z.string(), }), + beforeLoad: () => ({ + beforeLoadOn: isBrowser ? 'client' : 'server', + }), loader: async () => { console.log(`[loader] Running on ${isBrowser ? 'client' : 'server'}`) return { @@ -18,11 +21,15 @@ export const Route = createFileRoute('/specialChars/search')({ function RouteComponent() { const search = Route.useSearch() + const { beforeLoadOn } = Route.useRouteContext() const { loadedOn } = Route.useLoaderData() return (
Hello "/specialChars/search"! +
+ Before load on: {beforeLoadOn} +
Loaded on: {loadedOn}
{search.searchParam}
diff --git a/e2e/react-start/basic/tests/special-characters.spec.ts b/e2e/react-start/basic/tests/special-characters.spec.ts index 7cf50c39c8..188d1f10a6 100644 --- a/e2e/react-start/basic/tests/special-characters.spec.ts +++ b/e2e/react-start/basic/tests/special-characters.spec.ts @@ -104,11 +104,16 @@ test.describe('Unicode route rendering', () => { const loadedOn = await page .getByTestId('special-search-loaded-info') .textContent() + const beforeLoadOn = await page + .getByTestId('special-search-before-load-info') + .textContent() if (isSpaMode) { expect(loadedOn).toBe('Loaded on: client') + expect(beforeLoadOn).toBe('Before load on: client') } else { expect(loadedOn).toBe('Loaded on: server') + expect(beforeLoadOn).toBe('Before load on: server') } }) diff --git a/e2e/react-start/selective-ssr/src/routeTree.gen.ts b/e2e/react-start/selective-ssr/src/routeTree.gen.ts index 1bba3ef326..5c7daa4701 100644 --- a/e2e/react-start/selective-ssr/src/routeTree.gen.ts +++ b/e2e/react-start/selective-ssr/src/routeTree.gen.ts @@ -10,6 +10,7 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' +import { Route as Issue4614RouteImport } from './routes/issue-4614' import { Route as PostsRouteImport } from './routes/posts' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' @@ -18,6 +19,11 @@ const IndexRoute = IndexRouteImport.update({ path: '/', getParentRoute: () => rootRouteImport, } as any) +const Issue4614Route = Issue4614RouteImport.update({ + id: '/issue-4614', + path: '/issue-4614', + getParentRoute: () => rootRouteImport, +} as any) const PostsRoute = PostsRouteImport.update({ id: '/posts', path: '/posts', @@ -31,30 +37,34 @@ const PostsPostIdRoute = PostsPostIdRouteImport.update({ export interface FileRoutesByFullPath { '/': typeof IndexRoute + '/issue-4614': typeof Issue4614Route '/posts': typeof PostsRouteWithChildren '/posts/$postId': typeof PostsPostIdRoute } export interface FileRoutesByTo { '/': typeof IndexRoute + '/issue-4614': typeof Issue4614Route '/posts': typeof PostsRouteWithChildren '/posts/$postId': typeof PostsPostIdRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute + '/issue-4614': typeof Issue4614Route '/posts': typeof PostsRouteWithChildren '/posts/$postId': typeof PostsPostIdRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' | '/posts' | '/posts/$postId' + fullPaths: '/' | '/issue-4614' | '/posts' | '/posts/$postId' fileRoutesByTo: FileRoutesByTo - to: '/' | '/posts' | '/posts/$postId' - id: '__root__' | '/' | '/posts' | '/posts/$postId' + to: '/' | '/issue-4614' | '/posts' | '/posts/$postId' + id: '__root__' | '/' | '/issue-4614' | '/posts' | '/posts/$postId' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute + Issue4614Route: typeof Issue4614Route PostsRoute: typeof PostsRouteWithChildren } @@ -67,6 +77,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/issue-4614': { + id: '/issue-4614' + path: '/issue-4614' + fullPath: '/issue-4614' + preLoaderRoute: typeof Issue4614RouteImport + parentRoute: typeof rootRouteImport + } '/posts': { id: '/posts' path: '/posts' @@ -96,6 +113,7 @@ const PostsRouteWithChildren = PostsRoute._addFileChildren(PostsRouteChildren) const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, + Issue4614Route: Issue4614Route, PostsRoute: PostsRouteWithChildren, } export const routeTree = rootRouteImport diff --git a/e2e/react-start/selective-ssr/src/router.tsx b/e2e/react-start/selective-ssr/src/router.tsx index 82a730704a..d227bfa5d2 100644 --- a/e2e/react-start/selective-ssr/src/router.tsx +++ b/e2e/react-start/selective-ssr/src/router.tsx @@ -5,6 +5,9 @@ export function getRouter() { const router = createRouter({ routeTree, scrollRestoration: true, + defaultPreload: 'intent', + defaultPreloadDelay: 0, + defaultPreloadStaleTime: 0, }) return router diff --git a/e2e/react-start/selective-ssr/src/routes/__root.tsx b/e2e/react-start/selective-ssr/src/routes/__root.tsx index 1672e3ff68..bc71c44c1c 100644 --- a/e2e/react-start/selective-ssr/src/routes/__root.tsx +++ b/e2e/react-start/selective-ssr/src/routes/__root.tsx @@ -30,7 +30,11 @@ export const Route = createRootRoute({ ], links: [{ rel: 'stylesheet', href: appCss }], }), - validateSearch: z.object({ root: ssrSchema }), + validateSearch: z.object({ + root: ssrSchema, + issue4614: z.string().optional(), + }), + loaderDeps: ({ search }) => ({ issue4614: search.issue4614 }), ssr: ({ search }) => { if (typeof window !== 'undefined') { const error = `ssr() for ${Route.id} should not be called on the client` @@ -41,7 +45,7 @@ export const Route = createRootRoute({ return search.value.root?.ssr } }, - beforeLoad: ({ search }) => { + beforeLoad: ({ search, location, cause, preload }) => { console.log( `beforeLoad for ${Route.id} called on the ${typeof window !== 'undefined' ? 'client' : 'server'}`, ) @@ -53,9 +57,21 @@ export const Route = createRootRoute({ console.error(error) throw new Error(error) } + const root = typeof window === 'undefined' ? 'server' : 'client' + const issue4614Context = `${root}:${search.issue4614 ?? 'cached'}` + if (typeof window !== 'undefined' && location.pathname === '/issue-4614') { + const calls = ((globalThis as any).__issue4614RootBeforeLoads ??= []) + calls.push({ + cause, + preload, + root, + issue4614Context, + }) + } return { - root: typeof window === 'undefined' ? 'server' : 'client', + root, search, + issue4614Context, } }, loader: ({ context }) => { @@ -133,6 +149,14 @@ function RootDocument({ children }: { children: React.ReactNode }) { > Home + + Issue 4614 reloaded parent +

diff --git a/e2e/react-start/selective-ssr/src/routes/issue-4614.tsx b/e2e/react-start/selective-ssr/src/routes/issue-4614.tsx new file mode 100644 index 0000000000..7e59b8d0ac --- /dev/null +++ b/e2e/react-start/selective-ssr/src/routes/issue-4614.tsx @@ -0,0 +1,15 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/issue-4614')({ + ssr: false, + beforeLoad: ({ context, cause, preload, search }) => { + ;(globalThis as any).__issue4614TargetBeforeLoad = { + cause, + preload, + rootContext: context.root, + issue4614Context: context.issue4614Context, + scenario: search.issue4614 ?? 'cached', + } + }, + component: () =>
Issue 4614
, +}) diff --git a/e2e/react-start/selective-ssr/tests/app.spec.ts b/e2e/react-start/selective-ssr/tests/app.spec.ts index aea216d506..35df560e92 100644 --- a/e2e/react-start/selective-ssr/tests/app.spec.ts +++ b/e2e/react-start/selective-ssr/tests/app.spec.ts @@ -4,6 +4,37 @@ import { test } from '@tanstack/router-e2e-utils' const testCount = 7 test.describe('selective ssr', () => { + test('new loaderDeps match generation propagates fresh parent context to child (control)', async ({ + page, + }) => { + await page.goto('/') + await page.getByTestId('issue-4614-reload-link').hover() + + await expect + .poll(() => + page.evaluate(() => (globalThis as any).__issue4614RootBeforeLoads), + ) + .toEqual([ + { + cause: 'preload', + preload: true, + root: 'client', + issue4614Context: 'client:reload', + }, + ]) + await expect + .poll(() => + page.evaluate(() => (globalThis as any).__issue4614TargetBeforeLoad), + ) + .toEqual({ + cause: 'preload', + preload: true, + rootContext: 'client', + issue4614Context: 'client:reload', + scenario: 'reload', + }) + }) + test('testcount matches', async ({ page }) => { await page.goto('/') diff --git a/packages/react-router/tests/Matches.test.tsx b/packages/react-router/tests/Matches.test.tsx index dd3ca3dfa6..33153a4e11 100644 --- a/packages/react-router/tests/Matches.test.tsx +++ b/packages/react-router/tests/Matches.test.tsx @@ -1,6 +1,14 @@ import { afterEach, describe, expect, test } from 'vitest' -import { act, cleanup, fireEvent, render, screen } from '@testing-library/react' +import { + act, + cleanup, + fireEvent, + render, + screen, + waitFor, +} from '@testing-library/react' import { createMemoryHistory } from '@tanstack/history' +import { createControlledPromise } from '@tanstack/router-core' import { Link, Outlet, @@ -126,11 +134,11 @@ test('when filtering useMatches by loaderData', async () => { test('should show pendingComponent of root route', async () => { const root = createRootRoute({ - pendingComponent: () =>
, + pendingComponent: () =>
, loader: async () => { await new Promise((r) => setTimeout(r, 50)) }, - component: () =>
, + component: () =>
, }) const router = createRouter({ @@ -145,6 +153,134 @@ test('should show pendingComponent of root route', async () => { expect(await rendered.findByTestId('root-content')).toBeInTheDocument() }) +test('useMatchRoute follows superseding pending locations', async () => { + const aGate = createControlledPromise() + const bGate = createControlledPromise() + + function Layout() { + const matchRoute = useMatchRoute() + return ( +
+ + {String(Boolean(matchRoute({ to: '/a', pending: true })))} + + + {String(Boolean(matchRoute({ to: '/b', pending: true })))} + + + {String(Boolean(matchRoute({ to: '/b', pending: false })))} + + +
+ ) + } + + const root = createRootRoute({ component: Layout }) + const index = createRoute({ + getParentRoute: () => root, + path: '/', + }) + const a = createRoute({ + getParentRoute: () => root, + path: '/a', + loader: () => aGate, + }) + const b = createRoute({ + getParentRoute: () => root, + path: '/b', + loader: () => bGate, + }) + const router = createRouter({ + routeTree: root.addChildren([index, a, b]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + render() + + await waitFor(() => { + expect(screen.getByTestId('pending-a')).toHaveTextContent('false') + expect(router.stores.status.get()).toBe('idle') + expect(router.stores.resolvedLocation.get()?.pathname).toBe('/') + }) + + let navigationA!: Promise + act(() => { + navigationA = router.navigate({ to: '/a' }) + }) + await waitFor(() => { + expect(screen.getByTestId('pending-a')).toHaveTextContent('true') + expect(screen.getByTestId('resolved-b')).toHaveTextContent('false') + }) + + let navigationB!: Promise + act(() => { + navigationB = router.navigate({ to: '/b' }) + }) + await waitFor(() => { + expect(screen.getByTestId('pending-a')).toHaveTextContent('false') + expect(screen.getByTestId('pending-b')).toHaveTextContent('true') + expect(screen.getByTestId('resolved-b')).toHaveTextContent('false') + }) + + await act(async () => { + aGate.resolve() + bGate.resolve() + await Promise.allSettled([navigationA, navigationB]) + }) + await waitFor(() => { + expect(screen.getByTestId('pending-b')).toHaveTextContent('false') + expect(screen.getByTestId('resolved-b')).toHaveTextContent('true') + }) +}) + +test('legacy notFoundRoute drops a stale parent layout after navigation', async () => { + let legacyLoads = 0 + const root = createRootRoute({ component: Outlet }) + const parent = createRoute({ + getParentRoute: () => root, + path: '/parent', + component: () => ( +
+ Parent layout + +
+ ), + }) + const known = createRoute({ + getParentRoute: () => parent, + path: '/known', + }) + const legacyNotFound = createRoute({ + getParentRoute: () => root, + path: '/404', + loader: () => { + legacyLoads++ + return 'not found' + }, + component: () =>
Legacy not found
, + staleTime: Infinity, + gcTime: Infinity, + }) + const router = createRouter({ + routeTree: root.addChildren([parent.addChildren([known])]), + history: createMemoryHistory({ initialEntries: ['/parent/missing'] }), + notFoundRoute: legacyNotFound, + }) + + const rendered = render() + expect(await rendered.findByText('Parent layout')).toBeInTheDocument() + expect(await rendered.findByText('Legacy not found')).toBeInTheDocument() + expect(legacyLoads).toBe(1) + + await act(async () => { + await router.navigate({ to: '/missing' } as any) + }) + + expect(rendered.queryByText('Parent layout')).not.toBeInTheDocument() + expect(await rendered.findByText('Legacy not found')).toBeInTheDocument() + expect(legacyLoads).toBe(1) + rendered.unmount() +}) + describe('matching on different param types', () => { const testCases = [ { diff --git a/packages/react-router/tests/Scripts.test.tsx b/packages/react-router/tests/Scripts.test.tsx index b7605c9402..47e345960d 100644 --- a/packages/react-router/tests/Scripts.test.tsx +++ b/packages/react-router/tests/Scripts.test.tsx @@ -158,15 +158,12 @@ describe('ssr scripts', () => { { src: 'script3.js' }, ]) - const { container } = await act(() => - render(), - ) - expect(await screen.findByTestId('root')).toBeInTheDocument() - expect(await screen.findByTestId('index')).toBeInTheDocument() - - expect(container.innerHTML).toEqual( - `
root
index
`, + const html = ReactDOMServer.renderToString( + , ) + expect(html).toContain('') + expect(html).toContain('') + expect(html).not.toContain('script2.js') }) }) diff --git a/packages/react-router/tests/component-preload-retry-pending-min.test.tsx b/packages/react-router/tests/component-preload-retry-pending-min.test.tsx new file mode 100644 index 0000000000..906e2334e5 --- /dev/null +++ b/packages/react-router/tests/component-preload-retry-pending-min.test.tsx @@ -0,0 +1,40 @@ +import { cleanup, render, screen } from '@testing-library/react' +import { afterEach, expect, test } from 'vitest' +import { createControlledPromise } from '@tanstack/router-core' +import { + RouterProvider, + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, +} from '../src' + +afterEach(() => { + cleanup() +}) + +test('delayed component preload reveals pending UI', async () => { + const componentGate = createControlledPromise() + const Page = Object.assign(() =>
Page content
, { + preload: () => componentGate, + }) + const rootRoute = createRootRoute({}) + const pageRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/page', + component: Page, + pendingMs: 10, + pendingComponent: () =>
Loading page...
, + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + render() + + expect(await screen.findByText('Loading page...')).toBeInTheDocument() + componentGate.resolve() + expect(await screen.findByText('Page content')).toBeInTheDocument() + expect(screen.queryByText('Loading page...')).not.toBeInTheDocument() +}) diff --git a/packages/react-router/tests/errorComponent.test.tsx b/packages/react-router/tests/errorComponent.test.tsx index 296c0caf34..28757a0b98 100644 --- a/packages/react-router/tests/errorComponent.test.tsx +++ b/packages/react-router/tests/errorComponent.test.tsx @@ -1,6 +1,5 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' import { act, cleanup, fireEvent, render, screen } from '@testing-library/react' -import ReactDOMServer from 'react-dom/server' import { HeadContent, @@ -8,6 +7,7 @@ import { Outlet, RouterProvider, createBrowserHistory, + createControlledPromise, createLazyRoute, createMemoryHistory, createRootRoute, @@ -273,6 +273,109 @@ describe.each([true, false])( }, ) +test('global catch boundary resets when a background child generation recovers', async () => { + const refresh = createControlledPromise() + let loaderCalls = 0 + const rootRoute = createRootRoute({ component: Outlet }) + const childRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + loader: { + staleReloadMode: 'background', + handler: () => (++loaderCalls === 1 ? 1 : refresh), + }, + component: () => { + const revision = childRoute.useLoaderData() + if (revision === 1) { + throw new Error('stale child render failed') + } + return
Recovered child revision {revision}
+ }, + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([childRoute]), + history, + }) + vi.spyOn(console, 'warn').mockImplementation(() => {}) + vi.spyOn(console, 'error').mockImplementation(() => {}) + + render() + expect( + await screen.findByText('stale child render failed'), + ).toBeInTheDocument() + + const invalidation = router.invalidate() + await vi.waitFor(() => expect(loaderCalls).toBe(2)) + expect(screen.getByText('stale child render failed')).toBeInTheDocument() + expect(screen.queryByText(/Recovered child revision/)).not.toBeInTheDocument() + refresh.resolve(2) + await invalidation + + expect( + await screen.findByText('Recovered child revision 2'), + ).toBeInTheDocument() + expect( + screen.queryByText('stale child render failed'), + ).not.toBeInTheDocument() +}) + +test('ancestor route errorComponent resets when a background child generation recovers', async () => { + const refresh = createControlledPromise() + let loaderCalls = 0 + const rootRoute = createRootRoute({ + component: Outlet, + errorComponent: ({ error }) =>
Ancestor error: {error.message}
, + }) + const childRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + loader: { + staleReloadMode: 'background', + handler: () => (++loaderCalls === 1 ? 1 : refresh), + }, + component: () => { + const revision = childRoute.useLoaderData() + if (revision === 1) { + throw new Error('stale child render failed') + } + return
Recovered child revision {revision}
+ }, + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([childRoute]), + history, + }) + vi.spyOn(console, 'warn').mockImplementation(() => {}) + vi.spyOn(console, 'error').mockImplementation(() => {}) + + let invalidation: Promise | undefined + try { + render() + expect( + await screen.findByText('Ancestor error: stale child render failed'), + ).toBeInTheDocument() + + invalidation = router.invalidate({ + filter: (match) => match.routeId === childRoute.id, + }) + await vi.waitFor(() => expect(loaderCalls).toBe(2)) + expect( + screen.getByText('Ancestor error: stale child render failed'), + ).toBeInTheDocument() + refresh.resolve(2) + await invalidation + + expect( + await screen.findByText('Recovered child revision 2'), + ).toBeInTheDocument() + } finally { + refresh.resolve(2) + if (invalidation) { + await Promise.allSettled([invalidation]) + } + } +}) + test('errorComponent receives primitive errors thrown from beforeLoad', async () => { const rootRoute = createRootRoute() const indexRoute = createRoute({ @@ -320,7 +423,6 @@ test('errorComponent receives primitive errors thrown from beforeLoad', async () }) test('SSR errorComponent receives primitive errors thrown from beforeLoad', async () => { - const history = createMemoryHistory({ initialEntries: ['/about'] }) const rootRoute = createRootRoute({ component: function Root() { return @@ -336,20 +438,234 @@ test('SSR errorComponent receives primitive errors thrown from beforeLoad', asyn errorComponent: ({ error }) =>
Error: {String(error)}
, }) - const router = createRouter({ - routeTree: rootRoute.addChildren([aboutRoute]), - history, + const handler = createRequestHandler({ + request: new Request('http://localhost/about'), + createRouter: () => + createRouter({ + routeTree: rootRoute.addChildren([aboutRoute]), + isServer: true, + }), }) - router.isServer = true - await router.load() + const response = await handler(({ router, responseHeaders }) => + renderRouterToString({ + router, + responseHeaders, + children: , + }), + ) - expect(router.state.statusCode).toBe(500) - const html = ReactDOMServer.renderToString() + expect(response.status).toBe(500) + const html = await response.text() expect(html).toContain('Error:') expect(html).toContain('primitive error thrown') }) +test('a later fresh ancestor loader failure owns the reachable boundary', async () => { + const parentStarted = createControlledPromise() + const childStarted = createControlledPromise() + const parentGate = createControlledPromise() + const childGate = createControlledPromise() + const childSettled = createControlledPromise() + const rootRoute = createRootRoute({ component: Outlet }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
Home
, + }) + const parentRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + component: Outlet, + loader: async () => { + parentStarted.resolve() + await parentGate + throw new Error('later parent failure') + }, + errorComponent: () =>
Parent error boundary
, + }) + const childRoute = createRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loader: async () => { + childStarted.resolve() + await childGate + childSettled.resolve() + throw new Error('first child failure') + }, + errorComponent: () =>
Child error boundary
, + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([ + indexRoute, + parentRoute.addChildren([childRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + render() + + let navigation!: Promise + await act(async () => { + navigation = router.navigate({ to: '/parent/child' }) + await Promise.all([parentStarted, childStarted]) + }) + await act(async () => { + childGate.resolve() + await childSettled + parentGate.resolve() + await navigation + }) + + expect(screen.getByText('Parent error boundary')).toBeInTheDocument() + expect(screen.queryByText('Child error boundary')).not.toBeInTheDocument() +}) + +test('a fresh ancestor failure waits for lazy options before rendering its error', async () => { + const parentStarted = createControlledPromise() + const childStarted = createControlledPromise() + const parentGate = createControlledPromise() + const childGate = createControlledPromise() + const childSettled = createControlledPromise() + const lazyParentOptions = createLazyRoute('/parent')({ + component: () => ( + <> +
Lazy parent shell
+ + + ), + }) + const parentChunk = createControlledPromise() + const rootRoute = createRootRoute({ component: Outlet }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
Home
, + }) + const parentRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + loader: async () => { + parentStarted.resolve() + await parentGate + throw new Error('later parent failure') + }, + errorComponent: () =>
Parent error boundary
, + }).lazy(() => parentChunk) + const childRoute = createRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loader: async () => { + childStarted.resolve() + await childGate + childSettled.resolve() + throw new Error('first child failure') + }, + errorComponent: () =>
Child error boundary
, + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([ + indexRoute, + parentRoute.addChildren([childRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + render() + + let navigation: Promise | undefined + try { + await act(async () => { + navigation = router.navigate({ to: '/parent/child' }) + await Promise.all([parentStarted, childStarted]) + }) + await act(async () => { + childGate.resolve() + await childSettled + parentGate.resolve() + await Promise.resolve() + }) + + expect(parentChunk.status).toBe('pending') + expect(screen.getByText('Home')).toBeInTheDocument() + expect(screen.queryByText('Parent error boundary')).not.toBeInTheDocument() + expect(screen.queryByText('Child error boundary')).not.toBeInTheDocument() + expect(screen.queryByText('Lazy parent shell')).not.toBeInTheDocument() + } finally { + await act(async () => { + childGate.resolve() + parentGate.resolve() + parentChunk.resolve(lazyParentOptions) + await navigation + }) + } + + expect(screen.getByText('Parent error boundary')).toBeInTheDocument() + expect(screen.queryByText('Lazy parent shell')).not.toBeInTheDocument() + expect(screen.queryByText('Child error boundary')).not.toBeInTheDocument() +}) + +test('SSR renders a later fresh ancestor loader failure', async () => { + const parentStarted = createControlledPromise() + const childStarted = createControlledPromise() + const parentGate = createControlledPromise() + const childGate = createControlledPromise() + const childSettled = createControlledPromise() + const rootRoute = createRootRoute({ component: Outlet }) + const parentRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + component: Outlet, + loader: async () => { + parentStarted.resolve() + await parentGate + throw new Error('later parent failure') + }, + errorComponent: () =>
Parent error boundary
, + }) + const childRoute = createRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loader: async () => { + childStarted.resolve() + await childGate + childSettled.resolve() + throw new Error('first child failure') + }, + errorComponent: () =>
Child error boundary
, + }) + const handler = createRequestHandler({ + request: new Request('http://localhost/parent/child'), + createRouter: () => + createRouter({ + routeTree: rootRoute.addChildren([ + parentRoute.addChildren([childRoute]), + ]), + isServer: true, + }), + }) + + const responsePromise = handler(({ router, responseHeaders }) => + renderRouterToString({ + router, + responseHeaders, + children: , + }), + ) + await Promise.all([parentStarted, childStarted]) + childGate.resolve() + await childSettled + parentGate.resolve() + const response = await responsePromise + const html = await response.text() + + expect(response.status).toBe(500) + expect(html).toContain('Parent error boundary') + expect(html).not.toContain('Child error boundary') +}) + // https://github.com/TanStack/router/issues/4684 test('#4684: SSR renders head content when beforeLoad throws', async () => { const rootRoute = createRootRoute({ @@ -496,13 +812,11 @@ describe('notFoundComponent is rendered when an error is thrown in params.parse' render() - await act(() => router.latestLoadPromise) - expect(rootLoader).toHaveBeenCalledTimes(1) - const linkToRottenPizza = await screen.findByRole('link', { name: 'link to rotten pizza', }) + expect(rootLoader).toHaveBeenCalledTimes(1) expect(linkToRottenPizza).toBeInTheDocument() await act(() => fireEvent.mouseOver(linkToRottenPizza)) await act(() => fireEvent.click(linkToRottenPizza)) diff --git a/packages/react-router/tests/hydration-terminal-lane.test.tsx b/packages/react-router/tests/hydration-terminal-lane.test.tsx new file mode 100644 index 0000000000..35784ea916 --- /dev/null +++ b/packages/react-router/tests/hydration-terminal-lane.test.tsx @@ -0,0 +1,98 @@ +import { cleanup, render, screen } from '@testing-library/react' +import { afterEach, describe, expect, test, vi } from 'vitest' +import { hydrate } from '@tanstack/router-core/ssr/client' +import { dehydrateSsrMatchId } from '../../router-core/src/ssr/ssr-match-id' +import { + Outlet, + RouterProvider, + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, +} from '../src' +import type { AnyRouteMatch } from '@tanstack/router-core' +import type { TsrSsrGlobal } from '@tanstack/router-core/ssr/client' + +function bootstrap( + matches: Array<{ + match: AnyRouteMatch + status: AnyRouteMatch['status'] + ssr: AnyRouteMatch['ssr'] + data?: unknown + error?: unknown + }>, +): void { + window.$_TSR = { + router: { + manifest: undefined, + matches: matches.map(({ match, status, ssr, data, error }) => ({ + i: dehydrateSsrMatchId(match.id), + l: data, + e: error, + s: status, + ssr, + u: Date.now(), + })), + }, + h: vi.fn(), + e: vi.fn(), + c: vi.fn(), + p: vi.fn(), + buffer: [], + } as TsrSsrGlobal +} + +afterEach(() => { + cleanup() + delete window.$_TSR +}) + +describe('hydration terminal lane', () => { + test('keeps server data while loading only the missing client suffix', async () => { + const parentLoader = vi.fn(() => 'client-parent') + const childLoader = vi.fn(() => 'client-child') + const rootRoute = createRootRoute({ component: Outlet }) + const parentRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + loader: parentLoader, + component: () => ( + <> +
{parentRoute.useLoaderData()}
+ + + ), + }) + const childRoute = createRoute({ + getParentRoute: () => parentRoute, + path: '/child', + ssr: false, + loader: childLoader, + component: () =>
{childRoute.useLoaderData()}
, + }) + const router = createRouter({ + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + routeTree: rootRoute.addChildren([parentRoute.addChildren([childRoute])]), + }) + const matches = router.matchRoutes(router.state.location) + bootstrap([ + { match: matches[0]!, status: 'success', ssr: true }, + { + match: matches[1]!, + status: 'success', + ssr: true, + data: 'server-parent', + }, + { match: matches[2]!, status: 'pending', ssr: false }, + ]) + + await hydrate(router) + render() + + expect(await screen.findByText('server-parent')).toBeInTheDocument() + expect(await screen.findByText('client-child')).toBeInTheDocument() + expect(screen.queryByText('client-parent')).not.toBeInTheDocument() + expect(parentLoader).not.toHaveBeenCalled() + expect(childLoader).toHaveBeenCalledTimes(1) + }) +}) diff --git a/packages/react-router/tests/loaders.test.tsx b/packages/react-router/tests/loaders.test.tsx index 869aeec76c..3b5790ef41 100644 --- a/packages/react-router/tests/loaders.test.tsx +++ b/packages/react-router/tests/loaders.test.tsx @@ -647,7 +647,7 @@ test('reproducer #4546', async () => { } }) -test('clears pendingTimeout when match resolves', async () => { +test('does not show pending UI when loaders finish before their pending delays', async () => { const defaultPendingComponentOnMountMock = vi.fn() const nestedPendingComponentOnMountMock = vi.fn() const fooPendingComponentOnMountMock = vi.fn() @@ -716,7 +716,6 @@ test('clears pendingTimeout when match resolves', async () => { }) render() - await act(() => router.latestLoadPromise) const linkToFoo = await screen.findByTestId('link-to-foo') fireEvent.click(linkToFoo) const fooElement = await screen.findByText('Nested Foo page') @@ -757,7 +756,7 @@ test('throw abortError from loader upon initial load with basepath', async () => expect(window.location.pathname.startsWith('/app')).toBe(true) }) -test('cancelMatches after pending timeout', async () => { +test('navigating away from a pending route aborts its loader', async () => { function getPendingComponent(onMount: () => void) { const PendingComponent = () => { useEffect(() => { @@ -770,6 +769,7 @@ test('cancelMatches after pending timeout', async () => { } const onAbortMock = vi.fn() const fooPendingComponentOnMountMock = vi.fn() + let fooSignal: AbortSignal | undefined const rootRoute = createRootRoute({ component: () => (
@@ -787,17 +787,18 @@ test('cancelMatches after pending timeout', async () => { const fooRoute = createRoute({ getParentRoute: () => rootRoute, path: '/foo', - pendingMs: WAIT_TIME * 20, + pendingMs: 0, loader: async ({ abortController }) => { + fooSignal = abortController.signal await new Promise((resolve) => { - const timer = setTimeout(() => { - resolve() - }, WAIT_TIME * 40) - abortController.signal.addEventListener('abort', () => { - onAbortMock() - clearTimeout(timer) - resolve() - }) + abortController.signal.addEventListener( + 'abort', + () => { + onAbortMock() + resolve() + }, + { once: true }, + ) }) }, pendingComponent: getPendingComponent(fooPendingComponentOnMountMock), @@ -811,18 +812,22 @@ test('cancelMatches after pending timeout', async () => { const routeTree = rootRoute.addChildren([fooRoute, barRoute]) const router = createRouter({ routeTree, history }) render() - await act(() => router.latestLoadPromise) const fooLink = await screen.findByTestId('link-to-foo') fireEvent.click(fooLink) - await sleep(WAIT_TIME * 30) const pendingElement = await screen.findByText('Pending...') expect(pendingElement).toBeInTheDocument() - const barLink = await screen.findByTestId('link-to-bar') - fireEvent.click(barLink) - const barElement = await screen.findByText('Bar page') + expect(fooSignal?.aborted).toBe(false) + await act(() => router.navigate({ to: '/bar' })) + const barElement = screen.getByText('Bar page') expect(barElement).toBeInTheDocument() + expect(fooPendingComponentOnMountMock).toHaveBeenCalled() - expect(onAbortMock).toHaveBeenCalled() + expect(onAbortMock).toHaveBeenCalledTimes(1) + expect(fooSignal?.aborted).toBe(true) + expect(screen.queryByText('Pending...')).not.toBeInTheDocument() + expect(screen.queryByText('Foo page')).not.toBeInTheDocument() + expect(router.state.location.href).toBe('/bar') + expect(router.state.status).toBe('idle') }) test('reproducer for #6388 - rapid navigation between parameterized routes should not trigger errorComponent', async () => { diff --git a/packages/react-router/tests/not-found.test.tsx b/packages/react-router/tests/not-found.test.tsx index 3754785d35..bc6a1b9df1 100644 --- a/packages/react-router/tests/not-found.test.tsx +++ b/packages/react-router/tests/not-found.test.tsx @@ -1,11 +1,12 @@ import { afterEach, beforeEach, expect, test } from 'vitest' -import { cleanup, render, screen } from '@testing-library/react' +import { act, cleanup, render, screen } from '@testing-library/react' import { Link, Outlet, RouterProvider, createBrowserHistory, + createControlledPromise, createRootRoute, createRoute, createRouter, @@ -27,6 +28,44 @@ afterEach(() => { cleanup() }) +test('navigating to an actively preloaded missing URL renders the global not-found boundary', async () => { + const missingLoaderStarted = createControlledPromise() + const missingLoader = createControlledPromise() + const rootRoute = createRootRoute({ + component: Outlet, + notFoundComponent: () =>
Missing URL boundary
, + loader: ({ location }) => { + if (location.pathname === '/missing') { + missingLoaderStarted.resolve() + return missingLoader + } + return + }, + shouldReload: true, + }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
Home
, + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute]), + history, + }) + + render() + expect(await screen.findByText('Home')).toBeInTheDocument() + + const preload = router.preloadRoute({ to: '/missing' } as any) + await missingLoaderStarted + const navigation = router.navigate({ to: '/missing' } as any) + missingLoader.resolve() + await act(() => Promise.all([preload, navigation])) + + expect(screen.getByText('Missing URL boundary')).toBeInTheDocument() + expect(screen.queryByText('Home')).not.toBeInTheDocument() +}) + test.each([ { notFoundMode: 'fuzzy' as const, diff --git a/packages/react-router/tests/public-presentation-lane-contract.test.tsx b/packages/react-router/tests/public-presentation-lane-contract.test.tsx new file mode 100644 index 0000000000..aa744b87f6 --- /dev/null +++ b/packages/react-router/tests/public-presentation-lane-contract.test.tsx @@ -0,0 +1,133 @@ +import { act, cleanup, render, screen, waitFor } from '@testing-library/react' +import { afterEach, describe, expect, test } from 'vitest' +import { createControlledPromise } from '@tanstack/router-core' +import { + Outlet, + RouterProvider, + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, +} from '../src' + +afterEach(() => { + cleanup() +}) + +describe('public presentation lane contracts', () => { + test('visible pending UI publishes every matched route and its loading state', async () => { + const parentGate = createControlledPromise() + + const rootRoute = createRootRoute({ component: Outlet }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
Home
, + }) + const parentRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + pendingMs: 0, + pendingComponent: () =>
Loading parent
, + loader: () => parentGate, + component: Outlet, + }) + const childRoute = createRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loader: () => 'child data', + component: () =>
Child content
, + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([ + indexRoute, + parentRoute.addChildren([childRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + render() + expect(await screen.findByText('Home')).toBeInTheDocument() + await waitFor(() => expect(router.state.status).toBe('idle')) + + let navigation!: Promise + await act(async () => { + navigation = router.navigate({ to: '/parent/child' }) + await Promise.resolve() + }) + + expect(await screen.findByText('Loading parent')).toBeInTheDocument() + expect(screen.queryByText('Child content')).not.toBeInTheDocument() + + expect(router.state.matches.map((match) => match.routeId)).toEqual([ + rootRoute.id, + parentRoute.id, + childRoute.id, + ]) + expect( + router.state.matches.find((match) => match.routeId === parentRoute.id), + ).toMatchObject({ status: 'pending', isFetching: 'loader' }) + + await act(async () => { + parentGate.resolve('parent data') + await navigation + }) + + expect(screen.getByText('Child content')).toBeInTheDocument() + expect(router.state.status).toBe('idle') + }) + + test('a reentrant navigation from onResolved suppresses the stale onRendered event', async () => { + const rootRoute = createRootRoute({ component: Outlet }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
Home
, + }) + const firstRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/first', + component: () =>
First
, + }) + const secondRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/second', + component: () =>
Second
, + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute, firstRoute, secondRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + render() + expect(await screen.findByText('Home')).toBeInTheDocument() + await waitFor(() => expect(router.state.status).toBe('idle')) + + const renderedPaths: Array = [] + let successor: Promise | undefined + const unsubscribeResolved = router.subscribe('onResolved', (event) => { + if (event.toLocation.pathname === '/first') { + successor = router.navigate({ to: '/second' }) + } + }) + const unsubscribeRendered = router.subscribe('onRendered', (event) => { + if (event.toLocation.pathname !== '/') { + renderedPaths.push(event.toLocation.pathname) + } + }) + + try { + await act(() => router.navigate({ to: '/first' })) + await act(async () => { + await successor + }) + + expect(screen.getByText('Second')).toBeInTheDocument() + expect(screen.queryByText('First')).not.toBeInTheDocument() + expect(renderedPaths).toEqual(['/second']) + } finally { + unsubscribeResolved() + unsubscribeRendered() + } + }) +}) diff --git a/packages/react-router/tests/redirect.test.tsx b/packages/react-router/tests/redirect.test.tsx index cc15f0da36..a0d7906ac3 100644 --- a/packages/react-router/tests/redirect.test.tsx +++ b/packages/react-router/tests/redirect.test.tsx @@ -1,5 +1,6 @@ import * as React from 'react' import { + act, cleanup, configure, fireEvent, @@ -8,6 +9,7 @@ import { } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' +import { createControlledPromise } from '@tanstack/router-core' import { Link, @@ -45,6 +47,35 @@ const WAIT_TIME = 100 describe('redirect', () => { describe('SPA', () => { configure({ reactStrictMode: true }) + + test('allows a same-location redirect to settle after a side effect', async () => { + let firstLoad = true + const loader = vi.fn(() => { + if (firstLoad) { + firstLoad = false + throw redirect({ to: '/' }) + } + }) + const rootRoute = createRootRoute() + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + loader, + component: () =>
Index page
, + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute]), + history, + }) + + render() + + expect(await screen.findByText('Index page')).toBeInTheDocument() + expect(window.location.pathname).toBe('/') + expect(loader).toHaveBeenCalledTimes(2) + expect(router.state.status).toBe('idle') + }) + test('when `redirect` is thrown in `beforeLoad`', async () => { const nestedLoaderMock = vi.fn() const nestedFooLoaderMock = vi.fn() @@ -115,6 +146,7 @@ describe('redirect', () => { test('when root `beforeLoad` redirects while root pendingComponent is showing and the target route is lazy', async () => { let hasRedirected = false + const beforeLoad = createControlledPromise() const consoleError = vi .spyOn(console, 'error') .mockImplementation(() => {}) @@ -124,7 +156,7 @@ describe('redirect', () => { pendingMs: 0, pendingComponent: () =>
loading
, beforeLoad: async () => { - await sleep(WAIT_TIME) + await beforeLoad if (!hasRedirected) { hasRedirected = true throw redirect({ to: '/posts' }) @@ -150,6 +182,14 @@ describe('redirect', () => { render() + try { + expect(await screen.findByTestId('pending')).toBeInTheDocument() + } finally { + await act(() => { + beforeLoad.resolve() + }) + } + // The lazy target route adds the async boundary that exposes the stale // redirected-match render path this regression is guarding. expect(await screen.findByTestId('lazy-route-page')).toBeInTheDocument() diff --git a/packages/react-router/tests/renderRouterToStream.test.tsx b/packages/react-router/tests/renderRouterToStream.test.tsx index 74e180a552..116d8a259f 100644 --- a/packages/react-router/tests/renderRouterToStream.test.tsx +++ b/packages/react-router/tests/renderRouterToStream.test.tsx @@ -197,7 +197,7 @@ describe('renderRouterToStream - pipeable sync errors', () => { } }) - test('request abort aborts pipeable and errors body', async () => { + test('request abort cancels pipeable rendering before the response body is consumed', async () => { const abort = vi.fn() reactDomServerMocks.renderToPipeableStream.mockImplementationOnce( (_children, opts) => { @@ -220,13 +220,14 @@ describe('renderRouterToStream - pipeable sync errors', () => { }), ) + expect(response.body).not.toBeNull() controller.abort(new Error('request-gone')) + await vi.waitFor(() => expect(abort).toHaveBeenCalledOnce()) const terminated = await Promise.race([ expectBodyRejects(response, 'request-gone').then(() => true), new Promise((resolve) => setTimeout(() => resolve(false), 2000)), ]) expect(terminated).toBe(true) - expect(abort).toHaveBeenCalledOnce() } finally { router.serverSsr?.cleanup() } diff --git a/packages/react-router/tests/routeContext.test.tsx b/packages/react-router/tests/routeContext.test.tsx index d11f86420e..e00228e280 100644 --- a/packages/react-router/tests/routeContext.test.tsx +++ b/packages/react-router/tests/routeContext.test.tsx @@ -141,7 +141,7 @@ describe('context function', () => { }) test('when loader deps change', async () => { - const mockContextFn = vi.fn() + let generation = 0 const rootRoute = createRootRoute() const indexRoute = createRoute({ @@ -153,14 +153,21 @@ describe('context function', () => { path: '/', loaderDeps: ({ search }) => ({ foo: search.foo }), context: ({ deps }) => { - mockContextFn(deps) + return { + generation: ++generation, + deps: JSON.stringify(deps), + } }, component: () => { const navigate = indexRoute.useNavigate() + const context = indexRoute.useRouteContext() return (

Index page

search: {JSON.stringify(indexRoute.useSearch())}

+

+ context: {context.generation}:{context.deps} +

+ + Destination + + + ), + }) + const aRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/a', + }) + const bRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/b', + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([aRoute, bRoute]), + history, + }) + + render(() => ) + + const link = await screen.findByTestId('dynamic-link') + expect(link).toHaveAttribute('href', '/a') + + fireEvent.click( + screen.getByRole('button', { name: 'Change destination' }), + ) + + await waitFor(() => { + expect(link).toHaveAttribute('href', '/b') + }) + }) + test('updates exact and fuzzy active state before the next route renders', async () => { const postLoader = createControlledPromise() @@ -1414,22 +1453,17 @@ describe('Link', () => { '/Dashboard/posts?page=2&filter=inactive', ) - await fireEvent.click(updateSearchLink) + fireEvent.click(updateSearchLink) - // Wait for navigation to complete and search params to update await waitFor(() => { + expect(window.location.pathname).toBe('/Dashboard/posts') expect(window.location.search).toBe('?page=2&filter=inactive') + expect(screen.getByTestId('current-page')).toHaveTextContent('Page: 2') + expect(screen.getByTestId('current-filter')).toHaveTextContent( + 'Filter: inactive', + ) }) - - await screen.findByTestId('current-page') - // Verify search was updated - expect(window.location.pathname).toBe('/Dashboard/posts') - expect(window.location.search).toBe('?page=2&filter=inactive') - - const updatedPage = await screen.findByTestId('current-page') - const updatedFilter = await screen.findByTestId('current-filter') - expect(updatedPage).toHaveTextContent('Page: 2') - expect(updatedFilter).toHaveTextContent('Filter: inactive') + await vi.waitFor(() => expect(router.state.status).toBe('idle')) }) test('when navigating to /posts with invalid search', async () => { diff --git a/packages/solid-router/tests/loaders.test.tsx b/packages/solid-router/tests/loaders.test.tsx index 063660f592..b2ace7b43d 100644 --- a/packages/solid-router/tests/loaders.test.tsx +++ b/packages/solid-router/tests/loaders.test.tsx @@ -18,6 +18,7 @@ import { import { sleep } from './utils' afterEach(() => { + vi.useRealTimers() vi.resetAllMocks() window.history.replaceState(null, 'root', '/') cleanup() @@ -617,7 +618,8 @@ test('reproducer #4546', async () => { fireEvent.click(indexLink) { - // Wait for navigation to complete before checking values + // beforeLoad is fresh for this navigation, while the cached loader data is + // allowed to remain visible until its background reload completes. await screen.findByText('Index route') const headerCounter = await screen.findByTestId('header-counter') expect(headerCounter).toHaveTextContent('3') @@ -626,7 +628,7 @@ test('reproducer #4546', async () => { expect(routeContext).toHaveTextContent('3') const loaderData = await screen.findByTestId('index-loader-data') - expect(loaderData).toHaveTextContent('3') + await vi.waitFor(() => expect(loaderData).toHaveTextContent('3')) } fireEvent.click(invalidateRouterButton) @@ -647,7 +649,7 @@ test('reproducer #4546', async () => { fireEvent.click(idLink) { - // Wait for navigation to complete before checking values + // This loader generation can likewise begin with reusable cached data. await screen.findByText('$id route') const headerCounter = await screen.findByTestId('header-counter') expect(headerCounter).toHaveTextContent('5') @@ -656,11 +658,11 @@ test('reproducer #4546', async () => { expect(routeContext).toHaveTextContent('5') const loaderData = await screen.findByTestId('id-loader-data') - expect(loaderData).toHaveTextContent('5') + await vi.waitFor(() => expect(loaderData).toHaveTextContent('5')) } }) -test('clears pendingTimeout when match resolves', async () => { +test('does not show pending UI when loaders finish before their pending delays', async () => { const defaultPendingComponentOnMountMock = vi.fn() const nestedPendingComponentOnMountMock = vi.fn() const fooPendingComponentOnMountMock = vi.fn() @@ -728,13 +730,16 @@ test('clears pendingTimeout when match resolves', async () => { }) render(() => ) - await router.latestLoadPromise - const linkToFoo = await screen.findByTestId('link-to-foo') - fireEvent.click(linkToFoo) - const fooElement = await screen.findByText('Nested Foo page') + await screen.findByTestId('link-to-foo') + vi.useFakeTimers() + const navigation = router.navigate({ to: '/nested/foo' }) + await vi.advanceTimersByTimeAsync(WAIT_TIME * 5) + await navigation + const fooElement = screen.getByText('Nested Foo page') expect(fooElement).toBeInTheDocument() expect(router.state.location.href).toBe('/nested/foo') + expect(router.state.status).toBe('idle') // none of the pending components should have been called expect(defaultPendingComponentOnMountMock).not.toHaveBeenCalled() @@ -787,7 +792,7 @@ test('useLoaderData retains previous data while route match is pending', async ( expect(screen.getByTestId('combined')).toHaveTextContent('6:0') }) -test('cancelMatches after pending timeout', async () => { +test('navigating away from a pending route aborts its loader', async () => { function getPendingComponent(onMount: () => void) { const PendingComponent = () => { onMount() @@ -797,6 +802,7 @@ test('cancelMatches after pending timeout', async () => { } const onAbortMock = vi.fn() const fooPendingComponentOnMountMock = vi.fn() + let fooSignal: AbortSignal | undefined const history = createMemoryHistory({ initialEntries: ['/'] }) const rootRoute = createRootRoute({ component: () => ( @@ -815,17 +821,18 @@ test('cancelMatches after pending timeout', async () => { const fooRoute = createRoute({ getParentRoute: () => rootRoute, path: '/foo', - pendingMs: WAIT_TIME * 20, + pendingMs: 0, loader: async ({ abortController }) => { + fooSignal = abortController.signal await new Promise((resolve) => { - const timer = setTimeout(() => { - resolve() - }, WAIT_TIME * 40) - abortController.signal.addEventListener('abort', () => { - onAbortMock() - clearTimeout(timer) - resolve() - }) + abortController.signal.addEventListener( + 'abort', + () => { + onAbortMock() + resolve() + }, + { once: true }, + ) }) }, pendingComponent: getPendingComponent(fooPendingComponentOnMountMock), @@ -839,16 +846,20 @@ test('cancelMatches after pending timeout', async () => { const routeTree = rootRoute.addChildren([fooRoute, barRoute]) const router = createRouter({ routeTree, history }) render(() => ) - await router.latestLoadPromise const fooLink = await screen.findByTestId('link-to-foo') fireEvent.click(fooLink) - await sleep(WAIT_TIME * 30) const pendingElement = await screen.findByText('Pending...') expect(pendingElement).toBeInTheDocument() - const barLink = await screen.findByTestId('link-to-bar') - fireEvent.click(barLink) - const barElement = await screen.findByText('Bar page') + expect(fooSignal?.aborted).toBe(false) + await router.navigate({ to: '/bar' }) + const barElement = screen.getByText('Bar page') expect(barElement).toBeInTheDocument() + expect(fooPendingComponentOnMountMock).toHaveBeenCalled() - expect(onAbortMock).toHaveBeenCalled() + expect(onAbortMock).toHaveBeenCalledTimes(1) + expect(fooSignal?.aborted).toBe(true) + expect(screen.queryByText('Pending...')).not.toBeInTheDocument() + expect(screen.queryByText('Foo page')).not.toBeInTheDocument() + expect(router.state.location.href).toBe('/bar') + expect(router.state.status).toBe('idle') }) diff --git a/packages/solid-router/tests/redirect.test.tsx b/packages/solid-router/tests/redirect.test.tsx index 81bd1f6bc6..34681a8f85 100644 --- a/packages/solid-router/tests/redirect.test.tsx +++ b/packages/solid-router/tests/redirect.test.tsx @@ -1,6 +1,7 @@ import { cleanup, fireEvent, render, screen } from '@solidjs/testing-library' import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' +import { createControlledPromise } from '@tanstack/router-core' import { Link, @@ -107,6 +108,7 @@ describe('redirect', () => { test('when root `beforeLoad` redirects while root pendingComponent is showing and the target route is lazy', async () => { let hasRedirected = false + const beforeLoad = createControlledPromise() const consoleError = vi .spyOn(console, 'error') .mockImplementation(() => {}) @@ -116,7 +118,7 @@ describe('redirect', () => { pendingMs: 0, pendingComponent: () =>
loading
, beforeLoad: async () => { - await sleep(WAIT_TIME) + await beforeLoad if (!hasRedirected) { hasRedirected = true throw redirect({ to: '/posts' }) @@ -142,12 +144,20 @@ describe('redirect', () => { render(() => ) + try { + expect(await screen.findByTestId('pending')).toBeInTheDocument() + expect(screen.queryByTestId('index-page')).not.toBeInTheDocument() + } finally { + beforeLoad.resolve() + } + // The lazy target route adds the async boundary that exposes the stale // redirected-match render path this regression is guarding. expect(await screen.findByTestId('lazy-route-page')).toBeInTheDocument() expect(screen.queryByTestId('pending')).not.toBeInTheDocument() + expect(screen.queryByTestId('index-page')).not.toBeInTheDocument() expect(router.state.location.href).toBe('/posts') - expect(router.state.status).toBe('idle') + await vi.waitFor(() => expect(router.state.status).toBe('idle')) expect(consoleError).not.toHaveBeenCalled() }) diff --git a/packages/solid-router/tests/renderRouterToStream.test.tsx b/packages/solid-router/tests/renderRouterToStream.test.tsx index ba5ee51ca0..6351d83ebd 100644 --- a/packages/solid-router/tests/renderRouterToStream.test.tsx +++ b/packages/solid-router/tests/renderRouterToStream.test.tsx @@ -56,7 +56,7 @@ function drainBody(response: Response) { } describe('renderRouterToStream - bot abort', () => { - test('request abort during bot wait returns and terminates response stream', async () => { + test('request abort during bot wait terminates before rendering starts', async () => { const neverReady = new Promise(() => {}) solidMocks.pipeTo.mockImplementationOnce( (writable: WritableStream) => { @@ -99,6 +99,7 @@ describe('renderRouterToStream - bot abort', () => { expect(result).not.toBe(false) expect(solidMocks.pipeTo).not.toHaveBeenCalled() const response = unwrapResponse(result as Exclude) + expect(response.body).not.toBeNull() const terminated = await Promise.race([ drainBody(response), diff --git a/packages/solid-router/tests/routeContext.test.tsx b/packages/solid-router/tests/routeContext.test.tsx index b7e0a902a9..fdee696bf8 100644 --- a/packages/solid-router/tests/routeContext.test.tsx +++ b/packages/solid-router/tests/routeContext.test.tsx @@ -133,7 +133,7 @@ describe('context function', () => { }) test('when loader deps change', async () => { - const mockContextFn = vi.fn() + let generation = 0 const rootRoute = createRootRoute() const indexRoute = createRoute({ @@ -145,14 +145,21 @@ describe('context function', () => { path: '/', loaderDeps: ({ search }) => ({ foo: search.foo }), context: ({ deps }) => { - mockContextFn(deps) + return { + generation: ++generation, + deps: JSON.stringify(deps), + } }, component: () => { const navigate = indexRoute.useNavigate() + const context = indexRoute.useRouteContext() return (

Index page

search: {JSON.stringify(indexRoute.useSearch()())}

+

+ context: {context().generation}:{context().deps} +

} ``` -For synchronous invalidation (wait until loaders finish): +Use `await router.invalidate({ sync: true })` when the next step requires refreshed loader data. -```tsx -await router.invalidate({ sync: true }) -``` +Treat the mutation and invalidation as one workflow. The mutation must persist before invalidation starts, and the loader must read from the same authoritative store. Verify create, update, and delete through the rendered route, including a fresh reload; local component state can hide a stale loader or non-persistent write. ### Error Handling @@ -361,16 +376,13 @@ export const Route = createFileRoute('/posts')({ }, }) -// CORRECT — loaders run in the browser, use fetch or API calls +// CORRECT for an SPA — use a client-safe API helper export const Route = createFileRoute('/posts')({ - loader: async () => { - const res = await fetch('/api/posts') - return res.json() - }, + loader: () => fetchPosts(), }) ``` -Do NOT put database queries, filesystem access, or server-only code in loaders unless you are using TanStack Start server functions. +Do NOT put database queries, filesystem access, or server-only code directly in loaders. In TanStack Start, put that work in a server function and call the function from the loader. Do not use a relative `fetch('/api/...')` in an SSR loader. ### MEDIUM: Not understanding staleTime default is 0 diff --git a/packages/router-core/skills/router-core/navigation/SKILL.md b/packages/router-core/skills/router-core/navigation/SKILL.md index 0e09ddbcec..ba1f0fd2e6 100644 --- a/packages/router-core/skills/router-core/navigation/SKILL.md +++ b/packages/router-core/skills/router-core/navigation/SKILL.md @@ -1,14 +1,15 @@ --- -name: router-core/navigation +name: navigation description: >- Link component, useNavigate, Navigate component, router.navigate, ToOptions/NavigateOptions/LinkOptions, from/to relative navigation, activeOptions/activeProps, preloading (intent/viewport/render), preloadDelay, navigation blocking (useBlocker, Block), createLink, linkOptions helper, scroll restoration, MatchRoute. -type: sub-skill -library: tanstack-router -library_version: '1.166.2' +metadata: + type: sub-skill + library: tanstack-router + library_version: '1.171.15' requires: - router-core sources: diff --git a/packages/router-core/skills/router-core/not-found-and-errors/SKILL.md b/packages/router-core/skills/router-core/not-found-and-errors/SKILL.md index b4fde048f8..2a69f16888 100644 --- a/packages/router-core/skills/router-core/not-found-and-errors/SKILL.md +++ b/packages/router-core/skills/router-core/not-found-and-errors/SKILL.md @@ -1,13 +1,14 @@ --- -name: router-core/not-found-and-errors +name: not-found-and-errors description: >- notFound() function, notFoundComponent, defaultNotFoundComponent, notFoundMode (fuzzy/root), errorComponent, CatchBoundary, CatchNotFound, isNotFound, NotFoundRoute (deprecated), route masking (mask option, createRouteMask, unmaskOnReload). -type: sub-skill -library: tanstack-router -library_version: '1.166.2' +metadata: + type: sub-skill + library: tanstack-router + library_version: '1.171.15' requires: - router-core sources: diff --git a/packages/router-core/skills/router-core/path-params/SKILL.md b/packages/router-core/skills/router-core/path-params/SKILL.md index 32964d06cc..b4819f04b5 100644 --- a/packages/router-core/skills/router-core/path-params/SKILL.md +++ b/packages/router-core/skills/router-core/path-params/SKILL.md @@ -1,13 +1,14 @@ --- -name: router-core/path-params +name: path-params description: >- Dynamic path segments ($paramName), splat routes ($ / _splat), optional params ({-$paramName}), prefix/suffix patterns ({$param}.ext), useParams, params.parse/stringify, pathParamsAllowedCharacters, i18n locale patterns. -type: sub-skill -library: tanstack-router -library_version: '1.166.2' +metadata: + type: sub-skill + library: tanstack-router + library_version: '1.171.15' requires: - router-core sources: diff --git a/packages/router-core/skills/router-core/search-params/SKILL.md b/packages/router-core/skills/router-core/search-params/SKILL.md index 5b509e5a50..0960297d00 100644 --- a/packages/router-core/skills/router-core/search-params/SKILL.md +++ b/packages/router-core/skills/router-core/search-params/SKILL.md @@ -1,13 +1,14 @@ --- -name: router-core/search-params +name: search-params description: >- validateSearch, search param validation with Zod/Valibot/ArkType adapters, fallback(), search middlewares (retainSearchParams, stripSearchParams), custom serialization (parseSearch, stringifySearch), search param inheritance, loaderDeps for cache keys, reading and writing search params. -type: sub-skill -library: tanstack-router -library_version: '1.166.2' +metadata: + type: sub-skill + library: tanstack-router + library_version: '1.171.15' requires: - router-core sources: diff --git a/packages/router-core/skills/router-core/ssr/SKILL.md b/packages/router-core/skills/router-core/ssr/SKILL.md index de3fc9a6d7..d1e0524f13 100644 --- a/packages/router-core/skills/router-core/ssr/SKILL.md +++ b/packages/router-core/skills/router-core/ssr/SKILL.md @@ -1,5 +1,5 @@ --- -name: router-core/ssr +name: ssr description: >- Non-streaming and streaming SSR, RouterClient/RouterServer, renderRouterToString/renderRouterToStream, createRequestHandler, @@ -7,9 +7,10 @@ description: >- components, head route option (meta/links/styles/scripts), ScriptOnce, automatic loader dehydration/hydration, memory history on server, data serialization, document head management. -type: sub-skill -library: tanstack-router -library_version: '1.166.2' +metadata: + type: sub-skill + library: tanstack-router + library_version: '1.171.15' requires: - router-core - router-core/data-loading diff --git a/packages/router-core/skills/router-core/type-safety/SKILL.md b/packages/router-core/skills/router-core/type-safety/SKILL.md index 7b7c53bfa8..ce9204677e 100644 --- a/packages/router-core/skills/router-core/type-safety/SKILL.md +++ b/packages/router-core/skills/router-core/type-safety/SKILL.md @@ -1,14 +1,15 @@ --- -name: router-core/type-safety +name: type-safety description: >- Full type inference philosophy (never cast, never annotate inferred values), Register module declaration, from narrowing on hooks and Link, strict:false for shared components, getRouteApi for code-split typed access, addChildren with object syntax for TS perf, LinkProps and ValidateLinkOptions type utilities, as const satisfies pattern. -type: sub-skill -library: tanstack-router -library_version: '1.166.2' +metadata: + type: sub-skill + library: tanstack-router + library_version: '1.171.15' requires: - router-core sources: @@ -489,4 +490,10 @@ const search = Route.useSearch() If a build error mentions `react-router-dom`, `next/`, `pages/_app`, or duplicate `/` routes, fix the import — don't paper over with type assertions. +### 6. CRITICAL: Treating typecheck as proof of runtime schema propagation + +Types can say a field exists while a database projection, API serializer, or server function omits it. When adding or renaming a field, trace the value through storage, validation, handler output, loader data, and rendered UI. Do not cast the response to the desired type. +Add a runtime assertion against the real handler or serialized response, such as `expect(await getOrder({ data: { id } })).toMatchObject({ totalCents: 2599 })`. +Then run the route-level test and production build. The type test remains necessary, but it is not the runtime contract test. + See also: router-core (Register setup), router-core/navigation (from narrowing), router-core/code-splitting (getRouteApi). diff --git a/packages/router-plugin/skills/_artifacts/domain_map.yaml b/packages/router-plugin/skills/_artifacts/domain_map.yaml index 8ce9e31f4d..dd83f2c913 100644 --- a/packages/router-plugin/skills/_artifacts/domain_map.yaml +++ b/packages/router-plugin/skills/_artifacts/domain_map.yaml @@ -1,12 +1,12 @@ # domain_map.yaml # Library: @tanstack/router-plugin -# Version: 1.166.2 +# Version: 1.168.23 # Date: 2026-03-08 # Status: reviewed library: name: '@tanstack/router-plugin' - version: '1.166.2' + version: '1.168.23' repository: 'https://github.com/TanStack/router' description: >- Bundler plugin for route generation and automatic code splitting. @@ -59,4 +59,12 @@ skills: priority: MEDIUM status: active + - mistake: 'Editing routeTree.gen.ts instead of regenerating' + mechanism: >- + Manual changes are overwritten and can put source routes, generated + types, and runtime routing out of sync. Fix source or config and + regenerate the tree. + priority: HIGH + status: active + gaps: [] diff --git a/packages/router-plugin/skills/_artifacts/skill_spec.md b/packages/router-plugin/skills/_artifacts/skill_spec.md index 4d4881232f..586ee34ade 100644 --- a/packages/router-plugin/skills/_artifacts/skill_spec.md +++ b/packages/router-plugin/skills/_artifacts/skill_spec.md @@ -12,13 +12,14 @@ Bundler plugin for route generation and automatic code splitting. Supports Vite, | Skill | Type | Domain | What it covers | Failure modes | | ------------- | ---- | ------------------- | -------------------------------------------------------------- | ------------- | -| router-plugin | core | bundler-integration | Vite/Webpack/Rspack/esbuild plugins, route gen, code splitting | 2 | +| router-plugin | core | bundler-integration | Vite/Webpack/Rspack/esbuild plugins, route gen, code splitting | 3 | ## Failure Mode Inventory -### router-plugin (2 failure modes) +### router-plugin (3 failure modes) -| # | Mistake | Priority | Source | -| --- | ---------------------------------------------------- | -------- | ----------- | -| 1 | Using wrong plugin export for bundler | HIGH | source/docs | -| 2 | Misconfiguring routesDirectory or generatedRouteTree | MEDIUM | source/docs | +| # | Mistake | Priority | Source | +| --- | ---------------------------------------------------- | -------- | ----------------- | +| 1 | Using wrong plugin export for bundler | HIGH | source/docs | +| 2 | Misconfiguring routesDirectory or generatedRouteTree | MEDIUM | source/docs | +| 3 | Editing routeTree.gen.ts instead of regenerating | HIGH | consistency audit | diff --git a/packages/router-plugin/skills/_artifacts/skill_tree.yaml b/packages/router-plugin/skills/_artifacts/skill_tree.yaml index eccd302d0f..a313cc8a4e 100644 --- a/packages/router-plugin/skills/_artifacts/skill_tree.yaml +++ b/packages/router-plugin/skills/_artifacts/skill_tree.yaml @@ -1,6 +1,6 @@ library: name: '@tanstack/router-plugin' - version: '1.166.2' + version: '1.168.23' repository: 'https://github.com/TanStack/router' description: >- Bundler plugin for route generation and automatic code splitting. diff --git a/packages/router-plugin/skills/router-plugin/SKILL.md b/packages/router-plugin/skills/router-plugin/SKILL.md index b3ba5db5a3..972584dc96 100644 --- a/packages/router-plugin/skills/router-plugin/SKILL.md +++ b/packages/router-plugin/skills/router-plugin/SKILL.md @@ -5,9 +5,10 @@ description: >- code splitting. Supports Vite, Webpack, Rspack, and esbuild. Configures autoCodeSplitting, routesDirectory, target framework, and code split groupings. -type: core -library: tanstack-router -library_version: '1.166.2' +metadata: + type: core + library: tanstack-router + library_version: '1.168.23' sources: - TanStack/router:packages/router-plugin/src - TanStack/router:docs/router/routing/file-based-routing.md @@ -170,6 +171,18 @@ The composed plugin assembles up to 3 sub-plugins: 2. **Code Splitter** (when `autoCodeSplitting: true`) — Splits route files into lazy-loaded chunks using virtual modules 3. **HMR** (dev mode, when code splitter is off) — Hot-reloads route changes without full refresh +## Route Refactor Workflow + +When moving, renaming, adding, or deleting file routes: + +1. Change the source files under `routesDirectory`. Keep the exported route identifier named `Route`. +2. Let the bundler plugin regenerate the tree, or run `pnpm exec tsr generate` when the project uses the CLI. +3. Inspect the generated diff for the expected route IDs, parents, paths, and imports. Never repair `routeTree.gen.ts` by hand. +4. Update links, redirects, `from` narrowing, params, preload calls, and tests that reference the old route. +5. Run route-generation tests, type tests, and a production build. A passing editor typecheck does not prove the plugin generated or split the new route correctly. + +Commit `routeTree.gen.ts`; it is generated source used by the application at runtime. + ## Individual Plugin Exports For advanced use, each sub-plugin is exported separately from the Vite entry: @@ -227,6 +240,10 @@ function AboutPage() { } ``` +### 4. HIGH: Editing the generated route tree + +Changes to `routeTree.gen.ts` are overwritten and can leave source routes, generated types, and runtime routing out of sync. Fix route filenames or plugin configuration, regenerate, and verify the generated diff instead. + ## Cross-References - [router-core/code-splitting](../../../router-core/skills/router-core/code-splitting/SKILL.md) — manual code splitting concepts diff --git a/packages/solid-router/skills/solid-router/SKILL.md b/packages/solid-router/skills/solid-router/SKILL.md index 75c82805df..0cfd22ad73 100644 --- a/packages/solid-router/skills/solid-router/SKILL.md +++ b/packages/solid-router/skills/solid-router/SKILL.md @@ -8,10 +8,11 @@ description: >- Outlet, CatchBoundary, ErrorComponent. Solid-specific patterns with Accessor returns, createSignal/createMemo/createEffect, Show/Switch/Match/Dynamic, and @solidjs/meta for head management. -type: framework -library: tanstack-router -library_version: '1.166.2' -framework: solid +metadata: + type: framework + library: tanstack-router + library_version: '1.166.2' + framework: solid requires: - router-core sources: @@ -22,8 +23,6 @@ sources: This skill builds on router-core. Read [router-core](../../../router-core/skills/router-core/SKILL.md) first for foundational concepts. -This skill covers the Solid-specific bindings, components, hooks, and setup for TanStack Router. - > **CRITICAL**: TanStack Router types are FULLY INFERRED. Never cast, never annotate inferred values. > **CRITICAL**: TanStack Router is CLIENT-FIRST. Loaders run on the client by default, not on the server. > **CRITICAL**: Most hooks return `Accessor` — you MUST call the accessor (`value()`) to read the reactive value. This is the #1 difference from the React version. diff --git a/packages/solid-start/skills/solid-start/SKILL.md b/packages/solid-start/skills/solid-start/SKILL.md index 03bab29ba8..236bac2b0c 100644 --- a/packages/solid-start/skills/solid-start/SKILL.md +++ b/packages/solid-start/skills/solid-start/SKILL.md @@ -5,10 +5,11 @@ description: >- Vite plugin, StartClient, StartServer, Solid-specific setup, re-exports from @tanstack/start-client-core. Full project setup with Solid. -type: framework -library: tanstack-start -library_version: '1.166.2' -framework: solid +metadata: + type: framework + library: tanstack-start + library_version: '1.166.2' + framework: solid requires: - start-core sources: diff --git a/packages/start-client-core/skills/_artifacts/domain_map.yaml b/packages/start-client-core/skills/_artifacts/domain_map.yaml index 25c34aabfe..ac17d49f56 100644 --- a/packages/start-client-core/skills/_artifacts/domain_map.yaml +++ b/packages/start-client-core/skills/_artifacts/domain_map.yaml @@ -1,13 +1,13 @@ # domain_map.yaml # Generated by skill-domain-discovery # Library: TanStack Start -# Version: 1.166.2 +# Version: 1.170.14 # Date: 2026-03-07 # Status: reviewed library: name: '@tanstack/react-start' - version: '1.166.2' + version: '1.170.14' repository: 'https://github.com/TanStack/router' description: >- Full-stack React framework built on TanStack Router and Vite. Adds @@ -189,6 +189,15 @@ skills: priority: HIGH status: active + - mistake: 'Not using useServerFn for component calls' + mechanism: >- + Component calls need useServerFn so redirects and not-found + responses use the active router instead of falling through as + ordinary function results. + source: 'docs/start/framework/react/guide/server-functions.md' + priority: MEDIUM + status: active + - mistake: 'Generating Next.js or Remix server patterns' mechanism: >- Agents generate getServerSideProps, "use server" directives, @@ -204,6 +213,39 @@ skills: priority: CRITICAL status: active + - mistake: 'Relying on a route guard to protect a server function' + mechanism: >- + beforeLoad protects route UX, but createServerFn exposes an + independently callable endpoint. Private handlers must enforce + authentication and authorization themselves or through middleware. + source: 'protocol-v4 evaluation' + priority: CRITICAL + status: active + + - mistake: 'Self-fetching a relative API URL from an SSR loader' + mechanism: >- + Loaders also run on the server, where relative URLs may not have a + base. App-internal loaders should call a server function directly. + source: 'protocol-v4 evaluation' + priority: CRITICAL + status: active + + - mistake: 'Mutating without invalidating cached loader data' + mechanism: >- + Local state can show a write that was not persisted or leave route + cache stale. Await the write, invalidate, and verify a fresh reload. + source: 'protocol-v4 evaluation' + priority: HIGH + status: active + + - mistake: 'Treating typecheck as proof of output schema propagation' + mechanism: >- + A typed model can still be projected or serialized without the new + field. Assert the actual handler or HTTP payload at runtime. + source: 'protocol-v4 evaluation' + priority: CRITICAL + status: active + # ── Middleware and Context ─────────────────────────────────────── - name: 'Middleware' slug: 'middleware' @@ -354,6 +396,23 @@ skills: priority: CRITICAL status: active + - mistake: 'Hydration mismatches from env-dependent rendering' + mechanism: >- + Rendering different output from server-only environment state + causes the client hydration pass to disagree with the SSR HTML. + Transfer stable data or defer environment-dependent UI. + source: 'docs/start/framework/react/guide/execution-model.md' + priority: HIGH + status: active + + - mistake: 'Using a relative URL in an isomorphic loader' + mechanism: >- + Browser fetch resolves relative URLs against the document, while an + SSR runtime may have no base URL. Use a server function boundary. + source: 'protocol-v4 evaluation' + priority: CRITICAL + status: active + # ── Server Routes ──────────────────────────────────────────────── - name: 'Server Routes' slug: 'server-routes' @@ -387,6 +446,39 @@ skills: priority: MEDIUM status: active + - mistake: 'Forgetting to await request body methods' + mechanism: >- + Request body readers return promises. Using request.json(), + request.text(), or request.formData() without await passes a promise + instead of the parsed request body. + source: 'docs/start/framework/react/guide/server-routes.md' + priority: MEDIUM + status: active + + - mistake: 'Relying on page auth to protect a server route' + mechanism: >- + API handlers are directly callable and must authenticate and + authorize private reads and writes at the handler boundary. + source: 'protocol-v4 evaluation' + priority: CRITICAL + status: active + + - mistake: 'Self-fetching a server route from an SSR loader' + mechanism: >- + A relative API URL can fail during SSR. Share one server-side service + between a server function and server route instead. + source: 'protocol-v4 evaluation' + priority: CRITICAL + status: active + + - mistake: 'Omitting a field from serialized response output' + mechanism: >- + Typechecking does not inspect the runtime Response payload. Test the + serialized output when a schema changes. + source: 'protocol-v4 evaluation' + priority: CRITICAL + status: active + # ── Deployment and Rendering ───────────────────────────────────── - name: 'Deployment' slug: 'deployment' diff --git a/packages/start-client-core/skills/_artifacts/skill_spec.md b/packages/start-client-core/skills/_artifacts/skill_spec.md index a29621e478..841105ee17 100644 --- a/packages/start-client-core/skills/_artifacts/skill_spec.md +++ b/packages/start-client-core/skills/_artifacts/skill_spec.md @@ -17,33 +17,38 @@ TanStack Start is a full-stack React framework built on TanStack Router and Vite | Skill | Type | Domain | What it covers | Failure modes | | ------------------- | --------- | ------------------------ | ------------------------------------------------------- | ------------- | -| start-core | core | project-setup | tanstackStart(), getRouter(), root route, entries | 3 | -| server-functions | core | server-functions | createServerFn, validation, useServerFn, streaming | 4 | +| start-core | core | project-setup | tanstackStart(), getRouter(), root route, entries | 4 | +| server-functions | core | server-functions | createServerFn, validation, useServerFn, streaming | 8 | | middleware | core | middleware-and-context | createMiddleware, context, global middleware, factories | 3 | -| execution-model | core | execution-model | Isomorphic defaults, environment functions, env vars | 4 | -| server-routes | core | server-routes | server property, HTTP handlers, createHandlers | 2 | +| execution-model | core | execution-model | Isomorphic defaults, environment functions, env vars | 5 | +| server-routes | core | server-routes | server property, HTTP handlers, createHandlers | 5 | | deployment | core | deployment-and-rendering | Hosting, SSR modes, prerendering, SEO | 3 | | react-start | framework | project-setup | React bindings, useServerFn, full setup | 3 | | migrate-from-nextjs | lifecycle | project-setup | Next.js App Router migration checklist | 3 | ## Failure Mode Inventory -### start-core (3 failure modes) +### start-core (4 failure modes) | # | Mistake | Priority | Source | | --- | ----------------------------------------------- | -------- | ----------------------- | | 1 | React plugin before Start plugin in Vite config | CRITICAL | docs/build-from-scratch | | 2 | Enabling verbatimModuleSyntax in tsconfig | HIGH | docs/build-from-scratch | | 3 | Missing Scripts component in root route | HIGH | docs/guide/routing | +| 4 | Loading every overlapping Start skill | HIGH | protocol-v4 evaluation | -### server-functions (4 failure modes) +### server-functions (8 failure modes) -| # | Mistake | Priority | Source | -| --- | --------------------------------------------------------------------------- | -------- | --------------------------- | -| 1 | Putting server-only code in loaders instead of server functions | CRITICAL | maintainer interview | -| 2 | Generating Next.js/Remix server patterns ("use server", getServerSideProps) | CRITICAL | maintainer interview | -| 3 | Using dynamic imports for server functions | HIGH | docs/guide/server-functions | -| 4 | Not using useServerFn for component calls | MEDIUM | docs/guide/server-functions | +| # | Mistake | Priority | Source | +| --- | --------------------------------------------------------------- | -------- | --------------------------- | +| 1 | Putting server-only code in loaders instead of server functions | CRITICAL | maintainer interview | +| 2 | Using dynamic imports for server functions | HIGH | docs/guide/server-functions | +| 3 | Not using useServerFn for component calls | MEDIUM | docs/guide/server-functions | +| 4 | Generating Next.js or Remix server patterns | CRITICAL | maintainer interview | +| 5 | Relying on a route guard to protect a server function | CRITICAL | protocol-v4 evaluation | +| 6 | Self-fetching a relative API URL from an SSR loader | CRITICAL | protocol-v4 evaluation | +| 7 | Mutating without invalidating cached loader data | HIGH | protocol-v4 evaluation | +| 8 | Treating typecheck as proof of output schema propagation | CRITICAL | protocol-v4 evaluation | ### middleware (3 failure modes) @@ -53,7 +58,7 @@ TanStack Start is a full-stack React framework built on TanStack Router and Vite | 2 | Confusing request vs server function middleware | MEDIUM | docs/guide/middleware | | 3 | Wrong middleware method order | MEDIUM | docs/guide/middleware | -### execution-model (4 failure modes) +### execution-model (5 failure modes) | # | Mistake | Priority | Source | | --- | ------------------------------------------------- | -------- | -------------------------------- | @@ -61,13 +66,17 @@ TanStack Start is a full-stack React framework built on TanStack Router and Vite | 2 | Exposing secrets via module-level process.env | CRITICAL | docs/guide/execution-model | | 3 | Using VITE\_ prefix for server secrets | CRITICAL | docs/guide/environment-variables | | 4 | Hydration mismatches from env-dependent rendering | HIGH | docs/guide/execution-model | +| 5 | Using a relative URL in an isomorphic loader | CRITICAL | protocol-v4 evaluation | -### server-routes (2 failure modes) +### server-routes (5 failure modes) -| # | Mistake | Priority | Source | -| --- | ---------------------------------------- | -------- | ------------------------ | -| 1 | Duplicate route path resolution | MEDIUM | docs/guide/server-routes | -| 2 | Forgetting to await request body methods | MEDIUM | docs/guide/server-routes | +| # | Mistake | Priority | Source | +| --- | ------------------------------------------------ | -------- | ------------------------ | +| 1 | Duplicate path resolution for server routes | MEDIUM | docs/guide/server-routes | +| 2 | Forgetting to await request body methods | MEDIUM | docs/guide/server-routes | +| 3 | Relying on page auth to protect a server route | CRITICAL | protocol-v4 evaluation | +| 4 | Self-fetching a server route from an SSR loader | CRITICAL | protocol-v4 evaluation | +| 5 | Omitting a field from serialized response output | CRITICAL | protocol-v4 evaluation | ### deployment (3 failure modes) diff --git a/packages/start-client-core/skills/_artifacts/skill_tree.yaml b/packages/start-client-core/skills/_artifacts/skill_tree.yaml index 3ab8793408..673f7ab543 100644 --- a/packages/start-client-core/skills/_artifacts/skill_tree.yaml +++ b/packages/start-client-core/skills/_artifacts/skill_tree.yaml @@ -1,7 +1,7 @@ # packages/start-client-core/skills/_artifacts/skill_tree.yaml library: name: '@tanstack/start-client-core' - version: '1.166.2' + version: '1.170.14' repository: 'https://github.com/TanStack/router' description: >- Framework-agnostic client-side core for TanStack Start. diff --git a/packages/start-client-core/skills/start-core/SKILL.md b/packages/start-client-core/skills/start-core/SKILL.md index 82481417bc..60d9f3fcd1 100644 --- a/packages/start-client-core/skills/start-core/SKILL.md +++ b/packages/start-client-core/skills/start-core/SKILL.md @@ -5,9 +5,10 @@ description: >- getRouter() factory, root route document shell (HeadContent, Scripts, Outlet), client/server entry points, routeTree.gen.ts, tsconfig configuration. Entry point for all Start skills. -type: core -library: tanstack-start -library_version: '1.166.2' +metadata: + type: core + library: tanstack-start + library_version: '1.170.14' sources: - TanStack/router:docs/start/framework/react/build-from-scratch.md - TanStack/router:docs/start/framework/react/quick-start.md @@ -22,6 +23,8 @@ TanStack Start is a full-stack React framework built on TanStack Router and Vite > **CRITICAL**: TanStack Start is NOT Next.js. Do not generate `getServerSideProps`, `"use server"` directives, `app/layout.tsx`, or any Next.js/Remix patterns. Use `createServerFn` for server-only code. > **CRITICAL**: Types are FULLY INFERRED. Never cast, never annotate inferred values. +Use this entry skill to pick one primary workflow. Do not load every Start sub-skill. Add another only when the implementation crosses that boundary; for example, a protected mutation needs `server-functions` plus `auth-server-primitives`, while a public REST endpoint needs `server-routes` alone. + ## Sub-Skills | Task | Sub-Skill | @@ -55,6 +58,20 @@ Need to deploy, configure SSR, or prerender? → start-core/deployment ``` +## Full-Stack Delivery Workflow + +For application data loaded by a Start route: + +1. Put database, filesystem, secrets, and persistence code behind `createServerFn`. +2. Call the server function directly from the route loader. Do not self-fetch a relative `/api/...` URL from an SSR loader. +3. Validate every input and enforce auth in the server function or middleware. `beforeLoad` only protects route UX. +4. After a mutation resolves, invalidate the router or the external query cache and await the refresh when the UI must be current before continuing. +5. For schema changes, update storage, validation, handler serialization, loader, and UI. Assert the actual runtime payload; typechecking alone cannot detect an omitted serialized field. + +Use a server route when the raw HTTP contract is the product: webhooks, third-party clients, feeds, file responses, or a public REST API. When the Start UI and a server route share data, call one server-side service from both instead of making the SSR loader fetch its own API route. + +Before finishing, test the initial SSR request, client navigation, mutation followed by refresh, and a direct anonymous request to every protected endpoint. + ## Project Setup ### 1. Install Dependencies @@ -211,4 +228,4 @@ function RootComponent() { ## Version Note -This skill targets `@tanstack/react-start` v1.166.2 and `@tanstack/start-client-core` v1.166.2. +This skill targets `@tanstack/start-client-core` v1.170.14. diff --git a/packages/start-client-core/skills/start-core/auth-server-primitives/SKILL.md b/packages/start-client-core/skills/start-core/auth-server-primitives/SKILL.md index 40205bc074..8f20dcaa27 100644 --- a/packages/start-client-core/skills/start-core/auth-server-primitives/SKILL.md +++ b/packages/start-client-core/skills/start-core/auth-server-primitives/SKILL.md @@ -1,5 +1,5 @@ --- -name: start-core/auth-server-primitives +name: auth-server-primitives description: >- Server-side authentication primitives for TanStack Start: session cookies (HttpOnly, Secure, SameSite, __Host- prefix), session @@ -8,9 +8,10 @@ description: >- enumeration defense, CSRF for non-GET RPCs, rate limiting auth endpoints, session rotation on privilege change. Pairs with router-core/auth-and-guards for the routing side. -type: sub-skill -library: tanstack-start -library_version: '1.166.2' +metadata: + type: sub-skill + library: tanstack-start + library_version: '1.170.14' requires: - start-core - start-core/server-functions @@ -38,6 +39,7 @@ This skill covers the **server half** of authentication: session storage, cookie - Use CSRF or same-origin protections for non-GET server functions and server routes. - Log authentication events and monitor failures. - Test direct unauthenticated calls to protected server functions; they should reject before returning data. +- Follow the anonymous redirect and inspect its HTML and serialized state. Login and unauthorized pages must not name the protected user, tenant, or record. ## Session Cookies diff --git a/packages/start-client-core/skills/start-core/deployment/SKILL.md b/packages/start-client-core/skills/start-core/deployment/SKILL.md index 62111f9edd..39939ea5e7 100644 --- a/packages/start-client-core/skills/start-core/deployment/SKILL.md +++ b/packages/start-client-core/skills/start-core/deployment/SKILL.md @@ -1,13 +1,14 @@ --- -name: start-core/deployment +name: deployment description: >- Deploy to Cloudflare Workers, Netlify, Vercel, Node.js/Docker, Bun, Railway. Selective SSR (ssr option per route), SPA mode, static prerendering, ISR with Cache-Control headers, SEO and head management. -type: sub-skill -library: tanstack-start -library_version: '1.166.2' +metadata: + type: sub-skill + library: tanstack-start + library_version: '1.170.14' requires: - start-core sources: diff --git a/packages/start-client-core/skills/start-core/execution-model/SKILL.md b/packages/start-client-core/skills/start-core/execution-model/SKILL.md index ffed9211dc..271efe6bf1 100644 --- a/packages/start-client-core/skills/start-core/execution-model/SKILL.md +++ b/packages/start-client-core/skills/start-core/execution-model/SKILL.md @@ -1,14 +1,15 @@ --- -name: start-core/execution-model +name: execution-model description: >- Isomorphic-by-default principle, environment boundary functions (createServerFn, createServerOnlyFn, createClientOnlyFn, createIsomorphicFn), ClientOnly component, useHydrated hook, import protection, dead code elimination, environment variable safety (VITE_ prefix, process.env). -type: sub-skill -library: tanstack-start -library_version: '1.166.2' +metadata: + type: sub-skill + library: tanstack-start + library_version: '1.170.14' requires: - start-core sources: @@ -24,6 +25,7 @@ Understanding where code runs is fundamental to TanStack Start. This skill cover > **CRITICAL**: ALL code in TanStack Start is isomorphic by default — it runs in BOTH server and client bundles. Route loaders run on BOTH server (during SSR) AND client (during navigation). Server-only operations MUST use `createServerFn`. > **CRITICAL**: Module-level `process.env` access is wrong on **two** axes — security (values leak into the client bundle) AND runtime correctness (on Cloudflare Workers and other edge runtimes, env is injected per-request, so module-level reads evaluate to `undefined` even on the server). Read env inside `.handler()` or another per-request function, never at module scope. > **CRITICAL**: `VITE_` prefixed environment variables are exposed to the client bundle. Server secrets must NOT have the `VITE_` prefix. +> **CRITICAL**: Relative URLs belong to browser-only code. An isomorphic loader also runs during SSR, where `fetch('/api/...')` may have no base URL. Call a server function from the loader, or keep the fetch inside an explicit environment boundary. ## Execution Control APIs diff --git a/packages/start-client-core/skills/start-core/middleware/SKILL.md b/packages/start-client-core/skills/start-core/middleware/SKILL.md index 483c6b89ae..972ed474af 100644 --- a/packages/start-client-core/skills/start-core/middleware/SKILL.md +++ b/packages/start-client-core/skills/start-core/middleware/SKILL.md @@ -1,14 +1,15 @@ --- -name: start-core/middleware +name: middleware description: >- createMiddleware, request middleware (.server only), server function middleware (.client + .server), context passing via next({ context }), sendContext for client-server transfer, global middleware via createStart in src/start.ts, middleware factories, method order enforcement, fetch override precedence. -type: sub-skill -library: tanstack-start -library_version: '1.166.2' +metadata: + type: sub-skill + library: tanstack-start + library_version: '1.170.14' requires: - start-core - start-core/server-functions diff --git a/packages/start-client-core/skills/start-core/server-functions/SKILL.md b/packages/start-client-core/skills/start-core/server-functions/SKILL.md index 3bafbad624..603cbfa2be 100644 --- a/packages/start-client-core/skills/start-core/server-functions/SKILL.md +++ b/packages/start-client-core/skills/start-core/server-functions/SKILL.md @@ -1,14 +1,15 @@ --- -name: start-core/server-functions +name: server-functions description: >- createServerFn (GET/POST), validator (Zod or function), useServerFn hook, server context utilities (getRequest, getRequestHeader, setResponseHeader, setResponseStatus), error handling (throw errors, redirect, notFound), streaming, FormData handling, file organization (.functions.ts, .server.ts). -type: sub-skill -library: tanstack-start -library_version: '1.166.2' +metadata: + type: sub-skill + library: tanstack-start + library_version: '1.170.14' requires: - start-core sources: @@ -92,6 +93,52 @@ function DeleteButton({ postId }: { postId: string }) { } ``` +## Cache-Coherent Mutations + +Keep reads and writes behind server functions that use the same authoritative store. Await the write, then invalidate the route cache so its loader reads the persisted result: + +```tsx +const listIssues = createServerFn({ method: 'GET' }).handler(() => { + return db.issues.findMany() +}) + +const createIssue = createServerFn({ method: 'POST' }) + .validator((data: unknown) => { + if ( + typeof data !== 'object' || + data === null || + !('title' in data) || + typeof data.title !== 'string' || + data.title.trim().length === 0 + ) { + throw new Error('Title is required') + } + return { title: data.title.trim() } + }) + .handler(async ({ data }) => { + return db.issues.create({ data }) + }) + +export const Route = createFileRoute('/issues')({ + loader: () => listIssues(), + component: IssuesPage, +}) + +function IssuesPage() { + const router = useRouter() + const createIssueFn = useServerFn(createIssue) + + const handleCreate = async (title: string) => { + await createIssueFn({ data: { title } }) + await router.invalidate({ sync: true }) + } + + // render Route.useLoaderData() and call handleCreate from the form +} +``` + +Do not update only local component state after a persistent mutation. Verify the rendered list after create, update, delete, and a fresh page load. + ## Input Validation ### Basic Validator @@ -123,6 +170,8 @@ const createUser = createServerFn({ method: 'POST' }) }) ``` +Input validation does not validate output serialization. When a response schema changes, update the database selection, service return value, server-function result, loader consumer, and UI. Add a runtime test that calls the handler or HTTP boundary and asserts the new field in the returned payload. + ### FormData ```tsx @@ -425,6 +474,22 @@ const signupFn = useServerFn(signup) // signup throws redirect on success If in doubt: wrap with `useServerFn`. It's a no-op for plain-data functions and the safe default when a function might later add a redirect. +### 8. CRITICAL: Self-fetching a relative API URL from a loader + +```tsx +// WRONG — this loader also runs during SSR, where a relative URL may fail +export const Route = createFileRoute('/issues')({ + loader: () => fetch('/api/issues').then((response) => response.json()), +}) + +// CORRECT — call the server function; the client build gets an RPC stub +export const Route = createFileRoute('/issues')({ + loader: () => listIssues(), +}) +``` + +Relative `fetch` is fine in a browser-only event handler. It is not a universal loader data strategy. + ## Cross-References - [start-core/execution-model](../execution-model/SKILL.md) — understanding where code runs diff --git a/packages/start-client-core/skills/start-core/server-routes/SKILL.md b/packages/start-client-core/skills/start-core/server-routes/SKILL.md index 101541b2aa..9655f243ed 100644 --- a/packages/start-client-core/skills/start-core/server-routes/SKILL.md +++ b/packages/start-client-core/skills/start-core/server-routes/SKILL.md @@ -1,14 +1,15 @@ --- -name: start-core/server-routes +name: server-routes description: >- Server-side API endpoints using the server property on createFileRoute, HTTP method handlers (GET, POST, PUT, DELETE), createHandlers for per-handler middleware, handler context (request, params, context), request body parsing, response helpers, file naming for API routes. -type: sub-skill -library: tanstack-start -library_version: '1.166.2' +metadata: + type: sub-skill + library: tanstack-start + library_version: '1.170.14' requires: - start-core sources: @@ -19,6 +20,8 @@ sources: Server routes are API endpoints defined alongside app routes in the `src/routes` directory. They use the `server` property on `createFileRoute` and handle raw HTTP requests. +Use server routes when callers need an HTTP contract. For data used only by the Start application, prefer a server function and call it directly from the loader. A route loader runs during SSR and client navigation, so `fetch('/api/...')` is not a portable loader pattern. + ## Basic Server Route ```ts @@ -77,6 +80,39 @@ function HelloComponent() { } ``` +The relative `fetch('/hello')` above is safe because it runs only in a browser click handler. + +## Sharing Data with a Start Route + +Do not make the SSR loader call its own server route. Put the business operation in a server-only service, then expose it through both boundaries when both are required: + +```tsx +// src/server/issues.server.ts +export function listIssues() { + return db.issues.findMany() +} + +// src/routes/api/issues.ts +export const Route = createFileRoute('/api/issues')({ + server: { + handlers: { + GET: async () => Response.json(await listIssues()), + }, + }, +}) + +// src/routes/issues.tsx +const getIssues = createServerFn({ method: 'GET' }).handler(() => { + return listIssues() +}) + +export const Route = createFileRoute('/issues')({ + loader: () => getIssues(), +}) +``` + +This keeps SSR independent of URL resolution and keeps one source of business logic. + ## File Route Conventions Server routes follow TanStack Router file-based routing conventions: @@ -253,7 +289,11 @@ export const Route = createFileRoute('/api/posts')({ ## Common Mistakes -### 1. MEDIUM: Duplicate route paths +### 1. CRITICAL: Protecting the page but not the server route + +Every handler that reads or writes private data must authenticate and authorize the request through route middleware, handler middleware, or an in-handler check. A router `beforeLoad` redirect does not protect `/api/...`. Test the handler directly without cookies and assert that no private payload is returned. + +### 2. MEDIUM: Duplicate route paths ```text # WRONG — both resolve to /users, causes error @@ -264,7 +304,7 @@ routes/users/index.ts routes/users.ts ``` -### 2. MEDIUM: Forgetting to await request body methods +### 3. MEDIUM: Forgetting to await request body methods ```ts // WRONG — body is a Promise, not the actual data @@ -274,6 +314,10 @@ const body = request.json() const body = await request.json() ``` +### 4. HIGH: Trusting TypeScript as response-schema validation + +Validate request input and test the serialized `Response` output. A typed service can still be projected or serialized without a newly added field. For schema changes, assert `await response.json()` at the server-route boundary. + ## Cross-References - [start-core/middleware](../middleware/SKILL.md) — middleware for server routes diff --git a/packages/start-server-core/skills/_artifacts/domain_map.yaml b/packages/start-server-core/skills/_artifacts/domain_map.yaml index 1a6351bdea..7dc4d303ac 100644 --- a/packages/start-server-core/skills/_artifacts/domain_map.yaml +++ b/packages/start-server-core/skills/_artifacts/domain_map.yaml @@ -1,12 +1,12 @@ # domain_map.yaml # Library: @tanstack/start-server-core -# Version: 1.166.2 +# Version: 1.169.17 # Date: 2026-03-08 # Status: reviewed library: name: '@tanstack/start-server-core' - version: '1.166.2' + version: '1.169.17' repository: 'https://github.com/TanStack/router' description: >- Server-side runtime for TanStack Start: createStartHandler, @@ -62,4 +62,20 @@ skills: priority: MEDIUM status: active + - mistake: 'Capturing request or environment state at module scope' + mechanism: >- + Request context and edge runtime environment values are per-request. + Module-level capture can read undefined values or leak state between + concurrent requests. + priority: CRITICAL + status: active + + - mistake: 'Treating cookie session data as authoritative persistence' + mechanism: >- + Cookie sessions should contain a small stable identifier. Current + permissions and revocable state belong in an authoritative server + store and must be loaded for each protected request. + priority: HIGH + status: active + gaps: [] diff --git a/packages/start-server-core/skills/_artifacts/skill_spec.md b/packages/start-server-core/skills/_artifacts/skill_spec.md index 19270dc59a..402eb2c118 100644 --- a/packages/start-server-core/skills/_artifacts/skill_spec.md +++ b/packages/start-server-core/skills/_artifacts/skill_spec.md @@ -12,13 +12,15 @@ Server-side runtime for TanStack Start: createStartHandler, request/response uti | Skill | Type | Domain | What it covers | Failure modes | | ----------------- | ---- | -------------- | ---------------------------------------------------------------- | ------------- | -| start-server-core | core | server-runtime | createStartHandler, cookies, sessions, three-phase handling, ALS | 2 | +| start-server-core | core | server-runtime | createStartHandler, cookies, sessions, three-phase handling, ALS | 4 | ## Failure Mode Inventory -### start-server-core (2 failure modes) +### start-server-core (4 failure modes) -| # | Mistake | Priority | Source | -| --- | ---------------------------------------------- | -------- | ----------- | -| 1 | Missing AsyncLocalStorage setup | HIGH | source/docs | -| 2 | Incorrect handler export for deployment target | MEDIUM | source/docs | +| # | Mistake | Priority | Source | +| --- | --------------------------------------------------------- | -------- | ---------------------- | +| 1 | Missing AsyncLocalStorage setup | HIGH | source/docs | +| 2 | Incorrect handler export for deployment target | MEDIUM | source/docs | +| 3 | Capturing request or environment state at module scope | CRITICAL | protocol-v4 evaluation | +| 4 | Treating cookie session data as authoritative persistence | HIGH | protocol-v4 evaluation | diff --git a/packages/start-server-core/skills/_artifacts/skill_tree.yaml b/packages/start-server-core/skills/_artifacts/skill_tree.yaml index 6c792cf1bd..70125a39b6 100644 --- a/packages/start-server-core/skills/_artifacts/skill_tree.yaml +++ b/packages/start-server-core/skills/_artifacts/skill_tree.yaml @@ -1,6 +1,6 @@ library: name: '@tanstack/start-server-core' - version: '1.166.2' + version: '1.169.17' repository: 'https://github.com/TanStack/router' description: >- Server-side runtime for TanStack Start: createStartHandler, diff --git a/packages/start-server-core/skills/start-server-core/SKILL.md b/packages/start-server-core/skills/start-server-core/SKILL.md index 7dc16d517f..95fb028cea 100644 --- a/packages/start-server-core/skills/start-server-core/SKILL.md +++ b/packages/start-server-core/skills/start-server-core/SKILL.md @@ -5,9 +5,10 @@ description: >- request/response utilities (getRequest, setResponseHeader, setCookie, getCookie, useSession), three-phase request handling, AsyncLocalStorage context. -type: core -library: tanstack-start -library_version: '1.166.2' +metadata: + type: core + library: tanstack-start + library_version: '1.169.17' sources: - TanStack/router:packages/start-server-core/src - TanStack/router:docs/start/framework/react/guide/server-entry-point.md @@ -20,6 +21,8 @@ Server-side runtime for TanStack Start. Provides the request handler, request/re > **CRITICAL**: These utilities are SERVER-ONLY. Import them from `@tanstack/-start/server`, not from the main entry point. They throw if called outside a server request context. > > **CRITICAL**: Types are FULLY INFERRED. Never cast, never annotate inferred values. +> +> **CRITICAL**: Read cookies, headers, request URLs, and runtime environment values inside the active request. Do not capture them at module scope; edge runtimes may inject them per request, and concurrent requests must never share request-derived state. ## `createStartHandler` @@ -120,7 +123,8 @@ const serverFn = createServerFn({ method: 'POST' }).handler(async () => { setCookie('preference', 'dark', { httpOnly: true, - secure: true, + secure: process.env.NODE_ENV === 'production', + sameSite: 'lax', maxAge: 60 * 60 * 24 * 30, // 30 days path: '/', }) @@ -143,29 +147,84 @@ import { clearSession, } from '@tanstack/react-start/server' -const sessionConfig = { - password: process.env.SESSION_SECRET!, - name: 'my-app-session', - maxAge: 60 * 60 * 24 * 7, // 7 days +type SessionData = { + userId?: string +} + +function getSessionConfig() { + const password = process.env.SESSION_SECRET + if (!password || password.length < 32) { + throw new Error('SESSION_SECRET must be at least 32 characters') + } + + return { + password, + name: 'my-app-session', + maxAge: 60 * 60 * 24 * 7, + cookie: { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + sameSite: 'lax' as const, + path: '/', + }, + } +} + +function getDummyPasswordHash() { + // Precompute this with the same algorithm and cost as real password hashes. + const hash = process.env.DUMMY_PASSWORD_HASH + if (!hash) { + throw new Error('DUMMY_PASSWORD_HASH is required') + } + return hash } // Full session manager const getUser = createServerFn({ method: 'GET' }).handler(async () => { - const session = await useSession<{ userId: string }>(sessionConfig) - return session.data + const session = await useSession(getSessionConfig()) + if (!session.data.userId) { + return null + } + return db.users.findById(session.data.userId) }) // Update session const login = createServerFn({ method: 'POST' }) - .validator((data: { userId: string }) => data) + .validator((data: unknown) => { + if ( + typeof data !== 'object' || + data === null || + !('email' in data) || + typeof data.email !== 'string' || + data.email.trim().length === 0 || + !('password' in data) || + typeof data.password !== 'string' || + data.password.length === 0 + ) { + throw new Error('Invalid credentials') + } + return { + email: data.email.trim().toLowerCase(), + password: data.password, + } + }) .handler(async ({ data }) => { - await updateSession(sessionConfig, { userId: data.userId }) + const user = await db.users.findByEmail(data.email) + const passwordHash = user?.passwordHash ?? getDummyPasswordHash() + const passwordMatches = await verifyPassword(data.password, passwordHash) + if (!user || !passwordMatches) { + throw new Error('Invalid credentials') + } + + await updateSession(getSessionConfig(), { + userId: user.id, + }) return { success: true } }) // Clear session const logout = createServerFn({ method: 'POST' }).handler(async () => { - await clearSession(sessionConfig) + await clearSession(getSessionConfig()) return { success: true } }) ``` @@ -190,6 +249,14 @@ await session.update({ userId: '123' }) // Persist session data await session.clear() // Clear session data ``` +### Production Session Rules + +- Keep cookie session data small and non-sensitive. Store a stable session or user ID, then load current permissions and account state from the authoritative store on each protected request. +- Use a server-side session record when you need revocation, device tracking, large data, or immediate role changes. Put only its opaque ID in the cookie. +- Rotate the session after login, privilege changes, password changes, and logout. +- Use `HttpOnly`, `SameSite`, `Path=/`, and `Secure` in production. Use a `__Host-` cookie name in production only when `Secure`, no `Domain`, and `Path=/` are all enforced. +- Use the same cookie name and path when clearing a session. Test login, authenticated refresh, expiry, logout, and a replay of the old cookie. + ## Query Validation Validate query string parameters using a Standard Schema: @@ -255,6 +322,10 @@ const getAuth = createServerFn({ method: 'GET' }).handler(async () => { Session cookies should use `secure: true` in production. The default cookie options may not enforce this. +### 4. CRITICAL: Capturing request or environment state at module scope + +Do not create session config from `process.env` at module load or cache `getRequest()`, headers, cookies, or session data in a module variable. Create config and read request state inside the handler or middleware callback. This is required for per-request edge environments and prevents cross-request data leaks. + ## Cross-References - [start-core/server-functions](../../../start-client-core/skills/start-core/server-functions/SKILL.md) — creating server functions that use these utilities diff --git a/packages/start-server-core/vite.config.ts b/packages/start-server-core/vite.config.ts index aaece761fb..dd252ddb08 100644 --- a/packages/start-server-core/vite.config.ts +++ b/packages/start-server-core/vite.config.ts @@ -24,6 +24,12 @@ const fakeRouterEntry = fileURLToPath( const fakeStartManifest = fileURLToPath( new URL('./tests/fixtures/start-manifest.ts', import.meta.url), ) +const fakePluginAdapters = fileURLToPath( + new URL('./src/empty-plugin-adapters.ts', import.meta.url), +) +const fakeServerFnResolver = fileURLToPath( + new URL('./src/fake-start-server-fn-resolver.ts', import.meta.url), +) export default mergeConfig( config, @@ -53,6 +59,8 @@ export default mergeConfig( alias: { '#tanstack-start-entry': fakeStartEntry, '#tanstack-router-entry': fakeRouterEntry, + '#tanstack-start-plugin-adapters': fakePluginAdapters, + '#tanstack-start-server-fn-resolver': fakeServerFnResolver, 'tanstack-start-manifest:v': fakeStartManifest, }, }, diff --git a/packages/virtual-file-routes/skills/_artifacts/domain_map.yaml b/packages/virtual-file-routes/skills/_artifacts/domain_map.yaml index db82f87ec0..0841238c5e 100644 --- a/packages/virtual-file-routes/skills/_artifacts/domain_map.yaml +++ b/packages/virtual-file-routes/skills/_artifacts/domain_map.yaml @@ -1,12 +1,12 @@ # domain_map.yaml # Library: @tanstack/virtual-file-routes -# Version: 1.161.4 +# Version: 1.162.0 # Date: 2026-03-08 # Status: reviewed library: name: '@tanstack/virtual-file-routes' - version: '1.161.4' + version: '1.162.0' repository: 'https://github.com/TanStack/router' description: >- Programmatic route tree building as an alternative to filesystem @@ -63,4 +63,11 @@ skills: priority: MEDIUM status: active + - mistake: 'Creating duplicate effective paths or physical mounts' + mechanism: >- + Virtual nodes and mounted physical subtrees can resolve to the same + path or ID. Regenerate and inspect the full tree after every refactor. + priority: HIGH + status: active + gaps: [] diff --git a/packages/virtual-file-routes/skills/_artifacts/skill_spec.md b/packages/virtual-file-routes/skills/_artifacts/skill_spec.md index 834294f8f6..02f2942727 100644 --- a/packages/virtual-file-routes/skills/_artifacts/skill_spec.md +++ b/packages/virtual-file-routes/skills/_artifacts/skill_spec.md @@ -12,13 +12,14 @@ Programmatic route tree building as an alternative to filesystem conventions. Pr | Skill | Type | Domain | What it covers | Failure modes | | ------------------- | ---- | -------------------- | ---------------------------------------------------------- | ------------- | -| virtual-file-routes | core | virtual-route-config | rootRoute, index, route, layout, physical, subtree configs | 2 | +| virtual-file-routes | core | virtual-route-config | rootRoute, index, route, layout, physical, subtree configs | 3 | ## Failure Mode Inventory -### virtual-file-routes (2 failure modes) +### virtual-file-routes (3 failure modes) -| # | Mistake | Priority | Source | -| --- | --------------------------------------- | -------- | ----------- | -| 1 | Forgetting rootRoute wrapper | HIGH | source/docs | -| 2 | Using physical() path outside routesDir | MEDIUM | source/docs | +| # | Mistake | Priority | Source | +| --- | -------------------------------------------- | -------- | ----------------- | +| 1 | Forgetting rootRoute wrapper | HIGH | source/docs | +| 2 | Using physical() path outside routesDir | MEDIUM | source/docs | +| 3 | Creating duplicate effective paths or mounts | HIGH | consistency audit | diff --git a/packages/virtual-file-routes/skills/_artifacts/skill_tree.yaml b/packages/virtual-file-routes/skills/_artifacts/skill_tree.yaml index f615e46c8f..7d8b510ca9 100644 --- a/packages/virtual-file-routes/skills/_artifacts/skill_tree.yaml +++ b/packages/virtual-file-routes/skills/_artifacts/skill_tree.yaml @@ -1,6 +1,6 @@ library: name: '@tanstack/virtual-file-routes' - version: '1.161.4' + version: '1.162.0' repository: 'https://github.com/TanStack/router' description: >- Programmatic route tree building as an alternative to filesystem diff --git a/packages/virtual-file-routes/skills/virtual-file-routes/SKILL.md b/packages/virtual-file-routes/skills/virtual-file-routes/SKILL.md index 093d73c0ba..8e2c497fa2 100644 --- a/packages/virtual-file-routes/skills/virtual-file-routes/SKILL.md +++ b/packages/virtual-file-routes/skills/virtual-file-routes/SKILL.md @@ -5,9 +5,10 @@ description: >- conventions: rootRoute, index, route, layout, physical, defineVirtualSubtreeConfig. Use with TanStack Router plugin's virtualRouteConfig option. -type: core -library: tanstack-router -library_version: '1.161.4' +metadata: + type: core + library: tanstack-router + library_version: '1.162.0' sources: - TanStack/router:packages/virtual-file-routes/src - TanStack/router:docs/router/routing/virtual-file-routes.md @@ -17,6 +18,8 @@ sources: Build route trees programmatically instead of relying on filesystem conventions. Useful when you want explicit control over route structure, need to mix virtual and physical routes, or want to define route subtrees within file-based routing directories. +Use this skill only when the project config contains `virtualRouteConfig`, a virtual route config file, or `__virtual.ts`. Ordinary file-route changes belong to Router Core and the Router Plugin. + > **CRITICAL**: Types are FULLY INFERRED. Never cast, never annotate inferred values. ## Install @@ -216,3 +219,13 @@ layout('dashboardLayout.tsx', [route('/dashboard', 'dashboard.tsx')]) // The URL is /dashboard, and dashboardLayout.tsx wraps it ``` + +## Refactor Audit + +After changing a virtual tree: + +1. Confirm there is exactly one `rootRoute` and that every referenced file exists relative to `routesDirectory`. +2. Check each `route`, `layout`, and `physical` node for duplicate effective paths or IDs. +3. Confirm each `physical()` directory is mounted once at the intended path prefix and does not overlap a virtual child. +4. Regenerate `routeTree.gen.ts` and inspect parentage, full paths, and imports. Do not edit generated output. +5. Update links, redirects, params, and `from` narrowing, then run generator tests, type tests, and a production build. diff --git a/packages/vue-router/skills/vue-router/SKILL.md b/packages/vue-router/skills/vue-router/SKILL.md index 17108957aa..5fea4fef7a 100644 --- a/packages/vue-router/skills/vue-router/SKILL.md +++ b/packages/vue-router/skills/vue-router/SKILL.md @@ -8,10 +8,11 @@ description: >- Outlet, CatchBoundary, ErrorComponent, Html, Body. Vue-specific patterns with Ref returns, defineComponent, h() render functions, provide/inject, and computed refs. -type: framework -library: tanstack-router -library_version: '1.166.2' -framework: vue +metadata: + type: framework + library: tanstack-router + library_version: '1.166.2' + framework: vue requires: - router-core sources: diff --git a/packages/vue-start/skills/vue-start/SKILL.md b/packages/vue-start/skills/vue-start/SKILL.md index 13fac52d1b..fde7f32ace 100644 --- a/packages/vue-start/skills/vue-start/SKILL.md +++ b/packages/vue-start/skills/vue-start/SKILL.md @@ -5,10 +5,11 @@ description: >- Vite plugin, StartClient, StartServer, Vue-specific setup, re-exports from @tanstack/start-client-core. Full project setup with Vue. -type: framework -library: tanstack-start -library_version: '1.166.2' -framework: vue +metadata: + type: framework + library: tanstack-start + library_version: '1.166.2' + framework: vue requires: - start-core sources: From f04ddc61e7a2332899dbaa65d82ec2c10bda9eef Mon Sep 17 00:00:00 2001 From: Cong-Cong Pan Date: Thu, 30 Jul 2026 03:11:16 +0800 Subject: [PATCH 33/51] fix(start): skip errored Rspack modules during import protection (#7685) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- benchmarks/bundle-size/package.json | 2 +- .../rspack-basic-file-based/package.json | 2 +- .../package.json | 2 +- e2e/react-start/basic/package.json | 2 +- e2e/react-start/css-inline/package.json | 2 +- .../custom-server-rsbuild/package.json | 2 +- .../deferred-hydration/package.json | 2 +- e2e/react-start/hmr/package.json | 2 +- .../import-protection/package.json | 2 +- e2e/react-start/rsbuild/.gitignore | 21 + e2e/react-start/rsbuild/.prettierignore | 1 + e2e/react-start/rsbuild/package.json | 40 + e2e/react-start/rsbuild/playwright.config.ts | 29 + e2e/react-start/rsbuild/rsbuild.config.ts | 14 + e2e/react-start/rsbuild/server.js | 62 ++ e2e/react-start/rsbuild/src/routeTree.gen.ts | 68 ++ e2e/react-start/rsbuild/src/router.tsx | 10 + e2e/react-start/rsbuild/src/routes/__root.tsx | 37 + e2e/react-start/rsbuild/src/routes/index.tsx | 14 + .../rsbuild/tests/error-overlay.spec.ts | 401 +++++++++ e2e/react-start/rsbuild/tsconfig.json | 21 + e2e/react-start/rsc-rsbuild/package.json | 2 +- e2e/react-start/rsc/package.json | 2 +- e2e/react-start/server-functions/package.json | 2 +- .../rspack-basic-file-based/package.json | 2 +- .../package.json | 2 +- e2e/solid-start/basic/package.json | 2 +- .../deferred-hydration/package.json | 2 +- .../rspack-basic-file-based/package.json | 2 +- .../package.json | 2 +- e2e/vue-start/basic/package.json | 8 +- .../quickstart-rspack-file-based/package.json | 2 +- .../quickstart-rspack-file-based/package.json | 2 +- packages/react-start/package.json | 2 +- packages/router-plugin/package.json | 1 + packages/solid-start/package.json | 2 +- packages/start-plugin-core/package.json | 2 +- .../src/rsbuild/import-protection.ts | 5 + packages/vue-start/package.json | 2 +- pnpm-lock.yaml | 784 ++++++++---------- 40 files changed, 1110 insertions(+), 454 deletions(-) create mode 100644 e2e/react-start/rsbuild/.gitignore create mode 100644 e2e/react-start/rsbuild/.prettierignore create mode 100644 e2e/react-start/rsbuild/package.json create mode 100644 e2e/react-start/rsbuild/playwright.config.ts create mode 100644 e2e/react-start/rsbuild/rsbuild.config.ts create mode 100644 e2e/react-start/rsbuild/server.js create mode 100644 e2e/react-start/rsbuild/src/routeTree.gen.ts create mode 100644 e2e/react-start/rsbuild/src/router.tsx create mode 100644 e2e/react-start/rsbuild/src/routes/__root.tsx create mode 100644 e2e/react-start/rsbuild/src/routes/index.tsx create mode 100644 e2e/react-start/rsbuild/tests/error-overlay.spec.ts create mode 100644 e2e/react-start/rsbuild/tsconfig.json diff --git a/benchmarks/bundle-size/package.json b/benchmarks/bundle-size/package.json index c667744ea6..500cf9b8f2 100644 --- a/benchmarks/bundle-size/package.json +++ b/benchmarks/bundle-size/package.json @@ -48,7 +48,7 @@ "vue": "^3.5.16" }, "devDependencies": { - "@rsbuild/core": "^2.0.11", + "@rsbuild/core": "^2.1.0", "@rsbuild/plugin-react": "^2.0.0", "@tanstack/router-plugin": "workspace:^", "@types/react": "^19.0.8", diff --git a/e2e/react-router/rspack-basic-file-based/package.json b/e2e/react-router/rspack-basic-file-based/package.json index e193651193..455ff94779 100644 --- a/e2e/react-router/rspack-basic-file-based/package.json +++ b/e2e/react-router/rspack-basic-file-based/package.json @@ -18,7 +18,7 @@ }, "devDependencies": { "@playwright/test": "^1.61.0", - "@rsbuild/core": "^2.0.11", + "@rsbuild/core": "^2.1.0", "@rsbuild/plugin-react": "^2.0.0", "@tailwindcss/postcss": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", diff --git a/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/package.json b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/package.json index 9fe1da839f..28d3e9b302 100644 --- a/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/package.json +++ b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/package.json @@ -18,7 +18,7 @@ }, "devDependencies": { "@playwright/test": "^1.61.0", - "@rsbuild/core": "^2.0.11", + "@rsbuild/core": "^2.1.0", "@rsbuild/plugin-react": "^2.0.0", "@tailwindcss/postcss": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", diff --git a/e2e/react-start/basic/package.json b/e2e/react-start/basic/package.json index 8551357485..9bd494b835 100644 --- a/e2e/react-start/basic/package.json +++ b/e2e/react-start/basic/package.json @@ -32,7 +32,7 @@ }, "devDependencies": { "@playwright/test": "^1.61.0", - "@rsbuild/core": "^2.0.11", + "@rsbuild/core": "^2.1.0", "@rsbuild/plugin-react": "^2.0.0", "@tailwindcss/postcss": "^4.2.2", "@tailwindcss/vite": "^4.2.2", diff --git a/e2e/react-start/css-inline/package.json b/e2e/react-start/css-inline/package.json index c4720b7eed..7d9c0520fc 100644 --- a/e2e/react-start/css-inline/package.json +++ b/e2e/react-start/css-inline/package.json @@ -24,7 +24,7 @@ }, "devDependencies": { "@playwright/test": "^1.61.0", - "@rsbuild/core": "^2.0.11", + "@rsbuild/core": "^2.1.0", "@rsbuild/plugin-react": "^2.0.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/react-start/custom-server-rsbuild/package.json b/e2e/react-start/custom-server-rsbuild/package.json index deaa8db5fd..865e424daa 100644 --- a/e2e/react-start/custom-server-rsbuild/package.json +++ b/e2e/react-start/custom-server-rsbuild/package.json @@ -21,7 +21,7 @@ }, "devDependencies": { "@playwright/test": "^1.61.0", - "@rsbuild/core": "^2.0.11", + "@rsbuild/core": "^2.1.0", "@rsbuild/plugin-react": "^2.0.0", "@tailwindcss/postcss": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", diff --git a/e2e/react-start/deferred-hydration/package.json b/e2e/react-start/deferred-hydration/package.json index d8a1e2a38e..fb8a99c8e3 100644 --- a/e2e/react-start/deferred-hydration/package.json +++ b/e2e/react-start/deferred-hydration/package.json @@ -26,7 +26,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@rsbuild/core": "^2.0.11", + "@rsbuild/core": "^2.1.0", "@rsbuild/plugin-react": "^2.0.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/react-start/hmr/package.json b/e2e/react-start/hmr/package.json index adcf8f9a94..7a6dc23f76 100644 --- a/e2e/react-start/hmr/package.json +++ b/e2e/react-start/hmr/package.json @@ -21,7 +21,7 @@ }, "devDependencies": { "@playwright/test": "^1.61.0", - "@rsbuild/core": "^2.0.11", + "@rsbuild/core": "^2.1.0", "@rsbuild/plugin-react": "^2.0.0", "@tailwindcss/postcss": "^4.2.2", "@tailwindcss/vite": "^4.2.2", diff --git a/e2e/react-start/import-protection/package.json b/e2e/react-start/import-protection/package.json index b84d458333..2d3010470e 100644 --- a/e2e/react-start/import-protection/package.json +++ b/e2e/react-start/import-protection/package.json @@ -25,7 +25,7 @@ }, "devDependencies": { "@playwright/test": "^1.61.0", - "@rsbuild/core": "^2.0.11", + "@rsbuild/core": "^2.1.0", "@rsbuild/plugin-react": "^2.0.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/react-start/rsbuild/.gitignore b/e2e/react-start/rsbuild/.gitignore new file mode 100644 index 0000000000..1593a8a0aa --- /dev/null +++ b/e2e/react-start/rsbuild/.gitignore @@ -0,0 +1,21 @@ +node_modules +package-lock.json +yarn.lock + +.DS_Store +.cache +.env +.vercel +.output + +/build/ +/api/ +/server/build +/public/build +/dist/ +/dist-rsbuild-*/ +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ +port-*.txt diff --git a/e2e/react-start/rsbuild/.prettierignore b/e2e/react-start/rsbuild/.prettierignore new file mode 100644 index 0000000000..69750fb197 --- /dev/null +++ b/e2e/react-start/rsbuild/.prettierignore @@ -0,0 +1 @@ +.prettierrc diff --git a/e2e/react-start/rsbuild/package.json b/e2e/react-start/rsbuild/package.json new file mode 100644 index 0000000000..d0b7566489 --- /dev/null +++ b/e2e/react-start/rsbuild/package.json @@ -0,0 +1,40 @@ +{ + "name": "tanstack-react-start-e2e-rsbuild", + "private": true, + "sideEffects": false, + "type": "module", + "scripts": { + "dev": "rsbuild dev", + "dev:e2e": "rsbuild dev --port $PORT", + "build": "rsbuild build && tsc --noEmit", + "start": "node server.js", + "test:e2e-full": "pnpm build && sh -c 'rm -f \"port-${E2E_PORT_KEY:-$npm_package_name}.txt\" \"port-${E2E_PORT_KEY:-$npm_package_name}-syntax-error-overlay.txt\"' && playwright test --project=chromium" + }, + "nx": { + "metadata": { + "playwrightModes": [ + { + "toolchain": "rsbuild", + "mode": "ssr" + } + ] + } + }, + "dependencies": { + "@tanstack/react-router": "workspace:^", + "@tanstack/react-start": "workspace:^", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@playwright/test": "^1.61.0", + "@rsbuild/core": "^2.1.0", + "@rsbuild/plugin-react": "^2.0.0", + "@tanstack/router-e2e-utils": "workspace:^", + "@types/node": "^22.10.2", + "@types/react": "^19.0.8", + "@types/react-dom": "^19.0.3", + "srvx": "^0.10.0", + "typescript": "^5.7.2" + } +} diff --git a/e2e/react-start/rsbuild/playwright.config.ts b/e2e/react-start/rsbuild/playwright.config.ts new file mode 100644 index 0000000000..6e8f176361 --- /dev/null +++ b/e2e/react-start/rsbuild/playwright.config.ts @@ -0,0 +1,29 @@ +import fs from 'node:fs' +import { defineConfig, devices } from '@playwright/test' +import packageJson from './package.json' with { type: 'json' } + +const e2ePortKey = process.env.E2E_PORT_KEY ?? packageJson.name + +if (process.env.TEST_WORKER_INDEX === undefined) { + for (const portFile of [ + `port-${e2ePortKey}.txt`, + `port-${e2ePortKey}-syntax-error-overlay.txt`, + ]) { + fs.rmSync(portFile, { force: true }) + } +} + +export default defineConfig({ + testDir: './tests', + workers: 1, + reporter: [['line']], + + projects: [ + { + name: 'chromium', + use: { + ...devices['Desktop Chrome'], + }, + }, + ], +}) diff --git a/e2e/react-start/rsbuild/rsbuild.config.ts b/e2e/react-start/rsbuild/rsbuild.config.ts new file mode 100644 index 0000000000..5f8b31dc0b --- /dev/null +++ b/e2e/react-start/rsbuild/rsbuild.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from '@rsbuild/core' +import { pluginReact } from '@rsbuild/plugin-react' +import { tanstackStart } from '@tanstack/react-start/plugin/rsbuild' + +const outDir = process.env.E2E_DIST_DIR ?? 'dist' + +export default defineConfig({ + plugins: [pluginReact({ splitChunks: false }), tanstackStart()], + output: { + distPath: { + root: outDir, + }, + }, +}) diff --git a/e2e/react-start/rsbuild/server.js b/e2e/react-start/rsbuild/server.js new file mode 100644 index 0000000000..f3c49399e6 --- /dev/null +++ b/e2e/react-start/rsbuild/server.js @@ -0,0 +1,62 @@ +import fs from 'node:fs' +import path from 'node:path' +import { spawn } from 'node:child_process' +import { pathToFileURL } from 'node:url' + +const distDir = process.env.E2E_DIST_DIR || 'dist' + +function resolveDistClientDir() { + return path.resolve(distDir, 'client') +} + +function resolveDistServerEntryPath() { + const serverJsPath = path.resolve(distDir, 'server', 'server.js') + if (fs.existsSync(serverJsPath)) { + return serverJsPath + } + + const indexJsPath = path.resolve(distDir, 'server', 'index.js') + if (fs.existsSync(indexJsPath)) { + return indexJsPath + } + + return serverJsPath +} + +export function resolveStartCommand() { + const distClientDir = resolveDistClientDir() + const distServerEntryPath = resolveDistServerEntryPath() + return `srvx --prod -s ${JSON.stringify(distClientDir)} ${JSON.stringify(distServerEntryPath)}` +} + +export function start() { + const child = spawn( + 'srvx', + ['--prod', '-s', resolveDistClientDir(), resolveDistServerEntryPath()], + { + stdio: 'inherit', + shell: process.platform === 'win32', + }, + ) + + child.on('error', (error) => { + console.error('Failed to start srvx:', error) + process.exit(1) + }) + + child.on('exit', (code, signal) => { + if (signal) { + process.kill(process.pid, signal) + return + } + + process.exit(code ?? 0) + }) +} + +if ( + process.argv[1] && + import.meta.url === pathToFileURL(process.argv[1]).href +) { + start() +} diff --git a/e2e/react-start/rsbuild/src/routeTree.gen.ts b/e2e/react-start/rsbuild/src/routeTree.gen.ts new file mode 100644 index 0000000000..dceedffdc1 --- /dev/null +++ b/e2e/react-start/rsbuild/src/routeTree.gen.ts @@ -0,0 +1,68 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// 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 IndexRouteImport } from './routes/index' + +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' + fileRoutesByTo: FileRoutesByTo + to: '/' + id: '__root__' | '/' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + } +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() + +import type { getRouter } from './router.tsx' +import type { createStart } from '@tanstack/react-start' +declare module '@tanstack/react-start' { + interface Register { + ssr: true + router: Awaited> + } +} diff --git a/e2e/react-start/rsbuild/src/router.tsx b/e2e/react-start/rsbuild/src/router.tsx new file mode 100644 index 0000000000..e59e199b48 --- /dev/null +++ b/e2e/react-start/rsbuild/src/router.tsx @@ -0,0 +1,10 @@ +import { createRouter } from '@tanstack/react-router' +import { routeTree } from './routeTree.gen' + +export function getRouter() { + return createRouter({ + routeTree, + scrollRestoration: true, + defaultPreload: 'intent', + }) +} diff --git a/e2e/react-start/rsbuild/src/routes/__root.tsx b/e2e/react-start/rsbuild/src/routes/__root.tsx new file mode 100644 index 0000000000..c73ca2a11d --- /dev/null +++ b/e2e/react-start/rsbuild/src/routes/__root.tsx @@ -0,0 +1,37 @@ +import { + HeadContent, + Outlet, + Scripts, + createRootRoute, + useHydrated, +} from '@tanstack/react-router' + +export const Route = createRootRoute({ + head: () => ({ + meta: [ + { charSet: 'utf-8' }, + { name: 'viewport', content: 'width=device-width, initial-scale=1' }, + { title: 'Rsbuild test' }, + ], + }), + component: RootComponent, +}) + +function RootComponent() { + const hydrated = useHydrated() + + return ( + + + + + + + {hydrated ? 'hydrated' : 'hydrating'} + + + + + + ) +} diff --git a/e2e/react-start/rsbuild/src/routes/index.tsx b/e2e/react-start/rsbuild/src/routes/index.tsx new file mode 100644 index 0000000000..4b8cede36e --- /dev/null +++ b/e2e/react-start/rsbuild/src/routes/index.tsx @@ -0,0 +1,14 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/')({ + component: Home, +}) + +function Home() { + return ( +
+

Rsbuild fixture

+

Error overlay fixture

+
+ ) +} diff --git a/e2e/react-start/rsbuild/tests/error-overlay.spec.ts b/e2e/react-start/rsbuild/tests/error-overlay.spec.ts new file mode 100644 index 0000000000..5d6497f740 --- /dev/null +++ b/e2e/react-start/rsbuild/tests/error-overlay.spec.ts @@ -0,0 +1,401 @@ +import fs from 'node:fs' +import path from 'node:path' +import { spawn } from 'node:child_process' +import { expect } from '@playwright/test' +import { getTestServerPort, test } from '@tanstack/router-e2e-utils' +import packageJson from '../package.json' with { type: 'json' } +import type { Page } from '@playwright/test' + +const e2ePortKey = process.env.E2E_PORT_KEY ?? packageJson.name +const fixtureRoot = path.resolve(import.meta.dirname, '..') +const workspaceRoot = path.resolve(import.meta.dirname, '../../../..') +const errorOverlaySelector = 'rsbuild-error-overlay' +const errorOverlayTimeout = 30_000 +const syntaxErrorRoutePatch = '\nexport const broken = ;\n' +const devServerTimeout = 30_000 + +class DevServer { + readonly url: string + private stopPromise: Promise | null = null + private startupError: Error | null = null + private readonly startupErrorPromise: Promise + + private constructor( + readonly port: number, + private readonly childProcess: ReturnType, + ) { + this.url = `http://localhost:${port}` + this.startupErrorPromise = new Promise((_, reject) => { + this.childProcess.once('error', (error) => { + this.startupError = error + reject(this.createStartupError(error)) + }) + }) + this.startupErrorPromise.catch(() => {}) + } + + static async start(port: number): Promise { + const devServer = new DevServer( + port, + spawn('pnpm', ['exec', 'rsbuild', 'dev', '--port', String(port)], { + cwd: fixtureRoot, + env: { + ...process.env, + PORT: String(port), + VITE_SERVER_PORT: String(port), + VITE_NODE_ENV: 'test', + }, + stdio: 'ignore', + }), + ) + + try { + await devServer.waitUntilReady() + return devServer + } catch (error) { + await devServer.stop() + throw error + } + } + + async waitUntilReady(timeoutMs = devServerTimeout): Promise { + const startedAt = Date.now() + + while (Date.now() - startedAt <= timeoutMs) { + this.throwIfStartupFailed() + + if (this.childProcess.exitCode !== null) { + throw new Error( + `Dev server exited before responding: code=${this.childProcess.exitCode} signal=${this.childProcess.signalCode}`, + ) + } + + try { + const res = await this.awaitWithStartupFailure( + fetch(this.url, { + signal: AbortSignal.timeout(1000), + }), + ) + if (res.ok) { + return + } + } catch { + this.throwIfStartupFailed() + // ignore + } + + await this.awaitWithStartupFailure(this.delay(200)) + } + + throw new Error(`Timed out waiting for ${this.url}`) + } + + private async awaitWithStartupFailure(promise: Promise): Promise { + this.throwIfStartupFailed() + return await Promise.race([promise, this.startupErrorPromise]) + } + + private throwIfStartupFailed(): void { + if (this.startupError) { + throw this.createStartupError(this.startupError) + } + } + + private createStartupError(error: Error): Error { + return new Error('Dev server failed to start', { + cause: error, + }) + } + + private async delay(ms: number): Promise { + await new Promise((resolve) => setTimeout(resolve, ms)) + } + + async stop(): Promise { + if (this.childProcess.exitCode !== null || this.childProcess.killed) { + return + } + + this.stopPromise ??= new Promise((resolve) => { + let resolved = false + let forceKillTimer: ReturnType | null = null + let forceResolveTimer: ReturnType | null = null + + const done = () => { + if (resolved) { + return + } + resolved = true + + if (forceKillTimer) { + clearTimeout(forceKillTimer) + } + if (forceResolveTimer) { + clearTimeout(forceResolveTimer) + } + + this.childProcess.off('exit', done) + this.childProcess.off('error', done) + resolve() + } + + this.childProcess.once('exit', done) + this.childProcess.once('error', done) + + try { + this.childProcess.kill('SIGTERM') + } catch { + done() + return + } + + forceKillTimer = setTimeout(() => { + try { + this.childProcess.kill('SIGKILL') + } catch { + // ignore + } + + forceResolveTimer = setTimeout(done, 500) + }, 3000) + }) + + await this.stopPromise + } + + async [Symbol.asyncDispose](): Promise { + await this.stop() + } +} + +type ErrorOverlayIssue = { + title: string | null + description: string | null + runtime: 'client' | 'server' | null + sourceFrame: string | null + importTrace: Array +} + +const runtimeTraceEntries = [ + ['/default-entry/client.tsx', 'client'], + ['/default-entry/server.ts', 'server'], +] as const + +function normalizeErrorOverlayText(value: string): string { + return value + .replaceAll(fixtureRoot, '') + .replaceAll(workspaceRoot, '') + .replace( + /from [^)]+@rsbuild\/core\/dist\/transformLoader\.mjs/g, + 'from ', + ) + .replaceAll('\u00d7', 'x') + .replaceAll('\u2570\u2500\u25b6', '->') + .replaceAll('\u2026', '...') + .replaceAll('\u2192', '->') + .replace(/[ \t]+\n/g, '\n') + .replace(/\n{3,}/g, '\n\n') + .trim() +} + +async function readErrorOverlay(page: Page): Promise<{ + title: string | null + content: string +}> { + const snapshot = await page + .locator(errorOverlaySelector) + .evaluate((element) => { + const root = (element as HTMLElement).shadowRoot ?? element + const titleElement = root.querySelector('.title') + const contentElement = root.querySelector('.content') + const titleText = titleElement ? titleElement.textContent : null + const contentText = contentElement + ? contentElement.textContent + : root.textContent + + return { + title: titleText === null ? null : titleText.trim(), + content: contentText, + } + }) + + return { + title: snapshot.title, + content: normalizeErrorOverlayText(snapshot.content), + } +} + +function getErrorRuntime(block: string): ErrorOverlayIssue['runtime'] { + return ( + runtimeTraceEntries.find(([traceEntry]) => + block.includes(traceEntry), + )?.[1] ?? null + ) +} + +function findTrimmedLine(lines: Array, text: string): string | null { + return lines.find((line) => line.includes(text))?.trim() ?? null +} + +function getSyntaxErrorDescription(lines: Array): string | null { + return ( + findTrimmedLine(lines, 'SyntaxError:') + ?.replace(/^.*SyntaxError:/, 'SyntaxError:') + .trim() ?? null + ) +} + +function getSourceFrame(lines: Array): string | null { + const sourceFrame = [ + lines[0]?.trim(), + findTrimmedLine(lines, 'Module build failed'), + findTrimmedLine(lines, 'SyntaxError:'), + ].filter(Boolean) + + return sourceFrame.length > 0 ? sourceFrame.join('\n') : null +} + +function getImportTrace(lines: Array): Array { + const traceStartIndex = lines.findIndex((line) => + line.startsWith('Import traces'), + ) + + if (traceStartIndex === -1) { + return [] + } + + return lines + .slice(traceStartIndex + 1) + .map((line) => line.trim()) + .filter(Boolean) +} + +function parseErrorOverlayIssue( + block: string, + title: string | null, +): ErrorOverlayIssue { + const lines = block.split('\n') + + return { + title, + description: getSyntaxErrorDescription(lines), + runtime: getErrorRuntime(block), + sourceFrame: getSourceFrame(lines), + importTrace: getImportTrace(lines), + } +} + +async function captureErrorOverlaySnapshot( + page: Page, +): Promise> { + const { title, content } = await readErrorOverlay(page) + const blocks = content.split(/\n(?=File: )/).filter(Boolean) + + return blocks.map((block) => parseErrorOverlayIssue(block, title)) +} + +function formatErrorOverlaySnapshot( + snapshot: Array, +): string { + return `${JSON.stringify(snapshot, null, 2)}\n` +} + +function dedentSnapshot(snapshot: string): string { + const lines = snapshot + .replace(/^\n/, '') + .replace(/\n\s*$/, '') + .split('\n') + const indent = Math.min( + ...lines + .filter((line) => line.trim() !== '') + .map((line) => line.match(/^ */)?.[0].length ?? 0), + ) + + return `${lines.map((line) => line.slice(indent)).join('\n')}\n` +} + +async function toDisplayRedbox( + page: Page, + expectedSnapshot: string, +): Promise { + await expect(page.locator(errorOverlaySelector)).toBeAttached({ + timeout: errorOverlayTimeout, + }) + + const normalizedExpectedSnapshot = dedentSnapshot(expectedSnapshot) + + await expect + .poll( + async () => { + return formatErrorOverlaySnapshot( + await captureErrorOverlaySnapshot(page), + ) + }, + { timeout: errorOverlayTimeout }, + ) + .toBe(normalizedExpectedSnapshot) +} + +test.use({ + whitelistErrors: [ + 'Build failed', + 'Module build failed', + 'SyntaxError', + 'Syntax Error', + 'Expression expected', + ], +}) + +test('shows a module syntax error through the error overlay', async ({ + page, +}) => { + const homeRouteFile = path.resolve(fixtureRoot, 'src/routes/index.tsx') + const originalSource = await fs.promises.readFile(homeRouteFile, 'utf-8') + const port = await getTestServerPort(`${e2ePortKey}-syntax-error-overlay`) + await using devServer = await DevServer.start(port) + + try { + await page.goto(devServer.url, { waitUntil: 'domcontentloaded' }) + + await fs.promises.writeFile( + homeRouteFile, + `${originalSource}${syntaxErrorRoutePatch}`, + ) + + await toDisplayRedbox( + page, + ` + [ + { + "title": "Build failed", + "description": "SyntaxError: Unexpected token (16:22)", + "runtime": "client", + "sourceFrame": "File: ./src/routes/index.tsx:1:1\\nx Module build failed (from ):\\n-> x SyntaxError: Unexpected token (16:22)", + "importTrace": [ + "../../../packages/react-start/dist/plugin/default-entry/client.tsx", + "../../../packages/react-start/dist/esm/client.js", + "... (5 hidden)", + "./src/routeTree.gen.ts", + "./src/routes/index.tsx x" + ] + }, + { + "title": "Build failed", + "description": "SyntaxError: Unexpected token (16:22)", + "runtime": "server", + "sourceFrame": "File: ./src/routes/index.tsx:1:1\\nx Module build failed (from ):\\n-> x SyntaxError: Unexpected token (16:22)", + "importTrace": [ + "../../../packages/react-start/dist/plugin/default-entry/server.ts", + "../../../packages/react-start/dist/esm/server.js", + "... (4 hidden)", + "./src/routeTree.gen.ts", + "./src/routes/index.tsx x" + ] + } + ] + `, + ) + } finally { + await fs.promises.writeFile(homeRouteFile, originalSource) + } +}) diff --git a/e2e/react-start/rsbuild/tsconfig.json b/e2e/react-start/rsbuild/tsconfig.json new file mode 100644 index 0000000000..cef9369516 --- /dev/null +++ b/e2e/react-start/rsbuild/tsconfig.json @@ -0,0 +1,21 @@ +{ + "include": ["**/*.ts", "**/*.tsx"], + "compilerOptions": { + "strict": true, + "esModuleInterop": true, + "jsx": "react-jsx", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "isolatedModules": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "target": "ES2022", + "allowJs": true, + "forceConsistentCasingInFileNames": true, + "paths": { + "~/*": ["./src/*"] + }, + "noEmit": true + } +} diff --git a/e2e/react-start/rsc-rsbuild/package.json b/e2e/react-start/rsc-rsbuild/package.json index 992ae65684..e3800f291a 100644 --- a/e2e/react-start/rsc-rsbuild/package.json +++ b/e2e/react-start/rsc-rsbuild/package.json @@ -28,7 +28,7 @@ }, "devDependencies": { "@playwright/test": "^1.61.0", - "@rsbuild/core": "^2.0.8", + "@rsbuild/core": "^2.1.0", "@rsbuild/plugin-react": "^2.0.0", "@tanstack/router-e2e-utils": "workspace:^", "@types/node": "^22.10.2", diff --git a/e2e/react-start/rsc/package.json b/e2e/react-start/rsc/package.json index c02d940835..0e7414e2aa 100644 --- a/e2e/react-start/rsc/package.json +++ b/e2e/react-start/rsc/package.json @@ -40,7 +40,7 @@ }, "devDependencies": { "@playwright/test": "^1.61.0", - "@rsbuild/core": "^2.0.11", + "@rsbuild/core": "^2.1.0", "@rsbuild/plugin-react": "^2.0.0", "@tanstack/eslint-plugin-start": "workspace:^", "@tanstack/router-e2e-utils": "workspace:^", diff --git a/e2e/react-start/server-functions/package.json b/e2e/react-start/server-functions/package.json index 5df9557f77..7c8d4f10ca 100644 --- a/e2e/react-start/server-functions/package.json +++ b/e2e/react-start/server-functions/package.json @@ -31,7 +31,7 @@ }, "devDependencies": { "@playwright/test": "^1.61.0", - "@rsbuild/core": "^2.0.11", + "@rsbuild/core": "^2.1.0", "@rsbuild/plugin-react": "^2.0.0", "@tailwindcss/postcss": "^4.2.2", "@tailwindcss/vite": "^4.2.2", diff --git a/e2e/solid-router/rspack-basic-file-based/package.json b/e2e/solid-router/rspack-basic-file-based/package.json index 45530b7b7b..5750c2519e 100644 --- a/e2e/solid-router/rspack-basic-file-based/package.json +++ b/e2e/solid-router/rspack-basic-file-based/package.json @@ -17,7 +17,7 @@ }, "devDependencies": { "@playwright/test": "^1.61.0", - "@rsbuild/core": "^2.0.11", + "@rsbuild/core": "^2.1.0", "@rsbuild/plugin-babel": "^1.1.2", "@rsbuild/plugin-solid": "^1.1.1", "@tailwindcss/postcss": "^4.2.2", diff --git a/e2e/solid-router/rspack-basic-virtual-named-export-config-file-based/package.json b/e2e/solid-router/rspack-basic-virtual-named-export-config-file-based/package.json index 3f410649e2..50d3e1d415 100644 --- a/e2e/solid-router/rspack-basic-virtual-named-export-config-file-based/package.json +++ b/e2e/solid-router/rspack-basic-virtual-named-export-config-file-based/package.json @@ -17,7 +17,7 @@ }, "devDependencies": { "@playwright/test": "^1.61.0", - "@rsbuild/core": "^2.0.11", + "@rsbuild/core": "^2.1.0", "@rsbuild/plugin-babel": "^1.1.2", "@rsbuild/plugin-solid": "^1.1.1", "@tailwindcss/postcss": "^4.2.2", diff --git a/e2e/solid-start/basic/package.json b/e2e/solid-start/basic/package.json index 528f2e028d..dfe9b7d58a 100644 --- a/e2e/solid-start/basic/package.json +++ b/e2e/solid-start/basic/package.json @@ -27,7 +27,7 @@ }, "devDependencies": { "@playwright/test": "^1.61.0", - "@rsbuild/core": "^2.0.11", + "@rsbuild/core": "^2.1.0", "@rsbuild/plugin-babel": "^1.1.2", "@rsbuild/plugin-solid": "^1.1.1", "@tailwindcss/postcss": "^4.2.2", diff --git a/e2e/solid-start/deferred-hydration/package.json b/e2e/solid-start/deferred-hydration/package.json index 5d1a2dd5e0..0b272e838a 100644 --- a/e2e/solid-start/deferred-hydration/package.json +++ b/e2e/solid-start/deferred-hydration/package.json @@ -23,7 +23,7 @@ "solid-js": "^1.9.10" }, "devDependencies": { - "@rsbuild/core": "^2.0.11", + "@rsbuild/core": "^2.1.0", "@rsbuild/plugin-babel": "^1.1.2", "@rsbuild/plugin-solid": "^1.1.1", "@tanstack/router-e2e-utils": "workspace:^", diff --git a/e2e/vue-router/rspack-basic-file-based/package.json b/e2e/vue-router/rspack-basic-file-based/package.json index 4a97239304..d80486bc1a 100644 --- a/e2e/vue-router/rspack-basic-file-based/package.json +++ b/e2e/vue-router/rspack-basic-file-based/package.json @@ -17,7 +17,7 @@ }, "devDependencies": { "@playwright/test": "^1.61.0", - "@rsbuild/core": "^2.0.11", + "@rsbuild/core": "^2.1.0", "@rsbuild/plugin-babel": "^1.1.2", "@rsbuild/plugin-vue": "^1.2.7", "@rsbuild/plugin-vue-jsx": "^2.0.0", diff --git a/e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/package.json b/e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/package.json index bbd4a78409..17d8fd711e 100644 --- a/e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/package.json +++ b/e2e/vue-router/rspack-basic-virtual-named-export-config-file-based/package.json @@ -17,7 +17,7 @@ }, "devDependencies": { "@playwright/test": "^1.61.0", - "@rsbuild/core": "^2.0.11", + "@rsbuild/core": "^2.1.0", "@rsbuild/plugin-babel": "^1.1.2", "@rsbuild/plugin-vue": "^1.2.7", "@rsbuild/plugin-vue-jsx": "^2.0.0", diff --git a/e2e/vue-start/basic/package.json b/e2e/vue-start/basic/package.json index 6a6c9c64d7..173ab3175b 100644 --- a/e2e/vue-start/basic/package.json +++ b/e2e/vue-start/basic/package.json @@ -27,10 +27,10 @@ }, "devDependencies": { "@playwright/test": "^1.61.0", - "@rsbuild/core": "^2.0.11", - "@rsbuild/plugin-babel": "^1.0.5", - "@rsbuild/plugin-vue": "^1.2.2", - "@rsbuild/plugin-vue-jsx": "^1.1.1", + "@rsbuild/core": "^2.1.0", + "@rsbuild/plugin-babel": "^1.1.2", + "@rsbuild/plugin-vue": "^1.2.7", + "@rsbuild/plugin-vue-jsx": "^2.0.0", "@tailwindcss/postcss": "^4.2.2", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", diff --git a/examples/react/quickstart-rspack-file-based/package.json b/examples/react/quickstart-rspack-file-based/package.json index 112f155583..5c6793dda5 100644 --- a/examples/react/quickstart-rspack-file-based/package.json +++ b/examples/react/quickstart-rspack-file-based/package.json @@ -17,7 +17,7 @@ "tailwindcss": "^4.2.2" }, "devDependencies": { - "@rsbuild/core": "^2.0.11", + "@rsbuild/core": "^2.1.0", "@rsbuild/plugin-react": "^2.0.0", "@tanstack/router-plugin": "^1.168.23", "@types/react": "^19.0.8", diff --git a/examples/solid/quickstart-rspack-file-based/package.json b/examples/solid/quickstart-rspack-file-based/package.json index 24dc52f1a2..149edaad33 100644 --- a/examples/solid/quickstart-rspack-file-based/package.json +++ b/examples/solid/quickstart-rspack-file-based/package.json @@ -16,7 +16,7 @@ "tailwindcss": "^4.2.2" }, "devDependencies": { - "@rsbuild/core": "^2.0.11", + "@rsbuild/core": "^2.1.0", "@rsbuild/plugin-babel": "^1.1.2", "@rsbuild/plugin-solid": "^1.1.1", "@tanstack/router-plugin": "^1.168.23", diff --git a/packages/react-start/package.json b/packages/react-start/package.json index 954c64597e..7a282aff33 100644 --- a/packages/react-start/package.json +++ b/packages/react-start/package.json @@ -188,7 +188,7 @@ } }, "devDependencies": { - "@rsbuild/core": "^2.0.11", + "@rsbuild/core": "^2.1.0", "@types/node": ">=20" } } diff --git a/packages/router-plugin/package.json b/packages/router-plugin/package.json index 825eaa98e5..c92015ef18 100644 --- a/packages/router-plugin/package.json +++ b/packages/router-plugin/package.json @@ -128,6 +128,7 @@ "zod": "^4.4.3" }, "devDependencies": { + "@rsbuild/core": "^2.1.0", "@types/babel__core": "^7.20.5", "@types/babel__template": "^7.4.4", "@types/node": ">=20" diff --git a/packages/solid-start/package.json b/packages/solid-start/package.json index 2a8c1dafc2..122def43c4 100644 --- a/packages/solid-start/package.json +++ b/packages/solid-start/package.json @@ -127,7 +127,7 @@ "pathe": "^2.0.3" }, "devDependencies": { - "@rsbuild/core": "^2.0.11", + "@rsbuild/core": "^2.1.0", "@tanstack/router-utils": "workspace:*", "@types/node": ">=20", "vite": "*" diff --git a/packages/start-plugin-core/package.json b/packages/start-plugin-core/package.json index 6a67316f4d..e8a6d5a295 100644 --- a/packages/start-plugin-core/package.json +++ b/packages/start-plugin-core/package.json @@ -107,7 +107,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@rsbuild/core": "^2.0.11", + "@rsbuild/core": "^2.1.0", "@types/babel__code-frame": "^7.0.6", "@types/babel__core": "^7.20.5", "@types/node": ">=20", diff --git a/packages/start-plugin-core/src/rsbuild/import-protection.ts b/packages/start-plugin-core/src/rsbuild/import-protection.ts index e3d63ba04d..d76dbe9aa9 100644 --- a/packages/start-plugin-core/src/rsbuild/import-protection.ts +++ b/packages/start-plugin-core/src/rsbuild/import-protection.ts @@ -752,6 +752,11 @@ function buildCompilationGraph(opts: { for (const connection of connections) { if (!connection.module) continue + // Only consider modules that are not errored + if ('error' in connection.module && connection.module.error) { + continue + } + const resolved = getModuleFile(connection.module) const specifier = getConnectionRequest(connection.dependency) diff --git a/packages/vue-start/package.json b/packages/vue-start/package.json index 9c9c59b245..462dfc453f 100644 --- a/packages/vue-start/package.json +++ b/packages/vue-start/package.json @@ -121,7 +121,7 @@ "pathe": "^2.0.3" }, "devDependencies": { - "@rsbuild/core": "^2.0.11", + "@rsbuild/core": "^2.1.0", "@tanstack/router-utils": "workspace:*", "@types/node": ">=20", "@vitejs/plugin-vue-jsx": "^4.1.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index be5b8a29f5..75be091d06 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -219,11 +219,11 @@ importers: version: 3.5.25(@typescript/typescript6@6.0.2) devDependencies: '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@rsbuild/plugin-react': specifier: ^2.0.0 - version: 2.0.0(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23)) + version: 2.0.0(@rsbuild/core@2.1.1)(@rspack/core@2.1.1(@swc/helpers@0.5.23)) '@tanstack/router-plugin': specifier: workspace:* version: link:../../packages/router-plugin @@ -1265,11 +1265,11 @@ importers: specifier: ^1.61.0 version: 1.61.1 '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@rsbuild/plugin-react': specifier: ^2.0.0 - version: 2.0.0(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23)) + version: 2.0.0(@rsbuild/core@2.1.1)(@rspack/core@2.1.1(@swc/helpers@0.5.23)) '@tailwindcss/postcss': specifier: ^4.2.2 version: 4.2.2 @@ -1320,11 +1320,11 @@ importers: specifier: ^1.61.0 version: 1.61.1 '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@rsbuild/plugin-react': specifier: ^2.0.0 - version: 2.0.0(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23)) + version: 2.0.0(@rsbuild/core@2.1.1)(@rspack/core@2.1.1(@swc/helpers@0.5.23)) '@tailwindcss/postcss': specifier: ^4.2.2 version: 4.2.2 @@ -1549,11 +1549,11 @@ importers: specifier: ^1.61.0 version: 1.61.1 '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@rsbuild/plugin-react': specifier: ^2.0.0 - version: 2.0.0(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23)) + version: 2.0.0(@rsbuild/core@2.1.1)(@rspack/core@2.1.1(@swc/helpers@0.5.23)) '@tailwindcss/postcss': specifier: ^4.2.2 version: 4.2.2 @@ -1973,11 +1973,11 @@ importers: specifier: ^1.61.0 version: 1.61.1 '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@rsbuild/plugin-react': specifier: ^2.0.0 - version: 2.0.0(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23)) + version: 2.0.0(@rsbuild/core@2.1.1)(@rspack/core@2.1.1(@swc/helpers@0.5.23)) '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -2044,7 +2044,7 @@ importers: version: 6.0.1(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) nitro: specifier: ^3.0.260311-beta - version: 3.0.260311-beta(@electric-sql/pglite@0.3.2)(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(dotenv@17.4.2)(giget@2.0.0)(jiti@2.7.0)(lru-cache@11.5.1)(miniflare@4.20260317.0)(mysql2@3.15.3)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) + version: 3.0.260311-beta(@electric-sql/pglite@0.3.2)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(dotenv@17.4.2)(giget@2.0.0)(jiti@2.7.0)(lru-cache@11.5.1)(miniflare@4.20260317.0)(mysql2@3.15.3)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) sass: specifier: ^1.97.2 version: 1.97.2 @@ -2153,11 +2153,11 @@ importers: specifier: ^1.61.0 version: 1.61.1 '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@rsbuild/plugin-react': specifier: ^2.0.0 - version: 2.0.0(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23)) + version: 2.0.0(@rsbuild/core@2.1.1)(@rspack/core@2.1.1(@swc/helpers@0.5.23)) '@tailwindcss/postcss': specifier: ^4.2.2 version: 4.2.2 @@ -2211,11 +2211,11 @@ importers: version: 19.2.3(react@19.2.3) devDependencies: '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@rsbuild/plugin-react': specifier: ^2.0.0 - version: 2.0.0(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23)) + version: 2.0.0(@rsbuild/core@2.1.1)(@rspack/core@2.1.1(@swc/helpers@0.5.23)) '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -2285,7 +2285,7 @@ importers: version: 6.0.1(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) nitro: specifier: ^3.0.260311-beta - version: 3.0.260311-beta(@electric-sql/pglite@0.3.2)(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(dotenv@17.4.2)(giget@2.0.0)(jiti@2.7.0)(lru-cache@11.5.1)(miniflare@4.20260317.0)(mysql2@3.15.3)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) + version: 3.0.260311-beta(@electric-sql/pglite@0.3.2)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(dotenv@17.4.2)(giget@2.0.0)(jiti@2.7.0)(lru-cache@11.5.1)(miniflare@4.20260317.0)(mysql2@3.15.3)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) srvx: specifier: ^0.11.9 version: 0.11.12 @@ -2389,7 +2389,7 @@ importers: version: 9.2.1 nitro: specifier: ^3.0.260311-beta - version: 3.0.260311-beta(@electric-sql/pglite@0.3.2)(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(dotenv@17.4.2)(giget@2.0.0)(jiti@2.7.0)(lru-cache@11.5.1)(miniflare@4.20260317.0)(mysql2@3.15.3)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) + version: 3.0.260311-beta(@electric-sql/pglite@0.3.2)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(dotenv@17.4.2)(giget@2.0.0)(jiti@2.7.0)(lru-cache@11.5.1)(miniflare@4.20260317.0)(mysql2@3.15.3)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) typescript: specifier: npm:@typescript/typescript6@^6.0.2 version: '@typescript/typescript6@6.0.2' @@ -2416,11 +2416,11 @@ importers: specifier: ^1.61.0 version: 1.61.1 '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@rsbuild/plugin-react': specifier: ^2.0.0 - version: 2.0.0(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23)) + version: 2.0.0(@rsbuild/core@2.1.1)(@rspack/core@2.1.1(@swc/helpers@0.5.23)) '@tailwindcss/postcss': specifier: ^4.2.2 version: 4.2.2 @@ -2532,11 +2532,11 @@ importers: specifier: ^1.61.0 version: 1.61.1 '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@rsbuild/plugin-react': specifier: ^2.0.0 - version: 2.0.0(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23)) + version: 2.0.0(@rsbuild/core@2.1.1)(@rspack/core@2.1.1(@swc/helpers@0.5.23)) '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -2678,6 +2678,49 @@ importers: specifier: ^8.0.14 version: 8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0) + e2e/react-start/rsbuild: + dependencies: + '@tanstack/react-router': + specifier: workspace:* + version: link:../../../packages/react-router + '@tanstack/react-start': + specifier: workspace:* + version: link:../../../packages/react-start + react: + specifier: ^19.2.3 + version: 19.2.3 + react-dom: + specifier: ^19.2.3 + version: 19.2.3(react@19.2.3) + devDependencies: + '@playwright/test': + specifier: ^1.61.0 + version: 1.61.1 + '@rsbuild/core': + specifier: ^2.1.0 + version: 2.1.1 + '@rsbuild/plugin-react': + specifier: ^2.0.0 + version: 2.0.0(@rsbuild/core@2.1.1)(@rspack/core@2.1.1(@swc/helpers@0.5.23)) + '@tanstack/router-e2e-utils': + specifier: workspace:^ + version: link:../../e2e-utils + '@types/node': + specifier: 25.0.9 + version: 25.0.9 + '@types/react': + specifier: ^19.2.8 + version: 19.2.9 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.9) + srvx: + specifier: ^0.10.0 + version: 0.10.1 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + e2e/react-start/rsc: dependencies: '@tanstack/react-router': @@ -2700,11 +2743,11 @@ importers: specifier: ^1.61.0 version: 1.61.1 '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@rsbuild/plugin-react': specifier: ^2.0.0 - version: 2.0.0(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23)) + version: 2.0.0(@rsbuild/core@2.1.1)(@rspack/core@2.1.1(@swc/helpers@0.5.23)) '@tanstack/eslint-plugin-start': specifier: workspace:^ version: link:../../../packages/eslint-plugin-start @@ -2868,11 +2911,11 @@ importers: specifier: ^1.61.0 version: 1.61.1 '@rsbuild/core': - specifier: ^2.0.8 - version: 2.0.8 + specifier: ^2.1.0 + version: 2.1.1 '@rsbuild/plugin-react': specifier: ^2.0.0 - version: 2.0.0(@rsbuild/core@2.0.8)(@rspack/core@2.0.8(@swc/helpers@0.5.23)) + version: 2.0.0(@rsbuild/core@2.1.1)(@rspack/core@2.1.1(@swc/helpers@0.5.23)) '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -3109,11 +3152,11 @@ importers: specifier: ^1.61.0 version: 1.61.1 '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@rsbuild/plugin-react': specifier: ^2.0.0 - version: 2.0.0(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23)) + version: 2.0.0(@rsbuild/core@2.1.1)(@rspack/core@2.1.1(@swc/helpers@0.5.23)) '@tailwindcss/postcss': specifier: ^4.2.2 version: 4.2.2 @@ -3473,7 +3516,7 @@ importers: version: 6.0.1(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) nitro: specifier: ^3.0.260311-beta - version: 3.0.260311-beta(@electric-sql/pglite@0.3.2)(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(dotenv@17.4.2)(giget@2.0.0)(jiti@2.7.0)(lru-cache@11.5.1)(miniflare@4.20260317.0)(mysql2@3.15.3)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) + version: 3.0.260311-beta(@electric-sql/pglite@0.3.2)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(dotenv@17.4.2)(giget@2.0.0)(jiti@2.7.0)(lru-cache@11.5.1)(miniflare@4.20260317.0)(mysql2@3.15.3)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) typescript: specifier: npm:@typescript/typescript6@^6.0.2 version: '@typescript/typescript6@6.0.2' @@ -3494,7 +3537,7 @@ importers: version: link:../../../packages/start-static-server-functions nitro: specifier: ^3.0.1-alpha.2 - version: 3.0.1-alpha.2(@electric-sql/pglite@0.3.2)(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(ioredis@5.9.2)(lru-cache@11.5.1)(mysql2@3.15.3)(rolldown@1.0.2)(rollup@4.56.0)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) + version: 3.0.1-alpha.2(@electric-sql/pglite@0.3.2)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(ioredis@5.9.2)(lru-cache@11.5.1)(mysql2@3.15.3)(rolldown@1.0.2)(rollup@4.56.0)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) react: specifier: ^19.2.3 version: 19.2.3 @@ -4243,14 +4286,14 @@ importers: specifier: ^1.61.0 version: 1.61.1 '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@rsbuild/plugin-babel': specifier: ^1.1.2 - version: 1.1.2(@rsbuild/core@2.0.14) + version: 1.1.2(@rsbuild/core@2.1.1) '@rsbuild/plugin-solid': specifier: ^1.1.1 - version: 1.1.1(@babel/core@7.29.0)(@rsbuild/core@2.0.14)(solid-js@1.9.12) + version: 1.1.1(@babel/core@7.29.0)(@rsbuild/core@2.1.1)(solid-js@1.9.12) '@tailwindcss/postcss': specifier: ^4.2.2 version: 4.2.2 @@ -4292,14 +4335,14 @@ importers: specifier: ^1.61.0 version: 1.61.1 '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@rsbuild/plugin-babel': specifier: ^1.1.2 - version: 1.1.2(@rsbuild/core@2.0.14) + version: 1.1.2(@rsbuild/core@2.1.1) '@rsbuild/plugin-solid': specifier: ^1.1.1 - version: 1.1.1(@babel/core@7.29.0)(@rsbuild/core@2.0.14)(solid-js@1.9.12) + version: 1.1.1(@babel/core@7.29.0)(@rsbuild/core@2.1.1)(solid-js@1.9.12) '@tailwindcss/postcss': specifier: ^4.2.2 version: 4.2.2 @@ -4491,14 +4534,14 @@ importers: specifier: ^1.61.0 version: 1.61.1 '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@rsbuild/plugin-babel': specifier: ^1.1.2 - version: 1.1.2(@rsbuild/core@2.0.14) + version: 1.1.2(@rsbuild/core@2.1.1) '@rsbuild/plugin-solid': specifier: ^1.1.1 - version: 1.1.1(@babel/core@7.29.0)(@rsbuild/core@2.0.14)(solid-js@1.9.12) + version: 1.1.1(@babel/core@7.29.0)(@rsbuild/core@2.1.1)(solid-js@1.9.12) '@tailwindcss/postcss': specifier: ^4.2.2 version: 4.2.2 @@ -4904,14 +4947,14 @@ importers: version: 1.9.12 devDependencies: '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@rsbuild/plugin-babel': specifier: ^1.1.2 - version: 1.1.2(@rsbuild/core@2.0.14) + version: 1.1.2(@rsbuild/core@2.1.1) '@rsbuild/plugin-solid': specifier: ^1.1.1 - version: 1.1.1(@babel/core@7.29.0)(@rsbuild/core@2.0.14)(solid-js@1.9.12) + version: 1.1.1(@babel/core@7.29.0)(@rsbuild/core@2.1.1)(solid-js@1.9.12) '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils @@ -6143,17 +6186,17 @@ importers: specifier: ^1.61.0 version: 1.61.1 '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@rsbuild/plugin-babel': specifier: ^1.1.2 - version: 1.1.2(@rsbuild/core@2.0.14) + version: 1.1.2(@rsbuild/core@2.1.1) '@rsbuild/plugin-vue': specifier: ^1.2.7 - version: 1.2.7(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23))(vue@3.5.25(@typescript/typescript6@6.0.2)) + version: 1.2.7(@rsbuild/core@2.1.1)(@rspack/core@2.1.1(@swc/helpers@0.5.23))(vue@3.5.25(@typescript/typescript6@6.0.2)) '@rsbuild/plugin-vue-jsx': specifier: ^2.0.0 - version: 2.0.0(@babel/core@7.29.0)(@rsbuild/core@2.0.14) + version: 2.0.0(@babel/core@7.29.0)(@rsbuild/core@2.1.1) '@tailwindcss/postcss': specifier: ^4.2.2 version: 4.2.2 @@ -6201,17 +6244,17 @@ importers: specifier: ^1.61.0 version: 1.61.1 '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@rsbuild/plugin-babel': specifier: ^1.1.2 - version: 1.1.2(@rsbuild/core@2.0.14) + version: 1.1.2(@rsbuild/core@2.1.1) '@rsbuild/plugin-vue': specifier: ^1.2.7 - version: 1.2.7(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23))(vue@3.5.25(@typescript/typescript6@6.0.2)) + version: 1.2.7(@rsbuild/core@2.1.1)(@rspack/core@2.1.1(@swc/helpers@0.5.23))(vue@3.5.25(@typescript/typescript6@6.0.2)) '@rsbuild/plugin-vue-jsx': specifier: ^2.0.0 - version: 2.0.0(@babel/core@7.29.0)(@rsbuild/core@2.0.14) + version: 2.0.0(@babel/core@7.29.0)(@rsbuild/core@2.1.1) '@tailwindcss/postcss': specifier: ^4.2.2 version: 4.2.2 @@ -6427,17 +6470,17 @@ importers: specifier: ^1.61.0 version: 1.61.1 '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@rsbuild/plugin-babel': - specifier: ^1.0.5 - version: 1.0.6(@rsbuild/core@2.0.14) + specifier: ^1.1.2 + version: 1.1.2(@rsbuild/core@2.1.1) '@rsbuild/plugin-vue': - specifier: ^1.2.2 - version: 1.2.2(@rsbuild/core@2.0.14)(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4)(vue@3.5.25(@typescript/typescript6@6.0.2)) + specifier: ^1.2.7 + version: 1.2.7(@rsbuild/core@2.1.1)(@rspack/core@2.1.1(@swc/helpers@0.5.23))(vue@3.5.25(@typescript/typescript6@6.0.2)) '@rsbuild/plugin-vue-jsx': - specifier: ^1.1.1 - version: 1.1.1(@babel/core@7.29.0)(@rsbuild/core@2.0.14) + specifier: ^2.0.0 + version: 2.0.0(@babel/core@7.29.0)(@rsbuild/core@2.1.1) '@tailwindcss/postcss': specifier: ^4.2.2 version: 4.2.2 @@ -8623,11 +8666,11 @@ importers: version: 4.2.2 devDependencies: '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@rsbuild/plugin-react': specifier: ^2.0.0 - version: 2.0.0(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23)) + version: 2.0.0(@rsbuild/core@2.1.1)(@rspack/core@2.1.1(@swc/helpers@0.5.23)) '@tanstack/router-plugin': specifier: workspace:* version: link:../../../packages/router-plugin @@ -9461,7 +9504,7 @@ importers: version: 6.0.1(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) nitro: specifier: ^3.0.260311-beta - version: 3.0.260311-beta(@electric-sql/pglite@0.3.2)(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(dotenv@17.4.2)(giget@2.0.0)(jiti@2.7.0)(lru-cache@11.5.1)(miniflare@4.20260317.0)(mysql2@3.15.3)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) + version: 3.0.260311-beta(@electric-sql/pglite@0.3.2)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(dotenv@17.4.2)(giget@2.0.0)(jiti@2.7.0)(lru-cache@11.5.1)(miniflare@4.20260317.0)(mysql2@3.15.3)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) tailwindcss: specifier: ^4.2.2 version: 4.2.2 @@ -9736,10 +9779,10 @@ importers: version: 2.0.14 '@rsbuild/plugin-react': specifier: ^2.0.0 - version: 2.0.0(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23)) + version: 2.0.0(@rsbuild/core@2.0.14)(@rspack/core@2.1.1(@swc/helpers@0.5.23)) '@rsbuild/plugin-tailwindcss': specifier: ^2.0.3 - version: 2.0.3(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23))(webpack@5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4)) + version: 2.0.3(@rsbuild/core@2.0.14)(@rspack/core@2.1.1(@swc/helpers@0.5.23))(webpack@5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4)) '@types/node': specifier: 25.0.9 version: 25.0.9 @@ -10294,7 +10337,7 @@ importers: version: 0.5.30(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) nitro: specifier: ^3.0.260311-beta - version: 3.0.260311-beta(@electric-sql/pglite@0.3.2)(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(dotenv@17.4.2)(giget@2.0.0)(jiti@2.7.0)(lru-cache@11.5.1)(miniflare@4.20260317.0)(mysql2@3.15.3)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) + version: 3.0.260311-beta(@electric-sql/pglite@0.3.2)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(dotenv@17.4.2)(giget@2.0.0)(jiti@2.7.0)(lru-cache@11.5.1)(miniflare@4.20260317.0)(mysql2@3.15.3)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) tailwindcss: specifier: ^4.1.18 version: 4.2.2 @@ -11837,14 +11880,14 @@ importers: version: 4.2.2 devDependencies: '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@rsbuild/plugin-babel': specifier: ^1.1.2 - version: 1.1.2(@rsbuild/core@2.0.14) + version: 1.1.2(@rsbuild/core@2.1.1) '@rsbuild/plugin-solid': specifier: ^1.1.1 - version: 1.1.1(@babel/core@7.29.0)(@rsbuild/core@2.0.14)(solid-js@1.9.12) + version: 1.1.1(@babel/core@7.29.0)(@rsbuild/core@2.1.1)(solid-js@1.9.12) '@tanstack/router-plugin': specifier: workspace:* version: link:../../../packages/router-plugin @@ -12160,7 +12203,7 @@ importers: version: typescript@7.0.2 nitro: specifier: ^3.0.260311-beta - version: 3.0.260311-beta(@electric-sql/pglite@0.3.2)(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(dotenv@17.4.2)(giget@2.0.0)(jiti@2.7.0)(lru-cache@11.5.1)(miniflare@4.20260317.0)(mysql2@3.15.3)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) + version: 3.0.260311-beta(@electric-sql/pglite@0.3.2)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(dotenv@17.4.2)(giget@2.0.0)(jiti@2.7.0)(lru-cache@11.5.1)(miniflare@4.20260317.0)(mysql2@3.15.3)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) tailwindcss: specifier: ^4.2.2 version: 4.2.2 @@ -12387,7 +12430,7 @@ importers: version: typescript@7.0.2 nitro: specifier: ^3.0.260311-beta - version: 3.0.260311-beta(@electric-sql/pglite@0.3.2)(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(dotenv@17.4.2)(giget@2.0.0)(jiti@2.7.0)(lru-cache@11.5.1)(miniflare@4.20260317.0)(mysql2@3.15.3)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) + version: 3.0.260311-beta(@electric-sql/pglite@0.3.2)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(dotenv@17.4.2)(giget@2.0.0)(jiti@2.7.0)(lru-cache@11.5.1)(miniflare@4.20260317.0)(mysql2@3.15.3)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) tailwindcss: specifier: ^4.2.2 version: 4.2.2 @@ -13380,8 +13423,8 @@ importers: version: 8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0) devDependencies: '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@types/node': specifier: 25.0.9 version: 25.0.9 @@ -13648,9 +13691,6 @@ importers: '@babel/types': specifier: ^7.28.5 version: 7.28.5 - '@rsbuild/core': - specifier: '>=1.0.2 || ^2.0.0' - version: 2.0.1 '@tanstack/react-router': specifier: workspace:* version: link:../react-router @@ -13682,6 +13722,9 @@ importers: specifier: ^4.4.3 version: 4.4.3 devDependencies: + '@rsbuild/core': + specifier: ^2.1.0 + version: 2.1.1 '@types/babel__core': specifier: ^7.20.5 version: 7.20.5 @@ -13875,8 +13918,8 @@ importers: version: 1.9.12 devDependencies: '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@tanstack/router-utils': specifier: workspace:* version: link:../router-utils @@ -14032,8 +14075,8 @@ importers: version: 4.4.3 devDependencies: '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@types/babel__code-frame': specifier: ^7.0.6 version: 7.0.6 @@ -14270,8 +14313,8 @@ importers: version: 2.0.3 devDependencies: '@rsbuild/core': - specifier: ^2.0.11 - version: 2.0.14 + specifier: ^2.1.0 + version: 2.1.1 '@tanstack/router-utils': specifier: workspace:* version: link:../router-utils @@ -14586,24 +14629,12 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-proposal-decorators@7.28.0': - resolution: {integrity: sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-proposal-decorators@7.29.0': resolution: {integrity: sha512-CVBVv3VY/XRMxRYq5dwr2DS7/MvqPm23cOCjbwNnVrfOqcWlnefua1uUs0sjdKOGjvPUG633o07uWzJq4oI6dA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-decorators@7.27.1': - resolution: {integrity: sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-decorators@7.28.6': resolution: {integrity: sha512-71EYI0ONURHJBL4rSFXnITXqXrrY8q4P0q006DPfN+Rk+ASM+++IBXem/ruokgBZR8YNEWZ8R6B+rCb8VcUTqA==} engines: {node: '>=6.9.0'} @@ -14628,12 +14659,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-class-properties@7.27.1': - resolution: {integrity: sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-class-properties@7.28.6': resolution: {integrity: sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==} engines: {node: '>=6.9.0'} @@ -15198,12 +15223,18 @@ packages: '@emnapi/core@1.10.0': resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + '@emnapi/core@1.11.1': + resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} + '@emnapi/core@1.4.5': resolution: {integrity: sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==} '@emnapi/runtime@1.10.0': resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + '@emnapi/runtime@1.11.1': + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + '@emnapi/runtime@1.4.5': resolution: {integrity: sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==} @@ -15213,6 +15244,9 @@ packages: '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + '@emotion/babel-plugin@11.13.5': resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} @@ -17097,6 +17131,12 @@ packages: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 + '@napi-rs/wasm-runtime@1.1.5': + resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + '@neon-rs/load@0.0.4': resolution: {integrity: sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==} @@ -19048,16 +19088,6 @@ packages: cpu: [x64] os: [win32] - '@rsbuild/core@2.0.1': - resolution: {integrity: sha512-5TwUpb10Y+VYaYH8oLL/rfJGrhxrk16BiGzv101kzaMPT60MtOXgjEUTxztbjRuq0ifbtRJ/w7rsIZQ4VziWYg==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - core-js: '>= 3.0.0' - peerDependenciesMeta: - core-js: - optional: true - '@rsbuild/core@2.0.14': resolution: {integrity: sha512-SSPer6vM+v82CV6JXIOzyyT++A1ECgsVvvUuveD5rfGxX/W58vWGnB+zWcYbMfYOjTntD+9ve6QGGh6kBjPx6A==} engines: {node: ^20.19.0 || >=22.12.0} @@ -19068,8 +19098,8 @@ packages: core-js: optional: true - '@rsbuild/core@2.0.8': - resolution: {integrity: sha512-V5Bhn3zqljsaB5grw9oMkSk6XZFvMkr6UpIYPZnDmOWsRAT1fNQ6XF6cn8fVUKv/KILblBrKLUZf0DpvDt3exw==} + '@rsbuild/core@2.1.1': + resolution: {integrity: sha512-s6Cc+pHCJK9RP6Yk4LwsQWHxxxasrk5kmo9nkZK7j1iObp4w2PLivsUdP6HtkeoXuxcnK3QKH8/r49plNBjaQw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -19078,11 +19108,6 @@ packages: core-js: optional: true - '@rsbuild/plugin-babel@1.0.6': - resolution: {integrity: sha512-tWnqG938MedKJx7U4F1lHb156VDtNzj7mSsi2ZoxZVBnECQE01/V6QTN1XKw7nWunGyGoETb+nQBGc+fkVZjvw==} - peerDependencies: - '@rsbuild/core': 1.x - '@rsbuild/plugin-babel@1.1.2': resolution: {integrity: sha512-qIZzosQlkj7VyFz8mmz1vAKDFlk79xrIvCRYzROqNwQHDXep+gd91nCnUPJdRPHrktzN8Yqa190CPTii3rON6w==} peerDependencies: @@ -19115,14 +19140,6 @@ packages: '@rsbuild/core': optional: true - '@rsbuild/plugin-vue-jsx@1.1.1': - resolution: {integrity: sha512-6QHm0dmx5cdgpvMXv7bSrTzuDi1uDBw7SaydmN8kQ/uhaxRPWMNMU3g6rGYKhu+6ExHKwtXOf8CI2YtXwDaP9Q==} - peerDependencies: - '@rsbuild/core': 1.x - peerDependenciesMeta: - '@rsbuild/core': - optional: true - '@rsbuild/plugin-vue-jsx@2.0.0': resolution: {integrity: sha512-P0kzXfILMle1oPkB7GfH7VvKFCF7F6P1+9TmxCGEE03ZH7/vxbv15EjEIPFwY3MqSdgrmhIBz08dAOT8T0ffSQ==} peerDependencies: @@ -19131,11 +19148,6 @@ packages: '@rsbuild/core': optional: true - '@rsbuild/plugin-vue@1.2.2': - resolution: {integrity: sha512-BVCejQjVIxsaG0WF3WSBy1jyVjoLjmrWyslSi0eq1zGxLB4BlyEkIvBQguXI9YsxKxKg0VsXIJQjXus03Rzqkg==} - peerDependencies: - '@rsbuild/core': 1.x - '@rsbuild/plugin-vue@1.2.7': resolution: {integrity: sha512-epDSChyeR4U+gwq2vfTARV9hbfrmdFTdmhBWIcv7MFyckn3yxJhweJVwdx14Wf3d8s1p4XZzDpqmphDyvX443Q==} peerDependencies: @@ -19149,13 +19161,13 @@ packages: cpu: [arm64] os: [darwin] - '@rspack/binding-darwin-arm64@2.0.5': - resolution: {integrity: sha512-++wjLQjQ20GcR0DwbzQmVXg9qy4XCX5NlfSzkzj2icHoDxr3KkrXhyVrQkdWuNG6l/bQrGLPnvLEAqkroC2Y7A==} + '@rspack/binding-darwin-arm64@2.0.8': + resolution: {integrity: sha512-vCgbgH7B7qom+uID+RCZsTCOYFb9wC4/4+1U6rMfytrXGVJ72eNQs2tbdjOl0lb18CT3N/n+VkWynUiLk84GwA==} cpu: [arm64] os: [darwin] - '@rspack/binding-darwin-arm64@2.0.8': - resolution: {integrity: sha512-vCgbgH7B7qom+uID+RCZsTCOYFb9wC4/4+1U6rMfytrXGVJ72eNQs2tbdjOl0lb18CT3N/n+VkWynUiLk84GwA==} + '@rspack/binding-darwin-arm64@2.1.1': + resolution: {integrity: sha512-6K8jA3pZphBwLt5DU78wl0IzY1myHrqNSvFVCd9CHa+szlOwv2iMQpZdYdpupIBxwiZ39rrDyQKtoZw7lsAG8w==} cpu: [arm64] os: [darwin] @@ -19164,13 +19176,13 @@ packages: cpu: [x64] os: [darwin] - '@rspack/binding-darwin-x64@2.0.5': - resolution: {integrity: sha512-JBD5mCN3JKjV64Mh9nDYx8lLUrWDfEl5tLBuMkREUnqEKbo+z4nfwotyqHHM8/XgZwL+Gr7ps4GLWuQQrZB8+Q==} + '@rspack/binding-darwin-x64@2.0.8': + resolution: {integrity: sha512-satPm2PD4B7jDTVlVAdvMVdUszwLvWUEnUDzLb77mvVkezKNDZmuhb+e8s+FfKs8hJpNbZ9VAejuA2rr8o985w==} cpu: [x64] os: [darwin] - '@rspack/binding-darwin-x64@2.0.8': - resolution: {integrity: sha512-satPm2PD4B7jDTVlVAdvMVdUszwLvWUEnUDzLb77mvVkezKNDZmuhb+e8s+FfKs8hJpNbZ9VAejuA2rr8o985w==} + '@rspack/binding-darwin-x64@2.1.1': + resolution: {integrity: sha512-RdqtEfIbSe5ucLNVvMac1k88UwmL4Gil8uXqlEFcSZ3VC2jTOkecadd+B0RYqJ+PirSInrRmKm1cr0740zppOA==} cpu: [x64] os: [darwin] @@ -19180,14 +19192,14 @@ packages: os: [linux] libc: [glibc] - '@rspack/binding-linux-arm64-gnu@2.0.5': - resolution: {integrity: sha512-JI8+//woanJPNsfL7iGjX39zyiWumnrKHznWQM/7lEtE5nPmk+j+X7TYXxczSWC9zfZegiqI74D3L5JPDC84Fw==} + '@rspack/binding-linux-arm64-gnu@2.0.8': + resolution: {integrity: sha512-pSI+npPQE/uDtiboqvcOIRJbEV2+B+H1xffmko/gw50la92oTUW60kVULFwsb6L0+GVCzIcwX3yq60GtYIn+Ug==} cpu: [arm64] os: [linux] libc: [glibc] - '@rspack/binding-linux-arm64-gnu@2.0.8': - resolution: {integrity: sha512-pSI+npPQE/uDtiboqvcOIRJbEV2+B+H1xffmko/gw50la92oTUW60kVULFwsb6L0+GVCzIcwX3yq60GtYIn+Ug==} + '@rspack/binding-linux-arm64-gnu@2.1.1': + resolution: {integrity: sha512-ROKtqXoLpbZO1vYEcNxQg6COvRhdlJcoib1Z4pzG1nIyctEAUuFmnD7pIvJiVe6M7YuJNXW+1p0BhpptT2XEUQ==} cpu: [arm64] os: [linux] libc: [glibc] @@ -19198,32 +19210,44 @@ packages: os: [linux] libc: [musl] - '@rspack/binding-linux-arm64-musl@2.0.5': - resolution: {integrity: sha512-5LujilxLtJFRiiPz5i5iWcWJriK9oy4gN7gZtTo8YRB7wwmwA8LMypTjjO0GLbkPS4/KeCfY4fDfTC29KmK+tA==} + '@rspack/binding-linux-arm64-musl@2.0.8': + resolution: {integrity: sha512-igjJ43yxWQ72GZqjDDZSSHax9/Vg+6rLMmOvFglTJUkQpB4Tyvu/YjW+WRjYj2xRw6blOjLxUSJWASvuSqqlvg==} cpu: [arm64] os: [linux] libc: [musl] - '@rspack/binding-linux-arm64-musl@2.0.8': - resolution: {integrity: sha512-igjJ43yxWQ72GZqjDDZSSHax9/Vg+6rLMmOvFglTJUkQpB4Tyvu/YjW+WRjYj2xRw6blOjLxUSJWASvuSqqlvg==} + '@rspack/binding-linux-arm64-musl@2.1.1': + resolution: {integrity: sha512-QdQpG9dK0GfPASjBd/8rUwDNW0ShfFfYJfcoGLNTM6A8EqmgvWwklXDiCVF5dXALbZIksAuRznIKnLDsUoXi9Q==} cpu: [arm64] os: [linux] libc: [musl] + '@rspack/binding-linux-riscv64-gnu@2.1.1': + resolution: {integrity: sha512-LGdYCAvblZp2qtHk9UseYftIRyaBzSWqEzd1toaS08sYESXERm+YBbrYKKiyq1yXiU1L7BkhMUxiWc3GCYmKcA==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rspack/binding-linux-riscv64-musl@2.1.1': + resolution: {integrity: sha512-87phiiPGFT1p4gy3Oz6YdNijCPXdjJy43LFdE8AFiZsV4ChFXQPqnVCF2aMRLybYa51A+9wGYlTvxt09yUQUww==} + cpu: [riscv64] + os: [linux] + libc: [musl] + '@rspack/binding-linux-x64-gnu@2.0.0': resolution: {integrity: sha512-dPjFGpoCvZfFpJBsWAUR+PR7mWYxpou6L026qIOpAVkz7WiTzErwKD3P1jVrpP4dM9yLb3fVE+PHHjTglhTJ4g==} cpu: [x64] os: [linux] libc: [glibc] - '@rspack/binding-linux-x64-gnu@2.0.5': - resolution: {integrity: sha512-241wqE132jh+/U/pn97qUPV4KpIy4bSrTH0tqfzQCocgw+8hrUj02GqNG+3MXVC3qtwaQeJFYgEBy3TqFKsrIQ==} + '@rspack/binding-linux-x64-gnu@2.0.8': + resolution: {integrity: sha512-zrkoEOnqj1hOEBO5T2I/2Ts2HSJsYFh1qXwMpK4dMJFGGNWDfNeUa6/LF5uq3VINF3JUl7RL47AgrucoSZJXPA==} cpu: [x64] os: [linux] libc: [glibc] - '@rspack/binding-linux-x64-gnu@2.0.8': - resolution: {integrity: sha512-zrkoEOnqj1hOEBO5T2I/2Ts2HSJsYFh1qXwMpK4dMJFGGNWDfNeUa6/LF5uq3VINF3JUl7RL47AgrucoSZJXPA==} + '@rspack/binding-linux-x64-gnu@2.1.1': + resolution: {integrity: sha512-om0/PvZzxISsnyz3hdmI3xBi3Qsn6faRGrfp6WJpGHa5JYYaRVWz1MJ9fe+/cQE9ON9r32HpMOeuataCLv+Owg==} cpu: [x64] os: [linux] libc: [glibc] @@ -19234,14 +19258,14 @@ packages: os: [linux] libc: [musl] - '@rspack/binding-linux-x64-musl@2.0.5': - resolution: {integrity: sha512-BhaXZD064Lci3Kia0kLDAb4TyxO2C+0UidMlj44e8+ctasxIfFZgnrhCJrhTFHAtOiAwqhU3FHun2UuxPqX0Eg==} + '@rspack/binding-linux-x64-musl@2.0.8': + resolution: {integrity: sha512-6CtDaGZjNDvJd9TBp7a9zABbrPORO21W96+3ZcGBn0YNUPUk4ARxIxrTTpeJ/1F41QDM8AYIkGDdqEYMqTYBsA==} cpu: [x64] os: [linux] libc: [musl] - '@rspack/binding-linux-x64-musl@2.0.8': - resolution: {integrity: sha512-6CtDaGZjNDvJd9TBp7a9zABbrPORO21W96+3ZcGBn0YNUPUk4ARxIxrTTpeJ/1F41QDM8AYIkGDdqEYMqTYBsA==} + '@rspack/binding-linux-x64-musl@2.1.1': + resolution: {integrity: sha512-YgAyTOM5ZWvWT0Exwcg7QkGUv9PsHT4yIsEyJbH+a99uAKliGMwkHIOP/aJgrCF8G+lTfigY2wyl9cWo4TA4aw==} cpu: [x64] os: [linux] libc: [musl] @@ -19250,26 +19274,26 @@ packages: resolution: {integrity: sha512-ANk73ZKtPrZf9gdtyRK2nQUfhi1uXoC5P2KF89pyVAE8+zcoLBnYtZGYpWa/cmNi5BcO5g4Z+v2l1UA3bUPLQQ==} cpu: [wasm32] - '@rspack/binding-wasm32-wasi@2.0.5': - resolution: {integrity: sha512-duEkRoXrl9SW8uGHv7JURJ5lgKu87qFDQ4Exy6UQPvsUJVXhtRXTfvMHCb/CejVJuW2Bw2D632/axZq3qRSuBQ==} - cpu: [wasm32] - '@rspack/binding-wasm32-wasi@2.0.8': resolution: {integrity: sha512-Yf4SiqTUroT5Ju+te0YAY2xxKOb35tECsO21v7hYyGa705wrgoAK/MmF7enOvs9GR1iZIqgiLD/wxsIxl8GjJw==} cpu: [wasm32] + '@rspack/binding-wasm32-wasi@2.1.1': + resolution: {integrity: sha512-OicgaB3Dcd+KXsH/I3DkJG7XQyREJScg/jLkCtycFn9BhT0IVJvgi37F8QNJKl8Bp9YZIGehsqK9HpQWav6SLw==} + cpu: [wasm32] + '@rspack/binding-win32-arm64-msvc@2.0.0': resolution: {integrity: sha512-IHZFRtJ85ONbM+BCtF4TeYXS2Fu9X0IJS2phX1rPibYq9iEtHGfBt4cNlnsJPhbPAXVvi4Oli/yiLRJ1zxtCIg==} cpu: [arm64] os: [win32] - '@rspack/binding-win32-arm64-msvc@2.0.5': - resolution: {integrity: sha512-q2WT3HFoWL+2g84l3s2kY7CiE1gEZ1bwB3txx3eZzQQ6YKP7bE82z6sl6S/pTOHGjHdAO4snQXpSaHwUt3LX5g==} + '@rspack/binding-win32-arm64-msvc@2.0.8': + resolution: {integrity: sha512-8NCuiQsAhXrwRBy57QZoypqrws/zLBkaQVGiB8hksr6v++8hNigNjqpQARLbd0iyMuHsQQ++8+auGk6xlDXmzw==} cpu: [arm64] os: [win32] - '@rspack/binding-win32-arm64-msvc@2.0.8': - resolution: {integrity: sha512-8NCuiQsAhXrwRBy57QZoypqrws/zLBkaQVGiB8hksr6v++8hNigNjqpQARLbd0iyMuHsQQ++8+auGk6xlDXmzw==} + '@rspack/binding-win32-arm64-msvc@2.1.1': + resolution: {integrity: sha512-fwL4P3SsC0r/vVGgnmexnc4vkvf+9vWMcGw5fdlPBJNR/zWlZRSXR5V6eXcHrpYVLhBsLYvZYE3NZa9paIUGxw==} cpu: [arm64] os: [win32] @@ -19278,13 +19302,13 @@ packages: cpu: [ia32] os: [win32] - '@rspack/binding-win32-ia32-msvc@2.0.5': - resolution: {integrity: sha512-nMJGIY7kvgbyMolEE7tXDe+Z9jSItDshTIqMQQkkD3WTHdjlBQozHxk4kBtKLsunO+3NkCLe5Oa3hXg1yyStIg==} + '@rspack/binding-win32-ia32-msvc@2.0.8': + resolution: {integrity: sha512-bxiekytbX7V9KFAra+HkwtNWC6pYfHEBBZFpiT0xUs3mCFOmAAFVBsBSQsoCP9AdCEXoMAvNdnrHNw3iov4OZw==} cpu: [ia32] os: [win32] - '@rspack/binding-win32-ia32-msvc@2.0.8': - resolution: {integrity: sha512-bxiekytbX7V9KFAra+HkwtNWC6pYfHEBBZFpiT0xUs3mCFOmAAFVBsBSQsoCP9AdCEXoMAvNdnrHNw3iov4OZw==} + '@rspack/binding-win32-ia32-msvc@2.1.1': + resolution: {integrity: sha512-j3BISXbjPyI57HeHxW9Jx+UP7RebNQ4t62uCbRP29caf9aghYJP+ZA5nJ2X7pol7N5Je2/V6WNahuNd0zQFDOA==} cpu: [ia32] os: [win32] @@ -19293,25 +19317,25 @@ packages: cpu: [x64] os: [win32] - '@rspack/binding-win32-x64-msvc@2.0.5': - resolution: {integrity: sha512-vP0BR6fxdPL9cb02HAuZATg/CjR07aecWel3s1vqRwW1aDffgXh9PVmqEKIHTgyaNsNR55kSKNJsB9AcQ8/QrA==} + '@rspack/binding-win32-x64-msvc@2.0.8': + resolution: {integrity: sha512-7zPs8YCe/ZVJTwd+5lpB0CP0tkn2pONf/T1ycmVY76u21Nrwt8mXQGc/2yH2eWP4B7fikYBr3hGr7mpR2fajqQ==} cpu: [x64] os: [win32] - '@rspack/binding-win32-x64-msvc@2.0.8': - resolution: {integrity: sha512-7zPs8YCe/ZVJTwd+5lpB0CP0tkn2pONf/T1ycmVY76u21Nrwt8mXQGc/2yH2eWP4B7fikYBr3hGr7mpR2fajqQ==} + '@rspack/binding-win32-x64-msvc@2.1.1': + resolution: {integrity: sha512-aPNNFuZT5iBM8+S9J4P5ywJbf3tUvZN3Ppe6mXJ8/jTVDUDhe0YVj3AgD/AuxnUvl+yHrLfQkN1nNwUfIcomfA==} cpu: [x64] os: [win32] '@rspack/binding@2.0.0': resolution: {integrity: sha512-WA2f9eQpejkvf5Vrnf6wNCn1m8RT1p08NjgOZpKhsCzr0uBjWeRvGduawlrFFHZh/jPnWZTVaVdQ08FEAWbwGw==} - '@rspack/binding@2.0.5': - resolution: {integrity: sha512-Ta1y4WXJA87wM1OstqaMddoPsBGv7Cu779bYToKxEAqR/Yy9DxLkp7bdgBaAx2JH++BwVjV+toWts2V9AaiTFQ==} - '@rspack/binding@2.0.8': resolution: {integrity: sha512-3uZ+y8aQxq33ty2srMxg2Nu0XuBI6vVrG50rkDaXqwWqOohfgGUSfFuQK7EnSUNy4aFUQlCG6NHialQHJov0wg==} + '@rspack/binding@2.1.1': + resolution: {integrity: sha512-6062hZP33fn1w51ClpQnum19GGXl/3eS754xrRYbQ7wwBNZk94HVwfSyiqcNCtY/pB1lsFjnaTKiW/Q+jv0axQ==} + '@rspack/core@2.0.0': resolution: {integrity: sha512-WD1mJM9LbZ7Z399Rbv9dE3BNEV0+3sE5OzDdzV8hOxUb3mX++ynK5n9kil8w60B6nGdcKeV9ly5aN4PgqiwWUg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -19324,8 +19348,8 @@ packages: '@swc/helpers': optional: true - '@rspack/core@2.0.5': - resolution: {integrity: sha512-9tv2HAnSiTote5WPH2tmz1hLZ1zKbzkiZc1eYp7LP/8jcsiJBuf40ihiWidAgbbuYtJo3kWET6q+qOm5UhNiGA==} + '@rspack/core@2.0.8': + resolution: {integrity: sha512-+NLGJf8gZxihDmMFzjlly3toc2SMjeDmuvz0/Cai9AMdV4F+Pqcnt2BA9V4e3SY2jmhJQtPwgyyLtR1RiJO77g==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: '@module-federation/runtime-tools': ^0.24.1 || ^2.0.0 @@ -19336,8 +19360,8 @@ packages: '@swc/helpers': optional: true - '@rspack/core@2.0.8': - resolution: {integrity: sha512-+NLGJf8gZxihDmMFzjlly3toc2SMjeDmuvz0/Cai9AMdV4F+Pqcnt2BA9V4e3SY2jmhJQtPwgyyLtR1RiJO77g==} + '@rspack/core@2.1.1': + resolution: {integrity: sha512-Rgz6xZcD0rm7ejZhYNi13qvW2dSnIQQt/7D3xbYoT5hOU838JbkF0P3SAMFGKE+FyLZswnyRpxGfnYHZQqDmJA==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: '@module-federation/runtime-tools': ^0.24.1 || ^2.0.0 @@ -19789,9 +19813,6 @@ packages: '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} - '@swc/helpers@0.5.21': - resolution: {integrity: sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg==} - '@swc/helpers@0.5.23': resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} @@ -20250,6 +20271,9 @@ packages: '@tybys/wasm-util@0.10.2': resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + '@tybys/wasm-util@0.9.0': resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} @@ -25883,18 +25907,6 @@ packages: rrweb-cssom@0.8.0: resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} - rspack-vue-loader@17.4.4: - resolution: {integrity: sha512-T4rkTZWg9hC7DZapTaJcIphser4ogsTffH/rwnX0pwMpNKjv1XKLBPa5Icd/xNPOh1NiKVxoxcT+gb8NQliukA==} - peerDependencies: - '@vue/compiler-sfc': '*' - vue: '*' - webpack: ^4.1.0 || ^5.0.0-0 - peerDependenciesMeta: - '@vue/compiler-sfc': - optional: true - vue: - optional: true - rspack-vue-loader@17.5.0: resolution: {integrity: sha512-hJrL2+jytfTs6ORHBOKTh5lU7BVZvmv7afELuQfyEpGC1ll7MKj1BxlgAIbhd6pZwoEwfdH79Lewtwe4VOlfCQ==} peerDependencies: @@ -27127,10 +27139,6 @@ packages: unwasm@0.5.3: resolution: {integrity: sha512-keBgTSfp3r6+s9ZcSma+0chwxQdmLbB5+dAD9vjtB21UTMYuKAxHXCU1K2CbCtnP09EaWeRvACnXk0EJtUx+hw==} - upath@2.0.1: - resolution: {integrity: sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==} - engines: {node: '>=4'} - update-browserslist-db@1.1.2: resolution: {integrity: sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==} hasBin: true @@ -28166,15 +28174,6 @@ snapshots: dependencies: '@babel/types': 7.29.0 - '@babel/plugin-proposal-decorators@7.28.0(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-decorators': 7.27.1(@babel/core@7.28.5) - transitivePeerDependencies: - - supports-color - '@babel/plugin-proposal-decorators@7.29.0(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -28184,11 +28183,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-syntax-decorators@7.27.1(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-decorators@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -28219,14 +28213,6 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 - transitivePeerDependencies: - - supports-color - '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -28336,17 +28322,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/preset-typescript@7.28.5(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5) - transitivePeerDependencies: - - supports-color - '@babel/preset-typescript@7.28.5(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -28956,6 +28931,12 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/core@1.11.1': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + '@emnapi/core@1.4.5': dependencies: '@emnapi/wasi-threads': 1.0.4 @@ -28966,6 +28947,11 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/runtime@1.11.1': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.4.5': dependencies: tslib: 2.8.1 @@ -28979,6 +28965,11 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/wasi-threads@1.2.2': + dependencies: + tslib: 2.8.1 + optional: true + '@emotion/babel-plugin@11.13.5': dependencies: '@babel/helper-module-imports': 7.27.1 @@ -30162,12 +30153,12 @@ snapshots: '@img/sharp-wasm32@0.34.4': dependencies: - '@emnapi/runtime': 1.10.0 + '@emnapi/runtime': 1.11.1 optional: true '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.10.0 + '@emnapi/runtime': 1.11.1 optional: true '@img/sharp-win32-arm64@0.34.4': @@ -30636,8 +30627,8 @@ snapshots: '@napi-rs/wasm-runtime@0.2.12': dependencies: - '@emnapi/core': 1.10.0 - '@emnapi/runtime': 1.10.0 + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 '@tybys/wasm-util': 0.10.2 optional: true @@ -30654,6 +30645,20 @@ snapshots: '@tybys/wasm-util': 0.10.2 optional: true + '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@tybys/wasm-util': 0.10.2 + optional: true + + '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@tybys/wasm-util': 0.10.3 + optional: true + '@neon-rs/load@0.0.4': {} '@netlify/api@14.0.7': @@ -31134,9 +31139,9 @@ snapshots: '@oxc-minify/binding-openharmony-arm64@0.110.0': optional: true - '@oxc-minify/binding-wasm32-wasi@0.110.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + '@oxc-minify/binding-wasm32-wasi@0.110.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -31268,9 +31273,9 @@ snapshots: '@oxc-transform/binding-openharmony-arm64@0.110.0': optional: true - '@oxc-transform/binding-wasm32-wasi@0.110.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + '@oxc-transform/binding-wasm32-wasi@0.110.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -32381,9 +32386,9 @@ snapshots: '@rolldown/binding-openharmony-arm64@1.0.2': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-rc.9(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + '@rolldown/binding-wasm32-wasi@1.0.0-rc.9(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -32560,13 +32565,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.56.0': optional: true - '@rsbuild/core@2.0.1': - dependencies: - '@rspack/core': 2.0.0(@swc/helpers@0.5.21) - '@swc/helpers': 0.5.21 - transitivePeerDependencies: - - '@module-federation/runtime-tools' - '@rsbuild/core@2.0.14': dependencies: '@rspack/core': 2.0.8(@swc/helpers@0.5.23) @@ -32574,28 +32572,14 @@ snapshots: transitivePeerDependencies: - '@module-federation/runtime-tools' - '@rsbuild/core@2.0.8': + '@rsbuild/core@2.1.1': dependencies: - '@rspack/core': 2.0.5(@swc/helpers@0.5.23) + '@rspack/core': 2.1.1(@swc/helpers@0.5.23) '@swc/helpers': 0.5.23 transitivePeerDependencies: - '@module-federation/runtime-tools' - '@rsbuild/plugin-babel@1.0.6(@rsbuild/core@2.0.14)': - dependencies: - '@babel/core': 7.28.5 - '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.5) - '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.28.5) - '@babel/preset-typescript': 7.28.5(@babel/core@7.28.5) - '@rsbuild/core': 2.0.14 - '@types/babel__core': 7.20.5 - deepmerge: 4.3.1 - reduce-configs: 1.1.1 - upath: 2.0.1 - transitivePeerDependencies: - - supports-color - - '@rsbuild/plugin-babel@1.1.2(@rsbuild/core@2.0.14)': + '@rsbuild/plugin-babel@1.1.2(@rsbuild/core@2.1.1)': dependencies: '@babel/core': 7.29.0 '@babel/plugin-proposal-decorators': 7.29.0(@babel/core@7.29.0) @@ -32604,89 +32588,65 @@ snapshots: '@types/babel__core': 7.20.5 reduce-configs: 1.1.1 optionalDependencies: - '@rsbuild/core': 2.0.14 + '@rsbuild/core': 2.1.1 transitivePeerDependencies: - supports-color - '@rsbuild/plugin-react@2.0.0(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23))': + '@rsbuild/plugin-react@2.0.0(@rsbuild/core@2.0.14)(@rspack/core@2.1.1(@swc/helpers@0.5.23))': dependencies: - '@rspack/plugin-react-refresh': 2.0.0(@rspack/core@2.0.8(@swc/helpers@0.5.23))(react-refresh@0.18.0) + '@rspack/plugin-react-refresh': 2.0.0(@rspack/core@2.1.1(@swc/helpers@0.5.23))(react-refresh@0.18.0) react-refresh: 0.18.0 optionalDependencies: '@rsbuild/core': 2.0.14 transitivePeerDependencies: - '@rspack/core' - '@rsbuild/plugin-react@2.0.0(@rsbuild/core@2.0.8)(@rspack/core@2.0.8(@swc/helpers@0.5.23))': + '@rsbuild/plugin-react@2.0.0(@rsbuild/core@2.1.1)(@rspack/core@2.1.1(@swc/helpers@0.5.23))': dependencies: - '@rspack/plugin-react-refresh': 2.0.0(@rspack/core@2.0.8(@swc/helpers@0.5.23))(react-refresh@0.18.0) + '@rspack/plugin-react-refresh': 2.0.0(@rspack/core@2.1.1(@swc/helpers@0.5.23))(react-refresh@0.18.0) react-refresh: 0.18.0 optionalDependencies: - '@rsbuild/core': 2.0.8 + '@rsbuild/core': 2.1.1 transitivePeerDependencies: - '@rspack/core' - '@rsbuild/plugin-solid@1.1.1(@babel/core@7.29.0)(@rsbuild/core@2.0.14)(solid-js@1.9.12)': + '@rsbuild/plugin-solid@1.1.1(@babel/core@7.29.0)(@rsbuild/core@2.1.1)(solid-js@1.9.12)': dependencies: - '@rsbuild/plugin-babel': 1.1.2(@rsbuild/core@2.0.14) + '@rsbuild/plugin-babel': 1.1.2(@rsbuild/core@2.1.1) babel-preset-solid: 1.9.10(@babel/core@7.29.0)(solid-js@1.9.12) solid-refresh: 0.7.8(solid-js@1.9.12) optionalDependencies: - '@rsbuild/core': 2.0.14 + '@rsbuild/core': 2.1.1 transitivePeerDependencies: - '@babel/core' - solid-js - supports-color - '@rsbuild/plugin-tailwindcss@2.0.3(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23))(webpack@5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4))': + '@rsbuild/plugin-tailwindcss@2.0.3(@rsbuild/core@2.0.14)(@rspack/core@2.1.1(@swc/helpers@0.5.23))(webpack@5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4))': dependencies: - '@tailwindcss/webpack': 4.3.1(@rspack/core@2.0.8(@swc/helpers@0.5.23))(webpack@5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4)) + '@tailwindcss/webpack': 4.3.1(@rspack/core@2.1.1(@swc/helpers@0.5.23))(webpack@5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4)) optionalDependencies: '@rsbuild/core': 2.0.14 transitivePeerDependencies: - '@rspack/core' - webpack - '@rsbuild/plugin-vue-jsx@1.1.1(@babel/core@7.29.0)(@rsbuild/core@2.0.14)': + '@rsbuild/plugin-vue-jsx@2.0.0(@babel/core@7.29.0)(@rsbuild/core@2.1.1)': dependencies: - '@rsbuild/plugin-babel': 1.1.2(@rsbuild/core@2.0.14) - '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.29.0) - babel-plugin-vue-jsx-hmr: 1.0.0 - optionalDependencies: - '@rsbuild/core': 2.0.14 - transitivePeerDependencies: - - '@babel/core' - - supports-color - - '@rsbuild/plugin-vue-jsx@2.0.0(@babel/core@7.29.0)(@rsbuild/core@2.0.14)': - dependencies: - '@rsbuild/plugin-babel': 1.1.2(@rsbuild/core@2.0.14) + '@rsbuild/plugin-babel': 1.1.2(@rsbuild/core@2.1.1) '@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.0) babel-plugin-vue-jsx-hmr: 1.0.0 optionalDependencies: - '@rsbuild/core': 2.0.14 + '@rsbuild/core': 2.1.1 transitivePeerDependencies: - '@babel/core' - supports-color - '@rsbuild/plugin-vue@1.2.2(@rsbuild/core@2.0.14)(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4)(vue@3.5.25(@typescript/typescript6@6.0.2))': + '@rsbuild/plugin-vue@1.2.7(@rsbuild/core@2.1.1)(@rspack/core@2.1.1(@swc/helpers@0.5.23))(vue@3.5.25(@typescript/typescript6@6.0.2))': dependencies: - '@rsbuild/core': 2.0.14 - rspack-vue-loader: 17.4.4(vue@3.5.25(@typescript/typescript6@6.0.2))(webpack@5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4)) - webpack: 5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4) - transitivePeerDependencies: - - '@swc/core' - - '@vue/compiler-sfc' - - esbuild - - uglify-js - - vue - - webpack-cli - - '@rsbuild/plugin-vue@1.2.7(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23))(vue@3.5.25(@typescript/typescript6@6.0.2))': - dependencies: - rspack-vue-loader: 17.5.0(@rspack/core@2.0.8(@swc/helpers@0.5.23))(vue@3.5.25(@typescript/typescript6@6.0.2)) + rspack-vue-loader: 17.5.0(@rspack/core@2.1.1(@swc/helpers@0.5.23))(vue@3.5.25(@typescript/typescript6@6.0.2)) optionalDependencies: - '@rsbuild/core': 2.0.14 + '@rsbuild/core': 2.1.1 transitivePeerDependencies: - '@rspack/core' - '@vue/compiler-sfc' @@ -32695,57 +32655,63 @@ snapshots: '@rspack/binding-darwin-arm64@2.0.0': optional: true - '@rspack/binding-darwin-arm64@2.0.5': + '@rspack/binding-darwin-arm64@2.0.8': optional: true - '@rspack/binding-darwin-arm64@2.0.8': + '@rspack/binding-darwin-arm64@2.1.1': optional: true '@rspack/binding-darwin-x64@2.0.0': optional: true - '@rspack/binding-darwin-x64@2.0.5': + '@rspack/binding-darwin-x64@2.0.8': optional: true - '@rspack/binding-darwin-x64@2.0.8': + '@rspack/binding-darwin-x64@2.1.1': optional: true '@rspack/binding-linux-arm64-gnu@2.0.0': optional: true - '@rspack/binding-linux-arm64-gnu@2.0.5': + '@rspack/binding-linux-arm64-gnu@2.0.8': optional: true - '@rspack/binding-linux-arm64-gnu@2.0.8': + '@rspack/binding-linux-arm64-gnu@2.1.1': optional: true '@rspack/binding-linux-arm64-musl@2.0.0': optional: true - '@rspack/binding-linux-arm64-musl@2.0.5': + '@rspack/binding-linux-arm64-musl@2.0.8': optional: true - '@rspack/binding-linux-arm64-musl@2.0.8': + '@rspack/binding-linux-arm64-musl@2.1.1': optional: true - '@rspack/binding-linux-x64-gnu@2.0.0': + '@rspack/binding-linux-riscv64-gnu@2.1.1': optional: true - '@rspack/binding-linux-x64-gnu@2.0.5': + '@rspack/binding-linux-riscv64-musl@2.1.1': + optional: true + + '@rspack/binding-linux-x64-gnu@2.0.0': optional: true '@rspack/binding-linux-x64-gnu@2.0.8': optional: true - '@rspack/binding-linux-x64-musl@2.0.0': + '@rspack/binding-linux-x64-gnu@2.1.1': optional: true - '@rspack/binding-linux-x64-musl@2.0.5': + '@rspack/binding-linux-x64-musl@2.0.0': optional: true '@rspack/binding-linux-x64-musl@2.0.8': optional: true + '@rspack/binding-linux-x64-musl@2.1.1': + optional: true + '@rspack/binding-wasm32-wasi@2.0.0': dependencies: '@emnapi/core': 1.10.0 @@ -32753,45 +32719,45 @@ snapshots: '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true - '@rspack/binding-wasm32-wasi@2.0.5': + '@rspack/binding-wasm32-wasi@2.0.8': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true - '@rspack/binding-wasm32-wasi@2.0.8': + '@rspack/binding-wasm32-wasi@2.1.1': dependencies: - '@emnapi/core': 1.10.0 - '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) optional: true '@rspack/binding-win32-arm64-msvc@2.0.0': optional: true - '@rspack/binding-win32-arm64-msvc@2.0.5': + '@rspack/binding-win32-arm64-msvc@2.0.8': optional: true - '@rspack/binding-win32-arm64-msvc@2.0.8': + '@rspack/binding-win32-arm64-msvc@2.1.1': optional: true '@rspack/binding-win32-ia32-msvc@2.0.0': optional: true - '@rspack/binding-win32-ia32-msvc@2.0.5': + '@rspack/binding-win32-ia32-msvc@2.0.8': optional: true - '@rspack/binding-win32-ia32-msvc@2.0.8': + '@rspack/binding-win32-ia32-msvc@2.1.1': optional: true '@rspack/binding-win32-x64-msvc@2.0.0': optional: true - '@rspack/binding-win32-x64-msvc@2.0.5': + '@rspack/binding-win32-x64-msvc@2.0.8': optional: true - '@rspack/binding-win32-x64-msvc@2.0.8': + '@rspack/binding-win32-x64-msvc@2.1.1': optional: true '@rspack/binding@2.0.0': @@ -32807,19 +32773,6 @@ snapshots: '@rspack/binding-win32-ia32-msvc': 2.0.0 '@rspack/binding-win32-x64-msvc': 2.0.0 - '@rspack/binding@2.0.5': - optionalDependencies: - '@rspack/binding-darwin-arm64': 2.0.5 - '@rspack/binding-darwin-x64': 2.0.5 - '@rspack/binding-linux-arm64-gnu': 2.0.5 - '@rspack/binding-linux-arm64-musl': 2.0.5 - '@rspack/binding-linux-x64-gnu': 2.0.5 - '@rspack/binding-linux-x64-musl': 2.0.5 - '@rspack/binding-wasm32-wasi': 2.0.5 - '@rspack/binding-win32-arm64-msvc': 2.0.5 - '@rspack/binding-win32-ia32-msvc': 2.0.5 - '@rspack/binding-win32-x64-msvc': 2.0.5 - '@rspack/binding@2.0.8': optionalDependencies: '@rspack/binding-darwin-arm64': 2.0.8 @@ -32833,11 +32786,20 @@ snapshots: '@rspack/binding-win32-ia32-msvc': 2.0.8 '@rspack/binding-win32-x64-msvc': 2.0.8 - '@rspack/core@2.0.0(@swc/helpers@0.5.21)': - dependencies: - '@rspack/binding': 2.0.0 - optionalDependencies: - '@swc/helpers': 0.5.21 + '@rspack/binding@2.1.1': + optionalDependencies: + '@rspack/binding-darwin-arm64': 2.1.1 + '@rspack/binding-darwin-x64': 2.1.1 + '@rspack/binding-linux-arm64-gnu': 2.1.1 + '@rspack/binding-linux-arm64-musl': 2.1.1 + '@rspack/binding-linux-riscv64-gnu': 2.1.1 + '@rspack/binding-linux-riscv64-musl': 2.1.1 + '@rspack/binding-linux-x64-gnu': 2.1.1 + '@rspack/binding-linux-x64-musl': 2.1.1 + '@rspack/binding-wasm32-wasi': 2.1.1 + '@rspack/binding-win32-arm64-msvc': 2.1.1 + '@rspack/binding-win32-ia32-msvc': 2.1.1 + '@rspack/binding-win32-x64-msvc': 2.1.1 '@rspack/core@2.0.0(@swc/helpers@0.5.23)': dependencies: @@ -32845,25 +32807,25 @@ snapshots: optionalDependencies: '@swc/helpers': 0.5.23 - '@rspack/core@2.0.5(@swc/helpers@0.5.23)': + '@rspack/core@2.0.8(@swc/helpers@0.5.23)': dependencies: - '@rspack/binding': 2.0.5 + '@rspack/binding': 2.0.8 optionalDependencies: '@swc/helpers': 0.5.23 - '@rspack/core@2.0.8(@swc/helpers@0.5.23)': + '@rspack/core@2.1.1(@swc/helpers@0.5.23)': dependencies: - '@rspack/binding': 2.0.8 + '@rspack/binding': 2.1.1 optionalDependencies: '@swc/helpers': 0.5.23 '@rspack/lite-tapable@1.1.0': {} - '@rspack/plugin-react-refresh@2.0.0(@rspack/core@2.0.8(@swc/helpers@0.5.23))(react-refresh@0.18.0)': + '@rspack/plugin-react-refresh@2.0.0(@rspack/core@2.1.1(@swc/helpers@0.5.23))(react-refresh@0.18.0)': dependencies: react-refresh: 0.18.0 optionalDependencies: - '@rspack/core': 2.0.8(@swc/helpers@0.5.23) + '@rspack/core': 2.1.1(@swc/helpers@0.5.23) '@rushstack/node-core-library@5.7.0(@types/node@25.0.9)': dependencies: @@ -33300,10 +33262,6 @@ snapshots: dependencies: tslib: 2.8.1 - '@swc/helpers@0.5.21': - dependencies: - tslib: 2.8.1 - '@swc/helpers@0.5.23': dependencies: tslib: 2.8.1 @@ -33449,14 +33407,14 @@ snapshots: tailwindcss: 4.2.2 vite: 8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0) - '@tailwindcss/webpack@4.3.1(@rspack/core@2.0.8(@swc/helpers@0.5.23))(webpack@5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4))': + '@tailwindcss/webpack@4.3.1(@rspack/core@2.1.1(@swc/helpers@0.5.23))(webpack@5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4))': dependencies: '@alloc/quick-lru': 5.2.0 '@tailwindcss/node': 4.3.1 '@tailwindcss/oxide': 4.3.1 tailwindcss: 4.3.1 optionalDependencies: - '@rspack/core': 2.0.8(@swc/helpers@0.5.23) + '@rspack/core': 2.1.1(@swc/helpers@0.5.23) webpack: 5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4) '@tanstack/devtools-client@0.0.4': @@ -33799,6 +33757,11 @@ snapshots: tslib: 2.8.1 optional: true + '@tybys/wasm-util@0.10.3': + dependencies: + tslib: 2.8.1 + optional: true + '@tybys/wasm-util@0.9.0': dependencies: tslib: 2.8.1 @@ -34954,22 +34917,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@vue/babel-plugin-jsx@1.5.0(@babel/core@7.29.0)': - dependencies: - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.29.0) - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 - '@babel/types': 7.29.0 - '@vue/babel-helper-vue-transform-on': 1.5.0 - '@vue/babel-plugin-resolve-type': 1.5.0(@babel/core@7.29.0) - '@vue/shared': 3.5.25 - optionalDependencies: - '@babel/core': 7.29.0 - transitivePeerDependencies: - - supports-color - '@vue/babel-plugin-jsx@2.0.1(@babel/core@7.29.0)': dependencies: '@babel/helper-module-imports': 7.27.1 @@ -34997,17 +34944,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@vue/babel-plugin-resolve-type@1.5.0(@babel/core@7.29.0)': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/parser': 7.28.5 - '@vue/compiler-sfc': 3.5.25 - transitivePeerDependencies: - - supports-color - '@vue/babel-plugin-resolve-type@2.0.1(@babel/core@7.29.0)': dependencies: '@babel/code-frame': 7.27.1 @@ -35304,6 +35240,7 @@ snapshots: acorn-import-phases@1.0.4(acorn@8.15.0): dependencies: acorn: 8.15.0 + optional: true acorn-jsx@5.3.2(acorn@8.15.0): dependencies: @@ -38784,7 +38721,8 @@ snapshots: loader-runner@4.3.0: {} - loader-runner@4.3.1: {} + loader-runner@4.3.1: + optional: true local-pkg@0.5.1: dependencies: @@ -39174,7 +39112,7 @@ snapshots: nf3@0.3.6: {} - nitro@3.0.1-alpha.2(@electric-sql/pglite@0.3.2)(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(ioredis@5.9.2)(lru-cache@11.5.1)(mysql2@3.15.3)(rolldown@1.0.2)(rollup@4.56.0)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)): + nitro@3.0.1-alpha.2(@electric-sql/pglite@0.3.2)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(ioredis@5.9.2)(lru-cache@11.5.1)(mysql2@3.15.3)(rolldown@1.0.2)(rollup@4.56.0)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)): dependencies: consola: 3.4.2 crossws: 0.4.3(srvx@0.10.1) @@ -39184,8 +39122,8 @@ snapshots: nf3: 0.3.6 ofetch: 2.0.0-alpha.3 ohash: 2.0.11 - oxc-minify: 0.110.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) - oxc-transform: 0.110.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + oxc-minify: 0.110.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + oxc-transform: 0.110.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) srvx: 0.10.1 undici: 7.24.4 unenv: 2.0.0-rc.24 @@ -39225,7 +39163,7 @@ snapshots: - sqlite3 - uploadthing - nitro@3.0.260311-beta(@electric-sql/pglite@0.3.2)(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(dotenv@17.4.2)(giget@2.0.0)(jiti@2.7.0)(lru-cache@11.5.1)(miniflare@4.20260317.0)(mysql2@3.15.3)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)): + nitro@3.0.260311-beta(@electric-sql/pglite@0.3.2)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@libsql/client@0.15.15)(@netlify/blobs@10.1.0)(chokidar@5.0.0)(dotenv@17.4.2)(giget@2.0.0)(jiti@2.7.0)(lru-cache@11.5.1)(miniflare@4.20260317.0)(mysql2@3.15.3)(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)): dependencies: consola: 3.4.2 crossws: 0.4.4(srvx@0.11.15) @@ -39237,7 +39175,7 @@ snapshots: ocache: 0.1.2 ofetch: 2.0.0-alpha.3 ohash: 2.0.11 - rolldown: 1.0.0-rc.9(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + rolldown: 1.0.0-rc.9(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) srvx: 0.11.15 unenv: 2.0.0-rc.24 unstorage: 2.0.0-alpha.6(@netlify/blobs@10.1.0)(chokidar@5.0.0)(db0@0.3.4(@electric-sql/pglite@0.3.2)(@libsql/client@0.15.15)(mysql2@3.15.3))(lru-cache@11.5.1)(ofetch@2.0.0-alpha.3) @@ -39703,7 +39641,7 @@ snapshots: outvariant@1.4.3: {} - oxc-minify@0.110.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): + oxc-minify@0.110.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1): optionalDependencies: '@oxc-minify/binding-android-arm-eabi': 0.110.0 '@oxc-minify/binding-android-arm64': 0.110.0 @@ -39721,7 +39659,7 @@ snapshots: '@oxc-minify/binding-linux-x64-gnu': 0.110.0 '@oxc-minify/binding-linux-x64-musl': 0.110.0 '@oxc-minify/binding-openharmony-arm64': 0.110.0 - '@oxc-minify/binding-wasm32-wasi': 0.110.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@oxc-minify/binding-wasm32-wasi': 0.110.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) '@oxc-minify/binding-win32-arm64-msvc': 0.110.0 '@oxc-minify/binding-win32-ia32-msvc': 0.110.0 '@oxc-minify/binding-win32-x64-msvc': 0.110.0 @@ -39755,7 +39693,7 @@ snapshots: - '@emnapi/core' - '@emnapi/runtime' - oxc-transform@0.110.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): + oxc-transform@0.110.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1): optionalDependencies: '@oxc-transform/binding-android-arm-eabi': 0.110.0 '@oxc-transform/binding-android-arm64': 0.110.0 @@ -39773,7 +39711,7 @@ snapshots: '@oxc-transform/binding-linux-x64-gnu': 0.110.0 '@oxc-transform/binding-linux-x64-musl': 0.110.0 '@oxc-transform/binding-openharmony-arm64': 0.110.0 - '@oxc-transform/binding-wasm32-wasi': 0.110.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@oxc-transform/binding-wasm32-wasi': 0.110.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) '@oxc-transform/binding-win32-arm64-msvc': 0.110.0 '@oxc-transform/binding-win32-ia32-msvc': 0.110.0 '@oxc-transform/binding-win32-x64-msvc': 0.110.0 @@ -40632,7 +40570,7 @@ snapshots: glob: 13.0.0 package-json-from-dist: 1.0.1 - rolldown@1.0.0-rc.9(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): + rolldown@1.0.0-rc.9(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1): dependencies: '@oxc-project/types': 0.115.0 '@rolldown/pluginutils': 1.0.0-rc.9 @@ -40649,7 +40587,7 @@ snapshots: '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.9 '@rolldown/binding-linux-x64-musl': 1.0.0-rc.9 '@rolldown/binding-openharmony-arm64': 1.0.0-rc.9 - '@rolldown/binding-wasm32-wasi': 1.0.0-rc.9(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@rolldown/binding-wasm32-wasi': 1.0.0-rc.9(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.9 '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.9 transitivePeerDependencies: @@ -40736,20 +40674,12 @@ snapshots: rrweb-cssom@0.8.0: {} - rspack-vue-loader@17.4.4(vue@3.5.25(@typescript/typescript6@6.0.2))(webpack@5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4)): - dependencies: - chalk: 4.1.2 - watchpack: 2.4.2 - webpack: 5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4) - optionalDependencies: - vue: 3.5.25(@typescript/typescript6@6.0.2) - - rspack-vue-loader@17.5.0(@rspack/core@2.0.8(@swc/helpers@0.5.23))(vue@3.5.25(@typescript/typescript6@6.0.2)): + rspack-vue-loader@17.5.0(@rspack/core@2.1.1(@swc/helpers@0.5.23))(vue@3.5.25(@typescript/typescript6@6.0.2)): dependencies: '@rspack/lite-tapable': 1.1.0 chalk: 4.1.2 optionalDependencies: - '@rspack/core': 2.0.8(@swc/helpers@0.5.23) + '@rspack/core': 2.1.1(@swc/helpers@0.5.23) vue: 3.5.25(@typescript/typescript6@6.0.2) run-applescript@7.0.0: {} @@ -41441,6 +41371,7 @@ snapshots: optionalDependencies: '@swc/core': 1.15.33(@swc/helpers@0.5.23) esbuild: 0.27.4 + optional: true terser@5.37.0: dependencies: @@ -41889,8 +41820,6 @@ snapshots: pathe: 2.0.3 pkg-types: 2.3.0 - upath@2.0.1: {} - update-browserslist-db@1.1.2(browserslist@4.24.4): dependencies: browserslist: 4.24.4 @@ -42300,6 +42229,7 @@ snapshots: dependencies: glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 + optional: true wbuf@1.7.3: dependencies: @@ -42400,7 +42330,8 @@ snapshots: webpack-sources@3.2.3: {} - webpack-sources@3.3.3: {} + webpack-sources@3.3.3: + optional: true webpack-virtual-modules@0.5.0: {} @@ -42437,6 +42368,7 @@ snapshots: - '@swc/core' - esbuild - uglify-js + optional: true webpack@5.97.1(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4): dependencies: From a3ee355b5934215ab5716019fbb4b9f55acdd64f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 22:14:03 +0200 Subject: [PATCH 34/51] ci: Version Packages (#7903) --- .changeset/thick-jars-listen.md | 5 ----- examples/react/start-bare/package.json | 2 +- examples/react/start-basic-auth/package.json | 2 +- examples/react/start-basic-authjs/package.json | 2 +- examples/react/start-basic-cloudflare/package.json | 2 +- examples/react/start-basic-react-query/package.json | 2 +- examples/react/start-basic-rsbuild/package.json | 6 +++--- examples/react/start-basic-static/package.json | 2 +- examples/react/start-basic/package.json | 2 +- examples/react/start-bun/package.json | 2 +- examples/react/start-clerk-basic/package.json | 2 +- examples/react/start-convex-trellaux/package.json | 2 +- examples/react/start-counter/package.json | 2 +- examples/react/start-i18n-paraglide/package.json | 2 +- examples/react/start-large/package.json | 2 +- examples/react/start-material-ui/package.json | 2 +- examples/react/start-rscs/package.json | 2 +- .../package.json | 2 +- examples/react/start-supabase-basic/package.json | 2 +- examples/react/start-tailwind-v4/package.json | 2 +- examples/react/start-trellaux/package.json | 2 +- examples/react/start-workos/package.json | 2 +- packages/react-start-rsc/CHANGELOG.md | 6 ++++++ packages/react-start-rsc/package.json | 2 +- packages/react-start/CHANGELOG.md | 7 +++++++ packages/react-start/package.json | 2 +- 26 files changed, 38 insertions(+), 30 deletions(-) delete mode 100644 .changeset/thick-jars-listen.md diff --git a/.changeset/thick-jars-listen.md b/.changeset/thick-jars-listen.md deleted file mode 100644 index cf26b35aae..0000000000 --- a/.changeset/thick-jars-listen.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@tanstack/react-start-rsc': patch ---- - -Raise the `@vitejs/plugin-rsc` peer range to `>=0.5.30`. Versions in `0.5.20 - 0.5.29` suppress client HMR for a route component co-located with a `createServerFn`, fixed upstream in `@vitejs/plugin-rsc@0.5.30`. diff --git a/examples/react/start-bare/package.json b/examples/react/start-bare/package.json index 2433dcb6ec..3bcab115f3 100644 --- a/examples/react/start-bare/package.json +++ b/examples/react/start-bare/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.32", + "@tanstack/react-start": "^1.168.33", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-basic-auth/package.json b/examples/react/start-basic-auth/package.json index 5c99438daa..2dc40e4007 100644 --- a/examples/react/start-basic-auth/package.json +++ b/examples/react/start-basic-auth/package.json @@ -16,7 +16,7 @@ "@prisma/client": "^7.0.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.32", + "@tanstack/react-start": "^1.168.33", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-basic-authjs/package.json b/examples/react/start-basic-authjs/package.json index bd024839bc..abd9ee7c63 100644 --- a/examples/react/start-basic-authjs/package.json +++ b/examples/react/start-basic-authjs/package.json @@ -13,7 +13,7 @@ "@auth/core": "^0.41.1", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.32", + "@tanstack/react-start": "^1.168.33", "react": "^19.0.0", "react-dom": "^19.0.0", "start-authjs": "^1.0.0", diff --git a/examples/react/start-basic-cloudflare/package.json b/examples/react/start-basic-cloudflare/package.json index 30bf9e8a79..b8abd3e495 100644 --- a/examples/react/start-basic-cloudflare/package.json +++ b/examples/react/start-basic-cloudflare/package.json @@ -14,7 +14,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.32", + "@tanstack/react-start": "^1.168.33", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/examples/react/start-basic-react-query/package.json b/examples/react/start-basic-react-query/package.json index 103669e0ca..4c13bd38ef 100644 --- a/examples/react/start-basic-react-query/package.json +++ b/examples/react/start-basic-react-query/package.json @@ -15,7 +15,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.32", + "@tanstack/react-start": "^1.168.33", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-basic-rsbuild/package.json b/examples/react/start-basic-rsbuild/package.json index c134cd2a3f..708aae9a12 100644 --- a/examples/react/start-basic-rsbuild/package.json +++ b/examples/react/start-basic-rsbuild/package.json @@ -10,9 +10,9 @@ "start": "srvx --prod -s ../client dist/server/index.js" }, "dependencies": { - "@tanstack/react-router": "workspace:*", - "@tanstack/react-router-devtools": "workspace:^", - "@tanstack/react-start": "workspace:*", + "@tanstack/react-router": "^1.170.18", + "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-start": "^1.168.33", "react": "^19.2.3", "react-dom": "^19.2.3", "tailwind-merge": "^3.6.0", diff --git a/examples/react/start-basic-static/package.json b/examples/react/start-basic-static/package.json index af3568f903..b4c38e1b88 100644 --- a/examples/react/start-basic-static/package.json +++ b/examples/react/start-basic-static/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.32", + "@tanstack/react-start": "^1.168.33", "@tanstack/start-static-server-functions": "^1.167.19", "@vitejs/plugin-react": "^6.0.1", "react": "^19.0.0", diff --git a/examples/react/start-basic/package.json b/examples/react/start-basic/package.json index 52155c3a21..998ebe1123 100644 --- a/examples/react/start-basic/package.json +++ b/examples/react/start-basic/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.32", + "@tanstack/react-start": "^1.168.33", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.6.0", diff --git a/examples/react/start-bun/package.json b/examples/react/start-bun/package.json index 6c7436ad1c..efe0dddbf2 100644 --- a/examples/react/start-bun/package.json +++ b/examples/react/start-bun/package.json @@ -18,7 +18,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.32", + "@tanstack/react-start": "^1.168.33", "@tanstack/router-plugin": "^1.168.23", "react": "^19.1.1", "react-dom": "^19.1.1", diff --git a/examples/react/start-clerk-basic/package.json b/examples/react/start-clerk-basic/package.json index fdad5a24f7..53cb3d6b84 100644 --- a/examples/react/start-clerk-basic/package.json +++ b/examples/react/start-clerk-basic/package.json @@ -13,7 +13,7 @@ "@clerk/tanstack-react-start": "^0.27.14", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.32", + "@tanstack/react-start": "^1.168.33", "@vitejs/plugin-react": "^6.0.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/start-convex-trellaux/package.json b/examples/react/start-convex-trellaux/package.json index dc4e0af235..8b2918bd3c 100644 --- a/examples/react/start-convex-trellaux/package.json +++ b/examples/react/start-convex-trellaux/package.json @@ -18,7 +18,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.32", + "@tanstack/react-start": "^1.168.33", "concurrently": "^8.2.2", "convex": "^1.19.0", "ky": "^1.7.4", diff --git a/examples/react/start-counter/package.json b/examples/react/start-counter/package.json index 921d61ab4c..a55dd56831 100644 --- a/examples/react/start-counter/package.json +++ b/examples/react/start-counter/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.32", + "@tanstack/react-start": "^1.168.33", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/examples/react/start-i18n-paraglide/package.json b/examples/react/start-i18n-paraglide/package.json index a139d82dcb..d3a67b4f3e 100644 --- a/examples/react/start-i18n-paraglide/package.json +++ b/examples/react/start-i18n-paraglide/package.json @@ -12,7 +12,7 @@ "@tanstack/react-devtools": "^0.7.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.32", + "@tanstack/react-start": "^1.168.33", "react": "^19.1.1", "react-dom": "^19.1.1" }, diff --git a/examples/react/start-large/package.json b/examples/react/start-large/package.json index 89a6128973..2414df5c9f 100644 --- a/examples/react/start-large/package.json +++ b/examples/react/start-large/package.json @@ -15,7 +15,7 @@ "@tanstack/react-query": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.32", + "@tanstack/react-start": "^1.168.33", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-material-ui/package.json b/examples/react/start-material-ui/package.json index 4367d763f8..3b4b72b51a 100644 --- a/examples/react/start-material-ui/package.json +++ b/examples/react/start-material-ui/package.json @@ -17,7 +17,7 @@ "@mui/material": "6.4.7", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.32", + "@tanstack/react-start": "^1.168.33", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-rscs/package.json b/examples/react/start-rscs/package.json index 45f198c53a..c9371ffe80 100644 --- a/examples/react/start-rscs/package.json +++ b/examples/react/start-rscs/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.32", + "@tanstack/react-start": "^1.168.33", "dexie": "^4.0.10", "react": "^19.2.0", "react-dom": "^19.2.0", diff --git a/examples/react/start-streaming-data-from-server-functions/package.json b/examples/react/start-streaming-data-from-server-functions/package.json index 7583f57493..60d43b762c 100644 --- a/examples/react/start-streaming-data-from-server-functions/package.json +++ b/examples/react/start-streaming-data-from-server-functions/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.32", + "@tanstack/react-start": "^1.168.33", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-supabase-basic/package.json b/examples/react/start-supabase-basic/package.json index 0a0538ea01..0fc9cb8d0e 100644 --- a/examples/react/start-supabase-basic/package.json +++ b/examples/react/start-supabase-basic/package.json @@ -17,7 +17,7 @@ "@supabase/supabase-js": "^2.48.1", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.32", + "@tanstack/react-start": "^1.168.33", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/start-tailwind-v4/package.json b/examples/react/start-tailwind-v4/package.json index 262bb2d30b..eecb98664d 100644 --- a/examples/react/start-tailwind-v4/package.json +++ b/examples/react/start-tailwind-v4/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.32", + "@tanstack/react-start": "^1.168.33", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.6.0", diff --git a/examples/react/start-trellaux/package.json b/examples/react/start-trellaux/package.json index 11834731f1..1501362163 100644 --- a/examples/react/start-trellaux/package.json +++ b/examples/react/start-trellaux/package.json @@ -15,7 +15,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.32", + "@tanstack/react-start": "^1.168.33", "ky": "^1.7.4", "msw": "^2.7.0", "react": "^19.0.0", diff --git a/examples/react/start-workos/package.json b/examples/react/start-workos/package.json index a425ddddcf..58e9faaf2b 100644 --- a/examples/react/start-workos/package.json +++ b/examples/react/start-workos/package.json @@ -16,7 +16,7 @@ "@radix-ui/themes": "^3.3.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.32", + "@tanstack/react-start": "^1.168.33", "@workos/authkit-tanstack-react-start": "^0.5.0", "react": "^19.0.0", "react-dom": "^19.0.0" diff --git a/packages/react-start-rsc/CHANGELOG.md b/packages/react-start-rsc/CHANGELOG.md index 4a84a4e80d..82307d6e51 100644 --- a/packages/react-start-rsc/CHANGELOG.md +++ b/packages/react-start-rsc/CHANGELOG.md @@ -1,5 +1,11 @@ # @tanstack/react-start-rsc +## 0.1.32 + +### Patch Changes + +- [#7900](https://github.com/TanStack/router/pull/7900) [`fc83c03`](https://github.com/TanStack/router/commit/fc83c0383f956c3ca02e5e027666c917d7e8b07a) - Raise the `@vitejs/plugin-rsc` peer range to `>=0.5.30`. Versions in `0.5.20 - 0.5.29` suppress client HMR for a route component co-located with a `createServerFn`, fixed upstream in `@vitejs/plugin-rsc@0.5.30`. + ## 0.1.31 ### Patch Changes diff --git a/packages/react-start-rsc/package.json b/packages/react-start-rsc/package.json index 84676aad5e..a00b81fd9f 100644 --- a/packages/react-start-rsc/package.json +++ b/packages/react-start-rsc/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start-rsc", - "version": "0.1.31", + "version": "0.1.32", "description": "React Server Components support for TanStack Start", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-start/CHANGELOG.md b/packages/react-start/CHANGELOG.md index 4ade979ee4..ffb1a760ac 100644 --- a/packages/react-start/CHANGELOG.md +++ b/packages/react-start/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/react-start +## 1.168.33 + +### Patch Changes + +- Updated dependencies [[`fc83c03`](https://github.com/TanStack/router/commit/fc83c0383f956c3ca02e5e027666c917d7e8b07a)]: + - @tanstack/react-start-rsc@0.1.32 + ## 1.168.32 ### Patch Changes diff --git a/packages/react-start/package.json b/packages/react-start/package.json index 7a282aff33..294bedcbfb 100644 --- a/packages/react-start/package.json +++ b/packages/react-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start", - "version": "1.168.32", + "version": "1.168.33", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", From 77ed6d5c3a9878adb1ac50bf2881b243e390812e Mon Sep 17 00:00:00 2001 From: Bhavesh Desale <143746617+bhaveshdesale@users.noreply.github.com> Date: Fri, 31 Jul 2026 02:12:20 +0530 Subject: [PATCH 35/51] fix(eslint-plugin-start): support ESLint 10 peer dependency (#7924) --- packages/eslint-plugin-start/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin-start/package.json b/packages/eslint-plugin-start/package.json index ad999620f6..df48592327 100644 --- a/packages/eslint-plugin-start/package.json +++ b/packages/eslint-plugin-start/package.json @@ -61,7 +61,7 @@ "typescript": "^5.8.2" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.7.0" } } From 7592555b86c968efbc8c817ac0cf6fdae60aabe0 Mon Sep 17 00:00:00 2001 From: Cong-Cong Pan Date: Fri, 31 Jul 2026 05:30:40 +0800 Subject: [PATCH 36/51] fix(start): align Rsbuild SSR asset URLs for css?url imports (#7590) * fix(start): align Rsbuild SSR asset URLs for css?url imports * rm vite-env.d.tst * ci: apply automated fixes * fix: ts type * ci: apply automated fixes * ci: apply automated fixes (attempt 2/3) * fix: docs code --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- .changeset/fair-assets-align.md | 5 + e2e/react-start/rsc-rsbuild/src/env.d.ts | 7 + .../src/node_modules/rsc-client-pkg/index.js | 19 +++ .../rsc-rsbuild/src/routeTree.gen.ts | 24 ++- .../rsc-rsbuild/src/routes/index.tsx | 156 +++++++++++++++++- .../rsc-rsbuild/src/routes/rsc-css-url.css | 30 ++++ .../rsc-rsbuild/src/routes/rsc-css-url.tsx | 24 +++ .../src/routes/rsc-node-module-client.tsx | 79 ++++++++- .../src/utils/RscClientPkgContent.tsx | 76 ++++++++- .../rsc-rsbuild/tests/rsc-css-url.spec.ts | 15 ++ .../start-plugin-core/src/rsbuild/planning.ts | 49 +++--- 11 files changed, 452 insertions(+), 32 deletions(-) create mode 100644 .changeset/fair-assets-align.md create mode 100644 e2e/react-start/rsc-rsbuild/src/env.d.ts create mode 100644 e2e/react-start/rsc-rsbuild/src/routes/rsc-css-url.css create mode 100644 e2e/react-start/rsc-rsbuild/src/routes/rsc-css-url.tsx create mode 100644 e2e/react-start/rsc-rsbuild/tests/rsc-css-url.spec.ts diff --git a/.changeset/fair-assets-align.md b/.changeset/fair-assets-align.md new file mode 100644 index 0000000000..a837187b98 --- /dev/null +++ b/.changeset/fair-assets-align.md @@ -0,0 +1,5 @@ +--- +'@tanstack/start-plugin-core': patch +--- + +Fix Rsbuild SSR asset URLs for `?url` imports by aligning server public asset paths with the client build. diff --git a/e2e/react-start/rsc-rsbuild/src/env.d.ts b/e2e/react-start/rsc-rsbuild/src/env.d.ts new file mode 100644 index 0000000000..8993929b40 --- /dev/null +++ b/e2e/react-start/rsc-rsbuild/src/env.d.ts @@ -0,0 +1,7 @@ +/// + +declare module '*.css' +declare module '*.css?url' { + const url: string + export default url +} diff --git a/e2e/react-start/rsc-rsbuild/src/node_modules/rsc-client-pkg/index.js b/e2e/react-start/rsc-rsbuild/src/node_modules/rsc-client-pkg/index.js index 75080ca08a..066938631b 100644 --- a/e2e/react-start/rsc-rsbuild/src/node_modules/rsc-client-pkg/index.js +++ b/e2e/react-start/rsc-rsbuild/src/node_modules/rsc-client-pkg/index.js @@ -2,6 +2,24 @@ import * as React from 'react' +const buttonStyle = { + display: 'inline-flex', + alignItems: 'center', + justifyContent: 'center', + minHeight: '40px', + padding: '0 16px', + backgroundColor: '#16a34a', + border: '0', + borderRadius: '6px', + boxShadow: '0 1px 2px rgba(15, 23, 42, 0.16)', + color: 'white', + cursor: 'pointer', + fontFamily: + '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', + fontSize: '13px', + fontWeight: 700, +} + export function NodeModuleClientWidget(props) { const [count, setCount] = React.useState(0) @@ -10,6 +28,7 @@ export function NodeModuleClientWidget(props) { { type: 'button', 'data-testid': 'node-module-client-widget', + style: buttonStyle, onClick: () => setCount((value) => value + 1), }, `${props.label}: ${count}`, diff --git a/e2e/react-start/rsc-rsbuild/src/routeTree.gen.ts b/e2e/react-start/rsc-rsbuild/src/routeTree.gen.ts index 80c2e951f9..393ffe98a9 100644 --- a/e2e/react-start/rsc-rsbuild/src/routeTree.gen.ts +++ b/e2e/react-start/rsc-rsbuild/src/routeTree.gen.ts @@ -10,6 +10,7 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' +import { Route as RscCssUrlRouteImport } from './routes/rsc-css-url' import { Route as RscNodeModuleClientRouteImport } from './routes/rsc-node-module-client' const IndexRoute = IndexRouteImport.update({ @@ -17,6 +18,11 @@ const IndexRoute = IndexRouteImport.update({ path: '/', getParentRoute: () => rootRouteImport, } as any) +const RscCssUrlRoute = RscCssUrlRouteImport.update({ + id: '/rsc-css-url', + path: '/rsc-css-url', + getParentRoute: () => rootRouteImport, +} as any) const RscNodeModuleClientRoute = RscNodeModuleClientRouteImport.update({ id: '/rsc-node-module-client', path: '/rsc-node-module-client', @@ -25,27 +31,31 @@ const RscNodeModuleClientRoute = RscNodeModuleClientRouteImport.update({ export interface FileRoutesByFullPath { '/': typeof IndexRoute + '/rsc-css-url': typeof RscCssUrlRoute '/rsc-node-module-client': typeof RscNodeModuleClientRoute } export interface FileRoutesByTo { '/': typeof IndexRoute + '/rsc-css-url': typeof RscCssUrlRoute '/rsc-node-module-client': typeof RscNodeModuleClientRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute + '/rsc-css-url': typeof RscCssUrlRoute '/rsc-node-module-client': typeof RscNodeModuleClientRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' | '/rsc-node-module-client' + fullPaths: '/' | '/rsc-css-url' | '/rsc-node-module-client' fileRoutesByTo: FileRoutesByTo - to: '/' | '/rsc-node-module-client' - id: '__root__' | '/' | '/rsc-node-module-client' + to: '/' | '/rsc-css-url' | '/rsc-node-module-client' + id: '__root__' | '/' | '/rsc-css-url' | '/rsc-node-module-client' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute + RscCssUrlRoute: typeof RscCssUrlRoute RscNodeModuleClientRoute: typeof RscNodeModuleClientRoute } @@ -58,6 +68,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/rsc-css-url': { + id: '/rsc-css-url' + path: '/rsc-css-url' + fullPath: '/rsc-css-url' + preLoaderRoute: typeof RscCssUrlRouteImport + parentRoute: typeof rootRouteImport + } '/rsc-node-module-client': { id: '/rsc-node-module-client' path: '/rsc-node-module-client' @@ -70,6 +87,7 @@ declare module '@tanstack/react-router' { const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, + RscCssUrlRoute: RscCssUrlRoute, RscNodeModuleClientRoute: RscNodeModuleClientRoute, } export const routeTree = rootRouteImport diff --git a/e2e/react-start/rsc-rsbuild/src/routes/index.tsx b/e2e/react-start/rsc-rsbuild/src/routes/index.tsx index d66168b65d..0e2f7f455c 100644 --- a/e2e/react-start/rsc-rsbuild/src/routes/index.tsx +++ b/e2e/react-start/rsc-rsbuild/src/routes/index.tsx @@ -1,14 +1,162 @@ -import { Link, createFileRoute } from '@tanstack/react-router' +import { createFileRoute, Link, linkOptions } from '@tanstack/react-router' +import type { CSSProperties } from 'react' export const Route = createFileRoute('/')({ component: Home, }) +const examples = linkOptions([ + { + to: '/rsc-node-module-client', + title: 'Node module client component', + description: + 'Hydrates a client component imported from node_modules inside an RSC route.', + marker: 'RSC', + markerColor: '#0284c7', + }, + { + to: '/rsc-css-url', + title: 'css?url stylesheet', + description: + 'Applies a stylesheet imported with the ?url query from route head metadata.', + marker: 'CSS', + markerColor: '#16a34a', + }, +]) + +const colors = { + server: '#0284c7', + client: '#16a34a', + async: '#f59e0b', +} + +const styles = { + page: { + maxWidth: '800px', + padding: '20px', + fontFamily: + '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', + }, + title: { + margin: '0 0 8px 0', + color: '#1e293b', + fontSize: '24px', + }, + description: { + color: '#64748b', + lineHeight: '1.5', + marginBottom: '20px', + }, + legend: { + display: 'flex', + gap: '16px', + marginBottom: '20px', + padding: '12px', + backgroundColor: '#f8fafc', + borderRadius: '8px', + flexWrap: 'wrap', + }, + legendItem: { + display: 'flex', + alignItems: 'center', + gap: '6px', + }, + legendText: { + color: '#475569', + fontSize: '13px', + }, + legendColor: { + width: '16px', + height: '16px', + borderRadius: '4px', + }, + grid: { + display: 'grid', + gap: '16px', + gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', + }, + card: { + display: 'block', + padding: '16px', + backgroundColor: '#f8fafc', + borderRadius: '8px', + border: '1px solid #e2e8f0', + textDecoration: 'none', + }, + marker: { + display: 'grid', + placeItems: 'center', + width: '40px', + height: '32px', + marginBottom: '8px', + borderRadius: '6px', + color: 'white', + fontSize: '13px', + fontWeight: 'bold', + }, + cardTitle: { + marginBottom: '4px', + color: '#0f172a', + fontWeight: 'bold', + }, + cardDescription: { + color: '#64748b', + fontSize: '13px', + lineHeight: '1.4', + }, +} satisfies Record + +function legendColor(color: string): CSSProperties { + return { + ...styles.legendColor, + backgroundColor: color, + } +} + +function markerStyle(color: string): CSSProperties { + return { + ...styles.marker, + backgroundColor: color, + } +} + function Home() { return ( -
-

Rsbuild RSC fixture

- Node module client component +
+

+ React Server Components Rsbuild E2E Tests +

+

+ These examples cover Rsbuild-specific RSC behavior with clear visual + distinction between server-rendered content and client-side assets. +

+ +
+
+ + Server Rendered (RSC) +
+
+ + Client Hydration and Assets +
+
+ + Rsbuild SSR Coverage +
+
+ +
) } diff --git a/e2e/react-start/rsc-rsbuild/src/routes/rsc-css-url.css b/e2e/react-start/rsc-rsbuild/src/routes/rsc-css-url.css new file mode 100644 index 0000000000..c6b47196e6 --- /dev/null +++ b/e2e/react-start/rsc-rsbuild/src/routes/rsc-css-url.css @@ -0,0 +1,30 @@ +.rsc-css-url-card { + display: grid; + gap: 12px; + max-width: 480px; + padding: 18px; + background-color: #ecfdf5; + border: 2px solid #10b981; + border-radius: 8px; + color: #064e3b; +} + +.rsc-css-url-badge { + width: max-content; + padding: 3px 8px; + background-color: #047857; + border-radius: 4px; + color: #fff; + font-size: 11px; + font-weight: 700; +} + +.rsc-css-url-title { + margin: 0; + font-size: 18px; +} + +.rsc-css-url-text { + margin: 0; + line-height: 1.5; +} diff --git a/e2e/react-start/rsc-rsbuild/src/routes/rsc-css-url.tsx b/e2e/react-start/rsc-rsbuild/src/routes/rsc-css-url.tsx new file mode 100644 index 0000000000..563e2fb46c --- /dev/null +++ b/e2e/react-start/rsc-rsbuild/src/routes/rsc-css-url.tsx @@ -0,0 +1,24 @@ +import { createFileRoute } from '@tanstack/react-router' +import cssUrl from './rsc-css-url.css?url' + +export const Route = createFileRoute('/rsc-css-url')({ + head: () => ({ + links: [{ rel: 'stylesheet', href: cssUrl }], + }), + component: RscCssUrlComponent, +}) + +function RscCssUrlComponent() { + return ( +
+

RSC css?url stylesheet

+
+ CSS URL +

Stylesheet imported with ?url

+

+ The SSR head should point at a client-served asset URL. +

+
+
+ ) +} diff --git a/e2e/react-start/rsc-rsbuild/src/routes/rsc-node-module-client.tsx b/e2e/react-start/rsc-rsbuild/src/routes/rsc-node-module-client.tsx index 00994ffb50..1bf4e7760b 100644 --- a/e2e/react-start/rsc-rsbuild/src/routes/rsc-node-module-client.tsx +++ b/e2e/react-start/rsc-rsbuild/src/routes/rsc-node-module-client.tsx @@ -1,4 +1,5 @@ -import { createFileRoute } from '@tanstack/react-router' +import { createFileRoute, Link } from '@tanstack/react-router' +import type { CSSProperties } from 'react' import { getNodeModuleClientServerComponent } from '~/utils/nodeModuleClientServerComponent' export const Route = createFileRoute('/rsc-node-module-client')({ @@ -9,14 +10,86 @@ export const Route = createFileRoute('/rsc-node-module-client')({ component: RscNodeModuleClientComponent, }) +const styles = { + page: { + maxWidth: '800px', + padding: '20px', + fontFamily: + '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', + }, + backLink: { + display: 'inline-block', + marginBottom: '16px', + color: '#0284c7', + fontSize: '13px', + fontWeight: 'bold', + textDecoration: 'none', + }, + title: { + margin: '0 0 8px 0', + color: '#1e293b', + fontSize: '24px', + }, + description: { + maxWidth: '680px', + margin: '0 0 20px 0', + color: '#64748b', + lineHeight: '1.5', + }, + summary: { + display: 'grid', + gap: '12px', + gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', + marginBottom: '20px', + }, + summaryItem: { + padding: '12px', + backgroundColor: '#f8fafc', + border: '1px solid #e2e8f0', + borderRadius: '8px', + }, + summaryLabel: { + marginBottom: '4px', + color: '#475569', + fontSize: '12px', + fontWeight: 'bold', + }, + summaryText: { + color: '#0f172a', + fontSize: '13px', + lineHeight: '1.4', + }, +} satisfies Record + function RscNodeModuleClientComponent() { const { Server } = Route.useLoaderData() return ( -
-

+
+ + Back to examples + +

RSC node_modules client component

+

+ This route renders a server component that imports a client component + from a package-style module and hydrates it on the client. +

+
+
+
Server source
+
+ Route loader + RSC server function +
+
+
+
Client source
+
+ Component exported from node_modules +
+
+
{Server}
) diff --git a/e2e/react-start/rsc-rsbuild/src/utils/RscClientPkgContent.tsx b/e2e/react-start/rsc-rsbuild/src/utils/RscClientPkgContent.tsx index 742f6cbc1a..1b22dd9afe 100644 --- a/e2e/react-start/rsc-rsbuild/src/utils/RscClientPkgContent.tsx +++ b/e2e/react-start/rsc-rsbuild/src/utils/RscClientPkgContent.tsx @@ -1,12 +1,82 @@ import { NodeModuleClientWidget } from 'rsc-client-pkg' +import type { CSSProperties } from 'react' + +const styles = { + container: { + padding: '16px', + backgroundColor: '#e0f2fe', + border: '2px solid #0284c7', + borderRadius: '8px', + }, + header: { + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + gap: '12px', + marginBottom: '12px', + }, + badge: { + display: 'inline-block', + padding: '2px 8px', + backgroundColor: '#0284c7', + borderRadius: '4px', + color: 'white', + fontSize: '11px', + fontWeight: 'bold', + }, + meta: { + color: '#64748b', + fontSize: '12px', + }, + title: { + margin: '0 0 8px 0', + color: '#0c4a6e', + fontSize: '18px', + }, + description: { + margin: '0 0 16px 0', + color: '#0369a1', + fontSize: '13px', + lineHeight: '1.5', + }, + clientSlot: { + padding: '14px', + backgroundColor: '#dcfce7', + border: '2px solid #16a34a', + borderRadius: '8px', + }, + clientLabel: { + marginBottom: '10px', + color: '#166534', + fontSize: '11px', + fontWeight: 'bold', + }, +} satisfies Record export function RscClientPkgContent() { return ( -
-

+
+
+ SERVER + Rendered before hydration +
+

Server rendered package boundary

- +

+ The server component owns this blue boundary, then hands the interactive + island below to a client component exported from a module package. +

+
+
CLIENT COMPONENT FROM NODE_MODULES
+ +
) } diff --git a/e2e/react-start/rsc-rsbuild/tests/rsc-css-url.spec.ts b/e2e/react-start/rsc-rsbuild/tests/rsc-css-url.spec.ts new file mode 100644 index 0000000000..377c4e6011 --- /dev/null +++ b/e2e/react-start/rsc-rsbuild/tests/rsc-css-url.spec.ts @@ -0,0 +1,15 @@ +import { expect } from '@playwright/test' +import { test } from '@tanstack/router-e2e-utils' + +test('applies styles imported with css?url during SSR', async ({ page }) => { + const response = await page.goto('/rsc-css-url') + expect(response?.status()).toBe(200) + + const card = page.getByTestId('rsc-css-url-card') + await expect(card).toBeVisible() + + const backgroundColor = await card.evaluate( + (el) => getComputedStyle(el).backgroundColor, + ) + expect(backgroundColor).toBe('rgb(236, 253, 245)') +}) diff --git a/packages/start-plugin-core/src/rsbuild/planning.ts b/packages/start-plugin-core/src/rsbuild/planning.ts index e6518ad2e6..65fbd94024 100644 --- a/packages/start-plugin-core/src/rsbuild/planning.ts +++ b/packages/start-plugin-core/src/rsbuild/planning.ts @@ -31,6 +31,29 @@ export type RsbuildEnvironmentName = (typeof RSBUILD_ENVIRONMENT_NAMES)[keyof typeof RSBUILD_ENVIRONMENT_NAMES] type RsbuildDistPath = NonNullable['distPath'] +type RsbuildDistPathObject = Exclude + +function createPublicAssetDistPath(root: string): RsbuildDistPathObject { + return { + root, + css: `${RSBUILD_CLIENT_ASSETS_DIR}/css`, + cssAsync: `${RSBUILD_CLIENT_ASSETS_DIR}/css/async`, + svg: `${RSBUILD_CLIENT_ASSETS_DIR}/svg`, + font: `${RSBUILD_CLIENT_ASSETS_DIR}/font`, + wasm: `${RSBUILD_CLIENT_ASSETS_DIR}/wasm`, + image: `${RSBUILD_CLIENT_ASSETS_DIR}/image`, + media: `${RSBUILD_CLIENT_ASSETS_DIR}/media`, + assets: `${RSBUILD_CLIENT_ASSETS_DIR}/assets`, + } +} + +function createClientAssetDistPath(root: string): RsbuildDistPathObject { + return { + ...createPublicAssetDistPath(root), + js: `${RSBUILD_CLIENT_ASSETS_DIR}/js`, + jsAsync: `${RSBUILD_CLIENT_ASSETS_DIR}/js/async`, + } +} export interface RsbuildResolvedEntryAliases { client: string @@ -121,19 +144,7 @@ export function createRsbuildEnvironmentPlan(opts: { output: { target: 'web', module: clientOutputModule, - distPath: { - root: opts.clientOutputDirectory, - js: `${RSBUILD_CLIENT_ASSETS_DIR}/js`, - jsAsync: `${RSBUILD_CLIENT_ASSETS_DIR}/js/async`, - css: `${RSBUILD_CLIENT_ASSETS_DIR}/css`, - cssAsync: `${RSBUILD_CLIENT_ASSETS_DIR}/css/async`, - svg: `${RSBUILD_CLIENT_ASSETS_DIR}/svg`, - font: `${RSBUILD_CLIENT_ASSETS_DIR}/font`, - wasm: `${RSBUILD_CLIENT_ASSETS_DIR}/wasm`, - image: `${RSBUILD_CLIENT_ASSETS_DIR}/image`, - media: `${RSBUILD_CLIENT_ASSETS_DIR}/media`, - assets: `${RSBUILD_CLIENT_ASSETS_DIR}/assets`, - }, + distPath: createClientAssetDistPath(opts.clientOutputDirectory), assetPrefix: opts.publicBase, }, resolve: { @@ -171,9 +182,8 @@ export function createRsbuildEnvironmentPlan(opts: { // which requires `--experimental-vm-modules`. Emit CJS for the dev // server bundle so SSR works without extra Node flags. ...(opts.dev ? { module: false } : {}), - distPath: { - root: opts.serverOutputDirectory, - }, + distPath: createPublicAssetDistPath(opts.serverOutputDirectory), + assetPrefix: opts.publicBase, }, resolve: { alias, @@ -208,9 +218,10 @@ export function createRsbuildEnvironmentPlan(opts: { output: { target: 'node', ...(opts.dev ? { module: false } : {}), - distPath: { - root: `${opts.serverOutputDirectory}/${opts.serverFnProviderEnv}`, - }, + distPath: createPublicAssetDistPath( + `${opts.serverOutputDirectory}/${opts.serverFnProviderEnv}`, + ), + assetPrefix: opts.publicBase, }, resolve: { alias, From 8212950807660c88ad49320f6c8d1074abd40944 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2026 00:04:54 +0200 Subject: [PATCH 37/51] ci: Version Packages (#7927) ci: changeset release Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/fair-assets-align.md | 5 ----- examples/react/start-bare/package.json | 2 +- examples/react/start-basic-auth/package.json | 2 +- examples/react/start-basic-authjs/package.json | 2 +- examples/react/start-basic-cloudflare/package.json | 2 +- examples/react/start-basic-react-query/package.json | 2 +- examples/react/start-basic-rsbuild/package.json | 2 +- examples/react/start-basic-static/package.json | 2 +- examples/react/start-basic/package.json | 2 +- examples/react/start-bun/package.json | 2 +- examples/react/start-clerk-basic/package.json | 2 +- examples/react/start-convex-trellaux/package.json | 2 +- examples/react/start-counter/package.json | 2 +- examples/react/start-i18n-paraglide/package.json | 2 +- examples/react/start-large/package.json | 2 +- examples/react/start-material-ui/package.json | 2 +- examples/react/start-rscs/package.json | 2 +- .../package.json | 2 +- examples/react/start-supabase-basic/package.json | 2 +- examples/react/start-tailwind-v4/package.json | 2 +- examples/react/start-trellaux/package.json | 2 +- examples/react/start-workos/package.json | 2 +- examples/solid/start-basic-auth/package.json | 2 +- examples/solid/start-basic-authjs/package.json | 2 +- examples/solid/start-basic-cloudflare/package.json | 2 +- examples/solid/start-basic-netlify/package.json | 2 +- examples/solid/start-basic-nitro/package.json | 2 +- examples/solid/start-basic-solid-query/package.json | 2 +- examples/solid/start-basic-static/package.json | 2 +- examples/solid/start-basic/package.json | 2 +- examples/solid/start-bun/package.json | 2 +- examples/solid/start-convex-better-auth/package.json | 2 +- examples/solid/start-counter/package.json | 2 +- examples/solid/start-i18n-paraglide/package.json | 2 +- examples/solid/start-large/package.json | 2 +- .../package.json | 2 +- examples/solid/start-supabase-basic/package.json | 2 +- examples/solid/start-tailwind-v4/package.json | 2 +- packages/react-start-rsc/CHANGELOG.md | 7 +++++++ packages/react-start-rsc/package.json | 2 +- packages/react-start/CHANGELOG.md | 8 ++++++++ packages/react-start/package.json | 2 +- packages/solid-start/CHANGELOG.md | 7 +++++++ packages/solid-start/package.json | 2 +- packages/start-plugin-core/CHANGELOG.md | 6 ++++++ packages/start-plugin-core/package.json | 2 +- packages/vue-start/CHANGELOG.md | 7 +++++++ packages/vue-start/package.json | 2 +- 48 files changed, 77 insertions(+), 47 deletions(-) delete mode 100644 .changeset/fair-assets-align.md diff --git a/.changeset/fair-assets-align.md b/.changeset/fair-assets-align.md deleted file mode 100644 index a837187b98..0000000000 --- a/.changeset/fair-assets-align.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@tanstack/start-plugin-core': patch ---- - -Fix Rsbuild SSR asset URLs for `?url` imports by aligning server public asset paths with the client build. diff --git a/examples/react/start-bare/package.json b/examples/react/start-bare/package.json index 3bcab115f3..bbda1dcee8 100644 --- a/examples/react/start-bare/package.json +++ b/examples/react/start-bare/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.33", + "@tanstack/react-start": "^1.168.34", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-basic-auth/package.json b/examples/react/start-basic-auth/package.json index 2dc40e4007..fa1048d35c 100644 --- a/examples/react/start-basic-auth/package.json +++ b/examples/react/start-basic-auth/package.json @@ -16,7 +16,7 @@ "@prisma/client": "^7.0.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.33", + "@tanstack/react-start": "^1.168.34", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-basic-authjs/package.json b/examples/react/start-basic-authjs/package.json index abd9ee7c63..6b094f6309 100644 --- a/examples/react/start-basic-authjs/package.json +++ b/examples/react/start-basic-authjs/package.json @@ -13,7 +13,7 @@ "@auth/core": "^0.41.1", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.33", + "@tanstack/react-start": "^1.168.34", "react": "^19.0.0", "react-dom": "^19.0.0", "start-authjs": "^1.0.0", diff --git a/examples/react/start-basic-cloudflare/package.json b/examples/react/start-basic-cloudflare/package.json index b8abd3e495..ffbcdf6c31 100644 --- a/examples/react/start-basic-cloudflare/package.json +++ b/examples/react/start-basic-cloudflare/package.json @@ -14,7 +14,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.33", + "@tanstack/react-start": "^1.168.34", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/examples/react/start-basic-react-query/package.json b/examples/react/start-basic-react-query/package.json index 4c13bd38ef..2102165945 100644 --- a/examples/react/start-basic-react-query/package.json +++ b/examples/react/start-basic-react-query/package.json @@ -15,7 +15,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.33", + "@tanstack/react-start": "^1.168.34", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-basic-rsbuild/package.json b/examples/react/start-basic-rsbuild/package.json index 708aae9a12..5638493cd6 100644 --- a/examples/react/start-basic-rsbuild/package.json +++ b/examples/react/start-basic-rsbuild/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.33", + "@tanstack/react-start": "^1.168.34", "react": "^19.2.3", "react-dom": "^19.2.3", "tailwind-merge": "^3.6.0", diff --git a/examples/react/start-basic-static/package.json b/examples/react/start-basic-static/package.json index b4c38e1b88..8c0fb9624e 100644 --- a/examples/react/start-basic-static/package.json +++ b/examples/react/start-basic-static/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.33", + "@tanstack/react-start": "^1.168.34", "@tanstack/start-static-server-functions": "^1.167.19", "@vitejs/plugin-react": "^6.0.1", "react": "^19.0.0", diff --git a/examples/react/start-basic/package.json b/examples/react/start-basic/package.json index 998ebe1123..410a904a1f 100644 --- a/examples/react/start-basic/package.json +++ b/examples/react/start-basic/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.33", + "@tanstack/react-start": "^1.168.34", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.6.0", diff --git a/examples/react/start-bun/package.json b/examples/react/start-bun/package.json index efe0dddbf2..fd25c97be2 100644 --- a/examples/react/start-bun/package.json +++ b/examples/react/start-bun/package.json @@ -18,7 +18,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.33", + "@tanstack/react-start": "^1.168.34", "@tanstack/router-plugin": "^1.168.23", "react": "^19.1.1", "react-dom": "^19.1.1", diff --git a/examples/react/start-clerk-basic/package.json b/examples/react/start-clerk-basic/package.json index 53cb3d6b84..91415c51ad 100644 --- a/examples/react/start-clerk-basic/package.json +++ b/examples/react/start-clerk-basic/package.json @@ -13,7 +13,7 @@ "@clerk/tanstack-react-start": "^0.27.14", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.33", + "@tanstack/react-start": "^1.168.34", "@vitejs/plugin-react": "^6.0.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/start-convex-trellaux/package.json b/examples/react/start-convex-trellaux/package.json index 8b2918bd3c..6921d6ae39 100644 --- a/examples/react/start-convex-trellaux/package.json +++ b/examples/react/start-convex-trellaux/package.json @@ -18,7 +18,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.33", + "@tanstack/react-start": "^1.168.34", "concurrently": "^8.2.2", "convex": "^1.19.0", "ky": "^1.7.4", diff --git a/examples/react/start-counter/package.json b/examples/react/start-counter/package.json index a55dd56831..e87657bdd9 100644 --- a/examples/react/start-counter/package.json +++ b/examples/react/start-counter/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.33", + "@tanstack/react-start": "^1.168.34", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/examples/react/start-i18n-paraglide/package.json b/examples/react/start-i18n-paraglide/package.json index d3a67b4f3e..d7f40df7aa 100644 --- a/examples/react/start-i18n-paraglide/package.json +++ b/examples/react/start-i18n-paraglide/package.json @@ -12,7 +12,7 @@ "@tanstack/react-devtools": "^0.7.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.33", + "@tanstack/react-start": "^1.168.34", "react": "^19.1.1", "react-dom": "^19.1.1" }, diff --git a/examples/react/start-large/package.json b/examples/react/start-large/package.json index 2414df5c9f..9aa1e9f3d1 100644 --- a/examples/react/start-large/package.json +++ b/examples/react/start-large/package.json @@ -15,7 +15,7 @@ "@tanstack/react-query": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.33", + "@tanstack/react-start": "^1.168.34", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-material-ui/package.json b/examples/react/start-material-ui/package.json index 3b4b72b51a..1244cdc9eb 100644 --- a/examples/react/start-material-ui/package.json +++ b/examples/react/start-material-ui/package.json @@ -17,7 +17,7 @@ "@mui/material": "6.4.7", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.33", + "@tanstack/react-start": "^1.168.34", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-rscs/package.json b/examples/react/start-rscs/package.json index c9371ffe80..370ee45e48 100644 --- a/examples/react/start-rscs/package.json +++ b/examples/react/start-rscs/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.33", + "@tanstack/react-start": "^1.168.34", "dexie": "^4.0.10", "react": "^19.2.0", "react-dom": "^19.2.0", diff --git a/examples/react/start-streaming-data-from-server-functions/package.json b/examples/react/start-streaming-data-from-server-functions/package.json index 60d43b762c..16fb4d5f6b 100644 --- a/examples/react/start-streaming-data-from-server-functions/package.json +++ b/examples/react/start-streaming-data-from-server-functions/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.33", + "@tanstack/react-start": "^1.168.34", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-supabase-basic/package.json b/examples/react/start-supabase-basic/package.json index 0fc9cb8d0e..9977c4de3c 100644 --- a/examples/react/start-supabase-basic/package.json +++ b/examples/react/start-supabase-basic/package.json @@ -17,7 +17,7 @@ "@supabase/supabase-js": "^2.48.1", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.33", + "@tanstack/react-start": "^1.168.34", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/start-tailwind-v4/package.json b/examples/react/start-tailwind-v4/package.json index eecb98664d..bc775c1cc6 100644 --- a/examples/react/start-tailwind-v4/package.json +++ b/examples/react/start-tailwind-v4/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.33", + "@tanstack/react-start": "^1.168.34", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.6.0", diff --git a/examples/react/start-trellaux/package.json b/examples/react/start-trellaux/package.json index 1501362163..d0255faf39 100644 --- a/examples/react/start-trellaux/package.json +++ b/examples/react/start-trellaux/package.json @@ -15,7 +15,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.33", + "@tanstack/react-start": "^1.168.34", "ky": "^1.7.4", "msw": "^2.7.0", "react": "^19.0.0", diff --git a/examples/react/start-workos/package.json b/examples/react/start-workos/package.json index 58e9faaf2b..7cc0dc743a 100644 --- a/examples/react/start-workos/package.json +++ b/examples/react/start-workos/package.json @@ -16,7 +16,7 @@ "@radix-ui/themes": "^3.3.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.33", + "@tanstack/react-start": "^1.168.34", "@workos/authkit-tanstack-react-start": "^0.5.0", "react": "^19.0.0", "react-dom": "^19.0.0" diff --git a/examples/solid/start-basic-auth/package.json b/examples/solid/start-basic-auth/package.json index d4f6e9b4d8..1b3452d83d 100644 --- a/examples/solid/start-basic-auth/package.json +++ b/examples/solid/start-basic-auth/package.json @@ -16,7 +16,7 @@ "@prisma/client": "^7.0.0", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.32", + "@tanstack/solid-start": "^1.168.33", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-authjs/package.json b/examples/solid/start-basic-authjs/package.json index 9970bb87ae..6d0e17c9f5 100644 --- a/examples/solid/start-basic-authjs/package.json +++ b/examples/solid/start-basic-authjs/package.json @@ -13,7 +13,7 @@ "@auth/core": "^0.41.1", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.32", + "@tanstack/solid-start": "^1.168.33", "solid-js": "^1.9.10", "start-authjs": "^1.0.0", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-cloudflare/package.json b/examples/solid/start-basic-cloudflare/package.json index 856beea675..44eebcc29d 100644 --- a/examples/solid/start-basic-cloudflare/package.json +++ b/examples/solid/start-basic-cloudflare/package.json @@ -14,7 +14,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.32", + "@tanstack/solid-start": "^1.168.33", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-netlify/package.json b/examples/solid/start-basic-netlify/package.json index c1bf2b569a..b8cb0df10a 100644 --- a/examples/solid/start-basic-netlify/package.json +++ b/examples/solid/start-basic-netlify/package.json @@ -11,7 +11,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.32", + "@tanstack/solid-start": "^1.168.33", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-nitro/package.json b/examples/solid/start-basic-nitro/package.json index 9765bf2aaa..77c306e3ad 100644 --- a/examples/solid/start-basic-nitro/package.json +++ b/examples/solid/start-basic-nitro/package.json @@ -11,7 +11,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.32", + "@tanstack/solid-start": "^1.168.33", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-solid-query/package.json b/examples/solid/start-basic-solid-query/package.json index fba0fe388a..dbf5022c72 100644 --- a/examples/solid/start-basic-solid-query/package.json +++ b/examples/solid/start-basic-solid-query/package.json @@ -15,7 +15,7 @@ "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@tanstack/solid-router-ssr-query": "^1.167.1", - "@tanstack/solid-start": "^1.168.32", + "@tanstack/solid-start": "^1.168.33", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-static/package.json b/examples/solid/start-basic-static/package.json index 53ee96046a..73d4fc48e5 100644 --- a/examples/solid/start-basic-static/package.json +++ b/examples/solid/start-basic-static/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.32", + "@tanstack/solid-start": "^1.168.33", "@tanstack/start-static-server-functions": "^1.167.19", "redaxios": "^0.5.1", "solid-js": "^1.9.10", diff --git a/examples/solid/start-basic/package.json b/examples/solid/start-basic/package.json index 774414336c..325f4980b2 100644 --- a/examples/solid/start-basic/package.json +++ b/examples/solid/start-basic/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.32", + "@tanstack/solid-start": "^1.168.33", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-bun/package.json b/examples/solid/start-bun/package.json index 595b7cd477..30fe57b408 100644 --- a/examples/solid/start-bun/package.json +++ b/examples/solid/start-bun/package.json @@ -18,7 +18,7 @@ "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@tanstack/solid-router-ssr-query": "^1.167.1", - "@tanstack/solid-start": "^1.168.32", + "@tanstack/solid-start": "^1.168.33", "@tanstack/router-plugin": "^1.168.23", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" diff --git a/examples/solid/start-convex-better-auth/package.json b/examples/solid/start-convex-better-auth/package.json index 28b8a36262..f3243c4e69 100644 --- a/examples/solid/start-convex-better-auth/package.json +++ b/examples/solid/start-convex-better-auth/package.json @@ -15,7 +15,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.32", + "@tanstack/solid-start": "^1.168.33", "better-auth": "^1.6.19", "clsx": "^2.1.1", "convex": "^1.28.2", diff --git a/examples/solid/start-counter/package.json b/examples/solid/start-counter/package.json index 103cc1be4b..1095a35974 100644 --- a/examples/solid/start-counter/package.json +++ b/examples/solid/start-counter/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.32", + "@tanstack/solid-start": "^1.168.33", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", diff --git a/examples/solid/start-i18n-paraglide/package.json b/examples/solid/start-i18n-paraglide/package.json index 83b3288fe1..4c1bf1c563 100644 --- a/examples/solid/start-i18n-paraglide/package.json +++ b/examples/solid/start-i18n-paraglide/package.json @@ -12,7 +12,7 @@ "@tanstack/solid-devtools": "^0.7.0", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.32", + "@tanstack/solid-start": "^1.168.33", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-large/package.json b/examples/solid/start-large/package.json index 70f5c13e9c..2123824b04 100644 --- a/examples/solid/start-large/package.json +++ b/examples/solid/start-large/package.json @@ -15,7 +15,7 @@ "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.32", + "@tanstack/solid-start": "^1.168.33", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", diff --git a/examples/solid/start-streaming-data-from-server-functions/package.json b/examples/solid/start-streaming-data-from-server-functions/package.json index 3bb06dd2ba..47e9eeb799 100644 --- a/examples/solid/start-streaming-data-from-server-functions/package.json +++ b/examples/solid/start-streaming-data-from-server-functions/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.32", + "@tanstack/solid-start": "^1.168.33", "solid-js": "^1.9.10", "zod": "^4.4.3" }, diff --git a/examples/solid/start-supabase-basic/package.json b/examples/solid/start-supabase-basic/package.json index 6811a56745..7190cbb582 100644 --- a/examples/solid/start-supabase-basic/package.json +++ b/examples/solid/start-supabase-basic/package.json @@ -16,7 +16,7 @@ "@supabase/supabase-js": "^2.48.1", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.32", + "@tanstack/solid-start": "^1.168.33", "solid-js": "^1.9.9", "redaxios": "^0.5.1" }, diff --git a/examples/solid/start-tailwind-v4/package.json b/examples/solid/start-tailwind-v4/package.json index f54d822c40..861e8e28a4 100644 --- a/examples/solid/start-tailwind-v4/package.json +++ b/examples/solid/start-tailwind-v4/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.32", + "@tanstack/solid-start": "^1.168.33", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", "zod": "^4.4.3" diff --git a/packages/react-start-rsc/CHANGELOG.md b/packages/react-start-rsc/CHANGELOG.md index 82307d6e51..495ebd4ae9 100644 --- a/packages/react-start-rsc/CHANGELOG.md +++ b/packages/react-start-rsc/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/react-start-rsc +## 0.1.33 + +### Patch Changes + +- Updated dependencies [[`7592555`](https://github.com/TanStack/router/commit/7592555b86c968efbc8c817ac0cf6fdae60aabe0)]: + - @tanstack/start-plugin-core@1.171.25 + ## 0.1.32 ### Patch Changes diff --git a/packages/react-start-rsc/package.json b/packages/react-start-rsc/package.json index a00b81fd9f..ac3110104f 100644 --- a/packages/react-start-rsc/package.json +++ b/packages/react-start-rsc/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start-rsc", - "version": "0.1.32", + "version": "0.1.33", "description": "React Server Components support for TanStack Start", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-start/CHANGELOG.md b/packages/react-start/CHANGELOG.md index ffb1a760ac..8ae3c51a6b 100644 --- a/packages/react-start/CHANGELOG.md +++ b/packages/react-start/CHANGELOG.md @@ -1,5 +1,13 @@ # @tanstack/react-start +## 1.168.34 + +### Patch Changes + +- Updated dependencies [[`7592555`](https://github.com/TanStack/router/commit/7592555b86c968efbc8c817ac0cf6fdae60aabe0)]: + - @tanstack/start-plugin-core@1.171.25 + - @tanstack/react-start-rsc@0.1.33 + ## 1.168.33 ### Patch Changes diff --git a/packages/react-start/package.json b/packages/react-start/package.json index 294bedcbfb..11593567d2 100644 --- a/packages/react-start/package.json +++ b/packages/react-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start", - "version": "1.168.33", + "version": "1.168.34", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/solid-start/CHANGELOG.md b/packages/solid-start/CHANGELOG.md index 8a43a6c5f9..2f954ccfd4 100644 --- a/packages/solid-start/CHANGELOG.md +++ b/packages/solid-start/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/solid-start +## 1.168.33 + +### Patch Changes + +- Updated dependencies [[`7592555`](https://github.com/TanStack/router/commit/7592555b86c968efbc8c817ac0cf6fdae60aabe0)]: + - @tanstack/start-plugin-core@1.171.25 + ## 1.168.32 ### Patch Changes diff --git a/packages/solid-start/package.json b/packages/solid-start/package.json index 122def43c4..a5ad74a583 100644 --- a/packages/solid-start/package.json +++ b/packages/solid-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-start", - "version": "1.168.32", + "version": "1.168.33", "description": "Modern and scalable routing for Solid applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-plugin-core/CHANGELOG.md b/packages/start-plugin-core/CHANGELOG.md index 3cc5088ed3..87f4faceb8 100644 --- a/packages/start-plugin-core/CHANGELOG.md +++ b/packages/start-plugin-core/CHANGELOG.md @@ -1,5 +1,11 @@ # @tanstack/start-plugin-core +## 1.171.25 + +### Patch Changes + +- [#7590](https://github.com/TanStack/router/pull/7590) [`7592555`](https://github.com/TanStack/router/commit/7592555b86c968efbc8c817ac0cf6fdae60aabe0) - Fix Rsbuild SSR asset URLs for `?url` imports by aligning server public asset paths with the client build. + ## 1.171.24 ### Patch Changes diff --git a/packages/start-plugin-core/package.json b/packages/start-plugin-core/package.json index e8a6d5a295..1855434753 100644 --- a/packages/start-plugin-core/package.json +++ b/packages/start-plugin-core/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-plugin-core", - "version": "1.171.24", + "version": "1.171.25", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/vue-start/CHANGELOG.md b/packages/vue-start/CHANGELOG.md index dd4a7e7233..ffd425ce27 100644 --- a/packages/vue-start/CHANGELOG.md +++ b/packages/vue-start/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/vue-start +## 1.168.32 + +### Patch Changes + +- Updated dependencies [[`7592555`](https://github.com/TanStack/router/commit/7592555b86c968efbc8c817ac0cf6fdae60aabe0)]: + - @tanstack/start-plugin-core@1.171.25 + ## 1.168.31 ### Patch Changes diff --git a/packages/vue-start/package.json b/packages/vue-start/package.json index 462dfc453f..a7dd6292d0 100644 --- a/packages/vue-start/package.json +++ b/packages/vue-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/vue-start", - "version": "1.168.31", + "version": "1.168.32", "description": "Modern and scalable routing for Vue applications", "author": "Tanner Linsley", "license": "MIT", From 03699c4c020c46d72fed26f0f86c2e6ff2169f24 Mon Sep 17 00:00:00 2001 From: Nicolas Beaussart Date: Sat, 1 Aug 2026 13:39:33 +0200 Subject: [PATCH 38/51] chore(ci): improve Nx sandbox cache declarations (#7833) * chore(ci): establish CI timing baseline * faster maybe ? * chore(ci): declare sandbox task inputs and outputs * chore(ci): declare remaining build sandbox inputs * another batch * try fixing e2e import protection * more sandbox fixes * fix(e2e): isolate import protection artifacts * fix script * fix import protection * tune even more * tune even more with some global ignore * ok, anohter try * fix(ci): avoid reading captured build logs * one last improvement for tonight --- .nx/workflows/dynamic-changesets.yaml | 2 +- .nx/workflows/sandboxing-config.yaml | 9 +++ .../import-protection/package.json | 12 ++-- .../import-protection/playwright.config.ts | 11 +++- .../tests/import-protection.spec.ts | 8 ++- .../tests/violations.setup.ts | 22 ++++--- .../tests/violations.utils.ts | 9 +++ nx.json | 64 +++++++++++++++---- scripts/nx/playwright-plugin.ts | 59 ++++++++++++++--- 9 files changed, 156 insertions(+), 40 deletions(-) create mode 100644 .nx/workflows/sandboxing-config.yaml diff --git a/.nx/workflows/dynamic-changesets.yaml b/.nx/workflows/dynamic-changesets.yaml index 786111053d..39d858d5b2 100644 --- a/.nx/workflows/dynamic-changesets.yaml +++ b/.nx/workflows/dynamic-changesets.yaml @@ -1,4 +1,4 @@ distribute-on: small-changeset: 4 linux-medium-js medium-changeset: 8 linux-medium-js - large-changeset: 14 linux-medium-js + large-changeset: 16 linux-medium-js diff --git a/.nx/workflows/sandboxing-config.yaml b/.nx/workflows/sandboxing-config.yaml new file mode 100644 index 0000000000..4fa2634bc3 --- /dev/null +++ b/.nx/workflows/sandboxing-config.yaml @@ -0,0 +1,9 @@ +exclude-reads: + - e2e/.gitignore + - '**/src/routeTree.gen.ts' +exclude-writes: + - '**/src/routeTree.gen.ts' +task-exclusions: + - target: test:build + exclude-reads: + - LICENSE diff --git a/e2e/react-start/import-protection/package.json b/e2e/react-start/import-protection/package.json index 2d3010470e..5459b84437 100644 --- a/e2e/react-start/import-protection/package.json +++ b/e2e/react-start/import-protection/package.json @@ -11,10 +11,7 @@ "build": "pnpm build:vite", "build:vite": "vite build && tsc --noEmit", "build:rsbuild": "rsbuild build && tsc --noEmit", - "start": "node server.js", - "test:e2e:mockMode": "rm -rf node_modules/.vite; rm -rf port*.txt; playwright test --project=chromium", - "test:e2e:errorMode": "rm -rf node_modules/.vite; rm -rf port*.txt; BEHAVIOR=error playwright test --project=chromium", - "test:e2e": "pnpm run test:e2e:mockMode && pnpm run test:e2e:errorMode" + "start": "node server.js" }, "dependencies": { "@tanstack/react-router": "workspace:^", @@ -58,6 +55,7 @@ "toolchain": "vite", "mode": "ssr", "env": { + "E2E_BUILD_LOG": "webserver-build.vite-ssr.log", "E2E_VITE_BUNDLED_DEV": "false" } }, @@ -66,12 +64,16 @@ "mode": "ssr", "name": "bundled-dev", "env": { + "E2E_BUILD_LOG": "webserver-build.vite-ssr-bundled-dev.log", "E2E_VITE_BUNDLED_DEV": "true" } }, { "toolchain": "rsbuild", - "mode": "ssr" + "mode": "ssr", + "env": { + "E2E_BUILD_LOG": "webserver-build.rsbuild-ssr.log" + } } ] } diff --git a/e2e/react-start/import-protection/playwright.config.ts b/e2e/react-start/import-protection/playwright.config.ts index ac239f36fe..778e865125 100644 --- a/e2e/react-start/import-protection/playwright.config.ts +++ b/e2e/react-start/import-protection/playwright.config.ts @@ -1,6 +1,7 @@ import { defineConfig, devices } from '@playwright/test' import { getTestServerPort } from '@tanstack/router-e2e-utils' import { isErrorMode } from './tests/utils/isErrorMode' +import { getViolationArtifactName } from './tests/violations.utils' import packageJson from './package.json' with { type: 'json' } const toolchain = process.env.E2E_TOOLCHAIN ?? 'vite' @@ -11,8 +12,12 @@ const e2ePortKey = const distDir = process.env.E2E_DIST_DIR ?? 'dist' const PORT = await getTestServerPort(e2ePortKey) const baseURL = `http://localhost:${PORT}` -const buildCommand = - toolchain === 'rsbuild' ? 'pnpm build:rsbuild' : 'pnpm build:vite' +const violationArtifacts = [ + getViolationArtifactName('build'), + getViolationArtifactName('dev'), + getViolationArtifactName('dev.cold'), + getViolationArtifactName('dev.warm'), +].join(' ') console.log('running in error mode:', isErrorMode.toString()) @@ -36,7 +41,7 @@ export default defineConfig({ ? {} : { webServer: { - command: `rm -f webserver-build.log webserver-dev.log violations.build.json violations.dev.json && VITE_SERVER_PORT=${PORT} PORT=${PORT} ${buildCommand} > webserver-build.log 2>&1 && PORT=${PORT} VITE_SERVER_PORT=${PORT} pnpm start`, + command: `rm -f webserver-dev.log ${violationArtifacts} && PORT=${PORT} VITE_SERVER_PORT=${PORT} pnpm start`, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', diff --git a/e2e/react-start/import-protection/tests/import-protection.spec.ts b/e2e/react-start/import-protection/tests/import-protection.spec.ts index cdc9aea32c..16b5b073eb 100644 --- a/e2e/react-start/import-protection/tests/import-protection.spec.ts +++ b/e2e/react-start/import-protection/tests/import-protection.spec.ts @@ -2,6 +2,7 @@ import path from 'node:path' import fs from 'node:fs' import { expect } from '@playwright/test' import { test } from '@tanstack/router-e2e-utils' +import { getViolationArtifactName } from './violations.utils' import type { Violation } from './violations.utils' import type { Page } from '@playwright/test' @@ -37,7 +38,7 @@ function normalizeKeyPath(value: string): string { async function readViolations( type: 'build' | 'dev' | 'dev.cold' | 'dev.warm', ): Promise> { - const filename = `violations.${type}.json` + const filename = getViolationArtifactName(type) const violationsPath = path.resolve(import.meta.dirname, '..', filename) const mod = await import(violationsPath, { with: { type: 'json' }, @@ -161,7 +162,8 @@ test('client-only violations route loads in mock mode', async ({ page }) => { }) for (const mode of ['build', 'dev'] as const) { - test(`violations.${mode}.json is written during ${mode}`, async () => { + const artifactName = getViolationArtifactName(mode) + test(`${artifactName} is written during ${mode}`, async () => { const violations = await readViolations(mode) expect(violations.length).toBeGreaterThan(0) }) @@ -204,7 +206,7 @@ test('build log does not contain mock-edge missing export warnings', () => { const buildLogPath = path.resolve( import.meta.dirname, '..', - 'webserver-build.log', + process.env.E2E_BUILD_LOG ?? 'webserver-build.log', ) if (!fs.existsSync(buildLogPath)) { diff --git a/e2e/react-start/import-protection/tests/violations.setup.ts b/e2e/react-start/import-protection/tests/violations.setup.ts index 02796227a7..2f863b398e 100644 --- a/e2e/react-start/import-protection/tests/violations.setup.ts +++ b/e2e/react-start/import-protection/tests/violations.setup.ts @@ -5,7 +5,10 @@ import { chromium } from '@playwright/test' import { getTestServerPort } from '@tanstack/router-e2e-utils' import packageJson from '../package.json' with { type: 'json' } -import { extractViolationsFromLog } from './violations.utils' +import { + extractViolationsFromLog, + getViolationArtifactName, +} from './violations.utils' import type { FullConfig } from '@playwright/test' import type { Violation } from './violations.utils' @@ -250,11 +253,11 @@ async function captureDevViolations(cwd: string): Promise { ) fs.writeFileSync( - path.resolve(cwd, 'violations.dev.json'), + path.resolve(cwd, getViolationArtifactName('dev')), JSON.stringify(coldViolations, null, 2), ) fs.writeFileSync( - path.resolve(cwd, 'violations.dev.cold.json'), + path.resolve(cwd, getViolationArtifactName('dev.cold')), JSON.stringify(coldViolations, null, 2), ) @@ -265,7 +268,7 @@ async function captureDevViolations(cwd: string): Promise { ) fs.writeFileSync( - path.resolve(cwd, 'violations.dev.warm.json'), + path.resolve(cwd, getViolationArtifactName('dev.warm')), JSON.stringify(warmViolations, null, 2), ) } @@ -275,19 +278,22 @@ export default async function globalSetup(config: FullConfig) { // This file lives in ./tests; fixture root is one directory up. const cwd = path.resolve(import.meta.dirname, '..') - // webServer.command writes build output to this file. - const logFile = path.resolve(cwd, 'webserver-build.log') + // The Nx build dependency writes build output to this file. + const logFile = path.resolve( + cwd, + process.env.E2E_BUILD_LOG ?? 'webserver-build.log', + ) if (!fs.existsSync(logFile)) { // If the log doesn't exist, leave an empty violations file. - fs.writeFileSync(path.resolve(cwd, 'violations.build.json'), '[]') + fs.writeFileSync(path.resolve(cwd, getViolationArtifactName('build')), '[]') return } const text = fs.readFileSync(logFile, 'utf-8') const violations = extractViolationsFromLog(text) fs.writeFileSync( - path.resolve(cwd, 'violations.build.json'), + path.resolve(cwd, getViolationArtifactName('build')), JSON.stringify(violations, null, 2), ) diff --git a/e2e/react-start/import-protection/tests/violations.utils.ts b/e2e/react-start/import-protection/tests/violations.utils.ts index c08a9b7a82..006c51543b 100644 --- a/e2e/react-start/import-protection/tests/violations.utils.ts +++ b/e2e/react-start/import-protection/tests/violations.utils.ts @@ -21,6 +21,15 @@ export type Violation = { message?: string } +export type ViolationArtifact = 'build' | 'dev' | 'dev.cold' | 'dev.warm' + +export function getViolationArtifactName(artifact: ViolationArtifact): string { + const modeKey = process.env.E2E_MODE_KEY + return modeKey + ? `violations.${modeKey}.${artifact}.json` + : `violations.${artifact}.json` +} + export function stripAnsi(input: string): string { // eslint-disable-next-line no-control-regex return input.replace(/\u001b\[[0-9;]*m/g, '') diff --git a/nx.json b/nx.json index 08e7563642..764112a2f6 100644 --- a/nx.json +++ b/nx.json @@ -9,10 +9,18 @@ }, "namedInputs": { "sharedGlobals": [ + "{workspaceRoot}/.npmrc", + "{workspaceRoot}/.nx/workflows/sandboxing-config.yaml", "{workspaceRoot}/.nvmrc", "{workspaceRoot}/package.json", "{workspaceRoot}/tsconfig.json" ], + "dependentTaskOutputs": [ + { + "dependentTasksOutputFiles": "**/*", + "transitive": true + } + ], "default": [ "sharedGlobals", "{projectRoot}/**/*", @@ -22,7 +30,8 @@ "default", "!{projectRoot}/tests/**/*", "!{projectRoot}/eslint.config.js" - ] + ], + "buildProduction": ["sharedGlobals", "{projectRoot}/**/*"] }, "targetDefaults": { "test:docs": { @@ -31,19 +40,34 @@ }, "test:eslint": { "cache": true, - "dependsOn": ["^build"], - "inputs": ["default", "^production", "{workspaceRoot}/eslint.config.js"] + "dependsOn": ["^build", "build"], + "inputs": [ + "default", + "^production", + "{workspaceRoot}/eslint.config.js", + "dependentTaskOutputs" + ] }, "test:unit": { "cache": true, "dependsOn": ["^build"], - "inputs": ["default", "^production"], + "inputs": [ + "default", + "^production", + "{workspaceRoot}/eslint.config.js", + "dependentTaskOutputs" + ], "outputs": ["{projectRoot}/coverage"] }, "test:e2e": { "cache": true, "dependsOn": ["^build", "test:e2e--*"], - "inputs": ["default", "^production"] + "inputs": ["default", "^production", "dependentTaskOutputs"], + "outputs": [ + "{projectRoot}/dist", + "{projectRoot}/port-*.txt", + "{projectRoot}/test-results" + ] }, "test:e2e:nitro": { "cache": true, @@ -62,24 +86,40 @@ }, "test:types": { "cache": true, - "dependsOn": ["^build"], - "inputs": ["default", "^production"] + "dependsOn": ["^build", "build"], + "inputs": [ + "default", + "^production", + "{workspaceRoot}/eslint.config.js", + "dependentTaskOutputs" + ] }, "test:types:ssr": { "cache": true, - "dependsOn": ["^build"], - "inputs": ["default", "^production"] + "dependsOn": ["^build", "build"], + "inputs": [ + "default", + "^production", + "{workspaceRoot}/benchmarks/ssr/bench-utils.ts", + "dependentTaskOutputs" + ] }, "build": { "cache": true, "dependsOn": ["^build"], - "inputs": ["production", "^production"], - "outputs": ["{projectRoot}/build", "{projectRoot}/dist"] + "inputs": ["buildProduction", "^buildProduction", "dependentTaskOutputs"], + "outputs": [ + "{projectRoot}/.netlify", + "{projectRoot}/.wrangler", + "{projectRoot}/.output", + "{projectRoot}/build", + "{projectRoot}/dist" + ] }, "test:build": { "cache": true, "dependsOn": ["build"], - "inputs": ["production"] + "inputs": ["buildProduction", "dependentTaskOutputs"] } }, "plugins": [ diff --git a/scripts/nx/playwright-plugin.ts b/scripts/nx/playwright-plugin.ts index d02276d269..675b976613 100644 --- a/scripts/nx/playwright-plugin.ts +++ b/scripts/nx/playwright-plugin.ts @@ -90,10 +90,15 @@ function createNodesInternal( const CI_TARGET_NAME = 'test:e2e' const MODE_TARGET_SEPARATOR = '--' -const TEST_INPUTS: TargetConfiguration['inputs'] = ['default', '^production'] +const TEST_INPUTS: TargetConfiguration['inputs'] = [ + 'default', + 'dependentTaskOutputs', + '^buildProduction', +] const BUILD_INPUTS: TargetConfiguration['inputs'] = [ - 'production', - '^production', + 'buildProduction', + 'dependentTaskOutputs', + '^buildProduction', ] const PLAYWRIGHT_TOOLCHAINS = ['vite', 'rsbuild'] as const @@ -107,6 +112,33 @@ const PLAYWRIGHT_BUILD_COMMANDS: Record = { rsbuild: 'rsbuild build && tsc --noEmit', } +function captureCommandOutput(command: string, outputFile?: string): string { + if (!outputFile) { + return command + } + + if (!/^[a-zA-Z0-9._-]+$/.test(outputFile)) { + throw new Error( + `[Playwright Sharding Plugin] Invalid E2E_BUILD_LOG value: ${outputFile}. ` + + `Expected a filename containing only letters, numbers, dots, dashes, and underscores.`, + ) + } + + const escapedCommand = command.replaceAll("'", "'\\''") + return `bash -o pipefail -c '{ ${escapedCommand}; } 2>&1 | tee ${outputFile}'` +} + +function getTestOutputs( + modeKey: string, + portKey: string, +): TargetConfiguration['outputs'] { + return [ + `{projectRoot}/port-${portKey}*.txt`, + `{projectRoot}/test-results/${portKey}`, + `{projectRoot}/violations.${modeKey}.*.json`, + ] +} + type PlaywrightModeMetadata = { toolchain: PlaywrightToolchain mode: PlaywrightMode @@ -231,22 +263,28 @@ function buildModeTargets( const buildTargetName = `build:${modeMetadata.toolchain}:${modeMetadata.mode}${modeMetadata.name ? `:${modeMetadata.name}` : ''}` const shardCount = modeMetadata.shards ?? 1 const distDir = `dist-${modeMetadata.toolchain}-${modeMetadata.mode}${variantPathSuffix}` + const modeKey = `${modeMetadata.toolchain}-${modeMetadata.mode}${variantPathSuffix}` const modeEnv = { ...modeMetadata.env, MODE: modeMetadata.mode, TOOLCHAIN: modeMetadata.toolchain, E2E_TOOLCHAIN: modeMetadata.toolchain, + E2E_MODE_KEY: modeKey, E2E_DIST: distDir, E2E_DIST_DIR: distDir, } - const modePortKey = `${packageName}-${modeMetadata.toolchain}-${modeMetadata.mode}${variantPathSuffix}` + const buildLog = modeEnv.E2E_BUILD_LOG + const modePortKey = `${packageName}-${modeKey}-e2e` const modeDescription = `${modeMetadata.toolchain}/${modeMetadata.mode}${modeMetadata.name ? `/${modeMetadata.name}` : ''}` const modeShardTargets: Array = [] targets[buildTargetName] = { executor: 'nx:run-commands', options: { - command: PLAYWRIGHT_BUILD_COMMANDS[modeMetadata.toolchain], + command: captureCommandOutput( + PLAYWRIGHT_BUILD_COMMANDS[modeMetadata.toolchain], + buildLog, + ), cwd: projectRoot, env: modeEnv, }, @@ -254,7 +292,10 @@ function buildModeTargets( cache: true, inputs: BUILD_INPUTS, dependsOn: ['^build'], - outputs: [`{projectRoot}/${distDir}`], + outputs: [ + `{projectRoot}/${distDir}`, + ...(buildLog ? [`{projectRoot}/${buildLog}`] : []), + ], metadata: { technologies: ['playwright'], description: `Build artifacts for ${modeDescription} e2e tests`, @@ -266,7 +307,7 @@ function buildModeTargets( targets[modeTargetName] = { executor: 'nx:run-commands', options: { - command: 'playwright test --project=chromium', + command: `playwright test --project=chromium --output=test-results/${modePortKey}`, cwd: projectRoot, env: { ...modeEnv, @@ -275,6 +316,7 @@ function buildModeTargets( }, cache: true, inputs: TEST_INPUTS, + outputs: getTestOutputs(modeKey, modePortKey), dependsOn: [buildTargetName], metadata: { technologies: ['playwright'], @@ -290,7 +332,7 @@ function buildModeTargets( targets[shardTargetName] = { executor: 'nx:run-commands', options: { - command: `playwright test --project=chromium --shard=${shardIndex}/${shardCount}`, + command: `playwright test --project=chromium --shard=${shardIndex}/${shardCount} --output=test-results/${shardPortKey}`, cwd: projectRoot, env: { ...modeEnv, @@ -299,6 +341,7 @@ function buildModeTargets( }, cache: true, inputs: TEST_INPUTS, + outputs: getTestOutputs(modeKey, shardPortKey), dependsOn: [buildTargetName], metadata: { technologies: ['playwright'], From 2cb221cfd3b95f55498b22e76e9ac96a32cd26d4 Mon Sep 17 00:00:00 2001 From: Ahmed Sabry <161619341+ahmedsabryfr@users.noreply.github.com> Date: Sat, 1 Aug 2026 15:15:16 +0300 Subject: [PATCH 39/51] Fix typo in path-params documentation (#7931) --- docs/router/guide/path-params.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/router/guide/path-params.md b/docs/router/guide/path-params.md index b1ae4f5d2f..fa8468ef48 100644 --- a/docs/router/guide/path-params.md +++ b/docs/router/guide/path-params.md @@ -226,7 +226,7 @@ function PostComponent() { -You can even combines prefixes with wildcard routes to create more complex patterns: +You can even combine prefixes with wildcard routes to create more complex patterns: From b5e540d74ae22f29bc17daee965cfe65d2782c7c Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Mon, 3 Aug 2026 21:03:29 +0200 Subject: [PATCH 40/51] docs: use the dynamic README header endpoint (#7940) * docs: use the dynamic README header endpoint * docs: add README header banners to the framework adapter packages --- README.md | 36 +++++++++++++++++----- media/header_router.png | Bin 144737 -> 0 bytes media/header_start.png | Bin 138225 -> 0 bytes packages/react-router-devtools/README.md | 17 ++++++++++ packages/react-router-ssr-query/README.md | 16 +++++++++- packages/react-router/README.md | 16 +++++++++- packages/react-start/README.md | 16 +++++++++- packages/solid-router-devtools/README.md | 17 ++++++++++ packages/solid-router-ssr-query/README.md | 16 +++++++++- packages/solid-router/README.md | 16 +++++++++- packages/solid-start/README.md | 16 +++++++++- packages/vue-router-devtools/README.md | 17 ++++++++++ packages/vue-router-ssr-query/README.md | 17 ++++++++++ packages/vue-router/README.md | 16 +++++++++- 14 files changed, 201 insertions(+), 15 deletions(-) delete mode 100644 media/header_router.png delete mode 100644 media/header_start.png diff --git a/README.md b/README.md index 8dd7deef41..736715b12a 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,20 @@ - + + + + TanStack Router + ## TanStack Router @@ -23,10 +33,20 @@ A modern router designed for type safety, data‑driven navigation, and seamless - + + + + TanStack Start + ## TanStack Start diff --git a/media/header_router.png b/media/header_router.png deleted file mode 100644 index 5f2d58134d23d0489fbd5054d780c0dcfe9bd135..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 144737 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

-oQNYJuTCaFNW*#K1*>`0o^= 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 z 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

2+Trenk>Hbxj1`l`6-A2y<;-?x$Z_Y-AyBWc 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%0DnIap!(f1+RI7L^9bprKo+Ic4 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 wG=7K1@6h-i8h`xxyITHTE&u +

    + + + + TanStack React Router + +
    # TanStack React Router Devtools See https://tanstack.com/router/latest/docs/framework/react/devtools diff --git a/packages/react-router-ssr-query/README.md b/packages/react-router-ssr-query/README.md index 0a19fd3b81..c3a60d1008 100644 --- a/packages/react-router-ssr-query/README.md +++ b/packages/react-router-ssr-query/README.md @@ -2,7 +2,21 @@ # TanStack React Router -![TanStack Router Header](https://raw.githubusercontent.com/TanStack/router/main/media/header_router.png) + + + + TanStack React Router + 🤖 Type-safe router w/ built-in caching & URL state management for React! diff --git a/packages/react-router/README.md b/packages/react-router/README.md index 0a19fd3b81..c3a60d1008 100644 --- a/packages/react-router/README.md +++ b/packages/react-router/README.md @@ -2,7 +2,21 @@ # TanStack React Router -![TanStack Router Header](https://raw.githubusercontent.com/TanStack/router/main/media/header_router.png) + + + + TanStack React Router + 🤖 Type-safe router w/ built-in caching & URL state management for React! diff --git a/packages/react-start/README.md b/packages/react-start/README.md index 4ead3764b3..e614561c15 100644 --- a/packages/react-start/README.md +++ b/packages/react-start/README.md @@ -2,7 +2,21 @@ # TanStack React Start -![TanStack React Start Header](https://raw.githubusercontent.com/TanStack/router/main/media/header_start.png) + + + + TanStack React Start + SSR, Streaming, Server Functions, API Routes, bundling and more powered by [TanStack Router](https://tanstack.com/router) and Vite. Ready to deploy to your favorite hosting provider. diff --git a/packages/solid-router-devtools/README.md b/packages/solid-router-devtools/README.md index 2011760db8..cf8c44252d 100644 --- a/packages/solid-router-devtools/README.md +++ b/packages/solid-router-devtools/README.md @@ -1,5 +1,22 @@ +
    + + + + TanStack Solid Router + +
    # TanStack Solid Router Devtools See https://tanstack.com/router/latest/docs/framework/solid/devtools diff --git a/packages/solid-router-ssr-query/README.md b/packages/solid-router-ssr-query/README.md index f7c1691123..2aadc50710 100644 --- a/packages/solid-router-ssr-query/README.md +++ b/packages/solid-router-ssr-query/README.md @@ -1,4 +1,18 @@ -![TanStack Router Header](https://raw.githubusercontent.com/TanStack/router/main/media/header_router.png) + + + + TanStack Solid Router + # @tanstack/solid-router-ssr-query diff --git a/packages/solid-router/README.md b/packages/solid-router/README.md index 94a0f37468..3e41f35f1b 100644 --- a/packages/solid-router/README.md +++ b/packages/solid-router/README.md @@ -2,7 +2,21 @@ # TanStack Solid Router -![TanStack Router Header](https://raw.githubusercontent.com/TanStack/router/main/media/header_router.png) + + + + TanStack Solid Router + 🤖 Type-safe router w/ built-in caching & URL state management for Solid! diff --git a/packages/solid-start/README.md b/packages/solid-start/README.md index 26f6c4840c..92e86fa1a5 100644 --- a/packages/solid-start/README.md +++ b/packages/solid-start/README.md @@ -2,7 +2,21 @@ # TanStack Solid Start -![TanStack Solid Start Header](https://raw.githubusercontent.com/TanStack/router/main/media/header_start.png) + + + + TanStack Solid Start + SSR, Streaming, Server Functions, API Routes, bundling and more powered by [TanStack Router](https://tanstack.com/router) and Vite. Ready to deploy to your favorite hosting provider. diff --git a/packages/vue-router-devtools/README.md b/packages/vue-router-devtools/README.md index f88118caa5..4ee1f0f38b 100644 --- a/packages/vue-router-devtools/README.md +++ b/packages/vue-router-devtools/README.md @@ -1,5 +1,22 @@ +
    + + + + TanStack Vue Router + +
    # TanStack Vue Router Devtools See https://tanstack.com/router/latest/docs/framework/vue/devtools diff --git a/packages/vue-router-ssr-query/README.md b/packages/vue-router-ssr-query/README.md index a0c0b4928c..8629d6abf3 100644 --- a/packages/vue-router-ssr-query/README.md +++ b/packages/vue-router-ssr-query/README.md @@ -1,3 +1,20 @@ +
    + + + + TanStack Vue Router + +
    # @tanstack/vue-router-ssr-query SSR Query integration for TanStack Vue Router. diff --git a/packages/vue-router/README.md b/packages/vue-router/README.md index 383a15d84d..4c71e74654 100644 --- a/packages/vue-router/README.md +++ b/packages/vue-router/README.md @@ -2,7 +2,21 @@ # TanStack Vue Router -![TanStack Router Header](https://raw.githubusercontent.com/TanStack/router/main/media/header_router.png) + + + + TanStack Vue Router + 🤖 Type-safe router w/ built-in caching & URL state management for Vue! From 65f7b7f791272f2ea581a1fe0fcd43183bc01162 Mon Sep 17 00:00:00 2001 From: Manuel Schiller <6340397+schiller-manuel@users.noreply.github.com> Date: Tue, 4 Aug 2026 00:02:37 +0200 Subject: [PATCH 41/51] fix(start): keep server-only routes out of RSC bundles (#7944) Use the Start storage context for request cancellation and focused server entrypoints to keep unrelated route dependencies out of the RSC module graph. Add emitted-build regression coverage for the boundary. fixes #7938 --- .changeset/calm-barrels-focus.md | 8 ++ .changeset/tidy-ravens-rest.md | 5 ++ e2e/react-start/rsc/src/routeTree.gen.ts | 21 +++++ .../rsc/src/routes/api.rsc-build-boundary.ts | 14 +++ .../rsc/src/utils/rscBuildBoundarySentinel.ts | 3 + .../rsc/tests/rsc-build-boundary.spec.ts | 87 +++++++++++++++++++ packages/react-start-rsc/eslint.config.js | 27 ++++++ packages/react-start-rsc/package.json | 1 - .../src/createCompositeComponent.ts | 3 +- .../src/renderServerComponent.ts | 3 +- .../tests/createServerComponent.test-d.tsx | 13 +-- .../src/defaultRenderHandler.tsx | 6 +- .../src/defaultStreamHandler.tsx | 6 +- packages/start-plugin-core/src/post-build.ts | 2 +- .../src/rsbuild/virtual-modules.ts | 2 +- .../src/vite/serialization-adapters-plugin.ts | 2 +- .../src/vite/start-compiler-plugin/plugin.ts | 2 +- .../src/vite/start-manifest-plugin/plugin.ts | 2 +- .../tests/post-server-build.test.ts | 2 +- .../tests/rsbuild/post-build.test.ts | 2 +- .../tests/start-manifest-plugin.test.ts | 4 +- packages/start-server-core/package.json | 12 +++ packages/start-server-core/vite.config.ts | 2 + .../src/defaultRenderHandler.tsx | 6 +- .../src/defaultStreamHandler.tsx | 6 +- pnpm-lock.yaml | 3 - 26 files changed, 207 insertions(+), 37 deletions(-) create mode 100644 .changeset/calm-barrels-focus.md create mode 100644 .changeset/tidy-ravens-rest.md create mode 100644 e2e/react-start/rsc/src/routes/api.rsc-build-boundary.ts create mode 100644 e2e/react-start/rsc/src/utils/rscBuildBoundarySentinel.ts create mode 100644 e2e/react-start/rsc/tests/rsc-build-boundary.spec.ts diff --git a/.changeset/calm-barrels-focus.md b/.changeset/calm-barrels-focus.md new file mode 100644 index 0000000000..d44541be14 --- /dev/null +++ b/.changeset/calm-barrels-focus.md @@ -0,0 +1,8 @@ +--- +'@tanstack/start-server-core': patch +'@tanstack/start-plugin-core': patch +'@tanstack/solid-start-server': patch +'@tanstack/vue-start-server': patch +--- + +Use focused server entrypoints for shared constants and handler helpers so build tooling and framework renderers do not traverse the full Start server barrel. diff --git a/.changeset/tidy-ravens-rest.md b/.changeset/tidy-ravens-rest.md new file mode 100644 index 0000000000..b81144ad53 --- /dev/null +++ b/.changeset/tidy-ravens-rest.md @@ -0,0 +1,5 @@ +--- +'@tanstack/react-start-rsc': patch +--- + +Read request cancellation from the Start storage context so RSC helpers do not pull the Start server barrel into the RSC module graph. diff --git a/e2e/react-start/rsc/src/routeTree.gen.ts b/e2e/react-start/rsc/src/routeTree.gen.ts index 209c514f89..38137dfb4b 100644 --- a/e2e/react-start/rsc/src/routeTree.gen.ts +++ b/e2e/react-start/rsc/src/routeTree.gen.ts @@ -51,6 +51,7 @@ import { Route as RscStreamingRouteImport } from './routes/rsc-streaming' import { Route as RscSuspenseRouteImport } from './routes/rsc-suspense' import { Route as RscTreeRouteImport } from './routes/rsc-tree' import { Route as RscUseServerFnRouteImport } from './routes/rsc-use-server-fn' +import { Route as ApiRscBuildBoundaryRouteImport } from './routes/api.rsc-build-boundary' import { Route as ApiRscFlightRouteImport } from './routes/api.rsc-flight' import { Route as RscCssConditionalIndexRouteImport } from './routes/rsc-css-conditional.index' import { Route as RscCssConditionalBranchRouteImport } from './routes/rsc-css-conditional.$branch' @@ -267,6 +268,11 @@ const RscUseServerFnRoute = RscUseServerFnRouteImport.update({ path: '/rsc-use-server-fn', getParentRoute: () => rootRouteImport, } as any) +const ApiRscBuildBoundaryRoute = ApiRscBuildBoundaryRouteImport.update({ + id: '/api/rsc-build-boundary', + path: '/api/rsc-build-boundary', + getParentRoute: () => rootRouteImport, +} as any) const ApiRscFlightRoute = ApiRscFlightRouteImport.update({ id: '/api/rsc-flight', path: '/api/rsc-flight', @@ -336,6 +342,7 @@ export interface FileRoutesByFullPath { '/rsc-suspense': typeof RscSuspenseRoute '/rsc-tree': typeof RscTreeRoute '/rsc-use-server-fn': typeof RscUseServerFnRoute + '/api/rsc-build-boundary': typeof ApiRscBuildBoundaryRoute '/api/rsc-flight': typeof ApiRscFlightRoute '/rsc-css-conditional/$branch': typeof RscCssConditionalBranchRoute '/rsc-param/$id': typeof RscParamIdRoute @@ -385,6 +392,7 @@ export interface FileRoutesByTo { '/rsc-suspense': typeof RscSuspenseRoute '/rsc-tree': typeof RscTreeRoute '/rsc-use-server-fn': typeof RscUseServerFnRoute + '/api/rsc-build-boundary': typeof ApiRscBuildBoundaryRoute '/api/rsc-flight': typeof ApiRscFlightRoute '/rsc-css-conditional/$branch': typeof RscCssConditionalBranchRoute '/rsc-param/$id': typeof RscParamIdRoute @@ -435,6 +443,7 @@ export interface FileRoutesById { '/rsc-suspense': typeof RscSuspenseRoute '/rsc-tree': typeof RscTreeRoute '/rsc-use-server-fn': typeof RscUseServerFnRoute + '/api/rsc-build-boundary': typeof ApiRscBuildBoundaryRoute '/api/rsc-flight': typeof ApiRscFlightRoute '/rsc-css-conditional/$branch': typeof RscCssConditionalBranchRoute '/rsc-param/$id': typeof RscParamIdRoute @@ -486,6 +495,7 @@ export interface FileRouteTypes { | '/rsc-suspense' | '/rsc-tree' | '/rsc-use-server-fn' + | '/api/rsc-build-boundary' | '/api/rsc-flight' | '/rsc-css-conditional/$branch' | '/rsc-param/$id' @@ -535,6 +545,7 @@ export interface FileRouteTypes { | '/rsc-suspense' | '/rsc-tree' | '/rsc-use-server-fn' + | '/api/rsc-build-boundary' | '/api/rsc-flight' | '/rsc-css-conditional/$branch' | '/rsc-param/$id' @@ -584,6 +595,7 @@ export interface FileRouteTypes { | '/rsc-suspense' | '/rsc-tree' | '/rsc-use-server-fn' + | '/api/rsc-build-boundary' | '/api/rsc-flight' | '/rsc-css-conditional/$branch' | '/rsc-param/$id' @@ -634,6 +646,7 @@ export interface RootRouteChildren { RscSuspenseRoute: typeof RscSuspenseRoute RscTreeRoute: typeof RscTreeRoute RscUseServerFnRoute: typeof RscUseServerFnRoute + ApiRscBuildBoundaryRoute: typeof ApiRscBuildBoundaryRoute ApiRscFlightRoute: typeof ApiRscFlightRoute RscCssConditionalBranchRoute: typeof RscCssConditionalBranchRoute RscParamIdRoute: typeof RscParamIdRoute @@ -937,6 +950,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof RscUseServerFnRouteImport parentRoute: typeof rootRouteImport } + '/api/rsc-build-boundary': { + id: '/api/rsc-build-boundary' + path: '/api/rsc-build-boundary' + fullPath: '/api/rsc-build-boundary' + preLoaderRoute: typeof ApiRscBuildBoundaryRouteImport + parentRoute: typeof rootRouteImport + } '/api/rsc-flight': { id: '/api/rsc-flight' path: '/api/rsc-flight' @@ -1018,6 +1038,7 @@ const rootRouteChildren: RootRouteChildren = { RscSuspenseRoute: RscSuspenseRoute, RscTreeRoute: RscTreeRoute, RscUseServerFnRoute: RscUseServerFnRoute, + ApiRscBuildBoundaryRoute: ApiRscBuildBoundaryRoute, ApiRscFlightRoute: ApiRscFlightRoute, RscCssConditionalBranchRoute: RscCssConditionalBranchRoute, RscParamIdRoute: RscParamIdRoute, diff --git a/e2e/react-start/rsc/src/routes/api.rsc-build-boundary.ts b/e2e/react-start/rsc/src/routes/api.rsc-build-boundary.ts new file mode 100644 index 0000000000..d29d2a8cb8 --- /dev/null +++ b/e2e/react-start/rsc/src/routes/api.rsc-build-boundary.ts @@ -0,0 +1,14 @@ +import { createFileRoute } from '@tanstack/react-router' +import { getRscBuildBoundarySentinel } from '~/utils/rscBuildBoundarySentinel' + +export const Route = createFileRoute('/api/rsc-build-boundary')({ + server: { + handlers: { + GET: () => { + return new Response(getRscBuildBoundarySentinel(), { + headers: { 'Content-Type': 'text/plain' }, + }) + }, + }, + }, +}) diff --git a/e2e/react-start/rsc/src/utils/rscBuildBoundarySentinel.ts b/e2e/react-start/rsc/src/utils/rscBuildBoundarySentinel.ts new file mode 100644 index 0000000000..706adb30d9 --- /dev/null +++ b/e2e/react-start/rsc/src/utils/rscBuildBoundarySentinel.ts @@ -0,0 +1,3 @@ +export function getRscBuildBoundarySentinel() { + return 'tanstack-start-rsc-server-only-route-sentinel' +} diff --git a/e2e/react-start/rsc/tests/rsc-build-boundary.spec.ts b/e2e/react-start/rsc/tests/rsc-build-boundary.spec.ts new file mode 100644 index 0000000000..f57fefe994 --- /dev/null +++ b/e2e/react-start/rsc/tests/rsc-build-boundary.spec.ts @@ -0,0 +1,87 @@ +import { readdir, readFile } from 'node:fs/promises' +import path from 'node:path' +import { expect } from '@playwright/test' +import { test } from '@tanstack/router-e2e-utils' + +// This marker deliberately lives behind a server-only route. If its route +// dependency graph enters the RSC build, the emitted-file assertion will fail. +const SENTINEL = 'tanstack-start-rsc-server-only-route-sentinel' +const EXECUTABLE_EXTENSIONS = new Set(['.js', '.mjs', '.cjs']) + +const distDir = path.resolve(process.cwd(), process.env.E2E_DIST_DIR ?? 'dist') +const serverDir = path.join(distDir, 'server') +const rscDir = path.join(serverDir, 'rsc') + +async function findExecutableFiles( + directory: string, + excludedDirectories = new Set(), +): Promise> { + const entries = await readdir(directory, { withFileTypes: true }) + const files: Array = [] + + for (const entry of entries) { + const entryPath = path.join(directory, entry.name) + + if (entry.isDirectory()) { + if (!excludedDirectories.has(entryPath)) { + files.push( + ...(await findExecutableFiles(entryPath, excludedDirectories)), + ) + } + } else if ( + entry.isFile() && + EXECUTABLE_EXTENSIONS.has(path.extname(entry.name)) + ) { + files.push(entryPath) + } + } + + return files.sort() +} + +async function findSentinelMatches(files: Array) { + const matches: Array = [] + + for (const file of files) { + if ((await readFile(file, 'utf-8')).includes(SENTINEL)) { + matches.push(path.relative(distDir, file)) + } + } + + return matches +} + +test('server-only route dependencies stay out of the RSC build output', async ({ + request, +}) => { + test.skip( + (process.env.E2E_TOOLCHAIN ?? 'vite') !== 'vite', + 'Vite emits the RSC environment as a separate output directory', + ) + + const response = await request.get('/api/rsc-build-boundary') + expect(response.ok()).toBe(true) + expect(await response.text()).toBe(SENTINEL) + + const [ssrFiles, rscFiles] = await Promise.all([ + findExecutableFiles(serverDir, new Set([rscDir])), + findExecutableFiles(rscDir), + ]) + const [ssrMatches, rscMatches] = await Promise.all([ + findSentinelMatches(ssrFiles), + findSentinelMatches(rscFiles), + ]) + + expect( + ssrMatches, + `Expected the sentinel in the SSR output. Scanned files:\n${ssrFiles + .map((file) => path.relative(distDir, file)) + .join('\n')}`, + ).not.toEqual([]) + expect( + rscMatches, + `The server-only route dependency leaked into these RSC files:\n${rscMatches.join( + '\n', + )}`, + ).toEqual([]) +}) diff --git a/packages/react-start-rsc/eslint.config.js b/packages/react-start-rsc/eslint.config.js index 931f0ec774..daf1baa88a 100644 --- a/packages/react-start-rsc/eslint.config.js +++ b/packages/react-start-rsc/eslint.config.js @@ -22,6 +22,33 @@ export default [ 'react-hooks/rules-of-hooks': 'error', }, }, + { + name: 'react-start-rsc/import-boundaries', + files: ['src/**/*.{ts,tsx}'], + rules: { + 'no-restricted-imports': [ + 'error', + { + paths: [ + { + name: '@tanstack/start-server-core', + message: + 'Import from a dedicated @tanstack/start-server-core subpath to avoid pulling the full server barrel into the RSC module graph.', + }, + ], + }, + ], + 'no-restricted-syntax': [ + 'error', + { + selector: + "ImportExpression[source.value='@tanstack/start-server-core']", + message: + 'Dynamically import a dedicated @tanstack/start-server-core subpath instead of the root barrel.', + }, + ], + }, + }, { files: ['**/__tests__/**'], rules: { diff --git a/packages/react-start-rsc/package.json b/packages/react-start-rsc/package.json index ac3110104f..a5c77327bd 100644 --- a/packages/react-start-rsc/package.json +++ b/packages/react-start-rsc/package.json @@ -103,7 +103,6 @@ "@tanstack/start-client-core": "workspace:*", "@tanstack/start-fn-stubs": "workspace:*", "@tanstack/start-plugin-core": "workspace:*", - "@tanstack/start-server-core": "workspace:*", "@tanstack/start-storage-context": "workspace:*", "pathe": "^2.0.3" }, diff --git a/packages/react-start-rsc/src/createCompositeComponent.ts b/packages/react-start-rsc/src/createCompositeComponent.ts index c6a43b0f9d..02a840dc64 100644 --- a/packages/react-start-rsc/src/createCompositeComponent.ts +++ b/packages/react-start-rsc/src/createCompositeComponent.ts @@ -1,6 +1,5 @@ import { createElement } from 'react' import { renderToReadableStream } from 'virtual:tanstack-rsc-runtime' -import { getRequest } from '@tanstack/start-server-core' import { getStartContext } from '@tanstack/start-storage-context' import { sanitizeSlotArgs } from './slotUsageSanitizer' import { ReplayableStream } from './ReplayableStream' @@ -99,7 +98,7 @@ export async function createCompositeComponent( // SSR path: buffer stream for replay, pre-decode for synchronous rendering if (isRouterRequest && ssrHandler) { - const signal = getRequest().signal + const signal = ctx.request.signal const stream = new ReplayableStream(flightStream, { signal }) // Pre-decode during loader phase for synchronous SSR rendering diff --git a/packages/react-start-rsc/src/renderServerComponent.ts b/packages/react-start-rsc/src/renderServerComponent.ts index d282d63111..4ef6037821 100644 --- a/packages/react-start-rsc/src/renderServerComponent.ts +++ b/packages/react-start-rsc/src/renderServerComponent.ts @@ -1,5 +1,4 @@ import { renderToReadableStream } from 'virtual:tanstack-rsc-runtime' -import { getRequest } from '@tanstack/start-server-core' import { getStartContext } from '@tanstack/start-storage-context' import { ReplayableStream } from './ReplayableStream' import { RENDERABLE_RSC, SERVER_COMPONENT_STREAM } from './ServerComponentTypes' @@ -72,7 +71,7 @@ export async function renderServerComponent( // SSR path: buffer stream for replay, pre-decode for synchronous rendering if (isRouterRequest && ssrHandler) { - const signal = getRequest().signal + const signal = ctx.request.signal const stream = new ReplayableStream(flightStream, { signal }) // Pre-decode during loader phase for synchronous SSR rendering diff --git a/packages/react-start-rsc/tests/createServerComponent.test-d.tsx b/packages/react-start-rsc/tests/createServerComponent.test-d.tsx index 9a161e2345..320ef4f30a 100644 --- a/packages/react-start-rsc/tests/createServerComponent.test-d.tsx +++ b/packages/react-start-rsc/tests/createServerComponent.test-d.tsx @@ -1,21 +1,10 @@ -import { expectTypeOf, test, vi } from 'vitest' +import { expectTypeOf, test } from 'vitest' import type { CompositeComponentResult, ValidateCompositeComponent, } from '../src/ServerComponentTypes' import { CompositeComponent } from '../src/CompositeComponent' -vi.mock('@tanstack/start-server-core', () => { - return { - getRequest: () => undefined, - } -}) - -vi.mock('@tanstack/start-storage-context', () => { - return { - getStartContext: () => undefined, - } -}) import { JSX } from 'react' test('when a server component is created with no props', () => { diff --git a/packages/solid-start-server/src/defaultRenderHandler.tsx b/packages/solid-start-server/src/defaultRenderHandler.tsx index e3be93323b..66b3e27889 100644 --- a/packages/solid-start-server/src/defaultRenderHandler.tsx +++ b/packages/solid-start-server/src/defaultRenderHandler.tsx @@ -1,5 +1,7 @@ -import { defineHandlerCallback } from '@tanstack/start-server-core' -import { renderRouterToString } from '@tanstack/solid-router/ssr/server' +import { + defineHandlerCallback, + renderRouterToString, +} from '@tanstack/solid-router/ssr/server' import { StartServer } from './StartServer' export const defaultRenderHandler = defineHandlerCallback( diff --git a/packages/solid-start-server/src/defaultStreamHandler.tsx b/packages/solid-start-server/src/defaultStreamHandler.tsx index 4ee257a186..9575b801e0 100644 --- a/packages/solid-start-server/src/defaultStreamHandler.tsx +++ b/packages/solid-start-server/src/defaultStreamHandler.tsx @@ -1,5 +1,7 @@ -import { defineHandlerCallback } from '@tanstack/start-server-core' -import { renderRouterToStream } from '@tanstack/solid-router/ssr/server' +import { + defineHandlerCallback, + renderRouterToStream, +} from '@tanstack/solid-router/ssr/server' import { StartServer } from './StartServer' export const defaultStreamHandler = defineHandlerCallback( diff --git a/packages/start-plugin-core/src/post-build.ts b/packages/start-plugin-core/src/post-build.ts index 966cdd38cc..0fd2410d56 100644 --- a/packages/start-plugin-core/src/post-build.ts +++ b/packages/start-plugin-core/src/post-build.ts @@ -1,4 +1,4 @@ -import { HEADERS } from '@tanstack/start-server-core' +import { HEADERS } from '@tanstack/start-server-core/constants' import { buildSitemap } from './build-sitemap' import type { TanStackStartOutputConfig } from './schema' diff --git a/packages/start-plugin-core/src/rsbuild/virtual-modules.ts b/packages/start-plugin-core/src/rsbuild/virtual-modules.ts index 9e4c0bc4df..591625ce1c 100644 --- a/packages/start-plugin-core/src/rsbuild/virtual-modules.ts +++ b/packages/start-plugin-core/src/rsbuild/virtual-modules.ts @@ -1,4 +1,4 @@ -import { VIRTUAL_MODULES } from '@tanstack/start-server-core' +import { VIRTUAL_MODULES } from '@tanstack/start-server-core/virtual-modules' import { generateSerializationAdaptersModule } from '../serialization-adapters-module' import { generateServerFnResolverModule } from '../start-compiler/server-fn-resolver-module' import { buildStartManifest } from '../start-manifest-plugin/manifestBuilder' diff --git a/packages/start-plugin-core/src/vite/serialization-adapters-plugin.ts b/packages/start-plugin-core/src/vite/serialization-adapters-plugin.ts index 3cf12ad97c..26eb92c5ca 100644 --- a/packages/start-plugin-core/src/vite/serialization-adapters-plugin.ts +++ b/packages/start-plugin-core/src/vite/serialization-adapters-plugin.ts @@ -1,4 +1,4 @@ -import { VIRTUAL_MODULES } from '@tanstack/start-server-core' +import { VIRTUAL_MODULES } from '@tanstack/start-server-core/virtual-modules' import { generateSerializationAdaptersModule } from '../serialization-adapters-module' import { START_ENVIRONMENT_NAMES } from '../constants' import { createIdFilter } from '../utils' diff --git a/packages/start-plugin-core/src/vite/start-compiler-plugin/plugin.ts b/packages/start-plugin-core/src/vite/start-compiler-plugin/plugin.ts index 1cc02f3b91..6814740074 100644 --- a/packages/start-plugin-core/src/vite/start-compiler-plugin/plugin.ts +++ b/packages/start-plugin-core/src/vite/start-compiler-plugin/plugin.ts @@ -1,4 +1,4 @@ -import { VIRTUAL_MODULES } from '@tanstack/start-server-core' +import { VIRTUAL_MODULES } from '@tanstack/start-server-core/virtual-modules' import { resolve as resolvePath } from 'pathe' import { SERVER_FN_LOOKUP, diff --git a/packages/start-plugin-core/src/vite/start-manifest-plugin/plugin.ts b/packages/start-plugin-core/src/vite/start-manifest-plugin/plugin.ts index 9b9c274520..11a490388a 100644 --- a/packages/start-plugin-core/src/vite/start-manifest-plugin/plugin.ts +++ b/packages/start-plugin-core/src/vite/start-manifest-plugin/plugin.ts @@ -1,5 +1,5 @@ import { joinURL } from 'ufo' -import { VIRTUAL_MODULES } from '@tanstack/start-server-core' +import { VIRTUAL_MODULES } from '@tanstack/start-server-core/virtual-modules' import { rootRouteId } from '@tanstack/router-core' import { DEV_CLIENT_ENTRY, START_ENVIRONMENT_NAMES } from '../../constants' import { diff --git a/packages/start-plugin-core/tests/post-server-build.test.ts b/packages/start-plugin-core/tests/post-server-build.test.ts index 763f5e5d05..e290382d4d 100644 --- a/packages/start-plugin-core/tests/post-server-build.test.ts +++ b/packages/start-plugin-core/tests/post-server-build.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest' -vi.mock('@tanstack/start-server-core', () => ({ +vi.mock('@tanstack/start-server-core/constants', () => ({ HEADERS: { TSS_SHELL: 'x-tss-shell', }, diff --git a/packages/start-plugin-core/tests/rsbuild/post-build.test.ts b/packages/start-plugin-core/tests/rsbuild/post-build.test.ts index 9f302d5c8e..26443c7e7c 100644 --- a/packages/start-plugin-core/tests/rsbuild/post-build.test.ts +++ b/packages/start-plugin-core/tests/rsbuild/post-build.test.ts @@ -3,7 +3,7 @@ import { mkdtemp, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'pathe' -vi.mock('@tanstack/start-server-core', () => ({ +vi.mock('@tanstack/start-server-core/constants', () => ({ HEADERS: { TSS_SHELL: 'x-tss-shell', }, diff --git a/packages/start-plugin-core/tests/start-manifest-plugin.test.ts b/packages/start-plugin-core/tests/start-manifest-plugin.test.ts index 68270fb6d7..8d671d1dbf 100644 --- a/packages/start-plugin-core/tests/start-manifest-plugin.test.ts +++ b/packages/start-plugin-core/tests/start-manifest-plugin.test.ts @@ -1,9 +1,9 @@ -import { VIRTUAL_MODULES } from '@tanstack/start-server-core' +import { VIRTUAL_MODULES } from '@tanstack/start-server-core/virtual-modules' import { describe, expect, test, vi } from 'vitest' import { DEV_CLIENT_ENTRY, START_ENVIRONMENT_NAMES } from '../src/constants' import { startManifestPlugin } from '../src/vite/start-manifest-plugin/plugin' -vi.mock('@tanstack/start-server-core', () => ({ +vi.mock('@tanstack/start-server-core/virtual-modules', () => ({ VIRTUAL_MODULES: { startManifest: 'tanstack-start-manifest:v', }, diff --git a/packages/start-server-core/package.json b/packages/start-server-core/package.json index 38ff82ce3c..a38803820d 100644 --- a/packages/start-server-core/package.json +++ b/packages/start-server-core/package.json @@ -65,6 +65,18 @@ "default": "./dist/esm/request-response.js" } }, + "./constants": { + "import": { + "types": "./dist/esm/constants.d.ts", + "default": "./dist/esm/constants.js" + } + }, + "./virtual-modules": { + "import": { + "types": "./dist/esm/virtual-modules.d.ts", + "default": "./dist/esm/virtual-modules.js" + } + }, "./package.json": "./package.json" }, "imports": { diff --git a/packages/start-server-core/vite.config.ts b/packages/start-server-core/vite.config.ts index dd252ddb08..a445c8fa1c 100644 --- a/packages/start-server-core/vite.config.ts +++ b/packages/start-server-core/vite.config.ts @@ -42,6 +42,8 @@ export default mergeConfig( './src/createServerRpc.ts', './src/createSsrRpc.ts', './src/request-response.ts', + './src/constants.ts', + './src/virtual-modules.ts', './src/fake-start-server-fn-resolver.ts', './src/empty-plugin-adapters.ts', ], diff --git a/packages/vue-start-server/src/defaultRenderHandler.tsx b/packages/vue-start-server/src/defaultRenderHandler.tsx index 69fa83d8ac..1e31e6e97e 100644 --- a/packages/vue-start-server/src/defaultRenderHandler.tsx +++ b/packages/vue-start-server/src/defaultRenderHandler.tsx @@ -1,5 +1,7 @@ -import { defineHandlerCallback } from '@tanstack/start-server-core' -import { renderRouterToString } from '@tanstack/vue-router/ssr/server' +import { + defineHandlerCallback, + renderRouterToString, +} from '@tanstack/vue-router/ssr/server' import { StartServer } from './StartServer' export const defaultRenderHandler = defineHandlerCallback( diff --git a/packages/vue-start-server/src/defaultStreamHandler.tsx b/packages/vue-start-server/src/defaultStreamHandler.tsx index 94106eee85..2eb8c09f82 100644 --- a/packages/vue-start-server/src/defaultStreamHandler.tsx +++ b/packages/vue-start-server/src/defaultStreamHandler.tsx @@ -1,5 +1,7 @@ -import { defineHandlerCallback } from '@tanstack/start-server-core' -import { renderRouterToStream } from '@tanstack/vue-router/ssr/server' +import { + defineHandlerCallback, + renderRouterToStream, +} from '@tanstack/vue-router/ssr/server' import { StartServer } from './StartServer' export const defaultStreamHandler = defineHandlerCallback( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 75be091d06..bbbc0e852c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13480,9 +13480,6 @@ importers: '@tanstack/start-plugin-core': specifier: workspace:* version: link:../start-plugin-core - '@tanstack/start-server-core': - specifier: workspace:* - version: link:../start-server-core '@tanstack/start-storage-context': specifier: workspace:* version: link:../start-storage-context From 10dc3f63e69871cc8a109c46e7abc88329fc6d68 Mon Sep 17 00:00:00 2001 From: Ahmed Sabry <161619341+ahmedsabryfr@users.noreply.github.com> Date: Tue, 4 Aug 2026 01:04:26 +0300 Subject: [PATCH 42/51] =?UTF-8?q?docs:=20version=20optional=20param=20to?= =?UTF-8?q?=20only=20capture=20version=20not=20capturing=20"e=E2=80=A6=20(?= =?UTF-8?q?#7932)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: version optional param to only capture version not capturing "extra" as version * fix: close template literal --- docs/router/guide/path-params.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/router/guide/path-params.md b/docs/router/guide/path-params.md index fa8468ef48..b42aefad7f 100644 --- a/docs/router/guide/path-params.md +++ b/docs/router/guide/path-params.md @@ -582,10 +582,10 @@ Optional parameters can be combined with wildcards for complex routing patterns: # React -```tsx title="src/routes/docs/{-$version}/$.tsx" -// Route: /docs/{-$version}/$ +```tsx title="src/routes/docs/v{-$version}/$.tsx" +// Route: /docs/v{-$version}/$ // Matches: /docs/extra/path, /docs/v2/extra/path -export const Route = createFileRoute('/docs/{-$version}/$')({ +export const Route = createFileRoute('/docs/v{-$version}/$')({ component: DocsComponent, }) @@ -595,7 +595,7 @@ function DocsComponent() { return (
    - Version: {version || 'latest'} + Version: {version ? `v${version}` : 'latest'} Path: {_splat}
    ) @@ -604,10 +604,10 @@ function DocsComponent() { # Solid -```tsx title="src/routes/docs/{-$version}/$.tsx" -// Route: /docs/{-$version}/$ +```tsx title="src/routes/docs/v{-$version}/$.tsx" +// Route: /docs/v{-$version}/$ // Matches: /docs/extra/path, /docs/v2/extra/path -export const Route = createFileRoute('/docs/{-$version}/$')({ +export const Route = createFileRoute('/docs/v{-$version}/$')({ component: DocsComponent, }) @@ -616,7 +616,7 @@ function DocsComponent() { return (
    - Version: {params().version || 'latest'} + Version: {params().version ? `v${params().version}` : 'latest'} Path: {params()._splat}
    ) From 65ae78cf0625a1d8975ac6ef0258d10d38ff1df0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2026 00:09:59 +0200 Subject: [PATCH 43/51] ci: Version Packages (#7945) ci: changeset release Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/calm-barrels-focus.md | 8 -------- .changeset/tidy-ravens-rest.md | 5 ----- examples/react/start-bare/package.json | 2 +- examples/react/start-basic-auth/package.json | 2 +- examples/react/start-basic-authjs/package.json | 2 +- examples/react/start-basic-cloudflare/package.json | 2 +- examples/react/start-basic-react-query/package.json | 2 +- examples/react/start-basic-rsbuild/package.json | 2 +- examples/react/start-basic-static/package.json | 2 +- examples/react/start-basic/package.json | 2 +- examples/react/start-bun/package.json | 2 +- examples/react/start-clerk-basic/package.json | 2 +- examples/react/start-convex-trellaux/package.json | 2 +- examples/react/start-counter/package.json | 2 +- examples/react/start-i18n-paraglide/package.json | 2 +- examples/react/start-large/package.json | 2 +- examples/react/start-material-ui/package.json | 2 +- examples/react/start-rscs/package.json | 2 +- .../package.json | 2 +- examples/react/start-supabase-basic/package.json | 2 +- examples/react/start-tailwind-v4/package.json | 2 +- examples/react/start-trellaux/package.json | 2 +- examples/react/start-workos/package.json | 2 +- examples/solid/start-basic-auth/package.json | 2 +- examples/solid/start-basic-authjs/package.json | 2 +- examples/solid/start-basic-cloudflare/package.json | 2 +- examples/solid/start-basic-netlify/package.json | 2 +- examples/solid/start-basic-nitro/package.json | 2 +- examples/solid/start-basic-solid-query/package.json | 2 +- examples/solid/start-basic-static/package.json | 2 +- examples/solid/start-basic/package.json | 2 +- examples/solid/start-bun/package.json | 2 +- examples/solid/start-convex-better-auth/package.json | 2 +- examples/solid/start-counter/package.json | 2 +- examples/solid/start-i18n-paraglide/package.json | 2 +- examples/solid/start-large/package.json | 2 +- .../package.json | 2 +- examples/solid/start-supabase-basic/package.json | 2 +- examples/solid/start-tailwind-v4/package.json | 2 +- packages/react-start-rsc/CHANGELOG.md | 9 +++++++++ packages/react-start-rsc/package.json | 2 +- packages/react-start-server/CHANGELOG.md | 7 +++++++ packages/react-start-server/package.json | 2 +- packages/react-start/CHANGELOG.md | 10 ++++++++++ packages/react-start/package.json | 2 +- packages/solid-start-server/CHANGELOG.md | 9 +++++++++ packages/solid-start-server/package.json | 2 +- packages/solid-start/CHANGELOG.md | 9 +++++++++ packages/solid-start/package.json | 2 +- packages/start-plugin-core/CHANGELOG.md | 9 +++++++++ packages/start-plugin-core/package.json | 2 +- packages/start-server-core/CHANGELOG.md | 6 ++++++ packages/start-server-core/package.json | 2 +- packages/vue-start-server/CHANGELOG.md | 9 +++++++++ packages/vue-start-server/package.json | 2 +- packages/vue-start/CHANGELOG.md | 9 +++++++++ packages/vue-start/package.json | 2 +- 57 files changed, 123 insertions(+), 59 deletions(-) delete mode 100644 .changeset/calm-barrels-focus.md delete mode 100644 .changeset/tidy-ravens-rest.md diff --git a/.changeset/calm-barrels-focus.md b/.changeset/calm-barrels-focus.md deleted file mode 100644 index d44541be14..0000000000 --- a/.changeset/calm-barrels-focus.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@tanstack/start-server-core': patch -'@tanstack/start-plugin-core': patch -'@tanstack/solid-start-server': patch -'@tanstack/vue-start-server': patch ---- - -Use focused server entrypoints for shared constants and handler helpers so build tooling and framework renderers do not traverse the full Start server barrel. diff --git a/.changeset/tidy-ravens-rest.md b/.changeset/tidy-ravens-rest.md deleted file mode 100644 index b81144ad53..0000000000 --- a/.changeset/tidy-ravens-rest.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@tanstack/react-start-rsc': patch ---- - -Read request cancellation from the Start storage context so RSC helpers do not pull the Start server barrel into the RSC module graph. diff --git a/examples/react/start-bare/package.json b/examples/react/start-bare/package.json index bbda1dcee8..df9095406a 100644 --- a/examples/react/start-bare/package.json +++ b/examples/react/start-bare/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.34", + "@tanstack/react-start": "^1.168.35", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-basic-auth/package.json b/examples/react/start-basic-auth/package.json index fa1048d35c..d6837c76aa 100644 --- a/examples/react/start-basic-auth/package.json +++ b/examples/react/start-basic-auth/package.json @@ -16,7 +16,7 @@ "@prisma/client": "^7.0.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.34", + "@tanstack/react-start": "^1.168.35", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-basic-authjs/package.json b/examples/react/start-basic-authjs/package.json index 6b094f6309..bded499b89 100644 --- a/examples/react/start-basic-authjs/package.json +++ b/examples/react/start-basic-authjs/package.json @@ -13,7 +13,7 @@ "@auth/core": "^0.41.1", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.34", + "@tanstack/react-start": "^1.168.35", "react": "^19.0.0", "react-dom": "^19.0.0", "start-authjs": "^1.0.0", diff --git a/examples/react/start-basic-cloudflare/package.json b/examples/react/start-basic-cloudflare/package.json index ffbcdf6c31..4c58962173 100644 --- a/examples/react/start-basic-cloudflare/package.json +++ b/examples/react/start-basic-cloudflare/package.json @@ -14,7 +14,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.34", + "@tanstack/react-start": "^1.168.35", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/examples/react/start-basic-react-query/package.json b/examples/react/start-basic-react-query/package.json index 2102165945..86bd1bdcdd 100644 --- a/examples/react/start-basic-react-query/package.json +++ b/examples/react/start-basic-react-query/package.json @@ -15,7 +15,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.34", + "@tanstack/react-start": "^1.168.35", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-basic-rsbuild/package.json b/examples/react/start-basic-rsbuild/package.json index 5638493cd6..18c30c8d7f 100644 --- a/examples/react/start-basic-rsbuild/package.json +++ b/examples/react/start-basic-rsbuild/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.34", + "@tanstack/react-start": "^1.168.35", "react": "^19.2.3", "react-dom": "^19.2.3", "tailwind-merge": "^3.6.0", diff --git a/examples/react/start-basic-static/package.json b/examples/react/start-basic-static/package.json index 8c0fb9624e..ceaa0a1c7b 100644 --- a/examples/react/start-basic-static/package.json +++ b/examples/react/start-basic-static/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.34", + "@tanstack/react-start": "^1.168.35", "@tanstack/start-static-server-functions": "^1.167.19", "@vitejs/plugin-react": "^6.0.1", "react": "^19.0.0", diff --git a/examples/react/start-basic/package.json b/examples/react/start-basic/package.json index 410a904a1f..23cc748f75 100644 --- a/examples/react/start-basic/package.json +++ b/examples/react/start-basic/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.34", + "@tanstack/react-start": "^1.168.35", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.6.0", diff --git a/examples/react/start-bun/package.json b/examples/react/start-bun/package.json index fd25c97be2..80ae09f891 100644 --- a/examples/react/start-bun/package.json +++ b/examples/react/start-bun/package.json @@ -18,7 +18,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.34", + "@tanstack/react-start": "^1.168.35", "@tanstack/router-plugin": "^1.168.23", "react": "^19.1.1", "react-dom": "^19.1.1", diff --git a/examples/react/start-clerk-basic/package.json b/examples/react/start-clerk-basic/package.json index 91415c51ad..96560e9406 100644 --- a/examples/react/start-clerk-basic/package.json +++ b/examples/react/start-clerk-basic/package.json @@ -13,7 +13,7 @@ "@clerk/tanstack-react-start": "^0.27.14", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.34", + "@tanstack/react-start": "^1.168.35", "@vitejs/plugin-react": "^6.0.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/start-convex-trellaux/package.json b/examples/react/start-convex-trellaux/package.json index 6921d6ae39..155f2b269b 100644 --- a/examples/react/start-convex-trellaux/package.json +++ b/examples/react/start-convex-trellaux/package.json @@ -18,7 +18,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.34", + "@tanstack/react-start": "^1.168.35", "concurrently": "^8.2.2", "convex": "^1.19.0", "ky": "^1.7.4", diff --git a/examples/react/start-counter/package.json b/examples/react/start-counter/package.json index e87657bdd9..d9e7ec5448 100644 --- a/examples/react/start-counter/package.json +++ b/examples/react/start-counter/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.34", + "@tanstack/react-start": "^1.168.35", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/examples/react/start-i18n-paraglide/package.json b/examples/react/start-i18n-paraglide/package.json index d7f40df7aa..14199a263b 100644 --- a/examples/react/start-i18n-paraglide/package.json +++ b/examples/react/start-i18n-paraglide/package.json @@ -12,7 +12,7 @@ "@tanstack/react-devtools": "^0.7.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.34", + "@tanstack/react-start": "^1.168.35", "react": "^19.1.1", "react-dom": "^19.1.1" }, diff --git a/examples/react/start-large/package.json b/examples/react/start-large/package.json index 9aa1e9f3d1..ceaa603188 100644 --- a/examples/react/start-large/package.json +++ b/examples/react/start-large/package.json @@ -15,7 +15,7 @@ "@tanstack/react-query": "^5.90.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.34", + "@tanstack/react-start": "^1.168.35", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-material-ui/package.json b/examples/react/start-material-ui/package.json index 1244cdc9eb..4a9f0b4362 100644 --- a/examples/react/start-material-ui/package.json +++ b/examples/react/start-material-ui/package.json @@ -17,7 +17,7 @@ "@mui/material": "6.4.7", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.34", + "@tanstack/react-start": "^1.168.35", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-rscs/package.json b/examples/react/start-rscs/package.json index 370ee45e48..013487a90a 100644 --- a/examples/react/start-rscs/package.json +++ b/examples/react/start-rscs/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.34", + "@tanstack/react-start": "^1.168.35", "dexie": "^4.0.10", "react": "^19.2.0", "react-dom": "^19.2.0", diff --git a/examples/react/start-streaming-data-from-server-functions/package.json b/examples/react/start-streaming-data-from-server-functions/package.json index 16fb4d5f6b..844af0104f 100644 --- a/examples/react/start-streaming-data-from-server-functions/package.json +++ b/examples/react/start-streaming-data-from-server-functions/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.34", + "@tanstack/react-start": "^1.168.35", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-supabase-basic/package.json b/examples/react/start-supabase-basic/package.json index 9977c4de3c..38efef297b 100644 --- a/examples/react/start-supabase-basic/package.json +++ b/examples/react/start-supabase-basic/package.json @@ -17,7 +17,7 @@ "@supabase/supabase-js": "^2.48.1", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.34", + "@tanstack/react-start": "^1.168.35", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/start-tailwind-v4/package.json b/examples/react/start-tailwind-v4/package.json index bc775c1cc6..9ba1b872b1 100644 --- a/examples/react/start-tailwind-v4/package.json +++ b/examples/react/start-tailwind-v4/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.34", + "@tanstack/react-start": "^1.168.35", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.6.0", diff --git a/examples/react/start-trellaux/package.json b/examples/react/start-trellaux/package.json index d0255faf39..d1f1b717a3 100644 --- a/examples/react/start-trellaux/package.json +++ b/examples/react/start-trellaux/package.json @@ -15,7 +15,7 @@ "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.34", + "@tanstack/react-start": "^1.168.35", "ky": "^1.7.4", "msw": "^2.7.0", "react": "^19.0.0", diff --git a/examples/react/start-workos/package.json b/examples/react/start-workos/package.json index 7cc0dc743a..c458ed9959 100644 --- a/examples/react/start-workos/package.json +++ b/examples/react/start-workos/package.json @@ -16,7 +16,7 @@ "@radix-ui/themes": "^3.3.0", "@tanstack/react-router": "^1.170.18", "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.34", + "@tanstack/react-start": "^1.168.35", "@workos/authkit-tanstack-react-start": "^0.5.0", "react": "^19.0.0", "react-dom": "^19.0.0" diff --git a/examples/solid/start-basic-auth/package.json b/examples/solid/start-basic-auth/package.json index 1b3452d83d..ec7539a2bf 100644 --- a/examples/solid/start-basic-auth/package.json +++ b/examples/solid/start-basic-auth/package.json @@ -16,7 +16,7 @@ "@prisma/client": "^7.0.0", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.33", + "@tanstack/solid-start": "^1.168.34", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-authjs/package.json b/examples/solid/start-basic-authjs/package.json index 6d0e17c9f5..914d7e5184 100644 --- a/examples/solid/start-basic-authjs/package.json +++ b/examples/solid/start-basic-authjs/package.json @@ -13,7 +13,7 @@ "@auth/core": "^0.41.1", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.33", + "@tanstack/solid-start": "^1.168.34", "solid-js": "^1.9.10", "start-authjs": "^1.0.0", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-cloudflare/package.json b/examples/solid/start-basic-cloudflare/package.json index 44eebcc29d..a8764bbec9 100644 --- a/examples/solid/start-basic-cloudflare/package.json +++ b/examples/solid/start-basic-cloudflare/package.json @@ -14,7 +14,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.33", + "@tanstack/solid-start": "^1.168.34", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-netlify/package.json b/examples/solid/start-basic-netlify/package.json index b8cb0df10a..6e51fff097 100644 --- a/examples/solid/start-basic-netlify/package.json +++ b/examples/solid/start-basic-netlify/package.json @@ -11,7 +11,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.33", + "@tanstack/solid-start": "^1.168.34", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-nitro/package.json b/examples/solid/start-basic-nitro/package.json index 77c306e3ad..2af6bc93b8 100644 --- a/examples/solid/start-basic-nitro/package.json +++ b/examples/solid/start-basic-nitro/package.json @@ -11,7 +11,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.33", + "@tanstack/solid-start": "^1.168.34", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-solid-query/package.json b/examples/solid/start-basic-solid-query/package.json index dbf5022c72..f960820abc 100644 --- a/examples/solid/start-basic-solid-query/package.json +++ b/examples/solid/start-basic-solid-query/package.json @@ -15,7 +15,7 @@ "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@tanstack/solid-router-ssr-query": "^1.167.1", - "@tanstack/solid-start": "^1.168.33", + "@tanstack/solid-start": "^1.168.34", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-static/package.json b/examples/solid/start-basic-static/package.json index 73d4fc48e5..babb41b1f0 100644 --- a/examples/solid/start-basic-static/package.json +++ b/examples/solid/start-basic-static/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.33", + "@tanstack/solid-start": "^1.168.34", "@tanstack/start-static-server-functions": "^1.167.19", "redaxios": "^0.5.1", "solid-js": "^1.9.10", diff --git a/examples/solid/start-basic/package.json b/examples/solid/start-basic/package.json index 325f4980b2..40f2780c29 100644 --- a/examples/solid/start-basic/package.json +++ b/examples/solid/start-basic/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.33", + "@tanstack/solid-start": "^1.168.34", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-bun/package.json b/examples/solid/start-bun/package.json index 30fe57b408..a71a471e65 100644 --- a/examples/solid/start-bun/package.json +++ b/examples/solid/start-bun/package.json @@ -18,7 +18,7 @@ "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", "@tanstack/solid-router-ssr-query": "^1.167.1", - "@tanstack/solid-start": "^1.168.33", + "@tanstack/solid-start": "^1.168.34", "@tanstack/router-plugin": "^1.168.23", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" diff --git a/examples/solid/start-convex-better-auth/package.json b/examples/solid/start-convex-better-auth/package.json index f3243c4e69..aa075d1999 100644 --- a/examples/solid/start-convex-better-auth/package.json +++ b/examples/solid/start-convex-better-auth/package.json @@ -15,7 +15,7 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.33", + "@tanstack/solid-start": "^1.168.34", "better-auth": "^1.6.19", "clsx": "^2.1.1", "convex": "^1.28.2", diff --git a/examples/solid/start-counter/package.json b/examples/solid/start-counter/package.json index 1095a35974..f66cd690ec 100644 --- a/examples/solid/start-counter/package.json +++ b/examples/solid/start-counter/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.33", + "@tanstack/solid-start": "^1.168.34", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", diff --git a/examples/solid/start-i18n-paraglide/package.json b/examples/solid/start-i18n-paraglide/package.json index 4c1bf1c563..e6875378a3 100644 --- a/examples/solid/start-i18n-paraglide/package.json +++ b/examples/solid/start-i18n-paraglide/package.json @@ -12,7 +12,7 @@ "@tanstack/solid-devtools": "^0.7.0", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.33", + "@tanstack/solid-start": "^1.168.34", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-large/package.json b/examples/solid/start-large/package.json index 2123824b04..eaecb7db39 100644 --- a/examples/solid/start-large/package.json +++ b/examples/solid/start-large/package.json @@ -15,7 +15,7 @@ "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.33", + "@tanstack/solid-start": "^1.168.34", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", diff --git a/examples/solid/start-streaming-data-from-server-functions/package.json b/examples/solid/start-streaming-data-from-server-functions/package.json index 47e9eeb799..fe2e7fd0e6 100644 --- a/examples/solid/start-streaming-data-from-server-functions/package.json +++ b/examples/solid/start-streaming-data-from-server-functions/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.33", + "@tanstack/solid-start": "^1.168.34", "solid-js": "^1.9.10", "zod": "^4.4.3" }, diff --git a/examples/solid/start-supabase-basic/package.json b/examples/solid/start-supabase-basic/package.json index 7190cbb582..9ac110bec0 100644 --- a/examples/solid/start-supabase-basic/package.json +++ b/examples/solid/start-supabase-basic/package.json @@ -16,7 +16,7 @@ "@supabase/supabase-js": "^2.48.1", "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.33", + "@tanstack/solid-start": "^1.168.34", "solid-js": "^1.9.9", "redaxios": "^0.5.1" }, diff --git a/examples/solid/start-tailwind-v4/package.json b/examples/solid/start-tailwind-v4/package.json index 861e8e28a4..24c8294b53 100644 --- a/examples/solid/start-tailwind-v4/package.json +++ b/examples/solid/start-tailwind-v4/package.json @@ -12,7 +12,7 @@ "dependencies": { "@tanstack/solid-router": "^1.170.18", "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.33", + "@tanstack/solid-start": "^1.168.34", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", "zod": "^4.4.3" diff --git a/packages/react-start-rsc/CHANGELOG.md b/packages/react-start-rsc/CHANGELOG.md index 495ebd4ae9..f75d31ebc0 100644 --- a/packages/react-start-rsc/CHANGELOG.md +++ b/packages/react-start-rsc/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/react-start-rsc +## 0.1.34 + +### Patch Changes + +- [#7944](https://github.com/TanStack/router/pull/7944) [`65f7b7f`](https://github.com/TanStack/router/commit/65f7b7f791272f2ea581a1fe0fcd43183bc01162) - Read request cancellation from the Start storage context so RSC helpers do not pull the Start server barrel into the RSC module graph. + +- Updated dependencies [[`65f7b7f`](https://github.com/TanStack/router/commit/65f7b7f791272f2ea581a1fe0fcd43183bc01162)]: + - @tanstack/start-plugin-core@1.171.26 + ## 0.1.33 ### Patch Changes diff --git a/packages/react-start-rsc/package.json b/packages/react-start-rsc/package.json index a5c77327bd..8968caac3c 100644 --- a/packages/react-start-rsc/package.json +++ b/packages/react-start-rsc/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start-rsc", - "version": "0.1.33", + "version": "0.1.34", "description": "React Server Components support for TanStack Start", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-start-server/CHANGELOG.md b/packages/react-start-server/CHANGELOG.md index 849c59b65b..10a3a2b7a0 100644 --- a/packages/react-start-server/CHANGELOG.md +++ b/packages/react-start-server/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/react-start-server +## 1.167.23 + +### Patch Changes + +- Updated dependencies [[`65f7b7f`](https://github.com/TanStack/router/commit/65f7b7f791272f2ea581a1fe0fcd43183bc01162)]: + - @tanstack/start-server-core@1.169.18 + ## 1.167.22 ### Patch Changes diff --git a/packages/react-start-server/package.json b/packages/react-start-server/package.json index 24d1423273..d593cb9237 100644 --- a/packages/react-start-server/package.json +++ b/packages/react-start-server/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start-server", - "version": "1.167.22", + "version": "1.167.23", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-start/CHANGELOG.md b/packages/react-start/CHANGELOG.md index 8ae3c51a6b..8236580be4 100644 --- a/packages/react-start/CHANGELOG.md +++ b/packages/react-start/CHANGELOG.md @@ -1,5 +1,15 @@ # @tanstack/react-start +## 1.168.35 + +### Patch Changes + +- Updated dependencies [[`65f7b7f`](https://github.com/TanStack/router/commit/65f7b7f791272f2ea581a1fe0fcd43183bc01162), [`65f7b7f`](https://github.com/TanStack/router/commit/65f7b7f791272f2ea581a1fe0fcd43183bc01162)]: + - @tanstack/start-server-core@1.169.18 + - @tanstack/start-plugin-core@1.171.26 + - @tanstack/react-start-rsc@0.1.34 + - @tanstack/react-start-server@1.167.23 + ## 1.168.34 ### Patch Changes diff --git a/packages/react-start/package.json b/packages/react-start/package.json index 11593567d2..c2c77d854b 100644 --- a/packages/react-start/package.json +++ b/packages/react-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start", - "version": "1.168.34", + "version": "1.168.35", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/solid-start-server/CHANGELOG.md b/packages/solid-start-server/CHANGELOG.md index 1f6be110c6..25263c1444 100644 --- a/packages/solid-start-server/CHANGELOG.md +++ b/packages/solid-start-server/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/solid-start-server +## 1.167.23 + +### Patch Changes + +- [#7944](https://github.com/TanStack/router/pull/7944) [`65f7b7f`](https://github.com/TanStack/router/commit/65f7b7f791272f2ea581a1fe0fcd43183bc01162) - Use focused server entrypoints for shared constants and handler helpers so build tooling and framework renderers do not traverse the full Start server barrel. + +- Updated dependencies [[`65f7b7f`](https://github.com/TanStack/router/commit/65f7b7f791272f2ea581a1fe0fcd43183bc01162)]: + - @tanstack/start-server-core@1.169.18 + ## 1.167.22 ### Patch Changes diff --git a/packages/solid-start-server/package.json b/packages/solid-start-server/package.json index b979aba125..8633af6d11 100644 --- a/packages/solid-start-server/package.json +++ b/packages/solid-start-server/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-start-server", - "version": "1.167.22", + "version": "1.167.23", "description": "Modern and scalable routing for Solid applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/solid-start/CHANGELOG.md b/packages/solid-start/CHANGELOG.md index 2f954ccfd4..a0d11f2e8e 100644 --- a/packages/solid-start/CHANGELOG.md +++ b/packages/solid-start/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/solid-start +## 1.168.34 + +### Patch Changes + +- Updated dependencies [[`65f7b7f`](https://github.com/TanStack/router/commit/65f7b7f791272f2ea581a1fe0fcd43183bc01162)]: + - @tanstack/start-server-core@1.169.18 + - @tanstack/start-plugin-core@1.171.26 + - @tanstack/solid-start-server@1.167.23 + ## 1.168.33 ### Patch Changes diff --git a/packages/solid-start/package.json b/packages/solid-start/package.json index a5ad74a583..207712cddd 100644 --- a/packages/solid-start/package.json +++ b/packages/solid-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-start", - "version": "1.168.33", + "version": "1.168.34", "description": "Modern and scalable routing for Solid applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-plugin-core/CHANGELOG.md b/packages/start-plugin-core/CHANGELOG.md index 87f4faceb8..5f83e305cd 100644 --- a/packages/start-plugin-core/CHANGELOG.md +++ b/packages/start-plugin-core/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/start-plugin-core +## 1.171.26 + +### Patch Changes + +- [#7944](https://github.com/TanStack/router/pull/7944) [`65f7b7f`](https://github.com/TanStack/router/commit/65f7b7f791272f2ea581a1fe0fcd43183bc01162) - Use focused server entrypoints for shared constants and handler helpers so build tooling and framework renderers do not traverse the full Start server barrel. + +- Updated dependencies [[`65f7b7f`](https://github.com/TanStack/router/commit/65f7b7f791272f2ea581a1fe0fcd43183bc01162)]: + - @tanstack/start-server-core@1.169.18 + ## 1.171.25 ### Patch Changes diff --git a/packages/start-plugin-core/package.json b/packages/start-plugin-core/package.json index 1855434753..494a5eb900 100644 --- a/packages/start-plugin-core/package.json +++ b/packages/start-plugin-core/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-plugin-core", - "version": "1.171.25", + "version": "1.171.26", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-server-core/CHANGELOG.md b/packages/start-server-core/CHANGELOG.md index 18b68b4bb9..aabe68f0e9 100644 --- a/packages/start-server-core/CHANGELOG.md +++ b/packages/start-server-core/CHANGELOG.md @@ -1,5 +1,11 @@ # @tanstack/start-server-core +## 1.169.18 + +### Patch Changes + +- [#7944](https://github.com/TanStack/router/pull/7944) [`65f7b7f`](https://github.com/TanStack/router/commit/65f7b7f791272f2ea581a1fe0fcd43183bc01162) - Use focused server entrypoints for shared constants and handler helpers so build tooling and framework renderers do not traverse the full Start server barrel. + ## 1.169.17 ### Patch Changes diff --git a/packages/start-server-core/package.json b/packages/start-server-core/package.json index a38803820d..ebadb3c162 100644 --- a/packages/start-server-core/package.json +++ b/packages/start-server-core/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-server-core", - "version": "1.169.17", + "version": "1.169.18", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/vue-start-server/CHANGELOG.md b/packages/vue-start-server/CHANGELOG.md index 90583544fd..22ae2bcba3 100644 --- a/packages/vue-start-server/CHANGELOG.md +++ b/packages/vue-start-server/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/vue-start-server +## 1.167.23 + +### Patch Changes + +- [#7944](https://github.com/TanStack/router/pull/7944) [`65f7b7f`](https://github.com/TanStack/router/commit/65f7b7f791272f2ea581a1fe0fcd43183bc01162) - Use focused server entrypoints for shared constants and handler helpers so build tooling and framework renderers do not traverse the full Start server barrel. + +- Updated dependencies [[`65f7b7f`](https://github.com/TanStack/router/commit/65f7b7f791272f2ea581a1fe0fcd43183bc01162)]: + - @tanstack/start-server-core@1.169.18 + ## 1.167.22 ### Patch Changes diff --git a/packages/vue-start-server/package.json b/packages/vue-start-server/package.json index 56dfad43da..527061f4cc 100644 --- a/packages/vue-start-server/package.json +++ b/packages/vue-start-server/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/vue-start-server", - "version": "1.167.22", + "version": "1.167.23", "description": "Modern and scalable routing for Vue applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/vue-start/CHANGELOG.md b/packages/vue-start/CHANGELOG.md index ffd425ce27..dbb185b0c6 100644 --- a/packages/vue-start/CHANGELOG.md +++ b/packages/vue-start/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/vue-start +## 1.168.33 + +### Patch Changes + +- Updated dependencies [[`65f7b7f`](https://github.com/TanStack/router/commit/65f7b7f791272f2ea581a1fe0fcd43183bc01162)]: + - @tanstack/start-server-core@1.169.18 + - @tanstack/start-plugin-core@1.171.26 + - @tanstack/vue-start-server@1.167.23 + ## 1.168.32 ### Patch Changes diff --git a/packages/vue-start/package.json b/packages/vue-start/package.json index a7dd6292d0..87fb9b22ca 100644 --- a/packages/vue-start/package.json +++ b/packages/vue-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/vue-start", - "version": "1.168.32", + "version": "1.168.33", "description": "Modern and scalable routing for Vue applications", "author": "Tanner Linsley", "license": "MIT", From 45c4ad8d629e291fab70c37900525449e415ffcd Mon Sep 17 00:00:00 2001 From: Flo Date: Tue, 4 Aug 2026 20:28:11 +0200 Subject: [PATCH 44/51] fix(router-core): lane match loader rewrite (#7805) * fix: lane match loader rewrite --------- Co-authored-by: Manuel Schiller <6340397+schiller-manuel@users.noreply.github.com> --- .changeset/lane-match-loader-rewrite.md | 30 + .../interrupted-navigations/shared.ts | 23 - .../scenarios/aborted-requests/shared.ts | 4 +- docs/router/api/router/RouteMatchType.md | 15 +- docs/router/api/router/RouteOptionsType.md | 16 +- docs/router/api/router/RouterOptionsType.md | 4 +- docs/router/api/router/RouterStateType.md | 29 +- docs/router/api/router/RouterType.md | 46 +- docs/router/api/router/useChildMatchesHook.md | 5 +- docs/router/api/router/useMatchesHook.md | 2 +- .../router/api/router/useParentMatchesHook.md | 5 +- docs/router/guide/data-loading.md | 15 +- docs/router/guide/path-params.md | 3 + docs/router/guide/preloading.md | 41 +- docs/router/how-to/debug-router-issues.md | 11 +- e2e/react-router/issue-7120/index.html | 10 + e2e/react-router/issue-7120/package.json | 27 + .../issue-7120/playwright.config.ts | 25 + e2e/react-router/issue-7120/src/main.tsx | 21 + .../issue-7120/src/redirectGate.ts | 35 + .../issue-7120/src/routeTree.gen.ts | 77 + .../issue-7120/src/routes/__root.tsx | 33 + .../issue-7120/src/routes/index.tsx | 5 + .../issue-7120/src/routes/posts.lazy.tsx | 14 + .../issue-7120/src/routes/posts.tsx | 3 + e2e/react-router/issue-7120/src/vite-env.d.ts | 1 + .../issue-7120/tests/issue-7120.repro.spec.ts | 33 + e2e/react-router/issue-7120/tsconfig.json | 15 + e2e/react-router/issue-7120/vite.config.ts | 7 + e2e/react-router/issue-7457/index.html | 10 + e2e/react-router/issue-7457/package.json | 28 + .../issue-7457/playwright.config.ts | 25 + e2e/react-router/issue-7457/src/main.tsx | 32 + .../issue-7457/src/routeTree.gen.ts | 77 + .../issue-7457/src/routes/__root.tsx | 25 + .../issue-7457/src/routes/another.tsx | 5 + .../issue-7457/src/routes/index.tsx | 8 + e2e/react-router/issue-7457/src/vite-env.d.ts | 1 + .../issue-7457/tests/issue-7457.repro.spec.ts | 35 + e2e/react-router/issue-7457/tsconfig.json | 15 + e2e/react-router/issue-7457/vite.config.js | 10 + .../tests/app.spec.ts | 184 ++ e2e/react-start/basic/src/routeTree.gen.ts | 42 + .../src/routes/issue-6221.article.$id.tsx | 42 + .../basic/src/routes/issue-6221.dashboard.tsx | 6 + .../basic/tests/issue-6221-head.spec.ts | 41 + e2e/react-start/hmr/tests/app.spec.ts | 215 +- .../selective-ssr/src/routes/__root.tsx | 10 +- .../selective-ssr/tests/app.spec.ts | 38 + e2e/solid-start/selective-ssr/dev-server.ts | 5 + .../selective-ssr/playwright.config.ts | 22 +- .../selective-ssr/src/routeTree.gen.ts | 69 +- .../src/routes/ssr-false-pending-min.tsx | 48 + .../tests/issue-7283-dev-hydration.spec.ts | 84 + packages/react-router/package.json | 1 + packages/react-router/src/CatchBoundary.tsx | 54 +- packages/react-router/src/ClientOnly.tsx | 6 +- packages/react-router/src/Match.tsx | 487 +-- packages/react-router/src/Matches.tsx | 92 +- packages/react-router/src/RouterProvider.tsx | 4 +- packages/react-router/src/Scripts.tsx | 72 +- packages/react-router/src/Transitioner.tsx | 169 +- .../react-router/src/headContentUtils.tsx | 229 +- .../react-router/src/lazyRouteComponent.tsx | 55 +- .../react-router/src/ssr/RouterClient.tsx | 11 +- .../src/ssr/renderRouterToStream.tsx | 17 +- .../src/ssr/renderRouterToString.tsx | 5 +- packages/react-router/src/useMatch.tsx | 24 +- packages/react-router/src/utils.ts | 5 +- packages/react-router/tests/Scripts.test.tsx | 243 +- ...ancestor-loader-child-pending-min.test.tsx | 157 + ...mponent-preload-retry-pending-min.test.tsx | 114 +- .../tests/component-preload-retry.test.tsx | 128 + .../tests/errorComponent.test.tsx | 40 + ...hydration-capped-boundary-pending.test.tsx | 264 ++ .../issue-4467-lazy-route-pending.test.tsx | 144 + ...sue-4476-react-query-cancellation.test.tsx | 129 + .../tests/issue-4759-pending-frame.test.tsx | 100 + ...e-6107-lazy-chunk-error-component.test.tsx | 89 + ...earch-default-normalization-abort.test.tsx | 169 ++ ...issue-7051-force-pending-suspense.test.tsx | 195 ++ ...-7635-error-head-after-navigation.test.tsx | 100 + packages/react-router/tests/loaders.test.tsx | 44 +- .../react-router/tests/not-found.test.tsx | 141 + .../on-rendered-same-href-state.test.tsx | 66 + ...ding-fallback-promise-replacement.test.tsx | 138 + .../tests/preloaded-mount-resolution.test.tsx | 155 + ...public-presentation-lane-contract.test.tsx | 113 +- .../react-render-owner-contract.test.tsx | 101 + ...tsx => redirect-chain-first-load.test.tsx} | 3 +- packages/react-router/tests/redirect.test.tsx | 192 +- .../tests/renderRouterToStream.test.tsx | 31 +- .../tests/root-pending-min.test.tsx | 186 ++ .../router-client-stream-cleanup.test.tsx | 55 + .../store-updates-during-navigation.test.tsx | 6 +- .../tests/transactional-loading.test.tsx | 233 +- .../transitioner-listener-errors.test.tsx | 81 + .../tests/transitioner-render-ack.test.tsx | 62 + .../react-start-client/src/hydrateStart.ts | 7 +- .../src/tests/hydrateStart.test.ts | 10 + packages/router-core/INTERNALS.md | 1317 ++++++++ packages/router-core/src/Matches.ts | 27 +- packages/router-core/src/await-signal.ts | 27 + packages/router-core/src/index.ts | 4 +- packages/router-core/src/isServer/client.ts | 1 + .../router-core/src/isServer/development.ts | 1 + packages/router-core/src/isServer/server.ts | 1 + packages/router-core/src/load-client.ts | 2651 +++++++++++++++++ packages/router-core/src/load-matches.ts | 1278 -------- packages/router-core/src/load-server.ts | 849 ++++++ packages/router-core/src/redirect.ts | 1 - packages/router-core/src/route.ts | 13 +- packages/router-core/src/router.ts | 1199 +++----- .../src/ssr/createRequestHandler.ts | 125 +- .../router-core/src/ssr/handlerCallback.ts | 86 +- packages/router-core/src/ssr/server.ts | 5 +- packages/router-core/src/ssr/ssr-client.ts | 311 +- packages/router-core/src/ssr/ssr-match-id.ts | 13 +- packages/router-core/src/ssr/ssr-server.ts | 17 +- .../src/ssr/transformStreamWithRouter.ts | 38 +- packages/router-core/src/ssr/types.ts | 1 - packages/router-core/src/stores.ts | 254 +- .../tests/background-assets-stale.test.ts | 79 + .../tests/background-trim-abort.test.ts | 59 +- .../tests/boundary-component-chunk.test.ts | 187 ++ .../router-core/tests/build-location.test.ts | 15 + packages/router-core/tests/callbacks.test.ts | 2 - .../tests/chunk-failure-lifecycle.test.ts | 82 + .../tests/client-lane-adversarial.test.ts | 474 ++- .../error-boundary-cache-generation.test.ts | 6 +- .../tests/fatal-load-rejection.test.ts | 86 + .../router-core/tests/granular-stores.test.ts | 246 -- .../tests/hmr-refresh-lifecycle.test.ts | 466 +++ packages/router-core/tests/hydrate.test.ts | 687 +++-- .../tests/hydrated-stay-match-data.test.ts | 160 + .../hydration-asset-context-order.test.ts | 99 + .../tests/hydration-boundary-chunks.test.ts | 235 ++ .../tests/hydration-currentness.test.ts | 675 +++++ ...ydration-terminal-error-child-head.test.ts | 175 ++ .../invalidate-pre-rematch-failure.test.ts | 63 + .../issue-3179-preload-cached-cause.test.ts | 59 + ...re-navigate-after-initial-redirect.test.ts | 40 + .../issue-3928-rapid-reload-abort.test.ts | 138 + ...4078-loader-notfound-root-boundary.test.ts | 20 +- ...-4444-param-parse-error-lazy-child.test.ts | 74 + ...4572-preload-root-beforeload-flags.test.ts | 173 ++ ...ssue-4684-head-on-beforeload-error.test.ts | 44 +- ...ue-5106-hydrated-notfound-boundary.test.ts | 128 +- ...27-hydration-root-global-not-found.test.ts | 136 + .../issue-6107-preload-chunk-failure.test.ts | 47 + .../issue-6221-head-waits-for-loader.test.ts | 73 +- .../issue-6351-fuzzy-notfound-layout.test.ts | 142 + ...search-default-normalization-abort.test.ts | 29 +- ...ssue-7379-head-matches-direct-load.test.ts | 81 + ...nt-beforeload-context-child-loader.test.ts | 108 + ...ue-7759-in-flight-preload-eviction.test.ts | 43 + .../tests/load-client-wait-for.test.ts | 209 ++ .../tests/load-route-chunk.test.ts | 30 + packages/router-core/tests/load.test.ts | 590 ++-- .../loader-architecture-regressions.test.ts | 1044 ++++++- .../tests/loader-self-abort.test.ts | 114 +- .../tests/loader-thrown-promise.test.ts | 57 + .../masked-location-state-commit.test.ts | 86 +- .../tests/parent-match-promise.test.ts | 45 +- .../tests/preflight-reentrant-context.test.ts | 101 + .../preflight-signal-currentness.test.ts | 49 + .../tests/preload-adoption.test.ts | 234 ++ .../tests/preload-beforeload-reuse.test.ts | 234 +- .../tests/preload-navigation-adoption.test.ts | 78 + .../preload-public-cache-behavior.test.ts | 635 +++- .../preload-public-signal-lifetime.test.ts | 155 + .../public-client-loading-contract.test.ts | 585 +++- .../tests/public-hydration-contract.test.ts | 1628 ++++++++++ .../public-preload-lane-contract.test.ts | 1023 ++++++- .../tests/rendered-matches.test.ts | 30 + packages/router-core/tests/routerTestUtils.ts | 23 + .../same-destination-navigation-join.test.ts | 245 +- ...rver-async-headers-decorative-hang.test.ts | 52 + .../server-beforeload-preload-flag.test.ts | 65 +- .../server-chunk-failure-lifecycle.test.ts | 238 ++ .../server-concurrent-error-notfound.test.ts | 467 +++ .../tests/server-loader-abort-error.test.ts | 294 +- .../tests/server-planning-redirect.test.ts | 79 + .../tests/server-route-lifecycle.test.ts | 76 + .../tests/server-serial-ssr-notfound.test.ts | 71 + .../tests/server-ssr-false-assets.test.ts | 192 ++ .../tests/server-ssr-option-error.test.ts | 178 ++ .../router-core/tests/ssr-match-id.bench.ts | 53 + .../router-core/tests/ssr-match-id.test.ts | 11 + .../tests/ssr-server-cleanup.test.ts | 231 +- .../tests/ssr-server-manifest.test.ts | 52 + .../tests/stay-match-abort.test.ts | 247 ++ .../tests/transformStreamWithRouter.test.ts | 32 + packages/router-devtools-core/package.json | 4 +- .../router-devtools-core/src/AgeTicker.tsx | 6 +- .../src/BaseTanStackRouterDevtoolsPanel.tsx | 77 +- .../router-devtools-core/src/useStyles.tsx | 3 +- packages/router-devtools-core/src/utils.tsx | 1 - .../tests/cache-replacement.test.ts | 104 + packages/router-devtools-core/tsconfig.json | 2 +- packages/router-devtools-core/vite.config.ts | 14 + .../src/core/hmr/handle-route-update.ts | 156 +- packages/router-plugin/tests/add-hmr.test.ts | 30 + .../snapshots/react/arrow-function@true.tsx | 88 +- .../react/arrow-function@webpack-hot.tsx | 88 +- ...ateFileRoute-lowercase-components@true.tsx | 88 +- .../createRootRoute-inline-component@true.tsx | 88 +- ...ateRootRoute-lowercase-components@true.tsx | 88 +- .../snapshots/react/createRootRoute@true.tsx | 88 +- ...ateRootRouteWithContext-type-args@true.tsx | 88 +- .../explicit-undefined-component@true.tsx | 88 +- .../react/function-declaration@true.tsx | 88 +- .../snapshots/react/multi-component@true.tsx | 88 +- .../react/string-literal-keys@true.tsx | 88 +- .../snapshots/solid/arrow-function@true.tsx | 88 +- .../tests/handle-route-update.test.ts | 319 +- packages/solid-router/src/CatchBoundary.tsx | 4 +- packages/solid-router/src/ClientOnly.tsx | 4 +- packages/solid-router/src/Match.tsx | 637 ++-- packages/solid-router/src/Matches.tsx | 33 +- packages/solid-router/src/Scripts.tsx | 105 +- packages/solid-router/src/Transitioner.tsx | 126 +- packages/solid-router/src/awaited.tsx | 2 +- .../solid-router/src/headContentUtils.tsx | 205 +- .../solid-router/src/lazyRouteComponent.tsx | 8 +- packages/solid-router/src/link.tsx | 20 +- packages/solid-router/src/matchContext.tsx | 4 - packages/solid-router/src/routerStores.ts | 59 +- .../solid-router/src/ssr/RouterClient.tsx | 13 +- .../src/ssr/renderRouterToStream.tsx | 7 +- .../src/ssr/renderRouterToString.tsx | 5 +- packages/solid-router/src/useMatch.tsx | 28 +- packages/solid-router/tests/Scripts.test.tsx | 158 +- .../tests/component-preload-retry.test.tsx | 69 +- .../tests/createLazyRoute.test.tsx | 74 +- .../tests/errorComponent.test.tsx | 102 +- packages/solid-router/tests/loaders.test.tsx | 103 +- ...ding-fallback-promise-replacement.test.tsx | 179 ++ packages/solid-router/tests/redirect.test.tsx | 113 - .../router-client-stream-cleanup.test.tsx | 39 + .../store-updates-during-navigation.test.tsx | 14 +- .../transitioner-listener-errors.test.tsx | 80 + .../tests/transitioner-render-ack.test.tsx | 333 +++ .../solid-start-client/src/hydrateStart.ts | 7 +- .../src/tests/hydrateStart.test.ts | 10 + .../src/client/hydrateStart.ts | 2 +- .../src/vite/dev-server-plugin/plugin.ts | 6 +- .../src/createStartHandler.ts | 254 +- .../tests/createStartHandler.test.ts | 677 ++++- packages/vue-router/src/CatchBoundary.tsx | 76 +- packages/vue-router/src/Match.tsx | 491 +-- packages/vue-router/src/Matches.tsx | 97 +- packages/vue-router/src/Scripts.tsx | 72 +- packages/vue-router/src/Transitioner.tsx | 224 +- packages/vue-router/src/awaited.tsx | 2 +- packages/vue-router/src/headContentUtils.tsx | 239 +- .../vue-router/src/lazyRouteComponent.tsx | 1 + packages/vue-router/src/matchContext.tsx | 35 +- packages/vue-router/src/not-found.tsx | 2 +- packages/vue-router/src/routerStores.ts | 41 +- packages/vue-router/src/ssr/RouterClient.tsx | 17 +- .../src/ssr/renderRouterToStream.tsx | 12 +- .../src/ssr/renderRouterToString.tsx | 5 +- packages/vue-router/src/useMatch.tsx | 56 +- packages/vue-router/tests/Scripts.test.tsx | 98 +- .../vue-router/tests/Transitioner.test.tsx | 37 +- .../tests/component-preload-retry.test.tsx | 65 + ...hydration-capped-boundary-pending.test.tsx | 153 + packages/vue-router/tests/loaders.test.tsx | 38 + packages/vue-router/tests/redirect.test.tsx | 104 - .../router-client-stream-cleanup.test.tsx | 46 + packages/vue-router/tests/ssr-test-utils.ts | 43 + .../store-updates-during-navigation.test.tsx | 20 +- .../transitioner-listener-errors.test.tsx | 75 + .../transitioner-remount-rendered.test.tsx | 29 +- packages/vue-router/tests/useMatch.test.tsx | 38 + .../vue-start-client/src/hydrateStart.test.ts | 23 + packages/vue-start-client/src/hydrateStart.ts | 16 +- pnpm-lock.yaml | 77 + 279 files changed, 28223 insertions(+), 8503 deletions(-) create mode 100644 .changeset/lane-match-loader-rewrite.md create mode 100644 e2e/react-router/issue-7120/index.html create mode 100644 e2e/react-router/issue-7120/package.json create mode 100644 e2e/react-router/issue-7120/playwright.config.ts create mode 100644 e2e/react-router/issue-7120/src/main.tsx create mode 100644 e2e/react-router/issue-7120/src/redirectGate.ts create mode 100644 e2e/react-router/issue-7120/src/routeTree.gen.ts create mode 100644 e2e/react-router/issue-7120/src/routes/__root.tsx create mode 100644 e2e/react-router/issue-7120/src/routes/index.tsx create mode 100644 e2e/react-router/issue-7120/src/routes/posts.lazy.tsx create mode 100644 e2e/react-router/issue-7120/src/routes/posts.tsx create mode 100644 e2e/react-router/issue-7120/src/vite-env.d.ts create mode 100644 e2e/react-router/issue-7120/tests/issue-7120.repro.spec.ts create mode 100644 e2e/react-router/issue-7120/tsconfig.json create mode 100644 e2e/react-router/issue-7120/vite.config.ts create mode 100644 e2e/react-router/issue-7457/index.html create mode 100644 e2e/react-router/issue-7457/package.json create mode 100644 e2e/react-router/issue-7457/playwright.config.ts create mode 100644 e2e/react-router/issue-7457/src/main.tsx create mode 100644 e2e/react-router/issue-7457/src/routeTree.gen.ts create mode 100644 e2e/react-router/issue-7457/src/routes/__root.tsx create mode 100644 e2e/react-router/issue-7457/src/routes/another.tsx create mode 100644 e2e/react-router/issue-7457/src/routes/index.tsx create mode 100644 e2e/react-router/issue-7457/src/vite-env.d.ts create mode 100644 e2e/react-router/issue-7457/tests/issue-7457.repro.spec.ts create mode 100644 e2e/react-router/issue-7457/tsconfig.json create mode 100644 e2e/react-router/issue-7457/vite.config.js create mode 100644 e2e/react-start/basic/src/routes/issue-6221.article.$id.tsx create mode 100644 e2e/react-start/basic/src/routes/issue-6221.dashboard.tsx create mode 100644 e2e/react-start/basic/tests/issue-6221-head.spec.ts create mode 100644 e2e/solid-start/selective-ssr/dev-server.ts create mode 100644 e2e/solid-start/selective-ssr/src/routes/ssr-false-pending-min.tsx create mode 100644 e2e/solid-start/selective-ssr/tests/issue-7283-dev-hydration.spec.ts create mode 100644 packages/react-router/tests/ancestor-loader-child-pending-min.test.tsx create mode 100644 packages/react-router/tests/component-preload-retry.test.tsx create mode 100644 packages/react-router/tests/hydration-capped-boundary-pending.test.tsx create mode 100644 packages/react-router/tests/issue-4467-lazy-route-pending.test.tsx create mode 100644 packages/react-router/tests/issue-4476-react-query-cancellation.test.tsx create mode 100644 packages/react-router/tests/issue-4759-pending-frame.test.tsx create mode 100644 packages/react-router/tests/issue-6107-lazy-chunk-error-component.test.tsx create mode 100644 packages/react-router/tests/issue-6371-search-default-normalization-abort.test.tsx create mode 100644 packages/react-router/tests/issue-7051-force-pending-suspense.test.tsx create mode 100644 packages/react-router/tests/issue-7635-error-head-after-navigation.test.tsx create mode 100644 packages/react-router/tests/on-rendered-same-href-state.test.tsx create mode 100644 packages/react-router/tests/pending-fallback-promise-replacement.test.tsx create mode 100644 packages/react-router/tests/preloaded-mount-resolution.test.tsx create mode 100644 packages/react-router/tests/react-render-owner-contract.test.tsx rename packages/react-router/tests/{issue-7457-redirect-chain-first-load.test.tsx => redirect-chain-first-load.test.tsx} (96%) create mode 100644 packages/react-router/tests/root-pending-min.test.tsx create mode 100644 packages/react-router/tests/router-client-stream-cleanup.test.tsx create mode 100644 packages/react-router/tests/transitioner-listener-errors.test.tsx create mode 100644 packages/router-core/INTERNALS.md create mode 100644 packages/router-core/src/await-signal.ts create mode 100644 packages/router-core/src/load-client.ts delete mode 100644 packages/router-core/src/load-matches.ts create mode 100644 packages/router-core/src/load-server.ts create mode 100644 packages/router-core/tests/background-assets-stale.test.ts create mode 100644 packages/router-core/tests/chunk-failure-lifecycle.test.ts create mode 100644 packages/router-core/tests/fatal-load-rejection.test.ts create mode 100644 packages/router-core/tests/hmr-refresh-lifecycle.test.ts create mode 100644 packages/router-core/tests/hydrated-stay-match-data.test.ts create mode 100644 packages/router-core/tests/hydration-asset-context-order.test.ts create mode 100644 packages/router-core/tests/hydration-boundary-chunks.test.ts create mode 100644 packages/router-core/tests/hydration-currentness.test.ts create mode 100644 packages/router-core/tests/hydration-terminal-error-child-head.test.ts create mode 100644 packages/router-core/tests/invalidate-pre-rematch-failure.test.ts create mode 100644 packages/router-core/tests/issue-3179-preload-cached-cause.test.ts create mode 100644 packages/router-core/tests/issue-3920-on-before-navigate-after-initial-redirect.test.ts create mode 100644 packages/router-core/tests/issue-3928-rapid-reload-abort.test.ts create mode 100644 packages/router-core/tests/issue-4444-param-parse-error-lazy-child.test.ts create mode 100644 packages/router-core/tests/issue-4572-preload-root-beforeload-flags.test.ts create mode 100644 packages/router-core/tests/issue-5427-hydration-root-global-not-found.test.ts create mode 100644 packages/router-core/tests/issue-6107-preload-chunk-failure.test.ts create mode 100644 packages/router-core/tests/issue-6351-fuzzy-notfound-layout.test.ts create mode 100644 packages/router-core/tests/issue-7379-head-matches-direct-load.test.ts create mode 100644 packages/router-core/tests/issue-7602-parent-beforeload-context-child-loader.test.ts create mode 100644 packages/router-core/tests/issue-7759-in-flight-preload-eviction.test.ts create mode 100644 packages/router-core/tests/load-client-wait-for.test.ts create mode 100644 packages/router-core/tests/load-route-chunk.test.ts create mode 100644 packages/router-core/tests/loader-thrown-promise.test.ts create mode 100644 packages/router-core/tests/preflight-reentrant-context.test.ts create mode 100644 packages/router-core/tests/preflight-signal-currentness.test.ts create mode 100644 packages/router-core/tests/preload-public-signal-lifetime.test.ts create mode 100644 packages/router-core/tests/public-hydration-contract.test.ts create mode 100644 packages/router-core/tests/rendered-matches.test.ts create mode 100644 packages/router-core/tests/server-async-headers-decorative-hang.test.ts create mode 100644 packages/router-core/tests/server-chunk-failure-lifecycle.test.ts create mode 100644 packages/router-core/tests/server-concurrent-error-notfound.test.ts create mode 100644 packages/router-core/tests/server-planning-redirect.test.ts create mode 100644 packages/router-core/tests/server-route-lifecycle.test.ts create mode 100644 packages/router-core/tests/server-serial-ssr-notfound.test.ts create mode 100644 packages/router-core/tests/server-ssr-false-assets.test.ts create mode 100644 packages/router-core/tests/server-ssr-option-error.test.ts create mode 100644 packages/router-core/tests/ssr-match-id.bench.ts create mode 100644 packages/router-core/tests/stay-match-abort.test.ts create mode 100644 packages/router-devtools-core/tests/cache-replacement.test.ts create mode 100644 packages/solid-router/tests/pending-fallback-promise-replacement.test.tsx create mode 100644 packages/solid-router/tests/router-client-stream-cleanup.test.tsx create mode 100644 packages/solid-router/tests/transitioner-listener-errors.test.tsx create mode 100644 packages/solid-router/tests/transitioner-render-ack.test.tsx create mode 100644 packages/vue-router/tests/component-preload-retry.test.tsx create mode 100644 packages/vue-router/tests/hydration-capped-boundary-pending.test.tsx create mode 100644 packages/vue-router/tests/router-client-stream-cleanup.test.tsx create mode 100644 packages/vue-router/tests/ssr-test-utils.ts create mode 100644 packages/vue-router/tests/transitioner-listener-errors.test.tsx create mode 100644 packages/vue-start-client/src/hydrateStart.test.ts diff --git a/.changeset/lane-match-loader-rewrite.md b/.changeset/lane-match-loader-rewrite.md new file mode 100644 index 0000000000..911a956864 --- /dev/null +++ b/.changeset/lane-match-loader-rewrite.md @@ -0,0 +1,30 @@ +--- +'@tanstack/router-core': patch +'@tanstack/react-router': patch +'@tanstack/solid-router': patch +'@tanstack/vue-router': patch +--- + +Rewrite match loading around a lane-based scheduler that tracks each navigation, preload, and background reload as an ordered unit of work. This fixes pending/redirect/retry state leaking between overlapping navigations, restores correct SSR status codes for redirects, errors, and not-found responses, and closes hydration gaps where the client re-ran work the server had already completed. + +- Invalidation now retires matching active preloads so older speculative loader results cannot become fresh cache data after invalidation. +- Route `headers()` now only runs on the server, matching the documented behavior — it is no longer invoked during client-side asset projection. +- Default `gcTime` and `preloadGcTime` are reduced from 30 minutes (`1_800_000`) to 5 minutes (`300_000`). + +**Removed / changed public API** + +- `RouterState` no longer includes `loadedAt`, `isTransitioning`, `statusCode`, or `redirect`. Use `match.updatedAt` in place of `loadedAt`; subscribe to `router.state.status` / `router.state.isLoading` in place of `isTransitioning`; server response status and redirect handling are now internal to the server loader and are no longer exposed on `router.state`. +- `RouteMatch.fetchCount` has been removed, with no replacement — it was purely informational. +- `RouteMatch.status` no longer includes `'redirected'` (it remains `'pending' | 'success' | 'error' | 'notFound'`) — redirected matches are dropped from the match list instead of being rendered. +- `RouteMatch.globalNotFound` has been renamed and privatized to the internal `_notFound` field. Use `match.status === 'notFound'` instead. +- Removed `RouterCore` members `getMatch()`, `updateMatch()`, `cancelMatch()`, and `cancelMatches()` — read matches from `router.state.matches` (e.g. `router.state.matches.find((m) => m.id === id)`); there is no replacement for mutating or cancelling an individual in-flight match from outside the router. +- Removed `hasNotFoundMatch()` — use `router.state.matches.some((m) => m.status === 'notFound')`. +- Removed `looseRoutesById` — use `routesById`. +- Removed `isPrerendering()`, `isViewTransitionTypesSupported`, and `viewTransitionPromise`, with no replacement. +- Removed `getParsedLocationHref()` and `clearExpiredCache()`, with no replacement — expired cache entries are now reconciled automatically as part of match commit. +- Removed `latestLoadPromise` and `beforeLoad()`, with no replacement. +- `commitLocationPromise` and `pendingBuiltLocation` are now private (`_commitPromise`, `_pendingLocation`) and no longer part of the public `RouterCore` surface. +- Removed the exported `GetMatchFn` and `UpdateMatchFn` types, along with the methods they typed. +- Removed the standalone `getMatchedRoutes()` export from `@tanstack/router-core` — use the `router.getMatchedRoutes()` instance method instead. +- `MatchRoutesOpts.preload` and `MatchRoutesOpts.dest` have been removed. +- `StartTransitionFn` is now `(fn, expected, urgent?) => Promise` (previously `(fn) => void`). This only affects custom framework adapters that implement `startTransition`. diff --git a/benchmarks/memory/client/scenarios/interrupted-navigations/shared.ts b/benchmarks/memory/client/scenarios/interrupted-navigations/shared.ts index f7f3a7cd8b..80f234f0c8 100644 --- a/benchmarks/memory/client/scenarios/interrupted-navigations/shared.ts +++ b/benchmarks/memory/client/scenarios/interrupted-navigations/shared.ts @@ -35,7 +35,6 @@ type RenderedEvent = { } type InterruptedNavigationRouter = { - latestLoadPromise: Promise | undefined load: () => Promise navigate: (options: { to: '/fast/$id' | '/slow/$id' @@ -104,18 +103,6 @@ function assertSlowNavigationSettlement(settlement: NavigationSettlement) { ) } -async function awaitExpectedLoadSettlement(loadPromise: Promise) { - try { - await loadPromise - } catch (reason) { - if (reasonHasAbortShape(reason) || reasonHasCancellationShape(reason)) { - return - } - - throw reason - } -} - function reasonHasAbortShape(reason: unknown) { return reason instanceof DOMException && reason.name === 'AbortError' } @@ -144,7 +131,6 @@ export function createWorkload( let navigateFast: (id: string) => Promise = uninitialized let startSlowNavigation: (id: string) => Promise = uninitializedSettlement - let getLatestLoadPromise: () => Promise | undefined = () => undefined function assertRenderedPage(page: 'shell' | 'fast', id?: string) { const element = container?.querySelector('[data-bench-page]') @@ -203,7 +189,6 @@ export function createWorkload( const mounted = mountTestApp(container) const router = mounted.router as InterruptedNavigationRouter unmount = mounted.unmount - getLatestLoadPromise = () => router.latestLoadPromise unsub = router.subscribe('onRendered', (event) => { if ( @@ -265,7 +250,6 @@ export function createWorkload( expectedRenderedPath = undefined navigateFast = uninitialized startSlowNavigation = uninitializedSettlement - getLatestLoadPromise = () => undefined } async function interrupt( @@ -276,12 +260,6 @@ export function createWorkload( const slowNavigation = startSlowNavigation(slowId) await waitForSlowLoader(slowId) - const slowLoadPromise = getLatestLoadPromise() - - if (!slowLoadPromise) { - throw new Error(`Slow navigation did not start a load for id: ${slowId}`) - } - await navigateFast(fastId) resolveSlowLoader(slowId) @@ -291,7 +269,6 @@ export function createWorkload( assertSlowNavigationSettlement(settlement) } - await awaitExpectedLoadSettlement(slowLoadPromise) await drainMicrotasks() } diff --git a/benchmarks/memory/server/scenarios/aborted-requests/shared.ts b/benchmarks/memory/server/scenarios/aborted-requests/shared.ts index f7f63c17e6..c98f462538 100644 --- a/benchmarks/memory/server/scenarios/aborted-requests/shared.ts +++ b/benchmarks/memory/server/scenarios/aborted-requests/shared.ts @@ -27,11 +27,11 @@ const textDecoder = new TextDecoder() const abortedRequestModes: Record = { react: { readMode: 'first-chunk', - cancelMode: 'plain', + cancelMode: 'swallow-abort-error', }, solid: { readMode: 'first-chunk', - cancelMode: 'plain', + cancelMode: 'swallow-abort-error', }, vue: { readMode: 'shell-before-deferred', diff --git a/docs/router/api/router/RouteMatchType.md b/docs/router/api/router/RouteMatchType.md index 251c2c031e..b5009ebe68 100644 --- a/docs/router/api/router/RouteMatchType.md +++ b/docs/router/api/router/RouteMatchType.md @@ -11,9 +11,8 @@ interface RouteMatch { routeId: string pathname: string params: Route['allParams'] - status: 'pending' | 'success' | 'error' | 'redirected' | 'notFound' + status: 'pending' | 'success' | 'error' | 'notFound' isFetching: false | 'beforeLoad' | 'loader' - showPending: boolean error: unknown paramsError: unknown searchError: unknown @@ -21,9 +20,17 @@ interface RouteMatch { loaderData?: Route['loaderData'] context: Route['allContext'] search: Route['fullSearchSchema'] - fetchedAt: number abortController: AbortController - cause: 'enter' | 'stay' + cause: 'preload' | 'enter' | 'stay' ssr?: boolean | 'data-only' } ``` + +`status` describes the match's render state. `isFetching` independently exposes +active `beforeLoad` or loader work. In particular, a successful match can report +`isFetching: 'loader'` while stale data remains visible during a background +reload. + +The router state can contain matches below the pending, error, or not-found +boundary. Those matches remain observable as part of the structurally matched +lane even though the route renderer stops at the boundary. diff --git a/docs/router/api/router/RouteOptionsType.md b/docs/router/api/router/RouteOptionsType.md index 1fb4c4eb30..a1c0cb42aa 100644 --- a/docs/router/api/router/RouteOptionsType.md +++ b/docs/router/api/router/RouteOptionsType.md @@ -56,6 +56,7 @@ The `RouteOptions` type accepts an object with the following properties: - Type: `(rawSearchParams: unknown) => TSearchSchema` - Optional - A function that will be called when this route is matched and passed the raw search params from the current location and return valid parsed search params. If this function throws, the route will be put into an error state and the error will be thrown during render. If this function does not throw, its return value will be used as the route's search params and the return type will be inferred into the rest of the router. +- This is a planning callback. It must be deterministic and side-effect-free for the same input; do not navigate or mutate application/router state from it. - Optionally, the parameter type can be tagged with the `SearchSchemaInput` type like this: `(searchParams: TSearchSchemaInput & SearchSchemaInput) => TSearchSchema`. If this tag is present, `TSearchSchemaInput` will be used to type the `search` property of `` and `navigate()` **instead of** `TSearchSchema`. The difference between `TSearchSchemaInput` and `TSearchSchema` can be useful, for example, to express optional search parameters. ### `search.middlewares` property @@ -70,6 +71,7 @@ The `RouteOptions` type accepts an object with the following properties: - Type: `(rawParams: Record) => TParams` - Optional - A function that will be called when this route is matched and passed the raw params from the current location and return valid parsed params. If this function throws, the route will be put into an error state and the error will be thrown during render. If this function does not throw, its return value will be used as the route's params and the return type will be inferred into the rest of the router. +- This is a planning callback. It must be deterministic and side-effect-free for the same input. ### `stringifyParams` method (⚠️ deprecated, use `params.stringify` instead) @@ -82,6 +84,7 @@ The `RouteOptions` type accepts an object with the following properties: - Type: `(rawParams: Record) => TParams | false` - Optional - A function that will be called when this route is matched and passed the raw params from the current location and return valid parsed params. If this function throws, the route will be put into an error state and the error will be thrown during render. If this function returns parsed params, its return value will be used as the route's params and the return type will be inferred into the rest of the router. +- This is a planning callback. It must be deterministic and side-effect-free for the same input. - Experimental: returning `false` during incoming route matching skips this route and allows matching to continue to another candidate route. ### `params.priority` property @@ -176,6 +179,7 @@ type loaderDeps = (opts: { search: TFullSearchSchema }) => Record - Optional - A function that will be called before this route is matched to provide additional unique identification to the route match and serve as a dependency tracker for when the match should be reloaded. It should return any serializable value that can uniquely identify the route match from navigation to navigation. +- This is a planning callback and cache-key function. It must be deterministic and side-effect-free for the same validated search input. The returned value and any serialization methods on it, such as `toJSON`, must also be side-effect-free. - By default, path params are already used to uniquely identify a route match, so it's unnecessary to return these here. - If your route match relies on search params for unique identification, it's required that you return them here so they can be made available in the `loader`'s `deps` argument. @@ -191,14 +195,14 @@ type loaderDeps = (opts: { search: TFullSearchSchema }) => Record - Type: `number` - Optional - Defaults to `routerOptions.defaultPreloadStaleTime`, which defaults to `30_000` ms (30 seconds) -- The amount of time in milliseconds that a route match's loader data will be considered fresh when preloading. If a route match is preloaded again within this time frame, its loader data will not be reloaded. If a route match is loaded (for navigation) within this time frame, the normal `staleTime` is used instead. +- The amount of time in milliseconds that loader data produced by a preload is considered fresh. Another preload or the first navigation can reuse it within this interval. After navigation accepts the generation, subsequent freshness uses `staleTime`. ### `gcTime` property - Type: `number` - Optional -- Defaults to `routerOptions.defaultGcTime`, which defaults to 30 minutes. -- The amount of time in milliseconds that a route match's loader data will be kept in memory after a preload or it is no longer in use. +- Defaults to `routerOptions.defaultGcTime`, which defaults to 5 minutes. +- The amount of time in milliseconds that loader data from an ordinary load will be kept in memory after it is no longer in use. ### `shouldReload` property @@ -234,12 +238,12 @@ type loaderDeps = (opts: { search: TFullSearchSchema }) => Record - Defaults to `routerOptions.defaultPendingMinMs` which defaults to `500` - The minimum amount of time in milliseconds that the pending component will be shown for if it is shown. This is useful to prevent the pending component from flashing on the screen for a split second. -### `preloadMaxAge` property +### `preloadGcTime` property - Type: `number` - Optional -- Defaults to `30_000` ms (30 seconds) -- The maximum amount of time in milliseconds that a route's preloaded route data will be cached for. If a route is not matched within this time frame, its loader data will be discarded. +- Defaults to `routerOptions.defaultPreloadGcTime`, which defaults to 5 minutes. +- The amount of time in milliseconds that loader data produced by a preload can remain in memory while it is not in use. This controls retention; use `preloadStaleTime` to control whether retained data is fresh enough to reuse without reloading. ### `preSearchFilters` property (⚠️ deprecated, use `search.middlewares` instead) diff --git a/docs/router/api/router/RouterOptionsType.md b/docs/router/api/router/RouterOptionsType.md index 7127e1191e..91f13e0e50 100644 --- a/docs/router/api/router/RouterOptionsType.md +++ b/docs/router/api/router/RouterOptionsType.md @@ -129,14 +129,14 @@ The `RouterOptions` type accepts an object with the following properties and met - Type: `number` - Optional -- Defaults to `routerOptions.defaultGcTime`, which defaults to 30 minutes. +- Defaults to 5 minutes. - The default `preloadGcTime` a route should use if no preloadGcTime is provided. ### `defaultGcTime` property - Type: `number` - Optional -- Defaults to 30 minutes. +- Defaults to 5 minutes. - The default `gcTime` a route should use if no gcTime is provided. ### `defaultOnCatch` property diff --git a/docs/router/api/router/RouterStateType.md b/docs/router/api/router/RouterStateType.md index a77f4e775f..4da9cd9bb6 100644 --- a/docs/router/api/router/RouterStateType.md +++ b/docs/router/api/router/RouterStateType.md @@ -3,16 +3,17 @@ id: RouterStateType title: RouterState type --- -The `RouterState` type represents shape of the internal state of the router. The Router's internal state is useful, if you need to access certain internals of the router, such as any pending matches, is the router in its loading state, etc. +The `RouterState` type represents the observable state of the router, including +the requested and resolved locations, match presentation, and foreground loading +status. ```tsx type RouterState = { status: 'pending' | 'idle' isLoading: boolean - isTransitioning: boolean matches: Array location: ParsedLocation - resolvedLocation: ParsedLocation + resolvedLocation?: ParsedLocation } ``` @@ -23,22 +24,24 @@ The `RouterState` type contains all of the properties that are available on the ### `status` property - Type: `'pending' | 'idle'` -- The current status of the router. If the router is pending, it means that it is currently loading a route or the router is still transitioning to the new route. +- The current foreground status of the router. `pending` means the requested route is still loading or waiting for its framework transition to settle. ### `isLoading` property - Type: `boolean` -- `true` if the router is currently loading a route or waiting for a route to finish loading. - -### `isTransitioning` property - -- Type: `boolean` -- `true` if the router is currently transitioning to a new route. +- `true` when `status` is `pending`. +- Background loader refreshes do not change this value. Inspect each match's `isFetching` field to observe foreground `beforeLoad`/loader work and background loader work. ### `matches` property - Type: [`Array`](./RouteMatchType.md) -- An array of all of the route matches that have been resolved and are currently active. +- The match presentation currently exposed to the application. +- A navigation can keep the previous presentation visible until pending UI is + published. Once the destination is presented, this contains its complete + structurally matched lane, including descendants that are still loading. +- Error and not-found results also preserve the complete structurally matched + lane. Rendering stops at the selected pending or terminal boundary; it does + not truncate this array. ### `location` property @@ -47,5 +50,5 @@ The `RouterState` type contains all of the properties that are available on the ### `resolvedLocation` property -- Type: [`ParsedLocation`](./ParsedLocationType.md) -- The location that the router has resolved and loaded. +- Type: [`ParsedLocation`](./ParsedLocationType.md) | `undefined` +- The location whose load and framework transition have settled. It is `undefined` before the initial location resolves. diff --git a/docs/router/api/router/RouterType.md b/docs/router/api/router/RouterType.md index 757a6098ca..cf857d5e88 100644 --- a/docs/router/api/router/RouterType.md +++ b/docs/router/api/router/RouterType.md @@ -35,16 +35,6 @@ An instance of the `Router` has the following properties and methods: - Matches a pathname and search params against the router's route tree and returns an array of route matches. - If `opts.throwOnError` is `true`, any errors that occur during the matching process will be thrown (in addition to being returned in the route match's `error` property). -### `.cancelMatch` method - -- Type: `(matchId: string) => void` -- Cancels a route match that is currently pending by calling `match.abortController.abort()`. - -### `.cancelMatches` method - -- Type: `() => void` -- Cancels all route matches that are currently pending by calling `match.abortController.abort()` on each one. - ### `.buildLocation` method Builds a new parsed location object that can be used later to navigate to a new location. @@ -138,23 +128,27 @@ Navigates to a new location. ### `.invalidate` method -Invalidates route matches by forcing their `beforeLoad` and `load` functions to be called again. +Invalidates selected route-match generations, reruns their loading lifecycle, +reruns `beforeLoad`, and reloads their loaders through the normal loading +protocol. - Type: `(opts?: {filter?: (d: MakeRouteMatchUnion) => boolean, sync?: boolean, forcePending?: boolean }) => Promise` - This is useful any time your loader data might be out of date or stale. For example, if you have a route that displays a list of posts, and you have a loader function that fetches the list of posts from an API, you might want to invalidate the route matches for that route any time a new post is created so that the list of posts is always up-to-date. -- if `filter` is not supplied, all matches will be invalidated -- if `filter` is supplied, only matches for which `filter` returns `true` will be invalidated. -- if `sync` is true, the promise returned by this function will only resolve once all loaders have finished. -- if `forcePending` is true, the invalidated matches will be put into `'pending'` state regardless whether they are in `'error'` state or not. +- If `filter` is not supplied, all committed, cached, and in-flight match generations are invalidated. +- If `filter` is supplied, it is evaluated against committed, cached, and in-flight matches. Selecting one generation invalidates every committed, cached, or in-flight generation with the same match ID. +- Invalidation reruns `beforeLoad`; reusable loader data is marked stale and reloads through the normal loading protocol. Route-level `context` remains reusable while the match ID is unchanged. +- If `sync` is `true`, stale loader work is blocking and the returned promise resolves after it finishes instead of leaving a background refresh detached. +- If `forcePending` is `true`, selected routes that need loading enter the normal pending protocol even when successful data was already available. - You might also want to invalidate the Router if you imperatively `reset` the router's `CatchBoundary` to trigger loaders again. ### `.clearCache` method -Remove cached route matches. +Remove cached route matches and matching active preloads. - Type: `(opts?: {filter?: (d: MakeRouteMatchUnion) => boolean}) => void` -- if `filter` is not supplied, all cached matches will be removed -- if `filter` is supplied, only matches for which `filter` returns `true` will be removed. +- If `filter` is not supplied, all cached matches and active preload lanes are removed. +- If `filter` is supplied, matching cached matches are removed. An active preload lane is canceled when any match in that lane passes the filter. +- Current committed and presented matches are not removed. ### `.load` method @@ -170,16 +164,24 @@ Loads all of the currently matched route matches and resolves when they are all Preloads all of the matches that match the provided `NavigateOptions`. -> ⚠️⚠️⚠️ **Preloaded route matches are not stored long-term in the router state. They are only stored until the next attempted navigation action.** +An active preload is speculative and is not published as the current match +presentation. Successful loader data can enter the normal in-memory route cache +and remain reusable according to `preloadStaleTime` and `preloadGcTime`. + +Every preload and navigation runs its own `beforeLoad` chain. Preloads can +donate cached or in-flight loader work, but never `beforeLoad` context or +control flow. -- Type: `(opts?: NavigateOptions) => Promise` +- Type: `(opts: NavigateOptions) => Promise` - Properties - `opts` - Type: `NavigateOptions` - - Optional, defaults to the current location. + - Required. - The options that will be used to determine which route matches to preload. - Returns - - A promise that resolves with an array of all of the route matches that were preloaded. + - A promise that resolves with the speculative route-match lane. An ordinary error or not-found is represented by a terminal match array rather than a rejected promise. + - It resolves with `undefined` when cancellation or control flow does not produce a reusable lane. + - The method is also available on server router instances. It remains speculative and does not change the request's current location or presented matches. ### `.loadRouteChunk` method diff --git a/docs/router/api/router/useChildMatchesHook.md b/docs/router/api/router/useChildMatchesHook.md index d6de892192..13212e61e0 100644 --- a/docs/router/api/router/useChildMatchesHook.md +++ b/docs/router/api/router/useChildMatchesHook.md @@ -5,8 +5,9 @@ title: useChildMatches hook The `useChildMatches` hook returns all of the child [`RouteMatch`](./RouteMatchType.md) objects from the closest match down to the leaf-most match. **It does not include the current match, which can be obtained using the `useMatch` hook.** -> [!IMPORTANT] -> If the router has pending matches and they are showing their pending component fallbacks, pending matches are used instead of active matches. +The result is derived from the router's current match presentation. It can +include still-loading descendants and descendants below the current render +boundary. ## useChildMatches options diff --git a/docs/router/api/router/useMatchesHook.md b/docs/router/api/router/useMatchesHook.md index d7a1be8dc3..e57c6c2c7e 100644 --- a/docs/router/api/router/useMatchesHook.md +++ b/docs/router/api/router/useMatchesHook.md @@ -3,7 +3,7 @@ id: useMatchesHook title: useMatches hook --- -The `useMatches` hook returns all of the [`RouteMatch`](./RouteMatchType.md) objects from the router **regardless of its callers position in the React component tree**. +The `useMatches` hook returns the router's complete presented array of [`RouteMatch`](./RouteMatchType.md) objects **regardless of its caller's position in the component tree**. The array can include still-loading descendants or matches below a pending/error/not-found render boundary. > [!TIP] > If you only want the parent or child matches, then you can use the [`useParentMatches`](./useParentMatchesHook.md) or the [`useChildMatches`](./useChildMatchesHook.md) based on the selection you need. diff --git a/docs/router/api/router/useParentMatchesHook.md b/docs/router/api/router/useParentMatchesHook.md index 7afc5585a6..a0ce985411 100644 --- a/docs/router/api/router/useParentMatchesHook.md +++ b/docs/router/api/router/useParentMatchesHook.md @@ -5,8 +5,9 @@ title: useParentMatches hook The `useParentMatches` hook returns all of the parent [`RouteMatch`](./RouteMatchType.md) objects from the root down to the immediate parent of the current match in context. **It does not include the current match, which can be obtained using the `useMatch` hook.** -> [!IMPORTANT] -> If the router has pending matches and they are showing their pending component fallbacks, pending matches are used instead of active matches. +The result is derived from the router's current match presentation. During a +navigation that may still be the previous presentation; after pending UI is +published it is the destination's complete structurally matched lane. ## useParentMatches options diff --git a/docs/router/guide/data-loading.md b/docs/router/guide/data-loading.md index 2ecdf443ad..dad97b1e39 100644 --- a/docs/router/guide/data-loading.md +++ b/docs/router/guide/data-loading.md @@ -150,7 +150,7 @@ Using these dependencies as keys, TanStack Router will cache the data returned f To control router dependencies and "freshness", TanStack Router provides a plethora of options to control the keying and caching behavior of your route loaders. Let's take a look at them in the order that you are most likely to use them: - `routeOptions.loaderDeps` - - A function that supplies you the search params for a router and returns an object of dependencies for use in your `loader` function. When these deps changed from navigation to navigation, it will cause the route to reload regardless of `staleTime`s. The deps are compared using a deep equality check. + - A deterministic, side-effect-free function that supplies you the validated search params for a router and returns a serializable object of dependencies for use in your `loader` function. When these deps change from navigation to navigation, it will cause the route to reload regardless of `staleTime`s. The deps are compared using a deep equality check. - `routeOptions.staleTime` - `routerOptions.defaultStaleTime` - The number of milliseconds that a route's data should be considered fresh when attempting to load. @@ -170,7 +170,7 @@ To control router dependencies and "freshness", TanStack Router provides a pleth - By default, the `staleTime` is set to `0`, meaning that the route's data is immediately considered stale. Stale matches are reloaded in the background when the route is entered again, when its loader key changes (path params used by the route or `loaderDeps`), or when `router.load()` is called explicitly. - By default, a previously preloaded route is considered fresh for **30 seconds**. This means if a route is preloaded, then preloaded again within 30 seconds, the second preload will be ignored. This prevents unnecessary preloads from happening too frequently. **When a route is loaded normally, the standard `staleTime` is used.** -- By default, the `gcTime` is set to **30 minutes**, meaning that any route data that has not been accessed in 30 minutes will be garbage collected and removed from the cache. +- By default, `gcTime` and `preloadGcTime` are **5 minutes**, meaning unused loader data is removed from the in-memory cache after 5 minutes. They can be configured independently. - By default, `staleReloadMode` is `'background'`, so stale successful matches keep rendering with their existing `loaderData` while the loader revalidates in the background. - `router.invalidate()` will force all active routes to reload their loaders immediately and mark every cached route's data as stale. @@ -180,6 +180,11 @@ Imagine a `/posts` route supports some pagination via search params `offset` and Once we have these deps in place, the route will always reload when the deps change. +`loaderDeps` defines a cache key during route planning. For the same validated +search input it must return the same value without navigating or mutating state. +The returned value and custom serialization methods such as `toJSON` must also +be side-effect-free. + ```tsx // /routes/posts.tsx export const Route = createFileRoute('/posts')({ @@ -302,7 +307,11 @@ export const Route = createFileRoute('/posts')({ ### Opting out of caching while still preloading -Even though you may opt-out of short-term caching for your route data, you can still get the benefits of preloading! With the above configuration, preloading will still "just work" with the default `preloadGcTime`. This means that if a route is preloaded, then navigated to, the route's data will be considered fresh and will not be reloaded. +Even though you may opt out of retaining ordinary route data, you can still get +the benefits of preloading. Preloaded results use `preloadGcTime` for retention +and `preloadStaleTime` for freshness, so the default settings keep a recent +preload in memory and let the first navigation reuse it without another loader +call. To opt out of preloading, don't turn it on via the `routerOptions.defaultPreload` or `routeOptions.preload` options. diff --git a/docs/router/guide/path-params.md b/docs/router/guide/path-params.md index b42aefad7f..7ebaaad19e 100644 --- a/docs/router/guide/path-params.md +++ b/docs/router/guide/path-params.md @@ -137,6 +137,9 @@ When multiple dynamic, optional, or wildcard routes can match the same URL, rout Higher `params.priority` values are tried first. The default priority is `0`, and if a higher-priority route's `params.parse` returns `false`, matching continues to the next candidate route. +`params.parse` runs during route planning and may be evaluated more than once. +It must be deterministic and side-effect-free for the same raw params. + ```tsx title="src/routes/posts.$postId.tsx" export const Route = createFileRoute('/posts/$postId')({ params: { diff --git a/docs/router/guide/preloading.md b/docs/router/guide/preloading.md index 453294ab6e..a2f57e78e8 100644 --- a/docs/router/guide/preloading.md +++ b/docs/router/guide/preloading.md @@ -18,10 +18,16 @@ Preloading in TanStack Router is a way to load a route before the user actually ## How long does preloaded data stay in memory? -Preloaded route matches are temporarily cached in memory with a few important caveats: +Successful preloaded loader results can enter the router's in-memory cache with +two independent lifetimes: -- **Unused preloaded data is removed after 30 seconds by default.** This can be configured by setting the `defaultPreloadMaxAge` option on your router. -- **Obviously, when a route is loaded, its preloaded version is promoted to the router's normal pending matches state.** +- **Freshness defaults to 30 seconds.** Configure it with + `defaultPreloadStaleTime` or a route's `preloadStaleTime`. +- **Unused retention defaults to 5 minutes.** Configure it with + `defaultPreloadGcTime` or a route's `preloadGcTime`. +- **The speculative lane is never promoted into router state.** Navigation + creates its own presentation and runs its own `beforeLoad` chain. It can reuse + cached loader data or join a loader that is still in flight. If you need more control over preloading, caching and/or garbage collection of preloaded data, you should use an external caching library like [TanStack Query](https://tanstack.com/query). @@ -87,9 +93,15 @@ const router = createRouter({ You can also set the `preloadDelay` prop on individual `` components to override the default behavior on a per-link basis. -## Built-in Preloading & `preloadStaleTime` +## Built-in Preloading, Freshness, and Retention -If you're using the built-in loaders, you can control how long preloaded data is considered fresh until another preload is triggered by setting either `routerOptions.defaultPreloadStaleTime` or `routeOptions.preloadStaleTime` to a number of milliseconds. **By default, preloaded data is considered fresh for 30 seconds.**. +If you're using the built-in loaders, you can control how long preloaded data is considered fresh by setting either `routerOptions.defaultPreloadStaleTime` or `routeOptions.preloadStaleTime` to a number of milliseconds. **By default, preloaded data is considered fresh for 30 seconds.** + +Freshness and retention are separate. `preloadStaleTime` controls whether the +retained loader result can be reused without another loader call. +`preloadGcTime` (or `defaultPreloadGcTime`) controls how long an unused preload +result can remain in the in-memory cache. Both preload GC options default to 5 +minutes. To change this, you can set the `defaultPreloadStaleTime` option on your router: @@ -125,11 +137,22 @@ Or, you can use the `routeOptions.preloadStaleTime` option on individual routes: // src/routes/posts.$postId.tsx export const Route = createFileRoute('/posts/$postId')({ loader: async ({ params }) => fetchPost(params.postId), - // Preload the route again if the preload cache is older than 10 seconds + // Reload preloaded data when it is more than 10 seconds old preloadStaleTime: 10_000, }) ``` +Client-side preloading runs each route's `beforeLoad` with `preload: true`. +Every later preload or navigation runs its own `beforeLoad` chain, so a +navigation observes `preload: false` even when an identical preload is still +active. Preloads can donate cached or in-flight loader work, but not +`beforeLoad` context, redirects, errors, or not-found results. The +`shouldReload` option remains loader-only. + +If a route has `preload: false`, its speculative lane still runs `beforeLoad`, +but skips that route's loader. Navigation runs `beforeLoad` again and performs +the skipped loader work. + ## Preloading with External Libraries When integrating external caching libraries like React Query, which have their own mechanisms for determining stale data, you may want to override the default preloading and stale-while-revalidate logic of TanStack Router. These libraries often use options like staleTime to control the freshness of data. @@ -168,7 +191,11 @@ This would then allow you, for instance, to use an option like React Query's `st ## Preloading Manually -If you need to manually preload a route, you can use the router's `preloadRoute` method. It accepts a standard TanStack `NavigateOptions` object and returns a promise that resolves when the route is preloaded. +If you need to manually preload a route, use the router's `preloadRoute` method. +It accepts a standard TanStack `NavigateOptions` object and returns the +speculative match lane. An ordinary error or not-found is represented in that +returned lane; cancellation or control flow that produces no reusable lane can +return `undefined`. diff --git a/docs/router/how-to/debug-router-issues.md b/docs/router/how-to/debug-router-issues.md index b4d20ec72f..27dadd361f 100644 --- a/docs/router/how-to/debug-router-issues.md +++ b/docs/router/how-to/debug-router-issues.md @@ -322,11 +322,16 @@ const route = createRoute({ ```tsx function DataLoadingDebug() { - const location = useLocation() + const state = useRouterState() console.log('Route status:', { - isLoading: location.isLoading, - isTransitioning: location.isTransitioning, + status: state.status, + isLoading: state.isLoading, + matches: state.matches.map((match) => ({ + routeId: match.routeId, + status: match.status, + isFetching: match.isFetching, + })), }) return null diff --git a/e2e/react-router/issue-7120/index.html b/e2e/react-router/issue-7120/index.html new file mode 100644 index 0000000000..04a3530c9d --- /dev/null +++ b/e2e/react-router/issue-7120/index.html @@ -0,0 +1,10 @@ + + + + + + Issues 7120 and 7367 + + + + diff --git a/e2e/react-router/issue-7120/package.json b/e2e/react-router/issue-7120/package.json new file mode 100644 index 0000000000..a16155eb61 --- /dev/null +++ b/e2e/react-router/issue-7120/package.json @@ -0,0 +1,27 @@ +{ + "name": "tanstack-router-e2e-react-issue-7120", + "private": true, + "type": "module", + "scripts": { + "dev": "vite --port 3000", + "dev:e2e": "vite", + "build": "vite build && tsc --noEmit", + "preview": "vite preview", + "start": "vite", + "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" + }, + "dependencies": { + "@tanstack/react-router": "workspace:^", + "@tanstack/router-plugin": "workspace:^", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@playwright/test": "^1.61.0", + "@tanstack/router-e2e-utils": "workspace:^", + "@types/react": "^19.0.8", + "@types/react-dom": "^19.0.3", + "@vitejs/plugin-react": "^6.0.1", + "vite": "^8.0.14" + } +} diff --git a/e2e/react-router/issue-7120/playwright.config.ts b/e2e/react-router/issue-7120/playwright.config.ts new file mode 100644 index 0000000000..61a995a815 --- /dev/null +++ b/e2e/react-router/issue-7120/playwright.config.ts @@ -0,0 +1,25 @@ +import { defineConfig, devices } from '@playwright/test' +import { getTestServerPort } from '@tanstack/router-e2e-utils' +import packageJson from './package.json' with { type: 'json' } + +const PORT = await getTestServerPort(packageJson.name) +const baseURL = `http://localhost:${PORT}` + +export default defineConfig({ + testDir: './tests', + workers: 1, + reporter: [['line']], + use: { baseURL }, + webServer: { + command: `VITE_NODE_ENV="test" VITE_SERVER_PORT=${PORT} pnpm dev:e2e --port ${PORT}`, + url: baseURL, + reuseExistingServer: !process.env.CI, + stdout: 'pipe', + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], +}) diff --git a/e2e/react-router/issue-7120/src/main.tsx b/e2e/react-router/issue-7120/src/main.tsx new file mode 100644 index 0000000000..0c983251af --- /dev/null +++ b/e2e/react-router/issue-7120/src/main.tsx @@ -0,0 +1,21 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import { RouterProvider, createRouter } from '@tanstack/react-router' +import { routeTree } from './routeTree.gen' + +const router = createRouter({ + routeTree, + defaultViewTransition: true, +}) + +declare module '@tanstack/react-router' { + interface Register { + router: typeof router + } +} + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/e2e/react-router/issue-7120/src/redirectGate.ts b/e2e/react-router/issue-7120/src/redirectGate.ts new file mode 100644 index 0000000000..399d303aac --- /dev/null +++ b/e2e/react-router/issue-7120/src/redirectGate.ts @@ -0,0 +1,35 @@ +type RedirectGate = { + waiting: boolean + released: boolean + release: () => void +} + +declare global { + interface Window { + redirectGate: RedirectGate + } +} + +let releasePromise!: () => void +const promise = new Promise((resolve) => { + releasePromise = resolve +}) + +const redirectGate: RedirectGate = { + waiting: false, + released: false, + release: () => { + if (!redirectGate.released) { + redirectGate.released = true + releasePromise() + } + }, +} + +window.redirectGate = redirectGate + +export async function waitForRedirectGate() { + redirectGate.waiting = true + await promise + redirectGate.waiting = false +} diff --git a/e2e/react-router/issue-7120/src/routeTree.gen.ts b/e2e/react-router/issue-7120/src/routeTree.gen.ts new file mode 100644 index 0000000000..fc671589c7 --- /dev/null +++ b/e2e/react-router/issue-7120/src/routeTree.gen.ts @@ -0,0 +1,77 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// 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 PostsRouteImport } from './routes/posts' +import { Route as IndexRouteImport } from './routes/index' + +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', + getParentRoute: () => rootRouteImport, +} as any).lazy(() => import('./routes/posts.lazy').then((d) => d.Route)) +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/posts': typeof PostsRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/posts': typeof PostsRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute + '/posts': typeof PostsRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' | '/posts' + fileRoutesByTo: FileRoutesByTo + to: '/' | '/posts' + id: '__root__' | '/' | '/posts' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + PostsRoute: typeof PostsRoute +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport + parentRoute: typeof rootRouteImport + } + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + } +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + PostsRoute: PostsRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() diff --git a/e2e/react-router/issue-7120/src/routes/__root.tsx b/e2e/react-router/issue-7120/src/routes/__root.tsx new file mode 100644 index 0000000000..2f01516a55 --- /dev/null +++ b/e2e/react-router/issue-7120/src/routes/__root.tsx @@ -0,0 +1,33 @@ +import { Outlet, createRootRoute, redirect } from '@tanstack/react-router' +import { waitForRedirectGate } from '../redirectGate' + +let shouldRedirect = true + +export const Route = createRootRoute({ + pendingMs: 0, + pendingMinMs: 500, + pendingComponent: RootPendingComponent, + beforeLoad: async ({ location }) => { + if (location.pathname !== '/' || !shouldRedirect) { + return + } + + await waitForRedirectGate() + + if (!shouldRedirect) { + return + } + + shouldRedirect = false + throw redirect({ to: '/posts', replace: true }) + }, + component: () => , +}) + +function RootPendingComponent() { + return ( +
    + Loading route +
    + ) +} diff --git a/e2e/react-router/issue-7120/src/routes/index.tsx b/e2e/react-router/issue-7120/src/routes/index.tsx new file mode 100644 index 0000000000..1626ce706b --- /dev/null +++ b/e2e/react-router/issue-7120/src/routes/index.tsx @@ -0,0 +1,5 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/')({ + component: () =>
    Home
    , +}) diff --git a/e2e/react-router/issue-7120/src/routes/posts.lazy.tsx b/e2e/react-router/issue-7120/src/routes/posts.lazy.tsx new file mode 100644 index 0000000000..dc0b0cfd7f --- /dev/null +++ b/e2e/react-router/issue-7120/src/routes/posts.lazy.tsx @@ -0,0 +1,14 @@ +import { createLazyFileRoute } from '@tanstack/react-router' + +export const Route = createLazyFileRoute('/posts')({ + component: PostsComponent, +}) + +function PostsComponent() { + return ( +
    +

    Posts loaded

    +

    The redirected lazy route rendered.

    +
    + ) +} diff --git a/e2e/react-router/issue-7120/src/routes/posts.tsx b/e2e/react-router/issue-7120/src/routes/posts.tsx new file mode 100644 index 0000000000..cc0519b74e --- /dev/null +++ b/e2e/react-router/issue-7120/src/routes/posts.tsx @@ -0,0 +1,3 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/posts')({}) diff --git a/e2e/react-router/issue-7120/src/vite-env.d.ts b/e2e/react-router/issue-7120/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/e2e/react-router/issue-7120/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/e2e/react-router/issue-7120/tests/issue-7120.repro.spec.ts b/e2e/react-router/issue-7120/tests/issue-7120.repro.spec.ts new file mode 100644 index 0000000000..081ae73691 --- /dev/null +++ b/e2e/react-router/issue-7120/tests/issue-7120.repro.spec.ts @@ -0,0 +1,33 @@ +import { expect, test } from '@playwright/test' + +test('#7120 / #7367: a root redirect cannot commit a stale match during a view transition', async ({ + page, +}) => { + const pageErrors: Array = [] + const consoleErrors: Array = [] + + page.on('pageerror', (error) => { + pageErrors.push(error.message || String(error)) + }) + page.on('console', (message) => { + if (message.type() === 'error') { + consoleErrors.push(message.text()) + } + }) + + await page.goto('/') + + const pending = page.getByTestId('root-pending') + await expect(pending).toBeVisible() + expect(await page.evaluate(() => window.redirectGate.waiting)).toBe(true) + + await page.evaluate(() => window.redirectGate.release()) + + await expect(page).toHaveURL(/\/posts$/) + await expect( + page.getByRole('heading', { name: 'Posts loaded' }), + ).toBeVisible() + await expect(pending).not.toBeVisible() + expect(pageErrors).toEqual([]) + expect(consoleErrors).toEqual([]) +}) diff --git a/e2e/react-router/issue-7120/tsconfig.json b/e2e/react-router/issue-7120/tsconfig.json new file mode 100644 index 0000000000..4f6089bc08 --- /dev/null +++ b/e2e/react-router/issue-7120/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "strict": true, + "esModuleInterop": true, + "jsx": "react-jsx", + "target": "ESNext", + "moduleResolution": "Bundler", + "module": "ESNext", + "resolveJsonModule": true, + "allowJs": true, + "skipLibCheck": true, + "types": ["vite/client"] + }, + "exclude": ["node_modules", "dist"] +} diff --git a/e2e/react-router/issue-7120/vite.config.ts b/e2e/react-router/issue-7120/vite.config.ts new file mode 100644 index 0000000000..5b1c613906 --- /dev/null +++ b/e2e/react-router/issue-7120/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import { tanstackRouter } from '@tanstack/router-plugin/vite' + +export default defineConfig({ + plugins: [tanstackRouter({ target: 'react' }), react()], +}) diff --git a/e2e/react-router/issue-7457/index.html b/e2e/react-router/issue-7457/index.html new file mode 100644 index 0000000000..76369b5957 --- /dev/null +++ b/e2e/react-router/issue-7457/index.html @@ -0,0 +1,10 @@ + + + + + + Issue 7457 + + + + diff --git a/e2e/react-router/issue-7457/package.json b/e2e/react-router/issue-7457/package.json new file mode 100644 index 0000000000..3220a4dbb4 --- /dev/null +++ b/e2e/react-router/issue-7457/package.json @@ -0,0 +1,28 @@ +{ + "name": "tanstack-router-e2e-react-issue-7457", + "private": true, + "type": "module", + "scripts": { + "dev": "vite --port 3000", + "dev:e2e": "vite", + "build": "vite build && tsc --noEmit", + "preview": "vite preview", + "start": "vite", + "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" + }, + "dependencies": { + "@tanstack/react-query": "^5.99.0", + "@tanstack/react-router": "workspace:^", + "@tanstack/router-plugin": "workspace:^", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@playwright/test": "^1.61.0", + "@tanstack/router-e2e-utils": "workspace:^", + "@types/react": "^19.0.8", + "@types/react-dom": "^19.0.3", + "@vitejs/plugin-react": "^6.0.1", + "vite": "^8.0.14" + } +} diff --git a/e2e/react-router/issue-7457/playwright.config.ts b/e2e/react-router/issue-7457/playwright.config.ts new file mode 100644 index 0000000000..61a995a815 --- /dev/null +++ b/e2e/react-router/issue-7457/playwright.config.ts @@ -0,0 +1,25 @@ +import { defineConfig, devices } from '@playwright/test' +import { getTestServerPort } from '@tanstack/router-e2e-utils' +import packageJson from './package.json' with { type: 'json' } + +const PORT = await getTestServerPort(packageJson.name) +const baseURL = `http://localhost:${PORT}` + +export default defineConfig({ + testDir: './tests', + workers: 1, + reporter: [['line']], + use: { baseURL }, + webServer: { + command: `VITE_NODE_ENV="test" VITE_SERVER_PORT=${PORT} pnpm dev:e2e --port ${PORT}`, + url: baseURL, + reuseExistingServer: !process.env.CI, + stdout: 'pipe', + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], +}) diff --git a/e2e/react-router/issue-7457/src/main.tsx b/e2e/react-router/issue-7457/src/main.tsx new file mode 100644 index 0000000000..1b6805edc0 --- /dev/null +++ b/e2e/react-router/issue-7457/src/main.tsx @@ -0,0 +1,32 @@ +import { StrictMode, useEffect } from 'react' +import { createRoot } from 'react-dom/client' +import { QueryClient } from '@tanstack/react-query' +import { RouterProvider, createRouter } from '@tanstack/react-router' +import { routeTree } from './routeTree.gen' + +const queryClient = new QueryClient() +const router = createRouter({ + routeTree, + context: { queryClient }, + defaultPendingComponent: DefaultPendingComponent, + defaultPreloadStaleTime: 0, +}) + +declare module '@tanstack/react-router' { + interface Register { + router: typeof router + } +} + +function DefaultPendingComponent() { + useEffect(() => { + ;(globalThis as any).__pendingSeen = true + }, []) + return
    loading
    +} + +createRoot(document.body).render( + + + , +) diff --git a/e2e/react-router/issue-7457/src/routeTree.gen.ts b/e2e/react-router/issue-7457/src/routeTree.gen.ts new file mode 100644 index 0000000000..72a8f23384 --- /dev/null +++ b/e2e/react-router/issue-7457/src/routeTree.gen.ts @@ -0,0 +1,77 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// 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 AnotherRouteImport } from './routes/another' +import { Route as IndexRouteImport } from './routes/index' + +const AnotherRoute = AnotherRouteImport.update({ + id: '/another', + path: '/another', + getParentRoute: () => rootRouteImport, +} as any) +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/another': typeof AnotherRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/another': typeof AnotherRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute + '/another': typeof AnotherRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' | '/another' + fileRoutesByTo: FileRoutesByTo + to: '/' | '/another' + id: '__root__' | '/' | '/another' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + AnotherRoute: typeof AnotherRoute +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/another': { + id: '/another' + path: '/another' + fullPath: '/another' + preLoaderRoute: typeof AnotherRouteImport + parentRoute: typeof rootRouteImport + } + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + } +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + AnotherRoute: AnotherRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() diff --git a/e2e/react-router/issue-7457/src/routes/__root.tsx b/e2e/react-router/issue-7457/src/routes/__root.tsx new file mode 100644 index 0000000000..988ba76263 --- /dev/null +++ b/e2e/react-router/issue-7457/src/routes/__root.tsx @@ -0,0 +1,25 @@ +import { Outlet, createRootRouteWithContext } from '@tanstack/react-router' +import type { QueryClient } from '@tanstack/react-query' + +export const Route = createRootRouteWithContext<{ + queryClient: QueryClient +}>()({ + beforeLoad: async ({ context }) => { + await context.queryClient.ensureQueryData({ + queryKey: ['issue-7457-root'], + queryFn: async () => { + await new Promise((resolve) => setTimeout(resolve, 1_500)) + return true + }, + }) + }, + component: RootComponent, +}) + +function RootComponent() { + return ( +
    + +
    + ) +} diff --git a/e2e/react-router/issue-7457/src/routes/another.tsx b/e2e/react-router/issue-7457/src/routes/another.tsx new file mode 100644 index 0000000000..6b6caa55b3 --- /dev/null +++ b/e2e/react-router/issue-7457/src/routes/another.tsx @@ -0,0 +1,5 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/another')({ + component: () =>
    Hello "/another"!
    , +}) diff --git a/e2e/react-router/issue-7457/src/routes/index.tsx b/e2e/react-router/issue-7457/src/routes/index.tsx new file mode 100644 index 0000000000..daaae2aeed --- /dev/null +++ b/e2e/react-router/issue-7457/src/routes/index.tsx @@ -0,0 +1,8 @@ +import { createFileRoute, redirect } from '@tanstack/react-router' + +export const Route = createFileRoute('/')({ + beforeLoad: () => { + throw redirect({ to: '/another', replace: true }) + }, + component: () =>
    You should never see this!
    , +}) diff --git a/e2e/react-router/issue-7457/src/vite-env.d.ts b/e2e/react-router/issue-7457/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/e2e/react-router/issue-7457/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/e2e/react-router/issue-7457/tests/issue-7457.repro.spec.ts b/e2e/react-router/issue-7457/tests/issue-7457.repro.spec.ts new file mode 100644 index 0000000000..8b20fac1eb --- /dev/null +++ b/e2e/react-router/issue-7457/tests/issue-7457.repro.spec.ts @@ -0,0 +1,35 @@ +import { expect, test } from '@playwright/test' + +test('#7457: initial child redirect after async root beforeLoad does not blank', async ({ + page, +}) => { + const pageErrors: Array = [] + const consoleErrors: Array = [] + page.on('pageerror', (error) => + pageErrors.push(error.message || String(error)), + ) + page.on('console', (message) => { + if (message.type() === 'error') { + consoleErrors.push(message.text()) + } + }) + + await page.goto('/') + + await expect(page).toHaveURL(/\/another$/) + const target = page.getByText('Hello "/another"!') + await expect + .poll(async () => ({ + targetCount: await target.count(), + pageErrors: [...pageErrors], + consoleErrors: [...consoleErrors], + })) + .toEqual({ targetCount: 1, pageErrors: [], consoleErrors: [] }) + await expect(target).toBeVisible() + await expect + .poll(() => page.evaluate(() => (globalThis as any).__pendingSeen)) + .toBe(true) + await expect(page.getByTestId('app-pending')).not.toBeVisible() + expect(pageErrors).toEqual([]) + expect(consoleErrors).toEqual([]) +}) diff --git a/e2e/react-router/issue-7457/tsconfig.json b/e2e/react-router/issue-7457/tsconfig.json new file mode 100644 index 0000000000..4f6089bc08 --- /dev/null +++ b/e2e/react-router/issue-7457/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "strict": true, + "esModuleInterop": true, + "jsx": "react-jsx", + "target": "ESNext", + "moduleResolution": "Bundler", + "module": "ESNext", + "resolveJsonModule": true, + "allowJs": true, + "skipLibCheck": true, + "types": ["vite/client"] + }, + "exclude": ["node_modules", "dist"] +} diff --git a/e2e/react-router/issue-7457/vite.config.js b/e2e/react-router/issue-7457/vite.config.js new file mode 100644 index 0000000000..9e39ea83d9 --- /dev/null +++ b/e2e/react-router/issue-7457/vite.config.js @@ -0,0 +1,10 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import { tanstackRouter } from '@tanstack/router-plugin/vite' + +export default defineConfig({ + plugins: [ + tanstackRouter({ target: 'react', autoCodeSplitting: true }), + react(), + ], +}) diff --git a/e2e/react-router/scroll-restoration-sandbox-vite/tests/app.spec.ts b/e2e/react-router/scroll-restoration-sandbox-vite/tests/app.spec.ts index f72d2d06ab..9c2d944dd5 100644 --- a/e2e/react-router/scroll-restoration-sandbox-vite/tests/app.spec.ts +++ b/e2e/react-router/scroll-restoration-sandbox-vite/tests/app.spec.ts @@ -2,6 +2,21 @@ import { expect, test } from '@playwright/test' import { linkOptions } from '@tanstack/react-router' import { toRuntimePath } from '@tanstack/router-e2e-utils' +type ScrollPaintTestState = { + sourceFrameScrollYs: Array + sourceWasReset: boolean + destinationMounted: boolean + destinationCommitScrollY: number | null + destinationFrameScrollYs: Array + cleanup: () => void +} + +declare global { + interface Window { + __scrollPaintTestState?: ScrollPaintTestState + } +} + test('Smoke - Renders home', async ({ page }) => { await page.goto(toRuntimePath('/')) await expect( @@ -9,6 +24,175 @@ test('Smoke - Renders home', async ({ page }) => { ).toBeVisible() }) +test('PUSH resets scroll before the destination can render a frame (#7815)', async ({ + page, +}) => { + await page.goto(toRuntimePath('/issue-7040-source')) + await expect(page.getByTestId('issue-7040-source-top')).toBeVisible() + + const sourceScrollY = await page.evaluate(async () => { + window.scrollTo(0, 500) + await new Promise((resolve) => requestAnimationFrame(() => resolve())) + return window.scrollY + }) + + expect(sourceScrollY).toBeGreaterThan(0) + + await page.evaluate((expectedSourceScrollY) => { + const root = document.querySelector('#app') + if (!root) { + throw new Error('App root not found') + } + + const geometry = document.createElement('div') + geometry.setAttribute('aria-hidden', 'true') + geometry.style.height = `${window.innerHeight + expectedSourceScrollY + 100}px` + geometry.style.pointerEvents = 'none' + geometry.style.width = '1px' + document.body.append(geometry) + + const state: ScrollPaintTestState = { + sourceFrameScrollYs: [], + sourceWasReset: false, + destinationMounted: false, + destinationCommitScrollY: null, + destinationFrameScrollYs: [], + cleanup: () => {}, + } + + const getDestinationHeading = () => + Array.from(document.querySelectorAll('h3')).find( + (heading) => heading.textContent === 'normal-page', + ) + + const commitObserver = new MutationObserver(() => { + const destinationHeading = getDestinationHeading() + + if (!destinationHeading) { + return + } + + commitObserver.disconnect() + state.destinationMounted = true + state.destinationCommitScrollY = window.scrollY + }) + + commitObserver.observe(root, { childList: true, subtree: true }) + + const handleScroll = () => { + if ( + document.querySelector('[data-testid="issue-7040-source-top"]') && + Math.abs(window.scrollY - expectedSourceScrollY) > 1 + ) { + state.sourceWasReset = true + } + } + window.addEventListener('scroll', handleScroll, { passive: true }) + + const originalScrollTo = window.scrollTo + window.scrollTo = ((...args: Array) => { + const requestedTop = + typeof args[0] === 'number' + ? args[1] + : typeof args[0] === 'object' && args[0] !== null + ? (args[0] as ScrollToOptions).top + : undefined + if ( + typeof requestedTop === 'number' && + Math.abs(requestedTop - expectedSourceScrollY) > 1 && + document.querySelector('[data-testid="issue-7040-source-top"]') + ) { + state.sourceWasReset = true + } + Reflect.apply(originalScrollTo, window, args) + }) as typeof window.scrollTo + + let animationFrameId = 0 + const recordFrame = () => { + if (getDestinationHeading()) { + state.destinationFrameScrollYs.push(window.scrollY) + } else if ( + document.querySelector('[data-testid="issue-7040-source-top"]') + ) { + state.sourceFrameScrollYs.push(window.scrollY) + } + animationFrameId = requestAnimationFrame(recordFrame) + } + animationFrameId = requestAnimationFrame(recordFrame) + + state.cleanup = () => { + commitObserver.disconnect() + cancelAnimationFrame(animationFrameId) + window.removeEventListener('scroll', handleScroll) + window.scrollTo = originalScrollTo + geometry.remove() + delete window.__scrollPaintTestState + } + window.__scrollPaintTestState = state + }, sourceScrollY) + + await page.evaluate( + () => + new Promise((resolve) => { + requestAnimationFrame(() => resolve()) + }), + ) + expect(await page.evaluate(() => window.scrollY)).toBe(sourceScrollY) + + await page + .getByRole('link', { name: 'Head-/normal-page' }) + .dispatchEvent('click') + + await page.waitForFunction(() => { + const state = window.__scrollPaintTestState + if (!state?.destinationMounted) { + return false + } + + const lastFrames = state.destinationFrameScrollYs.slice(-2) + return ( + lastFrames.length === 2 && + lastFrames.every((scrollY) => Math.abs(scrollY) <= 1) + ) + }) + + const observation = await page.evaluate(() => { + const state = window.__scrollPaintTestState + if (!state) { + throw new Error('Scroll paint observer not found') + } + + return { + sourceFrameScrollYs: state.sourceFrameScrollYs, + sourceWasReset: state.sourceWasReset, + destinationCommitScrollY: state.destinationCommitScrollY, + destinationFrameScrollYs: state.destinationFrameScrollYs, + destinationMaxScrollY: + document.documentElement.scrollHeight - window.innerHeight, + finalScrollY: window.scrollY, + } + }) + + expect(observation.sourceFrameScrollYs.length).toBeGreaterThan(0) + expect( + observation.sourceFrameScrollYs.every( + (scrollY) => Math.abs(scrollY - sourceScrollY) <= 1, + ), + ).toBe(true) + expect(observation.sourceWasReset).toBe(false) + expect(observation.destinationMaxScrollY).toBeGreaterThanOrEqual( + sourceScrollY, + ) + expect(observation.finalScrollY).toBe(0) + expect(observation.destinationFrameScrollYs.length).toBeGreaterThanOrEqual(2) + expect( + observation.destinationFrameScrollYs.every( + (scrollY) => Math.abs(scrollY) <= 1, + ), + `Destination paint opportunities: ${observation.destinationFrameScrollYs.join(', ')}, commit: ${observation.destinationCommitScrollY}, final: ${observation.finalScrollY}`, + ).toBe(true) +}) + test('restores the prior scroll position after browser back then forward', async ({ page, }) => { diff --git a/e2e/react-start/basic/src/routeTree.gen.ts b/e2e/react-start/basic/src/routeTree.gen.ts index 10a40bc044..ea09864d8e 100644 --- a/e2e/react-start/basic/src/routeTree.gen.ts +++ b/e2e/react-start/basic/src/routeTree.gen.ts @@ -29,6 +29,7 @@ import { Route as TypeOnlyReexportRouteImport } from './routes/type-only-reexpor import { Route as UsersRouteImport } from './routes/users' import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' import { Route as ApiUsersRouteImport } from './routes/api.users' +import { Route as Issue6221DashboardRouteImport } from './routes/issue-6221.dashboard' import { Route as MultiCookieRedirectIndexRouteImport } from './routes/multi-cookie-redirect/index' import { Route as MultiCookieRedirectTargetRouteImport } from './routes/multi-cookie-redirect/target' import { Route as NotFoundIndexRouteImport } from './routes/not-found/index' @@ -61,6 +62,7 @@ import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' import { Route as ApiUsersIdRouteImport } from './routes/api/users.$id' +import { Route as Issue6221ArticleIdRouteImport } from './routes/issue-6221.article.$id' import { Route as NotFoundDeepIndexRouteImport } from './routes/not-found/deep/index' import { Route as NotFoundDeepBRouteRouteImport } from './routes/not-found/deep/b/route' import { Route as NotFoundParentBoundaryIndexRouteImport } from './routes/not-found/parent-boundary/index' @@ -179,6 +181,11 @@ const ApiUsersRoute = ApiUsersRouteImport.update({ path: '/api/users', getParentRoute: () => rootRouteImport, } as any) +const Issue6221DashboardRoute = Issue6221DashboardRouteImport.update({ + id: '/issue-6221/dashboard', + path: '/issue-6221/dashboard', + getParentRoute: () => rootRouteImport, +} as any) const MultiCookieRedirectIndexRoute = MultiCookieRedirectIndexRouteImport.update({ id: '/multi-cookie-redirect/', @@ -346,6 +353,11 @@ const ApiUsersIdRoute = ApiUsersIdRouteImport.update({ path: '/$id', getParentRoute: () => ApiUsersRoute, } as any) +const Issue6221ArticleIdRoute = Issue6221ArticleIdRouteImport.update({ + id: '/issue-6221/article/$id', + path: '/issue-6221/article/$id', + getParentRoute: () => rootRouteImport, +} as any) const NotFoundDeepIndexRoute = NotFoundDeepIndexRouteImport.update({ id: '/', path: '/', @@ -468,6 +480,7 @@ export interface FileRoutesByFullPath { '/not-found/parent-boundary': typeof NotFoundParentBoundaryRouteRouteWithChildren '/specialChars/malformed': typeof SpecialCharsMalformedRouteRouteWithChildren '/api/users': typeof ApiUsersRouteWithChildren + '/issue-6221/dashboard': typeof Issue6221DashboardRoute '/multi-cookie-redirect/target': typeof MultiCookieRedirectTargetRoute '/not-found/via-beforeLoad': typeof NotFoundViaBeforeLoadRoute '/not-found/via-beforeLoad-target-root': typeof NotFoundViaBeforeLoadTargetRootRoute @@ -498,6 +511,7 @@ export interface FileRoutesByFullPath { '/layout-a': typeof LayoutLayout2LayoutARoute '/layout-b': typeof LayoutLayout2LayoutBRoute '/api/users/$id': typeof ApiUsersIdRoute + '/issue-6221/article/$id': typeof Issue6221ArticleIdRoute '/not-found/parent-boundary/via-beforeLoad': typeof NotFoundParentBoundaryViaBeforeLoadRoute '/posts/$postId/deep': typeof PostsPostIdDeepRoute '/redirect/$target/via-beforeLoad': typeof RedirectTargetViaBeforeLoadRoute @@ -531,6 +545,7 @@ export interface FileRoutesByTo { '/type-only-reexport': typeof TypeOnlyReexportRoute '/specialChars/malformed': typeof SpecialCharsMalformedRouteRouteWithChildren '/api/users': typeof ApiUsersRouteWithChildren + '/issue-6221/dashboard': typeof Issue6221DashboardRoute '/multi-cookie-redirect/target': typeof MultiCookieRedirectTargetRoute '/not-found/via-beforeLoad': typeof NotFoundViaBeforeLoadRoute '/not-found/via-beforeLoad-target-root': typeof NotFoundViaBeforeLoadTargetRootRoute @@ -560,6 +575,7 @@ export interface FileRoutesByTo { '/layout-a': typeof LayoutLayout2LayoutARoute '/layout-b': typeof LayoutLayout2LayoutBRoute '/api/users/$id': typeof ApiUsersIdRoute + '/issue-6221/article/$id': typeof Issue6221ArticleIdRoute '/not-found/parent-boundary/via-beforeLoad': typeof NotFoundParentBoundaryViaBeforeLoadRoute '/posts/$postId/deep': typeof PostsPostIdDeepRoute '/redirect/$target/via-beforeLoad': typeof RedirectTargetViaBeforeLoadRoute @@ -602,6 +618,7 @@ export interface FileRoutesById { '/specialChars/malformed': typeof SpecialCharsMalformedRouteRouteWithChildren '/_layout/_layout-2': typeof LayoutLayout2RouteWithChildren '/api/users': typeof ApiUsersRouteWithChildren + '/issue-6221/dashboard': typeof Issue6221DashboardRoute '/multi-cookie-redirect/target': typeof MultiCookieRedirectTargetRoute '/not-found/via-beforeLoad': typeof NotFoundViaBeforeLoadRoute '/not-found/via-beforeLoad-target-root': typeof NotFoundViaBeforeLoadTargetRootRoute @@ -632,6 +649,7 @@ export interface FileRoutesById { '/_layout/_layout-2/layout-a': typeof LayoutLayout2LayoutARoute '/_layout/_layout-2/layout-b': typeof LayoutLayout2LayoutBRoute '/api/users/$id': typeof ApiUsersIdRoute + '/issue-6221/article/$id': typeof Issue6221ArticleIdRoute '/not-found/parent-boundary/via-beforeLoad': typeof NotFoundParentBoundaryViaBeforeLoadRoute '/posts_/$postId/deep': typeof PostsPostIdDeepRoute '/redirect/$target/via-beforeLoad': typeof RedirectTargetViaBeforeLoadRoute @@ -674,6 +692,7 @@ export interface FileRouteTypes { | '/not-found/parent-boundary' | '/specialChars/malformed' | '/api/users' + | '/issue-6221/dashboard' | '/multi-cookie-redirect/target' | '/not-found/via-beforeLoad' | '/not-found/via-beforeLoad-target-root' @@ -704,6 +723,7 @@ export interface FileRouteTypes { | '/layout-a' | '/layout-b' | '/api/users/$id' + | '/issue-6221/article/$id' | '/not-found/parent-boundary/via-beforeLoad' | '/posts/$postId/deep' | '/redirect/$target/via-beforeLoad' @@ -737,6 +757,7 @@ export interface FileRouteTypes { | '/type-only-reexport' | '/specialChars/malformed' | '/api/users' + | '/issue-6221/dashboard' | '/multi-cookie-redirect/target' | '/not-found/via-beforeLoad' | '/not-found/via-beforeLoad-target-root' @@ -766,6 +787,7 @@ export interface FileRouteTypes { | '/layout-a' | '/layout-b' | '/api/users/$id' + | '/issue-6221/article/$id' | '/not-found/parent-boundary/via-beforeLoad' | '/posts/$postId/deep' | '/redirect/$target/via-beforeLoad' @@ -807,6 +829,7 @@ export interface FileRouteTypes { | '/specialChars/malformed' | '/_layout/_layout-2' | '/api/users' + | '/issue-6221/dashboard' | '/multi-cookie-redirect/target' | '/not-found/via-beforeLoad' | '/not-found/via-beforeLoad-target-root' @@ -837,6 +860,7 @@ export interface FileRouteTypes { | '/_layout/_layout-2/layout-a' | '/_layout/_layout-2/layout-b' | '/api/users/$id' + | '/issue-6221/article/$id' | '/not-found/parent-boundary/via-beforeLoad' | '/posts_/$postId/deep' | '/redirect/$target/via-beforeLoad' @@ -876,10 +900,12 @@ export interface RootRouteChildren { TypeOnlyReexportRoute: typeof TypeOnlyReexportRoute UsersRoute: typeof UsersRouteWithChildren ApiUsersRoute: typeof ApiUsersRouteWithChildren + Issue6221DashboardRoute: typeof Issue6221DashboardRoute MultiCookieRedirectTargetRoute: typeof MultiCookieRedirectTargetRoute RedirectTargetRoute: typeof RedirectTargetRouteWithChildren MultiCookieRedirectIndexRoute: typeof MultiCookieRedirectIndexRoute RedirectIndexRoute: typeof RedirectIndexRoute + Issue6221ArticleIdRoute: typeof Issue6221ArticleIdRoute PostsPostIdDeepRoute: typeof PostsPostIdDeepRoute FooBarQuxHereRoute: typeof FooBarQuxHereRouteWithChildren } @@ -1026,6 +1052,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ApiUsersRouteImport parentRoute: typeof rootRouteImport } + '/issue-6221/dashboard': { + id: '/issue-6221/dashboard' + path: '/issue-6221/dashboard' + fullPath: '/issue-6221/dashboard' + preLoaderRoute: typeof Issue6221DashboardRouteImport + parentRoute: typeof rootRouteImport + } '/multi-cookie-redirect/': { id: '/multi-cookie-redirect/' path: '/multi-cookie-redirect' @@ -1250,6 +1283,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ApiUsersIdRouteImport parentRoute: typeof ApiUsersRoute } + '/issue-6221/article/$id': { + id: '/issue-6221/article/$id' + path: '/issue-6221/article/$id' + fullPath: '/issue-6221/article/$id' + preLoaderRoute: typeof Issue6221ArticleIdRouteImport + parentRoute: typeof rootRouteImport + } '/not-found/deep/': { id: '/not-found/deep/' path: '/' @@ -1648,10 +1688,12 @@ const rootRouteChildren: RootRouteChildren = { TypeOnlyReexportRoute: TypeOnlyReexportRoute, UsersRoute: UsersRouteWithChildren, ApiUsersRoute: ApiUsersRouteWithChildren, + Issue6221DashboardRoute: Issue6221DashboardRoute, MultiCookieRedirectTargetRoute: MultiCookieRedirectTargetRoute, RedirectTargetRoute: RedirectTargetRouteWithChildren, MultiCookieRedirectIndexRoute: MultiCookieRedirectIndexRoute, RedirectIndexRoute: RedirectIndexRoute, + Issue6221ArticleIdRoute: Issue6221ArticleIdRoute, PostsPostIdDeepRoute: PostsPostIdDeepRoute, FooBarQuxHereRoute: FooBarQuxHereRouteWithChildren, } diff --git a/e2e/react-start/basic/src/routes/issue-6221.article.$id.tsx b/e2e/react-start/basic/src/routes/issue-6221.article.$id.tsx new file mode 100644 index 0000000000..c923a5f1cb --- /dev/null +++ b/e2e/react-start/basic/src/routes/issue-6221.article.$id.tsx @@ -0,0 +1,42 @@ +import { Link, createFileRoute } from '@tanstack/react-router' +import { createClientOnlyFn } from '@tanstack/react-start' + +const isAuthed = createClientOnlyFn( + () => localStorage.getItem('issue-6221-auth') === 'good', +) + +const fetchArticle = async (id: string) => { + await new Promise((resolve) => setTimeout(resolve, 1000)) + return isAuthed() + ? { + title: `Article Title for ${id}`, + content: `Article content for ${id}`, + } + : null +} + +export const Route = createFileRoute('/issue-6221/article/$id')({ + ssr: false, + loader: ({ params }) => fetchArticle(params.id), + head: ({ loaderData }) => ({ + meta: [{ title: loaderData?.title ?? 'title n/a' }], + }), + component: Article, +}) + +function Article() { + const article = Route.useLoaderData() + + return ( +
    + + Dashboard + + {article ? ( +
    {article.content}
    + ) : ( +
    Article Not Accessible.
    + )} +
    + ) +} diff --git a/e2e/react-start/basic/src/routes/issue-6221.dashboard.tsx b/e2e/react-start/basic/src/routes/issue-6221.dashboard.tsx new file mode 100644 index 0000000000..8c93da669c --- /dev/null +++ b/e2e/react-start/basic/src/routes/issue-6221.dashboard.tsx @@ -0,0 +1,6 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/issue-6221/dashboard')({ + head: () => ({ meta: [{ title: 'Dashboard' }] }), + component: () =>
    Dashboard
    , +}) diff --git a/e2e/react-start/basic/tests/issue-6221-head.spec.ts b/e2e/react-start/basic/tests/issue-6221-head.spec.ts new file mode 100644 index 0000000000..96274ce0f9 --- /dev/null +++ b/e2e/react-start/basic/tests/issue-6221-head.spec.ts @@ -0,0 +1,41 @@ +import { expect } from '@playwright/test' +import { test } from '@tanstack/router-e2e-utils' +import { isPreview } from './utils/isPreview' +import { isSpaMode } from './utils/isSpaMode' + +// Ported to React from the regression app in +// https://github.com/TanStack/router/pull/6222 +test.skip( + isSpaMode || isPreview, + 'head() coverage for an ssr:false route requires the SSR test mode', +) + +test('browser back uses fresh loaderData for both the body and head (#6221)', async ({ + page, +}) => { + await page.goto('/') + await page.evaluate(() => localStorage.setItem('issue-6221-auth', 'good')) + + await page.goto('/issue-6221/article/1') + + await expect(page.getByTestId('issue-6221-article')).toHaveText( + 'Article content for 1', + ) + await expect(page).toHaveTitle('Article Title for 1') + + await page.evaluate(() => localStorage.removeItem('issue-6221-auth')) + await page.reload() + await expect(page.getByText('Article Not Accessible.')).toBeVisible() + await expect(page).toHaveTitle('title n/a') + + await page.evaluate(() => localStorage.setItem('issue-6221-auth', 'good')) + await page.getByTestId('issue-6221-dashboard-link').click() + await expect(page.getByTestId('issue-6221-dashboard')).toBeVisible() + await expect(page).toHaveTitle('Dashboard') + + await page.goBack() + await expect(page.getByTestId('issue-6221-article')).toHaveText( + 'Article content for 1', + ) + await expect(page).toHaveTitle('Article Title for 1') +}) diff --git a/e2e/react-start/hmr/tests/app.spec.ts b/e2e/react-start/hmr/tests/app.spec.ts index 6c70843c04..69ce1d20f3 100644 --- a/e2e/react-start/hmr/tests/app.spec.ts +++ b/e2e/react-start/hmr/tests/app.spec.ts @@ -38,6 +38,21 @@ const routeFilePaths = { type RouteFileKey = keyof typeof routeFilePaths +const rootLoaderBlock = " loader: () => ({\n crumb: 'Home',\n }),\n" +const redirectRootLoaderBlock = + " staleTime: Infinity,\n loader: () => ({\n crumb:\n typeof window === 'undefined'\n ? 'Home Armed'\n : (window.sessionStorage.getItem('hmr-root-crumb') ?? 'Home Armed'),\n }),\n" +const childBeforeLoadBlock = + " beforeLoad: () => ({\n greeting: 'Hello',\n }),\n" +const childLoaderBlock = " loader: () => ({\n crumb: 'Child',\n }),\n" +const failingChildOnStayBlock = + " onStay: () => {\n ;(window as any).__TSR_HMR_FAILURE_RAN__ = true\n throw new Error('intentional HMR lifecycle failure')\n },\n" +const pendingNavigationChildLoaderBlock = + " loader: async () => {\n if (typeof window !== 'undefined') {\n const gate = (window as any).__TSR_HMR_PENDING_NAV_GATE__\n if (gate) {\n ;(window as any).__TSR_HMR_PENDING_NAV_GATE__ = undefined\n ;(window as any).__TSR_HMR_PENDING_NAV_EVENTS__.push('loader-start')\n await gate\n ;(window as any).__TSR_HMR_PENDING_NAV_EVENTS__.push('loader-end')\n }\n }\n return { crumb: 'Child' }\n },\n" +const pendingNavigationChildOnStayBlock = + " onStay: () => {\n ;(window as any).__TSR_HMR_PENDING_NAV_EVENTS__.push('onStay')\n throw new Error('intentional HMR failure after pending navigation')\n },\n" +const redirectChildBeforeLoadBlock = + " beforeLoad: () => {\n throw redirect({ to: '/' })\n },\n" + const routeFiles = Object.fromEntries( Object.entries(routeFilePaths).map(([key, relativePath]) => [ key, @@ -141,6 +156,8 @@ function normalizeRouteSource(routeFileKey: RouteFileKey, source: string) { } if (routeFileKey === 'root') { + next = next.replace(redirectRootLoaderBlock, rootLoaderBlock) + for (const marker of [ 'root-component-inline-baseline', 'root-component-inline-updated', @@ -209,30 +226,36 @@ function normalizeRouteSource(routeFileKey: RouteFileKey, source: string) { } if (routeFileKey === 'child') { - const beforeLoadBlock = - " beforeLoad: () => ({\n greeting: 'Hello',\n }),\n" - const loaderBlock = " loader: () => ({\n crumb: 'Child',\n }),\n" - next = replaceAll(next, "greeting: 'Hi'", "greeting: 'Hello'") + next = next.replace(failingChildOnStayBlock, '') + next = next.replace(pendingNavigationChildOnStayBlock, '') + next = next.replace(pendingNavigationChildLoaderBlock, childLoaderBlock) + next = next.replace(redirectChildBeforeLoadBlock, childBeforeLoadBlock) + next = next.replace( + "import { createFileRoute, redirect } from '@tanstack/react-router'", + "import { createFileRoute } from '@tanstack/react-router'", + ) + next = replaceAll(next, "crumb: 'Child Recovered'", "crumb: 'Child'") + next = replaceAll(next, "crumb: 'Child Failed'", "crumb: 'Child'") next = replaceAll(next, "crumb: 'Child Updated Again'", "crumb: 'Child'") next = replaceAll(next, "crumb: 'Child Updated'", "crumb: 'Child'") - if (!next.includes(beforeLoadBlock)) { + if (!next.includes(childBeforeLoadBlock)) { next = next.replace( ' component: Child,\n', - `${beforeLoadBlock} component: Child,\n`, + `${childBeforeLoadBlock} component: Child,\n`, ) } - if (!next.includes(loaderBlock)) { + if (!next.includes(childLoaderBlock)) { const withLoaderAfterBeforeLoad = next.replace( - `${beforeLoadBlock} component: Child,\n`, - `${beforeLoadBlock}${loaderBlock} component: Child,\n`, + `${childBeforeLoadBlock} component: Child,\n`, + `${childBeforeLoadBlock}${childLoaderBlock} component: Child,\n`, ) next = withLoaderAfterBeforeLoad === next ? next.replace( ' component: Child,\n', - `${loaderBlock} component: Child,\n`, + `${childLoaderBlock} component: Child,\n`, ) : withLoaderAfterBeforeLoad } @@ -661,6 +684,178 @@ test.describe('react-start hmr', () => { await expect(page.getByTestId('child')).toHaveText('child') }) + test('rolls back a failed route refresh and accepts the next HMR update', async ({ + page, + }) => { + await page.goto('/child') + await page.getByTestId('hydrated').waitFor({ state: 'visible' }) + await page.getByTestId('root-message').fill('preserved through failure') + await expect(page.getByTestId('crumb-/child')).toHaveText('Child') + + await page.evaluate(() => { + ;(window as any).__TSR_HMR_FAILURE_RAN__ = false + }) + + await rewriteRouteFile( + page, + 'child', + (source) => + source.replace( + childLoaderBlock, + `${failingChildOnStayBlock} loader: () => ({\n crumb: 'Child Failed',\n }),\n`, + ), + async () => { + await waitForRouteModuleUpdate(page, '/child', 'Child Failed') + await page.waitForFunction( + () => (window as any).__TSR_HMR_FAILURE_RAN__ === true, + ) + }, + ) + + await expect(page.getByTestId('crumb-/child')).toHaveText('Child') + await expect(page.getByTestId('root-message')).toHaveValue( + 'preserved through failure', + ) + + await rewriteRouteFile( + page, + 'child', + (source) => + source + .replace(failingChildOnStayBlock, '') + .replace("crumb: 'Child Failed'", "crumb: 'Child Recovered'"), + async () => { + await waitForRouteModuleUpdate(page, '/child', 'Child Recovered') + await hmrExpect(page.getByTestId('crumb-/child')).toHaveText( + 'Child Recovered', + ) + }, + ) + + await expect(page.getByTestId('root-message')).toHaveValue( + 'preserved through failure', + ) + }) + + test('rematerializes retained routes when an HMR update redirects', async ({ + page, + }) => { + await page.goto('/child') + await page.getByTestId('hydrated').waitFor({ state: 'visible' }) + await page.getByTestId('root-message').fill('preserved through redirect') + + await rewriteRouteFile( + page, + 'root', + (source) => source.replace(rootLoaderBlock, redirectRootLoaderBlock), + async () => { + await waitForRouteModuleUpdate(page, '__root__', 'Home Armed') + await hmrExpect(page.getByTestId('crumb-__root__')).toHaveText( + 'Home Armed', + ) + }, + ) + + await page.evaluate(() => { + window.sessionStorage.setItem('hmr-root-crumb', 'Home Redirected') + }) + + await rewriteRouteFile( + page, + 'child', + (source) => + source + .replace( + "import { createFileRoute } from '@tanstack/react-router'", + "import { createFileRoute, redirect } from '@tanstack/react-router'", + ) + .replace(childBeforeLoadBlock, redirectChildBeforeLoadBlock), + async () => { + await hmrExpect(page).toHaveURL((url) => url.pathname === '/') + await hmrExpect(page.getByTestId('crumb-__root__')).toHaveText( + 'Home Redirected', + ) + }, + ) + + await expect(page.getByTestId('root-message')).toHaveValue( + 'preserved through redirect', + ) + }) + + test('waits for a pending navigation before applying an HMR refresh', async ({ + page, + }) => { + await page.goto('/') + await page.getByTestId('hydrated').waitFor({ state: 'visible' }) + await page.getByTestId('root-message').fill('preserved while pending') + + await page.evaluate(() => { + let release!: () => void + const gate = new Promise((resolve) => { + release = resolve + }) + ;(window as any).__TSR_HMR_PENDING_NAV_EVENTS__ = [] + ;(window as any).__TSR_HMR_PENDING_NAV_GATE__ = gate + ;(window as any).__TSR_HMR_RELEASE_PENDING_NAV__ = release + }) + + await rewriteRouteFile( + page, + 'child', + (source) => + source.replace(childLoaderBlock, pendingNavigationChildLoaderBlock), + async () => { + await page.waitForFunction(() => { + const loader = (window as any).__TSR_ROUTER__?.routesById?.['/child'] + ?.options?.loader + return String(loader).includes('__TSR_HMR_PENDING_NAV_GATE__') + }) + }, + ) + + await page.getByTestId('child-link').click() + await page.waitForFunction( + () => + (window as any).__TSR_HMR_PENDING_NAV_EVENTS__?.[0] === 'loader-start', + ) + + await rewriteRouteFile( + page, + 'child', + (source) => + source.replace( + pendingNavigationChildLoaderBlock, + `${pendingNavigationChildOnStayBlock}${pendingNavigationChildLoaderBlock}`, + ), + async () => { + await page.waitForFunction(() => { + const onStay = (window as any).__TSR_ROUTER__?.routesById?.['/child'] + ?.options?.onStay + return String(onStay).includes( + 'intentional HMR failure after pending navigation', + ) + }) + }, + ) + + await page.evaluate(() => { + ;(window as any).__TSR_HMR_RELEASE_PENDING_NAV__() + }) + + await page.waitForFunction(() => + (window as any).__TSR_HMR_PENDING_NAV_EVENTS__?.includes('onStay'), + ) + await hmrExpect(page.getByTestId('child')).toHaveText('child') + await hmrExpect(page.getByTestId('crumb-/child')).toHaveText('Child') + expect( + await page.evaluate(() => (window as any).__TSR_HMR_PENDING_NAV_EVENTS__), + ).toEqual(['loader-start', 'loader-end', 'onStay']) + await expect(page.getByTestId('root-message')).toHaveValue( + 'preserved while pending', + ) + }) + test('adds a createFileRoute property during HMR', async ({ page }) => { await page.goto('/') await page.getByTestId('hydrated').waitFor({ state: 'visible' }) diff --git a/e2e/react-start/selective-ssr/src/routes/__root.tsx b/e2e/react-start/selective-ssr/src/routes/__root.tsx index bc71c44c1c..3465abf298 100644 --- a/e2e/react-start/selective-ssr/src/routes/__root.tsx +++ b/e2e/react-start/selective-ssr/src/routes/__root.tsx @@ -9,10 +9,10 @@ import { createRootRoute, useRouterState, } from '@tanstack/react-router' +import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' import { z } from 'zod' import { ssrSchema } from '~/search' import appCss from '~/styles/app.css?url' -import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' export const Route = createRootRoute({ head: () => ({ @@ -149,6 +149,14 @@ function RootDocument({ children }: { children: React.ReactNode }) { > Home + + Issue 4614 cached parent + { + test('#4614: cached parent loader data does not cache its beforeLoad context', async ({ + page, + }) => { + await page.goto('/') + await page.getByTestId('issue-4614-cached-link').hover() + + await expect + .poll(() => + page.evaluate(() => (globalThis as any).__issue4614TargetBeforeLoad), + ) + .not.toBeUndefined() + + const { rootBeforeLoads, targetBeforeLoad } = await page.evaluate(() => { + const rootBeforeLoads = + (globalThis as any).__issue4614RootBeforeLoads ?? [] + return { + rootBeforeLoads, + targetBeforeLoad: (globalThis as any).__issue4614TargetBeforeLoad, + } + }) + + expect(rootBeforeLoads).toEqual([ + { + cause: 'preload', + preload: true, + root: 'client', + issue4614Context: 'client:cached', + }, + ]) + expect(targetBeforeLoad).toEqual({ + cause: 'preload', + preload: true, + rootContext: 'client', + issue4614Context: 'client:cached', + scenario: 'cached', + }) + }) + test('new loaderDeps match generation propagates fresh parent context to child (control)', async ({ page, }) => { diff --git a/e2e/solid-start/selective-ssr/dev-server.ts b/e2e/solid-start/selective-ssr/dev-server.ts new file mode 100644 index 0000000000..519075f261 --- /dev/null +++ b/e2e/solid-start/selective-ssr/dev-server.ts @@ -0,0 +1,5 @@ +import { getTestServerPort } from '@tanstack/router-e2e-utils' +import packageJson from './package.json' with { type: 'json' } + +export const devPort = await getTestServerPort(`${packageJson.name}-dev`) +export const devBaseURL = `http://localhost:${devPort}` diff --git a/e2e/solid-start/selective-ssr/playwright.config.ts b/e2e/solid-start/selective-ssr/playwright.config.ts index badd6db0eb..09e5c50410 100644 --- a/e2e/solid-start/selective-ssr/playwright.config.ts +++ b/e2e/solid-start/selective-ssr/playwright.config.ts @@ -1,6 +1,7 @@ import { defineConfig, devices } from '@playwright/test' import { getTestServerPort } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } +import { devBaseURL, devPort } from './dev-server' const PORT = await getTestServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` @@ -18,12 +19,21 @@ export default defineConfig({ baseURL, }, - webServer: { - command: `VITE_SERVER_PORT=${PORT} pnpm build && NODE_ENV=production PORT=${PORT} VITE_SERVER_PORT=${PORT} pnpm start`, - url: baseURL, - reuseExistingServer: !process.env.CI, - stdout: 'pipe', - }, + webServer: [ + { + command: `VITE_SERVER_PORT=${PORT} pnpm build && NODE_ENV=production PORT=${PORT} VITE_SERVER_PORT=${PORT} pnpm start`, + url: baseURL, + reuseExistingServer: !process.env.CI, + stdout: 'pipe', + }, + { + command: `NODE_ENV=development VITE_SERVER_PORT=${devPort} pnpm dev:e2e --host localhost --port ${devPort} --strictPort`, + url: devBaseURL, + reuseExistingServer: !process.env.CI, + stdout: 'pipe', + timeout: 90_000, + }, + ], projects: [ { diff --git a/e2e/solid-start/selective-ssr/src/routeTree.gen.ts b/e2e/solid-start/selective-ssr/src/routeTree.gen.ts index a6984d095e..8fb128de38 100644 --- a/e2e/solid-start/selective-ssr/src/routeTree.gen.ts +++ b/e2e/solid-start/selective-ssr/src/routeTree.gen.ts @@ -9,14 +9,20 @@ // 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 IndexRouteImport } from './routes/index' -import { Route as DataOnlyPendingComponentRouteImport } from './routes/data-only-pending-component' +import { Route as SsrFalsePendingMinRouteImport } from './routes/ssr-false-pending-min' import { Route as PostsRouteImport } from './routes/posts' +import { Route as DataOnlyPendingComponentRouteImport } from './routes/data-only-pending-component' +import { Route as IndexRouteImport } from './routes/index' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const SsrFalsePendingMinRoute = SsrFalsePendingMinRouteImport.update({ + id: '/ssr-false-pending-min', + path: '/ssr-false-pending-min', + getParentRoute: () => rootRouteImport, +} as any) +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', getParentRoute: () => rootRouteImport, } as any) const DataOnlyPendingComponentRoute = @@ -25,9 +31,9 @@ const DataOnlyPendingComponentRoute = path: '/data-only-pending-component', getParentRoute: () => rootRouteImport, } as any) -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ @@ -40,12 +46,14 @@ export interface FileRoutesByFullPath { '/': typeof IndexRoute '/data-only-pending-component': typeof DataOnlyPendingComponentRoute '/posts': typeof PostsRouteWithChildren + '/ssr-false-pending-min': typeof SsrFalsePendingMinRoute '/posts/$postId': typeof PostsPostIdRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/data-only-pending-component': typeof DataOnlyPendingComponentRoute '/posts': typeof PostsRouteWithChildren + '/ssr-false-pending-min': typeof SsrFalsePendingMinRoute '/posts/$postId': typeof PostsPostIdRoute } export interface FileRoutesById { @@ -53,18 +61,30 @@ export interface FileRoutesById { '/': typeof IndexRoute '/data-only-pending-component': typeof DataOnlyPendingComponentRoute '/posts': typeof PostsRouteWithChildren + '/ssr-false-pending-min': typeof SsrFalsePendingMinRoute '/posts/$postId': typeof PostsPostIdRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' | '/data-only-pending-component' | '/posts' | '/posts/$postId' + fullPaths: + | '/' + | '/data-only-pending-component' + | '/posts' + | '/ssr-false-pending-min' + | '/posts/$postId' fileRoutesByTo: FileRoutesByTo - to: '/' | '/data-only-pending-component' | '/posts' | '/posts/$postId' + to: + | '/' + | '/data-only-pending-component' + | '/posts' + | '/ssr-false-pending-min' + | '/posts/$postId' id: | '__root__' | '/' | '/data-only-pending-component' | '/posts' + | '/ssr-false-pending-min' | '/posts/$postId' fileRoutesById: FileRoutesById } @@ -72,15 +92,23 @@ export interface RootRouteChildren { IndexRoute: typeof IndexRoute DataOnlyPendingComponentRoute: typeof DataOnlyPendingComponentRoute PostsRoute: typeof PostsRouteWithChildren + SsrFalsePendingMinRoute: typeof SsrFalsePendingMinRoute } declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/ssr-false-pending-min': { + id: '/ssr-false-pending-min' + path: '/ssr-false-pending-min' + fullPath: '/ssr-false-pending-min' + preLoaderRoute: typeof SsrFalsePendingMinRouteImport + parentRoute: typeof rootRouteImport + } + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } '/data-only-pending-component': { @@ -90,11 +118,11 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof DataOnlyPendingComponentRouteImport parentRoute: typeof rootRouteImport } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/posts/$postId': { @@ -121,6 +149,7 @@ const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, DataOnlyPendingComponentRoute: DataOnlyPendingComponentRoute, PostsRoute: PostsRouteWithChildren, + SsrFalsePendingMinRoute: SsrFalsePendingMinRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) diff --git a/e2e/solid-start/selective-ssr/src/routes/ssr-false-pending-min.tsx b/e2e/solid-start/selective-ssr/src/routes/ssr-false-pending-min.tsx new file mode 100644 index 0000000000..82425f9575 --- /dev/null +++ b/e2e/solid-start/selective-ssr/src/routes/ssr-false-pending-min.tsx @@ -0,0 +1,48 @@ +import { onCleanup, onMount } from 'solid-js' +import { createFileRoute } from '@tanstack/solid-router' + +const pendingMinMs = 1500 + +function recordEvent(type: string) { + if (typeof window === 'undefined') { + return + } + + const global = window as any + global.__events ??= [] + global.__events.push({ type, t: performance.now() }) +} + +function Pending() { + onMount(() => { + recordEvent('pending-mounted') + }) + onCleanup(() => { + recordEvent('pending-unmounted') + }) + + return
    Loading SSR false route...
    +} + +function Target() { + onMount(() => { + recordEvent('target-mounted') + }) + + return
    SSR false route loaded
    +} + +export const Route = createFileRoute('/ssr-false-pending-min')({ + ssr: false, + pendingMs: 0, + pendingMinMs, + pendingComponent: Pending, + loader: async () => { + recordEvent('loader-start') + await new Promise((resolve) => setTimeout(resolve, 50)) + recordEvent('loader-done') + + return { ok: true } + }, + component: Target, +}) diff --git a/e2e/solid-start/selective-ssr/tests/issue-7283-dev-hydration.spec.ts b/e2e/solid-start/selective-ssr/tests/issue-7283-dev-hydration.spec.ts new file mode 100644 index 0000000000..a054a0433a --- /dev/null +++ b/e2e/solid-start/selective-ssr/tests/issue-7283-dev-hydration.spec.ts @@ -0,0 +1,84 @@ +import { expect, test } from '@playwright/test' +import { devBaseURL } from '../dev-server' +import type { Page } from '@playwright/test' + +// Regression test for https://github.com/TanStack/router/issues/7283 +// +// Selective SSR routes (`ssr: false` / `ssr: 'data-only'`) that declare a +// `pendingComponent` must hydrate cleanly in DEV mode. Solid only validates +// hydration markers in dev, so the production webServer configured in +// playwright.config.ts masks the mismatch ("template is not a function", +// route never reaches its loaded component). Playwright therefore owns a +// separate `vite dev` server and drives the app against it. + +function collectHydrationFailures(page: Page) { + const failures: Array = [] + const isHydrationFailure = (text: string) => + text.includes('template is not a function') || + text.includes("wasn't caught by any route") || + text.includes('Hydration Mismatch') + page.on('pageerror', (err) => { + if (isHydrationFailure(err.message)) failures.push(err.message) + }) + page.on('console', (msg) => { + if (msg.type() === 'error' || msg.type() === 'warning') { + const text = msg.text() + if (isHydrationFailure(text)) failures.push(text) + } + }) + return failures +} + +test.describe('dev-mode hydration of selective SSR routes with pendingComponent', () => { + test.setTimeout(120_000) + + test('ssr:false route with pendingComponent hydrates cleanly and reaches its loaded component (issue #7283)', async ({ + page, + }) => { + const failures = collectHydrationFailures(page) + + await page.goto(`${devBaseURL}/ssr-false-pending-min`) + + // The loaded component is the issue oracle: main never reaches it. + await expect(page.getByTestId('ssr-false-target')).toBeVisible({ + timeout: 15_000, + }) + + // No dev hydration mismatch may occur along the way. + expect(failures).toEqual([]) + + await expect(page.getByTestId('ssr-false-pending')).not.toBeAttached() + + const lifecycleEvents = await page.evaluate(() => + ((globalThis as any).__events ?? []) + .map((event: { type: string }) => event.type) + .filter((type: string) => + ['pending-mounted', 'pending-unmounted', 'target-mounted'].includes( + type, + ), + ), + ) + expect(lifecycleEvents.slice(-3)).toEqual([ + 'pending-mounted', + 'pending-unmounted', + 'target-mounted', + ]) + }) + + test('data-only route with pendingComponent hydrates cleanly and reaches its loaded component (regression vs main)', async ({ + page, + }) => { + const failures = collectHydrationFailures(page) + + await page.goto(`${devBaseURL}/data-only-pending-component`) + + await expect( + page.getByTestId('data-only-pending-component-pending'), + ).toBeAttached() + await expect( + page.getByTestId('data-only-pending-component-ready-label'), + ).toHaveText('OK - loader finished', { timeout: 15_000 }) + + expect(failures).toEqual([]) + }) +}) diff --git a/packages/react-router/package.json b/packages/react-router/package.json index 042ad70e2c..9120fbacbf 100644 --- a/packages/react-router/package.json +++ b/packages/react-router/package.json @@ -100,6 +100,7 @@ "devDependencies": { "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.2.0", + "@tanstack/react-query": "catalog:", "@types/node": ">=20", "@vitejs/plugin-react": "^4.3.4", "combinate": "^1.1.11", diff --git a/packages/react-router/src/CatchBoundary.tsx b/packages/react-router/src/CatchBoundary.tsx index 98743f9447..d97e49f8f5 100644 --- a/packages/react-router/src/CatchBoundary.tsx +++ b/packages/react-router/src/CatchBoundary.tsx @@ -5,44 +5,25 @@ import type { ErrorRouteComponent } from './route' import type { ErrorInfo } from 'react' export function CatchBoundary(props: { - getResetKey: () => number | string + getResetKey: () => unknown children: React.ReactNode errorComponent?: ErrorRouteComponent onCatch?: (error: Error, errorInfo: ErrorInfo) => void }) { - const errorComponent = props.errorComponent ?? ErrorComponent - - return ( - { - if (error) { - return React.createElement(errorComponent, { - error, - reset, - }) - } - - return props.children - }} - /> - ) + return } class CatchBoundaryImpl extends React.Component<{ - getResetKey: () => number | string - children: (props: { - error: Error | null - reset: () => void - }) => React.ReactNode + getResetKey: () => unknown + children: React.ReactNode + errorComponent?: ErrorRouteComponent onCatch?: (error: Error, errorInfo: ErrorInfo) => void }> { - state = { error: null } as { error: Error | null; resetKey?: string | number } + state = { error: null } as { error: Error | null; resetKey?: unknown } static getDerivedStateFromProps( - props: { getResetKey: () => string | number }, - state: { resetKey?: string | number; error: Error | null }, + props: { getResetKey: () => unknown }, + state: { resetKey?: unknown; error: Error | null }, ) { const resetKey = props.getResetKey() @@ -55,21 +36,20 @@ class CatchBoundaryImpl extends React.Component<{ static getDerivedStateFromError(error: Error) { return { error } } - reset() { + reset = () => { this.setState({ error: null }) } componentDidCatch(error: Error, errorInfo: ErrorInfo) { - if (this.props.onCatch) { - this.props.onCatch(error, errorInfo) - } + this.props.onCatch?.(error, errorInfo) } render() { - return this.props.children({ - error: this.state.error, - reset: () => { - this.reset() - }, - }) + const error = this.state.error + return error + ? React.createElement(this.props.errorComponent ?? ErrorComponent, { + error, + reset: this.reset, + }) + : this.props.children } } diff --git a/packages/react-router/src/ClientOnly.tsx b/packages/react-router/src/ClientOnly.tsx index 10f903b204..102859d75a 100644 --- a/packages/react-router/src/ClientOnly.tsx +++ b/packages/react-router/src/ClientOnly.tsx @@ -31,11 +31,7 @@ export interface ClientOnlyProps { * ``` */ export function ClientOnly({ children, fallback = null }: ClientOnlyProps) { - return useHydrated() ? ( - {children} - ) : ( - {fallback} - ) + return {useHydrated() ? children : fallback} } /** diff --git a/packages/react-router/src/Match.tsx b/packages/react-router/src/Match.tsx index 5de5546aeb..6bca031a4d 100644 --- a/packages/react-router/src/Match.tsx +++ b/packages/react-router/src/Match.tsx @@ -2,14 +2,7 @@ import * as React from 'react' import { useStore } from '@tanstack/react-store' -import { - createControlledPromise, - getLocationChangeInfo, - invariant, - isNotFound, - isRedirect, - rootRouteId, -} from '@tanstack/router-core' +import { isNotFound, rootRouteId } from '@tanstack/router-core' import { isServer } from '@tanstack/router-core/isServer' import { CatchBoundary, ErrorComponent } from './CatchBoundary' import { useRouter } from './useRouter' @@ -19,131 +12,59 @@ import { SafeFragment } from './SafeFragment' import { renderRouteNotFound } from './renderRouteNotFound' import { ScrollRestoration } from './scroll-restoration' import { ClientOnly } from './ClientOnly' -import { useLayoutEffect } from './utils' import type { AnyRoute, AnyRouteMatch, - ParsedLocation, RootRouteOptions, } from '@tanstack/router-core' +export function renderPending( + router: ReturnType, + route?: AnyRoute, +) { + const PendingComponent = + route?.options.pendingComponent ?? router.options.defaultPendingComponent + return PendingComponent ? : null +} + type OutletMatchSelection = [ - routeId: string | undefined, parentGlobalNotFound: boolean, + parentNotFoundError: unknown, ] -const matchViewFieldsEqual = (a: AnyRouteMatch, b: AnyRouteMatch) => - a.routeId === b.routeId && a._displayPending === b._displayPending - const outletMatchSelectionEqual = ( a: OutletMatchSelection, b: OutletMatchSelection, ) => a[0] === b[0] && a[1] === b[1] export const Match = React.memo(function MatchImpl({ - matchId, + routeId, }: { - matchId: string + routeId: string }) { const router = useRouter() if (isServer ?? router.isServer) { - const match = router.stores.matchStores.get(matchId)?.get() - if (!match) { - if (process.env.NODE_ENV !== 'production') { - throw new Error( - `Invariant failed: Could not find match for matchId "${matchId}". Please file an issue!`, - ) - } - - invariant() - } - - const routeId = match.routeId as string - const parentRouteId = (router.routesById[routeId] as AnyRoute).parentRoute - ?.id - - return ( - - ) + const match = router.stores.byRoute.get(routeId)!.get()! + return } - // Subscribe directly to the match store from the pool. - // The matchId prop is stable for this component's lifetime (set by Outlet), - // and reconcileMatchPool reuses stores for the same matchId. - - const matchStore = router.stores.matchStores.get(matchId) - if (!matchStore) { - if (process.env.NODE_ENV !== 'production') { - throw new Error( - `Invariant failed: Could not find match for matchId "${matchId}". Please file an issue!`, - ) - } - - invariant() - } - // eslint-disable-next-line react-hooks/rules-of-hooks - const resetKey = useStore(router.stores.loadedAt, (loadedAt) => loadedAt) + const matchStore = router.stores.getMatchStore(routeId) // eslint-disable-next-line react-hooks/rules-of-hooks - const match = useStore(matchStore, (value) => value, matchViewFieldsEqual) - // eslint-disable-next-line react-hooks/rules-of-hooks - const matchState = React.useMemo(() => { - const routeId = match.routeId as string - const parentRouteId = (router.routesById[routeId] as AnyRoute).parentRoute - ?.id - - return { - routeId, - ssr: match.ssr, - _displayPending: match._displayPending, - parentRouteId: parentRouteId as string | undefined, - } satisfies MatchViewState - }, [match._displayPending, match.routeId, match.ssr, router.routesById]) - - return ( - - ) + const match = useStore(matchStore, (value) => value) + return }) -type MatchViewState = { - routeId: string - ssr: boolean | 'data-only' | undefined - _displayPending: boolean | undefined - parentRouteId: string | undefined -} - function MatchView({ router, - matchId, - resetKey, - matchState, + match, }: { router: ReturnType - matchId: string - resetKey: number - matchState: MatchViewState + match: AnyRouteMatch }) { - const route: AnyRoute = router.routesById[matchState.routeId] + const route: AnyRoute = router.routesById[match.routeId] - const PendingComponent = - route.options.pendingComponent ?? router.options.defaultPendingComponent - - const pendingElement = PendingComponent ? : null + const pendingElement = renderPending(router, route) const routeErrorComponent = route.options.errorComponent ?? router.options.defaultErrorComponent @@ -151,19 +72,16 @@ function MatchView({ const routeOnCatch = route.options.onCatch ?? router.options.defaultOnCatch const routeNotFoundComponent = route.isRoot - ? // If it's the root route, use the globalNotFound option, with fallback to the notFoundRoute's component + ? // If it's the root route, use the _notFound option, with fallback to the notFoundRoute's component (route.options.notFoundComponent ?? router.options.notFoundRoute?.options.component) : route.options.notFoundComponent - const resolvedNoSsr = - matchState.ssr === false || matchState.ssr === 'data-only' + const resolvedNoSsr = match.ssr === false || match.ssr === 'data-only' const ResolvedSuspenseBoundary = - // If we're on the root route, allow forcefully wrapping in suspense - (!route.isRoot || route.options.wrapInSuspense || resolvedNoSsr) && (route.options.wrapInSuspense ?? - PendingComponent ?? - ((route.options.errorComponent as any)?.preload || resolvedNoSsr)) + pendingElement ?? + ((route.options.errorComponent as any)?.preload || resolvedNoSsr)) ? React.Suspense : SafeFragment @@ -180,235 +98,68 @@ function MatchView({ : SafeFragment return ( - + resetKey} - errorComponent={routeErrorComponent || ErrorComponent} + getResetKey={() => match} + errorComponent={routeErrorComponent as any} onCatch={(error, errorInfo) => { // Forward not found errors (we don't want to show the error component for these) if (isNotFound(error)) { - error.routeId ??= matchState.routeId as any + error.routeId ??= match.routeId throw error } if (process.env.NODE_ENV !== 'production') { - console.warn(`Warning: Error in route match: ${matchId}`) + console.warn(`Warning: Error in route match: ${match.id}`) } routeOnCatch?.(error, errorInfo) }} > { - error.routeId ??= matchState.routeId as any - - // If the current not found handler doesn't exist or it has a - // route ID which doesn't match the current route, rethrow the error - if ( - !routeNotFoundComponent || - (error.routeId && error.routeId !== matchState.routeId) || - (!error.routeId && !route.isRoot) - ) + error.routeId ??= match.routeId + + if (error.routeId !== match.routeId) { throw error + } - return React.createElement(routeNotFoundComponent, error as any) + return React.createElement( + routeNotFoundComponent!, + error as any, + ) }} > - {resolvedNoSsr || matchState._displayPending ? ( + {resolvedNoSsr ? ( - + ) : ( - + )} - {matchState.parentRouteId === rootRouteId ? ( - <> - - {router.options.scrollRestoration && (isServer ?? router.isServer) ? ( - - ) : null} - + {(isServer ?? router.isServer) && + route.parentRoute?.id === rootRouteId && + router.options.scrollRestoration ? ( + ) : null} ) } -// On Rendered can't happen above the root layout because it needs to run after -// the route subtree has committed below the root layout. Keeping it here lets -// us fire onRendered even after a hydration mismatch above the root layout -// (like bad head/link tags, which is common). -function OnRendered() { - const router = useRouter() - - if (isServer ?? router.isServer) { - return null - } - - // Track the resolvedLocation as of the last render so that onRendered can - // report the correct fromLocation. By the time this effect fires, - // resolvedLocation has already been updated to the new location by - // Transitioner, so we cannot use router.stores.resolvedLocation.get() - // directly as the fromLocation. - // @ts-expect-error -- init to `undefined` but don't write `undefined` to shave bytes - // eslint-disable-next-line react-hooks/rules-of-hooks - const prevResolvedLocationRef = React.useRef< - ParsedLocation | undefined - >() - // eslint-disable-next-line react-hooks/rules-of-hooks - const renderedLocationKey = useStore( - router.stores.resolvedLocation, - (resolvedLocation) => resolvedLocation?.state.__TSR_key, - ) - - // eslint-disable-next-line react-hooks/rules-of-hooks - useLayoutEffect(() => { - const currentResolvedLocation = router.stores.resolvedLocation.get() - const previousResolvedLocation = prevResolvedLocationRef.current - - if ( - currentResolvedLocation && - (!previousResolvedLocation || - previousResolvedLocation.href !== currentResolvedLocation.href) - ) { - router.emit({ - type: 'onRendered', - ...getLocationChangeInfo( - router.stores.location.get(), - previousResolvedLocation ?? currentResolvedLocation, - ), - }) - } - prevResolvedLocationRef.current = currentResolvedLocation - }, [renderedLocationKey, router]) - - return null -} - export const MatchInner = React.memo(function MatchInnerImpl({ - matchId, + match, }: { - matchId: string + match: AnyRouteMatch }): any { const router = useRouter() - - const getMatchPromise = ( - match: { - id: string - _nonReactive: { - displayPendingPromise?: Promise - minPendingPromise?: Promise - loadPromise?: Promise - } - }, - key: 'displayPendingPromise' | 'minPendingPromise' | 'loadPromise', - ) => { - return ( - router.getMatch(match.id)?._nonReactive[key] ?? match._nonReactive[key] - ) - } - - if (isServer ?? router.isServer) { - const match = router.stores.matchStores.get(matchId)?.get() - if (!match) { - if (process.env.NODE_ENV !== 'production') { - throw new Error( - `Invariant failed: Could not find match for matchId "${matchId}". Please file an issue!`, - ) - } - - invariant() - } - - const routeId = match.routeId as string - const route = router.routesById[routeId] as AnyRoute - const remountFn = - (router.routesById[routeId] as AnyRoute).options.remountDeps ?? - router.options.defaultRemountDeps - const remountDeps = remountFn?.({ - routeId, - loaderDeps: match.loaderDeps, - params: match._strictParams, - search: match._strictSearch, - }) - const key = remountDeps ? JSON.stringify(remountDeps) : undefined - const Comp = route.options.component ?? router.options.defaultComponent - const out = Comp ? : - - if (match._displayPending) { - throw getMatchPromise(match, 'displayPendingPromise') - } - - if (match._forcePending) { - throw getMatchPromise(match, 'minPendingPromise') - } - - if (match.status === 'pending') { - throw getMatchPromise(match, 'loadPromise') - } - - if (match.status === 'notFound') { - if (!isNotFound(match.error)) { - if (process.env.NODE_ENV !== 'production') { - throw new Error('Invariant failed: Expected a notFound error') - } - - invariant() - } - return renderRouteNotFound(router, route, match.error) - } - - if (match.status === 'redirected') { - if (!isRedirect(match.error)) { - if (process.env.NODE_ENV !== 'production') { - throw new Error('Invariant failed: Expected a redirect error') - } - - invariant() - } - throw getMatchPromise(match, 'loadPromise') - } - - if (match.status === 'error') { - const RouteErrorComponent = - (route.options.errorComponent ?? - router.options.defaultErrorComponent) || - ErrorComponent - return ( - - ) - } - - return out - } - - const matchStore = router.stores.matchStores.get(matchId) - if (!matchStore) { - if (process.env.NODE_ENV !== 'production') { - throw new Error( - `Invariant failed: Could not find match for matchId "${matchId}". Please file an issue!`, - ) - } - - invariant() - } - // eslint-disable-next-line react-hooks/rules-of-hooks - const match = useStore(matchStore, (value) => value) - const routeId = match.routeId as string + const routeId = match.routeId const route = router.routesById[routeId] as AnyRoute - // eslint-disable-next-line react-hooks/rules-of-hooks const key = React.useMemo(() => { const remountFn = - (router.routesById[routeId] as AnyRoute).options.remountDeps ?? - router.options.defaultRemountDeps + route.options.remountDeps ?? router.options.defaultRemountDeps const remountDeps = remountFn?.({ routeId, loaderDeps: match.loaderDeps, @@ -421,85 +172,26 @@ export const MatchInner = React.memo(function MatchInnerImpl({ match.loaderDeps, match._strictParams, match._strictSearch, + route.options.remountDeps, router.options.defaultRemountDeps, - router.routesById, ]) - - // eslint-disable-next-line react-hooks/rules-of-hooks const out = React.useMemo(() => { const Comp = route.options.component ?? router.options.defaultComponent - if (Comp) { - return - } - return + return Comp ? : }, [key, route.options.component, router.options.defaultComponent]) - if (match._displayPending) { - throw getMatchPromise(match, 'displayPendingPromise') - } - - if (match._forcePending) { - throw getMatchPromise(match, 'minPendingPromise') - } - - // see also hydrate() in packages/router-core/src/ssr/ssr-client.ts if (match.status === 'pending') { - // We're pending, and if we have a minPendingMs, we need to wait for it - const pendingMinMs = - route.options.pendingMinMs ?? router.options.defaultPendingMinMs - if (pendingMinMs) { - const routerMatch = router.getMatch(match.id) - if (routerMatch && !routerMatch._nonReactive.minPendingPromise) { - // Create a promise that will resolve after the minPendingMs - if (!(isServer ?? router.isServer)) { - const minPendingPromise = createControlledPromise() - - routerMatch._nonReactive.minPendingPromise = minPendingPromise - - setTimeout(() => { - minPendingPromise.resolve() - // We've handled the minPendingPromise, so we can delete it - routerMatch._nonReactive.minPendingPromise = undefined - }, pendingMinMs) - } - } + if (router._tx) { + throw router._tx[5] } - throw getMatchPromise(match, 'loadPromise') + return renderPending(router, route) } if (match.status === 'notFound') { - if (!isNotFound(match.error)) { - if (process.env.NODE_ENV !== 'production') { - throw new Error('Invariant failed: Expected a notFound error') - } - - invariant() - } return renderRouteNotFound(router, route, match.error) } - if (match.status === 'redirected') { - // A match can be observed as redirected during an in-flight transition, - // especially when pending UI is already rendering. Suspend on the match's - // load promise so React can abandon this stale render and continue the - // redirect transition. - if (!isRedirect(match.error)) { - if (process.env.NODE_ENV !== 'production') { - throw new Error('Invariant failed: Expected a redirect error') - } - - invariant() - } - - throw getMatchPromise(match, 'loadPromise') - } - if (match.status === 'error') { - // If we're on the server, we need to use React's new and super - // wonky api for throwing errors from a server side render inside - // of a suspense boundary. This is the only way to get - // renderToPipeableStream to not hang indefinitely. - // We'll serialize the error and rethrow it on the client. if (isServer ?? router.isServer) { const RouteErrorComponent = (route.options.errorComponent ?? @@ -515,7 +207,6 @@ export const MatchInner = React.memo(function MatchInnerImpl({ /> ) } - throw match.error } @@ -530,74 +221,54 @@ export const MatchInner = React.memo(function MatchInnerImpl({ */ export const Outlet = React.memo(function OutletImpl() { const router = useRouter() - const matchId = React.useContext(matchContext) + const routeId = React.useContext(matchContext)! - let routeId: string | undefined - let parentGlobalNotFound = false - let childMatchId: string | undefined + let parentGlobalNotFound: boolean + let parentNotFoundError: unknown + let childRouteId: string | undefined if (isServer ?? router.isServer) { const matches = router.stores.matches.get() - const parentIndex = matchId - ? matches.findIndex((match) => match.id === matchId) - : -1 - const parentMatch = parentIndex >= 0 ? matches[parentIndex] : undefined - routeId = parentMatch?.routeId as string | undefined - parentGlobalNotFound = parentMatch?.globalNotFound ?? false - childMatchId = - parentIndex >= 0 ? (matches[parentIndex + 1]?.id as string) : undefined + const parentIndex = matches.findIndex((match) => match.routeId === routeId) + const parentMatch = matches[parentIndex]! + parentGlobalNotFound = !!parentMatch._notFound + parentNotFoundError = parentMatch.error + childRouteId = matches[parentIndex + 1]?.routeId } else { - // Subscribe directly to the match store from the pool instead of - // the two-level byId → matchStore pattern. - const parentMatchStore = matchId - ? router.stores.matchStores.get(matchId) - : undefined + const parentMatchStore = router.stores.getMatchStore(routeId) // eslint-disable-next-line react-hooks/rules-of-hooks - ;[routeId, parentGlobalNotFound] = useStore( + ;[parentGlobalNotFound, parentNotFoundError] = useStore( parentMatchStore, - (match): OutletMatchSelection => [ - match?.routeId as string | undefined, - match?.globalNotFound ?? false, - ], + (match): OutletMatchSelection => [!!match!._notFound, match!.error], outletMatchSelectionEqual, ) // eslint-disable-next-line react-hooks/rules-of-hooks - childMatchId = useStore(router.stores.matchesId, (ids) => { - const index = ids.findIndex((id) => id === matchId) - return ids[index + 1] + childRouteId = useStore(router.stores.ids, (ids) => { + return ids[ids.indexOf(routeId) + 1] }) } - const route = routeId ? router.routesById[routeId] : undefined - - const pendingElement = router.options.defaultPendingComponent ? ( - - ) : null - if (parentGlobalNotFound) { - if (!route) { - if (process.env.NODE_ENV !== 'production') { - throw new Error( - 'Invariant failed: Could not resolve route for Outlet render', - ) - } - - invariant() - } - return renderRouteNotFound(router, route, undefined) + return renderRouteNotFound( + router, + router.routesById[routeId], + parentNotFoundError, + ) } - if (!childMatchId) { + if (!childRouteId) { return null } - const nextMatch = + const nextMatch = if (routeId === rootRouteId) { return ( - {nextMatch} + + {nextMatch} + ) } diff --git a/packages/react-router/src/Matches.tsx b/packages/react-router/src/Matches.tsx index 62d0fc42ae..3be4173996 100644 --- a/packages/react-router/src/Matches.tsx +++ b/packages/react-router/src/Matches.tsx @@ -4,12 +4,13 @@ import * as React from 'react' import { useStore } from '@tanstack/react-store' import { rootRouteId } from '@tanstack/router-core' import { isServer } from '@tanstack/router-core/isServer' -import { CatchBoundary, ErrorComponent } from './CatchBoundary' +import { CatchBoundary } from './CatchBoundary' import { useRouter } from './useRouter' import { useStructuralSharing } from './useMatch' -import { Transitioner } from './Transitioner' +import { useLayoutEffect } from './utils' +import { Transitioner, settleOwner } from './Transitioner' import { matchContext } from './matchContext' -import { Match } from './Match' +import { Match, renderPending } from './Match' import { SafeFragment } from './SafeFragment' import type { StructuralSharingOption, @@ -48,23 +49,19 @@ export function Matches() { const router = useRouter() const rootRoute: AnyRoute = router.routesById[rootRouteId] - const PendingComponent = - rootRoute.options.pendingComponent ?? router.options.defaultPendingComponent - - const pendingElement = PendingComponent ? : null + const pendingElement = renderPending(router, rootRoute) // Do not render a root Suspense during SSR or hydrating from SSR const ResolvedSuspense = - (isServer ?? router.isServer) || - (typeof document !== 'undefined' && router.ssr) - ? SafeFragment - : React.Suspense + (isServer ?? router.isServer) || router.ssr ? SafeFragment : React.Suspense const inner = ( - + <> {!(isServer ?? router.isServer) && } - - + + + + ) return router.options.InnerWrap ? ( @@ -76,26 +73,33 @@ export function Matches() { function MatchesInner() { const router = useRouter() - const _isServer = isServer ?? router.isServer - const matchId = _isServer - ? router.stores.firstId.get() - : // eslint-disable-next-line react-hooks/rules-of-hooks - useStore(router.stores.firstId, (id) => id) - const resetKey = _isServer - ? router.stores.loadedAt.get() - : // eslint-disable-next-line react-hooks/rules-of-hooks - useStore(router.stores.loadedAt, (loadedAt) => loadedAt) + const acknowledgement = router._rendered! + const matches = + (isServer ?? router.isServer) + ? router.stores.matches.get() + : // eslint-disable-next-line react-hooks/rules-of-hooks + useStore( + router.stores.matches, + (value) => acknowledgement[0 /* offered */] ?? value, + ) + const match = matches[0] + const routeId = match?.routeId + + useLayoutEffect(() => { + if (acknowledgement[0 /* offered */] === matches) { + settleOwner(acknowledgement, true) + } + }, [acknowledgement, matches]) - const matchComponent = matchId ? : null + const matchComponent = routeId ? : null return ( - + {router.options.disableGlobalCatchBoundary ? ( matchComponent ) : ( resetKey} - errorComponent={ErrorComponent} + getResetKey={() => match} onCatch={ process.env.NODE_ENV !== 'production' ? (error) => { @@ -143,7 +147,11 @@ export function useMatchRoute() { if (!(isServer ?? router.isServer)) { // eslint-disable-next-line react-hooks/rules-of-hooks - useStore(router.stores.matchRouteDeps, (d) => d) + useStore(router.stores.location, (location) => location.href) + // eslint-disable-next-line react-hooks/rules-of-hooks + useStore(router.stores.resolvedLocation, (location) => location?.href) + // eslint-disable-next-line react-hooks/rules-of-hooks + useStore(router.stores.status, (status) => status) } return React.useCallback( @@ -255,20 +263,8 @@ export function useMatches< } /** - * Read the full array of active route matches or select a derived subset. - * - * Useful for debugging, breadcrumbs, or aggregating metadata across matches. - * - * @returns The array of matches (or the selected value). - * @link https://tanstack.com/router/latest/docs/framework/react/api/router/useMatchesHook - */ - -/** - * Read the full array of active route matches or select a derived subset. - * - * Useful for debugging, breadcrumbs, or aggregating metadata across matches. - * - * @link https://tanstack.com/router/latest/docs/framework/react/api/router/useMatchesHook + * Read the presented route matches above the current match, or select a + * derived value from them. */ export function useParentMatches< TRouter extends AnyRouter = RegisteredRouter, @@ -278,13 +274,13 @@ export function useParentMatches< opts?: UseMatchesBaseOptions & StructuralSharingOption, ): UseMatchesResult { - const contextMatchId = React.useContext(matchContext) + const contextRouteId = React.useContext(matchContext) return useMatches({ select: (matches: Array>) => { matches = matches.slice( 0, - matches.findIndex((d) => d.id === contextMatchId), + matches.findIndex((d) => d.routeId === contextRouteId), ) return opts?.select ? opts.select(matches) : matches }, @@ -293,8 +289,8 @@ export function useParentMatches< } /** - * Read the array of active route matches that are children of the current - * match (or selected parent) in the match tree. + * Read the presented route matches below the current match, or select a + * derived value from them. */ export function useChildMatches< TRouter extends AnyRouter = RegisteredRouter, @@ -304,12 +300,12 @@ export function useChildMatches< opts?: UseMatchesBaseOptions & StructuralSharingOption, ): UseMatchesResult { - const contextMatchId = React.useContext(matchContext) + const contextRouteId = React.useContext(matchContext) return useMatches({ select: (matches: Array>) => { matches = matches.slice( - matches.findIndex((d) => d.id === contextMatchId) + 1, + matches.findIndex((d) => d.routeId === contextRouteId) + 1, ) return opts?.select ? opts.select(matches) : matches }, diff --git a/packages/react-router/src/RouterProvider.tsx b/packages/react-router/src/RouterProvider.tsx index 4846ab17fc..81c3fb4ece 100644 --- a/packages/react-router/src/RouterProvider.tsx +++ b/packages/react-router/src/RouterProvider.tsx @@ -50,8 +50,8 @@ export function RouterContextProvider< } /** - * Top-level component that renders the active route matches and provides the - * router to the React tree via context. + * Renders the current match presentation and provides the router to the React + * tree via context. * * Accepts the same options as `createRouter` via props to update the router * instance after creation. diff --git a/packages/react-router/src/Scripts.tsx b/packages/react-router/src/Scripts.tsx index 5285fbd8db..a1b189d4c6 100644 --- a/packages/react-router/src/Scripts.tsx +++ b/packages/react-router/src/Scripts.tsx @@ -1,5 +1,5 @@ import { useStore } from '@tanstack/react-store' -import { deepEqual } from '@tanstack/router-core' +import { _getAssetMatches, deepEqual } from '@tanstack/router-core' import { isServer } from '@tanstack/router-core/isServer' import { Asset } from './Asset' import { useRouter } from './useRouter' @@ -17,23 +17,38 @@ export const Scripts = () => { const router = useRouter() const nonce = router.options.ssr?.nonce - const getAssetScripts = (matches: Array) => { - const assetScripts: Array = [] + const getScripts = (matches: Array) => { + matches = _getAssetMatches(matches) + const scripts = matches + .flatMap((match) => match.scripts ?? []) + .filter(Boolean) + .map( + ({ children, ...script }) => + ({ + tag: 'script', + attrs: { + ...script, + suppressHydrationWarning: true, + nonce, + }, + children, + }) satisfies RouterManagedTag, + ) as Array const manifest = router.ssr?.manifest if (!manifest) { - return [] + return scripts } for (const match of matches) { - const scripts = manifest.routes[match.routeId]?.scripts + const manifestScripts = manifest.routes[match.routeId]?.scripts - if (!scripts) { + if (!manifestScripts) { continue } - for (const asset of scripts) { - assetScripts.push({ + for (const asset of manifestScripts) { + scripts.push({ tag: 'script', attrs: { ...asset.attrs, nonce }, children: asset.children, @@ -44,64 +59,35 @@ export const Scripts = () => { } } - return assetScripts + return scripts } - const getScripts = (matches: Array): Array => - ( - matches - .map((match) => match.scripts!) - .flat(1) - .filter(Boolean) as Array - ).map( - ({ children, ...script }) => - ({ - tag: 'script', - attrs: { - ...script, - suppressHydrationWarning: true, - nonce, - }, - children, - }) satisfies RouterManagedTag, - ) - if (isServer ?? router.isServer) { const activeMatches = router.stores.matches.get() - const assetScripts = getAssetScripts(activeMatches) const scripts = getScripts(activeMatches) - return renderScripts(router, scripts, assetScripts) + return renderScripts(router, scripts) } - // eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static - const assetScripts = useStore( - router.stores.matches, - getAssetScripts, - deepEqual, - ) // eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static const scripts = useStore(router.stores.matches, getScripts, deepEqual) - return renderScripts(router, scripts, assetScripts) + return renderScripts(router, scripts) } function renderScripts( router: ReturnType, - scripts: Array, - assetScripts: Array, + scripts: Array, ) { - const allScripts = [...scripts, ...assetScripts] as Array - if ((isServer ?? router.isServer) && router.serverSsr) { const serverBufferedScript = router.serverSsr.takeBufferedScripts() if (serverBufferedScript) { - allScripts.unshift(serverBufferedScript) + scripts.unshift(serverBufferedScript) } } return ( <> - {allScripts.map((asset, i) => ( + {scripts.map((asset, i) => ( ))} diff --git a/packages/react-router/src/Transitioner.tsx b/packages/react-router/src/Transitioner.tsx index bf945571a6..341d09ffcc 100644 --- a/packages/react-router/src/Transitioner.tsx +++ b/packages/react-router/src/Transitioner.tsx @@ -1,43 +1,65 @@ 'use client' import * as React from 'react' -import { batch, useStore } from '@tanstack/react-store' import { getLocationChangeInfo, trimPathRight } from '@tanstack/router-core' -import { useLayoutEffect, usePrevious } from './utils' +import { useLayoutEffect } from './utils' import { useRouter } from './useRouter' +import type { AnyRouter } from '@tanstack/router-core' + +export function settleOwner( + owner: NonNullable, + rendered: boolean, +) { + const settle = owner[1 /* settle */] + owner.length = 0 + settle?.(rendered) +} export function Transitioner() { const router = useRouter() - const mountLoadForRouter = React.useRef({ router, mounted: false }) - - const [isTransitioning, setIsTransitioning] = React.useState(false) - // Track pending state changes - const isLoading = useStore(router.stores.isLoading, (value) => value) - const hasPending = useStore(router.stores.hasPending, (value) => value) - - const previousIsLoading = usePrevious(isLoading) - - const isAnyPending = isLoading || isTransitioning || hasPending - const previousIsAnyPending = usePrevious(isAnyPending) - - const isPagePending = isLoading || hasPending - const previousIsPagePending = usePrevious(isPagePending) - - router.startTransition = (fn: () => void) => { - setIsTransitioning(true) - React.startTransition(() => { - fn() - setIsTransitioning(false) + const acknowledgement = (router._rendered ??= []) + const mounted = + process.env.NODE_ENV !== 'production' + ? // eslint-disable-next-line react-hooks/rules-of-hooks + React.useRef(false) + : undefined + + router.startTransition = (fn, expected) => + new Promise((resolve, reject) => { + settleOwner(acknowledgement, false) + acknowledgement.push(expected, resolve) + React.startTransition(() => { + try { + fn() + } catch (cause) { + if (acknowledgement[1 /* settle */] === resolve) { + acknowledgement.length = 0 + } + reject(cause) + } + }) }) + if (process.env.NODE_ENV !== 'production') { + ;( + router as typeof router & { _cancelTransition?: () => void } + )._cancelTransition = () => settleOwner(acknowledgement, false) } - // Subscribe to location changes - // and try to load the new location - React.useEffect(() => { + // Subscribe before canonicalizing so the initial URL has exactly one load. + useLayoutEffect(() => { const unsub = router.history.subscribe(router.load) + if (mounted?.current) { + return unsub + } + if (mounted) { + mounted.current = true + } + + router.updateLatestLocation() + const location = router.latestLocation const nextLocation = router.buildLocation({ - to: router.latestLocation.pathname, + to: location.pathname, search: true, params: true, hash: true, @@ -46,86 +68,41 @@ export function Transitioner() { }) // Check if the current URL matches the canonical form. - // Compare publicHref (browser-facing URL) for consistency with - // the server-side redirect check in router.beforeLoad. + // Compare publicHref (browser-facing URL) consistently with server + // canonicalization. if ( - trimPathRight(router.latestLocation.publicHref) !== + trimPathRight(location.publicHref) !== trimPathRight(nextLocation.publicHref) ) { - router.commitLocation({ ...nextLocation, replace: true }) - } - - return () => { - unsub() + router.commitLocation({ + ...nextLocation, + replace: true, + ignoreBlocker: true, + }) + return unsub } - }, [router, router.history]) - // Try to load the initial location - useLayoutEffect(() => { + const resolvedLocation = router.stores.resolvedLocation.get() if ( - // if we are hydrating from SSR, loading is triggered in ssr-client - (typeof window !== 'undefined' && router.ssr) || - (mountLoadForRouter.current.router === router && - mountLoadForRouter.current.mounted) + resolvedLocation?.href === location.href && + resolvedLocation.state.__TSR_key === location.state.__TSR_key ) { - return - } - mountLoadForRouter.current = { router, mounted: true } - - const tryLoad = async () => { - try { - await router.load() - } catch (err) { - console.error(err) - } - } - - tryLoad() - }, [router]) - - useLayoutEffect(() => { - // The router was loading and now it's not - if (previousIsLoading && !isLoading) { - router.emit({ - type: 'onLoad', // When the new URL has committed, when the new matches have been loaded into state.matches - ...getLocationChangeInfo( - router.stores.location.get(), - router.stores.resolvedLocation.get(), - ), - }) - } - }, [previousIsLoading, router, isLoading]) - - useLayoutEffect(() => { - // emit onBeforeRouteMount - if (previousIsPagePending && !isPagePending) { - router.emit({ - type: 'onBeforeRouteMount', - ...getLocationChangeInfo( - router.stores.location.get(), - router.stores.resolvedLocation.get(), - ), + acknowledgement.push(router.stores.matches.get(), (rendered) => { + if (rendered) { + router.emit({ + type: 'onRendered', + ...getLocationChangeInfo(resolvedLocation, resolvedLocation), + }) + } }) + } else if (!router._tx) { + router.load().catch(console.error) } - }, [isPagePending, previousIsPagePending, router]) - - useLayoutEffect(() => { - if (previousIsAnyPending && !isAnyPending) { - const changeInfo = getLocationChangeInfo( - router.stores.location.get(), - router.stores.resolvedLocation.get(), - ) - router.emit({ - type: 'onResolved', - ...changeInfo, - }) - batch(() => { - router.stores.status.set('idle') - router.stores.resolvedLocation.set(router.stores.location.get()) - }) - } - }, [isAnyPending, previousIsAnyPending, router]) + return unsub + // `mounted` exists only in development and is a stable ref when present. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [router, router.history]) return null } diff --git a/packages/react-router/src/headContentUtils.tsx b/packages/react-router/src/headContentUtils.tsx index 5ed35bd1ce..d61ca829db 100644 --- a/packages/react-router/src/headContentUtils.tsx +++ b/packages/react-router/src/headContentUtils.tsx @@ -1,6 +1,7 @@ import * as React from 'react' import { useStore } from '@tanstack/react-store' import { + _getAssetMatches, appendUniqueUserTags, deepEqual, escapeHtml, @@ -22,6 +23,7 @@ function buildTagsFromMatches( matches: Array, assetCrossOrigin?: AssetCrossOriginConfig, ): Array { + matches = _getAssetMatches(matches) const routeMeta = matches .map((match) => match.meta) .filter((meta) => meta !== undefined) @@ -187,7 +189,7 @@ function buildTagsFromMatches( } /** - * Build the list of head/link/meta/script tags to render for active matches. + * Build the head/link/meta/script tags from the renderable presented prefix. * Used internally by `HeadContent`. */ export const useTags = (assetCrossOrigin?: AssetCrossOriginConfig) => { @@ -204,226 +206,11 @@ export const useTags = (assetCrossOrigin?: AssetCrossOriginConfig) => { } // eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static - const routeMeta = useStore( - router.stores.matches, - (matches) => { - return matches - .map((match) => match.meta) - .filter((meta) => meta !== undefined) - }, - deepEqual, + const selectTags = React.useCallback( + (matches: Array) => + buildTagsFromMatches(router, nonce, matches, assetCrossOrigin), + [assetCrossOrigin, nonce, router], ) - - // eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static - const meta: Array = React.useMemo(() => { - const resultMeta: Array = [] - const metaByAttribute: Record = {} - let title: RouterManagedTag | undefined - for (let i = routeMeta.length - 1; i >= 0; i--) { - const metas = routeMeta[i]! - for (let j = metas.length - 1; j >= 0; j--) { - const m = metas[j] - if (!m) continue - - if (m.title) { - if (!title) { - title = { - tag: 'title', - children: m.title, - } - } - } else if ('script:ld+json' in m) { - // Handle JSON-LD structured data - // Content is HTML-escaped to prevent XSS when injected via dangerouslySetInnerHTML - try { - const json = JSON.stringify(m['script:ld+json']) - resultMeta.push({ - tag: 'script', - attrs: { - type: 'application/ld+json', - }, - children: escapeHtml(json), - }) - } catch { - // Skip invalid JSON-LD objects - } - } else { - const attribute = m.name ?? m.property - if (attribute) { - if (metaByAttribute[attribute]) { - continue - } else { - metaByAttribute[attribute] = true - } - } - - resultMeta.push({ - tag: 'meta', - attrs: { - ...m, - nonce, - }, - }) - } - } - } - - if (title) { - resultMeta.push(title) - } - - if (nonce) { - resultMeta.push({ - tag: 'meta', - attrs: { - property: 'csp-nonce', - content: nonce, - }, - }) - } - resultMeta.reverse() - - return resultMeta - }, [routeMeta, nonce]) - - // eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static - const links = useStore( - router.stores.matches, - (matches) => { - const constructed = matches - .flatMap((match) => match.links ?? []) - .filter((link) => link !== undefined) - .map((link) => ({ - tag: 'link', - attrs: { - ...link, - nonce, - }, - })) satisfies Array - - return constructed - }, - deepEqual, - ) - - // eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static - const manifestCssTags = useStore( - router.stores.matches, - (matches) => { - const manifest = router.ssr?.manifest - const tags: Array = [] - - if (!manifest) { - return tags - } - - matches.forEach((match) => { - manifest.routes[match.routeId]?.css?.forEach((link) => { - const resolvedLink = resolveManifestCssLink(link) - tags.push({ - tag: 'link', - attrs: { - rel: 'stylesheet', - ...resolvedLink, - crossOrigin: - getAssetCrossOrigin(assetCrossOrigin, 'stylesheet') ?? - resolvedLink.crossOrigin, - suppressHydrationWarning: true, - nonce, - }, - }) - }) - }) - - if (manifest.inlineStyle) { - tags.push({ - tag: 'style', - attrs: { - ...manifest.inlineStyle.attrs, - nonce, - }, - children: manifest.inlineStyle.children, - inlineCss: true, - }) - } - - return tags - }, - deepEqual, - ) - // eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static - const preloadLinks = useStore( - router.stores.matches, - (matches) => { - const preloadLinks: Array = [] - const manifest = router.ssr?.manifest - - if (!manifest) { - return preloadLinks - } - - matches.forEach((match) => { - manifest.routes[match.routeId]?.preloads?.forEach((preload) => { - preloadLinks.push({ - tag: 'link', - attrs: { - ...getScriptPreloadAttrs(manifest, preload, assetCrossOrigin), - nonce, - }, - }) - }) - }) - - return preloadLinks - }, - deepEqual, - ) - - // eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static - const styles = useStore( - router.stores.matches, - (matches) => { - return matches - .flatMap((match) => match.styles ?? []) - .filter((style) => style !== undefined) - .map(({ children, ...attrs }) => ({ - tag: 'style', - attrs: { - ...attrs, - nonce, - }, - children: children as string | undefined, - })) satisfies Array - }, - deepEqual, - ) - - // eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static - const headScripts: Array = useStore( - router.stores.matches, - (matches) => { - return matches - .flatMap((match) => match.headScripts ?? []) - .filter((script) => script !== undefined) - .map(({ children, ...script }) => ({ - tag: 'script', - attrs: { - ...script, - nonce, - }, - children: children as string | undefined, - })) satisfies Array - }, - deepEqual, - ) - - const tags: Array = [] - appendUniqueUserTags(tags, meta) - tags.push(...preloadLinks) - appendUniqueUserTags(tags, links) - tags.push(...manifestCssTags) - appendUniqueUserTags(tags, styles) - appendUniqueUserTags(tags, headScripts) - return tags + return useStore(router.stores.matches, selectTags, deepEqual) } diff --git a/packages/react-router/src/lazyRouteComponent.tsx b/packages/react-router/src/lazyRouteComponent.tsx index b4fe8703c5..7c387e0e31 100644 --- a/packages/react-router/src/lazyRouteComponent.tsx +++ b/packages/react-router/src/lazyRouteComponent.tsx @@ -1,5 +1,6 @@ import * as React from 'react' import { isModuleNotFoundError } from '@tanstack/router-core' +import { isServer } from '@tanstack/router-core/isServer' import { reactUse } from './utils' import type { AsyncRouteComponent } from './route' @@ -24,42 +25,24 @@ export function lazyRouteComponent< let loadPromise: Promise | undefined let comp: T[TKey] | T['default'] let error: any - let reload: boolean const load = () => { if (!loadPromise) { + error = undefined loadPromise = importer() .then((res) => { - loadPromise = undefined + // Keep browser preload behavior unchanged; SSR can reuse the import. + if (!(isServer ?? typeof window === 'undefined')) { + loadPromise = undefined + } comp = res[exportName ?? 'default'] }) .catch((err) => { + loadPromise = undefined // We don't want an error thrown from preload in this case, because // there's nothing we want to do about module not found during preload. // Record the error, the rest is handled during the render path. error = err - // If the load fails due to module not found, it may mean a new version of - // the build was deployed and the user's browser is still using an old version. - // If this happens, the old version in the user's browser would have an outdated - // URL to the lazy module. - // In that case, we want to attempt one window refresh to get the latest. - if (isModuleNotFoundError(error)) { - if ( - error instanceof Error && - typeof window !== 'undefined' && - typeof sessionStorage !== 'undefined' - ) { - // Again, we want to reload one time on module not found error and not enter - // a reload loop if there is some other issue besides an old deploy. - // That's why we store our reload attempt in sessionStorage. - // Use error.message as key because it contains the module path that failed. - const storageKey = `tanstack_router_reload:${error.message}` - if (!sessionStorage.getItem(storageKey)) { - sessionStorage.setItem(storageKey, '1') - reload = true - } - } - } }) } @@ -67,15 +50,23 @@ export function lazyRouteComponent< } const lazyComp = function Lazy(props: any) { - // Now that we're out of preload and into actual render path, - if (reload) { - // If it was a module loading error, - // throw eternal suspense while we wait for window to reload - window.location.reload() - throw new Promise(() => {}) - } if (error) { - // Otherwise, just throw the error + // A missing module can mean that a newer deployment replaced the URL. + // Reload only for the error that is still current at render time, so a + // successful retry cannot leave a stale reload request armed. + if ( + isModuleNotFoundError(error) && + !(isServer ?? typeof window === 'undefined') && + typeof sessionStorage !== 'undefined' + ) { + const storageKey = `tanstack_router_reload:${error.message}` + if (!sessionStorage.getItem(storageKey)) { + sessionStorage.setItem(storageKey, '1') + window.location.reload() + // Suspend forever while the document reloads. + throw new Promise(() => {}) + } + } throw error } diff --git a/packages/react-router/src/ssr/RouterClient.tsx b/packages/react-router/src/ssr/RouterClient.tsx index 3271dcd63f..69abc97f0b 100644 --- a/packages/react-router/src/ssr/RouterClient.tsx +++ b/packages/react-router/src/ssr/RouterClient.tsx @@ -3,16 +3,11 @@ import { Await } from '../awaited' import { RouterProvider } from '../RouterProvider' import type { AnyRouter } from '@tanstack/router-core' -let hydrationPromise: Promise>> | undefined +let hydrationPromise: Promise | undefined export function RouterClient(props: { router: AnyRouter }) { - if (!hydrationPromise) { - if (!props.router.stores.matchesId.get().length) { - hydrationPromise = hydrate(props.router) - } else { - hydrationPromise = Promise.resolve() - } - } + hydrationPromise ??= hydrate(props.router).finally(() => window.$_TSR!.h()) + return ( stream.cancel().catch(() => {}) }, + { + signal: request.signal, + onAbort: () => stream.cancel().catch(() => {}), + }, ) return createSsrStreamResponse( router, new Response(responseStream as any, { - status: router.stores.statusCode.get(), + status: + router._serverResult?.type === 'render' + ? router._serverResult.status + : 200, headers: responseHeaders, }), ) @@ -177,7 +183,7 @@ export const renderRouterToStream = async ({ const responseStream = transformPipeableStreamWithRouter( router, reactAppPassthrough, - { onAbort: abortPipeable }, + { signal: request.signal, onAbort: abortPipeable }, ) responseAttached = true @@ -199,7 +205,10 @@ export const renderRouterToStream = async ({ return createSsrStreamResponse( router, new Response(responseStream as any, { - status: router.stores.statusCode.get(), + status: + router._serverResult?.type === 'render' + ? router._serverResult.status + : 200, headers: responseHeaders, }), ) diff --git a/packages/react-router/src/ssr/renderRouterToString.tsx b/packages/react-router/src/ssr/renderRouterToString.tsx index e9fe4ec779..5e299cc215 100644 --- a/packages/react-router/src/ssr/renderRouterToString.tsx +++ b/packages/react-router/src/ssr/renderRouterToString.tsx @@ -21,7 +21,10 @@ export const renderRouterToString = async ({ } return new Response(`${html}`, { - status: router.stores.statusCode.get(), + status: + router._serverResult?.type === 'render' + ? router._serverResult.status + : 200, headers: responseHeaders, }) } catch (error) { diff --git a/packages/react-router/src/useMatch.tsx b/packages/react-router/src/useMatch.tsx index 6c1d03421d..35d6673962 100644 --- a/packages/react-router/src/useMatch.tsx +++ b/packages/react-router/src/useMatch.tsx @@ -20,12 +20,7 @@ import type { ThrowOrOptional, } from '@tanstack/router-core' -const dummyStore = { - get() {}, - subscribe() { - return { unsubscribe() {} } - }, -} as any +const dummyMatch = {} export function useStructuralSharing< TRouter extends AnyRouter, @@ -147,16 +142,15 @@ export function useMatch< >, ): ThrowOrOptional, TThrow> { const router = useRouter() - const nearestMatchId = React.useContext( + const nearestRouteId = React.useContext( opts.from ? dummyMatchContext : matchContext, ) - const matchStore = opts.from - ? router.stores.getRouteMatchStore(opts.from) - : router.stores.matchStores.get(nearestMatchId!) + const routeId = opts.from ?? nearestRouteId + const matchStore = router.stores.getMatchStore(routeId!) if (isServer ?? router.isServer) { - const match = matchStore?.get() + const match = matchStore.get() if (!match) { if (opts.shouldThrow ?? true) { if (process.env.NODE_ENV !== 'production') { @@ -179,12 +173,12 @@ export function useMatch< useStructuralSharing(opts, router) // eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static - const matchSelection = useStore(matchStore ?? dummyStore, (match) => - match ? selector(match as any) : dummyStore, + const matchSelection = useStore(matchStore, (match) => + match ? selector(match as any) : dummyMatch, ) - if (matchSelection !== dummyStore) { - return matchSelection + if (matchSelection !== dummyMatch) { + return matchSelection as any } if (opts.shouldThrow ?? true) { diff --git a/packages/react-router/src/utils.ts b/packages/react-router/src/utils.ts index f3cbf54613..ca0653e350 100644 --- a/packages/react-router/src/utils.ts +++ b/packages/react-router/src/utils.ts @@ -1,5 +1,6 @@ 'use client' import * as React from 'react' +import { isServer } from '@tanstack/router-core/isServer' // Safe version of React.use() that will not cause compilation errors against // React 18 with Webpack, which statically analyzes imports and fails when it @@ -27,7 +28,9 @@ export function useStableCallback) => any>( } export const useLayoutEffect = - typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect + (isServer ?? typeof window === 'undefined') + ? React.useEffect + : React.useLayoutEffect /** * Taken from https://www.developerway.com/posts/implementing-advanced-use-previous-hook#part3 diff --git a/packages/react-router/tests/Scripts.test.tsx b/packages/react-router/tests/Scripts.test.tsx index 47e345960d..893cef01bc 100644 --- a/packages/react-router/tests/Scripts.test.tsx +++ b/packages/react-router/tests/Scripts.test.tsx @@ -1,4 +1,4 @@ -import { afterEach, describe, expect, test } from 'vitest' +import { afterEach, describe, expect, test, vi } from 'vitest' import { act, cleanup, @@ -9,11 +9,14 @@ import { } from '@testing-library/react' import { createPortal } from 'react-dom' import ReactDOMServer from 'react-dom/server' +import { hydrate } from '@tanstack/router-core/ssr/client' +import { dehydrateSsrMatchId } from '../../router-core/src/ssr/ssr-match-id' import { HeadContent, Link, Outlet, + RouterContextProvider, RouterProvider, createBrowserHistory, createMemoryHistory, @@ -57,6 +60,7 @@ afterEach(() => { cleanup() browserHistories.splice(0).forEach((history) => history.destroy()) window.history.replaceState(null, 'root', '/') + delete window.$_TSR }) describe('ssr scripts', () => { @@ -334,6 +338,119 @@ describe('scripts with async/defer attributes', () => { }) describe('ssr HeadContent', () => { + test('renders descendant assets during a data-only hydration handoff', async () => { + const rootRoute = createRootRoute({}) + const dataOnlyRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/report', + ssr: 'data-only', + loader: () => 'report', + }) + const childRoute = createRoute({ + getParentRoute: () => dataOnlyRoute, + path: '/details', + loader: () => 'details', + head: () => ({ + meta: [{ name: 'data-only-child', content: 'visible' }], + links: [{ rel: 'preload', href: '/data-only-head-link.js' }], + styles: [ + { + id: 'data-only-route-style', + children: '.data-only-child { color: green }', + }, + ], + scripts: [ + { + id: 'data-only-head-script', + type: 'application/json', + children: '{"source":"head"}', + }, + ], + }), + scripts: () => [ + { + id: 'data-only-body-script', + type: 'application/json', + children: '{"source":"body"}', + }, + ], + }) + const router = createRouter({ + history: createMemoryHistory({ + initialEntries: ['/report/details'], + }), + routeTree: rootRoute.addChildren([ + dataOnlyRoute.addChildren([childRoute]), + ]), + }) + const matches = router.matchRoutes(router.latestLocation) + window.$_TSR = { + router: { + dehydratedData: {}, + manifest: { + routes: { + [childRoute.id]: { + css: ['/data-only-manifest.css'], + preloads: ['/data-only-manifest.js'], + scripts: [ + { + attrs: { + id: 'data-only-manifest-script', + type: 'application/json', + }, + children: '{"source":"manifest"}', + }, + ], + }, + }, + }, + matches: matches.map((match, index) => ({ + i: dehydrateSsrMatchId(match.id), + s: 'success', + ssr: index === 1 ? 'data-only' : true, + l: index ? (index === 1 ? 'report' : 'details') : undefined, + u: Date.now(), + })), + }, + h: vi.fn(), + e: vi.fn(), + c: vi.fn(), + p: vi.fn(), + buffer: [], + } + + await hydrate(router) + + expect(router.state.matches.map((match) => match.status)).toEqual([ + 'success', + 'pending', + 'success', + ]) + render( + + + + , + ) + + expect( + document.querySelector('meta[name="data-only-child"]'), + ).not.toBeNull() + expect( + document.querySelector('link[href="/data-only-head-link.js"]'), + ).not.toBeNull() + expect(document.querySelector('#data-only-route-style')).not.toBeNull() + expect(document.querySelector('#data-only-head-script')).not.toBeNull() + expect(document.querySelector('#data-only-body-script')).not.toBeNull() + expect( + document.querySelector('link[href="/data-only-manifest.css"]'), + ).not.toBeNull() + expect( + document.querySelector('link[href="/data-only-manifest.js"]'), + ).not.toBeNull() + expect(document.querySelector('#data-only-manifest-script')).not.toBeNull() + }) + test('derives title, dedupes meta, and allows non-loader HeadContent', async () => { const rootRoute = createRootRoute({ loader: () => @@ -836,6 +953,130 @@ describe('ssr HeadContent', () => { ), ).toHaveLength(1) }) + + test('does not render retained descendant assets past a terminal parent boundary', async () => { + let failParent = false + const childHeadScript = '{"source":"child-head"}' + const childBodyScript = '{"source":"child-body"}' + const childManifestScript = '{"source":"child-manifest"}' + const childPreload = '/terminal-child-preload.js' + const childHeadLink = '/terminal-child-head-link.js' + const rootRoute = createRootRoute({ + component: () => ( + <> + {createPortal(, document.head)} + + + + ), + }) + const parentRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + shouldReload: true, + loader: () => { + if (failParent) { + throw new Error('parent failed') + } + }, + component: Outlet, + errorComponent: () =>
    Parent error
    , + }) + const childRoute = createRoute({ + getParentRoute: () => parentRoute, + path: '/child', + head: () => ({ + meta: [{ name: 'terminal-child', content: 'visible' }], + links: [{ rel: 'preload', href: childHeadLink }], + styles: [{ children: '.terminal-child { color: red }' }], + scripts: [{ type: 'application/ld+json', children: childHeadScript }], + }), + scripts: () => [ + { type: 'application/ld+json', children: childBodyScript }, + ], + component: () =>
    Child content
    , + }) + const router = createRouter({ + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + routeTree: rootRoute.addChildren([parentRoute.addChildren([childRoute])]), + }) + router.ssr = { + manifest: { + routes: { + [childRoute.id]: { + preloads: [childPreload], + scripts: [ + { + attrs: { type: 'application/ld+json' }, + children: childManifestScript, + }, + ], + }, + }, + }, + } + + await router.load() + await act(() => render()) + + await waitFor(() => { + expect( + document.head.querySelector('meta[name="terminal-child"]'), + ).not.toBeNull() + expect( + document.head.querySelector(`link[href="${childPreload}"]`), + ).not.toBeNull() + expect( + document.head.querySelector(`link[href="${childHeadLink}"]`), + ).not.toBeNull() + expect(document.head.textContent).toContain( + '.terminal-child { color: red }', + ) + expect(document.documentElement.textContent).toContain(childHeadScript) + expect(document.documentElement.textContent).toContain(childBodyScript) + expect(document.documentElement.textContent).toContain( + childManifestScript, + ) + }) + + failParent = true + await act(() => router.invalidate()) + await screen.findByText('Parent error') + + expect(router.state.matches).toHaveLength(3) + expect(router.state.matches[1]).toMatchObject({ + routeId: parentRoute.id, + status: 'error', + }) + expect(router.state.matches[2]).toMatchObject({ + routeId: childRoute.id, + meta: [{ name: 'terminal-child', content: 'visible' }], + scripts: [{ type: 'application/ld+json', children: childBodyScript }], + }) + await waitFor(() => { + expect( + document.head.querySelector('meta[name="terminal-child"]'), + ).toBeNull() + expect( + document.head.querySelector(`link[href="${childPreload}"]`), + ).toBeNull() + expect( + document.head.querySelector(`link[href="${childHeadLink}"]`), + ).toBeNull() + expect(document.head.textContent).not.toContain( + '.terminal-child { color: red }', + ) + expect(document.documentElement.textContent).not.toContain( + childHeadScript, + ) + expect(document.documentElement.textContent).not.toContain( + childBodyScript, + ) + expect(document.documentElement.textContent).not.toContain( + childManifestScript, + ) + }) + }) }) describe('data script rendering', () => { diff --git a/packages/react-router/tests/ancestor-loader-child-pending-min.test.tsx b/packages/react-router/tests/ancestor-loader-child-pending-min.test.tsx new file mode 100644 index 0000000000..09945bb9af --- /dev/null +++ b/packages/react-router/tests/ancestor-loader-child-pending-min.test.tsx @@ -0,0 +1,157 @@ +import * as React from 'react' +import { act, cleanup, render, screen } from '@testing-library/react' +import { afterEach, expect, test, vi } from 'vitest' +import { + Outlet, + RouterProvider, + createControlledPromise, + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, +} from '../src' + +afterEach(() => { + cleanup() + vi.useRealTimers() +}) + +test('a child fallback revealed after a fresh ancestor loader keeps its own pendingMinMs', async () => { + vi.useFakeTimers() + + const parentLoader = createControlledPromise() + const childLoader = createControlledPromise() + const rootRoute = createRootRoute({ + component: () => , + }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
    Index
    , + }) + const parentRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + loader: () => parentLoader, + component: () => , + }) + const childRoute = createRoute({ + getParentRoute: () => parentRoute, + path: '/child', + pendingMs: 0, + pendingMinMs: 100, + pendingComponent: () =>
    Child pending
    , + loader: () => childLoader, + component: () =>
    Child content
    , + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([ + indexRoute, + parentRoute.addChildren([childRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/'] }), + defaultPendingMs: 0, + }) + + await router.load() + render() + expect(screen.getByText('Index')).toBeInTheDocument() + + let navigation!: Promise + await act(async () => { + navigation = router.navigate({ to: '/parent/child' }) + await vi.advanceTimersByTimeAsync(25) + }) + + expect(screen.queryByText('Child pending')).not.toBeInTheDocument() + + await act(async () => { + parentLoader.resolve() + await vi.advanceTimersByTimeAsync(0) + }) + expect(screen.getByText('Child pending')).toBeInTheDocument() + expect(screen.queryByText('Child content')).not.toBeInTheDocument() + + await act(async () => { + await vi.advanceTimersByTimeAsync(25) + childLoader.resolve() + await Promise.resolve() + }) + + // The minimum is measured from the child's first visible frame, not from + // the navigation start or the ancestor's completion. + await act(async () => { + await vi.advanceTimersByTimeAsync(74) + }) + expect(screen.getByText('Child pending')).toBeInTheDocument() + expect(screen.queryByText('Child content')).not.toBeInTheDocument() + + await act(async () => { + await vi.advanceTimersByTimeAsync(1) + await navigation + }) + expect(screen.queryByText('Child pending')).not.toBeInTheDocument() + expect(screen.getByText('Child content')).toBeInTheDocument() +}) + +test('advancing from a parent loader to a parallel child loader does not restart pendingMs', async () => { + vi.useFakeTimers() + + const parentLoader = createControlledPromise() + const childLoader = createControlledPromise() + const rootRoute = createRootRoute({ component: Outlet }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
    Index
    , + }) + const parentRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + loader: () => parentLoader, + component: Outlet, + }) + const childRoute = createRoute({ + getParentRoute: () => parentRoute, + path: '/child', + pendingMs: 1_000, + pendingMinMs: 0, + pendingComponent: () =>
    Child pending
    , + loader: () => childLoader, + component: () =>
    Child content
    , + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([ + indexRoute, + parentRoute.addChildren([childRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + render() + + let navigation!: Promise + await act(async () => { + navigation = router.navigate({ to: '/parent/child' }) + await vi.advanceTimersByTimeAsync(900) + }) + expect(screen.queryByText('Child pending')).not.toBeInTheDocument() + + await act(async () => { + parentLoader.resolve() + await vi.advanceTimersByTimeAsync(99) + }) + expect(screen.queryByText('Child pending')).not.toBeInTheDocument() + + await act(async () => { + await vi.advanceTimersByTimeAsync(1) + }) + expect(screen.getByText('Child pending')).toBeInTheDocument() + + await act(async () => { + childLoader.resolve() + await navigation + }) + expect(screen.getByText('Child content')).toBeInTheDocument() +}) diff --git a/packages/react-router/tests/component-preload-retry-pending-min.test.tsx b/packages/react-router/tests/component-preload-retry-pending-min.test.tsx index 906e2334e5..dcdd94af67 100644 --- a/packages/react-router/tests/component-preload-retry-pending-min.test.tsx +++ b/packages/react-router/tests/component-preload-retry-pending-min.test.tsx @@ -1,5 +1,7 @@ -import { cleanup, render, screen } from '@testing-library/react' -import { afterEach, expect, test } from 'vitest' +import * as React from 'react' +import { act } from 'react' +import { afterEach, expect, test, vi } from 'vitest' +import { cleanup, fireEvent, render, screen } from '@testing-library/react' import { createControlledPromise } from '@tanstack/router-core' import { RouterProvider, @@ -7,9 +9,13 @@ import { createRootRoute, createRoute, createRouter, + useRouter, } from '../src' +import type { ErrorComponentProps } from '../src' afterEach(() => { + vi.useRealTimers() + vi.restoreAllMocks() cleanup() }) @@ -38,3 +44,107 @@ test('delayed component preload reveals pending UI', async () => { expect(await screen.findByText('Page content')).toBeInTheDocument() expect(screen.queryByText('Loading page...')).not.toBeInTheDocument() }) + +/** + * A component-only route can fail while preloading its code, then retry from + * its error UI through invalidate(). The retry is a fresh pending generation: + * its fallback must remain visible until the retried component preload is + * ready and pendingMinMs has elapsed. + */ +test('component preload retry remains pending through pendingMinMs', async () => { + vi.spyOn(console, 'error').mockImplementation(() => {}) + + const retryChunk = createControlledPromise() + let preloadAttempt = 0 + let retryInvalidation!: Promise + let retrySettled = false + + const Page = Object.assign( + () =>
    Page content
    , + { + preload: vi.fn(() => { + preloadAttempt++ + return preloadAttempt === 1 + ? Promise.reject(new Error('initial chunk request failed')) + : retryChunk + }), + }, + ) + + function RetryError({ reset }: ErrorComponentProps) { + const router = useRouter() + return ( + + ) + } + + const rootRoute = createRootRoute({}) + const pageRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/page', + component: Page, + errorComponent: RetryError, + pendingMs: 0, + pendingMinMs: 100, + pendingComponent: () => ( +
    Loading page...
    + ), + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + render() + + expect( + await screen.findByRole('button', { name: 'Retry chunk' }), + ).toBeInTheDocument() + expect(Page.preload).toHaveBeenCalledTimes(1) + + vi.useFakeTimers() + fireEvent.click(screen.getByRole('button', { name: 'Retry chunk' })) + + // Let pendingMs: 0 publish the retry lane. + await act(async () => { + await vi.advanceTimersByTimeAsync(0) + }) + expect(Page.preload).toHaveBeenCalledTimes(2) + expect(screen.getByTestId('page-pending')).toBeInTheDocument() + expect( + screen.queryByRole('button', { name: 'Retry chunk' }), + ).not.toBeInTheDocument() + + await act(async () => { + retryChunk.resolve() + await Promise.resolve() + }) + + expect.soft(retrySettled).toBe(false) + expect.soft(screen.queryByTestId('page-pending')).toBeInTheDocument() + expect.soft(screen.queryByTestId('page-content')).not.toBeInTheDocument() + + await act(async () => { + await vi.advanceTimersByTimeAsync(99) + }) + expect(screen.getByTestId('page-pending')).toBeInTheDocument() + + await act(async () => { + await vi.advanceTimersByTimeAsync(1) + await retryInvalidation + }) + + expect(screen.getByTestId('page-content')).toBeInTheDocument() + expect(screen.queryByTestId('page-pending')).not.toBeInTheDocument() +}) diff --git a/packages/react-router/tests/component-preload-retry.test.tsx b/packages/react-router/tests/component-preload-retry.test.tsx new file mode 100644 index 0000000000..ff33ec4bea --- /dev/null +++ b/packages/react-router/tests/component-preload-retry.test.tsx @@ -0,0 +1,128 @@ +import * as React from 'react' +import { afterEach, expect, test, vi } from 'vitest' +import { cleanup, fireEvent, render, screen } from '@testing-library/react' +import { createControlledPromise } from '@tanstack/router-core' +import { + RouterProvider, + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, + lazyRouteComponent, + useRouter, +} from '../src' +import type { ErrorComponentProps } from '../src' + +afterEach(() => { + cleanup() + vi.restoreAllMocks() + vi.unstubAllGlobals() + sessionStorage.clear() +}) + +test('a successful server component download is reused', async () => { + vi.stubGlobal('window', undefined) + const importer = vi.fn().mockResolvedValue({ default: () => null }) + const Page = lazyRouteComponent(importer) + + await Page.preload?.() + await Page.preload?.() + expect(importer).toHaveBeenCalledTimes(1) +}) + +test('concurrent component preloads share the import', async () => { + const componentImport = createControlledPromise<{ + default: () => null + }>() + const importer = vi.fn(() => componentImport) + const Page = lazyRouteComponent(importer) + + const first = Page.preload?.() + const second = Page.preload?.() + expect(importer).toHaveBeenCalledOnce() + + componentImport.resolve({ default: () => null }) + await Promise.all([first, second]) +}) + +test('a failed component download is retried from the route error UI', async () => { + vi.spyOn(console, 'error').mockImplementation(() => {}) + + const PageContent = () =>
    Page content
    + const importer = vi + .fn<() => Promise<{ default: typeof PageContent }>>() + .mockRejectedValueOnce(new Error('component download failed')) + .mockResolvedValue({ default: PageContent }) + const Page = lazyRouteComponent(importer) + + function RouteError({ reset }: ErrorComponentProps) { + const router = useRouter() + return ( + + ) + } + + const rootRoute = createRootRoute() + const pageRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/page', + component: Page, + errorComponent: RouteError, + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + render() + + const retryButton = await screen.findByRole('button', { name: 'Retry' }) + expect(importer).toHaveBeenCalledTimes(1) + + fireEvent.click(retryButton) + + expect(await screen.findByText('Page content')).toBeInTheDocument() + expect(importer).toHaveBeenCalledTimes(2) + expect( + screen.queryByRole('button', { name: 'Retry' }), + ).not.toBeInTheDocument() +}) + +test('renders after retrying a module download that failed during preload', async () => { + const PageContent = () =>
    Page content
    + const importer = vi + .fn<() => Promise<{ default: typeof PageContent }>>() + .mockRejectedValueOnce( + new TypeError( + 'Failed to fetch dynamically imported module: /assets/page.js', + ), + ) + .mockResolvedValue({ default: PageContent }) + const Page = lazyRouteComponent(importer) + + await Page.preload?.() + + const rootRoute = createRootRoute() + const pageRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/page', + component: Page, + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + render() + + expect(await screen.findByText('Page content')).toBeInTheDocument() + expect(importer).toHaveBeenCalledTimes(2) +}) diff --git a/packages/react-router/tests/errorComponent.test.tsx b/packages/react-router/tests/errorComponent.test.tsx index 28757a0b98..b5ee4b6709 100644 --- a/packages/react-router/tests/errorComponent.test.tsx +++ b/packages/react-router/tests/errorComponent.test.tsx @@ -422,6 +422,46 @@ test('errorComponent receives primitive errors thrown from beforeLoad', async () expect(screen.queryByText('About route content')).not.toBeInTheDocument() }) +test.each(['beforeLoad', 'loader'] as const)( + 'a Promise synchronously thrown from %s renders the route error UI', + async (hook) => { + const thrown = Promise.resolve('not route data') + const rootRoute = createRootRoute() + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + beforeLoad: + hook === 'beforeLoad' + ? () => { + throw thrown + } + : undefined, + loader: + hook === 'loader' + ? () => { + throw thrown + } + : undefined, + errorComponent: ({ error }) => ( +
    + {error instanceof Error && error.cause === thrown + ? 'Promise route error' + : 'Wrong route error'} +
    + ), + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + render() + + expect(await screen.findByText('Promise route error')).toBeInTheDocument() + expect(screen.queryByText('Wrong route error')).not.toBeInTheDocument() + }, +) + test('SSR errorComponent receives primitive errors thrown from beforeLoad', async () => { const rootRoute = createRootRoute({ component: function Root() { diff --git a/packages/react-router/tests/hydration-capped-boundary-pending.test.tsx b/packages/react-router/tests/hydration-capped-boundary-pending.test.tsx new file mode 100644 index 0000000000..57d99c8af5 --- /dev/null +++ b/packages/react-router/tests/hydration-capped-boundary-pending.test.tsx @@ -0,0 +1,264 @@ +import * as React from 'react' +import { act } from '@testing-library/react' +import { hydrateRoot } from 'react-dom/client' +import { renderToString } from 'react-dom/server' +import { afterEach, describe, expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { dehydrateSsrMatchId } from '../../router-core/src/ssr/ssr-match-id' +import { hydrate } from '../src/ssr/client' +import { + Outlet, + RouterProvider, + createRootRoute, + createRoute, + createRouter, + notFound, +} from '../src' +import type { TsrSsrGlobal } from '../src/ssr/client' + +declare global { + interface Window { + $_TSR?: TsrSsrGlobal + } +} + +const testCleanups: Array<() => void | Promise> = [] + +afterEach(async () => { + while (testCleanups.length) { + await testCleanups.pop()!() + } + vi.restoreAllMocks() + window.$_TSR = undefined + document.body.innerHTML = '' +}) + +describe('hydrating a server-capped boundary lane', () => { + test('recovers a /404 payload against a missing browser URL', async () => { + function MissingPage() { + return
    Missing page
    + } + + const makeRouteTree = () => { + const rootRoute = createRootRoute({ + component: Outlet, + notFoundComponent: MissingPage, + }) + const notFoundRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/404', + component: MissingPage, + }) + return rootRoute.addChildren([notFoundRoute]) + } + + const serverRouter = createRouter({ + routeTree: makeRouteTree(), + history: createMemoryHistory({ initialEntries: ['/404'] }), + }) + serverRouter.isServer = true + await serverRouter.load() + const serverMatches = serverRouter.stores.matches.get() + const serverHtml = renderToString() + expect(serverHtml).toContain('Missing page') + + const clientRouter = createRouter({ + routeTree: makeRouteTree(), + history: createMemoryHistory({ initialEntries: ['/missing'] }), + }) + window.$_TSR = { + router: { + manifest: { routes: {} }, + dehydratedData: {}, + matches: serverMatches.map((match) => ({ + i: dehydrateSsrMatchId(match.id), + u: match.updatedAt, + s: match.status, + l: match.loaderData, + e: match.error, + ssr: match.ssr, + ...(match._notFound ? { g: true } : {}), + })), + }, + h: vi.fn(), + e: vi.fn(), + c: vi.fn(), + p: vi.fn(), + buffer: [], + initialized: false, + } + + await hydrate(clientRouter) + + const container = document.createElement('div') + container.innerHTML = serverHtml + document.body.appendChild(container) + let root!: ReturnType + await act(async () => { + root = hydrateRoot(container, , { + onRecoverableError: () => {}, + }) + testCleanups.push(async () => { + await act(() => root.unmount()) + }) + await Promise.resolve() + }) + + expect(container).toHaveTextContent('Missing page') + expect(clientRouter.state.resolvedLocation?.pathname).toBe('/missing') + expect(clientRouter.state.matches).toHaveLength(1) + expect(clientRouter.state.matches[0]).toMatchObject({ _notFound: true }) + }) + + test.each([ + ['error', 'parent'], + ['notFound', 'parent'], + ['error', 'root'], + ['notFound', 'root'], + ] as const)( + 'keeps the server-rendered %s %s boundary visible', + async (outcome, boundary) => { + const childLoader = vi.fn(() => 'child data') + const boundaryCommits = vi.fn() + + function BoundaryError() { + React.useEffect(() => { + boundaryCommits() + }, []) + return
    Boundary error
    + } + + function BoundaryNotFound() { + React.useEffect(() => { + boundaryCommits() + }, []) + return
    Boundary not found
    + } + + const makeRouteTree = () => { + const boundaryOptions = { + beforeLoad: () => { + throw outcome === 'notFound' + ? notFound() + : new Error('server route failure') + }, + pendingComponent: () => ( +
    Boundary pending
    + ), + errorComponent: BoundaryError, + notFoundComponent: BoundaryNotFound, + } + const rootRoute = createRootRoute({ + component: Outlet, + ...(boundary === 'root' ? boundaryOptions : {}), + }) + const parentRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + component: Outlet, + ...(boundary === 'parent' ? boundaryOptions : {}), + }) + const childRoute = createRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loader: childLoader, + component: () =>
    Child
    , + }) + return { + routeTree: rootRoute.addChildren([ + parentRoute.addChildren([childRoute]), + ]), + } + } + + const serverRouter = createRouter({ + ...makeRouteTree(), + history: createMemoryHistory({ + initialEntries: ['/parent/child'], + }), + }) + serverRouter.isServer = true + await serverRouter.load() + + const serverMatches = serverRouter.stores.matches.get() + expect(serverMatches).toHaveLength(3) + const serverBoundary = serverMatches[boundary === 'root' ? 0 : 1]! + if (outcome === 'notFound' && boundary === 'root') { + expect(serverBoundary).toMatchObject({ + status: 'success', + _notFound: true, + }) + } else { + expect(serverBoundary.status).toBe(outcome) + } + const serverHtml = renderToString( + , + ) + expect(serverHtml).toContain( + outcome === 'notFound' ? 'Boundary not found' : 'Boundary error', + ) + expect(boundaryCommits).not.toHaveBeenCalled() + + const clientRouter = createRouter({ + ...makeRouteTree(), + history: createMemoryHistory({ + initialEntries: ['/parent/child'], + }), + }) + + window.$_TSR = { + router: { + manifest: { routes: {} }, + dehydratedData: {}, + matches: serverMatches + .slice(0, boundary === 'root' ? 1 : 2) + .map((match) => ({ + i: dehydrateSsrMatchId(match.id), + u: match.updatedAt, + s: match.status, + l: match.loaderData, + e: match.error, + ssr: match.ssr, + ...(match._notFound ? { g: true } : {}), + })), + }, + h: vi.fn(), + e: vi.fn(), + c: vi.fn(), + p: vi.fn(), + buffer: [], + initialized: false, + } + + await hydrate(clientRouter) + + const container = document.createElement('div') + container.innerHTML = serverHtml + document.body.appendChild(container) + const consoleError = vi + .spyOn(console, 'error') + .mockImplementation(() => {}) + let root!: ReturnType + await act(async () => { + root = hydrateRoot(container, ) + testCleanups.push(async () => { + await act(() => root.unmount()) + }) + await Promise.resolve() + }) + + // A shorter dehydrated lane means SPA mode only for an actual shell. + // Here it is shorter because the server already rendered a terminal + // boundary, so hydration must not replace that boundary with pending UI. + expect(boundaryCommits).toHaveBeenCalledTimes(1) + expect(container).toHaveTextContent( + outcome === 'notFound' ? 'Boundary not found' : 'Boundary error', + ) + expect(container).not.toHaveTextContent('Boundary pending') + expect(consoleError.mock.calls.flat().join(' ')).not.toMatch( + /hydration|did not match/i, + ) + expect(childLoader).not.toHaveBeenCalled() + }, + ) +}) diff --git a/packages/react-router/tests/issue-4467-lazy-route-pending.test.tsx b/packages/react-router/tests/issue-4467-lazy-route-pending.test.tsx new file mode 100644 index 0000000000..ef85b2f235 --- /dev/null +++ b/packages/react-router/tests/issue-4467-lazy-route-pending.test.tsx @@ -0,0 +1,144 @@ +import * as React from 'react' +import { act, cleanup, render, screen } from '@testing-library/react' +import { afterEach, expect, onTestFinished, test, vi } from 'vitest' + +import { + Outlet, + RouterProvider, + createControlledPromise, + createLazyRoute, + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, +} from '../src' + +afterEach(cleanup) + +// https://github.com/TanStack/router/issues/4467 +test('default pending component renders while lazy route options load', async () => { + const rootRoute = createRootRoute({ + component: Outlet, + }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>

    Index page

    , + }) + const lazyPageOptions = createLazyRoute('/page')({ + component: () =>

    Page

    , + }) + const lazyOptions = createControlledPromise() + const loadLazyOptions = vi.fn(() => lazyOptions) + const pageRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/page', + }).lazy(loadLazyOptions) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute, pageRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + defaultPendingMs: 0, + defaultPendingMinMs: 0, + defaultPendingComponent: () =>

    Loading page

    , + }) + let navigationPromise: Promise | undefined + + onTestFinished(async () => { + await act(async () => { + if (lazyOptions.status === 'pending') { + lazyOptions.resolve(lazyPageOptions) + } + await navigationPromise + }) + }) + + render() + + expect( + await screen.findByRole('heading', { name: 'Index page' }), + ).toBeInTheDocument() + + act(() => { + navigationPromise = router.navigate({ to: '/page' }) + }) + + expect(await screen.findByRole('status')).toHaveTextContent('Loading page') + expect( + screen.queryByRole('heading', { name: 'Page' }), + ).not.toBeInTheDocument() + expect(lazyOptions.status).toBe('pending') + expect(loadLazyOptions).toHaveBeenCalledTimes(1) + + await act(async () => { + lazyOptions.resolve(lazyPageOptions) + await navigationPromise + }) + + expect(screen.getByRole('heading', { name: 'Page' })).toBeInTheDocument() + expect(screen.queryByRole('status')).not.toBeInTheDocument() + expect(loadLazyOptions).toHaveBeenCalledTimes(1) +}) + +test('a lazy pending component is offered while the eager loader is still pending', async () => { + const rootRoute = createRootRoute({ component: Outlet }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>

    Index page

    , + }) + const loader = createControlledPromise() + const lazyPageOptions = createLazyRoute('/page')({ + pendingComponent: () =>

    Loading lazy page

    , + component: () =>

    Page

    , + }) + const lazyOptions = createControlledPromise() + const pageRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/page', + loader: () => loader, + }).lazy(() => lazyOptions) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute, pageRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + defaultPendingMs: 0, + defaultPendingMinMs: 0, + defaultPendingComponent: () =>

    Loading default

    , + }) + let navigation: Promise | undefined + + onTestFinished(async () => { + await act(async () => { + lazyOptions.resolve(lazyPageOptions) + loader.resolve() + await navigation + }) + }) + + render() + expect( + await screen.findByRole('heading', { name: 'Index page' }), + ).toBeInTheDocument() + + act(() => { + navigation = router.navigate({ to: '/page' }) + }) + expect(await screen.findByRole('status')).toHaveTextContent('Loading default') + + await act(async () => { + lazyOptions.resolve(lazyPageOptions) + }) + + expect(await screen.findByRole('status')).toHaveTextContent( + 'Loading lazy page', + ) + expect( + screen.queryByRole('heading', { name: 'Page' }), + ).not.toBeInTheDocument() + + await act(async () => { + loader.resolve() + await navigation + }) + + expect(screen.getByRole('heading', { name: 'Page' })).toBeInTheDocument() +}) diff --git a/packages/react-router/tests/issue-4476-react-query-cancellation.test.tsx b/packages/react-router/tests/issue-4476-react-query-cancellation.test.tsx new file mode 100644 index 0000000000..039608ffb2 --- /dev/null +++ b/packages/react-router/tests/issue-4476-react-query-cancellation.test.tsx @@ -0,0 +1,129 @@ +import { + cleanup, + fireEvent, + render, + screen, + waitFor, +} from '@testing-library/react' +import { + QueryClient, + QueryClientProvider, + useQuery, +} from '@tanstack/react-query' +import { afterEach, expect, onTestFinished, test, vi } from 'vitest' +import { + Link, + Outlet, + RouterProvider, + createControlledPromise, + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, +} from '../src' + +afterEach(cleanup) + +// https://github.com/TanStack/router/issues/4476 +test('#4476: pending navigation keeps the query observer mounted and its fetchQuery signal alive', async () => { + const queryClient = new QueryClient({ + defaultOptions: { queries: { retry: false, staleTime: 0 } }, + }) + const queryGate = createControlledPromise() + const queryKey = ['issue-4476'] as const + const routeError = vi.fn() + const pageTwoBeforeLoad = vi.fn() + const pendingComponentRendered = vi.fn() + let querySignal: AbortSignal | undefined + + const rootRoute = createRootRoute({ + component: () => ( + <> + + Page two + + + + ), + }) + function PageOneComponent() { + const query = useQuery({ + queryKey, + queryFn: () => Promise.resolve(3), + }) + return
    Page one: {query.data}
    + } + const pageOneRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: PageOneComponent, + }) + const pageTwoRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/page-two', + beforeLoad: async ({ preload }) => { + pageTwoBeforeLoad(preload) + if (preload) { + return + } + const data = await queryClient.fetchQuery({ + queryKey, + queryFn: ({ signal }) => { + querySignal = signal + return queryGate + }, + }) + return { data } + }, + errorComponent: ({ error }) => { + routeError(error) + return
    {error.name}
    + }, + component: () => { + const { data } = pageTwoRoute.useRouteContext() + return
    Page two: {data}
    + }, + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([pageOneRoute, pageTwoRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + defaultPreload: 'intent', + defaultPreloadDelay: 0, + defaultPreloadStaleTime: 0, + defaultPendingMs: 0, + defaultPendingComponent: () => { + pendingComponentRendered() + return
    Loading page two
    + }, + }) + + onTestFinished(() => { + queryGate.resolve(10) + queryClient.clear() + }) + + render( + + + , + ) + expect(await screen.findByText('Page one: 3')).toBeInTheDocument() + + const link = screen.getByRole('link', { name: 'Page two' }) + fireEvent.mouseOver(link) + await waitFor(() => expect(pageTwoBeforeLoad).toHaveBeenCalledWith(true)) + fireEvent.click(link) + + await waitFor(() => expect(querySignal).toBeDefined()) + expect(screen.getByTestId('page-one')).toBeInTheDocument() + expect(screen.getByTestId('page-two-pending')).toBeInTheDocument() + expect(querySignal?.aborted).toBe(false) + queryGate.resolve(10) + + expect(await screen.findByText('Page two: 10')).toBeInTheDocument() + expect(routeError).not.toHaveBeenCalled() + expect(screen.queryByTestId('page-one')).not.toBeInTheDocument() + expect(screen.queryByTestId('page-two-pending')).not.toBeInTheDocument() + expect(screen.queryByTestId('page-two-error')).not.toBeInTheDocument() + expect(router.state.location.pathname).toBe('/page-two') +}) diff --git a/packages/react-router/tests/issue-4759-pending-frame.test.tsx b/packages/react-router/tests/issue-4759-pending-frame.test.tsx new file mode 100644 index 0000000000..7b8b74161c --- /dev/null +++ b/packages/react-router/tests/issue-4759-pending-frame.test.tsx @@ -0,0 +1,100 @@ +import * as React from 'react' +import { + afterEach, + beforeEach, + describe, + expect, + onTestFinished, + test, + vi, +} from 'vitest' +import { act, cleanup, render, screen } from '@testing-library/react' +import { + RouterProvider, + createBrowserHistory, + createRootRoute, + createRoute, + createRouter, +} from '../src' +import type { RouterHistory } from '../src' + +let history: RouterHistory + +beforeEach(() => { + history = createBrowserHistory() + expect(window.location.pathname).toBe('/') +}) + +afterEach(() => { + history.destroy() + window.history.replaceState(null, 'root', '/') + vi.resetAllMocks() + cleanup() +}) + +// Repro for https://github.com/TanStack/router/issues/4759 +// +// JSDOM cannot observe browser paints. This unit reduction verifies the event +// ordering behind the issue: pending DOM must be published before the first +// macrotask when pendingMs is 0. +describe('issue #4759: pendingMs 0 publishes pending DOM before a macrotask', () => { + test('pending fallback is committed on mount without waiting for a macrotask', async () => { + vi.useFakeTimers() + let resolveLoader!: (value: string) => void + const loaderPromise = new Promise((resolve) => { + resolveLoader = resolve + }) + + const rootRoute = createRootRoute() + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + loader: () => loaderPromise, + component: () =>
    loaded
    , + }) + + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute]), + history, + }) + let resolveRendered!: () => void + const rendered = new Promise((resolve) => { + resolveRendered = resolve + }) + const unsubscribe = router.subscribe('onRendered', (event) => { + if (event.toLocation.pathname === '/') { + resolveRendered() + } + }) + + onTestFinished(() => { + unsubscribe() + resolveLoader('done') + vi.useRealTimers() + }) + + render( +
    + ( +
    pending...
    + )} + /> +
    , + ) + + // Fake timers keep the first macrotask frozen. An implementation that + // publishes pending state with setTimeout cannot satisfy this assertion. + await act(async () => {}) + expect(screen.getByTestId('pending')).toBeInTheDocument() + + // Sanity: the load still completes normally afterwards. + resolveLoader('done') + await act(() => rendered) + expect(screen.getByTestId('loaded')).toBeInTheDocument() + expect(screen.queryByTestId('pending')).not.toBeInTheDocument() + }) +}) diff --git a/packages/react-router/tests/issue-6107-lazy-chunk-error-component.test.tsx b/packages/react-router/tests/issue-6107-lazy-chunk-error-component.test.tsx new file mode 100644 index 0000000000..22440fe119 --- /dev/null +++ b/packages/react-router/tests/issue-6107-lazy-chunk-error-component.test.tsx @@ -0,0 +1,89 @@ +import * as React from 'react' +import { + cleanup, + fireEvent, + render, + screen, + waitFor, +} from '@testing-library/react' +import { afterEach, expect, test, vi } from 'vitest' +import { + Link, + Outlet, + RouterProvider, + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, +} from '../src' + +afterEach(() => { + vi.restoreAllMocks() + cleanup() +}) + +// https://github.com/TanStack/router/issues/6107 +test('#6107: lazy chunk hover failure is non-fatal and navigation renders defaultErrorComponent', async () => { + vi.spyOn(console, 'error').mockImplementation(() => {}) + const chunkError = new TypeError( + 'Failed to fetch dynamically imported module: /assets/posts.lazy.js', + ) + const defaultErrorRendered = vi.fn() + let lazyCalls = 0 + + const rootRoute = createRootRoute({ + component: () => ( + <> + + Posts + + + + ), + }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
    Index
    , + }) + const postsRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/posts', + }).lazy(() => { + lazyCalls++ + return Promise.reject(chunkError) + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute, postsRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + defaultPreloadDelay: 0, + defaultErrorComponent: ({ error }) => { + defaultErrorRendered(error) + return
    {error.message}
    + }, + }) + const preloadRoute = vi.spyOn(router, 'preloadRoute') + + render() + expect(await screen.findByText('Index')).toBeInTheDocument() + + const link = screen.getByRole('link', { name: 'Posts' }) + fireEvent.mouseEnter(link) + await waitFor(() => expect(preloadRoute).toHaveBeenCalledTimes(1)) + await preloadRoute.mock.results[0]!.value + expect(lazyCalls).toBeGreaterThanOrEqual(1) + expect(screen.getByText('Index')).toBeInTheDocument() + expect(screen.queryByTestId('default-error')).not.toBeInTheDocument() + expect(defaultErrorRendered).not.toHaveBeenCalled() + + const callsAfterPreload = lazyCalls + fireEvent.click(link) + expect(await screen.findByTestId('default-error')).toHaveTextContent( + chunkError.message, + ) + expect(lazyCalls).toBeGreaterThan(callsAfterPreload) + expect(defaultErrorRendered).toHaveBeenCalledWith(chunkError) + expect(screen.queryByText('Index')).not.toBeInTheDocument() + expect(router.state.location.pathname).toBe('/posts') + expect(router.state.status).toBe('idle') +}) diff --git a/packages/react-router/tests/issue-6371-search-default-normalization-abort.test.tsx b/packages/react-router/tests/issue-6371-search-default-normalization-abort.test.tsx new file mode 100644 index 0000000000..7698d117a6 --- /dev/null +++ b/packages/react-router/tests/issue-6371-search-default-normalization-abort.test.tsx @@ -0,0 +1,169 @@ +import { afterEach, expect, onTestFinished, test, vi } from 'vitest' +import { act, cleanup, render, screen } from '@testing-library/react' +import { + Outlet, + RouterProvider, + createControlledPromise, + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, + useLocation, +} from '../src' + +afterEach(() => { + cleanup() +}) + +test('#6371: initial search defaults produce one live canonical loader', async () => { + const loaderGate = createControlledPromise() + const canonicalLocation = createControlledPromise() + const abortedLoaderData = 'discarded aborted loader' + const loaderSignals: Array = [] + const loaderLocations: Array = [] + const errorComponentRendered = vi.fn() + const loader = vi.fn( + ({ + abortController, + location, + }: { + abortController: AbortController + location: { href: string } + }) => { + const signal = abortController.signal + loaderSignals.push(signal) + loaderLocations.push(location.href) + + return new Promise((resolve, reject) => { + const onAbort = () => { + resolve(abortedLoaderData) + } + + if (signal.aborted) { + onAbort() + return + } + + signal.addEventListener('abort', onAbort, { once: true }) + loaderGate.then((data) => { + signal.removeEventListener('abort', onAbort) + resolve(data) + }, reject) + }) + }, + ) + + const PendingLocation = () => { + const href = useLocation({ select: (location) => location.href }) + return
    {href}
    + } + + const rootRoute = createRootRoute({ + component: () => , + }) + const aboutRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/about', + validateSearch: (search: Record) => ({ + page: typeof search.page === 'number' ? search.page : 1, + }), + loader, + component: () => ( +
    {aboutRoute.useLoaderData()}
    + ), + errorComponent: ({ error }) => { + errorComponentRendered(error) + return
    {error.message}
    + }, + }) + const history = createMemoryHistory({ initialEntries: ['/about'] }) + const unsubscribeHistory = history.subscribe(() => { + if (history.location.href === '/about?page=1') { + canonicalLocation.resolve() + } + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([aboutRoute]), + history, + defaultPendingMs: 0, + defaultPendingMinMs: 0, + defaultPendingComponent: PendingLocation, + }) + + onTestFinished(async () => { + unsubscribeHistory() + await act(() => { + canonicalLocation.resolve() + loaderGate.resolve('about data') + }) + }) + + render() + + await act(async () => { + await canonicalLocation + }) + + expect(loader).toHaveBeenCalledTimes(1) + expect(loaderLocations).toEqual(['/about?page=1']) + expect(loaderSignals).toHaveLength(1) + expect(loaderSignals[0]?.aborted).toBe(false) + + expect( + await screen.findByText('/about?page=1', { + selector: '[data-testid="pending-location"]', + }), + ).toBeInTheDocument() + + await act(() => { + loaderGate.resolve('about data') + }) + + expect(await screen.findByTestId('about-data')).toHaveTextContent( + 'about data', + ) + expect(loader).toHaveBeenCalledTimes(1) + expect(loaderSignals[0]?.aborted).toBe(false) + expect(errorComponentRendered).not.toHaveBeenCalled() + expect(screen.queryByTestId('about-error')).not.toBeInTheDocument() +}) + +test('initial canonicalization bypasses existing navigation blockers', async () => { + const loader = vi.fn( + ({ location }: { location: { href: string } }) => location.href, + ) + const rootRoute = createRootRoute({ + component: () => , + }) + const aboutRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/about', + validateSearch: (search: Record) => ({ + page: typeof search.page === 'number' ? search.page : 1, + }), + loader, + component: () =>
    {aboutRoute.useLoaderData()}
    , + }) + const history = createMemoryHistory({ initialEntries: ['/about'] }) + const blockerFn = vi.fn(() => true) + const unblock = history.block({ + blockerFn, + enableBeforeUnload: false, + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([aboutRoute]), + history, + }) + + onTestFinished(unblock) + + render() + + expect(await screen.findByText('/about?page=1')).toBeInTheDocument() + expect(loader).toHaveBeenCalledTimes(1) + expect(history.location.href).toBe('/about?page=1') + expect(router.latestLocation.href).toBe('/about?page=1') + expect(router.state.location.href).toBe('/about?page=1') + expect(router.state.resolvedLocation?.href).toBe('/about?page=1') + expect(blockerFn).not.toHaveBeenCalled() +}) diff --git a/packages/react-router/tests/issue-7051-force-pending-suspense.test.tsx b/packages/react-router/tests/issue-7051-force-pending-suspense.test.tsx new file mode 100644 index 0000000000..5640cb7477 --- /dev/null +++ b/packages/react-router/tests/issue-7051-force-pending-suspense.test.tsx @@ -0,0 +1,195 @@ +import { act } from 'react' +import { afterEach, expect, test, vi } from 'vitest' +import { cleanup, render, screen } from '@testing-library/react' +import { + Outlet, + RouterProvider, + createControlledPromise, + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, +} from '../src' + +afterEach(() => { + vi.clearAllMocks() + cleanup() +}) + +// Ported from PR #7051. A forced-pending reload must keep showing its pending +// fallback until fresh content commits instead of exposing the error boundary. +test('invalidate({ forcePending: true }) keeps rendering the pending fallback instead of the error boundary', async () => { + const history = createMemoryHistory({ + initialEntries: ['/force-pending'], + }) + const errorComponentRendered = vi.fn() + let shouldSuspendReload = false + const reloadGate = createControlledPromise() + + const rootRoute = createRootRoute({ + component: () => , + }) + + const forcePendingRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/force-pending', + pendingMs: 0, + pendingMinMs: 10, + loader: async () => { + if (shouldSuspendReload) { + await reloadGate + } + + return 'done' + }, + component: () => ( +
    + {forcePendingRoute.useLoaderData()} +
    + ), + pendingComponent: () => ( +
    Pending...
    + ), + errorComponent: ({ error }) => { + errorComponentRendered(error) + return
    {String(error)}
    + }, + }) + + const router = createRouter({ + routeTree: rootRoute.addChildren([forcePendingRoute]), + history, + }) + + render() + + await act(() => router.load()) + expect(await screen.findByTestId('force-pending-route')).toHaveTextContent( + 'done', + ) + + shouldSuspendReload = true + let invalidation!: Promise + act(() => { + invalidation = router.invalidate({ forcePending: true }) + }) + + expect( + await screen.findByTestId('force-pending-fallback'), + ).toBeInTheDocument() + expect(errorComponentRendered).not.toHaveBeenCalled() + expect(screen.queryByTestId('force-pending-error')).not.toBeInTheDocument() + + act(() => { + reloadGate.resolve() + }) + + await act(() => invalidation) + expect(await screen.findByTestId('force-pending-route')).toHaveTextContent( + 'done', + ) + expect(screen.queryByTestId('force-pending-fallback')).not.toBeInTheDocument() + expect(screen.queryByTestId('force-pending-error')).not.toBeInTheDocument() + expect(errorComponentRendered).not.toHaveBeenCalled() + expect(router.state.location.pathname).toBe('/force-pending') + expect(router.state.status).toBe('idle') +}) + +test('regular navigation keeps the current pending fallback while its loader is aborted', async () => { + const firstLoaderAborted = createControlledPromise() + const secondLoaderStarted = createControlledPromise() + const secondLoaderGate = createControlledPromise() + const firstErrorComponentRendered = vi.fn() + let firstSignal: AbortSignal | undefined + + const rootRoute = createRootRoute({ + component: () => , + }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
    Home page
    , + }) + const firstRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/first', + pendingMs: 0, + loader: async ({ abortController }) => { + firstSignal = abortController.signal + await new Promise((_resolve, reject) => { + abortController.signal.addEventListener( + 'abort', + () => { + firstLoaderAborted.resolve() + reject(new DOMException('Aborted', 'AbortError')) + }, + { once: true }, + ) + }) + return 'first' + }, + component: () => ( +
    {firstRoute.useLoaderData()}
    + ), + pendingComponent: () => ( +
    Pending first route
    + ), + errorComponent: ({ error }) => { + firstErrorComponentRendered(error) + return
    {String(error)}
    + }, + }) + const secondRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/second', + loader: async () => { + secondLoaderStarted.resolve() + await secondLoaderGate + return 'second' + }, + component: () => ( +
    {secondRoute.useLoaderData()}
    + ), + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute, firstRoute, secondRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + defaultPreload: false, + }) + + render() + expect(await screen.findByTestId('home-page')).toBeInTheDocument() + + act(() => { + void router.navigate({ to: '/first' }) + }) + expect(await screen.findByTestId('first-pending')).toBeInTheDocument() + expect(firstSignal?.aborted).toBe(false) + + let secondNavigation!: Promise + act(() => { + secondNavigation = router.navigate({ to: '/second' }) + }) + await act(async () => { + await Promise.all([firstLoaderAborted, secondLoaderStarted]) + }) + + expect(firstSignal?.aborted).toBe(true) + expect(screen.getByTestId('first-pending')).toBeInTheDocument() + expect(screen.queryByTestId('first-error')).not.toBeInTheDocument() + expect(firstErrorComponentRendered).not.toHaveBeenCalled() + expect(router.state.location.pathname).toBe('/second') + expect(router.state.status).toBe('pending') + + act(() => { + secondLoaderGate.resolve() + }) + await act(() => secondNavigation) + + expect(await screen.findByTestId('second-page')).toHaveTextContent('second') + expect(screen.queryByTestId('first-pending')).not.toBeInTheDocument() + expect(screen.queryByTestId('first-error')).not.toBeInTheDocument() + expect(firstErrorComponentRendered).not.toHaveBeenCalled() + expect(router.state.location.pathname).toBe('/second') + expect(router.state.status).toBe('idle') +}) diff --git a/packages/react-router/tests/issue-7635-error-head-after-navigation.test.tsx b/packages/react-router/tests/issue-7635-error-head-after-navigation.test.tsx new file mode 100644 index 0000000000..56aed7b375 --- /dev/null +++ b/packages/react-router/tests/issue-7635-error-head-after-navigation.test.tsx @@ -0,0 +1,100 @@ +import { createPortal } from 'react-dom' +import { + cleanup, + fireEvent, + render, + screen, + waitFor, +} from '@testing-library/react' +import { afterEach, expect, test, vi } from 'vitest' +import { + HeadContent, + Link, + Outlet, + RouterProvider, + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, +} from '../src' + +afterEach(() => { + cleanup() + document.head.innerHTML = '' +}) + +// https://github.com/TanStack/router/issues/7635 +test('#7635: a parent beforeLoad error replaces the previous child title', async () => { + const appError = new Error('App beforeLoad failed') + const appErrorRendered = vi.fn() + const childHead = vi.fn(() => ({ + meta: [{ title: 'Child success title' }], + })) + + const rootRoute = createRootRoute({ + component: () => ( + <> + {createPortal(, document.head)} + + Fail app load + + + + ), + }) + const appRoute = createRoute({ + getParentRoute: () => rootRoute, + id: '_app', + validateSearch: (search: Record) => ({ + fail: search.fail === true || search.fail === 'true', + }), + beforeLoad: ({ search }) => { + if (search.fail) { + throw appError + } + }, + head: ({ match }) => ({ + meta: [ + { + title: match.error ? 'App error title' : 'App success title', + }, + ], + }), + component: Outlet, + errorComponent: ({ error }) => { + appErrorRendered(error) + return
    {error.message}
    + }, + }) + const childRoute = createRoute({ + getParentRoute: () => appRoute, + path: '/child', + head: childHead, + component: () =>
    Child content
    , + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([appRoute.addChildren([childRoute])]), + history: createMemoryHistory({ + initialEntries: ['/child?fail=false'], + }), + }) + + render() + + expect(await screen.findByTestId('child-content')).toBeInTheDocument() + await waitFor(() => expect(document.title).toBe('Child success title')) + expect(childHead).toHaveBeenCalled() + childHead.mockClear() + + fireEvent.click(screen.getByRole('link', { name: 'Fail app load' })) + + expect(await screen.findByTestId('app-error')).toHaveTextContent( + appError.message, + ) + expect(appErrorRendered).toHaveBeenCalledWith(appError) + expect(screen.queryByTestId('child-content')).not.toBeInTheDocument() + await waitFor(() => expect(document.title).toBe('App error title')) + expect(childHead).not.toHaveBeenCalled() + expect(router.state.location.href).toBe('/child?fail=true') + expect(router.state.status).toBe('idle') +}) diff --git a/packages/react-router/tests/loaders.test.tsx b/packages/react-router/tests/loaders.test.tsx index 3b5790ef41..6859b963e2 100644 --- a/packages/react-router/tests/loaders.test.tsx +++ b/packages/react-router/tests/loaders.test.tsx @@ -729,20 +729,32 @@ test('does not show pending UI when loaders finish before their pending delays', expect(fooPendingComponentOnMountMock).not.toHaveBeenCalled() }) -test('throw abortError from loader upon initial load with basepath', async () => { - window.history.replaceState(null, 'root', '/app') +// https://github.com/TanStack/router/pull/7673 +test('#7673: a spontaneous loader AbortError renders the boundary without executing the route component', async () => { + history.replace('/app') + history.flush() const rootRoute = createRootRoute({}) + const abortError = new DOMException('Aborted', 'AbortError') + const routeComponentRendered = vi.fn() + const renderedError = vi.fn() + let routeSignal: AbortSignal | undefined const indexRoute = createRoute({ getParentRoute: () => rootRoute, path: '/', - loader: async () => { - return Promise.reject(new DOMException('Aborted', 'AbortError')) + loader: async ({ abortController }): Promise<{ value: string }> => { + routeSignal = abortController.signal + return Promise.reject(abortError) + }, + component: () => { + routeComponentRendered() + const data = indexRoute.useLoaderData() + return
    {data.value}
    + }, + errorComponent: ({ error }) => { + renderedError(error) + return
    indexErrorComponent
    }, - component: () =>
    Index route content
    , - errorComponent: () => ( -
    indexErrorComponent
    - ), }) const routeTree = rootRoute.addChildren([indexRoute]) @@ -750,10 +762,18 @@ test('throw abortError from loader upon initial load with basepath', async () => render() - const indexElement = await screen.findByText('Index route content') - expect(indexElement).toBeInTheDocument() - expect(screen.queryByTestId('index-error')).not.toBeInTheDocument() - expect(window.location.pathname.startsWith('/app')).toBe(true) + expect(await screen.findByTestId('index-error')).toBeInTheDocument() + expect(screen.queryByTestId('index-content')).not.toBeInTheDocument() + expect(routeComponentRendered).not.toHaveBeenCalled() + expect(renderedError).toHaveBeenCalledWith(abortError) + expect(routeSignal?.aborted).toBe(false) + expect( + router.state.matches.find((match) => match.routeId === indexRoute.id), + ).toMatchObject({ + status: 'error', + error: abortError, + }) + expect(window.location.pathname).toBe('/app') }) test('navigating away from a pending route aborts its loader', async () => { diff --git a/packages/react-router/tests/not-found.test.tsx b/packages/react-router/tests/not-found.test.tsx index bc6a1b9df1..f27057c7ab 100644 --- a/packages/react-router/tests/not-found.test.tsx +++ b/packages/react-router/tests/not-found.test.tsx @@ -7,12 +7,18 @@ import { RouterProvider, createBrowserHistory, createControlledPromise, + createLazyRoute, createRootRoute, createRoute, createRouter, notFound, rootRouteId, } from '../src' +import { + RouterServer, + createRequestHandler, + renderRouterToString, +} from '../src/ssr/server' import type { NotFoundRouteProps, RouterHistory } from '../src' let history: RouterHistory @@ -66,6 +72,141 @@ test('navigating to an actively preloaded missing URL renders the global not-fou expect(screen.queryByText('Home')).not.toBeInTheDocument() }) +test('a lazy route notFoundComponent handles an eager beforeLoad failure', async () => { + const rootRoute = createRootRoute({ + component: Outlet, + notFoundComponent: () =>
    Root not found
    , + }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
    Home
    , + }) + const failingRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/lazy-not-found', + beforeLoad: () => { + throw notFound() + }, + }).lazy(() => + Promise.resolve( + createLazyRoute('/lazy-not-found')({ + notFoundComponent: () =>
    Lazy route not found
    , + }), + ), + ) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute, failingRoute]), + history, + }) + + render() + expect(await screen.findByText('Home')).toBeInTheDocument() + + await act(() => router.navigate({ to: '/lazy-not-found' })) + + expect(screen.getByText('Lazy route not found')).toBeInTheDocument() + expect(screen.queryByText('Root not found')).not.toBeInTheDocument() +}) + +test('SSR uses a lazy route notFoundComponent for an eager beforeLoad failure', async () => { + const rootRoute = createRootRoute({ + component: Outlet, + notFoundComponent: () =>
    Root not found
    , + }) + const failingRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/lazy-not-found', + beforeLoad: () => { + throw notFound() + }, + }).lazy(() => + Promise.resolve( + createLazyRoute('/lazy-not-found')({ + notFoundComponent: () =>
    Lazy route not found
    , + }), + ), + ) + const handler = createRequestHandler({ + request: new Request('http://localhost/lazy-not-found'), + createRouter: () => + createRouter({ + routeTree: rootRoute.addChildren([failingRoute]), + isServer: true, + }), + }) + + const response = await handler(({ router, responseHeaders }) => + renderRouterToString({ + router, + responseHeaders, + children: , + }), + ) + + expect(response.status).toBe(404) + const html = await response.text() + expect(html).toContain('Lazy route not found') + expect(html).not.toContain('Root not found') +}) + +test.each(['client', 'server'] as const)( + 'a lazy child boundary handles a fuzzy URL miss on the %s', + async (environment) => { + const rootRoute = createRootRoute({ + component: Outlet, + notFoundComponent: () =>
    Root fuzzy boundary
    , + }) + const parentRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + component: Outlet, + notFoundComponent: () =>
    Parent fuzzy boundary
    , + }) + const childRoute = createRoute({ + getParentRoute: () => parentRoute, + path: '/child', + }).lazy(() => + Promise.resolve( + createLazyRoute('/parent/child')({ + notFoundComponent: () =>
    Lazy child fuzzy boundary
    , + }), + ), + ) + const routeTree = rootRoute.addChildren([ + parentRoute.addChildren([childRoute]), + ]) + + if (environment === 'client') { + const router = createRouter({ routeTree, history }) + render() + await act(() => router.navigate({ to: '/parent/child/missing' as any })) + + expect(screen.getByText('Lazy child fuzzy boundary')).toBeInTheDocument() + expect( + screen.queryByText('Parent fuzzy boundary'), + ).not.toBeInTheDocument() + return + } + + const response = await createRequestHandler({ + request: new Request('http://localhost/parent/child/missing'), + createRouter: () => createRouter({ routeTree, isServer: true }), + })(({ router, responseHeaders }) => + renderRouterToString({ + router, + responseHeaders, + children: , + }), + ) + + expect(response.status).toBe(404) + const html = await response.text() + expect(html).toContain('Lazy child fuzzy boundary') + expect(html).not.toContain('Parent fuzzy boundary') + }, +) + test.each([ { notFoundMode: 'fuzzy' as const, diff --git a/packages/react-router/tests/on-rendered-same-href-state.test.tsx b/packages/react-router/tests/on-rendered-same-href-state.test.tsx new file mode 100644 index 0000000000..76e7a0cb9d --- /dev/null +++ b/packages/react-router/tests/on-rendered-same-href-state.test.tsx @@ -0,0 +1,66 @@ +import { act } from 'react' +import { cleanup, render, screen, waitFor } from '@testing-library/react' +import { afterEach, expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { + Outlet, + RouterProvider, + createRootRoute, + createRoute, + createRouter, +} from '../src' + +const testCleanups: Array<() => void> = [] + +afterEach(() => { + while (testCleanups.length) { + testCleanups.pop()!() + } + cleanup() +}) + +test('onRendered fires for a same-href navigation with a new history key', async () => { + const rootRoute = createRootRoute({ component: () => }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
    Index
    , + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + render() + expect(await screen.findByText('Index')).toBeInTheDocument() + await waitFor(() => { + expect(router.state.status).toBe('idle') + expect(router.state.resolvedLocation?.href).toBe('/') + }) + const initialHistoryKey = router.state.resolvedLocation?.state.__TSR_key + expect(initialHistoryKey).toBeDefined() + + const onRendered = vi.fn() + const unsubscribe = router.subscribe('onRendered', onRendered) + testCleanups.push(unsubscribe) + await act(() => + router.navigate({ + to: '/', + state: { sameHrefState: true } as any, + }), + ) + await waitFor(() => expect(onRendered).toHaveBeenCalledTimes(1)) + + const event = onRendered.mock.calls[0]![0] + expect(event.fromLocation?.state.sameHrefState).toBeUndefined() + expect(event.toLocation.state.sameHrefState).toBe(true) + expect(event.fromLocation?.href).toBe('/') + expect(event.toLocation.href).toBe('/') + expect(event.hrefChanged).toBe(false) + expect(event.fromLocation?.state.__TSR_key).toBe(initialHistoryKey) + expect(event.toLocation.state.__TSR_key).toBeDefined() + expect(event.toLocation.state.__TSR_key).not.toBe(initialHistoryKey) + expect(router.state.resolvedLocation?.state.__TSR_key).toBe( + event.toLocation.state.__TSR_key, + ) +}) diff --git a/packages/react-router/tests/pending-fallback-promise-replacement.test.tsx b/packages/react-router/tests/pending-fallback-promise-replacement.test.tsx new file mode 100644 index 0000000000..0f5ca3703d --- /dev/null +++ b/packages/react-router/tests/pending-fallback-promise-replacement.test.tsx @@ -0,0 +1,138 @@ +import * as React from 'react' +import { act } from 'react' +import { afterEach, expect, test, vi } from 'vitest' +import { cleanup, render, screen } from '@testing-library/react' +import { createControlledPromise } from '@tanstack/router-core' +import { + Outlet, + RouterProvider, + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, +} from '../src' +import type { AnyRouter } from '../src' + +afterEach(() => { + vi.useRealTimers() + cleanup() +}) + +test.each(['child', 'root'] as const)( + 'a mounted %s pending fallback follows an overlapping load generation', + async (routeLevel) => { + const firstReload = createControlledPromise() + const secondReload = createControlledPromise() + const reloads = [firstReload, secondReload] + let loaderCall = 0 + + const routeOptions = { + pendingMs: 0, + pendingMinMs: 100, + pendingComponent: () =>
    Loading...
    , + loader: () => { + const generation = ++loaderCall + const gate = reloads[generation - 2] + return gate ? gate.then(() => ({ generation })) : { generation } + }, + } + + const makeRouter = (): AnyRouter => { + if (routeLevel === 'root') { + const rootRoute = createRootRoute({ + ...routeOptions, + component: () => ( +
    + Generation {rootRoute.useLoaderData().generation} +
    + ), + }) + return createRouter({ + routeTree: rootRoute, + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + } + + const rootRoute = createRootRoute({ + component: () => , + }) + const pageRoute = createRoute({ + ...routeOptions, + getParentRoute: () => rootRoute, + path: '/page', + component: () => ( +
    + Generation {pageRoute.useLoaderData().generation} +
    + ), + }) + return createRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + } + const router = makeRouter() + + render() + expect(await screen.findByText('Generation 1')).toBeInTheDocument() + + vi.useFakeTimers() + + let firstInvalidation!: Promise + await act(async () => { + firstInvalidation = router.invalidate({ forcePending: true }) + await vi.advanceTimersByTimeAsync(0) + }) + + expect(loaderCall).toBe(2) + expect(screen.getByTestId('pending')).toBeInTheDocument() + + await act(async () => { + await vi.advanceTimersByTimeAsync(25) + }) + + let secondInvalidation!: Promise + await act(async () => { + secondInvalidation = router.invalidate({ forcePending: true }) + await vi.advanceTimersByTimeAsync(0) + }) + + expect(loaderCall).toBe(3) + + await act(async () => { + firstReload.resolve() + await Promise.resolve() + }) + + // Completing the superseded generation cannot release the currently + // mounted fallback or restore its stale loader data. + expect(screen.getByTestId('pending')).toBeInTheDocument() + expect(screen.queryByText('Generation 2')).not.toBeInTheDocument() + + let secondSettled = false + void secondInvalidation.then(() => { + secondSettled = true + }) + await act(async () => { + secondReload.resolve() + await Promise.resolve() + }) + + expect(secondSettled).toBe(false) + expect(screen.getByTestId('pending')).toBeInTheDocument() + + await act(async () => { + await vi.advanceTimersByTimeAsync(74) + }) + expect(secondSettled).toBe(false) + expect(screen.getByTestId('pending')).toBeInTheDocument() + + await act(async () => { + await vi.advanceTimersByTimeAsync(1) + await Promise.all([firstInvalidation, secondInvalidation]) + }) + + expect(screen.getByText('Generation 3')).toBeInTheDocument() + expect(screen.queryByTestId('pending')).not.toBeInTheDocument() + }, +) diff --git a/packages/react-router/tests/preloaded-mount-resolution.test.tsx b/packages/react-router/tests/preloaded-mount-resolution.test.tsx new file mode 100644 index 0000000000..1679546c79 --- /dev/null +++ b/packages/react-router/tests/preloaded-mount-resolution.test.tsx @@ -0,0 +1,155 @@ +import { afterEach, beforeEach, expect, onTestFinished, test, vi } from 'vitest' +import * as React from 'react' +import { createRoot } from 'react-dom/client' +import { createMemoryHistory } from '@tanstack/history' +import { + Outlet, + RouterProvider, + createControlledPromise, + createRootRoute, + createRoute, + createRouter, +} from '../src' + +/** + * A load that settles before RouterProvider mounts (or completes within the + * mount effect's batch) gives the Transitioner no isLoading flip to observe. + * The router status must still resolve to 'idle' with resolvedLocation set, + * and onRendered must fire — otherwise consumers waiting on those signals + * deadlock forever (this hung the memory-client benchmark for 6 hours). + * + * Uses a raw createRoot without the act() test environment: act-driven + * flushing re-renders between the isLoading toggles and masks the race. + * + * Note: vitest's jsdom scheduler still observes the flip more often than the + * benchmark's environment, so this test pins the CONTRACT; the deterministic + * regression guard for the original hang is the memory-client:react + * benchmark (benchmarks/memory/client/scenarios/mount-unmount), which CI runs. + */ + +let prevActEnv: unknown + +beforeEach(() => { + prevActEnv = (globalThis as any).IS_REACT_ACT_ENVIRONMENT + ;(globalThis as any).IS_REACT_ACT_ENVIRONMENT = false +}) + +afterEach(() => { + ;(globalThis as any).IS_REACT_ACT_ENVIRONMENT = prevActEnv +}) + +test('mounting after a settled load still resolves status and fires onRendered', async () => { + const lifecycle: Array<'layout' | 'rendered'> = [] + const Home = () => { + React.useLayoutEffect(() => { + lifecycle.push('layout') + }, []) + return
    Home
    + } + const rootRoute = createRootRoute({ + component: () => , + }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + loader: () => 'home data', + component: Home, + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + let resolveRendered!: () => void + const rendered = new Promise((resolve) => { + resolveRendered = resolve + }) + const onRendered = vi.fn(() => { + lifecycle.push('rendered') + resolveRendered() + }) + const onResolved = vi.fn() + const onLoad = vi.fn() + const unsubscribers = [ + router.subscribe('onRendered', onRendered), + router.subscribe('onResolved', onResolved), + router.subscribe('onLoad', onLoad), + ] + const unsubscribe = () => unsubscribers.forEach((fn) => fn()) + const container = document.createElement('div') + document.body.appendChild(container) + const reactRoot = createRoot(container) + let renderedTimeout: ReturnType | undefined + + onTestFinished(() => { + clearTimeout(renderedTimeout) + unsubscribe() + reactRoot.unmount() + container.remove() + }) + + // Load fully settles before the provider mounts — the exact shape of the + // memory benchmark's mount/unmount cycle. + await router.load() + expect(router.state.status).toBe('idle') + expect(router.state.resolvedLocation?.pathname).toBe('/') + expect(onLoad).toHaveBeenCalledTimes(1) + expect(onResolved).toHaveBeenCalledTimes(1) + expect(onRendered).not.toHaveBeenCalled() + + reactRoot.render() + await Promise.race([ + rendered, + new Promise((_, reject) => { + renderedTimeout = setTimeout(() => { + reject(new Error('Timed out waiting for onRendered')) + }, 2000) + }), + ]) + + expect(container.querySelector('[data-testid="home"]')).not.toBeNull() + expect(onRendered).toHaveBeenCalledTimes(1) + expect(lifecycle).toEqual(['layout', 'rendered']) + expect(router.state.status).toBe('idle') + expect(router.state.resolvedLocation?.pathname).toBe('/') +}) + +test('mounting during a load keeps the existing generation', async () => { + const gate = createControlledPromise() + const beforeLoad = vi.fn() + const loader = vi.fn(() => gate) + const rootRoute = createRootRoute({ component: () => }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + beforeLoad, + loader, + component: () =>
    Home
    , + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + const load = router.load() + await vi.waitFor(() => expect(loader).toHaveBeenCalledOnce()) + + const container = document.createElement('div') + document.body.appendChild(container) + const reactRoot = createRoot(container) + + onTestFinished(() => { + reactRoot.unmount() + container.remove() + }) + + reactRoot.render() + await vi.waitFor(() => expect(beforeLoad).toHaveBeenCalledOnce()) + gate.resolve() + await load + await vi.waitFor(() => { + expect(container.querySelector('[data-testid="home"]')).not.toBeNull() + }) + + expect(beforeLoad).toHaveBeenCalledOnce() + expect(loader).toHaveBeenCalledOnce() +}) diff --git a/packages/react-router/tests/public-presentation-lane-contract.test.tsx b/packages/react-router/tests/public-presentation-lane-contract.test.tsx index aa744b87f6..862fffc93f 100644 --- a/packages/react-router/tests/public-presentation-lane-contract.test.tsx +++ b/packages/react-router/tests/public-presentation-lane-contract.test.tsx @@ -1,5 +1,5 @@ import { act, cleanup, render, screen, waitFor } from '@testing-library/react' -import { afterEach, describe, expect, test } from 'vitest' +import { afterEach, describe, expect, test, vi } from 'vitest' import { createControlledPromise } from '@tanstack/router-core' import { Outlet, @@ -12,6 +12,7 @@ import { afterEach(() => { cleanup() + vi.useRealTimers() }) describe('public presentation lane contracts', () => { @@ -77,6 +78,116 @@ describe('public presentation lane contracts', () => { expect(router.state.status).toBe('idle') }) + test('same-boundary takeover republishes successor search without restarting pendingMinMs', async () => { + const firstGate = createControlledPromise() + const secondGate = createControlledPromise() + + const rootRoute = createRootRoute({ component: Outlet }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
    Home
    , + }) + const pageRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/page', + validateSearch: (search: Record) => ({ + revision: Number(search.revision), + }), + pendingMs: 0, + pendingMinMs: 100, + pendingComponent: () =>
    Loading page
    , + beforeLoad: ({ search }) => + search.revision === 1 ? firstGate : secondGate, + component: () => { + const search = pageRoute.useSearch() + return
    Page revision {search.revision}
    + }, + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute, pageRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + render() + expect(await screen.findByText('Home')).toBeInTheDocument() + await waitFor(() => expect(router.state.status).toBe('idle')) + + vi.useFakeTimers() + vi.setSystemTime(0) + + let successorSettled = false + let settledAtOriginalDeadline = false + let renderedAtOriginalDeadline = false + try { + await act(async () => { + void router.navigate({ + to: '/page', + search: { revision: 1 }, + }) + await vi.advanceTimersByTimeAsync(0) + }) + expect(screen.getByText('Loading page')).toBeInTheDocument() + expect(router.state.matches.at(-1)?.search).toMatchObject({ revision: 1 }) + + await act(async () => { + await vi.advanceTimersByTimeAsync(25) + }) + + let secondNavigation!: Promise + await act(async () => { + secondNavigation = router.navigate({ + to: '/page', + search: { revision: 2 }, + }) + await vi.advanceTimersByTimeAsync(0) + }) + + expect(screen.getByText('Loading page')).toBeInTheDocument() + expect(router.state.location.search).toMatchObject({ revision: 2 }) + expect(router.state.matches.at(-1)?.search).toMatchObject({ revision: 2 }) + + void secondNavigation.then(() => { + successorSettled = true + }) + await act(async () => { + secondGate.resolve() + await Promise.resolve() + }) + + await act(async () => { + await vi.advanceTimersByTimeAsync(74) + }) + expect(successorSettled).toBe(false) + expect(screen.getByText('Loading page')).toBeInTheDocument() + + await act(async () => { + await vi.advanceTimersByTimeAsync(1) + await Promise.resolve() + }) + + settledAtOriginalDeadline = successorSettled + renderedAtOriginalDeadline = + screen.queryByText('Page revision 2') !== null + } finally { + // Finish a faulty implementation too, so a deadline assertion cannot + // strand this router and contaminate the following test. + firstGate.resolve() + secondGate.resolve() + await act(async () => { + await vi.advanceTimersByTimeAsync(1_000) + await Promise.resolve() + }) + } + + expect({ + settled: settledAtOriginalDeadline, + rendered: renderedAtOriginalDeadline, + }).toEqual({ settled: true, rendered: true }) + expect(screen.getByText('Page revision 2')).toBeInTheDocument() + expect(screen.queryByText('Loading page')).not.toBeInTheDocument() + }) + test('a reentrant navigation from onResolved suppresses the stale onRendered event', async () => { const rootRoute = createRootRoute({ component: Outlet }) const indexRoute = createRoute({ diff --git a/packages/react-router/tests/react-render-owner-contract.test.tsx b/packages/react-router/tests/react-render-owner-contract.test.tsx new file mode 100644 index 0000000000..0797fa5264 --- /dev/null +++ b/packages/react-router/tests/react-render-owner-contract.test.tsx @@ -0,0 +1,101 @@ +import { act } from 'react' +import { cleanup, render, screen, waitFor } from '@testing-library/react' +import { afterEach, expect, test, vi } from 'vitest' +import { + RouterProvider, + createControlledPromise, + createMemoryHistory, + createRootRoute, + createRouter, +} from '../src' + +const testCleanups: Array<() => void> = [] + +afterEach(() => { + while (testCleanups.length) { + testCleanups.pop()!() + } + cleanup() +}) + +test('a suspended same-membership publication cannot acknowledge its successor', async () => { + const firstRenderStarted = createControlledPromise() + const firstRenderGate = createControlledPromise() + let signaledFirstRender = false + + const rootRoute = createRootRoute({ + validateSearch: (search: Record) => ({ + revision: Number(search.revision), + }), + component: () => { + const revision = rootRoute.useSearch().revision + if (revision === 1 && firstRenderGate.status === 'pending') { + if (!signaledFirstRender) { + signaledFirstRender = true + firstRenderStarted.resolve() + } + throw firstRenderGate + } + return
    Root revision {revision}
    + }, + }) + const router = createRouter({ + routeTree: rootRoute, + history: createMemoryHistory({ initialEntries: ['/?revision=0'] }), + }) + + render() + expect(await screen.findByText('Root revision 0')).toBeInTheDocument() + await waitFor(() => expect(router.state.status).toBe('idle')) + const initialIds = router.state.matches.map((match) => match.routeId) + + const renderedRevisions: Array = [] + const unsubscribe = router.subscribe('onRendered', (event) => { + renderedRevisions.push( + Number((event.toLocation.search as Record).revision), + ) + }) + testCleanups.push(unsubscribe) + + let firstNavigation!: Promise + await act(async () => { + firstNavigation = router.navigate({ + to: '/', + search: { revision: 1 }, + }) + await firstRenderStarted + }) + + const firstSettled = vi.fn() + void firstNavigation.then(firstSettled) + expect(router.state.matches.map((match) => match.routeId)).toEqual(initialIds) + expect(router.state.matches[0]?.search.revision).toBe(1) + expect(screen.getByText('Root revision 0')).toBeInTheDocument() + expect(firstSettled).not.toHaveBeenCalled() + expect(renderedRevisions).toEqual([]) + + try { + await act(() => + router.navigate({ + to: '/', + search: { revision: 2 }, + }), + ) + await firstNavigation + + expect(screen.getByText('Root revision 2')).toBeInTheDocument() + expect(router.state.matches.map((match) => match.routeId)).toEqual( + initialIds, + ) + expect(renderedRevisions).toEqual([2]) + expect(firstSettled).toHaveBeenCalledOnce() + } finally { + await act(async () => { + firstRenderGate.resolve() + await Promise.resolve() + }) + } + + expect(screen.getByText('Root revision 2')).toBeInTheDocument() + expect(renderedRevisions).toEqual([2]) +}) diff --git a/packages/react-router/tests/issue-7457-redirect-chain-first-load.test.tsx b/packages/react-router/tests/redirect-chain-first-load.test.tsx similarity index 96% rename from packages/react-router/tests/issue-7457-redirect-chain-first-load.test.tsx rename to packages/react-router/tests/redirect-chain-first-load.test.tsx index 3583190d79..b3f4618a28 100644 --- a/packages/react-router/tests/issue-7457-redirect-chain-first-load.test.tsx +++ b/packages/react-router/tests/redirect-chain-first-load.test.tsx @@ -18,12 +18,13 @@ afterEach(() => { cleanup() }) -// https://github.com/TanStack/router/issues/7457 // A chain of async layout beforeLoad redirects during the very first load // (search-stripping self-redirect -> layout redirect -> child redirect) used // to leave a match rendering with a nulled loadPromise, crashing // MatchInnerImpl with an uncaught `undefined`. Pending UI is enabled for // every match (defaultPendingMs: 0) to force pending publication mid-chain. +// The production auto-code-splitting reproduction for issue #7457 lives in +// e2e/react-router/issue-7457. test('chained layout beforeLoad redirects on first load render the final target without throwing from MatchInner', async () => { const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {}) diff --git a/packages/react-router/tests/redirect.test.tsx b/packages/react-router/tests/redirect.test.tsx index a0d7906ac3..0c254fb098 100644 --- a/packages/react-router/tests/redirect.test.tsx +++ b/packages/react-router/tests/redirect.test.tsx @@ -16,7 +16,6 @@ import { Outlet, RouterProvider, createBrowserHistory, - createMemoryHistory, createRootRoute, createRoute, createRouter, @@ -76,6 +75,85 @@ describe('redirect', () => { expect(router.state.status).toBe('idle') }) + test('renders a root error after too many same-location redirects', async () => { + const loader = vi.fn(() => { + throw redirect({ to: '/' }) + }) + const rootRoute = createRootRoute({ + errorComponent: ({ error }) => ( +
    Root: {error.message}
    + ), + }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + loader, + errorComponent: ({ error }) => ( +
    Index: {error.message}
    + ), + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute]), + history, + }) + + render() + + expect(await screen.findByTestId('root-error')).toHaveTextContent( + 'Root: Too many redirects', + ) + expect(screen.queryByTestId('index-error')).not.toBeInTheDocument() + expect(window.location.pathname).toBe('/') + expect(loader).toHaveBeenCalledTimes(21) + expect(router.state.status).toBe('idle') + }) + + test('renders a root error after too many alternating redirects', async () => { + const indexLoader = vi.fn(() => { + throw redirect({ to: '/other' }) + }) + const otherLoader = vi.fn(() => { + throw redirect({ to: '/' }) + }) + const rootRoute = createRootRoute({ + errorComponent: ({ error }) => ( +
    Root: {error.message}
    + ), + }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + loader: indexLoader, + errorComponent: ({ error }) => ( +
    Index: {error.message}
    + ), + }) + const otherRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/other', + loader: otherLoader, + errorComponent: ({ error }) => ( +
    Other: {error.message}
    + ), + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute, otherRoute]), + history, + }) + + render() + + expect(await screen.findByTestId('root-error')).toHaveTextContent( + 'Root: Too many redirects', + ) + expect(screen.queryByTestId('index-error')).not.toBeInTheDocument() + expect(screen.queryByTestId('other-error')).not.toBeInTheDocument() + expect(window.location.pathname).toBe('/') + expect(indexLoader).toHaveBeenCalledTimes(11) + expect(otherLoader).toHaveBeenCalledTimes(10) + expect(router.state.status).toBe('idle') + }) + test('when `redirect` is thrown in `beforeLoad`', async () => { const nestedLoaderMock = vi.fn() const nestedFooLoaderMock = vi.fn() @@ -351,116 +429,4 @@ describe('redirect', () => { expect(window.location.pathname).toBe('/final') }) }) - - describe('SSR', () => { - test('when `redirect` is thrown in `beforeLoad`', async () => { - const rootRoute = createRootRoute() - - const indexRoute = createRoute({ - path: '/', - getParentRoute: () => rootRoute, - beforeLoad: () => { - throw redirect({ - to: '/about', - }) - }, - }) - - const aboutRoute = createRoute({ - path: '/about', - getParentRoute: () => rootRoute, - component: () => { - return 'About' - }, - }) - - const router = createRouter({ - routeTree: rootRoute.addChildren([indexRoute, aboutRoute]), - // Mock server mode - isServer: true, - history: createMemoryHistory({ - initialEntries: ['/'], - }), - }) - - await router.load() - - expect(router.state.redirect).toBeDefined() - expect(router.state.redirect).toBeInstanceOf(Response) - const redirectResponse = router.state.redirect! - - expect(redirectResponse.options).toEqual({ - _fromLocation: expect.objectContaining({ - hash: '', - href: '/', - pathname: '/', - search: {}, - searchStr: '', - }), - to: '/about', - href: '/about', - statusCode: 307, - }) - }) - - test('when `redirect` is thrown in `loader`', async () => { - const rootRoute = createRootRoute() - - const indexRoute = createRoute({ - path: '/', - getParentRoute: () => rootRoute, - loader: () => { - throw redirect({ - to: '/about', - }) - }, - }) - - const aboutRoute = createRoute({ - path: '/about', - getParentRoute: () => rootRoute, - component: () => { - return 'About' - }, - }) - - const router = createRouter({ - history: createMemoryHistory({ - initialEntries: ['/'], - }), - routeTree: rootRoute.addChildren([indexRoute, aboutRoute]), - // Mock server mode - isServer: true, - }) - - await router.load() - - const currentRedirect = router.state.redirect - - expect(currentRedirect).toBeDefined() - expect(currentRedirect).toBeInstanceOf(Response) - const redirectResponse = currentRedirect! - expect(redirectResponse.status).toEqual(307) - expect(redirectResponse.headers.get('Location')).toEqual('/about') - expect(redirectResponse.options).toEqual({ - _fromLocation: { - external: false, - hash: '', - href: '/', - publicHref: '/', - pathname: '/', - search: {}, - searchStr: '', - state: { - __TSR_index: 0, - __TSR_key: redirectResponse.options._fromLocation!.state.__TSR_key, - key: redirectResponse.options._fromLocation!.state.key, - }, - }, - href: '/about', - to: '/about', - statusCode: 307, - }) - }) - }) }) diff --git a/packages/react-router/tests/renderRouterToStream.test.tsx b/packages/react-router/tests/renderRouterToStream.test.tsx index 116d8a259f..f42456b7be 100644 --- a/packages/react-router/tests/renderRouterToStream.test.tsx +++ b/packages/react-router/tests/renderRouterToStream.test.tsx @@ -1,4 +1,4 @@ -import { afterEach, describe, expect, test, vi } from 'vitest' +import { afterEach, describe, expect, onTestFinished, test, vi } from 'vitest' import { attachRouterServerSsrUtils } from '@tanstack/router-core/ssr/server' import { createMemoryHistory, createRootRoute, createRouter } from '../src' @@ -52,6 +52,35 @@ function unwrapResponse( } describe('renderRouterToStream - pipeable sync errors', () => { + test('request abort cancels readable rendering without consuming the response body', async () => { + const cancel = vi.fn() + const stream = Object.assign(new ReadableStream({ cancel }), { + allReady: Promise.resolve(), + }) + reactDomServerMocks.renderToReadableStream = vi.fn(() => stream) + + const router = await buildRouter() + const controller = new AbortController() + onTestFinished(() => { + router.serverSsr?.cleanup() + }) + + const response = unwrapResponse( + await renderRouterToStream({ + request: new Request('http://localhost/', { + signal: controller.signal, + }), + router, + responseHeaders: new Headers(), + children: null, + }), + ) + + expect(response.body).not.toBeNull() + controller.abort(new Error('request-gone')) + await vi.waitFor(() => expect(cancel).toHaveBeenCalledOnce()) + }) + test('sync onError before pipeable is assigned still aborts pipeable', async () => { const abort = vi.fn() reactDomServerMocks.renderToPipeableStream.mockImplementationOnce( diff --git a/packages/react-router/tests/root-pending-min.test.tsx b/packages/react-router/tests/root-pending-min.test.tsx new file mode 100644 index 0000000000..0216ae4fec --- /dev/null +++ b/packages/react-router/tests/root-pending-min.test.tsx @@ -0,0 +1,186 @@ +import * as React from 'react' +import { act, cleanup, render, screen } from '@testing-library/react' +import { hydrateRoot } from 'react-dom/client' +import { renderToString } from 'react-dom/server' +import { afterEach, expect, test, vi } from 'vitest' +import { dehydrateSsrMatchId } from '../../router-core/src/ssr/ssr-match-id' +import { hydrate } from '../src/ssr/client' +import { + RouterProvider, + createControlledPromise, + createMemoryHistory, + createRootRoute, + createRouter, +} from '../src' + +const testCleanups: Array<() => void | Promise> = [] + +afterEach(async () => { + while (testCleanups.length) { + await testCleanups.pop()!() + } + cleanup() + vi.useRealTimers() + delete window.$_TSR +}) + +test('a post-hydration root reload keeps its fallback through pendingMinMs', async () => { + const reloadGate = createControlledPromise() + const rootLoader = vi.fn(() => reloadGate.then(() => ({ generation: 2 }))) + const rootRoute = createRootRoute({ + pendingMs: 0, + pendingMinMs: 100, + pendingComponent: () =>
    Pending
    , + loader: rootLoader, + component: () => ( +
    + Generation {rootRoute.useLoaderData().generation} +
    + ), + }) + const router = createRouter({ + routeTree: rootRoute, + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + const rootMatch = router.matchRoutes(router.latestLocation)[0]! + // This is the same public bootstrap shape produced by the server. Calling + // hydrate() ensures router.ssr and the active match are established through + // the real client hydration path rather than by mutating router stores. + window.$_TSR = { + router: { + manifest: { routes: {} }, + dehydratedData: {}, + matches: [ + { + i: dehydrateSsrMatchId(rootMatch.id), + s: 'success', + ssr: true, + l: { generation: 1 }, + u: Date.now(), + }, + ], + }, + h: vi.fn(), + e: vi.fn(), + c: vi.fn(), + p: vi.fn(), + buffer: [], + initialized: false, + } + + await hydrate(router) + expect(router.ssr).toBeDefined() + + render() + expect(screen.getByTestId('root-content')).toHaveTextContent('Generation 1') + expect(rootLoader).not.toHaveBeenCalled() + + vi.useFakeTimers() + + let invalidation!: Promise + await act(async () => { + invalidation = router.invalidate({ forcePending: true }) + await vi.advanceTimersByTimeAsync(0) + }) + + expect(rootLoader).toHaveBeenCalledTimes(1) + expect(screen.getByTestId('root-pending')).toBeInTheDocument() + expect(screen.getByTestId('root-content')).not.toBeVisible() + + await act(async () => { + reloadGate.resolve() + await Promise.resolve() + }) + + await act(async () => { + await vi.advanceTimersByTimeAsync(99) + }) + expect(screen.getByTestId('root-pending')).toBeInTheDocument() + + await act(async () => { + await vi.advanceTimersByTimeAsync(1) + await invalidation + }) + expect(screen.queryByTestId('root-pending')).not.toBeInTheDocument() + expect(screen.getByTestId('root-content')).toHaveTextContent('Generation 2') +}) + +test('root route hydration preserves component state across its Suspense boundary', async () => { + const mounts = vi.fn() + const unmounts = vi.fn() + const initializers = vi.fn(() => 'preserved') + + const rootRoute = createRootRoute({ + pendingComponent: () =>
    Root pending
    , + component: function RootComponent() { + const [value] = React.useState(initializers) + React.useEffect(() => { + mounts() + return unmounts + }, []) + return
    {value}
    + }, + }) + const router = createRouter({ + routeTree: rootRoute, + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + await router.load() + + // Model the server render and the client router produced by hydrate(). The + // outer root boundary is intentionally absent from both trees, while the + // route's own pending boundary is present in both. + router.ssr = { manifest: { routes: {} } } + router.isServer = true + const html = renderToString() + router.isServer = false + expect(html).toContain('') + expect(html).toContain('preserved') + + const container = document.createElement('div') + container.innerHTML = html + document.body.appendChild(container) + const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {}) + let root!: ReturnType + await act(async () => { + root = hydrateRoot(container, ) + testCleanups.push(async () => { + await act(() => root.unmount()) + consoleError.mockRestore() + container.remove() + }) + await Promise.resolve() + }) + + expect(container).toHaveTextContent('preserved') + // One initializer belongs to the server render and one to client + // hydration. The stable boundary must preserve that hydrated client + // instance instead of creating a third one. + expect(initializers).toHaveBeenCalledTimes(2) + expect(mounts).toHaveBeenCalledTimes(1) + expect(unmounts).not.toHaveBeenCalled() + expect(consoleError).not.toHaveBeenCalled() +}) + +test('server rendering uses the root pending boundary for route component suspension', async () => { + const gate = createControlledPromise() + const rootRoute = createRootRoute({ + pendingComponent: () =>
    Server root pending
    , + component: () => { + throw gate + }, + }) + const router = createRouter({ + routeTree: rootRoute, + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + router.isServer = true + await router.load() + + const html = renderToString() + + // renderToString cannot wait for Suspense, but the root route's stable + // boundary contains the suspension and emits its fallback. Streaming SSR + // can wait for the same boundary instead. + expect(html).toContain('Server root pending') +}) diff --git a/packages/react-router/tests/router-client-stream-cleanup.test.tsx b/packages/react-router/tests/router-client-stream-cleanup.test.tsx new file mode 100644 index 0000000000..865b7cd732 --- /dev/null +++ b/packages/react-router/tests/router-client-stream-cleanup.test.tsx @@ -0,0 +1,55 @@ +import { act, cleanup, render, screen, waitFor } from '@testing-library/react' +import { afterEach, expect, test, vi } from 'vitest' +import { Component } from 'react' +import { createMemoryHistory } from '@tanstack/history' +import { RouterClient } from '../src/ssr/RouterClient' +import { createRootRoute, createRouter } from '../src' +import type { ReactNode } from 'react' + +const hydrate = vi.hoisted(() => vi.fn()) + +vi.mock('@tanstack/router-core/ssr/client', () => ({ hydrate })) + +class ErrorBoundary extends Component< + { children: ReactNode }, + { error?: Error } +> { + state: { error?: Error } = {} + + static getDerivedStateFromError(error: Error) { + return { error } + } + + render() { + return this.state.error ? this.state.error.message : this.props.children + } +} + +afterEach(() => { + cleanup() + delete window.$_TSR + hydrate.mockReset() +}) + +test('RouterClient signals streaming cleanup without hiding a hydration failure', async () => { + const error = new Error('hydration failed') + hydrate.mockRejectedValue(error) + const rootRoute = createRootRoute({ component: () =>
    Ready
    }) + const router = createRouter({ + routeTree: rootRoute, + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + const hydrated = vi.fn() + window.$_TSR = { h: hydrated } as any + + await act(async () => { + render( + + + , + ) + }) + + await waitFor(() => expect(hydrated).toHaveBeenCalledTimes(1)) + expect(await screen.findByText(error.message)).toBeInTheDocument() +}) diff --git a/packages/react-router/tests/store-updates-during-navigation.test.tsx b/packages/react-router/tests/store-updates-during-navigation.test.tsx index 0c4c1b4146..8a10e06339 100644 --- a/packages/react-router/tests/store-updates-during-navigation.test.tsx +++ b/packages/react-router/tests/store-updates-during-navigation.test.tsx @@ -136,7 +136,7 @@ describe("Store doesn't update *too many* times during navigation", () => { // This number should be as small as possible to minimize the amount of work // that needs to be done during a navigation. // Any change that increases this number should be investigated. - expect(updates).toBe(8) + expect(updates).toBe(7) }) test('redirection in preload', async () => { @@ -154,7 +154,7 @@ describe("Store doesn't update *too many* times during navigation", () => { // This number should be as small as possible to minimize the amount of work // that needs to be done during a navigation. // Any change that increases this number should be investigated. - expect(updates).toBe(1) + expect(updates).toBe(2) }) test('sync beforeLoad', async () => { @@ -196,7 +196,7 @@ describe("Store doesn't update *too many* times during navigation", () => { // This number should be as small as possible to minimize the amount of work // that needs to be done during a navigation. // Any change that increases this number should be investigated. - expect(updates).toBe(4) + expect(updates).toBe(3) }) test('hover preload, then navigate, w/ async loaders', async () => { diff --git a/packages/react-router/tests/transactional-loading.test.tsx b/packages/react-router/tests/transactional-loading.test.tsx index 433354bab4..a6769c6063 100644 --- a/packages/react-router/tests/transactional-loading.test.tsx +++ b/packages/react-router/tests/transactional-loading.test.tsx @@ -6,18 +6,32 @@ import { screen, waitFor, } from '@testing-library/react' -import { afterEach, beforeEach, describe, expect, test } from 'vitest' +import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' import { Link, Outlet, RouterProvider, createBrowserHistory, + createMemoryHistory, createRootRoute, createRoute, createRouter, } from '../src' import type { RouterHistory } from '../src' +type Deferred = { + promise: Promise + resolve: (value: T) => void +} + +function deferred(): Deferred { + let resolve!: (value: T) => void + const promise = new Promise((resolver) => { + resolve = resolver + }) + return { promise, resolve } +} + let history: RouterHistory beforeEach(() => { @@ -28,9 +42,136 @@ afterEach(() => { history.destroy() window.history.replaceState(null, 'root', '/') cleanup() + vi.useRealTimers() }) describe('transactional route loading', () => { + test('publishes a parent and child background refresh atomically after the child observes fresh parent data', async () => { + const parentRefresh = deferred() + const childRefresh = deferred() + const childObservedFreshParent = deferred() + let parentLoads = 0 + let childLoads = 0 + + const rootRoute = createRootRoute({ + component: () => ( + <> + Other route + Data route + + + ), + }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
    Home route
    , + }) + const otherRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/other', + component: () =>
    Other route content
    , + }) + const parentRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + staleTime: 0, + gcTime: 60_000, + loader: async () => { + parentLoads += 1 + if (parentLoads === 1) { + return 'parent-v1' + } + return parentRefresh.promise + }, + component: () => ( + <> +
    {parentRoute.useLoaderData()}
    + + + ), + }) + const childRoute = createRoute({ + getParentRoute: () => parentRoute, + path: '/child', + staleTime: 0, + gcTime: 60_000, + loader: async ({ parentMatchPromise }) => { + childLoads += 1 + const parentMatch = await parentMatchPromise + const parentData = parentMatch.loaderData as string + if (childLoads > 1) { + childObservedFreshParent.resolve(undefined) + await childRefresh.promise + } + return `child-saw-${parentData}` + }, + component: () =>
    {childRoute.useLoaderData()}
    , + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([ + indexRoute, + otherRoute, + parentRoute.addChildren([childRoute]), + ]), + history, + }) + + render() + + expect(await screen.findByText('Home route')).toBeInTheDocument() + await waitFor(() => { + expect(router.state.status).toBe('idle') + expect(router.state.resolvedLocation?.pathname).toBe('/') + }) + fireEvent.click(screen.getByText('Data route')) + + expect(await screen.findByText('parent-v1')).toBeInTheDocument() + expect(await screen.findByText('child-saw-parent-v1')).toBeInTheDocument() + await waitFor(() => { + expect(router.state.status).toBe('idle') + expect(router.state.resolvedLocation?.pathname).toBe('/parent/child') + }) + + fireEvent.click(screen.getByText('Other route')) + expect(await screen.findByText('Other route content')).toBeInTheDocument() + await waitFor(() => { + expect(router.state.status).toBe('idle') + expect(router.state.resolvedLocation?.pathname).toBe('/other') + }) + + fireEvent.click(screen.getByText('Data route')) + + expect(await screen.findByText('parent-v1')).toBeInTheDocument() + expect(await screen.findByText('child-saw-parent-v1')).toBeInTheDocument() + await waitFor(() => { + expect(router.state.status).toBe('idle') + expect(router.state.resolvedLocation?.pathname).toBe('/parent/child') + }) + + await act(async () => { + parentRefresh.resolve('parent-v2') + await childObservedFreshParent.promise + }) + + expect(screen.getByText('parent-v1')).toBeInTheDocument() + expect(screen.getByText('child-saw-parent-v1')).toBeInTheDocument() + expect(screen.queryByText('parent-v2')).not.toBeInTheDocument() + expect(screen.queryByText('child-saw-parent-v2')).not.toBeInTheDocument() + + await act(async () => { + childRefresh.resolve(undefined) + await Promise.resolve() + }) + + await waitFor(() => { + expect(screen.getByText('parent-v2')).toBeInTheDocument() + expect(screen.getByText('child-saw-parent-v2')).toBeInTheDocument() + expect(screen.queryByText('parent-v1')).not.toBeInTheDocument() + expect(screen.queryByText('child-saw-parent-v1')).not.toBeInTheDocument() + }) + }) + test('renders a leaf error at the leaf boundary when its background refresh fails', async () => { let loads = 0 const rootRoute = createRootRoute({ @@ -94,4 +235,94 @@ describe('transactional route loading', () => { expect(screen.getByText('Root shell')).toBeInTheDocument() expect(screen.getByText('Parent shell')).toBeInTheDocument() }) + + test('renders the first settled background failure after foreground work completes', async () => { + const rootRefresh = deferred() + const parentStarted = deferred() + const childStarted = deferred() + const parentGate = deferred() + const childGate = deferred() + const parentSettled = deferred() + const childSettled = deferred() + let rootLoads = 0 + let parentLoads = 0 + let childLoads = 0 + + const rootRoute = createRootRoute({ + loader: { + staleReloadMode: 'blocking', + handler: async () => { + if (++rootLoads > 1) { + await rootRefresh.promise + } + }, + }, + component: Outlet, + }) + const parentRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + loader: { + staleReloadMode: 'background', + handler: async () => { + if (++parentLoads > 1) { + parentStarted.resolve(undefined) + await parentGate.promise + parentSettled.resolve(undefined) + throw new Error('later parent failure') + } + return 'parent data' + }, + }, + component: Outlet, + errorComponent: () =>
    Parent refresh failed
    , + }) + const childRoute = createRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loader: { + staleReloadMode: 'background', + handler: async () => { + if (++childLoads > 1) { + childStarted.resolve(undefined) + await childGate.promise + childSettled.resolve(undefined) + throw new Error('first child failure') + } + return 'child data' + }, + }, + component: () =>
    {childRoute.useLoaderData()}
    , + errorComponent: () =>
    Child refresh failed
    , + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([parentRoute.addChildren([childRoute])]), + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + }) + + render() + expect(await screen.findByText('child data')).toBeInTheDocument() + await waitFor(() => expect(router.state.status).toBe('idle')) + + let invalidation!: Promise + await act(async () => { + invalidation = router.invalidate() + await Promise.all([parentStarted.promise, childStarted.promise]) + }) + await act(async () => { + childGate.resolve(undefined) + await childSettled.promise + parentGate.resolve(undefined) + await parentSettled.promise + }) + expect(screen.getByText('child data')).toBeInTheDocument() + + await act(async () => { + rootRefresh.resolve(undefined) + await invalidation + }) + + expect(screen.getByText('Child refresh failed')).toBeInTheDocument() + expect(screen.queryByText('Parent refresh failed')).not.toBeInTheDocument() + }) }) diff --git a/packages/react-router/tests/transitioner-listener-errors.test.tsx b/packages/react-router/tests/transitioner-listener-errors.test.tsx new file mode 100644 index 0000000000..9682b8f60c --- /dev/null +++ b/packages/react-router/tests/transitioner-listener-errors.test.tsx @@ -0,0 +1,81 @@ +import * as React from 'react' +import { act, cleanup, render, screen } from '@testing-library/react' +import { afterEach, expect, test, vi } from 'vitest' +import { + Outlet, + RouterProvider, + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, +} from '../src' + +const testCleanups: Array<() => void | Promise> = [] + +afterEach(async () => { + while (testCleanups.length) { + await testCleanups.pop()!() + } + cleanup() +}) + +test('a throwing load-event listener cannot interrupt route hooks or later navigations', async () => { + const firstOnEnter = vi.fn() + const secondOnEnter = vi.fn() + const listenerError = new Error('onLoad listener failed') + const laterOnLoad = vi.fn() + const loadedPaths: Array = [] + + const rootRoute = createRootRoute({ component: Outlet }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
    Index route
    , + }) + const firstRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/first', + onEnter: firstOnEnter, + component: () =>
    First route
    , + }) + const secondRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/second', + onEnter: secondOnEnter, + component: () =>
    Second route
    , + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute, firstRoute, secondRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + render() + expect(await screen.findByText('Index route')).toBeInTheDocument() + + const unsubscribe = router.subscribe('onLoad', (event) => { + if (event.toLocation.pathname === '/first') { + throw listenerError + } + }) + const unsubscribeLater = router.subscribe('onLoad', (event) => { + if (event.toLocation.pathname !== '/') { + loadedPaths.push(event.toLocation.pathname) + laterOnLoad(event) + } + }) + testCleanups.push(unsubscribe, unsubscribeLater) + + await act(() => router.navigate({ to: '/first' })) + + expect(screen.getByText('First route')).toBeInTheDocument() + expect(loadedPaths).toEqual(['/first']) + + unsubscribe() + await act(() => router.navigate({ to: '/second' })) + + expect(screen.getByText('Second route')).toBeInTheDocument() + expect(firstOnEnter).toHaveBeenCalledTimes(1) + expect(secondOnEnter).toHaveBeenCalledTimes(1) + expect(laterOnLoad).toHaveBeenCalledTimes(2) + expect(loadedPaths).toEqual(['/first', '/second']) +}) diff --git a/packages/react-router/tests/transitioner-render-ack.test.tsx b/packages/react-router/tests/transitioner-render-ack.test.tsx index 535a1e0e71..74bbe7d485 100644 --- a/packages/react-router/tests/transitioner-render-ack.test.tsx +++ b/packages/react-router/tests/transitioner-render-ack.test.tsx @@ -19,6 +19,68 @@ afterEach(() => { } cleanup() vi.useRealTimers() + vi.unstubAllEnvs() +}) + +test('a route lifecycle callback cannot strand a production navigation', async () => { + vi.stubEnv('NODE_ENV', 'production') + expect(process.env.NODE_ENV).toBe('production') + const rootRoute = createRootRoute({ component: Outlet }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
    Index
    , + }) + const error = new Error('onEnter failed') + const onEnter = vi.fn(() => { + throw error + }) + const nextRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/next', + onEnter, + component: () =>
    Next
    , + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute, nextRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + render() + expect(await screen.findByText('Index')).toBeInTheDocument() + await waitFor(() => expect(router.state.status).toBe('idle')) + + const globalReport = vi.fn() + const preventGlobalReport = (event: ErrorEvent) => { + if (event.error === error) { + globalReport() + event.preventDefault() + } + } + window.addEventListener('error', preventGlobalReport) + testCleanups.push(() => { + window.removeEventListener('error', preventGlobalReport) + }) + + let settled = false + const navigation = router.navigate({ to: '/next' }).then( + () => { + settled = true + }, + () => { + settled = true + }, + ) + await waitFor(() => expect(settled).toBe(true)) + await navigation + + expect(onEnter).toHaveBeenCalledOnce() + expect(globalReport).not.toHaveBeenCalled() + expect(screen.getByText('Next')).toBeInTheDocument() + expect(router.state.status).toBe('idle') + + await router.navigate({ to: '/' }) + expect(await screen.findByText('Index')).toBeInTheDocument() }) test('same-location invalidation resolves after its refreshed DOM commits', async () => { diff --git a/packages/react-start-client/src/hydrateStart.ts b/packages/react-start-client/src/hydrateStart.ts index 22f4848de7..21f1fa2039 100644 --- a/packages/react-start-client/src/hydrateStart.ts +++ b/packages/react-start-client/src/hydrateStart.ts @@ -4,9 +4,6 @@ import type { AnyRouter } from '@tanstack/router-core' /** * React-specific wrapper for hydrateStart that signals hydration completion */ -export async function hydrateStart(): Promise { - const router = await coreHydrateStart() - // Signal that router hydration is complete so cleanup can happen if stream has ended - window.$_TSR?.h() - return router +export function hydrateStart(): Promise { + return coreHydrateStart().finally(() => window.$_TSR?.h()) } diff --git a/packages/react-start-client/src/tests/hydrateStart.test.ts b/packages/react-start-client/src/tests/hydrateStart.test.ts index 0610a8fa0c..2d34400009 100644 --- a/packages/react-start-client/src/tests/hydrateStart.test.ts +++ b/packages/react-start-client/src/tests/hydrateStart.test.ts @@ -21,3 +21,13 @@ test('signals streaming cleanup after hydration succeeds', async () => { await expect(hydrateStart()).resolves.toBe(router) expect(hydrated).toHaveBeenCalledTimes(1) }) + +test('signals streaming cleanup without hiding a hydration failure', async () => { + const error = new Error('hydration failed') + coreHydrateStart.mockRejectedValue(error) + const hydrated = vi.fn() + window.$_TSR = { h: hydrated } as any + + await expect(hydrateStart()).rejects.toBe(error) + expect(hydrated).toHaveBeenCalledTimes(1) +}) diff --git a/packages/router-core/INTERNALS.md b/packages/router-core/INTERNALS.md new file mode 100644 index 0000000000..0335da541a --- /dev/null +++ b/packages/router-core/INTERNALS.md @@ -0,0 +1,1317 @@ +# Match loading internals + +This document describes the architecture that loads route matches on the +client, on the server, and across hydration. It is for maintainers of router +core and the framework adapters. + +Match loading coordinates several asynchronous flows, but its runtime +coordination state is limited to actual writers and owned resources. Phase names +and invalid transitions belong in TypeScript whenever possible; they do not +justify a second runtime state machine. + +All `_`-prefixed fields mentioned here are internal. Their spelling and shape +may change, but the ownership rules in this document must continue to hold. + +## Vocabulary and the short version + +- A **match** is one route at one concrete set of path params, search-derived + loader dependencies, and loader/cache identity. +- A **lane** is one location plus its ordered array of matches. +- A **generation** is one concrete match result for a match ID. Several + generations can share an ID while holding different loader data or ownership; + a newer one can exist without being the one currently displayed. +- A **loader flight** is one loader invocation together with its promise and + abort controller. `router._flights` lets later consumers find the newest + same-ID flight. +- A **lease** is one match's ownership of a flight. Registry membership makes a + flight discoverable; leases keep it alive. +- **Semantic state** is the accepted lane used for reuse and lifecycle + decisions. **Presentation state** is the lane currently exposed to rendering + and selectors. +- A **cutoff** is the end of the match prefix allowed to render or contribute a + particular output. The first pending or terminal boundary normally sets it, + even though later matches remain structurally present. +- To **publish** is to replace router state that renderers or users can observe. + An **identity check** means that publication is allowed only while the exact + owner or base reference used to produce the result is still current. + +The normal client flow is: + +```text +match a private lane + -> build context and run beforeLoad parent-first + -> run eligible loaders and component readiness work + -> select one final outcome and derive assets + -> publish only if the navigation still owns the result +``` + +Preloads run the same work without publishing a lane. Server loading uses a +request signal instead of a client navigation owner. Hydration reconstructs the +accepted server prefix, then hands any remaining client work to the initial +client load. + +## The architectural rule + +For each mutable result, there must be one answer to each of these questions: + +1. Who may publish it? +2. Who owns the work and cancellation signal that produce it? +3. What proves that the answer is still current after an `await`? + +Do not add a flag, counter, copied deadline, or second completion promise merely +to describe an existing fact. Add runtime state only when it removes an +independent writer, resource owner, or invalid transition. + +The main authorities are: + +| Authority | What it owns | +| ---------------------------- | ------------------------------------------------------------------- | +| `_tx` | The client navigation allowed to commit and publish foreground flow | +| `_preflight` | The current client plan or asynchronous hydration reconstruction | +| `_handoff` | The temporary right to transfer one reconstructed SSR prefix | +| `_committed` | The accepted current lane and lifecycle/background identity base | +| `stores.matches` | The current match presentation exposed to renderers and users | +| `router._cache` | Off-screen loader generations preferred during same-ID planning | +| Active preload entry | Cancellation, cache clearing, and private redirect-chain ownership | +| Loader flight registry entry | The latest same-ID loader generation available to new consumers | +| Match flight lease | Ownership keeping that loader work alive | +| Pending session | One reveal/minimum-visible deadline and its current owner | +| React acknowledgement slot | The one requested publication whose render may settle a transition | +| Refresh transaction | Its starting presentation, handoff, and ability to roll back | +| Request signal | Lifetime of one server request and any accepted SSR stream | +| Accepted SSR stream response | Cleanup ownership transferred from the handler to the response body | + +These authorities are related, but none is a substitute for another. In +particular, presentation is not semantic authority, registry membership is not +resource ownership, and a promise settling is not permission to publish. + +## Code map + +- `src/router.ts` matches locations, creates match objects, owns public router + state, history, cache operations, and entry points into loading. +- `src/stores.ts` reconciles route-keyed presentation stores and their ordered + aggregate. +- `src/load-client.ts` owns client planning, transactions, preloads, loader + flights, lazy route and component readiness, reduction, pending presentation, + background reloads, commits, and hydration reconstruction. +- `src/load-server.ts` runs the request-local server lane. +- `src/ssr/createRequestHandler.ts` connects request lifetime, server loading, + dehydration, redirects, rendering, and cleanup. +- `src/ssr/handlerCallback.ts`, `src/ssr/ssr-server.ts`, and + `src/ssr/transformStreamWithRouter.ts` transfer stream ownership and + coordinate serialization, injection, abort, and cleanup. +- Framework `Transitioner` and `Matches` implementations acknowledge exact + publications and render only through the selected boundary. Framework + `RouterClient` and render-to-stream implementations complete hydration and + connect request abort to their renderer. + +## Semantic state and presentation state + +The rewrite deliberately separates two views of matches. + +### Semantic matches + +`_committed` is the accepted current semantic lane. It supplies lifecycle +identity and is the exact base checked before a background publication. Pending +presentation never replaces it as a planning base. + +For an individual match ID, matching first consults `router._cache` and then +falls back to `_committed`. A cached loader generation can therefore shadow the +currently displayed same-ID generation for future planning without becoming +current presentation or lifecycle authority. + +Semantic matches may own loader-flight leases. They are not mutated by losing +transactions or by pending presentation. + +### Presented matches + +`stores.matches` is the array visible to router state and framework stores. +Before `pendingMs` expires it can still contain the source presentation. Once +pending presentation is published, it contains the whole destination lane, +including descendants that are still loading. Terminal publication follows the +same membership rule: an error is retained at the throwing match, while +not-found is moved to its selected boundary, but neither removes +structurally matched descendants. The framework renderer derives its cutoff +from the first pending or terminal boundary instead of requiring core to hide +descendants. + +The render cutoff also bounds route response headers, SSR manifest assets, +dehydration, and cache exclusion during commit. Framework construction of head +and scripts uses the closely related asset cutoff. It normally stops at the +same boundary, with one selective-SSR hydration exception: a verified +`'data-only'` prefix can carry `_assetEnd`, allowing already-projected assets +from verified descendants to remain active past a pending render boundary until +the client continuation commits. A structural descendant below the relevant +cutoff remains observable in state, but it cannot contribute that output or +evict a newer cache generation merely because it is present in the lane. + +Start's static early hints are another deliberate exception. They are +speculative route-tree hints emitted before loading selects a terminal boundary, +and an already-sent 103 response cannot be retracted. + +The presentation pool is keyed by route ID, not match ID. `stores.ids` defines +active membership and order, while `stores.getMatchStore` obtains the one stable +mutable atom in `stores.byRoute` for a route. That atom contains the route's +presented match or `undefined`. An active route branch contains a route at most +once, so this shape cannot alias two visible matches. When params, search, or +loader dependencies produce a new semantic match ID for the same route, +reconciliation replaces the value in that route's existing atom. Leaving a +route tombstones that atom with `undefined`; re-entering fills the same atom +again. Route components and `useMatch({ from })` therefore keep one subscription +across match generations and A-to-B-to-A membership changes. `ids` is published +before departure tombstones so framework trees stop reading a leaving route +before its atom is cleared. The pool retains atoms for route IDs encountered +during the router's lifetime; it is not an LRU or a cache of match generations. +Semantic caches and loader flights remain keyed by match ID and must not use the +presentation pool as their authority. + +This distinction matters for user code. Once the destination is presented, a +selector can inspect every destination match and observe `isFetching` while the +renderer still shows only the valid prefix. Before that publication, same-ID +matches in the source presentation can expose updated fetching state, but a new +destination-only match is still private. + +Pending entries are flight-free snapshots. They may copy loader data and context +for presentation, but they never become a planning base and never own semantic +resources. + +### Location state + +`stores.location` is the requested location. It can advance before loading +finishes. For foreground client navigation, `stores.resolvedLocation` advances +only after the framework transition acknowledgement settles. Settlement, not a +`true` render result, is what permits navigation completion. Router `status` is +`pending` during that interval and returns to `idle` at completion. A `true` +result separately permits `onRendered` and pending minimum timing. Server +publication and hydration perform their request/initial-load handoffs directly, +as described below. + +### Canonical locations and rewrites + +Initial client and server canonicalization compares `publicHref`, the +browser-facing URL produced by the rewrite contract. Parsed semantic `href` and +rebuilt `href` are not necessarily symmetric: input and output rewrites run in +opposite directions. The client ignores a trailing-slash-only difference while +the server can redirect to the exact browser-facing canonical URL. + +## A lane and its phases + +A lane is one location plus an ordered array of work matches. Its phase is +encoded in TypeScript: + +```text +matched -> contextualized -> reduced -> projected +``` + +### Matched + +Matching establishes route order, params, validated search results, loader +dependencies, match IDs, initial status, and possible semantic reuse. It does +not run `beforeLoad` or grant publication authority. + +A match ID identifies loader/cache compatibility. It is derived from route +identity, interpolated path params, and serialized `loaderDeps`. Other search +values affect loader identity only when the route includes them in `loaderDeps`. + +Matching treats `params.parse`, `validateSearch`, and `loaderDeps` as pure +planning functions. For the same input they must return the same value without +navigating or mutating router/application state. A `loaderDeps` result and its +serialization hooks must also be side-effect-free. These callbacks may be +evaluated more than once; supporting reentrancy from them would add runtime +ownership checks to every planning step without representing a supported use. + +### Contextualized + +Contextualization walks parent to child. For each route it: + +1. computes route context from the completed parent context, +2. handles params/search validation, +3. runs the route's `beforeLoad`, when defined, and +4. merges the result before moving to the child. + +This serial order guarantees that child route context, child `beforeLoad`, and +child loader context cannot observe a partially completed parent guard. + +Route context is synchronous in the public type contract. Its own contribution +is cached on the match as `_ctx`, and match identity includes route identity, +path params, and `loaderDeps`. A same-ID cache hit may therefore reuse that +route-local result, including one produced by a completed preload. The merged +context is still rebuilt parent-first from the current parent's merged context, +the route's `_ctx`, and the current `beforeLoad` result. Reusing `_ctx` must never +reuse an older merged context or `beforeLoad` contribution. + +Server requests have fresh matches and execute route context normally. +Hydration executes each accepted route context locally, stores its `_ctx`, and +then merges transported `beforeLoad` output. Every normal client lane, +including every preload, performs its own contextualization and `beforeLoad` +chain. + +### Reduced + +On the client, eligible loaders and normal component chunks start concurrently. +On the server, loaders reduce before normal render chunks are consumed. +Reduction turns their outcomes into one terminal semantic lane and one cutoff. +No task publishes while reduction is in progress. + +### Projected + +After semantic reduction, client asset hooks derive `meta`, `links`, styles, and +scripts from the final lane. Server projection additionally derives response +headers. Projection cannot replace the selected loader/before-load result. + +Only an owner that remains current after projection may publish. + +## Client planning and the single writer + +Planning is intentionally separate from transaction installation. Lifecycle +events and route execution callbacks can synchronously reenter the router. +Planning callbacks used for params, search, and loader-key derivation are the +pure functions described above and do not create a second reentrancy boundary. + +A planning controller is installed before `onBeforeNavigate`, `onBeforeLoad`, +and matching, invalidating an older synchronous plan. The planner checks its +authority after supported reentrant callbacks and before installing a +transaction. A stale plan exits without installing a transaction or altering +semantic state. + +Once planning succeeds, the router installs one `_tx`. The transaction owns: + +- the destination and its private matches, +- one lane cancellation controller, +- `done`, the transaction-completion promise used by current transaction + waiters and by React to retain the Suspense source tree while a published + pending match is still loading, +- redirect depth for the chain currently being executed, and +- any pending session transferred to it. + +`LoadTransaction`, the lane execution options, and the pending session are +labeled TypeScript tuples. They are deliberately not runtime state machines: +the labels make each slot type-safe for maintainers, while the compact runtime +shape avoids repeating property names throughout the client loader. Changing a +slot means updating the tuple declaration and every typed consumer together; +it must not introduce a second owner or completion signal. + +Throwing `done` from a pending React match does not stop the transaction's work. +Once the lane has reduced and projected, its successful destination is +published inside the framework transition; that render acknowledgement then +allows `done` to settle. `_commitPromise` is the internal promise backing public +history/navigation completion. Current completion resolves it, and a +superseding transaction can chain it forward, but it is also not permission to +publish. These promises describe different wait relationships; `_tx` remains +the only client writer authority. + +Redirect depth transfers only through the exact `_pendingLocation` created by +`followRedirect`. It is not inherited from the previous transaction: a user +navigation that reenters during a redirect starts a fresh chain even though the +redirecting transaction is still alive. + +The latest `_tx` remains installed after it settles. Its presence is writer +identity, not a loading flag; public loading state comes from router `status` +and per-match `isFetching`. + +Installing a successor removes the predecessor's publication authority. The +predecessor must still settle and release everything it owns; cancellation is a +liveness mechanism, not a license to abandon cleanup. + +Commit and cache handoff install the accepted semantic/cache recipients before +releasing replaced resources. Releasing the last flight lease aborts a public +signal and can synchronously reenter user code, so an old generation must never +be released while it still appears to be the accepted owner. The committed lane +is also removed from the transaction's private ownership before publication. + +Every asynchronous navigation or presentation publication checks `_tx` +immediately before the write. Background publication additionally checks the +exact committed base array from which it was derived. Preload cache admission +and private redirect continuation use their own identity and controller checks +instead. + +## `beforeLoad`: execution and hydration + +`beforeLoad` context is not a cache. + +A completed client preload never stores reusable `beforeLoad` output. When its +loader data enters the route cache, the merged context is discarded; the +same-ID route-local `_ctx` may remain reusable. A later navigation rebuilds the +merged context from the current parent and `_ctx`, then reruns `beforeLoad`, even +when it reuses completed loader data. + +There is one deliberate exception: hydration. + +### Client lanes + +Every navigation and every preload rematches and runs its own serial +`beforeLoad` chain. This remains true for identical concurrent preloads and for +a navigation that targets a still-running preload. A call made with +`preload: true` is never accepted as the navigation's call with +`preload: false`; that call's context and control or terminal outcome stay +private to its speculative lane. This does not isolate the normalized outcome of +a same-ID loader flight that multiple lanes deliberately share. + +`router._preloads` has no semantic adoption role. Its controller entry owns +cancellation, invalidation, and cache clearing and proves that a standalone +preload remained active through normal settlement; successful removal authorizes +private redirect continuation. Its live lane signal is also the final authority +for cache publication, including the microtask window after a loader outcome has +fulfilled. Loader results remain independently reusable: a completed preload may +seed the match cache, and a still-running preload may donate its same-ID loader +flight after the receiving lane has run its own `beforeLoad` and made its reload +decision. + +### Hydration + +The server-resolved prefix is authoritative for the initial document. Hydration +therefore restores transported `beforeLoad` output for the accepted prefix +without rerunning it on the client. This applies to the server-rendered prefix +in selective SSR; the unresolved client suffix follows normal navigation +rules. + +Hydration is not a general `beforeLoad` cache. Its temporary handoff is valid +only for the initial client load of the same document entry and exact committed +owner; rejection, invalidation, or any later load returns to normal serial +execution. The claim also requires the same raw browser href and history-state +object. Finish-time match-ID validation proves that rematching still produced +the accepted prefix; opaque router context and route-tree objects are not +compared. That initial load may transfer the accepted hydration prefix and keep +its transported context while it completes a selective-SSR suffix. A preload +never claims this prefix. Frameworks must start the initial client load before +descendant route code can preload; invoking a preload in the gap after raw +`hydrate()` and before that load is outside the supported handoff protocol. + +## Loader data, cache entries, and flights + +Loader data is designed to be reusable. It is independent from `beforeLoad` +provenance. + +### Completed cache entries + +Successful loader-backed matches can be cached by match ID. Staleness, +invalidation, `shouldReload`, stale reload mode, and GC policy decide whether a +lane uses that data or requires a loader generation. A discoverable same-ID +flight may satisfy that requirement, including when `shouldReload` returns +`true`. + +It is valid for cached loader data to have been produced under context from an +older `beforeLoad` generation. Loaders are the cache boundary; guards are not. +Likewise, a shared in-flight invocation may have started with another lane's +older context. Only a newly started loader invocation receives the current +lane's freshly built context. + +An invalid successful entry may remain in the cache as stale data and preserve +its generation identity, but it can never satisfy freshness and must reload. +Failed, canceled, loaderless, and expired generations do not become reusable +loader-cache entries. + +A terminal preload lane can still contain independently successful loader +generations when its error or not-found came from `beforeLoad`, validation, or +another route. Each preload loader success attempts cache admission immediately, +before whole-lane reduction. The cache receives a non-terminal copy and an +additional flight lease; the speculative lane keeps its own lease and terminal +meaning until it is discarded. This works even below the eventual render +boundary and does not preserve the speculative parent chain: merged +context and `beforeLoad` output are cleared. Same-ID `_ctx`, loader identity, +and successful loader data remain reusable by design. + +Hydration retry is the transported-work exception because it did not run those +client loader tasks. There, `loaderData` membership together with +`invalid === false` proves a transported successful generation even when +terminal boundary state is attached to the match. Hydration normalizes that copy +before passing it through the same cache identity and lease rules. + +The dehydrated payload omits `loaderData` both when no loader result exists and +when the accepted result is `undefined`. This deliberately keeps the HTML +payload smaller at the cost of making those states indistinguishable after +transport. Reconstruction preserves the absence and does not treat an omitted +value as reusable loader success during hydration retry. + +The cache may deliberately contain a successful generation with the same match +ID as a committed match. For example, a speculative lane can produce reusable +ancestor loader data before failing below it while the older committed +generation remains visible. Cache-first matching lets the next lane use that +newer loader generation. Its merged context and `beforeLoad` contribution have +been removed; the merged chain is rebuilt from current parents and same-ID +route-local context before `beforeLoad` reruns. + +Commit removes a cache entry when the accepted render prefix contains that ID, +or when a successful match anywhere in the committed lane contains it. A +non-success descendant below the render cutoff is only structural membership; +it must not evict a newer same-ID cache generation. + +### Same-ID in-flight work + +`router._flights` is the only registry from which a new consumer discovers +same-ID loader work. Every new loader generation registers there, whether it +was started by navigation, preload, or background refresh. A newer generation +replaces the registry entry synchronously. A flight has its own abort +controller; it does not use one consumer transaction's controller as its +lifetime. + +Two facts must remain separate: + +- registry membership means new consumers may join the flight; +- a match lease means an existing consumer keeps the flight alive. + +Registry membership normally owns no lease. A successfully settled generation +may remain discoverable while at least one semantic or cached match owns it. A +non-success outcome removes the exact current flight from the registry as it +settles; existing leases keep that outcome alive only for consumers that already +joined, while later planners start a fresh generation. Releasing the last lease +removes a successful generation only if it is still the current registry entry, +then aborts its controller. Every copied semantic match that retains a flight +must acquire a lease, and every discarded match must release one exactly once. + +There is one short exception to normal zero-lease cleanup. When one navigation +replaces another with the same match ID, the predecessor's loader flight can +reach zero leases before the successor finishes `beforeLoad` and decides whether +to reuse it. While the current transaction is visibly running `beforeLoad` for +that same-ID successor, the flight remains discoverable. The same grace period +applies if any other same-ID flight loses its final lease during that phase. +Outside this phase, merely having a current `_tx` is not enough to retain a +zero-lease flight. + +Loader planning ends the grace period synchronously. The successor either +acquires the discoverable flight or one sweep removes and aborts it. A lane with +no `beforeLoad` reaches loader planning before contextualization yields; an +asynchronous `beforeLoad` keeps the grace period visible until it settles. +Preloads neither create nor sweep this navigation-only reservation. An explicit +`shouldReload: false` declines the flight, while invalidation removes discovery +for selected IDs before starting the replacement load. No extra flag, counter, +or completion promise represents the grace period. + +Releasing a set of matches is deliberately two-phase. First every outgoing +match drops its `_flight` lease and every zero-owner generation not reserved by +the current transaction is removed from the discovery registry. Only after the +entire outgoing set is detached are the collected flight controllers aborted. +Do not interleave one flight abort with detaching later matches in the same +replacement: an abort listener can synchronously reenter, and that load must +observe every logically removed lease and registry entry as already gone. + +A successful accepted match may keep its lease after the loader promise has +settled. This keeps the loader's public `AbortSignal` alive for that semantic +generation. The signal aborts when the last active or cached owner is replaced, +unloaded, expired, or discarded; promise settlement alone does not end it. + +Loader error normalization, including route `onError`, runs once while the exact +flight still has a match lease. The terminal flight leaves the discovery +registry before normalization, so a navigation reentered by `onError` starts a +fresh generation. Releasing every match before a late rejection makes the +generation semantically discarded; abort-triggered rejection must not call user +error hooks. This is an ownership check, not a separate cancellation flag. A +loader that aborts its own flight controller while its match remains owned can +still fulfill or reject normally. + +A planned match holds only the lease for the accepted generation copied from +cache or committed state. After `beforeLoad` and `shouldReload` decide that a +loader will run, it may synchronously acquire the registry's latest different +same-ID generation. It never reacquires its own accepted generation merely to +avoid a requested reload. A blocking reload replaces the accepted lease with +the donor; a background reload keeps accepted data visible and gives the donor +lease to its private candidate. This one lookup covers work started by active +preloads, navigation, and background refresh without scanning those owners. + +Active preload flights need no special handoff. Their speculative matches keep +positive leases until the preload settles or is canceled, so another lane can +discover and acquire the flight without adopting the preload lane. + +Consumers already joined to one loader flight observe its single normalized +outcome, including error and not-found. Per-lane cancellation can stop only that +consumer's wait. A non-success flight retires from discovery at settlement, so +only lanes planned afterward retry. Redirect remains control flow and is never +cached as loader data. + +### Semantic parent chain + +`parentMatchPromise` represents the semantic parent generation, not merely the +currently displayed parent. + +This distinction is essential for mixed reload modes. If a parent is refreshing +in the background while a child reloads in blocking mode, the child borrows the +fresh parent candidate. The final lane must not combine fresh parent data with +child data derived from the stale visible parent. + +The same semantic-parent chain is used by blocking and background loader work. +Task arrays track readiness/outcomes; they are not a second parent authority. + +### Components and lazy route options + +There is no router-level component-promise cache. The browser module cache and +framework lazy-component machinery already cache loaded JavaScript. + +Lazy route loading retains only the authority needed to install lazy route +options, ignore obsolete HMR settlements, and retry failed imports. It is not a +general JavaScript-module cache. + +Lazy option installation has one route-owned promise. Success installs options +only while that promise is still the route's owner. Rejection clears the owner +so a later load can retry, and development refresh can clear ownership so an +obsolete import cannot install options afterward. + +Normal component readiness is part of route readiness, not merely an asset +prefetch side effect. Client loader and normal component work may run in +parallel, and a blocking match becomes successful only after both are ready. +When a client lane installs a `pendingComponent` from lazy options, that +component wakes pending selection once it is itself ready, without waiting for +the normal component. This does not require retaining a component promise on +the match: the route's lazy-option owner and the framework/module loader provide +the necessary work identity. + +Client and server not-found boundary searches settle lazy options on each +candidate route before testing for `notFoundComponent`. A lazy rejection while +locating the boundary does not replace an already selected not-found, but +cancellation or request abort still stops the search. The selected terminal +boundary component is then loaded best effort. + +## Outcomes and failure selection + +Internal work normalizes returned and thrown values into a small closed set: + +```text +success | error | not-found | redirect | canceled/skipped +``` + +Redirect and cancellation are control flow, not committed match statuses. +Error and not-found are terminal semantic outcomes assigned once during +reduction. + +Returned and thrown redirects/not-founds normalize identically. Only an error +invokes route `onError`; if `onError` throws, its value is normalized again and +may itself become an error, not-found, or redirect. +Router cancellation and request abort bypass `onError`. Aborting the +`AbortController` exposed to a loader is not by itself proof that the router +discarded the work: a still-owned loader may fulfill or reject afterward, and +that settlement is normalized normally. On the client, zero flight leases prove +that an aborted generation was discarded. On the server, the request signal +proves request cancellation, while the already selected failure/control outcome +proves that an aborted descendant is obsolete. The client calls a discarded +non-result `canceled`, while the server calls it `skipped`; neither is a +publishable terminal state. + +The client and server use the same settlement order and renderable-ancestor +rules. + +### Serial phase + +Route context, validation, and `beforeLoad` run parent-first. The first terminal +serial outcome stops descent and wins over later loader or chunk work when the +ancestors needed to render its boundary remain usable. + +An error from the serial phase allows loaders strictly above the throwing route +to finish. A serial not-found allows work through its effective ancestor +boundary, but never past the throwing route. A serial redirect or cancellation +starts no loader work. + +### Parallel loader phase + +Eligible loaders start concurrently. The first loader error or not-found to +settle becomes the provisional failure. The choice follows promise settlement +order, not route order or boundary depth. After settlement, the required render +prefix is checked root-to-leaf. The first failed ancestor without its own accepted +`loaderData` property replaces a deeper failure because that deeper boundary is +not reachable. Locally retained loader data, including an accepted `undefined`, +keeps the ancestor renderable with stale data and preserves settlement +chronology. An `undefined` result reconstructed from SSR is intentionally absent +under the transport policy above. + +A redirect is control flow and wins even after an ancestor loader has already +failed. Reduction therefore waits for started descendant loader work to reveal +a redirect, including a descendant already refreshing in the +background. An error or not-found does not cancel already-started descendants +before this selection completes. Once all relevant work has settled, the first +such failure is used only if no redirect won. The client retains the full +structural branch and releases only work that no accepted semantic or background +candidate owns; the server may abort work below the selected boundary. + +Loader settlement does not immediately make each error or not-found terminal. +As an internal staging state, a failed attempt is reset to `status: 'success'` +and marked `invalid: true`. This lets already-started descendants settle, allows +a descendant redirect to remain control flow, and gives reduction one place to +choose the terminal failure that will be published. Reduction then installs the +selected error or not-found on its boundary. A failed ancestor without accepted +loader data replaces a deeper failure because that deeper boundary cannot +render. Every non-selected failed attempt stays invalid and must reload rather +than becoming fresh cache data. Semantic `parentMatchPromise` snapshots still +expose each loader's own outcome to its descendants. + +No error-over-not-found sort is performed. The selected not-found is moved to +its effective not-found boundary; an untargeted not-found searches eligible +ancestors, while a targeted not-found respects its target. + +A global path miss is terminal by `_notFound` even when the selected match +remains successful and has no error attached. An explicit not-found reduced to +root also attaches its error there. Both forms cap rendering and hydration and +produce a 404 response. + +For a fuzzy global miss, synchronous matching installs the best boundary visible +from eager route options. Before contextualization, client and server feed that +fallback through the same lazy-aware ancestor search used for explicit +not-found outcomes. This prevents serial hooks below the effective boundary +from running and prevents loader tasks from starting there, while retaining the +complete structural branch. The historical deepest-route-with-children fallback +still applies when no route supplies a not-found component. When `notFoundMode` +is `'root'`, the search is bypassed but the same execution cap applies at root. + +### Chunk readiness + +Normal route chunks needed before the selected cutoff are awaited. Although the +work may start concurrently, readiness outcomes are consumed root-to-leaf. The +first relevant chunk error replaces a deeper selected serial or loader failure +because that boundary is no longer reachable; a redirect from relevant +readiness remains control flow. + +The resolved boundary is retained while that selected failure remains current. +A later lazy retry in the same lane cannot expand the required prefix after its +readiness has already been consumed. +Terminal boundary-component preloading is best effort during normal loading; it +does not start a second failure-selection algorithm. + +On the client, lazy/chunk readiness starts independently of loader completion +and notifies pending selection when it settles. A `pendingComponent` installed +by lazy options can therefore become the visible boundary while an eager loader +is still unresolved. + +### Projection errors + +Client `head` and `scripts`, plus server `head`, `scripts`, and `headers`, are +decorative with respect to route control flow. They run only after semantic +reduction. Rejections are logged and swallowed; they never replace the chosen +loader/before-load outcome or trigger another boundary-selection pass. + +The implementation should stay this simple. If a proposed fix requires a new +error candidate list, ranking pass, boundary score, or convergence loop, it is +almost certainly rebuilding the discarded complex architecture. + +## Terminal commit and lifecycle + +A successful client lane remains private through projection. At commit, one +framework transition publishes semantic matches, cache changes, and route +lifecycle callbacks. + +The client order is: + +1. publish final matches/cache and run `onLeave`/`onEnter`/`onStay`, +2. emit `onLoad`, then `onBeforeRouteMount`, while the transaction is current, +3. wait for the framework transition acknowledgement to settle, +4. publish `resolvedLocation` and `idle`, +5. emit `onResolved`, and +6. emit `onRendered` only if the acknowledgement was `true` and the same + transaction is still current. + +Each reentrant callback can start another navigation. Checking that the +transaction is still current after each publication boundary suppresses stale +later events. In particular, an `onResolved` navigation suppresses the old +transaction's `onRendered`. + +Route lifecycle callbacks are invoked directly and are expected not to throw. +The coordinator does not carry a second error-handling path for callback +failures. All `onLeave` callbacks run before callbacks for retained or newly +entered routes; the relative ordering of `onEnter` and `onStay` is not a public +contract. + +Server render results are published request-locally and run the documented +route `onLeave`/`onEnter`/`onStay` callbacks against the previous server +generation after final matches have been installed. Server redirects do not +publish a render lane or run those callbacks. + +Terminal outcomes do not change route membership. Client and server state keep +the complete structurally matched branch, lifecycle compares that full branch, +and renderers derive the visible prefix from match status. Projection likewise +stops after the terminal match. SSR transport may serialize only that terminal +prefix because the client can reconstruct hidden structural descendants without +executing them. + +## Pending presentation + +Pending UI is presentation, not partial semantic commit. + +The first unresolved boundary is the only pending candidate. Its route or the +router default must provide a pending component, and its effective `pendingMs` +must allow presentation. Core does not skip an ineligible ancestor to expose an +unrelated deeper fallback. + +A successful route without a loader still participates in chunk readiness and +projection, but is not changed back to pending merely because a descendant has +blocking work. + +When pending is offered: + +- `stores.matches` receives a flight-free snapshot of the complete destination + lane; +- the selected boundary is marked pending; +- descendants remain observable in state; and +- the renderer stops at the boundary. + +There is one pending session in `router._pending` and one absolute deadline: + +```text +reveal deadline -> exact render acknowledgement -> minimum-visible deadline +``` + +The session also remembers the pending component identity. An active client +lane loading a lazy route chunk can install a more specific `pendingComponent` +after the default fallback has already rendered. Once that pending component is +ready, core re-offers the same lane so the framework can replace the fallback +without creating another pending session or deadline. + +The reveal deadline is anchored to the transaction's lane-level `startedAt`. +Discovering lazy pending options later, advancing to another boundary, or +retrying the offer does not restart `pendingMs`. If the absolute deadline is +already past when a pending component becomes eligible, core publishes it in +the current turn instead of introducing a `setTimeout(0)` race. + +`pendingMinMs` starts only after the framework confirms that the pending +publication rendered. A superseded publication that never rendered creates no +minimum-visible obligation. + +Hydration or a redirect can leave an already visible pending presentation +without a pending session that owns its original acknowledgement. On takeover, +core conservatively treats that presentation as rendered and starts its minimum +from the takeover time instead of delaying its reveal again. + +A successor may take over timing only when the boundary index and match ID are +the same. It keeps the existing deadline but republishes a full snapshot from +the successor, so pending UI cannot show stale search, params, or context from +the superseded navigation. Changing the boundary discards the old session. + +## Exact framework acknowledgement + +`startTransition` returns a promise whose boolean result has a precise meaning: + +- settlement means the framework transition can no longer block navigation + completion; +- `true` means the exact requested match publication rendered; +- `false` means core must finish without emitting `onRendered` or starting a + pending minimum based on that publication. + +React cannot await `React.startTransition` directly. Its adapter keeps one +router-owned acknowledgement tuple, and `Matches` settles that tuple from a +layout effect. A new expected publication first settles the previous receipt as +`false`, then stores the exact offered array before the transition callback +runs. This ordering matters because publication can invoke a route lifecycle +callback that synchronously starts and publishes a successor navigation. +Installing the expectation after the callback would let the superseded +publication replace the successor's receipt. + +An already-settled router mounted into React uses the same acknowledgement slot +for its initial `onRendered` event. It therefore emits only after the exact match +tree and its descendant layout effects have committed, rather than from the +earlier history-subscription effect. + +While a receipt is pending, the aggregate match subscription selects the exact +offered array instead of reconstructing an equivalent presentation. The layout +effect acknowledges only that reference. A suspended older render can therefore +never satisfy a newer receipt merely because its IDs or statuses look the same. +No generation counter or structural signature is needed. + +Solid awaits its transition, and Vue awaits its render tick. For an +already-settled Solid mount, history subscription remains before the route tree +while a post-match notifier emits the initial `onRendered` event after +descendant mount effects. + +React assumes one provider and one router. Keeping the tuple on that router lets +the render tree and core share the exact same receipt without component-local +generation state, structural signatures, or router-swap machinery. + +Every core write passed to `startTransition` must notify the aggregate matches +store even if much of the lane is structurally reused. Suppressing the write can +strand the acknowledgement promise. + +## `isFetching` and background reloads + +`isFetching` is public presentation state. It is observable during normal +`beforeLoad`, normal loaders, and background loader refreshes. + +For a foreground navigation this means the destination match exposes its phase +once the destination lane is presented. Before pending publication, a +destination-only match remains private; a same-ID source match can be reconciled +to the active phase earlier. Background reloads operate on the already +presented lane and therefore expose their phase immediately. + +A background reload keeps successful loader data visible while a private +candidate runs. The full presented lane remains installed and the affected +match reports the active phase. Completion clears fetching state whether the +candidate publishes, fails, or is superseded. A successor may join its loader +flight, but never adopts the private candidate lane. + +Background loader and chunk work may begin and settle while the foreground +publication renders. Background reduction, projection, and publication do not +start until the foreground transition acknowledgement settles. A fast refresh +therefore cannot replace the exact generation that the framework still needs to +acknowledge. + +When background tasks exist, execution creates their settlement observer +eagerly alongside foreground reduction and retains that promise in the lane +result. `runBackground` consumes the same settlement chronology after the +foreground acknowledgement. Recreating the observer then would process work +that already settled by task attachment or iteration order and could change +which error, not-found, or descendant redirect wins. Retaining this promise +preserves the observed settlement order; it does not grant permission to publish +or define when the lane is complete. + +Background work starts from an exact committed base. Before projection it uses a +fully private lane, including clones of untouched matches, so asynchronous asset +hooks cannot mutate committed objects without a store publication. + +Final background publication requires both: + +```text +router._tx is the owner +router._committed is the exact base +``` + +If either check fails, the entire staged lane is discarded and all candidate +and clone resources are released. A successful background publication replaces +the semantic/presented lane atomically but does not change location, foreground +status, or foreground navigation lifecycle events. + +Foreground completion does not join background publication. After the +foreground acknowledgement, the refresh continues independently and may publish +before or after `resolvedLocation`, `idle`, and `onResolved`. Active work +remains publicly observable through `isFetching`; any later publication still +requires the owner/base identity check. + +An error or not-found from background work also stays private through reduction +and projection, then may atomically replace the successful base with the full +matched branch carrying its terminal boundary. Hidden descendants retain route +membership, so background publication does not synthesize leave/enter lifecycle +events. It is not published incrementally. + +Background redirects use the same control-flow and ownership rules as foreground +redirects. A losing background lane cannot redirect. + +## Invalidation, cache clearing, and development refresh + +Invalidation creates a new semantic generation and reloads through the normal +transaction path. It does not turn `stores.matches` into a planning lane. + +Filtered invalidation evaluates committed, cached, active-transaction, and +active-preload matches and collects the selected match IDs. Every committed and +cached generation with one of those IDs is made invalid. Cached matches are +already settled successes, so their data owner is marked invalid in place. +Matching active preload owners are retired first, which prevents older +speculative work from clearing that stale mark or publishing fresh cache data; +the lane signal remains the publication fence if its loader outcome already +fulfilled. The loader discovery entry is also detached before the superseding +load. Unselected preload lanes remain active. This ID-wide rule prevents a +cache-first or in-flight same-ID generation from escaping invalidation merely +because a different generation was the one passed to the filter. Route context +retains same-ID cacheability across this replacement and needs no separate +invalidation marker. + +Invalidated successful data may remain visible until pending or terminal +publication, depending on reload mode. Error/not-found generations reset through +the same loading protocol rather than becoming cache successes. + +Cache clearing first snapshots every selected cache match and active preload so +a throwing public filter changes no authority. It then prunes both authorities +and directly releases every discarded match lease. When the last lease is +discarded, cache clearing removes the discovery entry +instead of preserving the normal zero-owner navigation handoff. Only after all +leases and discovery entries are detached does it abort the collected flight and +preload-lane controllers. A public loader signal can synchronously reenter from +its abort listener; that reentrant load must observe the cleared authorities and +every removed lease as already detached. Unselected concurrent preloads keep +shared flights discoverable, and every later cache publication must still have a +live preload signal and pass the per-match cache-entry identity check captured +during planning. + +Development refresh is deliberately aggressive about reuse. It removes all +loader flights from discovery, discards active preloads and cache entries, and +rematches with committed/cache reuse disabled so obsolete params, context, +loader data, or projected assets cannot seed the refreshed lane. Selected cache +and preload resources are detached before their controllers are aborted. + +Refresh does not immediately discard the accepted committed lane or abort the +loader signals it still owns. The previous semantic lane, presentation, and +their resources remain available to the refresh transaction until the new +publication settles or rolls back. Settlement releases the replaced generation; +rollback restores it. The ability to roll back belongs to that refresh +transaction, not to a separate router-global owner. + +## Speculative preloading + +A preload uses the same match, contextualize, reduce, and project phases as +navigation, but it never becomes `_tx` and never publishes match presentation. + +Its match/cache ownership effects are limited to: + +- joinable same-ID loader flights; and +- individual successful preload loader generations entering the loader cache as + they settle. + +A preload can also install durable lazy route options through the separate +route-chunk owner described above. That is route definition readiness, not +authority over a completed match lane or `beforeLoad` result. + +Preload lane-local matching, route context, validation, cancellation, and +`beforeLoad` outcomes do not become authority for another preload or a later +navigation. Consumers that join its same-ID loader flight nevertheless share +that flight's normalized outcome. After a normal standalone preload redirect, +the private chain continues only if that lane's controller was still present and +was successfully removed from `_preloads`; replacing an unrelated `_tx` does not +suppress it. The chain remains depth-bounded, never follows `reloadDocument`, +and never publishes presentation or history. + +The public `preloadRoute` result describes the speculative lane, not merely its +cacheable subset. An error or not-found therefore resolves with the terminal +match array while any eligible successful loader generations can still enter +the cache. Cancellation or control flow that does not yield a reusable lane can +resolve `undefined`. + +Each preload loader task compares the current cache entry for its match ID with +the entry captured when that task was planned. It cannot overwrite a cache +generation installed since that plan. Admission happens at loader settlement, +without waiting for whole-lane success. A distinct successful generation may +coexist with an older committed same-ID generation; this changes future +planning precedence, not current presentation. A duplicate sharing the already +accepted flight is discarded. + +`preloadRoute` also works on a server router. It runs the same speculative +protocol with `preload: true`, can return matches and populate that router's +loader cache, and does not replace the request's location, committed lane, or +presentation. Normal request loading still uses the request-local server lane +and calls its hooks with `preload: false`. + +## Server loading and request lifetime + +Server loading is request-local, so it does not need the client `_tx` +coordinator. It still uses the same semantic phases, context ordering, outcome +normalization, settlement-order failure selection, semantic parent promises, +and projection behavior. + +Each server match gets the public controller passed to its callbacks. The +request signal—not the mere fact that a callback aborted that controller—is the +request-liveness authority checked across contextualization, loaders, chunk +readiness, terminal boundary readiness, and projection. A request abort cancels +the whole lane. A loader error or not-found does not abort already-started +descendants before selection, so a later descendant redirect can still win. +After selection, applying the terminal boundary aborts the hidden suffix that +the result no longer owns. Redirect aborts the whole request-local lane. + +The request signal also governs the surrounding request pipeline. The generic +handler races manifest lookup, route loading, custom dehydration, and the +handler/render callback. Start applies the same rule to entry and router +resolution, middleware, manifest work, and redirect finalization. An abort can +therefore settle the handler during every awaited phase rather than waiting for +user code that ignores its signal. + +A raced promise may still fulfill after abort. If that late value owns an SSR +stream, the race disposes it instead of allowing it to regain response or +cleanup authority. Other user promises may continue executing, but after abort +they cannot publish a response or inject SSR output. If cleanup occurs while +application dehydration is awaited, dehydration returns before starting +serialization; injection and serialization completion also ignore later work. + +A server result has one of two forms: render status plus matches, or an HTTP +redirect. Redirects short-circuit framework rendering and preserve their real +status, `Location`, and custom headers. + +Projection parity is intentional: server `head`, `scripts`, and `headers` use +the final reduced lane and loader data. As on the client, failures are logged and +swallowed. + +### Stream cleanup handoff + +Until a response is accepted, the request handler owns router SSR cleanup. A +non-stream response, redirect, or failure leaves cleanup with the handler. An +accepted SSR stream transfers that ownership to the response and is immediately +bound to the request signal, so an abort after handoff still disposes it. + +Disposal is idempotent and severs router SSR ownership before best-effort body +cancellation. This order matters because a custom or framework stream may ignore +or indefinitely delay cancellation. Replacing a stream response, resolving a +redirect from it, or stripping a HEAD body disposes the old stream under the +same request signal before accepting the replacement. + +Framework renderers connect request abort to their upstream renderer as well as +the router stream transform. Normal completion, downstream cancellation, +request abort, renderer failure, and stream lifetime timeout all converge on the +same cleanup authority; none creates a second response owner. + +Once a server lane is accepted for rendering, its match controllers are +registered with that same SSR cleanup authority. They remain live through +dehydration and response streaming so deferred loader work can finish while the +response is active, then abort when the response or stream lifetime ends. + +## Selective SSR + +SSR policy is the first parent-to-child serial step, before route context, +params/search validation handling, and `beforeLoad`: + +- `true` runs server `beforeLoad` and loaders, loads render chunks, projects + assets, and renders the component. +- `'data-only'` runs server `beforeLoad` and loaders and projects `head`, + `scripts`, and `headers`, but does not render that route component. +- `false` skips server `beforeLoad`, loaders, and component chunks. The first + `false` boundary still projects `head`, `scripts`, and `headers` for its server + shell, then projection stops before descendants that inherit `false`. Route + context and params/search validation still run, so their errors remain real + server outcomes. + +A parent restriction cannot be relaxed by a child: `false` remains false, and a +`'data-only'` parent caps a child requesting `true` at `'data-only'`. + +If a functional `ssr` option throws, the inherited/default policy is established +before calling it. The failure therefore retains the correct boundary +renderability instead of leaving `ssr` undefined. An error or not-found from +the policy still reconstructs route context for its boundary. If route context +also fails, the original policy failure keeps precedence. Redirects remain +control flow and skip route-context reconstruction. + +Shell mode resolves and dehydrates the root semantic match while the presented +server lane may include the first client-only pending boundary and its +descendants. This permits server and initial client presentation to agree. + +## Hydration handoff + +Hydration reconstructs server work; it does not run a competing hydration +loader. While reconstruction is asynchronous, its controller is `_preflight`. +Under the framework-supported startup order, `RouterClient` finishes hydration +before mounting the provider that starts the initial client load, so no client +transaction or descendant preload competes with reconstruction. Core does +not enforce that ordering as a blanket guard. A navigation can supersede hydration +by installing a new `_preflight`; a preload started directly in this gap is +unsupported rather than blocked. + +The identity of `_preflight` proves that reconstruction is still current. A +replacement installs itself before aborting the prior controller. The same +controller interrupts asynchronous application hydration and chunk work, and +every asynchronous phase checks that identity before mutating or publishing. + +Once hydration has accepted a semantic prefix and is ready to publish it, +`_preflight` is no longer the right authority: no planning operation is in +progress, but the first normal client load may still need to continue that +prefix. Hydration therefore installs `_handoff`, a temporary two-phase transfer, +and detaches its controller from `_preflight`. The handoff is the one owner that +can decide whether the initial load may continue the prefix; it is not a second +completion promise or a general cache. + +The client router is fresh when hydration begins. Application `hydrate` hooks +may restore external integration state or update router options, but must not +call router loading or preloading before reconstruction finishes. Core does not +guard those calls; this is part of the supported startup order. + +The high-level process is: + +1. install serialization adapters and application-dehydrated data, +2. match a fresh candidate lane for the browser location, +3. accept the identity-compatible serialized lane as the ordered prefix + guaranteed by the document protocol, +4. copy server loader data, `beforeLoad` context, terminal state, and effective + SSR policy into private candidates, and install each transported effective + SSR value on its route so a functional server policy is not re-evaluated, +5. start exactly the chunks required by the accepted prefix and any selected + terminal boundary concurrently, then consume their outcomes in route order + so the earliest failed position can retire its suffix without waiting for + irrelevant descendants, +6. rebuild route context parent-first, +7. project client `head` and `scripts` through the same projection + function used after a normal client load, and +8. publish accepted semantic work and the complete structural presentation. + +Hydration relies on the framework transport contract. For normal and selective +SSR, the data was produced for the exact document URL by the same route build, +and its serialized matches form an ordered prefix of the client lane. An SPA +shell uses the same prefix protocol for its root-only payload; the framework is +responsible for serving a shell that applies to the document. Core does not +serialize a second URL identity. Instead, hydration bounds reconstruction to +the local lane and validates every transported position with a compact match ID. + +A mismatch ends the accepted prefix and leaves the local suffix for normal +client loading. A longer server lane is accepted only through a local global +not-found boundary that already caps the branch; otherwise no transported +prefix is accepted. A terminal server error, not-found, or global not-found caps +client execution so omitted descendants do not run. The client still creates +those descendants as structurally matched but unexecuted matches. A loaderless +descendant may initially have `status: 'success'`, so status alone does not prove +that hydration ran its context or route hooks. Terminal hydration loads the +chunks required by the selected error/not-found boundary, but not normal +component chunks below it. Neither route context nor projection runs below the +transported boundary. +The transported terminal route remains authoritative even when its effective +SSR policy is `false` or `'data-only'`: route context or validation was still +allowed to fail on the server, so hydration must not turn that outcome into an +unresolved client-only route. Before-load and loader work remain skipped there +according to the server policy. + +Every executed context, `head`, and `scripts` hook nevertheless receives the +complete locally matched candidate lane. Hook arguments describe structural +membership; the accepted prefix describes execution authority. + +If a required chunk or route-context reconstruction fails, hydration preserves +only the successfully reconstructed committed prefix. The public presentation +still contains the complete locally matched lane, so a +terminal server boundary does not make route membership disappear while its +client reconstruction is retried. Eligible transported loader successes cross +the normal loader-cache boundary with merged context and `beforeLoad` +contribution removed. Because `resolvedLocation` remains unset, normal +initial client loading retries the failed boundary or context and finishes the +unresolved suffix. + +For a non-terminal selective-SSR handoff, the semantic committed prefix is the +complete contiguous transported prefix accepted as resolved. The first +`'data-only'` match is the presentation continuation boundary, but it does not +truncate semantic adoption: later transported successful data-only matches are +also committed and keep their server loader and `beforeLoad` results. A `false`, +pending, or otherwise unresolved match is the first semantic continuation +boundary and is not committed. Presentation can still contain the complete +candidate lane and marks its first presentation boundary pending as needed. In +particular, a shorter non-terminal server payload ending in a pending +`ssr: false` match is a valid selective-SSR handoff: hydration accepts the +resolved ancestors, presents the complete local branch, and lets the normal +initial client load execute from that client-only boundary. + +For a successfully reconstructed terminal handoff, transport remains +prefix-capped but committed and presented membership use the complete locally +matched branch. Matches below the terminal boundary remain unexecuted and +hidden; they own no transported `beforeLoad` result or loader data. This keeps +public membership and lifecycle stable without increasing the SSR payload or +loading unreachable client chunks. If reconstruction fails, only the accepted +prefix is committed as described above; the complete branch remains +presentation, not semantic reuse authority. + +Only the subsequent normal client load may transfer the whole hydration +prefix, and only while the exact committed-prefix owner, raw history-state +object, browser href, and live hydration controller remain current with no +active transaction. Core captures the raw browser href and history-state object +so a reentrant navigation from an initial lifecycle event cannot hand the old +document's work to a successor location. Finish also validates the transported +match IDs against the newly matched lane. Core does not coordinate speculative +work started between raw `hydrate()` and that load; framework adapters own the +supported ordering. + +The two-phase transfer proceeds as follows: + +1. Before public navigation events or matching, the initial client-load planner + probes the handoff without consuming it. +2. It installs its own `_preflight`, emits the events, matches a private lane, + and asks the handoff to finish the transfer. +3. Finish revalidates handoff identity, transaction absence, hydration + controller liveness, captured location identity, and the exact committed + owner. +4. On rejection, the handoff clears itself before aborting its controller, + so abort listeners cannot reenter and claim a rejected handoff. +5. On acceptance, the accepted matches replace the planner's private copies. A + successfully reconstructed terminal handoff owns the complete local branch, + even though its SSR payload was prefix-capped, so it has no local suffix to + remove. For a non-terminal handoff, the remaining suffix stays in the lane + and transfers to the hydration controller so one signal owns the + continuation. +6. The handoff remains available across synchronous reentrancy until the + current load installs `_tx`. That load installs `_tx` before clearing the + handoff; stale planners may release only their own private work. + +This ordering closes the gap between hydration publication and client +transaction installation without making the handoff an independent completion +authority. If reconstruction is superseded before publication, the `_preflight` +identity check aborts its private work. If the published handoff becomes +incompatible, its failed identity check retires the hydration controller +and starts normal client loading from a fresh preflight. + +Generic framework `RouterClient` components signal streaming hydration +completion after the hydration attempt settles, including rejection. This +finally-style handoff allows bootstrap globals to be removed once the server +stream has also ended without stranding the stream on a hydration failure. + +Each framework owns one module-level hydration promise for the document. The +SSR protocol provides one global bootstrap and one `RouterClient`; additional +client-only routers use `RouterProvider` and do not independently consume that +bootstrap. The promise deduplicates framework rendering/replay only. It replaces +neither `_preflight` reconstruction authority nor `_handoff` +continuation authority. + +## Change checklist + +The detailed sections above are the source of truth. Use this shorter checklist +to find the failure modes a change needs to test; do not duplicate the full +algorithm here. + +### Publication and reentrancy + +- Does every asynchronous write recheck its exact owner immediately before the + write: `_tx`, `_preflight`, `_handoff`, an active-preload controller, or the + request signal? +- Does a background publication check both its `_tx` and the exact `_committed` + base from which it was derived? +- If an abort, lifecycle callback, or store publication can synchronously + reenter, are replacement owners installed and discarded resources detached + before that callback can run? +- Does framework acknowledgement settlement still gate resolved/idle + completion, with only an exact rendered publication producing `true` and + enabling `onRendered` or `pendingMinMs`? + +### Matches, loaders, and cache ownership + +- Are `_committed` semantic state and `stores.matches` presentation kept + separate? Are match-ID cache compatibility and route-ID lifecycle identity + also kept separate? +- Does every normal client lane run its own `beforeLoad` chain? Hydration is + the only completed-work exception; preloads may share loader data or flights, + but never their merged context, `beforeLoad` result, or control flow. +- Is flight discovery separate from lease ownership? Does every copied owner + acquire exactly one lease and every discarded owner release it exactly once? +- Are registry entries and all discarded leases detached before any collected + controller is aborted? +- Do cache admission, invalidation, and clearing preserve the intended + generation identity, including the rule that an unexecuted descendant below a + cutoff cannot evict a newer same-ID cache entry? +- Does each child `parentMatchPromise` follow the fresh semantic parent + generation rather than the visible stale parent? + +### Outcomes and presentation + +- Do error/not-found selection, required ancestor readiness, component chunks, + and descendant redirects still reduce to one final outcome? +- Does pending or terminal publication retain the complete structural branch + while rendering and outputs apply their relevant cutoff, including the + selective-SSR hydration asset-prefix exception? +- Are pending snapshots flight-free, and do reveal/minimum timing remain owned + by one transferable pending session? +- Do projection failures remain logged and swallowed, and does `isFetching` + clear on every success, failure, cancellation, and supersession path? + +### Server and hydration lifetime + +- Can request abort settle every awaited server phase, and can late work no + longer publish a response or injected output? +- Does exactly one owner perform SSR cleanup, with router ownership released + before best-effort stream cancellation? +- Does hydration validate transported match IDs, execute only the accepted + prefix, retain the complete local branch, and preserve any verified + `'data-only'` asset prefix? +- Does the initial-load handoff revalidate its controller, browser history entry, + committed owner, and rematched IDs? Do framework adapters still obey the + no-preload startup gap described above? + +If a fix fails one of these checks, consolidate ownership instead of layering a +special case on top. + +## Testing changes + +Tests must assert public behavior, not the internal tuple shape, phase tag, +private promise, timer, or field name used to implement it. + +Useful assertions include: + +- rendered pending/error/not-found content and the boundary that rendered it; +- the complete public matches array and `isFetching` transitions; +- loader/before-load call counts, contexts, parent promises, and abort signals; +- navigation completion, lifecycle callbacks, and their surrounding router + events, without asserting a relative `onEnter`/`onStay` order; +- preload/cache reuse observable through user loader calls; +- HTTP status, headers, redirects, and absence of renderer invocation; +- request-abort settlement, late-stream disposal, and single stream cleanup; +- hydration output, completion on rejection, and absence of client reruns below + server terminal boundaries; +- absence of stale data/assets after supersession; and +- framework acknowledgement tied to the actual rendered destination. + +While `tx.done` suspends a pending React destination, React may retain the previous +source tree in the DOM but hide it. Pending tests should assert visible user +content, not physical absence of the old route's nodes. + +When testing a boundary, give root, parent, and child visibly distinct output. +An assertion such as `/Not Found/` or `/Error/` can pass at the wrong boundary +and is not sufficient. + +Run focused category tests while changing the loader, then affected core and +framework unit/type suites, selective-SSR E2E tests for server/hydration changes, +and the bundle-size benchmark for any client runtime change. diff --git a/packages/router-core/src/Matches.ts b/packages/router-core/src/Matches.ts index 852d186b67..41fd4018b5 100644 --- a/packages/router-core/src/Matches.ts +++ b/packages/router-core/src/Matches.ts @@ -9,7 +9,7 @@ import type { RouteIds, } from './routeInfo' import type { AnyRouter, RegisteredRouter, SSROption } from './router' -import type { Constrain, ControlledPromise } from './utils' +import type { Constrain } from './utils' export type AnyMatchAndValue = { match: any; value: any } @@ -131,47 +131,32 @@ export interface RouteMatch< pathname: string params: TAllParams _strictParams: TAllParams - status: 'pending' | 'success' | 'error' | 'redirected' | 'notFound' + status: 'pending' | 'success' | 'error' | 'notFound' isFetching: false | 'beforeLoad' | 'loader' error: unknown paramsError: unknown searchError: unknown updatedAt: number - _nonReactive: { - /** @internal */ - beforeLoadPromise?: ControlledPromise - /** @internal */ - loaderPromise?: ControlledPromise - /** @internal */ - pendingTimeout?: ReturnType - loadPromise?: ControlledPromise - displayPendingPromise?: Promise - minPendingPromise?: ControlledPromise - dehydrated?: boolean - /** @internal */ - error?: unknown - } loaderData?: TLoaderData + /** @internal Exclusive end of the SSR-verified asset prefix. */ + _assetEnd?: number /** @internal */ - __routeContext?: Record + _ctx?: Record /** @internal */ __beforeLoadContext?: Record context: TAllContext search: TFullSearchSchema _strictSearch: TFullSearchSchema - fetchCount: number abortController: AbortController cause: 'preload' | 'enter' | 'stay' loaderDeps: TLoaderDeps preload: boolean invalid: boolean headers?: Record - globalNotFound?: boolean + _notFound?: boolean staticData: StaticDataRouteOption /** This attribute is not reactive */ ssr?: SSROption - _forcePending?: boolean - _displayPending?: boolean } export interface PreValidationErrorHandlingRouteMatch< diff --git a/packages/router-core/src/await-signal.ts b/packages/router-core/src/await-signal.ts new file mode 100644 index 0000000000..c3506e2abd --- /dev/null +++ b/packages/router-core/src/await-signal.ts @@ -0,0 +1,27 @@ +export function waitForReason( + value: T | PromiseLike, + signal: AbortSignal, + onLate?: (value: T) => void, +): Promise { + const promise = Promise.resolve(value) + if (signal.aborted) { + if (!onLate) { + return Promise.race([Promise.reject(signal.reason), promise]) + } + void promise.then(onLate, () => {}) + return Promise.reject(signal.reason) + } + return new Promise((resolve, reject) => { + const abort = () => reject(signal.reason) + signal.addEventListener('abort', abort, { once: true }) + promise + .then((result) => { + if (signal.aborted) { + onLate?.(result) + } else { + resolve(result) + } + }, reject) + .finally(() => signal.removeEventListener('abort', abort)) + }) +} diff --git a/packages/router-core/src/index.ts b/packages/router-core/src/index.ts index fd673ca410..a296629ffa 100644 --- a/packages/router-core/src/index.ts +++ b/packages/router-core/src/index.ts @@ -103,6 +103,7 @@ export { resolveManifestCssLink, } from './manifest' export { isMatch } from './Matches' +export { _getAssetMatches, _getRenderedMatches } from './load-client' export type { AnyMatchAndValue, FindValueByIndex, @@ -242,7 +243,6 @@ export { SearchParamError, PathParamError, getInitialRouterState, - getMatchedRoutes, trailingSlashOptions, } from './router' @@ -274,9 +274,7 @@ export type { InjectedHtmlEntry, EmitFn, LoadFn, - GetMatchFn, SubscribeFn, - UpdateMatchFn, CommitLocationFn, GetMatchRoutesFn, MatchRoutesFn, diff --git a/packages/router-core/src/isServer/client.ts b/packages/router-core/src/isServer/client.ts index f2f86b6ec7..e12407f409 100644 --- a/packages/router-core/src/isServer/client.ts +++ b/packages/router-core/src/isServer/client.ts @@ -1 +1,2 @@ export const isServer = false +export const loadServerRoute: never = undefined as never diff --git a/packages/router-core/src/isServer/development.ts b/packages/router-core/src/isServer/development.ts index 3632fabf05..e4f13879ad 100644 --- a/packages/router-core/src/isServer/development.ts +++ b/packages/router-core/src/isServer/development.ts @@ -1,2 +1,3 @@ // Development/test mode - returns undefined so fallback to router.isServer is used export const isServer: boolean | undefined = undefined +export { loadServerRoute } from '../load-server' diff --git a/packages/router-core/src/isServer/server.ts b/packages/router-core/src/isServer/server.ts index cfa181440a..d13401a9e9 100644 --- a/packages/router-core/src/isServer/server.ts +++ b/packages/router-core/src/isServer/server.ts @@ -1 +1,2 @@ export const isServer = process.env.NODE_ENV === 'test' ? undefined : true +export { loadServerRoute } from '../load-server' diff --git a/packages/router-core/src/load-client.ts b/packages/router-core/src/load-client.ts new file mode 100644 index 0000000000..83a7697499 --- /dev/null +++ b/packages/router-core/src/load-client.ts @@ -0,0 +1,2651 @@ +// Keep this filename free of a secondary extension so declaration generation +// can rewrite relative imports for both ESM and CJS. +import { isNotFound } from './not-found' +import { isRedirect } from './redirect' +import { getLocationChangeInfo, runRouteLifecycle } from './router' +import { hydrateSsrMatchId } from './ssr/ssr-match-id' +import type { GLOBAL_SEROVAL, GLOBAL_TSR } from './ssr/constants' +import type { AnySerializationAdapter } from './ssr/serializer/transformer' +import type { TsrSsrGlobal } from './ssr/types' +import type { ParsedLocation } from './location' +import type { AnyRouteMatch } from './Matches' +import type { NotFoundError } from './not-found' +import type { + AnyRoute, + BeforeLoadContextOptions, + LoaderFnContext, + RouteContextOptions, + RouteLoaderFn, +} from './route' +import type { AnyRedirect } from './redirect' +import type { AnyRouter } from './router' + +type RouteComponentType = + | 'component' + | 'pendingComponent' + | 'errorComponent' + | 'notFoundComponent' + +export function replaceRouteChunk( + route: AnyRoute, + lazyFn: AnyRoute['lazyFn'], +): void { + route.lazyFn = lazyFn ?? route.lazyFn + route._lazy = undefined +} + +function preloadComponent( + route: AnyRoute, + type: RouteComponentType, +): Promise | undefined { + return (route.options[type] as any)?.preload?.() +} + +function loadComponents( + route: AnyRoute, + onPendingReady?: () => void, +): Promise | undefined { + const component = preloadComponent(route, 'component') + const pending = preloadComponent(route, 'pendingComponent') + const pendingReady = + onPendingReady && pending ? pending.then(onPendingReady) : pending + if (onPendingReady && !pending) { + onPendingReady() + } + if (component && pendingReady) { + return Promise.all([component, pendingReady]).then(() => {}) + } + return component ?? pendingReady +} + +export function loadRouteChunk( + route: AnyRoute, + // `false` waits only for lazy route options, before a boundary is selected. + componentType?: 'errorComponent' | 'notFoundComponent' | false, + onPendingReady?: () => void, +): Promise | undefined { + const afterLazy = () => + componentType === false + ? undefined + : componentType + ? preloadComponent(route, componentType) + : loadComponents(route, onPendingReady) + const current = route._lazy + if (current) { + return current === true ? afterLazy() : current.then(afterLazy) + } + if (!route.lazyFn) { + return afterLazy() + } + + const promise = route.lazyFn().then( + (lazyRoute) => { + // HMR clears the owner before an obsolete import can settle. + if (process.env.NODE_ENV === 'production' || route._lazy === promise) { + const { id: _id, ...options } = lazyRoute.options + Object.assign(route.options, options) + route._lazy = true + } + }, + (error) => { + if (process.env.NODE_ENV === 'production' || route._lazy === promise) { + route._lazy = undefined + } + throw error + }, + ) + route._lazy = promise + return promise.then(afterLazy) +} + +/** Return the structural lane through the first terminal render boundary. */ +export function _getRenderedMatches( + matches: Array, +): Array { + const end = + matches.findIndex( + (match) => match.status !== 'success' || match._notFound, + ) + 1 + return end && end < matches.length ? matches.slice(0, end) : matches +} + +/** Return the lane whose document assets belong to the current presentation. */ +export function _getAssetMatches( + matches: Array, +): Array { + let end = matches.length + for (let index = 0; index < end; index++) { + const match = matches[index]! + // `_assetEnd` is only ever set on hydration presentation clones that are + // `status: 'pending'`, `ssr: 'data-only'`, error-free, and not not-found + // (see hydrate.ts), and commits clear it — so its presence alone is the guard. + if (match._assetEnd !== undefined) { + end = Math.min(end, Math.max(index + 1, match._assetEnd)) + continue + } + if (match.status !== 'success' || match._notFound) { + end = index + 1 + break + } + } + // `end` only ever shrinks to `index + 1 >= 1`, so no zero guard is needed. + return end < matches.length ? matches.slice(0, end) : matches +} + +declare const lanePhase: unique symbol + +type LanePhase = 'matched' | 'contextualized' | 'reduced' | 'projected' + +/** + * Lane matches carry their lane's phase so functions can demand evidence of + * pipeline position (e.g. `commitMatches` only accepts a projected lane's + * matches). The brand is phantom — it never exists at runtime. + */ +type LaneMatches = Array & { + readonly [lanePhase]?: TPhase +} + +type Lane = [ + location: ParsedLocation, + matches: LaneMatches, + background?: Array, + backgroundSettlement?: Promise, +] & { readonly [lanePhase]?: TPhase } + +type MatchedLane = Lane<'matched'> +type ContextualizedLane = Lane<'contextualized'> +type ReducedLane = Lane<'reduced'> +type ProjectedLane = Lane<'projected'> + +const SUCCESS = 0 +const ERROR = 1 +const NOT_FOUND = 2 +// Control outcomes stay contiguous so the hot path can test them together. +const REDIRECTED = 3 +const CANCELED = 4 + +type LoaderOutcome = + | [kind: typeof SUCCESS, data: unknown] + | [kind: typeof ERROR, error: unknown] + | [kind: typeof NOT_FOUND, error: NotFoundError] + | [kind: typeof REDIRECTED, redirect: AnyRedirect] + | [kind: typeof CANCELED] + +type IndexedOutcome = [index: number, outcome: LoaderOutcome, boundary?: number] + +export type LoaderFlight = [ + outcome: Promise, + controller: AbortController, + leases: number, +] + +type WorkMatch = AnyRouteMatch & { + _flight?: LoaderFlight +} + +declare const matchPhase: unique symbol + +/** + * A match whose loader outcome has been applied by `settleInto`, which is the + * sole granter of this brand (phantom, zero-runtime). Consumers that require + * it — e.g. `cacheLoaderMatch` — can only be reached after settlement, so the + * compiler enforces the loader→settle→cache ordering. Sources that arrive + * already settled (dehydrated server data) must cast at a named boundary. + */ +type SettledMatch = WorkMatch & { readonly [matchPhase]: 'settled' } + +export type LoadTransaction = [ + controller: AbortController, + redirects: number, + location: ParsedLocation, + matches: Array, + startedAt: number, + done: Promise, + /** + * Dev-only HMR refresh mode. Presence is the mode flag; a refresh always + * carries the presentation it started from and its optional hydration + * handoff. While a publication awaits acknowledgement, its rollback lives + * with the transaction that owns the publication. + */ + refresh?: [ + presentation: Array, + handoff: NonNullable | undefined, + rollback?: () => boolean, + ], +] + +export type PendingSession = [ + owner: LoadTransaction, + boundary: number, + /** Pending reveal time until acknowledged, then minimum-visible-until time. */ + deadline: number, + timer?: ReturnType, + ack?: Promise, + component?: unknown, +] + +type CoordinatorRouter = AnyRouter & { + /** Active speculative lanes retained for cancellation, invalidation, and cache clearing. */ + _preloads?: Map> + _refreshNextLoad?: boolean + _cancelTransition?: () => void +} + +type PublicationCheckpoint = { + previousMatches: Array + previousPresentation: Array + previousCache: Map + commitPromise: CoordinatorRouter['_commitPromise'] + published: boolean +} + +type LoaderTask = [ + index: number, + outcome: Promise, + chunkFailure: Promise, + candidate?: WorkMatch, +] + +type BackgroundLoaderTask = [ + index: number, + outcome: Promise, + chunkFailure: Promise, + candidate: WorkMatch, +] + +type ExecuteLaneOptions = [ + controller: AbortController, + redirects: number, + isCurrent: () => boolean, + base: Array, + preload?: boolean, + sync?: boolean, + forceStaleReload?: boolean, + resolvedPrefix?: number, + onReady?: () => void, +] + +type ControlOutcome = + | [kind: typeof REDIRECTED, redirect: AnyRedirect] + | [kind: typeof CANCELED] + +type LaneResult = ProjectedLane | ControlOutcome + +function isControl( + result: Lane | ControlOutcome, +): result is ControlOutcome { + return typeof result[0 /* location or kind */] === 'number' +} + +export function waitFor( + value: T | PromiseLike, + signal: AbortSignal, +): Promise { + if (signal.aborted) { + return Promise.race([Promise.reject(signal), value]) + } + return new Promise((resolve, reject) => { + const abort = () => reject(signal) + signal.addEventListener('abort', abort, { once: true }) + Promise.resolve(value) + .then(resolve, reject) + .finally(() => signal.removeEventListener('abort', abort)) + }) +} + +export function getRoute(router: AnyRouter, match: WorkMatch): AnyRoute { + return (router.routesById as Record)[match.routeId]! +} + +function normalize( + value: unknown, + rejected: boolean, + routeId?: string, +): LoaderOutcome { + if (isRedirect(value)) { + return [REDIRECTED, value] + } + if (isNotFound(value)) { + value.routeId ||= routeId + return [NOT_FOUND, value] + } + if (rejected && typeof (value as any)?.then === 'function') { + value = new Error('A Promise was thrown', { cause: value }) + } + return rejected ? [ERROR, value] : [SUCCESS, value] +} + +function normalizeError(route: AnyRoute, cause: unknown): LoaderOutcome { + let outcome = normalize(cause, true, route.id) + if (outcome[0 /* kind */] !== ERROR) { + return outcome + } + try { + route.options.onError?.(outcome[1 /* error */]) + } catch (onErrorCause) { + outcome = normalize(onErrorCause, true, route.id) + } + return outcome +} + +function normalizeLaneError( + route: AnyRoute, + cause: unknown, + options: ExecuteLaneOptions, +): LoaderOutcome { + if ( + options[0 /* controller */].signal.aborted || + !options[2 /* isCurrent */]() + ) { + options[0 /* controller */].abort() + return [CANCELED] + } + return normalizeError(route, cause) +} + +export function navigateFrom(router: AnyRouter, location: ParsedLocation) { + return (opts: any) => + router.navigate({ + ...opts, + _fromLocation: location, + }) +} + +async function contextualize( + router: AnyRouter, + lane: MatchedLane, + options: ExecuteLaneOptions, + end: number, + planSuccessfulLane: () => void, +): Promise { + const [location, matches] = lane + const signal = options[0 /* controller */].signal + const preload = !!options[4 /* preload */] + for (let index = options[7 /* resolvedPrefix */] ?? 0; index < end; index++) { + const match = matches[index]! + const route = getRoute(router, match) + + match.abortController = options[0 /* controller */] + // Contextualization is serial, so the previous match already contains the + // complete parent context for this route. + const parentContext = + matches[index - 1]?.context ?? router.options.context ?? {} + const common = { + params: match.params, + location, + navigate: navigateFrom(router, location), + buildLocation: router.buildLocation, + cause: preload ? ('preload' as const) : match.cause, + abortController: options[0 /* controller */], + preload, + matches, + routeId: route.id, + } + let context = parentContext + try { + let routeContext = match._ctx + if (!routeContext && route.options.context) { + routeContext = match._ctx = + route.options.context({ + ...common, + deps: match.loaderDeps, + context: parentContext, + } satisfies RouteContextOptions) || {} + } + context = { + ...parentContext, + ...routeContext, + } + match.context = context + } catch (cause) { + releaseFlight(router, match) + return [index, normalizeLaneError(route, cause, options)] + } + if (signal.aborted || !options[2 /* isCurrent */]()) { + options[0 /* controller */].abort() + return [index, [CANCELED]] + } + const validationError = match.paramsError ?? match.searchError + if (validationError !== undefined) { + releaseFlight(router, match) + return [index, normalizeLaneError(route, validationError, options)] + } + const beforeLoad = route.options.beforeLoad + if (!beforeLoad) { + continue + } + + const beforeLoadContext: BeforeLoadContextOptions< + any, + any, + any, + any, + any, + any, + any, + any, + any + > = { + ...common, + search: match.search, + context, + ...router.options.additionalContext, + } + + const previousStatus = match.status + if (previousStatus === 'success') { + match.status = 'pending' + } + options[8 /* onReady */]?.() + try { + setFetching(router, match, 'beforeLoad', options[0 /* controller */]) + const result = await waitFor(beforeLoad(beforeLoadContext), signal) + if (!options[2 /* isCurrent */]()) { + options[0 /* controller */].abort() + return [index, [CANCELED]] + } + const outcome = normalize(result, false, route.id) + if (outcome[0 /* kind */] !== SUCCESS) { + releaseFlight(router, match) + return [index, outcome] + } + match.context = { + ...context, + ...result, + } + } catch (cause) { + releaseFlight(router, match) + return [index, normalizeLaneError(route, cause, options)] + } finally { + if (previousStatus === 'success' && match.status === 'pending') { + match.status = 'success' + } + setFetching(router, match, false, options[0 /* controller */]) + } + } + + // Let a synchronous lane claim predecessor flights before this frame yields. + planSuccessfulLane() + return +} + +function releaseOwnedFlight( + router: AnyRouter, + match: WorkMatch, + flight?: LoaderFlight, +): AbortController | undefined { + if (!flight || --flight[2 /* leases */]) { + return + } + if (router._flights?.get(match.id) === flight) { + const current = router._tx + if ( + current && + !current[0 /* controller */].signal.aborted && + !(process.env.NODE_ENV !== 'production' && current[6 /* refresh */]) && + !current[3 /* matches */].includes(match) && + current[3 /* matches */].some((candidate) => candidate.id === match.id) && + current[3 /* matches */].some( + (candidate) => candidate.isFetching === 'beforeLoad', + ) + ) { + // Keep work discoverable only while the current lane is still running + // beforeLoad. Loader planning performs the matching zero-owner sweep. + return + } + router._flights.delete(match.id) + } + return flight[1 /* controller */] +} + +function releaseFlight(router: AnyRouter, match: WorkMatch): void { + const flight = match._flight + match._flight = undefined + releaseOwnedFlight(router, match, flight)?.abort() +} +/** + * Not passing in a `next` ownership recipient + * is equivalent to discarding the match resources + */ +function transferMatchResources( + router: AnyRouter, + previous: Array, + next?: Array, +): void { + const abort: Array = [] + for (const match of previous as Array) { + if (!next?.includes(match)) { + const flight = match._flight + match._flight = undefined + const controller = releaseOwnedFlight(router, match, flight) + if (controller) { + abort.push(controller) + } + } + } + for (const controller of abort) { + controller.abort() + } +} + +function transferPredecessorResources( + router: AnyRouter, + previous: Array, + next: Array, +): void { + const abort: Array = [] + for (const match of previous as Array) { + if (!next.includes(match)) { + const flight = match._flight + match._flight = undefined + if ( + flight?.[2 /* leases */] === 1 && + router._flights?.get(match.id) === flight && + !( + process.env.NODE_ENV !== 'production' && router._tx?.[6 /* refresh */] + ) && + next.some((candidate) => candidate.id === match.id) + ) { + // The successor has not made its same-ID reload decision yet. + flight[2 /* leases */] = 0 + } else { + const controller = releaseOwnedFlight(router, match, flight) + if (controller) { + abort.push(controller) + } + } + } + } + for (const controller of abort) { + controller.abort() + } +} + +function releaseUnownedFlights(router: AnyRouter): void { + const abort: Array = [] + for (const [id, flight] of router._flights ?? []) { + if (!flight[2 /* leases */]) { + router._flights!.delete(id) + abort.push(flight[1 /* controller */]) + } + } + for (const controller of abort) { + controller.abort() + } +} + +function acquireMatchResources(matches: Array): void { + for (const match of matches as Array) { + const flight = match._flight + if (flight) { + flight[2 /* leases */]++ + } + } +} + +function setFetching( + router: AnyRouter, + match: WorkMatch, + value: AnyRouteMatch['isFetching'], + owner?: AbortController, +): void { + match.isFetching = value + if (owner && router._tx?.[0 /* controller */] !== owner) { + return + } + const store = router.stores.byRoute.get(match.routeId) + const presented = store?.get() + if (presented?.id === match.id) { + store!.set({ ...presented, isFetching: value }) + } +} + +function getLoaderContext( + router: AnyRouter, + lane: ContextualizedLane, + match: WorkMatch, + route: AnyRoute, + controller: AbortController, + parentMatchPromise: Promise | undefined, + preload: boolean, +): LoaderFnContext { + const location = lane[0 /* location */] + return { + params: match.params, + location, + navigate: navigateFrom(router, location), + cause: preload ? ('preload' as const) : match.cause, + abortController: controller, + preload, + deps: match.loaderDeps, + parentMatchPromise: parentMatchPromise as any, + context: match.context, + route, + ...router.options.additionalContext, + } +} + +async function loadResource( + router: AnyRouter, + lane: ContextualizedLane, + match: WorkMatch, + route: AnyRoute, + loader: RouteLoaderFn | undefined, + parentMatchPromise: Promise | undefined, + preload: boolean, + owner: AbortController, +): Promise { + const signal = owner.signal + if (signal.aborted) { + return [CANCELED] + } + if (!loader) { + return [SUCCESS, undefined] + } + + let flight = match._flight + setFetching(router, match, 'loader', owner) + try { + if (!flight) { + const controller = new AbortController() + flight = [ + Promise.resolve() + .then(() => + loader( + getLoaderContext( + router, + lane, + match, + route, + controller, + parentMatchPromise, + preload, + ), + ), + ) + .then( + (value) => normalize(value, false, route.id), + (cause) => normalize(cause, true, route.id), + ) + .then((result): LoaderOutcome => { + // The registry controls discovery; leases keep current consumers + // sharing the same terminal outcome. + if ( + result[0 /* kind */] !== SUCCESS && + router._flights?.get(match.id) === flight + ) { + router._flights!.delete(match.id) + if (!flight![2 /* leases */]) { + controller.abort() + } + } + return result[0 /* kind */] === ERROR && flight![2 /* leases */] + ? normalizeError(route, result[1 /* error */]) + : result + }), + controller, + 1, + ] + ;(router._flights ??= new Map()).set(match.id, flight) + } + match._flight = flight + match.abortController = flight[1 /* controller */] + return await waitFor(flight[0 /* outcome */], signal) + } catch (cause) { + if (cause !== signal) { + throw cause + } + releaseFlight(router, match) + return [CANCELED] + } finally { + setFetching(router, match, false, owner) + } +} + +function settleInto( + match: WorkMatch, + result: LoaderOutcome, + preload: boolean, +): asserts match is SettledMatch { + if (result[0 /* kind */] === SUCCESS) { + match.loaderData = result[1 /* data */] + match.error = undefined + match.status = 'success' + match.invalid = false + match.updatedAt = Date.now() + match.preload = preload + } else if (result[0 /* kind */] !== REDIRECTED) { + // Reduction installs only the selected terminal failure. Every other + // settled attempt remains a renderable, stale match in that lane. + match.status = 'success' + match.error = undefined + match.invalid = true + } +} + +export function cacheLoaderMatch( + router: CoordinatorRouter, + match: SettledMatch, + planned: AnyRouteMatch | undefined, +): void { + const current = router._cache.get(match.id) as WorkMatch | undefined + if ( + current !== planned || + router._committed.some( + (candidate) => + candidate.id === match.id && + (candidate as WorkMatch)._flight === match._flight, + ) + ) { + return + } + const cached = { + ...match, + _notFound: undefined, + context: {}, + } as WorkMatch + if (cached._flight) { + cached._flight[2 /* leases */]++ + } + router._cache.set(match.id, cached) + if (current) { + releaseFlight(router, current) + } +} + +function getParentSnapshot( + match: WorkMatch, + outcome: LoaderOutcome, +): WorkMatch { + if (outcome[0 /* kind */] === ERROR || outcome[0 /* kind */] === NOT_FOUND) { + return { + ...match, + status: outcome[0 /* kind */] === ERROR ? 'error' : 'notFound', + error: outcome[1 /* error */], + _flight: undefined, + } + } + return match +} + +function createLoaderTask( + router: AnyRouter, + lane: ContextualizedLane, + index: number, + tasks: Array, + semanticParent: Promise | undefined, + options: ExecuteLaneOptions, +): Promise { + const match = lane[1 /* matches */][index]! + const route = getRoute(router, match) + const preload = !!options[4 /* preload */] + const plannedCacheMatch = preload ? router._cache.get(match.id) : undefined + let configured + let reload = false + let reloadFailure: LoaderOutcome | undefined + try { + if (match.status === 'success') { + configured = route.options.shouldReload + if (typeof configured === 'function') { + configured = configured( + getLoaderContext( + router, + lane, + match, + route, + options[0 /* controller */], + semanticParent, + preload, + ), + ) + } + if (!options[2 /* isCurrent */]()) { + options[0 /* controller */].abort() + reloadFailure = [CANCELED] + } + } + if (!reloadFailure) { + if (match.status !== 'success') { + reload = true + } else { + const staleAge = + options[4 /* preload */] || match.preload + ? (route.options.preloadStaleTime ?? + router.options.defaultPreloadStaleTime ?? + 30_000) + : (route.options.staleTime ?? router.options.defaultStaleTime ?? 0) + reload = !!( + match.invalid || + configured || + (configured === undefined && + Date.now() - match.updatedAt >= staleAge && + (options[6 /* forceStaleReload */] || + match.cause === 'enter' || + options[3 /* base */].some( + (candidate) => + candidate.routeId === match.routeId && + candidate.id !== match.id, + ))) + ) + } + } + } catch (cause) { + match.invalid = true + releaseFlight(router, match) + reloadFailure = normalizeLaneError(route, cause, options) + } + const routeLoader = route.options.loader + const loader = + typeof routeLoader === 'function' ? routeLoader : routeLoader?.handler + let donor = + (!preload || route.options.preload !== false) && + routeLoader && + !(process.env.NODE_ENV !== 'production' && router._tx?.[6 /* refresh */]) + ? router._flights?.get(match.id) + : undefined + if (donor === match._flight || reloadFailure) { + donor = undefined + } else if (donor && !reload && !preload && configured === undefined) { + // Normal cache policy accepts an already-running generation even when this + // lane itself would not have started another loader. + reload = true + } else if (!reload) { + donor = undefined + } + const background = !!( + routeLoader && + reload && + match.status === 'success' && + !preload && + !options[5 /* sync */] && + ((typeof routeLoader === 'function' + ? undefined + : routeLoader?.staleReloadMode) ?? + router.options.defaultStaleReloadMode) !== 'blocking' + ) + const loaded = reload && (!preload || route.options.preload !== false) + const blocking = + loaded && !background && (match.status !== 'success' || !!routeLoader) + const onLazyReady = + route.lazyFn && route._lazy !== true ? options[8 /* onReady */] : undefined + if (loaded && !routeLoader) { + match.invalid = false + match.updatedAt = Date.now() + } + if (donor) { + donor[2 /* leases */]++ + } + if (blocking) { + const acceptedFlight = match._flight + match._flight = donor + releaseOwnedFlight(router, match, acceptedFlight)?.abort() + // A successful route without a loader has no blocking work to present. It + // still gets a task so its chunk and derived assets participate in the + // lane, but putting it back into pending would hide an already-rendered + // ancestor while only a descendant is loading. + if (match.status === 'success') { + match.status = 'pending' + } + options[8 /* onReady */]?.() + } + if (!loaded) { + match.isFetching = false + } + const rawOutcome = reloadFailure + ? Promise.resolve(reloadFailure) + : !blocking + ? Promise.resolve([SUCCESS, match.loaderData]) + : loadResource( + router, + lane, + match, + route, + loader, + semanticParent, + preload, + options[0 /* controller */], + ) + const outcome = rawOutcome.then((result) => { + if (blocking) { + settleInto(match, result, preload) + if (result[0 /* kind */] === SUCCESS) { + if ( + preload && + routeLoader && + !options[0 /* controller */].signal.aborted + ) { + cacheLoaderMatch(router, match, plannedCacheMatch) + } + // A route is renderable only after both its data and normal component + // chunk are ready. Its loader data is already available to descendants. + match.status = 'pending' + } + } + return result + }) + + const rawChunkFailure = waitFor( + Promise.resolve().then(() => loadRouteChunk(route, undefined, onLazyReady)), + options[0 /* controller */].signal, + ).then( + () => undefined, + (cause): IndexedOutcome => [ + index, + normalizeLaneError(route, cause, options), + ], + ) + const chunkFailure = rawChunkFailure.then((failure) => + outcome.then((result) => { + if ( + blocking && + !failure && + result[0 /* kind */] === SUCCESS && + match.status === 'pending' && + options[2 /* isCurrent */]() + ) { + match.status = 'success' + options[8 /* onReady */]?.() + } + return failure + }), + ) + tasks.push([index, outcome, chunkFailure]) + if (!background) { + return outcome.then((result) => getParentSnapshot(match, result)) + } + const candidate: WorkMatch = { + ...match, + status: 'pending', + preload: false, + _flight: donor, + } + match.invalid = false + match.isFetching = 'loader' + const backgroundOutcome = loadResource( + router, + lane, + candidate, + route, + loader, + semanticParent, + false, + options[0 /* controller */], + ).then((result) => { + match.isFetching = false + settleInto(candidate, result, false) + return result + }) + ;(lane[2 /* background */] ??= []).push([ + index, + backgroundOutcome, + chunkFailure, + candidate, + ]) + return backgroundOutcome.then((result) => + getParentSnapshot(candidate, result), + ) +} + +async function getNotFoundBoundary( + router: AnyRouter, + matches: Array, + indexed: IndexedOutcome | undefined, + signal: AbortSignal, + fallback = 0, +): Promise { + const cause = indexed?.[1 /* outcome */][1 /* error or redirect */] as + | NotFoundError + | undefined + let index = cause?.routeId + ? matches.findIndex((match) => match.routeId === cause.routeId) + : (indexed?.[0 /* index */] ?? matches.length - 1) + if (index < 0) { + index = 0 + } + for (let i = index; i >= 0; i--) { + const route = getRoute(router, matches[i]!) + const loading = loadRouteChunk(route, false) + if (loading) { + try { + await waitFor(loading, signal) + } catch (cause) { + if (cause === signal) { + throw cause + } + } + } + if (route.options.notFoundComponent) { + return i + } + } + return cause?.routeId ? index : fallback +} + +function discardBackground(router: AnyRouter, lane: Lane): void { + if (lane[2 /* background */]) { + transferMatchResources( + router, + lane[2 /* background */].map((task) => task[3 /* candidate */]), + ) + lane[2 /* background */] = undefined + } +} + +async function settleTasks( + tasks: Array, + serialFailure?: IndexedOutcome, + redirectTasks?: Array, + gate?: number | Promise, +): Promise { + let loaderFailure: IndexedOutcome | undefined + + try { + await Promise.all( + tasks.map((task) => + task[1 /* outcome */].then(async (outcome) => { + const taskIndex = task[0 /* index */] + if (gate && taskIndex >= (await gate)) { + return + } + if (outcome[0 /* kind */] >= REDIRECTED) { + throw [taskIndex, outcome] as IndexedOutcome + } + if (!loaderFailure && outcome[0 /* kind */] !== SUCCESS) { + loaderFailure = [taskIndex, outcome] + // Every started descendant must settle before an ordinary failure + // wins because a redirect from any of them remains control flow. + await Promise.all( + (redirectTasks ?? []).map((nextTask) => { + if (nextTask[0 /* index */] <= taskIndex) { + return + } + return nextTask[1 /* outcome */].then((nextOutcome) => { + if (nextOutcome[0 /* kind */] === REDIRECTED) { + throw [ + nextTask[0 /* index */], + nextOutcome, + ] as IndexedOutcome + } + }) + }), + ) + } + }), + ), + ) + } catch (cause) { + return cause as IndexedOutcome + } + return serialFailure ?? loaderFailure +} + +async function reduceLane( + router: AnyRouter, + lane: ContextualizedLane, + tasks: Array, + controller: AbortController, + redirects: number, + settlement: Promise, + onReady?: () => void, +): Promise { + const matches = lane[1 /* matches */] + let failure = await settlement + let redirectLimitExceeded = false + const plannedBoundary = matches.findIndex((match) => match._notFound) + const boundaryOf = (found: IndexedOutcome) => + found[1 /* outcome */][0 /* kind */] === NOT_FOUND + ? getNotFoundBoundary(router, matches, found, controller.signal) + : found[0 /* index */] + let readinessEnd = plannedBoundary < 0 ? matches.length : plannedBoundary + + if ((failure?.[1 /* outcome */][0 /* kind */] ?? 0) >= REDIRECTED) { + readinessEnd = 0 + } else if (failure) { + readinessEnd = failure[2 /* boundary */] ??= await boundaryOf(failure) + for (const task of tasks) { + if (task[0 /* index */] >= readinessEnd) { + break + } + const outcome = await task[1 /* outcome */] + // Presence means a loader previously succeeded, even with `undefined`. + if ( + outcome[0 /* kind */] !== SUCCESS && + outcome[0 /* kind */] < REDIRECTED && + !('loaderData' in matches[task[0 /* index */]]!) + ) { + failure = [task[0 /* index */], outcome] + readinessEnd = failure[2 /* boundary */] = await boundaryOf(failure) + break + } + } + } + + for (const task of tasks) { + if (task[0 /* index */] >= readinessEnd) { + break + } + const chunkFailure = await task[2 /* chunkFailure */] + if (!chunkFailure) { + continue + } + failure = chunkFailure + break + } + + if ((failure?.[1 /* outcome */][0 /* kind */] ?? 0) >= REDIRECTED) { + const outcome = failure![1 /* outcome */] + if ( + outcome[0 /* kind */] !== REDIRECTED || + outcome[1 /* redirect */].options.reloadDocument || + redirects < 20 + ) { + discardBackground(router, lane) + return outcome as ControlOutcome + } + redirectLimitExceeded = true + failure = [0, [ERROR, new Error('Too many redirects')]] + } + + const boundary = failure + ? (failure[2 /* boundary */] ?? (await boundaryOf(failure))) + : plannedBoundary + if (boundary >= 0) { + const outcome = failure?.[1 /* outcome */] + const kind = outcome?.[0 /* kind */] + const match = matches[boundary]! + const cause = outcome?.[1 /* error or redirect */] + const install = () => { + if (outcome) { + match._notFound = undefined + if (kind === ERROR) { + match.status = 'error' + } else { + ;(cause as NotFoundError).routeId = match.routeId + if (match.routeId === router.routeTree.id) { + match.status = 'success' + match._notFound = true + } else { + match.status = 'notFound' + } + } + match.error = cause + match.isFetching = false + } + } + install() + try { + await waitFor( + outcome + ? Promise.resolve().then(() => + loadRouteChunk( + getRoute(router, match), + kind === ERROR ? 'errorComponent' : 'notFoundComponent', + ), + ) + : Promise.all([ + loadRouteChunk(getRoute(router, match)), + loadRouteChunk(getRoute(router, match), 'notFoundComponent'), + ]), + controller.signal, + ) + } catch (cause) { + if (cause === controller.signal) { + discardBackground(router, lane) + return [CANCELED] + } + } + if (!outcome) { + match.status = 'success' + onReady?.() + } else if (redirectLimitExceeded) { + controller.abort() + await Promise.all([ + ...tasks.map((task) => task[1 /* outcome */]), + ...tasks.map((task) => task[2 /* chunkFailure */]), + ...(lane[2 /* background */] ?? []).map( + (task) => task[1 /* outcome */], + ), + ]) + discardBackground(router, lane) + transferMatchResources(router, matches) + install() + } + } + + return lane as ReducedLane +} + +export async function projectLane( + router: AnyRouter, + lane: ReducedLane, + signal: AbortSignal, + start = 0, + end = lane[1 /* matches */].length, +): Promise { + const matches = lane[1 /* matches */] + for (let index = start; index < end; index++) { + const match = matches[index]! + const routeOptions = getRoute(router, match).options + if (routeOptions.head || routeOptions.scripts) { + try { + const context = { + ssr: router.options.ssr, + matches, + match, + params: match.params, + loaderData: match.loaderData, + } + const [head, scripts] = await waitFor( + Promise.all([ + routeOptions.head?.(context), + routeOptions.scripts?.(context), + ]), + signal, + ) + match.meta = head?.meta + match.links = head?.links + match.headScripts = head?.scripts + match.styles = head?.styles + match.scripts = scripts + } catch (cause) { + if (cause === signal) { + break + } + console.error(cause) + } + } + if (match.status !== 'success' || match._notFound) { + break + } + } + return lane as ProjectedLane +} + +async function executeClientLane( + router: AnyRouter, + location: ParsedLocation, + matches: Array, + options: ExecuteLaneOptions, +): Promise { + const matched = [location, matches as Array] as MatchedLane + let plannedBoundary = matches.findIndex((match) => match._notFound) + if (router.options.notFoundMode !== 'root' && plannedBoundary >= 0) { + const boundary = await getNotFoundBoundary( + router, + matched[1 /* matches */], + undefined, + options[0 /* controller */].signal, + plannedBoundary, + ) + if (boundary !== plannedBoundary) { + matches[plannedBoundary]!._notFound = undefined + matches[boundary]!._notFound = true + } + plannedBoundary = boundary + } + let end = plannedBoundary < 0 ? matches.length : plannedBoundary + 1 + const tasks: Array = [] + const start = options[7 /* resolvedPrefix */] ?? 0 + let semanticParent = start + ? Promise.resolve(matched[1 /* matches */][start - 1]!) + : undefined + const planSuccessfulLane = () => { + for (let index = start; index < end; index++) { + if (options[0 /* controller */].signal.aborted) { + break + } + semanticParent = createLoaderTask( + router, + matched as ContextualizedLane, + index, + tasks, + semanticParent, + options, + ) + } + } + // From here on `matched` is contextualized: `contextualize` communicates + // through mutation plus a failure return, so the phase brand is asserted at + // the two use sites below rather than granted by a (byte-costing) return. + const failure = await contextualize( + router, + matched, + options, + end, + planSuccessfulLane, + ) + if (failure) { + options[5 /* sync */] = true + end = failure[0 /* index */] + if (failure[1 /* outcome */][0 /* kind */] === NOT_FOUND) { + failure[2 /* boundary */] = await getNotFoundBoundary( + router, + matched[1 /* matches */], + failure, + options[0 /* controller */].signal, + ) + end = Math.min(end, failure[2 /* boundary */] + 1) + } else if (failure[1 /* outcome */][0 /* kind */] >= REDIRECTED) { + end = 0 + } + planSuccessfulLane() + } + if (options[2 /* isCurrent */]() && !options[4 /* preload */]) { + releaseUnownedFlights(router) + } + let reduced: ReducedLane | ControlOutcome + try { + const reduction = reduceLane( + router, + matched as ContextualizedLane, + tasks, + options[0 /* controller */], + options[1 /* redirects */], + settleTasks(tasks, failure, matched[2 /* background */]), + options[8 /* onReady */], + ) + if (matched[2 /* background */]?.length) { + matched[3 /* backgroundSettlement */] = settleTasks( + matched[2 /* background */], + undefined, + undefined, + reduction.then( + (foreground) => + isControl(foreground) + ? 0 + : _getRenderedMatches(foreground[1 /* matches */]).length, + () => 0, + ), + ) + } + reduced = await reduction + } catch (cause) { + discardBackground(router, matched) + throw cause + } + if (isControl(reduced)) { + return reduced + } + return projectLane( + router, + reduced, + options[0 /* controller */].signal, + options[7 /* resolvedPrefix */] === reduced[1 /* matches */].length + ? options[7 /* resolvedPrefix */] + : 0, + ) +} + +/** + * Finds the first route that should show pending UI and its two timing values. + * A fallback already on screen remains selected after its route loads, so we + * do not jump to a child fallback. Matches put back into pending by invalidation + * skip pendingMs, and a route without a usable fallback blocks pending UI for deeper routes. + */ +function pendingConfig( + router: AnyRouter, + matches: Array, +): + | [delay: number, boundary: number, min: number, component: unknown] + | undefined + | void { + const presented = router.stores.matches.get() + for (let index = 0; index < matches.length; index++) { + const match = matches[index]! + const success = match.status === 'success' + const visible = + success && + presented[index]?.id === match.id && + presented[index]?.status === 'pending' + if (success && !visible) { + continue + } + const route = getRoute(router, match as WorkMatch) + const delay = + visible || match.invalid + ? 0 + : (route.options.pendingMs ?? router.options.defaultPendingMs) + const component = + route.options.pendingComponent ?? + (router.options as any).defaultPendingComponent + return component && typeof delay === 'number' && delay !== Infinity + ? [ + delay, + index, + route.options.pendingMinMs ?? router.options.defaultPendingMinMs ?? 0, + component, + ] + : undefined + } +} + +/** + * Waits for `pendingMs`, then presents the complete lane. Rendering applies the + * selected boundary cutoff while retaining every match's structural state. + * A replacement load for the same match keeps the timer; choosing a different + * match resets it. `pendingMinMs` starts after the fallback renders. + */ +function offerPending(router: CoordinatorRouter, tx: LoadTransaction): void { + if (router._tx !== tx) { + return + } + let session = router._pending + let tookOver = false + const sessionMatchId = + session?.[0 /* owner */][3 /* matches */][session[1 /* boundary */]]?.id + if (session?.[0 /* owner */] !== tx) { + if ( + session && + tx[3 /* matches */][session[1 /* boundary */]]?.id === sessionMatchId + ) { + session[0 /* owner */] = tx + tookOver = true + } else { + clearTimeout(session?.[3 /* timer */]) + router._pending = session = undefined + } + } + const config = pendingConfig(router, tx[3 /* matches */]) + if (!config) { + return + } + const [delay, boundary, min, component] = config + const matchId = tx[3 /* matches */][boundary]!.id + if ( + !session || + session[1 /* boundary */] !== boundary || + sessionMatchId !== matchId + ) { + // Hydration and redirects can preserve pending presentation without a session. + // Do not delay it again; conservatively start pendingMinMs from now. + clearTimeout(session?.[3 /* timer */]) + const presented = router.stores.matches.get()[boundary] + const visible = presented?.id === matchId && presented.status === 'pending' + router._pending = session = [ + tx, + boundary, + visible ? Date.now() + min : tx[4 /* startedAt */] + delay, + undefined, + visible ? Promise.resolve(true) : undefined, + component, + ] + } + if ( + session[4 /* ack */] && + !tookOver && + session[5 /* component */] === component + ) { + return + } + session[5 /* component */] = component + if (!session[4 /* ack */]) { + clearTimeout(session[3 /* timer */]) + const remaining = session[2 /* deadline */] - Date.now() + if (remaining > 0) { + session[3 /* timer */] = setTimeout(() => { + offerPending(router, tx) + }, remaining) + return + } + session[2 /* deadline */] = 0 + } + const offered = tx[3 /* matches */].map((match) => ({ + ...match, + _flight: undefined, + })) + offered[boundary]!.status = 'pending' + const ack = router + .startTransition(() => router.stores.setMatches(offered), offered) + .then((rendered) => { + if ( + rendered && + router._pending === session && + session[4 /* ack */] === ack && + !session[2 /* deadline */] + ) { + session[2 /* deadline */] = Date.now() + min + } + return rendered + }) + session[4 /* ack */] = ack +} + +/** + * Cancels pending UI timing when its load ends. The ownership check prevents + * an older, superseded load from clearing pending UI that a newer load took over. + */ +function finishPending(router: CoordinatorRouter, tx: LoadTransaction): void { + const session = router._pending + if (session?.[0 /* owner */] === tx) { + clearTimeout(session[3 /* timer */]) + router._pending = undefined + } +} + +function publishMatches( + router: CoordinatorRouter, + matches: Array, +): void { + router._committed = matches + router.stores.setMatches(matches) +} + +function discardLane(router: AnyRouter, lane: ProjectedLane): void { + transferMatchResources(router, lane[1 /* matches */]) + discardBackground(router, lane) +} + +function commitMatches( + router: CoordinatorRouter, + tx: LoadTransaction, + matches: LaneMatches<'projected'>, + resolvedPrefix?: number, +): void { + const previous = router._committed + const previousCached = router._cache + for (const match of matches) { + match.preload = false + if (resolvedPrefix) { + match._assetEnd = undefined + } + } + const cut = _getRenderedMatches(matches).length + const cached = new Map() + const now = Date.now() + for (const match of [...previous, ...previousCached.values()]) { + // Rendered-prefix ids and settled successes anywhere in the lane are + // authoritative: retaining an older same-id generation would shadow them + // at the next planning pass. Unsettled beyond-boundary matches are not — + // they must not evict a newer same-id preload. + if ( + match.status !== 'success' || + matches.some( + (candidate, index) => + candidate.id === match.id && + (index < cut || candidate.status === 'success'), + ) + ) { + continue + } + const work = match as WorkMatch + const route = getRoute(router, work) + if ( + !route.options.loader || + now - match.updatedAt >= + (match.preload + ? (route.options.preloadGcTime ?? + router.options.defaultPreloadGcTime ?? + 300_000) + : (route.options.gcTime ?? router.options.defaultGcTime ?? 300_000)) + ) { + continue + } + cached.set( + match.id, + previousCached.get(match.id) === match + ? match + : ({ + ...match, + _flight: undefined, + isFetching: false, + context: {}, + } as WorkMatch), + ) + } + // The lane becomes committed before publication can synchronously reenter. + tx[3 /* matches */] = [] + router._cache = cached + publishMatches(router, matches) + transferMatchResources( + router, + [...previousCached.values(), ...previous], + [...matches, ...cached.values()], + ) + runRouteLifecycle(router, previous, matches, () => router._tx === tx) +} + +function commitRefreshMatches( + router: CoordinatorRouter, + tx: LoadTransaction, + matches: LaneMatches<'projected'>, + checkpoint: PublicationCheckpoint, +): void { + const previous = router._committed + const previousCached = router._cache + for (const match of matches) { + match.preload = false + } + const cached = new Map() + // Delay releasing the previous owners until the HMR render is acknowledged. + // Old generations must not become reusable cache entries after refresh. + tx[3 /* matches */] = [] + router._cache = cached + checkpoint.previousMatches = previous + checkpoint.previousCache = previousCached + checkpoint.published = true + publishMatches(router, matches) + if (!checkpoint.published || router._tx !== tx) { + return + } + runRouteLifecycle(router, previous, matches, () => router._tx === tx) +} + +function settlePublication( + router: CoordinatorRouter, + checkpoint: PublicationCheckpoint, +): void { + if (!checkpoint.published) { + return + } + checkpoint.published = false + transferMatchResources( + router, + [...checkpoint.previousCache.values(), ...checkpoint.previousMatches], + [...router._cache.values(), ...router._committed], + ) +} + +function rollbackPublication( + router: CoordinatorRouter, + tx: LoadTransaction, + lane: ProjectedLane, + checkpoint: PublicationCheckpoint, +): boolean { + if ( + !checkpoint.published || + router._tx !== tx || + router._committed !== lane[1 /* matches */] + ) { + settlePublication(router, checkpoint) + return false + } + + const discarded = [...router._cache.values(), ...router._committed] + const restored = [ + ...checkpoint.previousCache.values(), + ...checkpoint.previousMatches, + ] + router._cache = checkpoint.previousCache + router._committed = checkpoint.previousMatches + checkpoint.published = false + + for (const match of discarded as Array) { + if ( + !restored.includes(match) && + match._flight && + router._flights?.get(match.id) === match._flight + ) { + router._flights.delete(match.id) + } + } + + finishPending(router, tx) + router.batch(() => { + router.stores.status.set('idle') + router.stores.setMatches(checkpoint.previousPresentation) + }) + tx[0 /* controller */].abort() + transferMatchResources(router, discarded, restored) + discardBackground(router, lane) + if (router._tx === tx && router._commitPromise === checkpoint.commitPromise) { + router._commitPromise?.resolve() + router._commitPromise = undefined + } + return true +} + +async function transitionRefresh( + router: CoordinatorRouter, + tx: LoadTransaction, + lane: ProjectedLane, + changeInfo: ReturnType, +): Promise { + const refresh = tx[6 /* refresh */]! + const checkpoint: PublicationCheckpoint = { + previousMatches: router._committed, + previousPresentation: refresh[0 /* presentation */], + previousCache: router._cache, + commitPromise: router._commitPromise, + published: false, + } + const commit = () => { + finishPending(router, tx) + refresh[2 /* rollback */] = rollback + commitRefreshMatches(router, tx, lane[1 /* matches */], checkpoint) + if (!checkpoint.published || router._tx !== tx) { + return + } + router.emit({ type: 'onLoad', ...changeInfo }) + if (router._tx === tx) { + router.emit({ type: 'onBeforeRouteMount', ...changeInfo }) + } + } + const rollback = () => { + if (refresh[2 /* rollback */] === rollback) { + refresh[2 /* rollback */] = undefined + } + const restored = rollbackPublication(router, tx, lane, checkpoint) + router._cancelTransition?.() + return restored + } + try { + const rendered = await router.startTransition(commit, lane[1 /* matches */]) + if (refresh[2 /* rollback */] === rollback) { + refresh[2 /* rollback */] = undefined + } + if (checkpoint.published) { + const handoff = refresh[1 /* handoff */] + if (handoff && router._handoff === handoff) { + handoff[1 /* finish */]() + } + if (router._tx === tx) { + tx[6 /* refresh */] = undefined + } + } + settlePublication(router, checkpoint) + return rendered + } catch (cause) { + if (rollback()) { + return + } + throw cause + } +} + +async function awaitCurrent( + router: CoordinatorRouter, + owner?: LoadTransaction, +): Promise { + let current = router._tx + while (current && current !== owner) { + await current[5 /* done */] + if (router._tx === current) { + return + } + current = router._tx + } +} + +async function followRedirect( + router: CoordinatorRouter, + tx: LoadTransaction, + redirect: AnyRedirect, +): Promise { + await router.navigate({ + ...redirect.options, + replace: true, + ignoreBlocker: true, + _redirects: tx[1 /* redirects */] + 1, + } as any) +} + +function restoreCommitted( + router: CoordinatorRouter, + tx: LoadTransaction, +): void { + finishPending(router, tx) + tx[0 /* controller */].abort() + transferMatchResources(router, tx[3 /* matches */]) + tx[3 /* matches */] = [] + if (router._tx !== tx) { + return + } + router.batch(() => { + router.stores.status.set('idle') + router.stores.setMatches(router._committed) + }) + if (router._tx === tx) { + router._commitPromise?.resolve() + router._commitPromise = undefined + } +} + +async function runBackground( + router: CoordinatorRouter, + tx: LoadTransaction, + base: Array, + tasks: Array, + settlement: Promise, +): Promise { + const next = base.map((match) => ({ ...match })) + acquireMatchResources(next) + for (const task of tasks) { + releaseFlight(router, next[task[0 /* index */]]!) + next[task[0 /* index */]] = task[3 /* candidate */] + } + // Phase jump: the clones inherit beforeLoad context from the committed + // foreground lane, which already ran `contextualize` for these matches. + const lane = [tx[2 /* location */], next] as ContextualizedLane + let reduced: ReducedLane | ControlOutcome + try { + reduced = await reduceLane( + router, + lane, + tasks, + tx[0 /* controller */], + tx[1 /* redirects */], + settlement, + ) + } catch (cause) { + transferMatchResources(router, next) + throw cause + } + if (isControl(reduced)) { + transferMatchResources(router, next) + if ( + reduced[0 /* kind */] === REDIRECTED && + router._tx === tx && + router._committed === base + ) { + await followRedirect(router, tx, reduced[1 /* redirect */]) + } + return + } + const projected = await projectLane( + router, + reduced, + tx[0 /* controller */].signal, + ) + if (router._tx !== tx || router._committed !== base) { + transferMatchResources(router, projected[1 /* matches */]) + return + } + for (const match of projected[1 /* matches */] as Array) { + const cached = router._cache.get(match.id) as WorkMatch | undefined + if (cached?._flight && cached._flight === match._flight) { + router._cache.delete(match.id) + releaseFlight(router, cached) + } + } + publishMatches(router, projected[1 /* matches */]) + transferMatchResources(router, base, projected[1 /* matches */]) +} + +async function runClientTransaction( + router: CoordinatorRouter, + tx: LoadTransaction, + forceStaleReload: boolean, + onReady?: () => void, + sync?: boolean, + resolvedPrefix?: number, +): Promise { + const options: ExecuteLaneOptions = [ + tx[0 /* controller */], + tx[1 /* redirects */], + () => router._tx === tx && !!tx[3 /* matches */].length, + router._committed, + undefined, + sync, + forceStaleReload, + resolvedPrefix, + onReady, + ] + const result = await executeClientLane( + router, + tx[2 /* location */], + tx[3 /* matches */], + options, + ) + + if (isControl(result)) { + if (result[0 /* kind */] === REDIRECTED && router._tx === tx) { + finishPending(router, tx) + transferMatchResources(router, tx[3 /* matches */]) + tx[3 /* matches */] = [] + if (router._tx === tx) { + if (process.env.NODE_ENV !== 'production' && tx[6 /* refresh */]) { + router._refreshNextLoad = true + } + await followRedirect(router, tx, result[1 /* redirect */]) + } + } else { + restoreCommitted(router, tx) + } + return + } + const pending = router._pending + if (pending?.[0 /* owner */] === tx) { + /** + * Loading finished, so cancel any pending reveal. If the fallback rendered, + * wait out the rest of `pendingMinMs` before replacing it. If it never + * rendered, there is no minimum wait; if another load took it over, that + * load owns the deadline. + */ + clearTimeout(pending[3 /* timer */]) + if (pending[4 /* ack */]) { + const signal = tx[0 /* controller */].signal + let rendered = false + try { + rendered = await waitFor(pending[4 /* ack */], signal) + } catch (cause) { + if (cause !== signal) { + throw cause + } + } + if ( + rendered && + router._pending === pending && + pending[0 /* owner */] === tx + ) { + const remaining = pending[2 /* deadline */] - Date.now() + if (remaining > 0) { + try { + await waitFor( + new Promise((resolve) => { + pending[3 /* timer */] = setTimeout(resolve, remaining) + }), + signal, + ) + } catch {} + clearTimeout(pending[3 /* timer */]) + } + } + } + } + if (router._tx !== tx) { + finishPending(router, tx) + discardLane(router, result) + return + } + const toLocation = tx[2 /* location */] + const changeInfo = getLocationChangeInfo( + toLocation, + router.stores.resolvedLocation.get(), + ) + const background = result[2 /* background */] + await router.startViewTransition(async () => { + if (router._tx !== tx) { + discardLane(router, result) + return + } + const commit = () => { + finishPending(router, tx) + commitMatches(router, tx, result[1 /* matches */], resolvedPrefix) + if (router._tx !== tx) { + return + } + router.emit({ type: 'onLoad', ...changeInfo }) + if (router._tx === tx) { + router.emit({ type: 'onBeforeRouteMount', ...changeInfo }) + } + } + const rendered = + process.env.NODE_ENV !== 'production' && tx[6 /* refresh */] + ? await transitionRefresh(router, tx, result, changeInfo) + : await router.startTransition(commit, result[1 /* matches */]) + if ( + process.env.NODE_ENV !== 'production' && + tx[6 /* refresh */] && + rendered === undefined + ) { + return + } + if (router._tx !== tx) { + discardBackground(router, result) + return + } + if (background?.length) { + // Publish refreshes only after the foreground render acknowledgement. + // Otherwise a fast refresh can replace the acknowledged generation + // before the framework commits it and strand the navigation. + runBackground( + router, + tx, + result[1 /* matches */], + background, + result[3 /* backgroundSettlement */]!, + ).catch(console.error) + } + router.batch(() => { + router.stores.resolvedLocation.set(toLocation) + router.stores.status.set('idle') + if (router._tx === tx) { + router.emit({ type: 'onResolved', ...changeInfo }) + } + if (rendered && router._tx === tx) { + router.emit({ type: 'onRendered', ...changeInfo }) + } + }) + if (router._tx !== tx) { + return + } + router._commitPromise?.resolve() + router._commitPromise = undefined + }) +} + +export async function loadClientRoute( + router: CoordinatorRouter, + opts?: { sync?: boolean }, +): Promise { + let rematerialize = false + if (process.env.NODE_ENV !== 'production') { + router._tx?.[6 /* refresh */]?.[2 /* rollback */]?.() + rematerialize = !!router._refreshNextLoad || !!router._tx?.[6 /* refresh */] + } + const refreshPresentation = rematerialize + ? router.stores.matches.get() + : undefined + const previousOwner = router._tx + const resolvedLocation = router.stores.resolvedLocation.get() + const previousLocation = resolvedLocation ?? router.stores.location.get() + const location = router.latestLocation + const pendingLocation = router._pendingLocation as + | (ParsedLocation & { _redirects?: number }) + | undefined + const redirects = + pendingLocation?.href === location.href + ? (pendingLocation._redirects ?? 0) + : 0 + const handoff = router._handoff + const hydrationController = rematerialize + ? undefined + : handoff?.[0 /* claim */]() + const preflight = new AbortController() + const previousPreflight = router._preflight + router._preflight = preflight + if (!rematerialize && !hydrationController) { + handoff?.[1 /* finish */]() + } + previousPreflight?.abort() + // The preflight controller is not exposed to route hooks. Every replacement + // aborts its predecessor, so a live signal is the sole authority here. + if (preflight.signal.aborted) { + await awaitCurrent(router, previousOwner) + return + } + + const changeInfo = getLocationChangeInfo(location, resolvedLocation) + router.emit({ type: 'onBeforeNavigate', ...changeInfo }) + if (!preflight.signal.aborted) { + router.emit({ type: 'onBeforeLoad', ...changeInfo }) + } + if (preflight.signal.aborted) { + await awaitCurrent(router, previousOwner) + return + } + const sameHref = previousLocation.href === location.href + let matches: Array + let controller = preflight + try { + matches = + process.env.NODE_ENV !== 'production' && rematerialize + ? router.matchRoutes(location, { + _controller: preflight, + _rematerialize: true, + }) + : router.matchRoutes(location, { _controller: preflight }) + acquireMatchResources(matches) + } catch (cause) { + preflight.abort() + if (!isRedirect(cause)) { + if (process.env.NODE_ENV !== 'production' && rematerialize) { + router._refreshNextLoad = undefined + } + await awaitCurrent(router) + router._commitPromise?.resolve() + router._commitPromise = undefined + return + } + await router.navigate({ + ...cause.options, + replace: true, + ignoreBlocker: true, + }) + await awaitCurrent(router, previousOwner) + return + } + const resolvedPrefix = hydrationController + ? handoff![1 /* finish */](matches) + : undefined + if (resolvedPrefix) { + controller = hydrationController! + } else { + hydrationController?.abort() + } + if (preflight.signal.aborted) { + transferMatchResources(router, matches) + await awaitCurrent(router, previousOwner) + return + } + router._preflight = undefined + + const tx: LoadTransaction = [ + controller, + redirects, + location, + matches, + Date.now(), + Promise.resolve() + .then(() => + runClientTransaction( + router, + tx, + sameHref, + () => offerPending(router, tx), + opts?.sync, + resolvedPrefix, + ), + ) + .catch(() => { + if (router._tx === tx) { + restoreCommitted(router, tx) + } + }), + ] + if (process.env.NODE_ENV !== 'production' && rematerialize) { + // `refreshPresentation` is always captured when `rematerialize` is set. + tx[6 /* refresh */] = [refreshPresentation!, handoff] + router._refreshNextLoad = undefined + } + router._tx = tx + if (previousOwner) { + for (const match of router.stores.matches.get() as Array) { + if (router._tx !== tx) { + break + } + if (match.isFetching) { + setFetching(router, match, false) + } + } + previousOwner[0 /* controller */].abort() + transferPredecessorResources( + router, + previousOwner[3 /* matches */], + tx[3 /* matches */], + ) + } + if (router._tx !== tx) { + transferMatchResources(router, tx[3 /* matches */]) + tx[3 /* matches */] = [] + await awaitCurrent(router, tx) + return + } + router.batch(() => { + router.stores.status.set('pending') + router.stores.location.set(location) + }) + offerPending(router, tx) + try { + await tx[5 /* done */] + } finally { + await awaitCurrent(router, tx) + } +} + +export async function refreshClientRoute( + router: CoordinatorRouter, +): Promise { + router._tx?.[6 /* refresh */]?.[2 /* rollback */]?.() + const pending = router._tx + if ( + pending && + !pending[6 /* refresh */] && + router.stores.status.get() === 'pending' + ) { + await pending[5 /* done */] + if (router._tx !== pending) { + await awaitCurrent(router, pending) + } + } + // Existing owners remain alive for rollback but cannot donate stale work. + router._flights?.clear() + router.clearCache() + router._refreshNextLoad = true + await loadClientRoute(router, { sync: true }) +} + +export async function preloadClientRoute( + router: CoordinatorRouter, + opts: any, + redirects = 0, +): Promise | undefined> { + if (redirects > 20) { + return + } + if ( + process.env.NODE_ENV !== 'production' && + (router._refreshNextLoad || router._tx?.[6 /* refresh */]) + ) { + return + } + const location = opts._builtLocation ?? router.buildLocation(opts) + const base = router._committed + const controller = new AbortController() + let matches: Array + try { + matches = router.matchRoutes(location, { + _controller: controller, + }) + acquireMatchResources(matches) + } catch (cause) { + controller.abort() + if (!isNotFound(cause)) { + console.error(cause) + } + return + } + ;(router._preloads ??= new Map()).set(controller, matches) + let active: boolean + try { + let result: LaneResult + try { + result = await executeClientLane(router, location, matches, [ + controller, + redirects, + // Preload lanes run to completion even when unrelated navigations commit: + // finished work seeds the cache. + () => true, + base, + true, + ]) + } finally { + active = router._preloads.delete(controller) + transferMatchResources(router, matches) + controller.abort() + } + if (!isControl(result)) { + return result[1 /* matches */] + } + if ( + active && + result[0 /* kind */] === REDIRECTED && + !result[1 /* redirect */].options.reloadDocument + ) { + return preloadClientRoute( + router, + { + ...result[1 /* redirect */].options, + _fromLocation: location, + }, + redirects + 1, + ) + } + } catch (cause) { + if (!isNotFound(cause)) { + console.error(cause) + } + } + return +} + +// --- SSR hydration (client entry via @tanstack/router-core/ssr/client) --- + +declare global { + interface Window { + [GLOBAL_TSR]?: TsrSsrGlobal + [GLOBAL_SEROVAL]?: any + } +} + +export async function hydrate(router: AnyRouter): Promise { + if (process.env.NODE_ENV !== 'production' && !window.$_TSR) { + throw new Error( + 'Invariant failed: Expected to find bootstrap data on window.$_TSR, but we did not. Please file an issue!', + ) + } + const tsr = window.$_TSR! + + const adapters = router.options.serializationAdapters as + | Array + | undefined + if (adapters?.length) { + tsr.t = new Map( + adapters.map((adapter) => [adapter.key, adapter.fromSerializable]), + ) + tsr.buffer.forEach((script) => script()) + } + tsr.initialized = true + + const dehydratedRouter = tsr.router + if (process.env.NODE_ENV !== 'production' && !dehydratedRouter) { + throw new Error( + 'Invariant failed: Expected to find a dehydrated data on window.$_TSR.router, but we did not. Please file an issue!', + ) + } + router.ssr = { manifest: dehydratedRouter!.manifest } + router.options.ssr = { + nonce: ( + document.querySelector('meta[property="csp-nonce"]') as + | HTMLMetaElement + | undefined + )?.content, + } + + const dehydratedMatches = dehydratedRouter!.matches + + const controller = new AbortController() + const previousPreflight = router._preflight + router._preflight = controller + previousPreflight?.abort() + // Route context can abort this controller itself. Only a new slot owner + // supersedes hydration. + const isCurrent = () => router._preflight === controller + + let location!: AnyRouter['latestLocation'] + let candidates!: Array + let handoffHistoryHref!: string + let handoffHistoryState: unknown + try { + await waitFor( + router.options.hydrate?.(dehydratedRouter!.dehydratedData), + controller.signal, + ) + if (!isCurrent()) { + return + } + // Hydration trusts transported context and beforeLoad. The raw history + // entry owns the handoff; route structure is verified after rematching. + const historyLocation = router.history.location + handoffHistoryHref = historyLocation.href + handoffHistoryState = historyLocation.state + router.updateLatestLocation() + location = router.latestLocation + router.stores.location.set(location) + candidates = router.matchRoutes(location, { + _controller: controller, + }) + } catch (cause) { + if (isCurrent()) { + router._preflight = undefined + } + controller.abort(cause) + if (cause !== controller.signal) { + throw cause + } + } + if (!isCurrent()) { + return + } + const committed: Array = [] + let pendingBoundary: number | undefined + let verifiedAssetEnd = 0 + const retryFrom = (index: number) => { + // The failing route's identity is still verified, but no descendant is. + verifiedAssetEnd = Math.min(verifiedAssetEnd, index + 1) + const removed = committed.splice(index) + for (const match of removed) { + if ( + getRoute(router, match).options.loader && + (match.status === 'success' || + (!match.invalid && 'loaderData' in match)) + ) { + cacheLoaderMatch( + router, + // Phase jump: dehydrated server data is already past the loader + // phase — the guard above verified a settled success (or transported + // loaderData), so this clone is settled without a client settleInto. + { + ...match, + status: 'success', + error: undefined, + preload: true, + } as SettledMatch, + router._cache.get(match.id), + ) + } + } + transferMatchResources(router, removed) + } + + // A longer server lane is valid only when the local match already caps the + // branch at a global not-found boundary. Otherwise no transported work is + // safe to attach to the shorter client lane. + const shared = + dehydratedMatches.length > candidates.length + ? candidates.findIndex((match) => match._notFound) + 1 + : dehydratedMatches.length + let isTerminal = false + for (let index = 0; index < shared; index++) { + const candidate = candidates[index]! + const dehydrated = dehydratedMatches[index]! + if ( + typeof dehydrated.i !== 'string' || + hydrateSsrMatchId(dehydrated.i) !== candidate.id + ) { + pendingBoundary ??= index + break + } + verifiedAssetEnd = index + 1 + const route = getRoute(router, candidate) + if ( + 'l' in dehydrated || + (dehydrated.s === 'success' && + dehydrated.e === undefined && + route.options.loader) + ) { + candidate.loaderData = dehydrated.l + } + candidate.status = dehydrated.s + candidate.ssr = dehydrated.ssr + route.options.ssr = candidate.ssr + candidate.updatedAt = dehydrated.u + candidate.error = dehydrated.e + candidate._notFound ||= dehydrated.g + const terminal = + candidate.status === 'error' || + candidate.status === 'notFound' || + candidate._notFound + if (terminal) { + isTerminal = true + committed.push(candidate) + if (candidate.ssr === false || candidate.ssr === 'data-only') { + pendingBoundary ??= index + } + break + } + if (candidate.status === 'pending') { + pendingBoundary ??= index + break + } + + committed.push(candidate) + if (candidate.ssr === 'data-only') { + pendingBoundary ??= index + } + } + if ( + !isTerminal && + committed.length === shared && + shared < candidates.length + ) { + pendingBoundary = shared + } + + // Hooks observe structural membership. Execution remains limited to + // `committed`, the accepted server prefix. + const chunks = committed.map(async (match) => { + try { + const route = getRoute(router, match) + if (match._notFound) { + await Promise.all([ + loadRouteChunk(route), + loadRouteChunk(route, 'notFoundComponent'), + ]) + } else { + await loadRouteChunk( + route, + match.status === 'error' + ? 'errorComponent' + : match.status === 'notFound' + ? 'notFoundComponent' + : undefined, + ) + } + return true + } catch { + return false + } + }) + let chunkFailure = 0 + try { + while ( + chunkFailure < chunks.length && + (await waitFor(chunks[chunkFailure]!, controller.signal)) + ) { + chunkFailure++ + } + } catch { + return + } + if (!isCurrent()) { + return + } + if (chunkFailure < committed.length) { + retryFrom(chunkFailure) + } + + // The first pending match is already visible, so prepare its route context + // without granting its beforeLoad or loader any hydration authority. + const contextEnd = Math.max( + pendingBoundary === committed.length + ? committed.length + 1 + : committed.length, + // `chunks.length` keeps the pre-retry committed length, so a smaller + // `chunkFailure` is the exclusive bound of the verified context prefix. + chunkFailure < chunks.length ? chunkFailure : verifiedAssetEnd, + ) + for (let index = 0; index < contextEnd; index++) { + const match = candidates[index]! + const route = getRoute(router, match) + const parentContext = + candidates[index - 1]?.context ?? router.options.context ?? {} + let routeContext + if (route.options.context) { + try { + routeContext = match._ctx = + route.options.context({ + deps: match.loaderDeps, + params: match.params, + context: parentContext, + location, + navigate: navigateFrom(router, location), + buildLocation: router.buildLocation, + cause: match.cause, + abortController: controller, + preload: false, + matches: candidates, + routeId: route.id, + }) || {} + } catch { + if (!isCurrent()) { + return + } + if ( + match.status !== 'error' && + match.status !== 'notFound' && + !match._notFound + ) { + retryFrom(index) + break + } + } + if (!isCurrent()) { + return + } + } + match.context = { + ...parentContext, + ...routeContext, + ...(committed[index] && dehydratedMatches[index]!.b), + } + } + + await projectLane( + router, + [location, candidates] as any, + controller.signal, + 0, + verifiedAssetEnd, + ) + if (!isCurrent()) { + return + } + const needsClientLoad = + pendingBoundary !== undefined || committed.length < shared + const committedMatches = + isTerminal && committed.length === shared ? candidates : committed + let presented = needsClientLoad ? candidates : committedMatches + let dataOnlyAssetEnd: number | undefined + if (needsClientLoad && pendingBoundary !== undefined) { + const boundary = presented[pendingBoundary]! + // A verified descendant proves this data-only boundary was nonterminal. + dataOnlyAssetEnd = + boundary.ssr === 'data-only' && verifiedAssetEnd > pendingBoundary + 1 + ? verifiedAssetEnd + : undefined + presented = presented.slice() + presented[pendingBoundary] = { + ...boundary, + status: 'pending', + ssr: boundary.ssr === 'data-only' ? 'data-only' : false, + _assetEnd: dataOnlyAssetEnd, + } + } + + const claim = () => { + const historyLocation = router.history.location + return needsClientLoad && + !router._tx && + historyLocation.href === handoffHistoryHref && + historyLocation.state === handoffHistoryState && + router._committed === committedMatches && + committedMatches.length && + !controller.signal.aborted + ? controller + : undefined + } + const handoff: NonNullable = [ + claim, + (matches) => { + if (router._handoff !== handoff) { + return + } + // `finish` is single-use. Consume the slot before validating or moving + // resources so reentrant work cannot claim the same handoff. + router._handoff = undefined + const prefix = committedMatches.length + if ( + !matches || + !claim() || + committedMatches.some((match, index) => match.id !== matches[index]?.id) + ) { + controller.abort() + return + } + let handoffAssetEnd = dataOnlyAssetEnd + if (handoffAssetEnd !== undefined) { + for (let index = prefix; index < handoffAssetEnd; index++) { + if (candidates[index]?.id !== matches[index]?.id) { + handoffAssetEnd = index > pendingBoundary! + 1 ? index : undefined + break + } + } + } + const clones = committedMatches.map((match) => ({ ...match })) + if (handoffAssetEnd !== undefined) { + clones[pendingBoundary!]!._assetEnd = handoffAssetEnd + } + transferMatchResources(router, matches.splice(0, prefix, ...clones)) + for (let index = prefix; index < matches.length; index++) { + const match = matches[index]! + const hydrated = candidates[index] + if (hydrated?.id === match.id && hydrated._ctx) { + match._ctx = hydrated._ctx + } + match.abortController = controller + } + return prefix + }, + ] + router._committed = committedMatches + router._handoff = handoff + router._preflight = undefined + router.batch(() => { + router.stores.setMatches(presented) + router.stores.status.set('idle') + if (!needsClientLoad) { + router.stores.resolvedLocation.set(router.stores.location.get()) + } + }) +} diff --git a/packages/router-core/src/load-matches.ts b/packages/router-core/src/load-matches.ts deleted file mode 100644 index f901a0c97d..0000000000 --- a/packages/router-core/src/load-matches.ts +++ /dev/null @@ -1,1278 +0,0 @@ -import { isServer } from '@tanstack/router-core/isServer' -import { invariant } from './invariant' -import { createControlledPromise, isPromise } from './utils' -import { isNotFound } from './not-found' -import { rootRouteId } from './root' -import { isRedirect } from './redirect' -import type { NotFoundError } from './not-found' -import type { ParsedLocation } from './location' -import type { - AnyRoute, - BeforeLoadContextOptions, - LoaderFnContext, - SsrContextOptions, -} from './route' -import type { AnyRouteMatch, MakeRouteMatch } from './Matches' -import type { AnyRouter, SSROption, UpdateMatchFn } from './router' - -/** - * An object of this shape is created when calling `loadMatches`. - * It contains everything we need for all other functions in this file - * to work. (It's basically the function's argument, plus a few mutable states) - */ -type InnerLoadContext = { - /** the calling router instance */ - router: AnyRouter - location: ParsedLocation - /** mutable state, scoped to a `loadMatches` call */ - firstBadMatchIndex?: number - /** mutable state, scoped to a `loadMatches` call */ - rendered?: boolean - serialError?: unknown - updateMatch: UpdateMatchFn - matches: Array - preload?: boolean - forceStaleReload?: boolean - onReady?: () => Promise - sync?: boolean -} - -const triggerOnReady = (inner: InnerLoadContext): void | Promise => { - if (!inner.rendered) { - inner.rendered = true - return inner.onReady?.() - } -} - -const hasForcePendingActiveMatch = (router: AnyRouter): boolean => { - return router.stores.matchesId.get().some((matchId) => { - return router.stores.matchStores.get(matchId)?.get()._forcePending - }) -} - -const resolvePreload = (inner: InnerLoadContext, matchId: string): boolean => { - return !!(inner.preload && !inner.router.stores.matchStores.has(matchId)) -} - -/** - * Builds the accumulated context from router options and all matches up to (and optionally including) the given index. - * Merges __routeContext and __beforeLoadContext from each match. - */ -const buildMatchContext = ( - inner: InnerLoadContext, - index: number, - includeCurrentMatch: boolean = true, -): Record => { - const context: Record = { - ...(inner.router.options.context ?? {}), - } - const end = includeCurrentMatch ? index : index - 1 - for (let i = 0; i <= end; i++) { - const innerMatch = inner.matches[i] - if (!innerMatch) continue - const m = inner.router.getMatch(innerMatch.id) - if (!m) continue - Object.assign(context, m.__routeContext, m.__beforeLoadContext) - } - return context -} - -const getNotFoundBoundaryIndex = ( - inner: InnerLoadContext, - err: NotFoundError, -): number | undefined => { - if (!inner.matches.length) { - return undefined - } - - const requestedRouteId = err.routeId - const matchedRootIndex = inner.matches.findIndex( - (m) => m.routeId === inner.router.routeTree.id, - ) - const rootIndex = matchedRootIndex >= 0 ? matchedRootIndex : 0 - - let startIndex = requestedRouteId - ? inner.matches.findIndex((match) => match.routeId === requestedRouteId) - : (inner.firstBadMatchIndex ?? inner.matches.length - 1) - - if (startIndex < 0) { - startIndex = rootIndex - } - - for (let i = startIndex; i >= 0; i--) { - const match = inner.matches[i]! - const route = inner.router.looseRoutesById[match.routeId]! - if (route.options.notFoundComponent) { - return i - } - } - - // If no boundary component is found, preserve explicit routeId targeting behavior, - // otherwise default to root for untargeted notFounds. - return requestedRouteId ? startIndex : rootIndex -} - -const handleRedirectAndNotFound = ( - inner: InnerLoadContext, - match: AnyRouteMatch | undefined, - err: unknown, -): void => { - if (!isRedirect(err) && !isNotFound(err)) return - - if (isRedirect(err) && err.redirectHandled && !err.options.reloadDocument) { - throw err - } - - // in case of a redirecting match during preload, the match does not exist - if (match) { - match._nonReactive.beforeLoadPromise?.resolve() - match._nonReactive.loaderPromise?.resolve() - match._nonReactive.beforeLoadPromise = undefined - match._nonReactive.loaderPromise = undefined - - match._nonReactive.error = err - - inner.updateMatch(match.id, (prev) => ({ - ...prev, - status: isRedirect(err) - ? 'redirected' - : isNotFound(err) - ? 'notFound' - : prev.status === 'pending' - ? 'success' - : prev.status, - context: buildMatchContext(inner, match.index), - isFetching: false, - error: err, - })) - - if (isNotFound(err) && !err.routeId) { - // Stamp the throwing match's routeId so that the finalization step in - // loadMatches knows where the notFound originated. The actual boundary - // resolution (walking up to the nearest notFoundComponent) is deferred to - // the finalization step, where firstBadMatchIndex is stable and - // headMaxIndex can be capped correctly. - err.routeId = match.routeId - } - - match._nonReactive.loadPromise?.resolve() - } - - if (isRedirect(err)) { - inner.rendered = true - err.options._fromLocation = inner.location - err.redirectHandled = true - err = inner.router.resolveRedirect(err) - } - - throw err -} - -const shouldSkipLoader = ( - inner: InnerLoadContext, - matchId: string, -): boolean => { - const match = inner.router.getMatch(matchId) - if (!match) { - return true - } - // upon hydration, we skip the loader if the match has been dehydrated on the server - if (!(isServer ?? inner.router.isServer) && match._nonReactive.dehydrated) { - return true - } - - if ((isServer ?? inner.router.isServer) && match.ssr === false) { - return true - } - - return false -} - -const syncMatchContext = ( - inner: InnerLoadContext, - matchId: string, - index: number, -): void => { - const nextContext = buildMatchContext(inner, index) - - inner.updateMatch(matchId, (prev) => { - return { - ...prev, - context: nextContext, - } - }) -} - -const handleSerialError = ( - inner: InnerLoadContext, - index: number, - err: any, -): void => { - const { id: matchId, routeId } = inner.matches[index]! - const route = inner.router.looseRoutesById[routeId]! - - // Much like suspense, we use a promise here to know if - // we've been outdated by a new loadMatches call and - // should abort the current async operation - if (err instanceof Promise) { - throw err - } - - inner.firstBadMatchIndex ??= index - handleRedirectAndNotFound(inner, inner.router.getMatch(matchId), err) - - try { - route.options.onError?.(err) - } catch (errorHandlerErr) { - err = errorHandlerErr - handleRedirectAndNotFound(inner, inner.router.getMatch(matchId), err) - } - - inner.updateMatch(matchId, (prev) => { - prev._nonReactive.beforeLoadPromise?.resolve() - prev._nonReactive.beforeLoadPromise = undefined - prev._nonReactive.loadPromise?.resolve() - - return { - ...prev, - error: err, - status: 'error', - isFetching: false, - updatedAt: Date.now(), - abortController: new AbortController(), - } - }) - - if (!inner.preload && !isRedirect(err) && !isNotFound(err)) { - inner.serialError ??= err - } -} - -const isBeforeLoadSsr = ( - inner: InnerLoadContext, - matchId: string, - index: number, - route: AnyRoute, -): void | Promise => { - const existingMatch = inner.router.getMatch(matchId)! - const parentMatchId = inner.matches[index - 1]?.id - const parentMatch = parentMatchId - ? inner.router.getMatch(parentMatchId)! - : undefined - - // in SPA mode, only SSR the root route - if (inner.router.isShell()) { - existingMatch.ssr = route.id === rootRouteId - return - } - - if (parentMatch?.ssr === false) { - existingMatch.ssr = false - return - } - - const parentOverride = (tempSsr: SSROption) => { - if (tempSsr === true && parentMatch?.ssr === 'data-only') { - return 'data-only' - } - return tempSsr - } - - const defaultSsr = inner.router.options.defaultSsr ?? true - - if (route.options.ssr === undefined) { - existingMatch.ssr = parentOverride(defaultSsr) - return - } - - if (typeof route.options.ssr !== 'function') { - existingMatch.ssr = parentOverride(route.options.ssr) - return - } - const { search, params } = existingMatch - - const ssrFnContext: SsrContextOptions = { - search: makeMaybe(search, existingMatch.searchError), - params: makeMaybe(params, existingMatch.paramsError), - location: inner.location, - matches: inner.matches.map((match) => ({ - index: match.index, - pathname: match.pathname, - fullPath: match.fullPath, - staticData: match.staticData, - id: match.id, - routeId: match.routeId, - search: makeMaybe(match.search, match.searchError), - params: makeMaybe(match.params, match.paramsError), - ssr: match.ssr, - })), - } - - const tempSsr = route.options.ssr(ssrFnContext) - if (isPromise(tempSsr)) { - return tempSsr.then((ssr) => { - existingMatch.ssr = parentOverride(ssr ?? defaultSsr) - }) - } - - existingMatch.ssr = parentOverride(tempSsr ?? defaultSsr) - return -} - -const setupPendingTimeout = ( - inner: InnerLoadContext, - matchId: string, - route: AnyRoute, - match: AnyRouteMatch, -): void => { - if (match._nonReactive.pendingTimeout !== undefined) return - - const pendingMs = - route.options.pendingMs ?? inner.router.options.defaultPendingMs - const shouldPending = !!( - inner.onReady && - !(isServer ?? inner.router.isServer) && - !resolvePreload(inner, matchId) && - (route.options.loader || - route.options.beforeLoad || - routeNeedsPreload(route)) && - typeof pendingMs === 'number' && - pendingMs !== Infinity && - (route.options.pendingComponent ?? - (inner.router.options as any)?.defaultPendingComponent) - ) - - 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 - } -} - -const preBeforeLoadSetup = ( - inner: InnerLoadContext, - matchId: string, - route: AnyRoute, -): void | Promise => { - const existingMatch = inner.router.getMatch(matchId)! - - // If we are in the middle of a load, either of these will be present - // (not to be confused with `loadPromise`, which is always defined) - if ( - !existingMatch._nonReactive.beforeLoadPromise && - !existingMatch._nonReactive.loaderPromise - ) - return - - setupPendingTimeout(inner, matchId, route, existingMatch) - - const then = () => { - const match = inner.router.getMatch(matchId)! - if ( - match.preload && - (match.status === 'redirected' || match.status === 'notFound') - ) { - handleRedirectAndNotFound(inner, match, match.error) - } - } - - // Wait for the previous beforeLoad to resolve before we continue - return existingMatch._nonReactive.beforeLoadPromise - ? existingMatch._nonReactive.beforeLoadPromise.then(then) - : then() -} - -const executeBeforeLoad = ( - inner: InnerLoadContext, - matchId: string, - index: number, - route: AnyRoute, -): void | Promise => { - const match = inner.router.getMatch(matchId)! - - // explicitly capture the previous loadPromise - let prevLoadPromise = match._nonReactive.loadPromise - match._nonReactive.loadPromise = createControlledPromise(() => { - prevLoadPromise?.resolve() - prevLoadPromise = undefined - }) - - const { paramsError, searchError } = match - - if (paramsError) { - handleSerialError(inner, index, paramsError) - } - - if (searchError) { - handleSerialError(inner, index, searchError) - } - - setupPendingTimeout(inner, matchId, route, match) - - const abortController = new AbortController() - - let isPending = false - const pending = () => { - if (isPending) return - isPending = true - inner.updateMatch(matchId, (prev) => ({ - ...prev, - isFetching: 'beforeLoad', - fetchCount: prev.fetchCount + 1, - abortController, - // Note: We intentionally don't update context here. - // Context should only be updated after beforeLoad resolves to avoid - // components seeing incomplete context during async beforeLoad execution. - })) - } - - const resolve = () => { - match._nonReactive.beforeLoadPromise?.resolve() - match._nonReactive.beforeLoadPromise = undefined - inner.updateMatch(matchId, (prev) => ({ - ...prev, - isFetching: false, - })) - } - - // if there is no `beforeLoad` option, just mark as pending and resolve - // Context will be updated later in loadRouteMatch after loader completes - if (!route.options.beforeLoad) { - inner.router.batch(() => { - pending() - resolve() - }) - return - } - - match._nonReactive.beforeLoadPromise = createControlledPromise() - - // Build context from all parent matches, excluding current match's __beforeLoadContext - // (since we're about to execute beforeLoad for this match) - const context = { - ...buildMatchContext(inner, index, false), - ...match.__routeContext, - } - const { search, params, cause } = match - const preload = resolvePreload(inner, matchId) - const beforeLoadFnContext: BeforeLoadContextOptions< - any, - any, - any, - any, - any, - any, - any, - any, - any - > = { - search, - abortController, - params, - preload, - context, - location: inner.location, - navigate: (opts: any) => - inner.router.navigate({ - ...opts, - _fromLocation: inner.location, - }), - buildLocation: inner.router.buildLocation, - cause: preload ? 'preload' : cause, - matches: inner.matches, - routeId: route.id, - ...inner.router.options.additionalContext, - } - - const updateContext = (beforeLoadContext: any) => { - if (beforeLoadContext === undefined) { - inner.router.batch(() => { - pending() - resolve() - }) - return - } - if (isRedirect(beforeLoadContext) || isNotFound(beforeLoadContext)) { - pending() - handleSerialError(inner, index, beforeLoadContext) - } - - inner.router.batch(() => { - pending() - inner.updateMatch(matchId, (prev) => ({ - ...prev, - __beforeLoadContext: beforeLoadContext, - })) - resolve() - }) - } - - let beforeLoadContext - try { - beforeLoadContext = route.options.beforeLoad(beforeLoadFnContext) - if (isPromise(beforeLoadContext)) { - pending() - return beforeLoadContext - .catch((err) => { - handleSerialError(inner, index, err) - }) - .then(updateContext) - } - } catch (err) { - pending() - handleSerialError(inner, index, err) - } - - updateContext(beforeLoadContext) - return -} - -const handleBeforeLoad = ( - inner: InnerLoadContext, - index: number, -): void | Promise => { - const { id: matchId, routeId } = inner.matches[index]! - const route = inner.router.looseRoutesById[routeId]! - - const serverSsr = () => { - // on the server, determine whether SSR the current match or not - if (isServer ?? inner.router.isServer) { - const maybePromise = isBeforeLoadSsr(inner, matchId, index, route) - if (isPromise(maybePromise)) return maybePromise.then(queueExecution) - } - return queueExecution() - } - - const execute = () => executeBeforeLoad(inner, matchId, index, route) - - const queueExecution = () => { - if (shouldSkipLoader(inner, matchId)) return - const result = preBeforeLoadSetup(inner, matchId, route) - return isPromise(result) ? result.then(execute) : execute() - } - - return serverSsr() -} - -const executeHead = ( - inner: InnerLoadContext, - matchId: string, - route: AnyRoute, -): void | Promise< - Pick< - AnyRouteMatch, - 'meta' | 'links' | 'headScripts' | 'headers' | 'scripts' | 'styles' - > -> => { - const match = inner.router.getMatch(matchId) - // in case of a redirecting match during preload, the match does not exist - if (!match) { - return - } - if (!route.options.head && !route.options.scripts && !route.options.headers) { - return - } - const assetContext = { - ssr: inner.router.options.ssr, - matches: inner.matches, - match, - params: match.params, - loaderData: match.loaderData, - } - - return Promise.all([ - route.options.head?.(assetContext), - route.options.scripts?.(assetContext), - route.options.headers?.(assetContext), - ]).then(([headFnContent, scripts, headers]) => { - const meta = headFnContent?.meta - const links = headFnContent?.links - const headScripts = headFnContent?.scripts - const styles = headFnContent?.styles - - return { - meta, - links, - headScripts, - headers, - scripts, - styles, - } - }) -} - -const getLoaderContext = ( - inner: InnerLoadContext, - matchPromises: Array>, - matchId: string, - index: number, - route: AnyRoute, -): LoaderFnContext => { - const parentMatchPromise = matchPromises[index - 1] as any - const { params, loaderDeps, abortController, cause } = - inner.router.getMatch(matchId)! - - const context = buildMatchContext(inner, index) - - const preload = resolvePreload(inner, matchId) - - return { - params, - deps: loaderDeps, - preload: !!preload, - parentMatchPromise, - abortController, - context, - location: inner.location, - navigate: (opts) => - inner.router.navigate({ - ...opts, - _fromLocation: inner.location, - }), - cause: preload ? 'preload' : cause, - route, - ...inner.router.options.additionalContext, - } -} - -const runLoader = async ( - inner: InnerLoadContext, - matchPromises: Array>, - matchId: string, - index: number, - route: AnyRoute, -): Promise => { - try { - // If the Matches component rendered - // the pending component and needs to show it for - // a minimum duration, we''ll wait for it to resolve - // before committing to the match and resolving - // the loadPromise - - const match = inner.router.getMatch(matchId)! - - // Actually run the loader and handle the result - try { - if (!(isServer ?? inner.router.isServer) || match.ssr === true) { - loadRouteChunk(route) - } - - // Kick off the loader! - const routeLoader = route.options.loader - const loader = - typeof routeLoader === 'function' ? routeLoader : routeLoader?.handler - const loaderResult = loader?.( - getLoaderContext(inner, matchPromises, matchId, index, route), - ) - const loaderResultIsPromise = !!loader && isPromise(loaderResult) - - const willLoadSomething = !!( - loaderResultIsPromise || - route._lazyPromise || - route._componentsPromise || - route.options.head || - route.options.scripts || - route.options.headers || - match._nonReactive.minPendingPromise - ) - - if (willLoadSomething) { - inner.updateMatch(matchId, (prev) => ({ - ...prev, - isFetching: 'loader', - })) - } - - if (loader) { - const loaderData = loaderResultIsPromise - ? await loaderResult - : loaderResult - - handleRedirectAndNotFound( - inner, - inner.router.getMatch(matchId), - loaderData, - ) - if (loaderData !== undefined) { - inner.updateMatch(matchId, (prev) => ({ - ...prev, - loaderData, - })) - } - } - - // Lazy option can modify the route options, - // so we need to wait for it to resolve before - // we can use the options - if (route._lazyPromise) await route._lazyPromise - const pendingPromise = match._nonReactive.minPendingPromise - if (pendingPromise) await pendingPromise - - // Last but not least, wait for the components - // to be preloaded before we resolve the match - if (route._componentsPromise) await route._componentsPromise - inner.updateMatch(matchId, (prev) => ({ - ...prev, - error: undefined, - context: buildMatchContext(inner, index), - status: 'success', - isFetching: false, - updatedAt: Date.now(), - })) - } catch (e) { - let error = e - - if ((error as any)?.name === 'AbortError') { - if (match.abortController.signal.aborted) { - match._nonReactive.loaderPromise?.resolve() - match._nonReactive.loaderPromise = undefined - return - } - inner.updateMatch(matchId, (prev) => ({ - ...prev, - status: prev.status === 'pending' ? 'success' : prev.status, - isFetching: false, - context: buildMatchContext(inner, index), - })) - return - } - - const pendingPromise = match._nonReactive.minPendingPromise - if (pendingPromise) await pendingPromise - - if (isNotFound(e)) { - await (route.options.notFoundComponent as any)?.preload?.() - } - - handleRedirectAndNotFound(inner, inner.router.getMatch(matchId), e) - - try { - route.options.onError?.(e) - } catch (onErrorError) { - error = onErrorError - handleRedirectAndNotFound( - inner, - inner.router.getMatch(matchId), - onErrorError, - ) - } - if (!isRedirect(error) && !isNotFound(error)) { - await loadRouteChunk(route, ['errorComponent']) - } - - inner.updateMatch(matchId, (prev) => ({ - ...prev, - error, - context: buildMatchContext(inner, index), - status: 'error', - isFetching: false, - })) - } - } catch (err) { - const match = inner.router.getMatch(matchId) - // in case of a redirecting match during preload, the match does not exist - if (match) { - match._nonReactive.loaderPromise = undefined - } - handleRedirectAndNotFound(inner, match, err) - } -} - -const loadRouteMatch = async ( - inner: InnerLoadContext, - matchPromises: Array>, - index: number, -): Promise => { - async function handleLoader( - preload: boolean, - prevMatch: AnyRouteMatch, - previousRouteMatchId: string | undefined, - match: AnyRouteMatch, - route: AnyRoute, - ) { - const age = Date.now() - prevMatch.updatedAt - - const staleAge = preload - ? (route.options.preloadStaleTime ?? - inner.router.options.defaultPreloadStaleTime ?? - 30_000) // 30 seconds for preloads by default - : (route.options.staleTime ?? inner.router.options.defaultStaleTime ?? 0) - - const shouldReloadOption = route.options.shouldReload - - // Default to reloading the route all the time - // Allow shouldReload to get the last say, - // if provided. - const shouldReload = - typeof shouldReloadOption === 'function' - ? shouldReloadOption( - getLoaderContext(inner, matchPromises, matchId, index, route), - ) - : shouldReloadOption - - // If the route is successful and still fresh, just resolve - const { status, invalid } = match - const staleMatchShouldReload = - age >= staleAge && - (!!inner.forceStaleReload || - match.cause === 'enter' || - (previousRouteMatchId !== undefined && - previousRouteMatchId !== match.id)) - loaderShouldRunAsync = - status === 'success' && - (invalid || (shouldReload ?? staleMatchShouldReload)) - if (preload && route.options.preload === false) { - // Do nothing - } else if ( - loaderShouldRunAsync && - !inner.sync && - shouldReloadInBackground - ) { - loaderIsRunningAsync = true - ;(async () => { - try { - await runLoader(inner, matchPromises, matchId, index, route) - const match = inner.router.getMatch(matchId)! - match._nonReactive.loaderPromise?.resolve() - match._nonReactive.loadPromise?.resolve() - match._nonReactive.loaderPromise = undefined - match._nonReactive.loadPromise = undefined - } catch (err) { - if (isRedirect(err)) { - await inner.router.navigate(err.options) - } - } - })() - } else if (status !== 'success' || loaderShouldRunAsync) { - await runLoader(inner, matchPromises, matchId, index, route) - } else { - syncMatchContext(inner, matchId, index) - } - } - - const { id: matchId, routeId } = inner.matches[index]! - let loaderShouldRunAsync = false - let loaderIsRunningAsync = false - const route = inner.router.looseRoutesById[routeId]! - const routeLoader = route.options.loader - const shouldReloadInBackground = - ((typeof routeLoader === 'function' - ? undefined - : routeLoader?.staleReloadMode) ?? - inner.router.options.defaultStaleReloadMode) !== 'blocking' - - if (shouldSkipLoader(inner, matchId)) { - const match = inner.router.getMatch(matchId) - if (!match) { - return inner.matches[index]! - } - - syncMatchContext(inner, matchId, index) - - if (isServer ?? inner.router.isServer) { - return inner.router.getMatch(matchId)! - } - } else { - const prevMatch = inner.router.getMatch(matchId)! // This is where all of the stale-while-revalidate magic happens - const activeIdAtIndex = inner.router.stores.matchesId.get()[index] - const activeAtIndex = - (activeIdAtIndex && - inner.router.stores.matchStores.get(activeIdAtIndex)) || - null - const previousRouteMatchId = - activeAtIndex?.routeId === routeId - ? activeIdAtIndex - : inner.router.stores.matches.get().find((d) => d.routeId === routeId) - ?.id - const preload = resolvePreload(inner, matchId) - - // there is a loaderPromise, so we are in the middle of a load - if (prevMatch._nonReactive.loaderPromise) { - // do not block if we already have stale data we can show - // but only if the ongoing load is not a preload since error handling is different for preloads - // and we don't want to swallow errors - if ( - prevMatch.status === 'success' && - !inner.sync && - !prevMatch.preload && - shouldReloadInBackground - ) { - return prevMatch - } - await prevMatch._nonReactive.loaderPromise - const match = inner.router.getMatch(matchId)! - const error = match._nonReactive.error || match.error - if (error) { - handleRedirectAndNotFound(inner, match, error) - } - - if (match.status === 'pending') { - await handleLoader( - preload, - prevMatch, - previousRouteMatchId, - match, - route, - ) - } - } else { - const nextPreload = - preload && !inner.router.stores.matchStores.has(matchId) - const match = inner.router.getMatch(matchId)! - match._nonReactive.loaderPromise = createControlledPromise() - if (nextPreload !== match.preload) { - inner.updateMatch(matchId, (prev) => ({ - ...prev, - preload: nextPreload, - })) - } - - await handleLoader(preload, prevMatch, previousRouteMatchId, match, route) - } - } - const match = inner.router.getMatch(matchId)! - if (!loaderIsRunningAsync) { - match._nonReactive.loaderPromise?.resolve() - match._nonReactive.loadPromise?.resolve() - match._nonReactive.loadPromise = undefined - } - - clearTimeout(match._nonReactive.pendingTimeout) - match._nonReactive.pendingTimeout = undefined - if (!loaderIsRunningAsync) match._nonReactive.loaderPromise = undefined - match._nonReactive.dehydrated = undefined - - const nextIsFetching = loaderIsRunningAsync ? match.isFetching : false - if (nextIsFetching !== match.isFetching || match.invalid !== false) { - inner.updateMatch(matchId, (prev) => ({ - ...prev, - isFetching: nextIsFetching, - invalid: false, - })) - return inner.router.getMatch(matchId)! - } else { - return match - } -} - -export async function loadMatches(arg: { - router: AnyRouter - location: ParsedLocation - matches: Array - preload?: boolean - forceStaleReload?: boolean - onReady?: () => Promise - updateMatch: UpdateMatchFn - sync?: boolean -}): Promise> { - const inner: InnerLoadContext = arg - const matchPromises: Array> = [] - - // make sure the pending component is immediately rendered when hydrating a match that is not SSRed - // the pending component was already rendered on the server and we want to keep it shown on the client until minPendingMs is reached - if ( - !(isServer ?? inner.router.isServer) && - hasForcePendingActiveMatch(inner.router) - ) { - triggerOnReady(inner) - } - - let beforeLoadNotFound: NotFoundError | undefined - - // Execute all beforeLoads one by one - for (let i = 0; i < inner.matches.length; i++) { - try { - const beforeLoad = handleBeforeLoad(inner, i) - if (isPromise(beforeLoad)) await beforeLoad - } catch (err) { - if (isRedirect(err)) { - throw err - } - if (isNotFound(err)) { - beforeLoadNotFound = err - } else { - if (!inner.preload) throw err - } - break - } - - if (inner.serialError || inner.firstBadMatchIndex != null) { - break - } - } - - // Execute loaders once, with max index adapted for beforeLoad notFound handling. - const baseMaxIndexExclusive = inner.firstBadMatchIndex ?? inner.matches.length - - const boundaryIndex = - beforeLoadNotFound && !inner.preload - ? getNotFoundBoundaryIndex(inner, beforeLoadNotFound) - : undefined - - const maxIndexExclusive = - beforeLoadNotFound && inner.preload - ? 0 - : boundaryIndex !== undefined - ? Math.min(boundaryIndex + 1, baseMaxIndexExclusive) - : baseMaxIndexExclusive - - let firstNotFound: NotFoundError | undefined - let firstUnhandledRejection: unknown - - for (let i = 0; i < maxIndexExclusive; i++) { - matchPromises.push(loadRouteMatch(inner, matchPromises, i)) - } - - try { - await Promise.all(matchPromises) - } catch { - const settled = await Promise.allSettled(matchPromises) - - for (const result of settled) { - if (result.status !== 'rejected') continue - - const reason = result.reason - if (isRedirect(reason)) { - throw reason - } - if (isNotFound(reason)) { - firstNotFound ??= reason - } else { - firstUnhandledRejection ??= reason - } - } - - if (firstUnhandledRejection !== undefined) { - throw firstUnhandledRejection - } - } - - const notFoundToThrow = - firstNotFound ?? - (beforeLoadNotFound && !inner.preload ? beforeLoadNotFound : undefined) - - let headMaxIndex = - inner.firstBadMatchIndex !== undefined - ? inner.firstBadMatchIndex - : inner.matches.length - 1 - - if (!notFoundToThrow && beforeLoadNotFound && inner.preload) { - return inner.matches - } - - if (notFoundToThrow) { - // Determine once which matched route will actually render the - // notFoundComponent, then pass this precomputed index through the remaining - // finalization steps. - // This can differ from the throwing route when routeId targets an ancestor - // boundary (or when bubbling resolves to a parent/root boundary). - const renderedBoundaryIndex = getNotFoundBoundaryIndex( - inner, - notFoundToThrow, - ) - - if (renderedBoundaryIndex === undefined) { - if (process.env.NODE_ENV !== 'production') { - throw new Error( - 'Invariant failed: Could not find match for notFound boundary', - ) - } - - invariant() - } - const boundaryMatch = inner.matches[renderedBoundaryIndex]! - - const boundaryRoute = inner.router.looseRoutesById[boundaryMatch.routeId]! - const defaultNotFoundComponent = (inner.router.options as any) - ?.defaultNotFoundComponent - - // Ensure a notFoundComponent exists on the boundary route - if (!boundaryRoute.options.notFoundComponent && defaultNotFoundComponent) { - boundaryRoute.options.notFoundComponent = defaultNotFoundComponent - } - - notFoundToThrow.routeId = boundaryMatch.routeId - - const boundaryIsRoot = boundaryMatch.routeId === inner.router.routeTree.id - - inner.updateMatch(boundaryMatch.id, (prev) => ({ - ...prev, - ...(boundaryIsRoot - ? // For root boundary, use globalNotFound so the root component's - // shell still renders and handles the not-found display, - // instead of replacing the entire root shell via status='notFound'. - { status: 'success' as const, globalNotFound: true, error: undefined } - : // For non-root boundaries, set status:'notFound' so MatchInner - // renders the notFoundComponent directly. - { status: 'notFound' as const, error: notFoundToThrow }), - isFetching: false, - })) - - headMaxIndex = renderedBoundaryIndex - - // Ensure the rendering boundary route chunk (and its lazy components, including - // lazy notFoundComponent) is loaded before we continue to head execution/render. - await loadRouteChunk(boundaryRoute, ['notFoundComponent']) - } else if (!inner.preload) { - // Clear stale root global-not-found state on normal navigations that do not - // throw notFound. This must live here (instead of only in runLoader success) - // because the root loader may be skipped when data is still fresh. - const rootMatch = inner.matches[0]! - // `rootMatch` is the next match for this navigation. If it is not global - // not-found, then any currently stored root global-not-found is stale. - if (!rootMatch.globalNotFound) { - // `currentRootMatch` is the current store state (from the previous - // navigation/load). Update only when a stale flag is actually present. - const currentRootMatch = inner.router.getMatch(rootMatch.id) - if (currentRootMatch?.globalNotFound) { - inner.updateMatch(rootMatch.id, (prev) => ({ - ...prev, - globalNotFound: false, - error: undefined, - })) - } - } - } - - // When a serial error occurred (e.g. beforeLoad threw a regular Error), - // the erroring route's lazy chunk wasn't loaded because loaders were skipped. - // We need to load it so the code-split errorComponent is available for rendering. - if (inner.serialError && inner.firstBadMatchIndex !== undefined) { - const errorRoute = - inner.router.looseRoutesById[ - inner.matches[inner.firstBadMatchIndex]!.routeId - ]! - await loadRouteChunk(errorRoute, ['errorComponent']) - } - - // serially execute heads once after loaders/notFound handling, ensuring - // all head functions get a chance even if one throws. - for (let i = 0; i <= headMaxIndex; i++) { - const match = inner.matches[i]! - const { id: matchId, routeId } = match - const route = inner.router.looseRoutesById[routeId]! - try { - const headResult = executeHead(inner, matchId, route) - if (headResult) { - const head = await headResult - inner.updateMatch(matchId, (prev) => ({ - ...prev, - ...head, - })) - } - } catch (err) { - console.error(`Error executing head for route ${routeId}:`, err) - } - } - - const readyPromise = triggerOnReady(inner) - if (isPromise(readyPromise)) { - await readyPromise - } - - if (notFoundToThrow) { - throw notFoundToThrow - } - - if (inner.serialError && !inner.preload && !inner.onReady) { - throw inner.serialError - } - - return inner.matches -} - -export type RouteComponentType = - | 'component' - | 'errorComponent' - | 'pendingComponent' - | 'notFoundComponent' - -function preloadRouteComponents( - route: AnyRoute, - componentTypesToLoad: Array, -): Promise | undefined { - const preloads = componentTypesToLoad - .map((type) => (route.options[type] as any)?.preload?.()) - .filter(Boolean) - - if (preloads.length === 0) return undefined - - return Promise.all(preloads) as any as Promise -} - -export function loadRouteChunk( - route: AnyRoute, - componentTypesToLoad: Array = componentTypes, -) { - if (!route._lazyLoaded && route._lazyPromise === undefined) { - if (route.lazyFn) { - route._lazyPromise = route.lazyFn().then((lazyRoute) => { - // explicitly don't copy over the lazy route's id - const { id: _id, ...options } = lazyRoute.options - Object.assign(route.options, options) - route._lazyLoaded = true - route._lazyPromise = undefined // gc promise, we won't need it anymore - }) - } else { - route._lazyLoaded = true - } - } - - const runAfterLazy = () => - route._componentsLoaded - ? undefined - : componentTypesToLoad === componentTypes - ? (() => { - if (route._componentsPromise === undefined) { - const componentsPromise = preloadRouteComponents( - route, - componentTypes, - ) - - if (componentsPromise) { - route._componentsPromise = componentsPromise.then(() => { - route._componentsLoaded = true - route._componentsPromise = undefined // gc promise, we won't need it anymore - }) - } else { - route._componentsLoaded = true - } - } - - return route._componentsPromise - })() - : preloadRouteComponents(route, componentTypesToLoad) - - return route._lazyPromise - ? route._lazyPromise.then(runAfterLazy) - : runAfterLazy() -} - -function makeMaybe( - value: TValue, - error: TError, -): { status: 'success'; value: TValue } | { status: 'error'; error: TError } { - if (error) { - return { status: 'error' as const, error } - } - return { status: 'success' as const, value } -} - -export function routeNeedsPreload(route: AnyRoute) { - for (const componentType of componentTypes) { - if ((route.options[componentType] as any)?.preload) { - return true - } - } - return false -} - -export const componentTypes: Array = [ - 'component', - 'errorComponent', - 'pendingComponent', - 'notFoundComponent', -] as const diff --git a/packages/router-core/src/load-server.ts b/packages/router-core/src/load-server.ts new file mode 100644 index 0000000000..083d7ffb40 --- /dev/null +++ b/packages/router-core/src/load-server.ts @@ -0,0 +1,849 @@ +// Keep this filename free of a secondary extension so declaration generation +// can rewrite relative imports for both ESM and CJS. +import { isNotFound } from './not-found' +import { isRedirect, redirect } from './redirect' +import { rootRouteId } from './root' +import { loadRouteChunk } from './load-client' +import { waitForReason } from './await-signal' +import { getLocationChangeInfo, runRouteLifecycle } from './router' +import type { ParsedLocation } from './location' +import type { AnyRouteMatch } from './Matches' +import type { NotFoundError } from './not-found' +import type { + AnyRoute, + BeforeLoadContextOptions, + LoaderFnContext, + RouteContextOptions, + SsrContextOptions, +} from './route' +import type { AnyRedirect } from './redirect' +import type { AnyRouter, SSROption } from './router' + +declare const serverLanePhase: unique symbol + +type ServerLane = { + readonly [serverLanePhase]: TPhase + location: ParsedLocation + matches: Array +} + +type MatchedLane = ServerLane<'matched'> + +type IndexedOutcome = [index: number, outcome: LoaderOutcome, boundary?: number] + +type ContextualizedLane = ServerLane<'contextualized'> & { + end: number + failure?: IndexedOutcome +} + +type ReducedLane = ServerLane<'reduced'> + +const SUCCESS = 0 +const ERROR = 1 +const NOT_FOUND = 2 +const REDIRECTED = 3 +const SKIPPED = 4 + +type LoaderOutcome = + | [typeof SUCCESS, data: unknown] + | [typeof ERROR, error: unknown] + | [typeof NOT_FOUND, error: NotFoundError] + | [typeof REDIRECTED, redirect: AnyRedirect] + | [typeof SKIPPED] + +type LoaderTask = { + index: number + outcome: Promise + match: Promise +} + +export type ServerLoadResult = + | { + type: 'render' + status: 200 | 404 | 500 + matches: Array + } + | { type: 'redirect'; redirect: AnyRedirect } + +function getRoute(router: AnyRouter, match: AnyRouteMatch): AnyRoute { + return router.routesById[match.routeId] +} + +function normalize(value: unknown, rejected: boolean): LoaderOutcome { + if (isRedirect(value)) { + return [REDIRECTED, value] + } + if (isNotFound(value)) { + return [NOT_FOUND, value] + } + if (rejected && typeof (value as any)?.then === 'function') { + value = new Error('A Promise was thrown', { cause: value }) + } + return rejected ? [ERROR, value] : [SUCCESS, value] +} + +function normalizeError(route: AnyRoute, cause: unknown): LoaderOutcome { + let outcome = normalize(cause, true) + if (outcome[0] !== ERROR) { + return outcome + } + try { + route.options.onError?.(outcome[1]) + } catch (onErrorCause) { + outcome = normalize(onErrorCause, true) + } + return outcome +} + +function maybe( + value: TValue, + cause: unknown, +): { status: 'success'; value: TValue } | { status: 'error'; error: unknown } { + if (cause !== undefined) { + return { status: 'error', error: cause } + } + return { status: 'success', value } +} + +function navigateFrom(router: AnyRouter, location: ParsedLocation) { + return (options: any) => + router.navigate({ + ...options, + _fromLocation: location, + }) +} + +function waitFor(value: Promise, signal?: AbortSignal): Promise { + return signal ? waitForReason(value, signal) : value +} + +async function resolveSsr( + router: AnyRouter, + lane: MatchedLane, + index: number, +): Promise { + const match = lane.matches[index]! + const route = getRoute(router, match) + const parentSsr = lane.matches[index - 1]?.ssr + + if (router.isShell()) { + return route.id === rootRouteId + } + if (parentSsr === false) { + return false + } + + const inherit = (value: SSROption): SSROption => { + return value === true && parentSsr === 'data-only' ? 'data-only' : value + } + const defaultSsr = router.options.defaultSsr ?? true + const inheritedDefault = inherit(defaultSsr) + // A functional override can fail. Establish the inherited policy first so + // the selected error boundary retains the route's actual renderability. + match.ssr = inheritedDefault + const option = route.options.ssr + if (option === undefined) { + return inheritedDefault + } + if (typeof option !== 'function') { + return inherit(option) + } + + const context: SsrContextOptions = { + search: maybe(match.search, match.searchError), + params: maybe(match.params, match.paramsError), + location: lane.location, + matches: lane.matches.map((candidate) => ({ + index: candidate.index, + pathname: candidate.pathname, + fullPath: candidate.fullPath, + staticData: candidate.staticData, + id: candidate.id, + routeId: candidate.routeId, + search: maybe(candidate.search, candidate.searchError), + params: maybe(candidate.params, candidate.paramsError), + ssr: candidate.ssr, + })), + } + return inherit((await option(context)) ?? defaultSsr) +} + +function stampNotFound( + match: AnyRouteMatch, + outcome: LoaderOutcome, +): LoaderOutcome { + if (outcome[0] === NOT_FOUND && !outcome[1].routeId) { + outcome[1].routeId = match.routeId + } + return outcome +} + +async function contextualize( + router: AnyRouter, + lane: MatchedLane, + signal?: AbortSignal, +): Promise { + const globalBoundary = lane.matches.findIndex((match) => match._notFound) + let end = globalBoundary < 0 ? lane.matches.length : globalBoundary + 1 + let failure: IndexedOutcome | undefined + let parentContext: Record = { + ...(router.options.context ?? {}), + } + + for (let index = 0; index < end; index++) { + const match = lane.matches[index]! + const route = getRoute(router, match) + try { + match.ssr = await resolveSsr(router, lane, index) + } catch (cause) { + signal?.throwIfAborted() + failure = [index, stampNotFound(match, normalizeError(route, cause))] + end = index + } + signal?.throwIfAborted() + if (failure?.[1][0] === REDIRECTED) { + break + } + + match.__beforeLoadContext = undefined + let context = parentContext + try { + let routeContext + if (route.options.context) { + const routeContextOptions: RouteContextOptions< + any, + any, + any, + any, + any + > = { + deps: match.loaderDeps, + params: match.params, + context: parentContext, + location: lane.location, + navigate: navigateFrom(router, lane.location), + buildLocation: router.buildLocation, + cause: match.cause, + abortController: match.abortController, + preload: false, + matches: lane.matches, + routeId: route.id, + } + routeContext = route.options.context(routeContextOptions) ?? undefined + } + context = { + ...parentContext, + ...routeContext, + } + match.context = context + } catch (cause) { + signal?.throwIfAborted() + if (!failure) { + failure = [index, stampNotFound(match, normalizeError(route, cause))] + } + end = index + break + } + signal?.throwIfAborted() + if (failure) { + break + } + const validationError = match.paramsError ?? match.searchError + if (validationError !== undefined) { + failure = [ + index, + stampNotFound(match, normalizeError(route, validationError)), + ] + end = index + break + } + signal?.throwIfAborted() + + if (match.ssr === false || !route.options.beforeLoad) { + parentContext = context + continue + } + + const abortController = match.abortController + const options: BeforeLoadContextOptions< + any, + any, + any, + any, + any, + any, + any, + any, + any + > = { + search: match.search, + abortController, + params: match.params, + preload: false, + context, + location: lane.location, + navigate: navigateFrom(router, lane.location), + buildLocation: router.buildLocation, + cause: match.cause, + matches: lane.matches, + routeId: route.id, + ...router.options.additionalContext, + } + + try { + const beforeLoadContext = await route.options.beforeLoad(options) + signal?.throwIfAborted() + const outcome = stampNotFound(match, normalize(beforeLoadContext, false)) + if (outcome[0] !== SUCCESS) { + failure = [index, outcome] + end = index + break + } + match.__beforeLoadContext = beforeLoadContext + match.context = { + ...context, + ...beforeLoadContext, + } + parentContext = match.context + } catch (cause) { + signal?.throwIfAborted() + failure = [index, stampNotFound(match, normalizeError(route, cause))] + end = index + break + } + } + + return { + location: lane.location, + matches: lane.matches, + end, + failure, + } as ContextualizedLane +} + +function getLoaderContext( + router: AnyRouter, + lane: ContextualizedLane, + match: AnyRouteMatch, + route: AnyRoute, + index: number, + tasks: Array, +): LoaderFnContext { + return { + params: match.params, + deps: match.loaderDeps, + preload: false, + parentMatchPromise: tasks[index - 1]?.match, + abortController: match.abortController, + context: match.context, + location: lane.location, + navigate: navigateFrom(router, lane.location), + cause: match.cause, + route, + ...router.options.additionalContext, + } +} + +function createLoaderTask( + router: AnyRouter, + lane: ContextualizedLane, + index: number, + tasks: Array, + signal?: AbortSignal, +): LoaderTask { + const match = lane.matches[index]! + const route = getRoute(router, match) + let outcome: Promise + + if (match.ssr === false) { + outcome = Promise.resolve([SKIPPED]) + } else { + const routeLoader = route.options.loader + const loader = + typeof routeLoader === 'function' ? routeLoader : routeLoader?.handler + if (!loader) { + outcome = Promise.resolve([SUCCESS, undefined]) + } else { + outcome = Promise.resolve() + .then(() => + loader(getLoaderContext(router, lane, match, route, index, tasks)), + ) + .then( + (result) => normalize(result, false), + (cause) => normalize(cause, true), + ) + .then((result): LoaderOutcome => { + if ( + result[0] !== REDIRECTED && + (signal?.aborted || match.abortController.signal.reason === lane) + ) { + return [SKIPPED] + } + if (result[0] === ERROR) { + result = normalizeError(route, result[1]) + } + return stampNotFound(match, result) + }) + } + } + + const parentMatch = outcome.then((result) => { + const snapshot = { ...match } + if (result[0] === SUCCESS) { + snapshot.loaderData = result[1] + snapshot.status = 'success' + snapshot.error = undefined + snapshot.invalid = false + snapshot.isFetching = false + } else if (result[0] === ERROR) { + snapshot.status = 'error' + snapshot.error = result[1] + } else if (result[0] === NOT_FOUND) { + snapshot.status = 'notFound' + snapshot.error = result[1] + } + return snapshot + }) + + return { index, outcome, match: parentMatch } +} + +async function getNotFoundBoundary( + router: AnyRouter, + matches: Array, + indexed: IndexedOutcome | undefined, + signal?: AbortSignal, + fallback = 0, +): Promise { + const cause = indexed?.[1][1] as NotFoundError | undefined + let index = cause?.routeId + ? matches.findIndex((match) => match.routeId === cause.routeId) + : (indexed?.[0] ?? matches.length - 1) + if (index < 0) { + index = 0 + } + for (let candidate = index; candidate >= 0; candidate--) { + const route = getRoute(router, matches[candidate]!) + const loading = loadRouteChunk(route, false) + if (loading) { + try { + await loading + } catch { + signal?.throwIfAborted() + } + } + signal?.throwIfAborted() + if (route.options.notFoundComponent) { + return candidate + } + } + return cause?.routeId ? index : fallback +} + +function abortMatches( + matches: Array, + start = 0, + reason?: unknown, +): void { + for (let index = start; index < matches.length; index++) { + matches[index]!.abortController.abort(reason) + } +} + +function resolveServerRedirect( + router: AnyRouter, + location: ParsedLocation, + value: AnyRedirect, +): ServerLoadResult { + value.options._fromLocation = location + return { type: 'redirect', redirect: router.resolveRedirect(value) } +} + +async function applyFailure( + router: AnyRouter, + lane: ContextualizedLane, + indexed: IndexedOutcome | undefined, + signal?: AbortSignal, +): Promise<{ status: 200 | 404 | 500; boundary?: number; kind?: number }> { + if (!indexed) { + const boundary = lane.matches.findIndex((match) => match._notFound) + if (boundary >= 0) { + abortMatches(lane.matches, boundary + 1) + return { status: 404, boundary, kind: NOT_FOUND } + } + return { status: 200 } + } + + const [index, outcome] = indexed + if (outcome[0] === ERROR) { + const match = lane.matches[index]! + match._notFound = undefined + match.status = 'error' + match.error = outcome[1] + match.isFetching = false + abortMatches(lane.matches, index + 1) + return { status: 500, boundary: index, kind: ERROR } + } + + const boundary = + indexed[2] ?? + (await getNotFoundBoundary(router, lane.matches, indexed, signal)) + const match = lane.matches[boundary]! + const cause = outcome[1] as NotFoundError + cause.routeId = match.routeId + match._notFound = undefined + if (match.routeId === router.routeTree.id) { + match.status = 'success' + match._notFound = true + match.error = cause + } else { + match.status = 'notFound' + match.error = cause + } + match.isFetching = false + abortMatches(lane.matches, boundary + 1) + return { status: 404, boundary, kind: NOT_FOUND } +} + +async function loadNormalChunks( + router: AnyRouter, + lane: ContextualizedLane, + end: number, + signal?: AbortSignal, +): Promise { + const chunks: Array> = [] + for (let index = 0; index < lane.matches.length; index++) { + const match = lane.matches[index]! + if (index >= end || match.ssr !== true || match.status !== 'success') { + continue + } + const route = getRoute(router, match) + try { + const loading = loadRouteChunk(route) + if (loading) { + const chunk = loading.then( + () => { + signal?.throwIfAborted() + return undefined + }, + (cause) => { + signal?.throwIfAborted() + return [ + index, + stampNotFound(match, normalizeError(route, cause)), + ] as IndexedOutcome + }, + ) + // Route-order reduction can return before later chunks settle. + void chunk.catch(() => {}) + chunks.push(chunk) + } + } catch (cause) { + signal?.throwIfAborted() + chunks.push([index, stampNotFound(match, normalizeError(route, cause))]) + } + } + for (const chunk of chunks) { + const indexed = Array.isArray(chunk) ? chunk : await chunk + if (indexed) { + return indexed + } + } + return undefined +} + +async function projectLane( + router: AnyRouter, + lane: ReducedLane, + signal?: AbortSignal, +): Promise { + for (const match of lane.matches) { + const routeOptions = getRoute(router, match).options + if (routeOptions.head || routeOptions.scripts || routeOptions.headers) { + const context = { + ssr: router.options.ssr, + matches: lane.matches, + match, + params: match.params, + loaderData: match.loaderData, + } + try { + const [head, scripts, headers] = await Promise.all([ + routeOptions.head?.(context), + routeOptions.scripts?.(context), + routeOptions.headers?.(context), + ]) + signal?.throwIfAborted() + match.meta = head?.meta + match.links = head?.links + match.headScripts = head?.scripts + match.styles = head?.styles + match.scripts = scripts + match.headers = headers + } catch (cause) { + signal?.throwIfAborted() + console.error(cause) + } + } + if (match.ssr === false || match.status !== 'success' || match._notFound) { + break + } + } +} + +async function executeServerLane( + router: AnyRouter, + location: ParsedLocation, + matchedMatches: Array, + signal?: AbortSignal, +): Promise { + const matched = { + location, + matches: matchedMatches.map((match) => ({ + ...match, + __beforeLoadContext: undefined, + context: {}, + isFetching: false, + abortController: new AbortController(), + })), + } as MatchedLane + const abortLane = () => abortMatches(matched.matches, 0, signal?.reason) + if (signal?.aborted) { + abortLane() + signal.throwIfAborted() + } + signal?.addEventListener('abort', abortLane, { once: true }) + + try { + const plannedGlobalBoundary = matched.matches.findIndex( + (match) => match._notFound, + ) + if (router.options.notFoundMode !== 'root' && plannedGlobalBoundary >= 0) { + const boundary = await getNotFoundBoundary( + router, + matched.matches, + undefined, + signal, + plannedGlobalBoundary, + ) + if (boundary !== plannedGlobalBoundary) { + matched.matches[plannedGlobalBoundary]!._notFound = undefined + matched.matches[boundary]!._notFound = true + } + } + const lane = await contextualize(router, matched, signal) + signal?.throwIfAborted() + + let loaderEnd = lane.end + if (lane.failure?.[1][0] === REDIRECTED) { + loaderEnd = 0 + } else if (lane.failure?.[1][0] === NOT_FOUND) { + lane.failure[2] = await getNotFoundBoundary( + router, + lane.matches, + lane.failure, + signal, + ) + loaderEnd = Math.min(loaderEnd, lane.failure[2] + 1) + } + + const tasks: Array = [] + for (let index = 0; index < loaderEnd; index++) { + const task = createLoaderTask(router, lane, index, tasks, signal) + tasks.push(task) + } + + let loaderFailure: IndexedOutcome | undefined + let control = lane.failure?.[1][0] === REDIRECTED ? lane.failure : undefined + try { + await Promise.all( + tasks.map((task) => + task.outcome.then((loadedOutcome) => { + const match = lane.matches[task.index]! + const outcome = loadedOutcome + if (outcome[0] === SUCCESS) { + match.loaderData = outcome[1] + match.status = 'success' + match.error = undefined + match.invalid = false + match.isFetching = false + match.updatedAt = Date.now() + } else if (outcome[0] === REDIRECTED) { + control = [task.index, outcome] + throw control + } else { + // A selective-SSR skip must stay pending for hydration. Every + // settled server attempt is otherwise renderable unless + // reduction selects it as the lane's terminal failure. + if (match.ssr !== false) { + match.status = 'success' + match.error = undefined + match.invalid = true + match.isFetching = false + } + if (!loaderFailure && outcome[0] !== SKIPPED) { + loaderFailure = [task.index, outcome] + } + } + }), + ), + ) + } catch (cause) { + if (!Array.isArray(cause)) { + throw cause + } + control = cause as IndexedOutcome + } + signal?.throwIfAborted() + + if (control?.[1][0] === REDIRECTED) { + abortMatches(lane.matches, 0, lane) + return resolveServerRedirect(router, location, control[1][1]) + } + + let failure = lane.failure ?? loaderFailure + const plannedBoundary = lane.matches.findIndex((match) => match._notFound) + let readinessEnd: number + if (failure) { + const outcomeEnd = (failure[2] ??= + failure[1][0] === NOT_FOUND + ? await getNotFoundBoundary(router, lane.matches, failure, signal) + : failure[0]) + for (const task of tasks) { + if (task.index >= outcomeEnd) { + break + } + const outcome = await task.outcome + // Presence means a loader previously succeeded, even with `undefined`. + if ( + outcome[0] !== SUCCESS && + outcome[0] < REDIRECTED && + !('loaderData' in lane.matches[task.index]!) + ) { + failure = [task.index, outcome] + failure[2] = + outcome[0] === NOT_FOUND + ? await getNotFoundBoundary(router, lane.matches, failure, signal) + : task.index + break + } + } + readinessEnd = failure[2] + } else { + readinessEnd = plannedBoundary < 0 ? lane.matches.length : plannedBoundary + } + const requiredFailure = await loadNormalChunks( + router, + lane, + readinessEnd, + signal, + ) + signal?.throwIfAborted() + if (requiredFailure) { + if (requiredFailure[1][0] === REDIRECTED) { + abortMatches(lane.matches) + return resolveServerRedirect(router, location, requiredFailure[1][1]) + } + failure = requiredFailure + } + + const terminal = await applyFailure(router, lane, failure, signal) + if (terminal.boundary !== undefined) { + const match = lane.matches[terminal.boundary]! + if (match.ssr === true) { + const route = getRoute(router, match) + try { + if (terminal.kind === ERROR) { + await loadRouteChunk(route, 'errorComponent') + } else if (match._notFound) { + await Promise.all([ + loadRouteChunk(route), + loadRouteChunk(route, 'notFoundComponent'), + ]) + } else { + await loadRouteChunk(route, 'notFoundComponent') + } + } catch {} + signal?.throwIfAborted() + } + } + + signal?.throwIfAborted() + await projectLane( + router, + { + location: lane.location, + matches: lane.matches, + } as ReducedLane, + signal, + ) + signal?.throwIfAborted() + router.serverSsr?.onCleanup(abortLane) + return { type: 'render', status: terminal.status, matches: lane.matches } + } finally { + signal?.removeEventListener('abort', abortLane) + } +} + +type ServerLoadOptions = NonNullable[0]> & { + _signal?: AbortSignal +} + +export async function loadServerRoute( + router: AnyRouter, + opts?: ServerLoadOptions, +): Promise { + router.updateLatestLocation() + const next = router.latestLocation + const previous = router._committed + let result: ServerLoadResult + try { + const canonical = router.buildLocation({ + to: next.pathname, + search: true, + params: true, + hash: true, + state: true, + _includeValidateSearch: true, + }) + if (next.publicHref !== canonical.publicHref) { + const href = canonical.publicHref || '/' + throw canonical.external + ? redirect({ href }) + : redirect({ href, _builtLocation: canonical }) + } + + const fromLocation = router.stores.resolvedLocation.get() + const changeInfo = getLocationChangeInfo(next, fromLocation) + router.emit({ type: 'onBeforeNavigate', ...changeInfo }) + router.emit({ type: 'onBeforeLoad', ...changeInfo }) + opts?._signal?.throwIfAborted() + result = await waitFor( + executeServerLane(router, next, router.matchRoutes(next), opts?._signal), + opts?._signal, + ) + opts?._signal?.throwIfAborted() + } catch (cause) { + opts?._signal?.throwIfAborted() + if (!isRedirect(cause)) { + throw cause + } + result = resolveServerRedirect(router, next, cause) + } + + router._serverResult = result + router.batch(() => { + router.stores.location.set(next) + router.stores.status.set('idle') + if (result.type === 'render') { + router.stores.setMatches(result.matches) + router.stores.resolvedLocation.set(next) + } + }) + if (result.type === 'render') { + router._committed = result.matches + runRouteLifecycle(router, previous, result.matches) + } + router._commitPromise?.resolve() + router._commitPromise = undefined +} diff --git a/packages/router-core/src/redirect.ts b/packages/router-core/src/redirect.ts index b10fac6d7a..2ac387daeb 100644 --- a/packages/router-core/src/redirect.ts +++ b/packages/router-core/src/redirect.ts @@ -21,7 +21,6 @@ export type Redirect< */ _builtLocation?: ParsedLocation } - redirectHandled?: boolean } export type RedirectOptions< diff --git a/packages/router-core/src/route.ts b/packages/router-core/src/route.ts index 2de7dc57f0..eae2f15e30 100644 --- a/packages/router-core/src/route.ts +++ b/packages/router-core/src/route.ts @@ -700,10 +700,6 @@ export interface Route< THandlers > isRoot: TParentRoute extends AnyRoute ? true : false - /** @internal */ - _componentsPromise?: Promise - /** @internal */ - _componentsLoaded?: boolean lazyFn?: () => Promise< LazyRoute< Route< @@ -729,9 +725,7 @@ export interface Route< > > /** @internal */ - _lazyPromise?: Promise - /** @internal */ - _lazyLoaded?: boolean + _lazy?: Promise | true rank: number to: TrimPathRight init: (opts: { originalIndex: number }) => void @@ -1711,10 +1705,7 @@ export class BaseRoute< > > /** @internal */ - _lazyPromise?: Promise - /** @internal */ - _componentsPromise?: Promise - + _lazy?: Promise | true constructor( options?: RouteOptions< TRegister, diff --git a/packages/router-core/src/router.ts b/packages/router-core/src/router.ts index df9251a739..5b0cbfb6be 100644 --- a/packages/router-core/src/router.ts +++ b/packages/router-core/src/router.ts @@ -1,8 +1,7 @@ import { createBrowserHistory, parseHref } from '@tanstack/history' -import { isServer } from '@tanstack/router-core/isServer' +import { isServer, loadServerRoute } from '@tanstack/router-core/isServer' import { DEFAULT_PROTOCOL_ALLOWLIST, - createControlledPromise, decodePath, deepEqual, encodePathLikeUrl, @@ -35,8 +34,14 @@ import { isNotFound } from './not-found' import { setupScrollRestoration } from './scroll-restoration' import { defaultParseSearch, defaultStringifySearch } from './searchParams' import { rootRouteId } from './root' -import { isRedirect, redirect } from './redirect' -import { loadMatches, loadRouteChunk, routeNeedsPreload } from './load-matches' +import { isRedirect } from './redirect' +import { + loadClientRoute, + loadRouteChunk, + preloadClientRoute, + refreshClientRoute, + replaceRouteChunk, +} from './load-client' import { composeRewrites, executeRewriteInput, @@ -51,6 +56,12 @@ import type { } from './new-process-route-tree' import type { SearchParser, SearchSerializer } from './searchParams' import type { AnyRedirect, ResolvedRedirect } from './redirect' +import type { + LoadTransaction, + LoaderFlight, + PendingSession, +} from './load-client' +import type { ServerLoadResult } from './load-server' import type { HistoryAction, HistoryLocation, @@ -62,7 +73,6 @@ import type { import type { Awaitable, Constrain, - ControlledPromise, NoInfer, NonNullableUpdater, PickAsRequired, @@ -75,8 +85,6 @@ import type { AnyRouteWithContext, LoaderStaleReloadMode, MakeRemountDepsOptionsUnion, - RouteContextOptions, - RouteLike, RouteMask, SearchMiddleware, SearchMiddlewareMeta, @@ -106,7 +114,6 @@ import type { } from './manifest' import type { AnySchema, AnyValidator } from './validators' import type { NavigateOptions, ResolveRelativePath, ToOptions } from './link' -import type { NotFoundError } from './not-found' import type { AnySerializationAdapter, ValidateSerializableInput, @@ -263,9 +270,9 @@ export interface RouterOptions< */ defaultPreloadStaleTime?: number /** - * The default `defaultPreloadGcTime` a route should use if no preloadGcTime is provided. + * The default `preloadGcTime` a route should use if none is provided. * - * @default 1_800_000 `(30 minutes)` + * @default 300_000 `(5 minutes)` * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultpreloadgctime-property) * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/preloading) */ @@ -297,7 +304,7 @@ export interface RouterOptions< /** * The default `gcTime` a route should use if no gcTime is provided. * - * @default 1_800_000 `(30 minutes)` + * @default 300_000 `(5 minutes)` * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultgctime-property) * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#key-options) */ @@ -544,14 +551,10 @@ export interface RouterState< in out TRouteMatch = MakeRouteMatchUnion, > { status: 'pending' | 'idle' - loadedAt: number isLoading: boolean - isTransitioning: boolean matches: Array location: ParsedLocation> resolvedLocation?: ParsedLocation> - statusCode: number - redirect?: AnyRedirect } export interface BuildNextOptions { @@ -619,9 +622,21 @@ export type SubscribeFn = ( ) => () => void export interface MatchRoutesOpts { - preload?: boolean throwOnError?: boolean - dest?: BuildNextOptions + /** @internal */ + _controller?: AbortController + /** @internal */ + _rematerialize?: boolean +} + +function routeNeedsLoad(route: AnyRoute): unknown { + return ( + route.options.loader || + route.options.beforeLoad || + route.lazyFn || + (route.options.component as any)?.preload || + (route.options.pendingComponent as any)?.preload + ) } export type InferRouterContext = @@ -748,6 +763,7 @@ export type EmitFn = (routerEvent: RouterEvent) => void export type LoadFn = (opts?: { sync?: boolean action?: { type: HistoryAction } + _signal?: AbortSignal }) => Promise export type CommitLocationFn = ({ @@ -756,7 +772,10 @@ export type CommitLocationFn = ({ ...next }: ParsedLocation & CommitLocationOptions) => Promise -export type StartTransitionFn = (fn: () => void) => void +export type StartTransitionFn = ( + fn: () => void, + expected: Array, +) => Promise export interface MatchRoutesFn { ( @@ -775,13 +794,6 @@ export interface MatchRoutesFn { ): Array } -export type GetMatchFn = (matchId: string) => AnyRouteMatch | undefined - -export type UpdateMatchFn = ( - id: string, - updater: (match: AnyRouteMatch) => AnyRouteMatch, -) => void - export type LoadRouteChunkFn = (route: AnyRoute) => Promise> export type ResolveRedirect = (err: AnyRedirect) => ResolvedRedirect @@ -896,12 +908,60 @@ export function getLocationChangeInfo( location: ParsedLocation, resolvedLocation?: ParsedLocation, ) { - const fromLocation = resolvedLocation - const toLocation = location - const pathChanged = fromLocation?.pathname !== toLocation.pathname - const hrefChanged = fromLocation?.href !== toLocation.href - const hashChanged = fromLocation?.hash !== toLocation.hash - return { fromLocation, toLocation, pathChanged, hrefChanged, hashChanged } + return { + fromLocation: resolvedLocation, + toLocation: location, + pathChanged: resolvedLocation?.pathname !== location.pathname, + hrefChanged: resolvedLocation?.href !== location.href, + hashChanged: resolvedLocation?.hash !== location.hash, + } +} + +/** + * Return only state owned by the application, excluding volatile history + * bookkeeping. Mask payloads (`__tempLocation`/`__tempKey`) are kept: they + * distinguish otherwise-identical locations. + */ +export function _getUserHistoryState({ + key: _key, + __TSR_key: _tsrKey, + __TSR_index: _tsrIndex, + __hashScrollIntoViewOptions: _hashScroll, + ...state +}: ParsedHistoryState): HistoryState { + return state +} + +/** Run route lifecycle callbacks in leave/enter/stay phases. */ +export function runRouteLifecycle( + router: AnyRouter, + previous: Array, + matches: Array, + isCurrent?: () => boolean, +): void { + for (const match of previous) { + if (isCurrent?.() === false) { + return + } + if (!matches.some((candidate) => candidate.routeId === match.routeId)) { + ;(router.routesById as Record)[ + match.routeId + ]!.options.onLeave?.(match) + } + } + for (const match of matches) { + if (isCurrent?.() === false) { + return + } + const route = (router.routesById as Record)[ + match.routeId + ]! + route.options[ + previous.some((candidate) => candidate.routeId === match.routeId) + ? 'onStay' + : 'onEnter' + ]?.(match) + } } type LightweightRouteMatchResult = { @@ -951,6 +1011,46 @@ declare global { | undefined } +export interface RouterCore< + in out TRouteTree extends AnyRoute, + in out TTrailingSlashOption extends TrailingSlashOption, + in out TDefaultStructuralSharingOption extends boolean, + in out TRouterHistory extends RouterHistory = RouterHistory, + in out TDehydrated extends Record = Record, +> { + shouldViewTransition?: boolean | ViewTransitionOptions + /** Current client load transaction and owner of navigation writes. */ + _tx?: LoadTransaction + /** Joinable in-flight loader generations keyed by match ID. */ + _flights?: Map + /** Active speculative lanes retained for cancellation, invalidation, and cache clearing. */ + _preloads?: Map> + /** Owns cancellable work before a client transaction publishes. */ + _preflight?: AbortController + /** Transfers one reconstructed SSR prefix into its initial client load. */ + _handoff?: HydrationHandoff + /** Pending-boundary reveal and minimum-visible timing state. */ + _pending?: PendingSession + /** Result of the latest server load, used to render or redirect. */ + _serverResult?: ServerLoadResult + /** Framework publication waiting for an exact render acknowledgement. */ + _rendered?: [ + offered?: Array, + settle?: (rendered: boolean) => void, + ] + /** Development-only HMR reload for a route and its descendants. */ + _refreshRoute: (() => Promise) | undefined + /** Development-only replacement for a route's lazy chunk owner. */ + _replaceRouteChunk: + | ((route: AnyRoute, lazyFn: AnyRoute['lazyFn']) => void) + | undefined +} + +export type HydrationHandoff = [ + claim: () => AbortController | undefined, + finish: (matches?: Array) => number | undefined, +] + /** * Core, framework-agnostic router engine that powers TanStack Router. * @@ -979,10 +1079,11 @@ export class RouterCore< restoration?: boolean reset?: boolean } = { next: true } - shouldViewTransition?: boolean | ViewTransitionOptions = undefined - isViewTransitionTypesSupported?: boolean = undefined subscribers = new Set>() - viewTransitionPromise?: ControlledPromise + /** Accepted off-screen loader generations keyed by match ID. */ + _cache = new Map() + /** Accepted semantic lane, excluding temporary pending presentation. */ + _committed: Array = [] // Must build in constructor stores!: RouterStores @@ -1003,7 +1104,7 @@ export class RouterCore< rewrite?: LocationRewrite origin?: string latestLocation!: ParsedLocation> - pendingBuiltLocation?: ParsedLocation> + _pendingLocation?: ParsedLocation> basepath!: string routeTree!: TRouteTree routesById!: RoutesById @@ -1048,24 +1149,19 @@ export class RouterCore< options.protocolAllowlist ?? DEFAULT_PROTOCOL_ALLOWLIST, }) - if (typeof document !== 'undefined') { + if (!(isServer ?? typeof document === 'undefined')) { self.__TSR_ROUTER__ = this } } - // This is a default implementation that can optionally be overridden - // by the router provider once rendered. We provide this so that the - // router can be used in a non-react environment if necessary - startTransition: StartTransitionFn = (fn) => fn() - + startTransition: StartTransitionFn = async (fn) => { + fn() + return false + } isShell() { return !!this.options.isShell } - isPrerendering() { - return !!this.options.isPrerendering - } - update: UpdateFn< TRouteTree, TTrailingSlashOption, @@ -1091,7 +1187,8 @@ export class RouterCore< ...newOptions, } - this.isServer = this.options.isServer ?? typeof document === 'undefined' + this.isServer = + this.options.isServer ?? isServer ?? typeof document === 'undefined' this.protocolAllowlist = new Set(this.options.protocolAllowlist) @@ -1165,17 +1262,13 @@ export class RouterCore< if (!this.stores && this.latestLocation) { const config = this.getStoreConfig(this) this.batch = config.batch - this.stores = createRouterStores( - getInitialRouterState(this.latestLocation), - config, - ) + this.stores = createRouterStores(this.latestLocation, config) if (!(isServer ?? this.isServer)) { setupScrollRestoration(this) } } - let needsLocationUpdate = false const nextBasepath = this.options.basepath ?? '/' const nextRewriteOption = this.options.rewrite const basepathChanged = basepathWasUnset || prevBasepath !== nextBasepath @@ -1208,21 +1301,9 @@ export class RouterCore< this.updateLatestLocation() } - needsLocationUpdate = true - } - - if (needsLocationUpdate && this.stores) { - this.stores.location.set(this.latestLocation) - } - - if ( - typeof window !== 'undefined' && - 'CSS' in window && - typeof window.CSS?.supports === 'function' - ) { - this.isViewTransitionTypesSupported = window.CSS.supports( - 'selector(:active-view-transition-type(a))', - ) + if (this.stores) { + this.stores.location.set(this.latestLocation) + } } } @@ -1293,11 +1374,15 @@ export class RouterCore< } emit: EmitFn = (routerEvent) => { - this.subscribers.forEach((listener) => { + for (const listener of this.subscribers) { if (listener.eventType === routerEvent.type) { - listener.fn(routerEvent) + try { + listener.fn(routerEvent) + } catch (e) { + console.error(e) + } } - }) + } } /** @@ -1405,10 +1490,6 @@ export class RouterCore< return branch } - get looseRoutesById() { - return this.routesById as Record - } - matchRoutes: MatchRoutesFn = ( pathnameOrNext: string | ParsedLocation, locationSearchOrOpts?: AnySchema | MatchRoutesOpts, @@ -1427,16 +1508,6 @@ export class RouterCore< return this.matchRoutesInternal(pathnameOrNext, locationSearchOrOpts) } - private getParentContext(parentMatch?: AnyRouteMatch) { - const parentMatchId = parentMatch?.id - - const parentContext = !parentMatchId - ? ((this.options.context as any) ?? undefined) - : (parentMatch.context ?? this.options.context ?? undefined) - - return parentContext - } - private matchRoutesInternal( next: ParsedLocation, opts?: MatchRoutesOpts, @@ -1463,18 +1534,19 @@ export class RouterCore< } } - const globalNotFoundRouteId = isGlobalNotFound + const _notFoundRouteId = isGlobalNotFound ? findGlobalNotFoundRouteId(this.options.notFoundMode, matchedRoutes) : undefined const matches = new Array(matchedRoutes.length) - // Snapshot of active match state keyed by routeId, used to stabilise - // params/search across navigations. - const previousActiveMatchesByRouteId = new Map() - for (const store of this.stores.matchStores.values()) { - if (store.routeId) { - previousActiveMatchesByRouteId.set(store.routeId, store.get()) - } + const committed = this._committed + const previousAt = (route: AnyRoute, index: number) => { + const match = committed[index] + return match?.routeId === route.id + ? match + : route === this.options.notFoundRoute + ? committed.find((candidate) => candidate.routeId === route.id) + : undefined } for (let index = 0; index < matchedRoutes.length; index++) { @@ -1506,7 +1578,6 @@ export class RouterCore< ...strictSearch, } strictMatchSearch = { ...parentStrictSearch, ...strictSearch } - searchError = undefined } catch (err: any) { let searchParamError = err if (!(err instanceof SearchParamError)) { @@ -1524,19 +1595,25 @@ export class RouterCore< searchError = searchParamError } } - // This is where we need to call route.options.loaderDeps() to get any additional // deps that the route's loader function might need to run. We need to do this // before we create the match so that we can pass the deps to the route's // potential key function which is used to uniquely identify the route match in state - const loaderDeps = - route.options.loaderDeps?.({ - search: preMatchSearch, - }) ?? '' - - const loaderDepsHash = loaderDeps ? JSON.stringify(loaderDeps) : '' - + let loaderDeps: any = '' + let loaderDepsHash = '' + try { + loaderDeps = + route.options.loaderDeps?.({ + search: preMatchSearch, + }) ?? '' + loaderDepsHash = loaderDeps ? JSON.stringify(loaderDeps) || '' : '' + } catch (cause) { + if (opts?.throwOnError) { + throw cause + } + searchError ??= cause + } const { interpolatedPath, usedParams } = interpolatePath({ path: route.fullPath, params: routeParams, @@ -1544,12 +1621,9 @@ export class RouterCore< server: this.isServer, }) - // Waste not, want not. If we already have a match for this route, - // reuse it. This is important for layout routes, which might stick - // around between navigation actions that only change leaf routes. - - // Existing matches are matches that are already loaded along with - // pending matches that are still loading + // Seed planning from the accepted same-ID cache generation first, then + // from the committed generation for this route. Presentation stores are + // deliberately not a semantic reuse authority. const matchId = // route.id for disambiguation route.id + @@ -1558,13 +1632,16 @@ export class RouterCore< // explicit deps loaderDepsHash - const existingMatch = this.getMatch(matchId) - - const previousMatch = previousActiveMatchesByRouteId.get(route.id) + const previousMatch = previousAt(route, index) + const existingMatch = + process.env.NODE_ENV !== 'production' && opts?._rematerialize + ? undefined + : (this._cache.get(matchId) ?? + (previousMatch?.id === matchId ? previousMatch : undefined)) const strictParams = existingMatch?._strictParams ?? usedParams - let paramsError: unknown = undefined + let paramsError: unknown if (!existingMatch) { try { @@ -1600,15 +1677,10 @@ export class RouterCore< ? nullReplaceEqualDeep(previousMatch.search, preMatchSearch) : nullReplaceEqualDeep(existingMatch.search, preMatchSearch), _strictSearch: strictMatchSearch, + searchError, } } else { - const status = - route.options.loader || - route.options.beforeLoad || - route.lazyFn || - routeNeedsPreload(route) - ? 'pending' - : 'success' + const status = routeNeedsLoad(route) ? 'pending' : 'success' match = { id: matchId, @@ -1623,96 +1695,42 @@ export class RouterCore< ? nullReplaceEqualDeep(previousMatch.search, preMatchSearch) : preMatchSearch, _strictSearch: strictMatchSearch, - searchError: undefined, + searchError, status, isFetching: false, error: undefined, paramsError, - __routeContext: undefined, - _nonReactive: { - loadPromise: createControlledPromise(), - }, - __beforeLoadContext: undefined, context: {}, - abortController: new AbortController(), - fetchCount: 0, + abortController: opts?._controller ?? new AbortController(), cause, loaderDeps: previousMatch ? replaceEqualDeep(previousMatch.loaderDeps, loaderDeps) : loaderDeps, invalid: false, preload: false, - links: undefined, - scripts: undefined, - headScripts: undefined, - meta: undefined, staticData: route.options.staticData || {}, fullPath: route.fullPath, } } - if (!opts?.preload) { - // If we have a global not found, mark the right match as global not found - match.globalNotFound = globalNotFoundRouteId === route.id - } - - // update the searchError if there is one - match.searchError = searchError - - const parentContext = this.getParentContext(parentMatch) - - match.context = { - ...parentContext, - ...match.__routeContext, - ...match.__beforeLoadContext, + // If we have a global not found, mark the right match as global not found + const _notFound = _notFoundRouteId === route.id + if (match._notFound && !_notFound) { + match.error = undefined } + match._notFound = _notFound matches[index] = match } for (let index = 0; index < matches.length; index++) { const match = matches[index]! - const route = this.looseRoutesById[match.routeId]! - const existingMatch = this.getMatch(match.id) - - // Update the match's params - const previousMatch = previousActiveMatchesByRouteId.get(match.routeId) - match.params = previousMatch - ? nullReplaceEqualDeep(previousMatch.params, routeParams) - : routeParams - - if (!existingMatch) { - const parentMatch = matches[index - 1] - const parentContext = this.getParentContext(parentMatch) - - // Update the match's context - - if (route.options.context) { - const contextFnContext: RouteContextOptions = - { - deps: match.loaderDeps, - params: match.params, - context: parentContext ?? {}, - location: next, - navigate: (opts: any) => - this.navigate({ ...opts, _fromLocation: next }), - buildLocation: this.buildLocation, - cause: match.cause, - abortController: match.abortController, - preload: !!match.preload, - matches, - routeId: route.id, - } - // Get the route context - match.__routeContext = - route.options.context(contextFnContext) ?? undefined - } - - match.context = { - ...parentContext, - ...match.__routeContext, - ...match.__beforeLoadContext, - } + match.params = + match.cause === 'stay' + ? nullReplaceEqualDeep(match.params, routeParams) + : routeParams + if (opts?._controller) { + match.context = {} } } @@ -1720,25 +1738,38 @@ export class RouterCore< } getMatchedRoutes: GetMatchRoutesFn = (pathname) => { - return getMatchedRoutes({ - pathname, - routesById: this.routesById, - processedTree: this.processedTree, - }) + const routeParams: Record = Object.create(null) + const match = findRouteMatch( + trimPathRight(pathname), + this.processedTree, + true, + ) + if (match) { + Object.assign(routeParams, match.rawParams) + } + return { + matchedRoutes: match?.branch || [this.routesById[rootRouteId]!], + routeParams, + foundRoute: match?.route, + } } /** * Lightweight route matching for buildLocation. * Only computes fullPath, accumulated search, and params - skipping expensive - * operations like AbortController, ControlledPromise, loaderDeps, and full match objects. + * operations like AbortController, loaderDeps, and full match objects. */ private matchRoutesLightweight( location: ParsedLocation, ): LightweightRouteMatchResult { - const lastStateMatchId = last(this.stores.matchesId.get()) + const lastRouteId = last(this.stores.ids.get()) + const lastStateMatch = lastRouteId + ? this.stores.byRoute.get(lastRouteId)!.get() + : undefined + const lastStateMatchId = lastStateMatch?.id const cached = this.lightweightCache.get(location) - if (cached && cached[0] === lastStateMatchId) { - return cached[1] + if (cached && cached[0 /* lastMatchId */] === lastStateMatchId) { + return cached[1 /* result */] } const { matchedRoutes, routeParams } = this.getMatchedRoutes( @@ -1769,8 +1800,6 @@ export class RouterCore< } // Determine params: reuse from state if possible, otherwise parse - const lastStateMatch = - lastStateMatchId && this.stores.matchStores.get(lastStateMatchId)?.get() const canReuseParams = lastStateMatch && lastStateMatch.routeId === lastRoute.id && @@ -1805,37 +1834,6 @@ export class RouterCore< return result } - cancelMatch = (id: string) => { - const match = this.getMatch(id) - - if (!match) return - - match.abortController.abort() - clearTimeout(match._nonReactive.pendingTimeout) - match._nonReactive.pendingTimeout = undefined - } - - cancelMatches = () => { - this.stores.pendingIds.get().forEach((matchId) => { - this.cancelMatch(matchId) - }) - - this.stores.matchesId.get().forEach((matchId) => { - if (this.stores.pendingMatchStores.has(matchId)) { - return - } - - const match = this.stores.matchStores.get(matchId)?.get() - if (!match) { - return - } - - if (match.status === 'pending' || match.isFetching === 'loader') { - this.cancelMatch(matchId) - } - }) - } - /** * Build the next ParsedLocation from navigation options without committing. * Resolves `to`/`from`, params/search/hash/state, applies search validation @@ -1851,7 +1849,7 @@ export class RouterCore< ): ParsedLocation => { // We allow the caller to override the current location const currentLocation = - dest._fromLocation || this.pendingBuiltLocation || this.latestLocation + dest._fromLocation || this._pendingLocation || this.latestLocation // Use lightweight matching - only computes what buildLocation needs // (fullPath, search, params) without creating full match objects @@ -1906,15 +1904,7 @@ export class RouterCore< : sourcePath // Resolve the next params - const nextParams = - dest.params === false || dest.params === null - ? Object.create(null) - : (dest.params ?? true) === true - ? fromParams - : Object.assign( - fromParams, - functionalUpdate(dest.params as any, fromParams), - ) + const nextParams = resolveNextParams(dest.params, fromParams) const destRoute = this.routesByPath[ trimPathRight(nextTo) as keyof typeof this.routesByPath @@ -2012,12 +2002,12 @@ export class RouterCore< nextSearch = validatedSearch } - nextSearch = applySearchMiddleware({ - search: nextSearch, + nextSearch = applySearchMiddleware( + nextSearch, dest, destRoutes, - _includeValidateSearch: opts._includeValidateSearch, - }) + opts._includeValidateSearch, + ) // Replace the equal deep nextSearch = nullReplaceEqualDeep(fromSearch, nextSearch) @@ -2117,12 +2107,7 @@ export class RouterCore< // If mask has a params function, call it with the matched params as context // Otherwise, use the matched params or the provided params value - const nextParams = - maskParams === false || maskParams === null - ? Object.create(null) - : (maskParams ?? true) === true - ? params - : Object.assign(params, functionalUpdate(maskParams, params)) + const nextParams = resolveNextParams(maskParams, params) maskedDest = { from: opts.from, @@ -2151,7 +2136,7 @@ export class RouterCore< return buildWithMatches(opts) } - commitLocationPromise: undefined | ControlledPromise + _commitPromise: (Promise & { resolve: () => void }) | undefined /** * Commit a previously built location to history (push/replace), optionally @@ -2163,37 +2148,26 @@ export class RouterCore< ...next }) => { let historyAction: HistoryAction | undefined - const isSameState = () => { - // the following props are ignored but may still be provided when navigating, - // temporarily add the previous values to the next state so they don't affect - // the comparison - const ignoredProps = [ - 'key', // TODO: Remove in v2 - use __TSR_key instead - '__TSR_key', - '__TSR_index', - '__hashScrollIntoViewOptions', - ] as const - ignoredProps.forEach((prop) => { - ;(next.state as any)[prop] = this.latestLocation.state[prop] - }) - const isEqual = deepEqual(next.state, this.latestLocation.state) - ignoredProps.forEach((prop) => { - delete next.state[prop] - }) - return isEqual - } - - const isSameUrl = - trimPathRight(this.latestLocation.href) === trimPathRight(next.href) + const isSameLocation = + trimPathRight(this.latestLocation.href) === trimPathRight(next.href) && + deepEqual( + _getUserHistoryState(next.state), + _getUserHistoryState(this.latestLocation.state), + ) - let previousCommitPromise = this.commitLocationPromise - this.commitLocationPromise = createControlledPromise(() => { + const previousCommitPromise = this._commitPromise + let resolve!: () => void + const commitPromise = new Promise((done) => { + resolve = done + }) as Promise & { resolve: () => void } + commitPromise.resolve = () => { + resolve() previousCommitPromise?.resolve() - previousCommitPromise = undefined - }) + } + this._commitPromise = commitPromise // Don't commit to history if nothing changed - if (isSameUrl && isSameState()) { + if (isSameLocation) { this.load() } else { let { @@ -2245,21 +2219,17 @@ export class RouterCore< nextHistory.state, { ignoreBlocker }, ) + + // Without a subscribed adapter the history commit cannot trigger the + // load itself. The same-location branch already loads directly. + if (!this.history.subscribers.size) { + this.load({ action: { type: historyAction } }) + } } this._scroll.next = next.resetScroll ?? true - if (!this.history.subscribers.size) { - this.load( - historyAction - ? { - action: { type: historyAction }, - } - : undefined, - ) - } - - return this.commitLocationPromise + return this._commitPromise } /** Convenience helper: build a location from options, then commit it. */ @@ -2269,9 +2239,11 @@ export class RouterCore< hashScrollIntoView, viewTransition, ignoreBlocker, + _redirects, href, ...rest - }: BuildNextOptions & CommitLocationOptions = {}) => { + }: BuildNextOptions & + CommitLocationOptions & { _redirects?: number } = {}) => { if (href) { const currentIndex = this.history.location.state.__TSR_index @@ -2295,8 +2267,12 @@ export class RouterCore< ...(rest as any), _includeValidateSearch: true, }) + if (_redirects) { + ;(location as typeof location & { _redirects?: number })._redirects = + _redirects + } - this.pendingBuiltLocation = location as ParsedLocation< + this._pendingLocation = location as ParsedLocation< FullSearchSchema > @@ -2312,8 +2288,8 @@ export class RouterCore< // Clear pending location after commit starts // We do this on next microtask to allow synchronous navigate calls to chain queueMicrotask(() => { - if (this.pendingBuiltLocation === location) { - this.pendingBuiltLocation = undefined + if (this._pendingLocation === location) { + this._pendingLocation = undefined } }) @@ -2409,253 +2385,17 @@ export class RouterCore< }) } - latestLoadPromise: undefined | Promise - - beforeLoad = () => { - // Cancel any pending matches - this.cancelMatches() - this.updateLatestLocation() - - if (isServer ?? this.isServer) { - // for SPAs on the initial load, this is handled by the Transitioner - const nextLocation = this.buildLocation({ - to: this.latestLocation.pathname, - search: true, - params: true, - hash: true, - state: true, - _includeValidateSearch: true, - }) - - // Check if location changed - origin check is unnecessary since buildLocation - // always uses this.origin when constructing URLs - if (this.latestLocation.publicHref !== nextLocation.publicHref) { - const href = this.getParsedLocationHref(nextLocation) - if (nextLocation.external) { - throw redirect({ href }) - } else { - throw redirect({ href, _builtLocation: nextLocation }) - } - } - } - - // Match the routes - const pendingMatches = this.matchRoutes(this.latestLocation) - - const nextCachedMatches = this.stores.cachedMatches - .get() - .filter((d) => !pendingMatches.some((e) => e.id === d.id)) - - // Ingest the new matches - this.batch(() => { - this.stores.status.set('pending') - this.stores.statusCode.set(200) - this.stores.isLoading.set(true) - this.stores.location.set(this.latestLocation) - this.stores.setPending(pendingMatches) - // If a cached match moved to pending matches, remove it from cached matches - this.stores.setCached(nextCachedMatches) - }) - } - load: LoadFn = async (opts): Promise => { - const historyAction = opts?.action?.type - let redirect: AnyRedirect | undefined - let notFound: NotFoundError | undefined - let loadPromise: Promise - const previousLocation = - this.stores.resolvedLocation.get() ?? this.stores.location.get() - - // eslint-disable-next-line prefer-const - loadPromise = new Promise((resolve) => { - this.startTransition(async () => { - try { - this.beforeLoad() - if (historyAction) { - this._scroll.hash = - historyAction === 'PUSH' || historyAction === 'REPLACE' - } - const next = this.latestLocation - const prevLocation = this.stores.resolvedLocation.get() - const locationChangeInfo = getLocationChangeInfo(next, prevLocation) - - if (!this.stores.redirect.get()) { - this.emit({ - type: 'onBeforeNavigate', - ...locationChangeInfo, - }) - } - - this.emit({ - type: 'onBeforeLoad', - ...locationChangeInfo, - }) - - await loadMatches({ - router: this, - sync: opts?.sync, - forceStaleReload: previousLocation.href === next.href, - matches: this.stores.pendingMatches.get(), - location: next, - updateMatch: this.updateMatch, - // eslint-disable-next-line @typescript-eslint/require-await - onReady: async () => { - // Wrap batch in framework-specific transition wrapper (e.g., Solid's startTransition) - this.startTransition(() => { - this.startViewTransition(async () => { - // this.viewTransitionPromise = createControlledPromise() - - // Commit the pending matches. If a previous match was - // removed, place it in the cachedMatches - // - // exitingMatches uses match.id (routeId + params + loaderDeps) so - // navigating /foo?page=1 → /foo?page=2 correctly caches the page=1 entry. - let exitingMatches: Array | null = null - - // Lifecycle-hook identity uses routeId only so that navigating between - // different params/deps of the same route fires onStay (not onLeave+onEnter). - let hookExitingMatches: Array | null = null - let hookEnteringMatches: Array | null = null - let hookStayingMatches: Array | null = null - - this.batch(() => { - const pendingMatches = this.stores.pendingMatches.get() - const mountPending = pendingMatches.length - const currentMatches = this.stores.matches.get() - - exitingMatches = mountPending - ? currentMatches.filter( - (match) => - !this.stores.pendingMatchStores.has(match.id), - ) - : null - - // Lifecycle-hook identity: routeId only (route presence in tree) - // Build routeId sets from pools to avoid derived stores. - const pendingRouteIds = new Set() - for (const s of this.stores.pendingMatchStores.values()) { - if (s.routeId) pendingRouteIds.add(s.routeId) - } - const activeRouteIds = new Set() - for (const s of this.stores.matchStores.values()) { - if (s.routeId) activeRouteIds.add(s.routeId) - } - - hookExitingMatches = mountPending - ? currentMatches.filter( - (match) => !pendingRouteIds.has(match.routeId), - ) - : null - hookEnteringMatches = mountPending - ? pendingMatches.filter( - (match) => !activeRouteIds.has(match.routeId), - ) - : null - hookStayingMatches = mountPending - ? pendingMatches.filter((match) => - activeRouteIds.has(match.routeId), - ) - : currentMatches - - this.stores.isLoading.set(false) - this.stores.loadedAt.set(Date.now()) - /** - * When committing new matches, cache any exiting matches that are still usable. - * Routes that resolved with `status: 'error'` or `status: 'notFound'` are - * deliberately excluded from `cachedMatches` so that subsequent invalidations - * or reloads re-run their loaders instead of reusing the failed/not-found data. - */ - if (mountPending) { - this.stores.setMatches(pendingMatches) - this.stores.setPending([]) - this.stores.setCached([ - ...this.stores.cachedMatches.get(), - ...exitingMatches!.filter( - (d) => - d.status !== 'error' && - d.status !== 'notFound' && - d.status !== 'redirected', - ), - ]) - this.clearExpiredCache() - } - }) - - // - for (const [matches, hook] of [ - [hookExitingMatches, 'onLeave'], - [hookEnteringMatches, 'onEnter'], - [hookStayingMatches, 'onStay'], - ] as const) { - if (!matches) continue - for (const match of matches as Array) { - this.looseRoutesById[match.routeId]!.options[hook]?.( - match, - ) - } - } - }) - }) - }, - }) - } catch (err) { - if (isRedirect(err)) { - redirect = err - if (!(isServer ?? this.isServer)) { - this.navigate({ - ...redirect.options, - replace: true, - ignoreBlocker: true, - }) - } - } else if (isNotFound(err)) { - notFound = err - } - - const nextStatusCode = redirect - ? redirect.status - : notFound - ? 404 - : this.stores.matches.get().some((d) => d.status === 'error') - ? 500 - : 200 - - this.batch(() => { - this.stores.statusCode.set(nextStatusCode) - this.stores.redirect.set(redirect) - }) - } - - if (this.latestLoadPromise === loadPromise) { - this.commitLocationPromise?.resolve() - this.latestLoadPromise = undefined - this.commitLocationPromise = undefined - } - - resolve() - }) - }) - - this.latestLoadPromise = loadPromise - - await loadPromise - - while ( - (this.latestLoadPromise as any) && - loadPromise !== this.latestLoadPromise - ) { - await this.latestLoadPromise + if (isServer ?? this.isServer) { + return loadServerRoute(this, opts) } - let newStatusCode: number | undefined = undefined - if (this.hasNotFoundMatch()) { - newStatusCode = 404 - } else if (this.stores.matches.get().some((d) => d.status === 'error')) { - newStatusCode = 500 - } - if (newStatusCode !== undefined) { - this.stores.statusCode.set(newStatusCode) + this.updateLatestLocation() + if (opts?.action) { + this._scroll.hash = + opts.action.type === 'PUSH' || opts.action.type === 'REPLACE' } + await loadClientRoute(this, opts) } startViewTransition = (fn: () => Promise) => { @@ -2670,9 +2410,8 @@ export class RouterCore< // Attempt to start a view transition (or just apply the changes if we can't) if ( shouldViewTransition && - typeof document !== 'undefined' && - 'startViewTransition' in document && - typeof document.startViewTransition === 'function' + !(isServer ?? typeof document === 'undefined') && + typeof (document as any).startViewTransition === 'function' ) { // lib.dom.ts doesn't support viewTransition types variant yet. // TODO: Fix this when dom types are updated @@ -2680,7 +2419,7 @@ export class RouterCore< if ( typeof shouldViewTransition === 'object' && - this.isViewTransitionTypesSupported + window.CSS?.supports?.('selector(:active-view-transition-type(a))') ) { const next = this.latestLocation const prevLocation = this.stores.resolvedLocation.get() @@ -2693,8 +2432,7 @@ export class RouterCore< : shouldViewTransition.types if (resolvedViewTransitionTypes === false) { - fn() - return + return fn() } startViewTransitionParams = { @@ -2705,58 +2443,21 @@ export class RouterCore< startViewTransitionParams = fn } - document.startViewTransition(startViewTransitionParams) - } else { - fn() + return (document as any).startViewTransition(startViewTransitionParams) + .updateCallbackDone } - } - - updateMatch: UpdateMatchFn = (id, updater) => { - this.startTransition(() => { - const pendingMatch = this.stores.pendingMatchStores.get(id) - if (pendingMatch) { - pendingMatch.set(updater) - return - } - - const activeMatch = this.stores.matchStores.get(id) - if (activeMatch) { - activeMatch.set(updater) - return - } - - const cachedMatch = this.stores.cachedMatchStores.get(id) - if (cachedMatch) { - const next = updater(cachedMatch.get()) - if (next.status === 'redirected') { - const deleted = this.stores.cachedMatchStores.delete(id) - if (deleted) { - this.stores.cachedIds.set((prev) => - prev.filter((matchId) => matchId !== id), - ) - } - } else { - cachedMatch.set(next) - } - } - }) - } - - getMatch: GetMatchFn = (matchId: string): AnyRouteMatch | undefined => { - return ( - this.stores.cachedMatchStores.get(matchId)?.get() ?? - this.stores.pendingMatchStores.get(matchId)?.get() ?? - this.stores.matchStores.get(matchId)?.get() - ) + return fn() } /** - * Invalidate the current matches and optionally force them back into a pending state. + * Invalidate selected match generations and optionally force current matches + * back into a pending state. + * + * - Marks committed and cached matches whose IDs are selected as invalid. + * - Retires selected active preloads so older work cannot publish fresh data. * - * - Marks all matches that pass the optional `filter` as `invalid: true`. - * - If `forcePending` is true, or a match is currently in `'error'` or `'notFound'` status, - * its status is reset to `'pending'` and its `error` cleared so that the loader is re-run - * on the next `load()` call (eg. after HMR or a manual invalidation). + * The next load decides when to publish pending UI, so invalidation does not + * mutate the currently rendered status. */ invalidate: InvalidateFn< RouterCore< @@ -2767,44 +2468,79 @@ export class RouterCore< TDehydrated > > = (opts) => { + const committedMatches = this._committed + const filter = opts?.filter + const preloads = this._preloads + const invalidIds = new Set( + [ + ...committedMatches, + ...this._cache.values(), + ...[...(preloads?.values() ?? [])].flat(), + ...(this._tx?.[3 /* matches */] ?? []), + ] + .filter( + (match) => !filter || filter(match as MakeRouteMatchUnion), + ) + .map((match) => match.id), + ) + const discardedPreloads: Array = [] + for (const [controller, matches] of preloads ?? []) { + if (matches.some((match) => invalidIds.has(match.id))) { + preloads!.delete(controller) + discardedPreloads.push(controller) + } + } const invalidate = (d: MakeRouteMatch) => { - if (opts?.filter?.(d as MakeRouteMatchUnion) ?? true) { - return { + if (invalidIds.has(d.id)) { + const route = this.routesById[d.routeId] as AnyRoute + const next = { ...d, invalid: true, - ...(opts?.forcePending || - d.status === 'error' || - d.status === 'notFound' + ...((opts?.forcePending || + d.status === 'error' || + d.status === 'notFound') && + routeNeedsLoad(route) ? ({ status: 'pending', error: undefined } as const) : undefined), } + // Invalidation replaces this owner; it does not create another lease. + ;(d as AnyRouteMatch & { _flight?: LoaderFlight })._flight = undefined + return next } return d } - this.batch(() => { - this.stores.setMatches(this.stores.matches.get().map(invalidate)) - this.stores.setCached(this.stores.cachedMatches.get().map(invalidate)) - this.stores.setPending(this.stores.pendingMatches.get().map(invalidate)) - }) + this._committed = committedMatches.map(invalidate) + // Cache entries are settled successes. Retiring matching active preload + // owners makes an in-place stale mark sufficient while preserving data. + for (const [id, match] of this._cache) { + if (invalidIds.has(id)) { + match.invalid = true + if (opts?.forcePending) { + match.status = 'pending' + } + } + } + // The superseding load must not discover any same-ID generation selected + // for replacement. Existing owners release it in their normal order. + for (const id of invalidIds) { + this._flights?.delete(id) + } + for (const controller of discardedPreloads) { + controller.abort() + } this.shouldViewTransition = false return this.load({ sync: opts?.sync }) } - getParsedLocationHref = (location: ParsedLocation) => { - // For redirects and external use, we need publicHref (with rewrite output applied) - // href is the internal path after rewrite input, publicHref is user-facing - return location.publicHref || '/' - } - resolveRedirect = (redirect: AnyRedirect): AnyRedirect => { const locationHeader = redirect.headers.get('Location') if (!redirect.options.href || redirect.options._builtLocation) { const location = redirect.options._builtLocation ?? this.buildLocation(redirect.options) - const href = this.getParsedLocationHref(location) + const href = location.publicHref || '/' redirect.options.href = href redirect.headers.set('Location', href) } else if (locationHeader) { @@ -2841,115 +2577,64 @@ export class RouterCore< } clearCache: ClearCacheFn = (opts) => { + const cached = this._cache + const preloads = this._preloads const filter = opts?.filter - if (filter !== undefined) { - this.stores.setCached( - this.stores.cachedMatches - .get() - .filter((m) => !filter(m as MakeRouteMatchUnion)), - ) - } else { - this.stores.setCached([]) + const discarded: Array = [] + const discardedIds: Array = [] + for (const [id, match] of cached) { + if (!filter || filter(match as MakeRouteMatchUnion)) { + discardedIds.push(id) + discarded.push(match) + } } - } - - clearExpiredCache = () => { - const now = Date.now() - // This is where all of the garbage collection magic happens - const filter = (d: MakeRouteMatch) => { - const route = this.looseRoutesById[d.routeId]! - - if (!route.options.loader) { - return true + const abort: Array = [] + for (const [controller, matches] of preloads ?? []) { + if (!filter || matches.some(filter as any)) { + abort.push(controller) + discarded.push(...matches) } + } - // If the route was preloaded, use the preloadGcTime - // otherwise, use the gcTime - const gcTime = - (d.preload - ? (route.options.preloadGcTime ?? this.options.defaultPreloadGcTime) - : (route.options.gcTime ?? this.options.defaultGcTime)) ?? - 5 * 60 * 1000 - - const isError = d.status === 'error' - if (isError) return true - - const gcEligible = now - d.updatedAt >= gcTime - return gcEligible + // Run every public filter before changing authority, then prune both maps + // before releasing a public loader + // signal, whose abort listeners can synchronously reenter the router. + for (const id of discardedIds) { + cached.delete(id) + } + for (const controller of abort) { + preloads!.delete(controller) + } + // clearCache is a force-retirement boundary, so a final discarded lease + // must not use the normal zero-owner handoff window. + for (const match of discarded as Array< + AnyRouteMatch & { _flight?: LoaderFlight } + >) { + const flight = match._flight + match._flight = undefined + if (flight && !--flight[2 /* leases */]) { + if (this._flights?.get(match.id) === flight) { + this._flights.delete(match.id) + } + abort.push(flight[1 /* controller */]) + } + } + for (const controller of abort) { + controller.abort() } - this.clearCache({ filter }) } - loadRouteChunk = loadRouteChunk + loadRouteChunk: ( + route: AnyRoute, + componentType?: 'errorComponent' | 'notFoundComponent' | false, + ) => Promise | undefined = loadRouteChunk preloadRoute: PreloadRouteFn< TRouteTree, TTrailingSlashOption, TDefaultStructuralSharingOption, TRouterHistory - > = async (opts) => { - const next = opts._builtLocation ?? this.buildLocation(opts as any) - - let matches = this.matchRoutes(next, { - throwOnError: true, - preload: true, - dest: opts, - }) - - const activeMatchIds = new Set([ - ...this.stores.matchesId.get(), - ...this.stores.pendingIds.get(), - ]) - - const loadedMatchIds = new Set([ - ...activeMatchIds, - ...this.stores.cachedIds.get(), - ]) - - // If the matches are already loaded, we need to add them to the cached matches. - const matchesToCache = matches.filter( - (match) => !loadedMatchIds.has(match.id), - ) - if (matchesToCache.length) { - const cachedMatches = this.stores.cachedMatches.get() - this.stores.setCached([...cachedMatches, ...matchesToCache]) - } - - try { - matches = await loadMatches({ - router: this, - matches, - location: next, - preload: true, - updateMatch: (id, updater) => { - // Don't update the match if it's currently loaded - if (activeMatchIds.has(id)) { - matches = matches.map((d) => (d.id === id ? updater(d) : d)) - } else { - this.updateMatch(id, updater) - } - }, - }) - - return matches - } catch (err) { - if (isRedirect(err)) { - if (err.options.reloadDocument) { - return undefined - } - - return await this.preloadRoute({ - ...err.options, - _fromLocation: next, - }) - } - if (!isNotFound(err)) { - // Preload errors are not fatal, but we should still log them - console.error(err) - } - return undefined - } - } + > = (opts) => preloadClientRoute(this, opts) matchRoute: MatchRouteFn< TRouteTree, @@ -2967,12 +2652,12 @@ export class RouterCore< } const next = this.buildLocation(matchLocation as any) - if (opts?.pending && this.stores.status.get() !== 'pending') { + const isPending = this.stores.status.get() === 'pending' + if (opts?.pending && !isPending) { return false } - const pending = - opts?.pending === undefined ? !this.stores.isLoading.get() : opts.pending + const pending = opts?.pending ?? !isPending const baseLocation = pending ? this.latestLocation @@ -3012,11 +2697,19 @@ export class RouterCore< serverSsr?: ServerSsr serverSsrLifecycle?: RouterSsrLifecycle +} - hasNotFoundMatch = () => { - return this.stores.matches - .get() - .some((d) => d.status === 'notFound' || d.globalNotFound) +/** + * In non-production environments, + * augment the RouterCore class with a `_refreshRoute` method + * dedicated to HMR. + */ +if (process.env.NODE_ENV !== 'production') { + RouterCore.prototype._replaceRouteChunk = replaceRouteChunk + RouterCore.prototype._refreshRoute = async function () { + this._serverResult = undefined + this.updateLatestLocation() + await refreshClientRoute(this) } } @@ -3053,14 +2746,11 @@ export function getInitialRouterState( location: ParsedLocation, ): RouterState { return { - loadedAt: 0, isLoading: false, - isTransitioning: false, status: 'idle', resolvedLocation: undefined, location, matches: [], - statusCode: 200, } } @@ -3092,56 +2782,12 @@ function validateSearch(validateSearch: AnyValidator, input: unknown): unknown { return {} } -/** - * Build the matched route chain and extract params for a pathname. - * Falls back to the root route if no specific route is found. - */ -export function getMatchedRoutes({ - pathname, - routesById, - processedTree, -}: { - pathname: string - routesById: Record - processedTree: ProcessedTree -}) { - const routeParams: Record = Object.create(null) - const trimmedPath = trimPathRight(pathname) - - let foundRoute: TRouteLike | undefined = undefined - const match = findRouteMatch(trimmedPath, processedTree, true) - if (match) { - foundRoute = match.route - Object.assign(routeParams, match.rawParams) // Copy params, because they're cached - } - - const matchedRoutes = match?.branch || [routesById[rootRouteId]!] - - return { matchedRoutes, routeParams, foundRoute } -} - -/** - * TODO: once caches are persisted across requests on the server, - * we can cache the built middleware chain using `last(destRoutes)` as the key - */ -function applySearchMiddleware({ - search, - dest, - destRoutes, - _includeValidateSearch, -}: { - search: any - dest: { search?: unknown } - destRoutes: ReadonlyArray - _includeValidateSearch: boolean | undefined -}) { - const middleware = buildMiddlewareChain(destRoutes) - return middleware(search, dest, _includeValidateSearch ?? false) -} - -function buildMiddlewareChain(destRoutes: ReadonlyArray) { - let dest: BuildNextOptions - let includeValidateSearch: boolean | undefined +function applySearchMiddleware( + search: any, + dest: BuildNextOptions, + destRoutes: ReadonlyArray, + includeValidateSearch: boolean | undefined, +) { const middlewares = [] as Array> for (const route of destRoutes) { @@ -3234,15 +2880,7 @@ function buildMiddlewareChain(destRoutes: ReadonlyArray) { return (middlewares[index]! as any)({ search: currentSearch, next, meta }) } - return function middleware( - search: any, - nextDest: BuildNextOptions, - _includeValidateSearch: boolean, - ) { - dest = nextDest - includeValidateSearch = _includeValidateSearch - return applyNext(0, search) - } + return applyNext(0, search) } function findGlobalNotFoundRouteId( @@ -3250,26 +2888,41 @@ function findGlobalNotFoundRouteId( routes: ReadonlyArray, ) { if (notFoundMode !== 'root') { + let fallback for (let i = routes.length - 1; i >= 0; i--) { const route = routes[i]! - if (route.children) { + if (route.options.notFoundComponent) { return route.id } + fallback ||= route.children && route.id + } + if (fallback) { + return fallback } } return rootRouteId } +function resolveNextParams( + spec: unknown, + base: Record, +): Record { + return spec === false || spec === null + ? Object.create(null) + : (spec ?? true) === true + ? base + : Object.assign(base, functionalUpdate(spec as any, base)) +} + function extractStrictParams( route: AnyRoute, accumulatedParams: Record, ) { const parseParams = route.options.params?.parse ?? route.options.parseParams if (parseParams) { - const result = parseParams(accumulatedParams as Record) - if (result === false) { - throw new Error('Route params.parse returned false for a matched route') - } - Object.assign(accumulatedParams, result) + Object.assign( + accumulatedParams, + parseParams(accumulatedParams as Record), + ) } } diff --git a/packages/router-core/src/ssr/createRequestHandler.ts b/packages/router-core/src/ssr/createRequestHandler.ts index 32377b2bea..a945effd01 100644 --- a/packages/router-core/src/ssr/createRequestHandler.ts +++ b/packages/router-core/src/ssr/createRequestHandler.ts @@ -1,11 +1,15 @@ import { createMemoryHistory } from '@tanstack/history' +import { _getRenderedMatches } from '../load-client' import { mergeHeaders } from './headers' import { attachRouterServerSsrUtils, getNormalizedURL, getOrigin, } from './ssr-server' -import { normalizeSsrResponse } from './handlerCallback' +import { + bindSsrResponseToRequest, + disposeSsrResponseDetached, +} from './handlerCallback' import type { HandlerCallback } from './handlerCallback' import type { AnyHeaders } from './headers' import type { AnyRouter } from '../router' @@ -15,6 +19,79 @@ export type RequestHandler = ( cb: HandlerCallback, ) => Promise +type RequestWaiter = ((reason: unknown) => void) | undefined + +const requestWaiters = new WeakMap>() + +function removeRequestWaiter( + waiters: Array, + index: number, + reject: (reason: unknown) => void, +) { + if (waiters[index] !== reject) { + return + } + if (index !== waiters.length - 1) { + waiters[index] = undefined + return + } + + waiters.pop() + while (waiters.length && waiters[waiters.length - 1] === undefined) { + waiters.pop() + } +} + +export function waitForRequest( + value: T | PromiseLike, + signal: AbortSignal, + onLate?: (value: T) => void, +): Promise { + const promise = Promise.resolve(value) + if (signal.aborted) { + void promise.then(onLate, () => {}) + return Promise.reject(signal.reason) + } + + return new Promise((resolve, reject) => { + let waiters = requestWaiters.get(signal) + let index: number + if (waiters) { + index = waiters.push(reject) - 1 + } else { + const newWaiters: Array = [reject] + waiters = newWaiters + index = 0 + requestWaiters.set(signal, newWaiters) + signal.addEventListener( + 'abort', + () => { + requestWaiters.delete(signal) + for (const rejectWaiter of newWaiters) { + rejectWaiter?.(signal.reason) + } + newWaiters.length = 0 + }, + { once: true }, + ) + } + void promise.then( + (result) => { + removeRequestWaiter(waiters, index, reject) + if (signal.aborted) { + onLate?.(result) + } else { + resolve(result) + } + }, + (error) => { + removeRequestWaiter(waiters, index, reject) + reject(error) + }, + ) + }) +} + export function createRequestHandler({ createRouter, request, @@ -25,13 +102,14 @@ export function createRequestHandler({ getRouterManifest?: () => ServerManifest | Promise }): RequestHandler { return async (cb) => { + request.signal.throwIfAborted() const router = createRouter() let responseOwnsCleanup = false try { attachRouterServerSsrUtils({ router, - manifest: await getRouterManifest?.(), + manifest: await waitForRequest(getRouterManifest?.(), request.signal), }) // normalizing and sanitizing the pathname here for server, so we always deal with the same format during SSR. @@ -50,20 +128,41 @@ export function createRequestHandler({ origin: router.options.origin ?? origin, }) - await router.load() + await router.load({ + _signal: request.signal, + }) + request.signal.throwIfAborted() + + const result = router._serverResult + if (result?.type === 'redirect') { + return result.redirect + } - await router.serverSsr?.dehydrate() + await waitForRequest(router.serverSsr?.dehydrate(), request.signal) + request.signal.throwIfAborted() const responseHeaders = getRequestHeaders({ router, }) - const response = await cb({ - request, + request.signal.throwIfAborted() + const response = await waitForRequest( + cb({ + request, + router, + responseHeaders, + }), + request.signal, + (late) => { + disposeSsrResponseDetached(late, request.signal.reason) + }, + ) + const ssrResponse = bindSsrResponseToRequest( router, - responseHeaders, - }) - const ssrResponse = normalizeSsrResponse(response) + response, + request.signal, + ) + request.signal.throwIfAborted() responseOwnsCleanup = ssrResponse.serverSsrCleanup === 'stream' return ssrResponse.response } finally { @@ -79,16 +178,10 @@ export function createRequestHandler({ function getRequestHeaders(opts: { router: AnyRouter }): Headers { const matchHeaders: Array = [] - for (const match of opts.router.stores.matches.get()) { + for (const match of _getRenderedMatches(opts.router.stores.matches.get())) { matchHeaders.push(match.headers) } - // Handle Redirects - const redirect = opts.router.stores.redirect.get() - if (redirect) { - matchHeaders.push(redirect.headers) - } - return mergeHeaders( { 'Content-Type': 'text/html; charset=UTF-8', diff --git a/packages/router-core/src/ssr/handlerCallback.ts b/packages/router-core/src/ssr/handlerCallback.ts index 22aa843063..368c8a2a61 100644 --- a/packages/router-core/src/ssr/handlerCallback.ts +++ b/packages/router-core/src/ssr/handlerCallback.ts @@ -30,6 +30,40 @@ export function normalizeSsrResponse( : { response: result, serverSsrCleanup: 'none' } } +export function disposeSsrResponse( + response: SsrResponse, + reason?: unknown, +): Promise { + if (response.serverSsrCleanup !== 'stream') { + return Promise.resolve() + } + try { + return Promise.resolve(response.dispose(reason)) + } catch (error) { + return Promise.reject(error) + } +} + +export function disposeSsrResponseDetached( + result: HandlerCallbackResult, + reason?: unknown, + onError: (error: unknown) => void = console.error, +): void { + const ssrResponse = normalizeSsrResponse(result) + if (ssrResponse.serverSsrCleanup === 'stream') { + void disposeSsrResponse(ssrResponse, reason).catch(onError) + return + } + + if (ssrResponse.response.body) { + try { + void ssrResponse.response.body.cancel(reason).catch(onError) + } catch (error) { + onError(error) + } + } +} + export function createSsrStreamResponse( router: TRouter, response: Response, @@ -43,29 +77,63 @@ export function createSsrStreamResponse( response, serverSsrCleanup: 'stream', async dispose(reason?: unknown) { - if (disposed) return + if (disposed) { + return + } disposed = true + // Sever router ownership before asking user/renderer stream machinery to + // cancel. A custom stream is allowed to ignore cancellation forever. + router.serverSsr?.cleanup() + try { await response.body!.cancel(reason) } catch { - // ignore; fallback cleanup below still releases router SSR state + // Cleanup above already released router SSR state. } - - router.serverSsr?.cleanup() }, } } +export function bindSsrResponseToRequest( + router: AnyRouter | undefined, + result: HandlerCallbackResult, + signal: AbortSignal, +): SsrResponse { + const ssrResponse = normalizeSsrResponse(result) + if (ssrResponse.serverSsrCleanup !== 'stream') { + if (signal.aborted) { + disposeSsrResponseDetached(result, signal.reason) + } + return ssrResponse + } + + const failed = (error: unknown) => { + router?.serverSsr?.cleanup() + console.error(error) + } + const abort = () => { + disposeSsrResponseDetached(ssrResponse, signal.reason, failed) + } + if (signal.aborted) { + abort() + return ssrResponse + } + + signal.addEventListener('abort', abort, { once: true }) + router?.serverSsr?.onCleanup(() => { + signal.removeEventListener('abort', abort) + }) + return ssrResponse +} + export async function replaceSsrResponse( result: HandlerCallbackResult, response: Response, reason?: unknown, ): Promise { const ssrResponse = normalizeSsrResponse(result) - if (ssrResponse.serverSsrCleanup === 'stream') { - await ssrResponse.dispose(reason) - } + await disposeSsrResponse(ssrResponse, reason) return { response, serverSsrCleanup: 'none' } } @@ -74,9 +142,7 @@ export async function stripSsrResponseBody( reason?: unknown, ): Promise { const ssrResponse = normalizeSsrResponse(result) - if (ssrResponse.serverSsrCleanup === 'stream') { - await ssrResponse.dispose(reason) - } + await disposeSsrResponse(ssrResponse, reason) return { response: new Response(null, ssrResponse.response), serverSsrCleanup: 'none', diff --git a/packages/router-core/src/ssr/server.ts b/packages/router-core/src/ssr/server.ts index df5b838676..93973999cb 100644 --- a/packages/router-core/src/ssr/server.ts +++ b/packages/router-core/src/ssr/server.ts @@ -1,8 +1,11 @@ -export { createRequestHandler } from './createRequestHandler' +export { createRequestHandler, waitForRequest } from './createRequestHandler' export type { RequestHandler } from './createRequestHandler' export { + bindSsrResponseToRequest, createSsrStreamResponse, defineHandlerCallback, + disposeSsrResponse, + disposeSsrResponseDetached, isSsrResponse, normalizeSsrResponse, replaceSsrResponse, diff --git a/packages/router-core/src/ssr/ssr-client.ts b/packages/router-core/src/ssr/ssr-client.ts index 2aa5358ac0..6b0921c8db 100644 --- a/packages/router-core/src/ssr/ssr-client.ts +++ b/packages/router-core/src/ssr/ssr-client.ts @@ -1,310 +1 @@ -import { invariant } from '../invariant' -import { isNotFound } from '../not-found' -import { createControlledPromise } from '../utils' -import { hydrateSsrMatchId } from './ssr-match-id' -import type { GLOBAL_SEROVAL, GLOBAL_TSR } from './constants' -import type { DehydratedMatch, TsrSsrGlobal } from './types' -import type { AnyRouteMatch } from '../Matches' -import type { AnyRouter } from '../router' -import type { RouteContextOptions } from '../route' -import type { AnySerializationAdapter } from './serializer/transformer' - -declare global { - interface Window { - [GLOBAL_TSR]?: TsrSsrGlobal - [GLOBAL_SEROVAL]?: any - } -} - -function hydrateMatch( - match: AnyRouteMatch, - deyhydratedMatch: DehydratedMatch, -): void { - match.id = deyhydratedMatch.i - match.__beforeLoadContext = deyhydratedMatch.b - match.loaderData = deyhydratedMatch.l - match.status = deyhydratedMatch.s - match.ssr = deyhydratedMatch.ssr - match.updatedAt = deyhydratedMatch.u - match.error = deyhydratedMatch.e - // Only hydrate global-not-found when a defined value is present in the - // dehydrated payload. If omitted, preserve the value computed from the - // current client location (important for SPA fallback HTML served at unknown - // URLs, where dehydrated matches may come from `/` but client matching marks - // root as globalNotFound). - if (deyhydratedMatch.g !== undefined) { - match.globalNotFound = deyhydratedMatch.g - } -} - -export async function hydrate(router: AnyRouter): Promise { - if (!window.$_TSR) { - if (process.env.NODE_ENV !== 'production') { - throw new Error( - 'Invariant failed: Expected to find bootstrap data on window.$_TSR, but we did not. Please file an issue!', - ) - } - - invariant() - } - - const serializationAdapters = router.options.serializationAdapters as - | Array - | undefined - - if (serializationAdapters?.length) { - const fromSerializableMap = new Map() - serializationAdapters.forEach((adapter) => { - fromSerializableMap.set(adapter.key, adapter.fromSerializable) - }) - window.$_TSR.t = fromSerializableMap - window.$_TSR.buffer.forEach((script) => script()) - } - window.$_TSR.initialized = true - - if (!window.$_TSR.router) { - if (process.env.NODE_ENV !== 'production') { - throw new Error( - 'Invariant failed: Expected to find a dehydrated data on window.$_TSR.router, but we did not. Please file an issue!', - ) - } - - invariant() - } - - const dehydratedRouter = window.$_TSR.router - dehydratedRouter.matches.forEach((dehydratedMatch) => { - dehydratedMatch.i = hydrateSsrMatchId(dehydratedMatch.i) - }) - if (dehydratedRouter.lastMatchId) { - dehydratedRouter.lastMatchId = hydrateSsrMatchId( - dehydratedRouter.lastMatchId, - ) - } - const { manifest, dehydratedData, lastMatchId } = dehydratedRouter - - router.ssr = { - manifest, - } - const meta = document.querySelector('meta[property="csp-nonce"]') as - | HTMLMetaElement - | undefined - const nonce = meta?.content - router.options.ssr = { - nonce, - } - - // Allow the user to handle custom hydration data before matching routes. - // This lets hydration install router config that affects matching, e.g. rewrites. - await router.options.hydrate?.(dehydratedData) - - // Hydrate the router state - const matches = router.matchRoutes(router.stores.location.get()) - - // kick off loading the route chunks - const routeChunkPromise = Promise.all( - matches.map((match) => - router.loadRouteChunk(router.looseRoutesById[match.routeId]!), - ), - ) - - function setMatchForcePending(match: AnyRouteMatch) { - // usually the minPendingPromise is created in the Match component if a pending match is rendered - // however, this might be too late if the match synchronously resolves - const route = router.looseRoutesById[match.routeId]! - const pendingMinMs = - route.options.pendingMinMs ?? router.options.defaultPendingMinMs - if (pendingMinMs) { - const minPendingPromise = createControlledPromise() - match._nonReactive.minPendingPromise = minPendingPromise - match._forcePending = true - - setTimeout(() => { - minPendingPromise.resolve() - // We've handled the minPendingPromise, so we can delete it - router.updateMatch(match.id, (prev) => { - prev._nonReactive.minPendingPromise = undefined - return { - ...prev, - _forcePending: undefined, - } - }) - }, pendingMinMs) - } - } - - function setRouteSsr(match: AnyRouteMatch) { - const route = router.looseRoutesById[match.routeId] - if (route) { - route.options.ssr = match.ssr - } - } - // Right after hydration and before the first render, we need to rehydrate each match - // First step is to reyhdrate loaderData and __beforeLoadContext - let firstNonSsrMatchIndex: number | undefined = undefined - matches.forEach((match) => { - const dehydratedMatch = dehydratedRouter.matches.find( - (d) => d.i === match.id, - ) - if (!dehydratedMatch) { - match._nonReactive.dehydrated = false - match.ssr = false - setRouteSsr(match) - return - } - - hydrateMatch(match, dehydratedMatch) - setRouteSsr(match) - - match._nonReactive.dehydrated = match.ssr !== false - - if (match.ssr === 'data-only' || match.ssr === false) { - if (firstNonSsrMatchIndex === undefined) { - firstNonSsrMatchIndex = match.index - setMatchForcePending(match) - } - } - }) - - router.stores.setMatches(matches) - - // now that all necessary data is hydrated: - // 1) fully reconstruct the route context - // 2) execute `head()` and `scripts()` for each match - const activeMatches = router.stores.matches.get() - const location = router.stores.location.get() - await Promise.all( - activeMatches.map(async (match) => { - try { - const route = router.looseRoutesById[match.routeId]! - - const parentMatch = activeMatches[match.index - 1] - const parentContext = parentMatch?.context ?? router.options.context - - // `context()` was already executed by `matchRoutes`, however route context was not yet fully reconstructed - // so run it again and merge route context - if (route.options.context) { - const contextFnContext: RouteContextOptions = - { - deps: match.loaderDeps, - params: match.params, - context: parentContext ?? {}, - location, - navigate: (opts: any) => - router.navigate({ - ...opts, - _fromLocation: location, - }), - buildLocation: router.buildLocation, - cause: match.cause, - abortController: match.abortController, - preload: false, - matches, - routeId: route.id, - } - match.__routeContext = - route.options.context(contextFnContext) ?? undefined - } - - match.context = { - ...parentContext, - ...match.__routeContext, - ...match.__beforeLoadContext, - } - - const assetContext = { - ssr: router.options.ssr, - matches: activeMatches, - match, - params: match.params, - loaderData: match.loaderData, - } - const headFnContent = await route.options.head?.(assetContext) - - const scripts = await route.options.scripts?.(assetContext) - - match.meta = headFnContent?.meta - match.links = headFnContent?.links - match.headScripts = headFnContent?.scripts - match.styles = headFnContent?.styles - match.scripts = scripts - } catch (err) { - if (isNotFound(err)) { - match.error = { isNotFound: true } - console.error( - `NotFound error during hydration for routeId: ${match.routeId}`, - err, - ) - } else { - match.error = err as any - console.error( - `Error during hydration for route ${match.routeId}:`, - err, - ) - throw err - } - } - }), - ) - - const isSpaMode = matches[matches.length - 1]!.id !== lastMatchId - const hasSsrFalseMatches = matches.some((m) => m.ssr === false) - // all matches have data from the server and we are not in SPA mode so we don't need to kick of router.load() - if (!hasSsrFalseMatches && !isSpaMode) { - matches.forEach((match) => { - // remove the dehydrated flag since we won't run router.load() which would remove it - match._nonReactive.dehydrated = undefined - }) - // Mark the current location as resolved so that later load cycles - // (e.g. preloads, invalidations) don't mistakenly detect a href change - // (resolvedLocation defaults to undefined and router.load() is skipped - // in the normal SSR hydration path). - router.stores.resolvedLocation.set(router.stores.location.get()) - return routeChunkPromise - } - - // schedule router.load() to run after the next tick so we can store the promise in the match before loading starts - const loadPromise = Promise.resolve() - .then(() => router.load()) - .catch((err) => { - console.error('Error during router hydration:', err) - }) - - // in SPA mode we need to keep the first match below the root route pending until router.load() is finished - // this will prevent that other pending components are rendered but hydration is not blocked - if (isSpaMode) { - const match = matches[1] - if (!match) { - if (process.env.NODE_ENV !== 'production') { - throw new Error( - 'Invariant failed: Expected to find a match below the root match in SPA mode.', - ) - } - - invariant() - } - setMatchForcePending(match) - - match._displayPending = true - match._nonReactive.displayPendingPromise = loadPromise - - loadPromise.then(() => { - router.batch(() => { - // ensure router is not in status 'pending' anymore - // this usually happens in Transitioner but if loading synchronously resolves, - // Transitioner won't be rendered while loading so it cannot track the change from loading:true to loading:false - if (router.stores.status.get() === 'pending') { - router.stores.status.set('idle') - router.stores.resolvedLocation.set(router.stores.location.get()) - } - // hide the pending component once the load is finished - router.updateMatch(match.id, (prev) => ({ - ...prev, - _displayPending: undefined, - displayPendingPromise: undefined, - })) - }) - }) - } - return routeChunkPromise -} +export { hydrate } from '../load-client' diff --git a/packages/router-core/src/ssr/ssr-match-id.ts b/packages/router-core/src/ssr/ssr-match-id.ts index 498c3a0c7e..6c7e2e9eaf 100644 --- a/packages/router-core/src/ssr/ssr-match-id.ts +++ b/packages/router-core/src/ssr/ssr-match-id.ts @@ -1,7 +1,16 @@ export function dehydrateSsrMatchId(id: string): string { - return id.replaceAll('/', '\0') + return id + .replaceAll('~', '~~') + .replaceAll('\0', '~0') + .replaceAll('\uFFFD', '~r') + .replaceAll('/', '\0') } export function hydrateSsrMatchId(id: string): string { - return id.replaceAll('\0', '/').replaceAll('\uFFFD', '/') + return id + .replaceAll('\0', '/') + .replaceAll('\uFFFD', '/') + .replace(/~([~0r])/g, (_, code) => + code === '0' ? '\0' : code === 'r' ? '\uFFFD' : code, + ) } diff --git a/packages/router-core/src/ssr/ssr-server.ts b/packages/router-core/src/ssr/ssr-server.ts index 31db2f5e41..30d3bc4890 100644 --- a/packages/router-core/src/ssr/ssr-server.ts +++ b/packages/router-core/src/ssr/ssr-server.ts @@ -8,6 +8,7 @@ import { import { decodePath } from '../utils' import { createLRUCache } from '../lru-cache' import { rootRouteId } from '../root' +import { _getRenderedMatches } from '../load-client' import minifiedTsrBootStrapScript from './tsrScript?script-string' import { GLOBAL_TSR, TSR_SCRIPT_BARRIER_ID } from './constants' import { dehydrateSsrMatchId } from './ssr-match-id' @@ -51,7 +52,7 @@ export function dehydrateMatch(match: AnyRouteMatch): DehydratedMatch { dehydratedMatch[shorthand] = match[key] } } - if (match.globalNotFound) { + if (match._notFound) { dehydratedMatch.g = true } return dehydratedMatch @@ -389,7 +390,7 @@ export function attachRouterServerSsrUtils({ if (!manifest) return manifest const requestAssets = getRequestAssets?.() - const matches = router.stores.matches.get() + const matches = _getRenderedMatches(router.stores.matches.get()) const hasAssets = hasRequestAssets(requestAssets) if (!hasAssets && !manifest.inlineCss) { @@ -495,8 +496,9 @@ export function attachRouterServerSsrUtils({ invariant() } - let matchesToDehydrate = router.stores.matches.get() - if (router.isShell()) { + let matchesToDehydrate = _getRenderedMatches(router.stores.matches.get()) + const isShell = router.isShell() + if (isShell) { // In SPA mode we only want to dehydrate the root match matchesToDehydrate = matchesToDehydrate.slice(0, 1) } @@ -540,11 +542,10 @@ export function attachRouterServerSsrUtils({ manifest: manifestToDehydrate, matches, } - const lastMatchId = matchesToDehydrate[matchesToDehydrate.length - 1]?.id - if (lastMatchId) { - dehydratedRouter.lastMatchId = dehydrateSsrMatchId(lastMatchId) - } const dehydratedData = await router.options.dehydrate?.() + if (cleanupStarted) { + return + } if (dehydratedData) { dehydratedRouter.dehydratedData = dehydratedData } diff --git a/packages/router-core/src/ssr/transformStreamWithRouter.ts b/packages/router-core/src/ssr/transformStreamWithRouter.ts index 4393f955b3..03c6ecf2e1 100644 --- a/packages/router-core/src/ssr/transformStreamWithRouter.ts +++ b/packages/router-core/src/ssr/transformStreamWithRouter.ts @@ -4,6 +4,8 @@ import { TSR_SCRIPT_BARRIER_ID } from './constants' import type { AnyRouter } from '../router' export type TransformStreamWithRouterOptions = { + /** The request lifetime that owns this response stream. */ + signal?: AbortSignal /** Timeout for serialization to complete after app render finishes (default: 60000ms) */ timeoutMs?: number /** Maximum lifetime of the stream transform (default: 120000ms). Safety net for cleanup. */ @@ -196,6 +198,22 @@ function createAbortNotifier(opts?: TransformStreamWithRouterOptions) { } } +function listenToAbort( + signal: AbortSignal | undefined, + onAbort: (reason?: unknown) => void, +) { + if (!signal) { + return + } + if (signal.aborted) { + onAbort(signal.reason) + return + } + const listener = () => onAbort(signal.reason) + signal.addEventListener('abort', listener, { once: true }) + return () => signal.removeEventListener('abort', listener) +} + export function transformStreamWithRouter( router: AnyRouter, appStream: ReadableStream, @@ -224,6 +242,7 @@ function makeFastPathStream( let controller: ReadableStreamDefaultController | undefined let state: MergeState = MergeState.ReadingBody let lifetimeTimeoutHandle: ReturnType | undefined + let stopListeningToAbort: (() => void) | undefined let stopListeningToInjectedHtml: (() => void) | undefined const readerState = createReaderState(appStream) const notifyAbort = createAbortNotifier(opts) @@ -251,6 +270,8 @@ function makeFastPathStream( clearTimeout(lifetimeTimeoutHandle) lifetimeTimeoutHandle = undefined } + stopListeningToAbort?.() + stopListeningToAbort = undefined try { stopListeningToInjectedHtml?.() } catch { @@ -360,6 +381,11 @@ function makeFastPathStream( }, }) + stopListeningToAbort = listenToAbort(opts?.signal, (reason) => { + safeError(reason) + cleanup(reason) + }) + return stream } @@ -380,6 +406,7 @@ function makeMainStream( let stopListeningToSerializationFinished: (() => void) | undefined let serializationTimeoutHandle: ReturnType | undefined let lifetimeTimeoutHandle: ReturnType | undefined + let stopListeningToAbort: (() => void) | undefined let cleanedUp = false let controller: ReadableStreamDefaultController | undefined @@ -509,6 +536,8 @@ function makeMainStream( } stopListeningToInjectedHtml = undefined stopListeningToSerializationFinished = undefined + stopListeningToAbort?.() + stopListeningToAbort = undefined if (serializationTimeoutHandle !== undefined) { clearTimeout(serializationTimeoutHandle) @@ -763,7 +792,14 @@ function makeMainStream( if (cleanedUp || isDone()) return stream } - // Transform the appStream + stopListeningToAbort = listenToAbort(opts?.signal, (reason) => { + safeError(reason) + cleanup(reason) + }) + if (cleanedUp || isDone()) + return stream + + // Transform the appStream ;(async () => { try { while (true) { diff --git a/packages/router-core/src/ssr/types.ts b/packages/router-core/src/ssr/types.ts index 70395cc9a0..27bf111843 100644 --- a/packages/router-core/src/ssr/types.ts +++ b/packages/router-core/src/ssr/types.ts @@ -15,7 +15,6 @@ export interface DehydratedMatch { export interface DehydratedRouter { manifest: Manifest | undefined dehydratedData?: any - lastMatchId?: string matches: Array } diff --git a/packages/router-core/src/stores.ts b/packages/router-core/src/stores.ts index c2ab8311c3..8c2beeb2e7 100644 --- a/packages/router-core/src/stores.ts +++ b/packages/router-core/src/stores.ts @@ -1,11 +1,9 @@ -import { createLRUCache } from './lru-cache' import { arraysEqual, functionalUpdate } from './utils' import type { AnyRoute } from './route' import type { RouterState } from './router' import type { FullSearchSchema } from './routeInfo' import type { ParsedLocation } from './location' -import type { AnyRedirect } from './redirect' import type { AnyRouteMatch } from './Matches' export interface RouterReadableStore { @@ -34,12 +32,9 @@ export type StoreConfig = { createMutableStore: MutableStoreFactory createReadonlyStore: ReadonlyStoreFactory batch: RouterBatchFn - init?: (stores: RouterStores) => void } -type MatchStore = RouterWritableStore & { - routeId?: string -} +type MatchStore = RouterWritableStore type ReadableStore = RouterReadableStore /** SSR non-reactive createMutableStore */ @@ -71,272 +66,115 @@ export function createNonReactiveReadonlyStore( export interface RouterStores { status: RouterWritableStore['status']> - loadedAt: RouterWritableStore - isLoading: RouterWritableStore - isTransitioning: RouterWritableStore location: RouterWritableStore>> resolvedLocation: RouterWritableStore< ParsedLocation> | undefined > - statusCode: RouterWritableStore - redirect: RouterWritableStore - matchesId: RouterWritableStore> - pendingIds: RouterWritableStore> - /** @internal */ - cachedIds: RouterWritableStore> + ids: RouterWritableStore> matches: ReadableStore> - pendingMatches: ReadableStore> - cachedMatches: ReadableStore> - firstId: ReadableStore - hasPending: ReadableStore - matchRouteDeps: ReadableStore<{ - locationHref: string - resolvedLocationHref: string | undefined - status: RouterState['status'] - }> __store: RouterReadableStore> - matchStores: Map - pendingMatchStores: Map - cachedMatchStores: Map + byRoute: Map /** - * Get a computed store that resolves a routeId to its current match state. - * Returns the same cached store instance for repeated calls with the same key. - * The computed depends on matchesId + the individual match store, so - * subscribers are only notified when the resolved match state changes. + * Get the stable atom for a route's presented match. The atom remains in the + * pool when the route leaves and contains `undefined` until it re-enters. */ - getRouteMatchStore: ( + getMatchStore: ( routeId: string, ) => RouterReadableStore setMatches: (nextMatches: Array) => void - setPending: (nextMatches: Array) => void - setCached: (nextMatches: Array) => void } export function createRouterStores( - initialState: RouterState, + initialLocation: RouterState['location'], config: StoreConfig, ): RouterStores { - const { createMutableStore, createReadonlyStore, batch, init } = config + const { createMutableStore, createReadonlyStore, batch } = config // non reactive utilities - const matchStores = new Map() - const pendingMatchStores = new Map() - const cachedMatchStores = new Map() + const byRoute = new Map() // atoms - const status = createMutableStore(initialState.status) - const loadedAt = createMutableStore(initialState.loadedAt) - const isLoading = createMutableStore(initialState.isLoading) - const isTransitioning = createMutableStore(initialState.isTransitioning) - const location = createMutableStore(initialState.location) - const resolvedLocation = createMutableStore(initialState.resolvedLocation) - const statusCode = createMutableStore(initialState.statusCode) - const redirect = createMutableStore(initialState.redirect) - const matchesId = createMutableStore>([]) - const pendingIds = createMutableStore>([]) - const cachedIds = createMutableStore>([]) + const status = createMutableStore['status']>('idle') + const location = createMutableStore(initialLocation) + const resolvedLocation = + createMutableStore['resolvedLocation']>(undefined) + const ids = createMutableStore>([]) // 1st order derived stores const matches = createReadonlyStore(() => - readPoolMatches(matchStores, matchesId.get()), - ) - const pendingMatches = createReadonlyStore(() => - readPoolMatches(pendingMatchStores, pendingIds.get()), - ) - const cachedMatches = createReadonlyStore(() => - readPoolMatches(cachedMatchStores, cachedIds.get()), - ) - const firstId = createReadonlyStore(() => matchesId.get()[0]) - const hasPending = createReadonlyStore(() => - matchesId.get().some((matchId) => { - const store = matchStores.get(matchId) - return store?.get().status === 'pending' - }), + ids.get().map((id) => byRoute.get(id)!.get()!), ) - const matchRouteDeps = createReadonlyStore(() => ({ - locationHref: location.get().href, - resolvedLocationHref: resolvedLocation.get()?.href, - status: status.get(), - })) // compatibility "big" state store const __store = createReadonlyStore(() => ({ status: status.get(), - loadedAt: loadedAt.get(), - isLoading: isLoading.get(), - isTransitioning: isTransitioning.get(), + isLoading: status.get() === 'pending', matches: matches.get(), location: location.get(), resolvedLocation: resolvedLocation.get(), - statusCode: statusCode.get(), - redirect: redirect.get(), })) - // Per-routeId computed store cache. - // Each entry resolves routeId → match state through the signal graph, - // giving consumers a single store to subscribe to instead of the - // two-level byRouteId → matchStore pattern. - // - // 64 max size is arbitrary, this is only for active matches anyway so - // it should be plenty. And we already have a 32 limit due to route - // matching bitmask anyway. - const matchStoreByRouteIdCache = createLRUCache< - string, - RouterReadableStore - >(64) - - function getRouteMatchStore( - routeId: string, - ): RouterReadableStore { - let cached = matchStoreByRouteIdCache.get(routeId) - if (!cached) { - cached = createReadonlyStore(() => { - // Reading matchesId.get() tracks it as a dependency. - // When matchesId changes (navigation), this computed re-evaluates. - const ids = matchesId.get() - for (const id of ids) { - const matchStore = matchStores.get(id) - if (matchStore && matchStore.routeId === routeId) { - // Reading matchStore.get() tracks it as a dependency. - // When the match store's state changes, this re-evaluates. - return matchStore.get() - } - } - return undefined - }) - matchStoreByRouteIdCache.set(routeId, cached) + function getMatchStore(routeId: string): MatchStore { + let matchStore = byRoute.get(routeId) + if (!matchStore) { + matchStore = createMutableStore(undefined) + byRoute.set(routeId, matchStore) } - return cached + return matchStore } const store = { // atoms status, - loadedAt, - isLoading, - isTransitioning, location, resolvedLocation, - statusCode, - redirect, - matchesId, - pendingIds, - cachedIds, + ids, // derived matches, - pendingMatches, - cachedMatches, - firstId, - hasPending, - matchRouteDeps, // non-reactive state - matchStores, - pendingMatchStores, - cachedMatchStores, + byRoute, // compatibility "big" state __store, - // per-key computed stores - getRouteMatchStore, + // stable per-route presentation atoms + getMatchStore, // methods setMatches, - setPending, - setCached, } - // initialize the active matches - setMatches(initialState.matches as Array) - init?.(store) - // setters to update non-reactive utilities in sync with the reactive stores function setMatches(nextMatches: Array) { - reconcileMatchPool( - nextMatches, - matchStores, - matchesId, - createMutableStore, - batch, - ) - } - - function setPending(nextMatches: Array) { - reconcileMatchPool( - nextMatches, - pendingMatchStores, - pendingIds, - createMutableStore, - batch, - ) - } - - function setCached(nextMatches: Array) { - reconcileMatchPool( - nextMatches, - cachedMatchStores, - cachedIds, - createMutableStore, - batch, - ) - } - - return store -} - -function readPoolMatches( - pool: Map, - ids: Array, -): Array { - const matches: Array = [] - for (const id of ids) { - const matchStore = pool.get(id) - if (matchStore) { - matches.push(matchStore.get()) - } - } - return matches -} - -function reconcileMatchPool( - nextMatches: Array, - pool: Map, - idStore: RouterWritableStore>, - createMutableStore: MutableStoreFactory, - batch: RouterBatchFn, -): void { - const nextIds = nextMatches.map((d) => d.id) - const nextIdSet = new Set(nextIds) - - batch(() => { - for (const id of pool.keys()) { - if (!nextIdSet.has(id)) { - pool.delete(id) + const previousIds = ids.get() + const nextIds = nextMatches.map((match) => match.routeId) + + batch(() => { + // Publish lane membership first so framework trees reconcile departures + // before observers of a leaving route receive its tombstone. + if (!arraysEqual(previousIds, nextIds)) { + ids.set(nextIds) } - } - for (const nextMatch of nextMatches) { - const existing = pool.get(nextMatch.id) - if (!existing) { - const matchStore = createMutableStore(nextMatch) as MatchStore - matchStore.routeId = nextMatch.routeId - pool.set(nextMatch.id, matchStore) - continue + for (const id of previousIds) { + if (!nextIds.includes(id)) { + byRoute.get(id)!.set(() => undefined) + } } - existing.routeId = nextMatch.routeId - if (existing.get() !== nextMatch) { - existing.set(nextMatch) + for (const nextMatch of nextMatches) { + const matchStore = getMatchStore(nextMatch.routeId) + if (matchStore.get() !== nextMatch) { + matchStore.set(nextMatch) + } } - } + }) + } - if (!arraysEqual(idStore.get(), nextIds)) { - idStore.set(nextIds) - } - }) + return store } diff --git a/packages/router-core/tests/background-assets-stale.test.ts b/packages/router-core/tests/background-assets-stale.test.ts new file mode 100644 index 0000000000..1c39969185 --- /dev/null +++ b/packages/router-core/tests/background-assets-stale.test.ts @@ -0,0 +1,79 @@ +import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute } from '../src' +import { createTestRouter } from './routerTestUtils' + +describe('background decorative asset failure', () => { + beforeEach(() => { + vi.useFakeTimers() + vi.setSystemTime(0) + }) + + afterEach(() => { + vi.useRealTimers() + vi.restoreAllMocks() + }) + + test('commits fresh loader data while preserving the previous projected assets', async () => { + const projectionError = new Error('head projection failed') + const log = vi.spyOn(console, 'error').mockImplementation(() => {}) + let resolveStaleReload!: (data: { title: string }) => void + let loaderCalls = 0 + const loader = () => { + loaderCalls += 1 + if (loaderCalls === 1) { + return { title: 'old' } + } + + return new Promise<{ title: string }>((resolve) => { + resolveStaleReload = resolve + }) + } + + const rootRoute = new BaseRootRoute({}) + const fooRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/foo', + loader, + head: ({ loaderData }) => { + // The loader always resolves before head runs in this test, so + // loaderData is never undefined here. + if (loaderData!.title === 'fresh') { + throw projectionError + } + + return { + meta: [{ title: loaderData!.title }], + } + }, + staleTime: 0, + gcTime: 60_000, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([fooRoute]), + history: createMemoryHistory(), + }) + + await router.navigate({ to: '/foo' }) + const matchId = router.state.matches.find( + (match) => match.routeId === fooRoute.id, + )!.id + const getMatch = () => + router.state.matches.find((match) => match.id === matchId) + + expect(getMatch()?.loaderData).toEqual({ title: 'old' }) + expect(getMatch()?.meta).toEqual([{ title: 'old' }]) + + await vi.advanceTimersByTimeAsync(1) + await router.load() + await vi.waitFor(() => expect(loaderCalls).toBe(2)) + + resolveStaleReload({ title: 'fresh' }) + await vi.waitFor(() => + expect(getMatch()?.loaderData).toEqual({ title: 'fresh' }), + ) + + expect(getMatch()?.meta).toEqual([{ title: 'old' }]) + expect(log).toHaveBeenCalledWith(projectionError) + }) +}) diff --git a/packages/router-core/tests/background-trim-abort.test.ts b/packages/router-core/tests/background-trim-abort.test.ts index efedc410f3..aa992df111 100644 --- a/packages/router-core/tests/background-trim-abort.test.ts +++ b/packages/router-core/tests/background-trim-abort.test.ts @@ -1,6 +1,11 @@ import { expect, test, vi } from 'vitest' import { createMemoryHistory } from '@tanstack/history' -import { BaseRootRoute, BaseRoute } from '../src' +import { + BaseRootRoute, + BaseRoute, + createControlledPromise, + notFound, +} from '../src' import { createTestRouter } from './routerTestUtils' test('a background boundary keeps the matched branch and its lifecycle stable', async () => { @@ -62,6 +67,58 @@ test('a background boundary keeps the matched branch and its lifecycle stable', expect(childOnLeave).not.toHaveBeenCalled() }) +test('a background notFound stays private until its parent boundary is ready', async () => { + const boundaryReady = createControlledPromise() + const ParentNotFound = Object.assign(() => null, { + preload: () => boundaryReady, + }) + let childLoads = 0 + + const rootRoute = new BaseRootRoute({}) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + notFoundComponent: ParentNotFound, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loader: () => { + if (++childLoads > 1) { + throw notFound() + } + return 'initial child data' + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([parentRoute.addChildren([childRoute])]), + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + }) + + await router.load() + await router.invalidate({ + filter: (match) => match.routeId === childRoute.id, + }) + await vi.waitFor(() => expect(childLoads).toBe(2)) + + expect(router.state.matches.map((match) => match.status)).toEqual([ + 'success', + 'success', + 'success', + ]) + + boundaryReady.resolve() + await vi.waitFor(() => { + expect(router.state.matches.map((match) => match.routeId)).toEqual([ + rootRoute.id, + parentRoute.id, + childRoute.id, + ]) + expect(router.state.matches[1]?.status).toBe('notFound') + expect(router.state.matches[2]?.status).toBe('success') + }) +}) + test('foreground supersession aborts every loader in a background batch', async () => { let parentLoads = 0 let childLoads = 0 diff --git a/packages/router-core/tests/boundary-component-chunk.test.ts b/packages/router-core/tests/boundary-component-chunk.test.ts index dc49380d06..b8e8322ca6 100644 --- a/packages/router-core/tests/boundary-component-chunk.test.ts +++ b/packages/router-core/tests/boundary-component-chunk.test.ts @@ -8,6 +8,17 @@ import { } from '../src' import { createTestRouter } from './routerTestUtils' +/** + * boundary component preloads should not be blocked by an unrelated + * normal route component preload. A route load starts the normal component + * preload before the loader settles; if that loader throws, the error boundary + * asks to load only the route's errorComponent. + * + * This test uses a real client router load. It keeps the normal route component + * preload pending, resolves the errorComponent preload, and expects the route + * error state to be committed without waiting for the normal component chunk. + */ + const pendingGates: Array>> = [] const pendingLoads: Array> = [] @@ -23,6 +34,152 @@ afterEach(async () => { }) describe('route boundary component preloads', () => { + test('errorComponent preload resolves without waiting for a pending route component preload', async () => { + const componentGate = createControlledPromise() + const errorComponentGate = createControlledPromise() + const routeError = new Error('loader failed') + let errorComponentPreloadCalls = 0 + pendingGates.push(componentGate, errorComponentGate) + + const SlowRouteComponent = Object.assign(() => null, { + preload: () => componentGate, + }) + const ErrorBoundary = Object.assign(() => null, { + preload: () => { + errorComponentPreloadCalls++ + return errorComponentGate + }, + }) + + const rootRoute = new BaseRootRoute({}) + const route = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/chunked', + loader: () => { + throw routeError + }, + component: SlowRouteComponent as any, + errorComponent: ErrorBoundary as any, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([route]), + history: createMemoryHistory({ initialEntries: ['/chunked'] }), + }) + + const loadPromise = router.load() + pendingLoads.push(loadPromise) + + await vi.waitFor(() => expect(errorComponentPreloadCalls).toBe(1)) + errorComponentGate.resolve() + + // The route component chunk (componentGate) is still pending: the error + // state and the public load promise must settle without waiting for it. + await loadPromise + expect(componentGate.status).toBe('pending') + const match = router.state.matches.find((item) => item.routeId === route.id) + expect(match?.status).toBe('error') + expect(match?.error).toBe(routeError) + + componentGate.resolve() + }) + + test('an error boundary ignores a late normal component failure', async () => { + const componentGate = createControlledPromise() + const errorComponentGate = createControlledPromise() + const routeError = new Error('loader failed') + const componentError = new Error('component chunk failed') + const onError = vi.fn() + let errorComponentPreloadCalls = 0 + pendingGates.push(errorComponentGate) + + const SlowRouteComponent = Object.assign(() => null, { + preload: () => componentGate, + }) + const ErrorBoundary = Object.assign(() => null, { + preload: () => { + errorComponentPreloadCalls++ + return errorComponentGate + }, + }) + + const rootRoute = new BaseRootRoute({}) + const route = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/chunked', + loader: () => { + throw routeError + }, + onError, + component: SlowRouteComponent as any, + errorComponent: ErrorBoundary as any, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([route]), + history: createMemoryHistory({ initialEntries: ['/chunked'] }), + }) + + const loadPromise = router.load() + pendingLoads.push(loadPromise) + + await vi.waitFor(() => expect(errorComponentPreloadCalls).toBe(1)) + errorComponentGate.resolve() + await loadPromise + + const match = router.state.matches.find((item) => item.routeId === route.id) + expect(match?.status).toBe('error') + expect(match?.error).toBe(routeError) + expect(onError).toHaveBeenCalledOnce() + + componentGate.reject(componentError) + await expect(componentGate).rejects.toBe(componentError) + await new Promise((resolve) => setTimeout(resolve, 0)) + + expect(onError).toHaveBeenCalledOnce() + expect(match?.error).toBe(routeError) + }) + + test('global notFound does not wait for component chunks below its boundary', async () => { + const hiddenComponentGate = createControlledPromise() + const notFoundPreload = vi.fn(() => Promise.resolve()) + pendingGates.push(hiddenComponentGate) + + const NotFoundBoundary = Object.assign(() => null, { + preload: notFoundPreload, + }) + const HiddenComponent = Object.assign(() => null, { + preload: () => hiddenComponentGate, + }) + + const rootRoute = new BaseRootRoute({}) + const layoutRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + id: '_layout', + notFoundComponent: NotFoundBoundary as any, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => layoutRoute, + path: '/child', + component: HiddenComponent as any, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([layoutRoute.addChildren([childRoute])]), + history: createMemoryHistory({ + initialEntries: ['/child/does-not-exist'], + }), + notFoundMode: 'fuzzy', + }) + + const loading = router.load() + pendingLoads.push(loading) + await loading + + expect(hiddenComponentGate.status).toBe('pending') + expect(notFoundPreload).toHaveBeenCalledOnce() + expect(router.state.matches.find((match) => match._notFound)?.routeId).toBe( + layoutRoute.id, + ) + }) + test('a late normal component chunk cannot replace a selected not-found boundary', async () => { const componentGate = createControlledPromise() const notFoundGate = createControlledPromise() @@ -72,4 +229,34 @@ describe('route boundary component preloads', () => { loaderData: 'ready', }) }) + + test('a required ancestor lazy chunk failure wins over a deeper loader notFound', async () => { + const chunkError = new Error('ancestor lazy chunk failed') + const rootRoute = new BaseRootRoute({}) + const ancestorRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/ancestor', + errorComponent: () => null, + }).lazy(() => Promise.reject(chunkError)) + const childRoute = new BaseRoute({ + getParentRoute: () => ancestorRoute, + path: '/child', + loader: () => { + throw notFound() + }, + notFoundComponent: () => null, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + ancestorRoute.addChildren([childRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/ancestor/child'] }), + }) + + await router.load() + + expect( + router.state.matches.find((match) => match.routeId === ancestorRoute.id), + ).toMatchObject({ status: 'error', error: chunkError }) + }) }) diff --git a/packages/router-core/tests/build-location.test.ts b/packages/router-core/tests/build-location.test.ts index 131fccf095..ce0d5c3c98 100644 --- a/packages/router-core/tests/build-location.test.ts +++ b/packages/router-core/tests/build-location.test.ts @@ -6,8 +6,23 @@ import { retainSearchParams, stripSearchParams, } from '../src' +import { _getUserHistoryState } from '../src/router' import { createTestRouter } from './routerTestUtils' +test('_getUserHistoryState removes volatile router bookkeeping but keeps mask payloads', () => { + expect( + _getUserHistoryState({ + key: 'legacy-key', + __TSR_key: 'key', + __TSR_index: 1, + __hashScrollIntoViewOptions: true, + __tempLocation: {} as any, + __tempKey: 'temp-key', + user: 'state', + } as any), + ).toEqual({ user: 'state', __tempLocation: {}, __tempKey: 'temp-key' }) +}) + describe('buildLocation - params function receives parsed params', () => { test('prev params should contain parsed params from route params.parse', async () => { const rootRoute = new BaseRootRoute({}) diff --git a/packages/router-core/tests/callbacks.test.ts b/packages/router-core/tests/callbacks.test.ts index a683edc14b..7a1b062d4f 100644 --- a/packages/router-core/tests/callbacks.test.ts +++ b/packages/router-core/tests/callbacks.test.ts @@ -200,7 +200,6 @@ describe('callbacks', () => { )?.id expect(page2MatchId).toBeDefined() expect(page2MatchId).not.toBe(page1MatchId) - await router.navigate({ to: '/foo', search: { page: '1' } }) expect(loader).toHaveBeenCalledTimes(2) expect(router.state.matches.some((d) => d.id === page1MatchId)).toBe(true) @@ -222,7 +221,6 @@ describe('callbacks', () => { )?.id expect(post2MatchId).toBeDefined() expect(post2MatchId).not.toBe(post1MatchId) - await router.navigate({ to: '/posts/$postId', params: { postId: '1' } }) expect(loader).toHaveBeenCalledTimes(2) expect(router.state.matches.some((d) => d.id === post1MatchId)).toBe(true) diff --git a/packages/router-core/tests/chunk-failure-lifecycle.test.ts b/packages/router-core/tests/chunk-failure-lifecycle.test.ts new file mode 100644 index 0000000000..bf9fa369d4 --- /dev/null +++ b/packages/router-core/tests/chunk-failure-lifecycle.test.ts @@ -0,0 +1,82 @@ +import { describe, expect, test } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute } from '../src' +import { createTestRouter } from './routerTestUtils' + +/** + * primary route chunk failures should go through the same route-error + * lifecycle as loader and beforeLoad failures. Skipping normalization means + * route onError callbacks, redirects, and notFound values from lazy/chunk + * loading can behave differently from other route failures. + * + * This test uses a real router navigation to a lazy route whose chunk rejects. + * The match reaches error state with the chunk error, but the route's onError + * callback should also receive that error as part of normal route failure + * handling. + */ + +describe('route chunk failure lifecycle', () => { + test('a rejected lazy chunk is retried on the next load instead of replaying forever', async () => { + let lazyCalls = 0 + const chunkError = new Error('lazy chunk failed once') + + const rootRoute = new BaseRootRoute({}) + const lazyRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/lazy', + loader: () => 'loaded', + }).lazy(() => { + lazyCalls++ + if (lazyCalls === 1) { + return Promise.reject(chunkError) + } + return Promise.resolve({ options: {} } as any) + }) + + const router = createTestRouter({ + routeTree: rootRoute.addChildren([lazyRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.navigate({ to: '/lazy' }) + expect( + router.state.matches.find((match) => match.routeId === lazyRoute.id) + ?.status, + ).toBe('error') + + await router.invalidate() + expect(lazyCalls).toBe(2) + expect( + router.state.matches.find((match) => match.routeId === lazyRoute.id) + ?.status, + ).toBe('success') + }) + + test('calls route onError when lazy route chunk rejects during navigation', async () => { + const chunkError = new Error('lazy chunk failed') + let capturedError: unknown + + const rootRoute = new BaseRootRoute({}) + const lazyRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/lazy', + onError: (error) => { + capturedError = error + }, + }).lazy(() => Promise.reject(chunkError)) + + const router = createTestRouter({ + routeTree: rootRoute.addChildren([lazyRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.navigate({ to: '/lazy' }) + + const lazyMatch = router.state.matches.find( + (match) => match.routeId === lazyRoute.id, + ) + expect(lazyMatch?.status).toBe('error') + expect(lazyMatch?.error).toBe(chunkError) + expect(capturedError).toBe(chunkError) + }) +}) diff --git a/packages/router-core/tests/client-lane-adversarial.test.ts b/packages/router-core/tests/client-lane-adversarial.test.ts index 06c96f132c..acfe3fede4 100644 --- a/packages/router-core/tests/client-lane-adversarial.test.ts +++ b/packages/router-core/tests/client-lane-adversarial.test.ts @@ -1,9 +1,225 @@ -import { describe, expect, test } from 'vitest' +import { afterEach, describe, expect, test, vi } from 'vitest' import { createMemoryHistory } from '@tanstack/history' -import { BaseRootRoute, BaseRoute, redirect } from '../src' -import { createTestRouter } from './routerTestUtils' +import { + BaseRootRoute, + BaseRoute, + createControlledPromise, + notFound, + redirect, +} from '../src' +import { createTestRouter, loadServerResponse } from './routerTestUtils' + +afterEach(() => { + vi.useRealTimers() +}) + +function abortAwareGate(signal: AbortSignal): Promise { + return new Promise((_resolve, reject) => { + signal.addEventListener( + 'abort', + () => { + const error = new Error('aborted') + error.name = 'AbortError' + reject(error) + }, + { once: true }, + ) + }) +} describe('adversarial client lane ownership', () => { + test('a navigation started by a pre-load listener supersedes the emitting load before it can continue', async () => { + const firstBeforeLoad = vi.fn() + const secondBeforeLoad = vi.fn() + const firstLoader = vi.fn(() => 'first data') + const secondLoader = vi.fn(() => 'second data') + + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const firstRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/first', + beforeLoad: firstBeforeLoad, + loader: firstLoader, + }) + const secondRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/second', + beforeLoad: secondBeforeLoad, + loader: secondLoader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, firstRoute, secondRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + const beforeLoadLocations: Array = [] + router.subscribe('onBeforeLoad', (event) => { + beforeLoadLocations.push(event.toLocation.pathname) + }) + router.subscribe('onBeforeNavigate', (event) => { + if (event.toLocation.pathname === '/first') { + void router.navigate({ to: '/second' }) + } + }) + + await router.navigate({ to: '/first' }) + + expect(router.state.location.pathname).toBe('/second') + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: secondRoute.id, + status: 'success', + }) + expect(beforeLoadLocations).toEqual(['/second']) + expect(firstBeforeLoad).not.toHaveBeenCalled() + expect(firstLoader).not.toHaveBeenCalled() + expect(secondBeforeLoad).toHaveBeenCalledTimes(1) + expect(secondLoader).toHaveBeenCalledTimes(1) + }) + + test.each(['onBeforeNavigate', 'onBeforeLoad'] as const)( + 'a throwing %s listener cannot interrupt later listeners or navigation finalization', + async (eventType) => { + const listenerError = new Error(`${eventType} listener failed`) + const laterListener = vi.fn() + + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const targetLoader = vi.fn(() => 'target data') + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + loader: targetLoader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, targetRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + router.subscribe(eventType, () => { + throw listenerError + }) + router.subscribe(eventType, laterListener) + + await router.navigate({ to: '/target' }) + + expect(laterListener).toHaveBeenCalledTimes(1) + expect(router.state.location.pathname).toBe('/target') + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: targetRoute.id, + status: 'success', + }) + expect(targetLoader).toHaveBeenCalledTimes(1) + }, + ) + + test('superseding a published pending lane diffs lifecycle hooks from the last final lane', async () => { + const aOnLeave = vi.fn() + const bOnEnter = vi.fn() + const bOnLeave = vi.fn() + const cOnEnter = vi.fn() + + const rootRoute = new BaseRootRoute({}) + const aRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/a', + onLeave: aOnLeave, + }) + const bRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/b', + pendingMs: 0, + pendingComponent: () => null, + loader: ({ abortController }) => abortAwareGate(abortController.signal), + onEnter: bOnEnter, + onLeave: bOnLeave, + }) + const cRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/c', + onEnter: cOnEnter, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([aRoute, bRoute, cRoute]), + history: createMemoryHistory({ initialEntries: ['/a'] }), + }) + + await router.load() + aOnLeave.mockClear() + + const bNavigation = router.navigate({ to: '/b' }) + await vi.waitFor(() => + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: bRoute.id, + status: 'pending', + }), + ) + + await router.navigate({ to: '/c' }) + await bNavigation + + expect(router.state.matches.at(-1)?.routeId).toBe(cRoute.id) + expect(aOnLeave).toHaveBeenCalledTimes(1) + expect(bOnEnter).not.toHaveBeenCalled() + expect(bOnLeave).not.toHaveBeenCalled() + expect(cOnEnter).toHaveBeenCalledTimes(1) + }) + + test('keeps a hidden child matched without projecting it below a parent error', async () => { + const parentError = new Error('parent failed') + const childHead = vi.fn(() => ({ + meta: [{ title: 'unreachable child' }], + })) + const childOnEnter = vi.fn() + + const rootRoute = new BaseRootRoute({}) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + loader: () => { + throw parentError + }, + errorComponent: () => null, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loader: () => { + throw notFound() + }, + notFoundComponent: () => null, + head: childHead, + onEnter: childOnEnter, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([parentRoute.addChildren([childRoute])]), + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + }) + + await router.load() + + expect(router.state.matches.map((match) => match.routeId)).toEqual([ + rootRoute.id, + parentRoute.id, + childRoute.id, + ]) + expect(router.state.matches[1]).toMatchObject({ + routeId: parentRoute.id, + status: 'error', + error: parentError, + }) + expect(childHead).not.toHaveBeenCalled() + expect(childOnEnter).toHaveBeenCalledTimes(1) + }) + test('a redirect aborts the discarded loader generation', async () => { let redirectSignal: AbortSignal | undefined @@ -76,4 +292,256 @@ describe('adversarial client lane ownership', () => { loaderData: childData, }) }) + + test('a terminal preload retains reusable descendant loader data', async () => { + const parentError = new Error('preload parent failed') + const childLoader = vi.fn(() => 'child data') + + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + loader: () => { + throw parentError + }, + errorComponent: () => null, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loader: childLoader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + indexRoute, + parentRoute.addChildren([childRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + await router.preloadRoute({ to: '/parent/child' }) + await router.navigate({ to: '/parent/child' }) + + expect(childLoader).toHaveBeenCalledTimes(1) + expect(router.state.matches[2]).toMatchObject({ + routeId: childRoute.id, + status: 'success', + loaderData: 'child data', + }) + }) + + test('navigation started by route context cannot be overwritten by the stale matching pass', async () => { + const bLoaderGate = createControlledPromise() + const bLoader = vi.fn(() => bLoaderGate) + let retainedLoaderSignal: AbortSignal | undefined + let abandonedContextSignal: AbortSignal | undefined + let redirected = false + + const rootRoute = new BaseRootRoute({ + staleTime: Infinity, + loader: ({ abortController }) => { + retainedLoaderSignal = abortController.signal + return 'root data' + }, + }) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const aRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/a', + context: ({ navigate, abortController }) => { + abandonedContextSignal = abortController.signal + if (!redirected) { + redirected = true + void navigate({ to: '/b' }) + } + return { fromA: true } + }, + }) + const bRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/b', + loader: bLoader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, aRoute, bRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + const navigation = router.navigate({ to: '/a' }) + await vi.waitFor(() => expect(bLoader).toHaveBeenCalledTimes(1)) + + bLoaderGate.resolve() + await navigation + + expect(router.state.location.pathname).toBe('/b') + expect(router.state.matches.at(-1)?.routeId).toBe(bRoute.id) + expect(abandonedContextSignal?.aborted).toBe(true) + expect(retainedLoaderSignal?.aborted).toBe(false) + }) + + test('a context-failure lane stays live until its terminal generation is replaced', async () => { + const contextError = new Error('context failed after starting work') + + let contextSignal: AbortSignal | undefined + let contextWorkAborted = false + const safeLoader = vi.fn(() => 'safe data') + + const rootRoute = new BaseRootRoute({}) + const brokenRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/broken', + context: ({ abortController }) => { + contextSignal = abortController.signal + abortController.signal.addEventListener( + 'abort', + () => { + contextWorkAborted = true + }, + { once: true }, + ) + throw contextError + }, + }) + const safeRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/safe', + loader: safeLoader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([brokenRoute, safeRoute]), + history: createMemoryHistory({ initialEntries: ['/broken'] }), + }) + + await router.load() + expect(contextSignal?.aborted).toBe(false) + expect(contextWorkAborted).toBe(false) + + await router.navigate({ to: '/safe' }) + expect(router.state.location.pathname).toBe('/safe') + expect(safeLoader).toHaveBeenCalledTimes(1) + expect(contextSignal?.aborted).toBe(true) + expect(contextWorkAborted).toBe(true) + }) + + test.each( + ([false, true] as const).flatMap((isServer) => [ + { + isServer, + thrownType: 'AbortSignal', + createThrownValue: (signal: AbortSignal) => signal, + }, + { + isServer, + thrownType: 'AbortError', + createThrownValue: () => + new DOMException('The operation was aborted.', 'AbortError'), + }, + ]), + )( + 'treats a user-thrown $thrownType in beforeLoad as an ordinary route error (isServer=$isServer)', + async ({ isServer, createThrownValue }) => { + let matchSignal: AbortSignal | undefined + let thrownValue: unknown + const rootRoute = new BaseRootRoute({}) + const brokenRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/broken', + beforeLoad: ({ abortController }) => { + matchSignal = abortController.signal + thrownValue = createThrownValue(matchSignal) + throw thrownValue + }, + errorComponent: () => null, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([brokenRoute]), + history: createMemoryHistory({ initialEntries: ['/broken'] }), + isServer, + }) + + if (isServer) { + const response = await loadServerResponse(router, '/broken') + expect(response.status).toBe(500) + } else { + await router.load() + } + + const match = router.state.matches.at(-1) + expect(matchSignal?.aborted).toBe(isServer) + expect(match).toMatchObject({ + routeId: brokenRoute.id, + status: 'error', + }) + expect(match?.error).toBe(thrownValue) + }, + ) + + test('a planning failure becomes the successor lane instead of stranding navigation', async () => { + const preflightError = new Error('next loaderDeps failed') + const pendingGate = createControlledPromise() + const brokenPreflightReached = createControlledPromise() + let pendingSignal: AbortSignal | undefined + const pendingStarted = createControlledPromise() + + const rootRoute = new BaseRootRoute({}) + const aRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/a', + }) + const pendingRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/pending', + pendingMs: 0, + pendingComponent: () => null, + loader: ({ abortController }) => { + pendingSignal = abortController.signal + pendingStarted.resolve() + return Promise.race([ + pendingGate, + abortAwareGate(abortController.signal), + ]) + }, + }) + const brokenRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/broken', + loaderDeps: (): Record => { + brokenPreflightReached.resolve() + throw preflightError + }, + loader: () => 'never runs', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([aRoute, pendingRoute, brokenRoute]), + history: createMemoryHistory({ initialEntries: ['/a'] }), + }) + + await router.load() + const pendingNavigation = router.navigate({ to: '/pending' }) + await pendingStarted + + const brokenNavigation = router.navigate({ to: '/broken' }) + await brokenPreflightReached + + await vi.waitFor(() => expect(pendingSignal?.aborted).toBe(true)) + + pendingGate.resolve() + await Promise.all([pendingNavigation, brokenNavigation]) + + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: brokenRoute.id, + status: 'error', + error: preflightError, + }) + expect(router.state.location.pathname).toBe('/broken') + }) }) diff --git a/packages/router-core/tests/error-boundary-cache-generation.test.ts b/packages/router-core/tests/error-boundary-cache-generation.test.ts index b36103e3b6..447dace6ea 100644 --- a/packages/router-core/tests/error-boundary-cache-generation.test.ts +++ b/packages/router-core/tests/error-boundary-cache-generation.test.ts @@ -1,8 +1,12 @@ -import { describe, expect, test, vi } from 'vitest' +import { afterEach, describe, expect, test, vi } from 'vitest' import { createMemoryHistory } from '@tanstack/history' import { BaseRootRoute, BaseRoute, createControlledPromise } from '../src' import { createTestRouter } from './routerTestUtils' +afterEach(() => { + vi.restoreAllMocks() +}) + describe('cache retention across an error-boundary commit', () => { test('a superseded generation cannot shadow newer beyond-boundary data', async () => { let parentFails = false diff --git a/packages/router-core/tests/fatal-load-rejection.test.ts b/packages/router-core/tests/fatal-load-rejection.test.ts new file mode 100644 index 0000000000..bac78891b0 --- /dev/null +++ b/packages/router-core/tests/fatal-load-rejection.test.ts @@ -0,0 +1,86 @@ +import { describe, expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { + BaseRootRoute, + BaseRoute, + createControlledPromise, + redirect, +} from '../src' +import { createTestRouter } from './routerTestUtils' + +/** + * A genuinely fatal router rejection (not a route outcome — for example, + * redirect resolution throwing while finalizing a serial beforeLoad + * redirect) must settle without publishing matches whose load + * promises never settled: the serial redirect capped the loader prefix at 0, + * so the loader phase never ran for ancestor matches and their pending + * loadPromise would hang Suspense forever. + */ + +describe('fatal load rejection', () => { + test('fatal rejection during a serial-redirect-capped pass settles the lane', async () => { + const boom = new Error('resolveRedirect failed') + const errorComponentGate = createControlledPromise() + const errorComponentStarted = createControlledPromise() + const errorComponentPreload = vi.fn(() => { + errorComponentStarted.resolve() + return errorComponentGate + }) + const ErrorComponent = Object.assign(() => null, { + preload: errorComponentPreload, + }) + + const rootRoute = new BaseRootRoute({ + // Never runs: the serial redirect caps the loader prefix at 0. Its + // loadPromise (created for the beforeLoad phase) must still settle. + loader: () => 'root data', + errorComponent: ErrorComponent, + }) + const badRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/bad', + beforeLoad: () => { + throw redirect({ + to: '/bad', + search: () => { + throw boom + }, + }) + }, + }) + const safeLoader = vi.fn(() => 'safe data') + const safeRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/safe', + loader: safeLoader, + }) + + const router = createTestRouter({ + routeTree: rootRoute.addChildren([badRoute, safeRoute]), + history: createMemoryHistory({ initialEntries: ['/bad'] }), + }) + + const load = router.load() + const outcome = await Promise.race([ + load.then(() => 'load-settled' as const), + errorComponentStarted.then(() => 'error-preload-started' as const), + ]) + if (outcome === 'error-preload-started') { + errorComponentGate.resolve() + } + expect(outcome).toBe('load-settled') + await load + expect(errorComponentPreload).not.toHaveBeenCalled() + expect(router.state.status).toBe('idle') + + errorComponentGate.resolve() + await router.navigate({ to: '/safe' }) + expect(router.state.location.pathname).toBe('/safe') + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: safeRoute.id, + status: 'success', + loaderData: 'safe data', + }) + expect(safeLoader).toHaveBeenCalledTimes(1) + }) +}) diff --git a/packages/router-core/tests/granular-stores.test.ts b/packages/router-core/tests/granular-stores.test.ts index 9d23b19faf..8d5b98755a 100644 --- a/packages/router-core/tests/granular-stores.test.ts +++ b/packages/router-core/tests/granular-stores.test.ts @@ -31,58 +31,6 @@ function createRouter() { }) } -function createLoaderRouter({ - initialEntries = ['/posts/123'], - staleTime = 0, -}: { - initialEntries?: Array - staleTime?: number -} = {}) { - let resolveLoader: (() => void) | undefined - let callCount = 0 - - const rootRoute = new BaseRootRoute({}) - - const indexRoute = new BaseRoute({ - getParentRoute: () => rootRoute, - path: '/', - }) - - const aboutRoute = new BaseRoute({ - getParentRoute: () => rootRoute, - path: '/about', - }) - - const postRoute = new BaseRoute({ - getParentRoute: () => rootRoute, - path: '/posts/$postId', - staleTime, - loader: () => { - callCount += 1 - - if (callCount === 1) { - return { version: 'initial' } - } - - return new Promise<{ version: string }>((resolve) => { - resolveLoader = () => resolve({ version: 'reloaded' }) - }) - }, - }) - - const routeTree = rootRoute.addChildren([indexRoute, aboutRoute, postRoute]) - - return { - router: createTestRouter({ - routeTree, - history: createMemoryHistory({ - initialEntries, - }), - }), - resolveLoader: () => resolveLoader?.(), - } -} - describe('granular stores', () => { test('keeps ordered route state current across match generations', async () => { const router = createRouter() @@ -112,198 +60,4 @@ describe('granular stores', () => { '/about', ]) }) - - test('match store updates are isolated to the touched active match', async () => { - const router = createRouter() - await router.navigate({ to: '/posts/123' }) - - const rootMatch = router.state.matches[0] - const leafMatch = router.state.matches[1] - - expect(rootMatch).toBeDefined() - expect(leafMatch).toBeDefined() - - if (!rootMatch || !leafMatch) { - throw new Error('Expected root and leaf matches to exist') - } - - const rootStore = router.stores.matchStores.get(rootMatch.id) - const leafStore = router.stores.matchStores.get(leafMatch.id) - - expect(rootStore).toBeDefined() - expect(leafStore).toBeDefined() - - if (!rootStore || !leafStore) { - throw new Error('Expected root and leaf match stores to exist') - } - - const rootBefore = rootStore.get() - const leafBefore = leafStore.get() - - router.updateMatch(leafMatch.id, (prev) => ({ - ...prev, - status: 'pending', - })) - - expect(rootStore.get()).toBe(rootBefore) - expect(leafStore.get()).not.toBe(leafBefore) - expect(leafStore.get().status).toBe('pending') - }) - - test('getRouteMatchStore caches store instances and clears when route is inactive', async () => { - const router = createRouter() - await router.navigate({ to: '/posts/123' }) - - const postsStore = router.stores.getRouteMatchStore('/posts/$postId') - - expect(router.stores.getRouteMatchStore('/posts/$postId')).toBe(postsStore) - expect(postsStore.get()?.routeId).toBe('/posts/$postId') - - await router.navigate({ to: '/about' }) - - expect(router.stores.getRouteMatchStore('/posts/$postId')).toBe(postsStore) - expect(postsStore.get()).toBeUndefined() - }) - - test('no-op match pool reconciliation preserves ids and match state references', async () => { - const router = createRouter() - await router.navigate({ to: '/posts/123' }) - - const activeMatches = router.state.matches - const activeIdsBefore = router.stores.matchesId.get() - const activeStoresBefore = activeMatches.map((match) => - router.stores.matchStores.get(match.id), - ) - const activeStatesBefore = activeStoresBefore.map((store) => store?.get()) - - router.stores.setMatches(activeMatches) - - expect(router.stores.matchesId.get()).toBe(activeIdsBefore) - expect(router.stores.matches.get()).toBe(activeMatches) - for (let i = 0; i < activeMatches.length; i++) { - const match = activeMatches[i]! - const store = router.stores.matchStores.get(match.id) - expect(store).toBe(activeStoresBefore[i]) - expect(store?.get()).toBe(activeStatesBefore[i]) - } - - const pendingMatches = activeMatches.map((match) => ({ - ...match, - id: `${match.id}__pending`, - status: 'pending' as const, - })) - - router.stores.setPending(pendingMatches) - - const pendingIdsBefore = router.stores.pendingIds.get() - const pendingStoresBefore = pendingMatches.map((match) => - router.stores.pendingMatchStores.get(match.id), - ) - const pendingStatesBefore = pendingStoresBefore.map((store) => store?.get()) - - router.stores.setPending(pendingMatches) - - expect(router.stores.pendingIds.get()).toBe(pendingIdsBefore) - for (let i = 0; i < pendingMatches.length; i++) { - const match = pendingMatches[i]! - const store = router.stores.pendingMatchStores.get(match.id) - expect(store).toBe(pendingStoresBefore[i]) - expect(store?.get()).toBe(pendingStatesBefore[i]) - } - }) - - test('updateMatch prefers the pending pool when active and pending share an id', async () => { - const { router, resolveLoader } = createLoaderRouter() - - await router.load() - - const activeLeaf = router.state.matches[1] - - expect(activeLeaf).toBeDefined() - - if (!activeLeaf) { - throw new Error('Expected active leaf match to exist') - } - - const activeStore = router.stores.matchStores.get(activeLeaf.id) - - expect(activeStore).toBeDefined() - - if (!activeStore) { - throw new Error('Expected active leaf store to exist') - } - - const activeBefore = activeStore.get() - - const reloadPromise = router.load() - await Promise.resolve() - - const pendingStore = router.stores.pendingMatchStores.get(activeLeaf.id) - - expect(pendingStore).toBeDefined() - expect(router.stores.matchStores.get(activeLeaf.id)).toBe(activeStore) - - if (!pendingStore) { - throw new Error('Expected pending leaf store to exist') - } - - router.updateMatch(activeLeaf.id, (prev) => ({ - ...prev, - status: 'error', - error: new Error('pending-only-update'), - })) - - expect(activeStore.get()).toBe(activeBefore) - expect(activeStore.get().status).toBe('success') - expect(pendingStore.get().status).toBe('error') - expect(pendingStore.get().error).toEqual(new Error('pending-only-update')) - - resolveLoader() - await reloadPromise - }) - - test('supports duplicate ids across pools without cross-pool contamination', async () => { - const router = createRouter() - await router.navigate({ to: '/posts/123' }) - - const activeLeaf = router.state.matches[1]! - const duplicatedId = activeLeaf.id - const pendingDuplicate = { - ...activeLeaf, - status: 'pending' as const, - } - const cachedDuplicate = { - ...activeLeaf, - status: 'success' as const, - } - - router.stores.setPending([pendingDuplicate]) - router.stores.setCached([cachedDuplicate]) - - router.stores.setMatches( - router.state.matches.map((match) => - match.id === duplicatedId - ? { - ...match, - status: 'error' as const, - error: new Error('active-only-update'), - } - : match, - ), - ) - - expect(router.stores.matchStores.get(duplicatedId)?.get().status).toBe( - 'error', - ) - expect( - router.stores.getRouteMatchStore(activeLeaf.routeId).get()?.status, - ).toBe('error') - // Pending pool has its own store for this id - expect( - router.stores.pendingMatchStores.get(duplicatedId)?.get().status, - ).toBe('pending') - expect(router.stores.pendingMatches.get()[0]?.status).toBe('pending') - expect(router.stores.cachedMatches.get()[0]?.status).toBe('success') - expect(router.getMatch(duplicatedId)?.status).toBe('success') - }) }) diff --git a/packages/router-core/tests/hmr-refresh-lifecycle.test.ts b/packages/router-core/tests/hmr-refresh-lifecycle.test.ts new file mode 100644 index 0000000000..ad7c743bf3 --- /dev/null +++ b/packages/router-core/tests/hmr-refresh-lifecycle.test.ts @@ -0,0 +1,466 @@ +import { + afterEach, + beforeEach, + describe, + expect, + onTestFinished, + test, + vi, +} from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { + BaseRootRoute, + BaseRoute, + createControlledPromise, + redirect, +} from '../src' +import { createTestRouter } from './routerTestUtils' + +beforeEach(() => { + vi.spyOn(window, 'scrollTo').mockImplementation(() => {}) +}) + +afterEach(() => { + vi.restoreAllMocks() +}) + +describe('HMR route refresh', () => { + test('reloads retained routes with onStay lifecycle semantics', async () => { + let generation = 1 + const loader = vi.fn(() => generation) + const onEnter = vi.fn() + const onLeave = vi.fn() + const order: Array = [] + const onStay = vi.fn(() => order.push('onStay')) + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + loader, + onEnter, + onLeave, + onStay, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + await router.load() + const unsubLoad = router.subscribe('onLoad', () => order.push('onLoad')) + const unsubMount = router.subscribe('onBeforeRouteMount', () => + order.push('onBeforeRouteMount'), + ) + generation = 2 + await router._refreshRoute!() + + expect(loader).toHaveBeenCalledTimes(2) + expect(router.state.matches.at(-1)?.loaderData).toBe(2) + expect(onEnter).toHaveBeenCalledTimes(1) + expect(onLeave).not.toHaveBeenCalled() + expect(onStay).toHaveBeenCalledTimes(1) + expect(order).toEqual(['onStay', 'onLoad', 'onBeforeRouteMount']) + unsubLoad() + unsubMount() + }) + + test('retires refresh mode after an acknowledged publication', async () => { + const otherLoader = vi.fn(() => 'other data') + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + }) + const otherRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/other', + loader: otherLoader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute, otherRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + await router.load() + await router._refreshRoute!() + await router.preloadRoute({ to: '/other' }) + + expect(router._tx?.[6]).toBeUndefined() + expect(otherLoader).toHaveBeenCalledOnce() + }) + + test('passes rematerialization to a reentrant preflight load', async () => { + let generation = 1 + const loader = vi.fn(() => generation) + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + staleTime: Infinity, + loader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + await router.load() + generation = 2 + let reenter = true + let reentrantLoad: Promise | undefined + const unsubscribe = router.subscribe('onBeforeNavigate', () => { + if (reenter) { + reenter = false + reentrantLoad = router.load({ sync: true }) + } + }) + onTestFinished(unsubscribe) + + await router._refreshRoute!() + await reentrantLoad + + expect(loader).toHaveBeenCalledTimes(2) + expect(router.state.matches.at(-1)?.loaderData).toBe(2) + }) + + test('passes rematerialization to a load that supersedes refresh work', async () => { + let generation = 1 + let reenter = false + let reentrantLoad: Promise | undefined + let router!: ReturnType + const loader = vi.fn(() => { + if (reenter) { + reenter = false + reentrantLoad = router.load({ sync: true }) + } + return generation + }) + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + staleTime: Infinity, + loader, + }) + router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + await router.load() + generation = 2 + reenter = true + await router._refreshRoute!() + await reentrantLoad + + expect(loader).toHaveBeenCalledTimes(3) + expect(router.state.matches.at(-1)?.loaderData).toBe(2) + }) + + test('passes rematerialization through a refresh redirect', async () => { + let generation = 1 + let shouldRedirect = false + const rootLoader = vi.fn(() => generation) + const rootRoute = new BaseRootRoute({ + staleTime: Infinity, + loader: rootLoader, + }) + const sourceRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/source', + beforeLoad: () => { + if (shouldRedirect) { + throw redirect({ to: '/target' }) + } + }, + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([sourceRoute, targetRoute]), + history: createMemoryHistory({ initialEntries: ['/source'] }), + }) + + await router.load() + generation = 2 + shouldRedirect = true + await router._refreshRoute!() + + expect(router.state.location.pathname).toBe('/target') + expect(rootLoader).toHaveBeenCalledTimes(2) + expect(router.state.matches[0]?.loaderData).toBe(2) + }) + + test('restores the committed presentation when publication fails', async () => { + let generation = 1 + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + loader: () => generation, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + await router.load() + const previousMatches = router.state.matches + generation = 2 + const startTransition = router.startTransition + router.startTransition = async (fn) => { + fn() + router.clearCache() + throw new Error('render failed') + } + + await router._refreshRoute!() + + expect(router.state.status).toBe('idle') + expect(router.state.matches).toHaveLength(previousMatches.length) + expect(router.state.matches.at(-1)?.loaderData).toBe(1) + + router.startTransition = startTransition + generation = 3 + await router._refreshRoute!() + expect(router.state.matches.at(-1)?.loaderData).toBe(3) + }) + + test('keeps the rendered generation alive until refresh is acknowledged', async () => { + let generation = 1 + const signals: Array = [] + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + loader: ({ abortController }) => { + signals.push(abortController.signal) + return generation + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + await router.load() + const renderedSignal = signals[0]! + generation = 2 + const startTransition = router.startTransition + router.startTransition = async (fn) => { + fn() + throw new Error('render failed') + } + + await router._refreshRoute!() + + expect(renderedSignal.aborted).toBe(false) + expect(signals[1]?.aborted).toBe(true) + expect(router.state.matches.at(-1)?.loaderData).toBe(1) + + router.startTransition = startTransition + generation = 3 + await router._refreshRoute!() + expect(renderedSignal.aborted).toBe(true) + expect(router.state.matches.at(-1)?.loaderData).toBe(3) + }) + + test('rolls overlapping refreshes back to the last acknowledged generation', async () => { + let generation = 1 + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + loader: () => generation, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + await router.load() + let transitionCount = 0 + let supersedeFirst!: () => void + router.startTransition = (fn) => { + transitionCount++ + fn() + if (transitionCount === 1) { + return new Promise((resolve) => { + supersedeFirst = () => resolve(false) + }) + } + return Promise.reject(new Error('replacement render failed')) + } + ;( + router as typeof router & { _cancelTransition?: () => void } + )._cancelTransition = () => supersedeFirst() + + generation = 2 + const firstRefresh = router._refreshRoute!() + await vi.waitFor(() => expect(transitionCount).toBe(1)) + + generation = 3 + const secondRefresh = router._refreshRoute!() + await Promise.all([firstRefresh, secondRefresh]) + + expect(router.state.status).toBe('idle') + expect(router.state.matches.at(-1)?.loaderData).toBe(1) + }) + + test('does not resolve a superseding navigation promise during rollback', async () => { + let generation = 1 + const destinationGate = createControlledPromise() + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + loader: () => generation, + }) + const destinationRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/destination', + loader: () => destinationGate, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute, destinationRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + await router.load() + let transitionCount = 0 + let cancelRefresh!: () => void + router.startTransition = (fn) => { + transitionCount++ + fn() + if (transitionCount === 1) { + return new Promise((resolve) => { + cancelRefresh = () => resolve(false) + }) + } + return Promise.resolve(true) + } + ;( + router as typeof router & { _cancelTransition?: () => void } + )._cancelTransition = () => cancelRefresh() + + generation = 2 + const refresh = router._refreshRoute!() + await vi.waitFor(() => expect(transitionCount).toBe(1)) + + const navigationSettled = vi.fn() + const navigation = router + .navigate({ to: '/destination' }) + .then(navigationSettled) + await vi.waitFor(() => + expect(router.state.location.pathname).toBe('/destination'), + ) + expect(navigationSettled).not.toHaveBeenCalled() + + destinationGate.resolve() + await Promise.all([refresh, navigation]) + expect(navigationSettled).toHaveBeenCalledOnce() + expect(router.state.matches.at(-1)?.routeId).toBe(destinationRoute.id) + }) + + test('waits for an ordinary pending navigation before refreshing', async () => { + const destinationGate = createControlledPromise() + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + }) + const destinationRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/destination', + pendingMs: 0, + pendingMinMs: 0, + pendingComponent: () => null, + loader: () => destinationGate, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute, destinationRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + await router.load() + const navigation = router.navigate({ to: '/destination' }) + await vi.waitFor(() => { + expect(router.state.status).toBe('pending') + expect(router.state.matches.at(-1)?.routeId).toBe(destinationRoute.id) + }) + destinationRoute.options.onStay = () => { + throw new Error('refreshed destination failed') + } + const refreshSettled = vi.fn() + const refresh = router._refreshRoute!().then(refreshSettled) + await Promise.resolve() + expect(refreshSettled).not.toHaveBeenCalled() + + destinationGate.resolve() + await Promise.all([navigation, refresh]) + + expect(router.state.status).toBe('idle') + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: destinationRoute.id, + status: 'success', + }) + expect(refreshSettled).toHaveBeenCalledOnce() + }) + + test('rolls back when an HMR lifecycle callback throws', async () => { + let generation = 1 + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + loader: () => generation, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + await router.load() + generation = 2 + pageRoute.options.onStay = () => { + throw new Error('lifecycle failed') + } + + await router._refreshRoute!() + + expect(router.state.status).toBe('idle') + expect(router.state.matches.at(-1)?.loaderData).toBe(1) + + pageRoute.options.onStay = undefined + generation = 3 + await router._refreshRoute!() + expect(router.state.matches.at(-1)?.loaderData).toBe(3) + }) + + test('does not adopt a preload created by HMR preflight hooks', async () => { + let generation = 1 + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + loader: () => generation, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + await router.load() + generation = 2 + const unsubscribe = router.subscribe('onBeforeLoad', () => { + void router.preloadRoute({ to: '/page' }) + }) + onTestFinished(unsubscribe) + + await router._refreshRoute!() + + expect(router.state.matches.at(-1)?.loaderData).toBe(2) + }) +}) diff --git a/packages/router-core/tests/hydrate.test.ts b/packages/router-core/tests/hydrate.test.ts index 8c2a1a917b..7ecec4a4e5 100644 --- a/packages/router-core/tests/hydrate.test.ts +++ b/packages/router-core/tests/hydrate.test.ts @@ -4,7 +4,9 @@ import { createMemoryHistory } from '@tanstack/history' import { BaseRootRoute, BaseRoute, + _getAssetMatches, createSerializationAdapter, + isNotFound, notFound, } from '../src' import { hydrate } from '../src/ssr/client' @@ -68,17 +70,13 @@ describe('hydrate', () => { let mockHead: any beforeEach(() => { - // Reset global window mock mockWindow = {} - ;(global as any).window = mockWindow + vi.stubGlobal('window', mockWindow) - // Reset mock head function mockHead = vi.fn() const history = createMemoryHistory({ initialEntries: ['/'] }) - const rootRoute = new BaseRootRoute({}) - const indexRoute = new BaseRoute({ getParentRoute: () => rootRoute, path: '/', @@ -87,22 +85,16 @@ describe('hydrate', () => { head: mockHead, }) - const otherRoute = new BaseRoute({ - getParentRoute: () => indexRoute, - path: '/other', - component: () => 'Other', + mockRouter = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute]), + history, + isServer: false, }) - - const routeTree = rootRoute.addChildren([ - indexRoute.addChildren([otherRoute]), - ]) - - mockRouter = createTestRouter({ routeTree, history, isServer: true }) }) afterEach(() => { - vi.resetAllMocks() - delete (global as any).window + vi.restoreAllMocks() + vi.unstubAllGlobals() }) it.each([ @@ -141,78 +133,22 @@ describe('hydrate', () => { expect(mockRouter._preflight).toBeUndefined() }) - it('round-trips adapted loader data through the bootstrap protocol', async () => { - class AdaptedValue { - constructor(readonly value: string) {} - } - - const adapter = createSerializationAdapter({ - key: 'adapted-value', - test: (value: unknown): value is AdaptedValue => - value instanceof AdaptedValue, - toSerializable: (value: AdaptedValue) => value.value, - fromSerializable: (value: string) => new AdaptedValue(value), - }) - - const serverRootRoute = new BaseRootRoute({}) - const serverIndexRoute = new BaseRoute({ - getParentRoute: () => serverRootRoute, - path: '/', - loader: () => new AdaptedValue('server loader data'), - }) - const serverRouter = createTestRouter({ - routeTree: serverRootRoute.addChildren([serverIndexRoute]), - history: createMemoryHistory({ initialEntries: ['/'] }), - isServer: true, - }) - serverRouter.options.serializationAdapters = [adapter] - - mockWindow.$_TSR = await dehydrateToBootstrap(serverRouter) - - const clientLoader = vi.fn(() => new AdaptedValue('client loader data')) - const clientRootRoute = new BaseRootRoute({}) - const clientIndexRoute = new BaseRoute({ - getParentRoute: () => clientRootRoute, - path: '/', - loader: clientLoader, - }) - const clientRouter = createTestRouter({ - routeTree: clientRootRoute.addChildren([clientIndexRoute]), - history: createMemoryHistory({ initialEntries: ['/'] }), - isServer: false, + it('clears hydration preflight when location reconstruction rejects', async () => { + const error = new Error('location parsing failed') + mockWindow.$_TSR = createMockBootstrap({ + manifest: testManifest, + dehydratedData: {}, + matches: [], }) - clientRouter.options.serializationAdapters = [adapter] - - await hydrate(clientRouter) - - const loaderData = clientRouter.state.matches.at(-1)?.loaderData - expect(loaderData).toBeInstanceOf(AdaptedValue) - expect(loaderData).toEqual(new AdaptedValue('server loader data')) - expect(clientLoader).not.toHaveBeenCalled() - }) - - it('should handle empty serialization adapters', async () => { - mockRouter.options.serializationAdapters = [] - - mockWindow.$_TSR = { - router: { - manifest: testManifest, - dehydratedData: {}, - lastMatchId: '/', - matches: [], - }, - h: vi.fn(), - e: vi.fn(), - c: vi.fn(), - p: vi.fn(), - buffer: [], - initialized: false, + mockRouter.options.hydrate = () => { + mockRouter.options.parseSearch = () => { + throw error + } } - await hydrate(mockRouter) + await expect(hydrate(mockRouter)).rejects.toBe(error) - expect(mockWindow.$_TSR.t).toBeUndefined() - expect(mockWindow.$_TSR.initialized).toBe(true) + expect(mockRouter._preflight).toBeUndefined() }) it('round-trips the manifest and matches without running client loaders', async () => { @@ -323,180 +259,405 @@ describe('hydrate', () => { expect(match).toHaveProperty('loaderData', undefined) }) - it('should hydrate globalNotFound when dehydrated flag is present', async () => { - const mockMatches = [ - { - id: '/', - routeId: '/', - index: 0, - ssr: undefined, - _nonReactive: {}, - }, - ] + it('preserves successful undefined loader data when selecting a later failure', async () => { + const serverRootRoute = new BaseRootRoute({}) + const serverParentRoute = new BaseRoute({ + getParentRoute: () => serverRootRoute, + path: '/parent', + loader: () => undefined, + }) + const serverChildRoute = new BaseRoute({ + getParentRoute: () => serverParentRoute, + path: '/child', + }) + const serverRouter = createTestRouter({ + routeTree: serverRootRoute.addChildren([ + serverParentRoute.addChildren([serverChildRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + isServer: true, + }) - const dehydratedMatches = [ - { - i: '/', - s: 'success' as const, - ssr: true, - u: Date.now(), - g: true as const, + mockWindow.$_TSR = await dehydrateToBootstrap(serverRouter) + expect(mockWindow.$_TSR.router?.matches[1]).not.toHaveProperty('l') + + const parentError = new Error('parent reload failed') + const childError = new Error('child guard failed') + const clientRootRoute = new BaseRootRoute({}) + const clientParentRoute = new BaseRoute({ + getParentRoute: () => clientRootRoute, + path: '/parent', + loader: () => { + throw parentError }, - ] + errorComponent: () => null, + }) + const clientChildRoute = new BaseRoute({ + getParentRoute: () => clientParentRoute, + path: '/child', + beforeLoad: () => { + throw childError + }, + errorComponent: () => null, + }) + const clientRouter = createTestRouter({ + routeTree: clientRootRoute.addChildren([ + clientParentRoute.addChildren([clientChildRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + isServer: false, + }) - mockRouter.matchRoutes = vi.fn().mockReturnValue(mockMatches) - mockRouter.state.matches = mockMatches + await hydrate(clientRouter) + await clientRouter.invalidate() - mockWindow.$_TSR = { - router: { - manifest: testManifest, - dehydratedData: {}, - lastMatchId: '/', - matches: dehydratedMatches, - }, - h: vi.fn(), - e: vi.fn(), - c: vi.fn(), - p: vi.fn(), - buffer: [], - initialized: false, - } + expect( + clientRouter.state.matches.find( + (match) => match.routeId === clientParentRoute.id, + ), + ).toMatchObject({ status: 'success', error: undefined }) + expect( + clientRouter.state.matches.find( + (match) => match.routeId === clientChildRoute.id, + ), + ).toMatchObject({ status: 'error', error: childError }) + }) - await hydrate(mockRouter) + it.each([ + ['mismatched', dehydrateSsrMatchId('/different-match')], + ['missing', undefined], + ['non-string', 42], + ])( + 'does not attach dehydrated data with a %s match identity', + async (_, identity) => { + const serverRootRoute = new BaseRootRoute({}) + const serverProductRoute = new BaseRoute({ + getParentRoute: () => serverRootRoute, + path: '/products/$productId', + loader: () => 'server data', + }) + const serverRouter = createTestRouter({ + routeTree: serverRootRoute.addChildren([serverProductRoute]), + history: createMemoryHistory({ initialEntries: ['/products/42'] }), + isServer: true, + }) + + mockWindow.$_TSR = await dehydrateToBootstrap(serverRouter) + mockWindow.$_TSR.router!.matches[1]!.i = identity as string + + const clientLoader = vi.fn(() => 'client data') + const clientRootRoute = new BaseRootRoute({}) + const clientProductRoute = new BaseRoute({ + getParentRoute: () => clientRootRoute, + path: '/products/$productId', + loader: clientLoader, + }) + const clientRouter = createTestRouter({ + routeTree: clientRootRoute.addChildren([clientProductRoute]), + history: createMemoryHistory({ initialEntries: ['/products/42'] }), + isServer: false, + }) + + await hydrate(clientRouter) + + const pendingMatch = clientRouter.state.matches.at(-1) + expect(pendingMatch).toMatchObject({ + routeId: clientProductRoute.id, + status: 'pending', + }) + expect(pendingMatch).not.toHaveProperty('loaderData') + expect(clientLoader).not.toHaveBeenCalled() + + await clientRouter.load() + + expect(clientRouter.state.matches.at(-1)).toMatchObject({ + routeId: clientProductRoute.id, + status: 'success', + loaderData: 'client data', + }) + expect(clientLoader).toHaveBeenCalledTimes(1) + }, + ) + + it('keeps an earlier data-only boundary when a descendant id mismatches', async () => { + const serverRootRoute = new BaseRootRoute({}) + const serverParentRoute = new BaseRoute({ + getParentRoute: () => serverRootRoute, + path: '/parent', + ssr: 'data-only', + loader: () => 'server parent data', + }) + const serverChildRoute = new BaseRoute({ + getParentRoute: () => serverParentRoute, + path: '/child', + loader: () => 'server child data', + }) + const serverRouter = createTestRouter({ + routeTree: serverRootRoute.addChildren([ + serverParentRoute.addChildren([serverChildRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + isServer: true, + }) + + mockWindow.$_TSR = await dehydrateToBootstrap(serverRouter) + mockWindow.$_TSR.router!.matches[2]!.i = dehydrateSsrMatchId( + '/different-child-match', + ) + + const childContext = vi.fn(() => ({ source: 'client child context' })) + const clientRootRoute = new BaseRootRoute({}) + const clientParentLoader = vi.fn(() => 'client parent data') + const clientParentRoute = new BaseRoute({ + getParentRoute: () => clientRootRoute, + path: '/parent', + ssr: 'data-only', + loader: clientParentLoader, + }) + const clientChildRoute = new BaseRoute({ + getParentRoute: () => clientParentRoute, + path: '/child', + context: childContext, + loader: () => 'client child data', + }) + const clientRouter = createTestRouter({ + routeTree: clientRootRoute.addChildren([ + clientParentRoute.addChildren([clientChildRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + isServer: false, + }) + + await hydrate(clientRouter) - expect((mockMatches[0] as AnyRouteMatch).globalNotFound).toBe(true) + expect(clientRouter.state.matches[1]).toMatchObject({ + routeId: clientParentRoute.id, + status: 'pending', + loaderData: 'server parent data', + }) + expect(childContext).not.toHaveBeenCalled() + expect(clientParentLoader).not.toHaveBeenCalled() + expect( + _getAssetMatches(clientRouter.state.matches).map( + (match) => match.routeId, + ), + ).toEqual([clientRootRoute.id, clientParentRoute.id]) + + await clientRouter.load() + + expect(childContext).toHaveBeenCalledTimes(1) + expect(clientParentLoader).not.toHaveBeenCalled() + expect(clientRouter.state.matches[1]).toMatchObject({ + routeId: clientParentRoute.id, + status: 'success', + loaderData: 'server parent data', + }) + expect(clientRouter.state.matches[2]).toMatchObject({ + routeId: clientChildRoute.id, + status: 'success', + loaderData: 'client child data', + }) }) - it('should leave globalNotFound undefined when dehydrated flag is omitted', async () => { - const mockMatches = [ - { - id: '/', - routeId: '/', - index: 0, - ssr: undefined, - _nonReactive: {}, - }, - ] + it('rejects a longer non-terminal server lane before attaching its data', async () => { + const serverRootRoute = new BaseRootRoute({}) + const serverParentRoute = new BaseRoute({ + getParentRoute: () => serverRootRoute, + path: '/parent', + loader: () => 'server data', + }) + const serverIndexRoute = new BaseRoute({ + getParentRoute: () => serverParentRoute, + path: '/', + }) + const serverRouter = createTestRouter({ + routeTree: serverRootRoute.addChildren([ + serverParentRoute.addChildren([serverIndexRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/parent'] }), + isServer: true, + }) - const dehydratedMatches = [ - { - i: '/', - s: 'success' as const, - ssr: true, - u: Date.now(), - }, - ] + mockWindow.$_TSR = await dehydrateToBootstrap(serverRouter) + expect(mockWindow.$_TSR.router?.matches).toHaveLength(3) + + const clientLoader = vi.fn(() => 'client data') + const clientRootRoute = new BaseRootRoute({}) + const clientParentRoute = new BaseRoute({ + getParentRoute: () => clientRootRoute, + path: '/parent', + loader: clientLoader, + }) + const clientRouter = createTestRouter({ + routeTree: clientRootRoute.addChildren([clientParentRoute]), + history: createMemoryHistory({ initialEntries: ['/parent'] }), + isServer: false, + }) - mockRouter.matchRoutes = vi.fn().mockReturnValue(mockMatches) - mockRouter.state.matches = mockMatches + await hydrate(clientRouter) - mockWindow.$_TSR = { - router: { - manifest: testManifest, - dehydratedData: {}, - lastMatchId: '/', - matches: dehydratedMatches, - }, - h: vi.fn(), - e: vi.fn(), - c: vi.fn(), - p: vi.fn(), - buffer: [], - initialized: false, + expect(clientRouter.state.resolvedLocation).toBeUndefined() + expect(clientRouter.state.matches.at(-1)).not.toHaveProperty('loaderData') + expect(clientLoader).not.toHaveBeenCalled() + + await clientRouter.load() + + expect(clientRouter.state.matches.at(-1)).toMatchObject({ + routeId: clientParentRoute.id, + status: 'success', + loaderData: 'client data', + }) + expect(clientLoader).toHaveBeenCalledTimes(1) + }) + + it('round-trips adapted loader data through the bootstrap protocol', async () => { + class AdaptedValue { + constructor(readonly value: string) {} } - await hydrate(mockRouter) + const adapter = createSerializationAdapter({ + key: 'adapted-value', + test: (value: unknown): value is AdaptedValue => + value instanceof AdaptedValue, + toSerializable: (value: AdaptedValue) => value.value, + fromSerializable: (value: string) => new AdaptedValue(value), + }) - expect((mockMatches[0] as AnyRouteMatch).globalNotFound).toBeUndefined() + const serverRootRoute = new BaseRootRoute({}) + const serverIndexRoute = new BaseRoute({ + getParentRoute: () => serverRootRoute, + path: '/', + loader: () => new AdaptedValue('server loader data'), + }) + const serverRouter = createTestRouter({ + routeTree: serverRootRoute.addChildren([serverIndexRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + isServer: true, + }) + serverRouter.options.serializationAdapters = [adapter] + + mockWindow.$_TSR = await dehydrateToBootstrap(serverRouter) + + const clientLoader = vi.fn(() => new AdaptedValue('client loader data')) + const clientRootRoute = new BaseRootRoute({}) + const clientIndexRoute = new BaseRoute({ + getParentRoute: () => clientRootRoute, + path: '/', + loader: clientLoader, + }) + const clientRouter = createTestRouter({ + routeTree: clientRootRoute.addChildren([clientIndexRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + isServer: false, + }) + clientRouter.options.serializationAdapters = [adapter] + + await hydrate(clientRouter) + + const loaderData = clientRouter.state.matches.at(-1)?.loaderData + expect(loaderData).toBeInstanceOf(AdaptedValue) + expect(loaderData).toEqual(new AdaptedValue('server loader data')) + expect(clientLoader).not.toHaveBeenCalled() }) - it('should preserve existing globalNotFound when dehydrated flag is omitted', async () => { - const mockMatches = [ - { - id: '/', - routeId: '/', - index: 0, - ssr: undefined, - _nonReactive: {}, - globalNotFound: true, + it('round-trips a server root notFound lane as _notFound', async () => { + const serverRootRoute = new BaseRootRoute({ + notFoundComponent: () => 'Not Found', + }) + const serverMissingRoute = new BaseRoute({ + getParentRoute: () => serverRootRoute, + path: '/missing', + loader: () => { + throw notFound({ data: { source: 'server loader' } }) }, - ] + }) + const serverRouter = createTestRouter({ + routeTree: serverRootRoute.addChildren([serverMissingRoute]), + history: createMemoryHistory({ initialEntries: ['/missing'] }), + isServer: true, + }) - const dehydratedMatches = [ - { - i: '/', - s: 'success' as const, - ssr: true, - u: Date.now(), - }, - ] + mockWindow.$_TSR = await dehydrateToBootstrap(serverRouter) - mockRouter.matchRoutes = vi.fn().mockReturnValue(mockMatches) - mockRouter.state.matches = mockMatches + expect(serverRouter.state.matches).toHaveLength(2) + expect(serverRouter.state.matches[0]).toMatchObject({ + routeId: serverRootRoute.id, + status: 'success', + _notFound: true, + }) + expect(isNotFound(serverRouter.state.matches[0]?.error)).toBe(true) - mockWindow.$_TSR = { - router: { - manifest: testManifest, - dehydratedData: {}, - lastMatchId: '/', - matches: dehydratedMatches, - }, - h: vi.fn(), - e: vi.fn(), - c: vi.fn(), - p: vi.fn(), - buffer: [], - initialized: false, - } + const clientLoader = vi.fn(() => 'client data') + const clientRootRoute = new BaseRootRoute({ + notFoundComponent: () => 'Not Found', + }) + const clientMissingRoute = new BaseRoute({ + getParentRoute: () => clientRootRoute, + path: '/missing', + loader: clientLoader, + }) + const clientRouter = createTestRouter({ + routeTree: clientRootRoute.addChildren([clientMissingRoute]), + history: createMemoryHistory({ initialEntries: ['/missing'] }), + isServer: false, + }) - await hydrate(mockRouter) + await hydrate(clientRouter) - expect((mockMatches[0] as AnyRouteMatch).globalNotFound).toBe(true) + expect(clientRouter.state.matches).toHaveLength(2) + expect(clientRouter.state.matches[0]).toMatchObject({ + routeId: clientRootRoute.id, + status: 'success', + _notFound: true, + }) + expect(clientRouter.state.matches[1]).toMatchObject({ + routeId: clientMissingRoute.id, + status: 'pending', + }) + expect(isNotFound(clientRouter.state.matches[0]?.error)).toBe(true) + expect(clientRouter.state.matches[0]?.error).toEqual( + expect.objectContaining({ data: { source: 'server loader' } }), + ) + expect(clientLoader).not.toHaveBeenCalled() }) - it('should decode dehydrated match ids before hydration lookup and SPA-mode checks', async () => { - const loadSpy = vi.spyOn(mockRouter, 'load') + it('preserves a client-matched _notFound when a shell payload omits the flag', async () => { + const serverRootRoute = new BaseRootRoute({ + notFoundComponent: () => 'Not Found', + }) + const serverKnownRoute = new BaseRoute({ + getParentRoute: () => serverRootRoute, + path: '/known', + }) + const serverRouter = createTestRouter({ + routeTree: serverRootRoute.addChildren([serverKnownRoute]), + history: createMemoryHistory({ initialEntries: ['/known'] }), + isServer: true, + isShell: true, + }) + mockWindow.$_TSR = await dehydrateToBootstrap(serverRouter) - const mockMatches = [ - { - id: '/', - routeId: '/', - index: 0, - ssr: undefined, - _nonReactive: {}, - }, - ] - - mockRouter.matchRoutes = vi.fn().mockReturnValue(mockMatches) - mockRouter.state.matches = mockMatches - - mockWindow.$_TSR = { - router: { - manifest: testManifest, - dehydratedData: {}, - lastMatchId: dehydrateSsrMatchId('/'), - matches: [ - { - i: dehydrateSsrMatchId('/'), - l: { indexData: 'server-data' }, - s: 'success', - ssr: true, - u: Date.now(), - }, - ], - }, - h: vi.fn(), - e: vi.fn(), - c: vi.fn(), - p: vi.fn(), - buffer: [], - initialized: false, - } + const clientRootRoute = new BaseRootRoute({ + notFoundComponent: () => 'Not Found', + }) + const clientKnownRoute = new BaseRoute({ + getParentRoute: () => clientRootRoute, + path: '/known', + }) + const clientRouter = createTestRouter({ + routeTree: clientRootRoute.addChildren([clientKnownRoute]), + history: createMemoryHistory({ initialEntries: ['/does-not-exist'] }), + isServer: false, + }) - await hydrate(mockRouter) + await hydrate(clientRouter) - expect(loadSpy).not.toHaveBeenCalled() - expect((mockRouter.state.matches[0] as AnyRouteMatch).id).toBe('/') + expect(clientRouter.state.matches).toHaveLength(1) + expect(clientRouter.state.matches[0]).toMatchObject({ + routeId: clientRootRoute.id, + status: 'success', + _notFound: true, + }) }) it('applies round-tripped custom hydration before matching a rewritten location', async () => { @@ -578,54 +739,38 @@ describe('hydrate', () => { expect(clientLoader).not.toHaveBeenCalled() }) - it('should handle errors during route context hydration', async () => { + it('logs and swallows a notFound thrown by a hydrated route head', async () => { const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) mockHead.mockImplementation(() => { - throw notFound() - }) - - const mockMatches = [ - { id: '/', routeId: '/', index: 0, ssr: true, _nonReactive: {} }, - ] - - mockRouter.matchRoutes = vi.fn().mockReturnValue(mockMatches) - mockRouter.state.matches = mockMatches - - mockWindow.$_TSR = { - router: { - manifest: testManifest, - dehydratedData: {}, - lastMatchId: '/', - matches: [ - { - i: '/', - l: { data: 'test' }, - s: 'success', - ssr: true, - u: Date.now(), - }, - ], - }, - h: vi.fn(), - e: vi.fn(), - c: vi.fn(), - p: vi.fn(), - buffer: [], - initialized: false, - } + throw notFound({ data: { source: 'index head' } }) + }) + const matched = mockRouter.matchRoutes(mockRouter.stores.location.get()) - await hydrate(mockRouter) + mockWindow.$_TSR = createMockBootstrap({ + manifest: testManifest, + dehydratedData: {}, + matches: matched.map((match: AnyRouteMatch) => ({ + i: dehydrateSsrMatchId(match.id), + s: 'success' as const, + ssr: true, + u: Date.now(), + })), + }) - const match = mockRouter.state.matches[0] as AnyRouteMatch - expect(match.error).toEqual({ isNotFound: true }) + await hydrate(mockRouter) + const match = mockRouter.state.matches.find( + (candidate: AnyRouteMatch) => candidate.routeId === '/', + ) as AnyRouteMatch | undefined + expect(match).toBeDefined() + expect(match?.status).toBe('success') + expect(match?.error).toBeUndefined() + expect(mockHead).toHaveBeenCalledOnce() expect(consoleSpy).toHaveBeenCalledWith( - 'NotFound error during hydration for routeId: /', expect.objectContaining({ isNotFound: true, + data: { source: 'index head' }, }), ) - - consoleSpy.mockRestore() }) }) diff --git a/packages/router-core/tests/hydrated-stay-match-data.test.ts b/packages/router-core/tests/hydrated-stay-match-data.test.ts new file mode 100644 index 0000000000..5f8333310a --- /dev/null +++ b/packages/router-core/tests/hydrated-stay-match-data.test.ts @@ -0,0 +1,160 @@ +import { runInNewContext } from 'node:vm' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute } from '../src' +import { hydrate } from '../src/ssr/client' +import { attachRouterServerSsrUtils } from '../src/ssr/ssr-server' +import { createTestRouter } from './routerTestUtils' +import type { AnyRouter } from '../src' +import type { TsrSsrGlobal } from '../src/ssr/types' +import type { ServerManifest } from '../src/manifest' + +const testManifest: ServerManifest = { routes: {} } + +async function dehydrateToBootstrap(router: AnyRouter): Promise { + attachRouterServerSsrUtils({ router, manifest: testManifest }) + try { + await router.load() + await router.serverSsr!.dehydrate() + + const script = router.serverSsr!.takeBufferedScripts() + expect(script?.children).toBeTruthy() + + const context: Record = { + document: { currentScript: { remove() {} } }, + } + context.self = context + runInNewContext(script!.children!, context) + + expect(context.$_TSR).toBeDefined() + return context.$_TSR + } finally { + router.serverSsr?.cleanup() + } +} + +describe('hydrated stay match data preservation', () => { + let mockWindow: { $_TSR?: TsrSsrGlobal } + + beforeEach(() => { + mockWindow = {} + vi.stubGlobal('window', mockWindow) + }) + + afterEach(() => { + vi.restoreAllMocks() + vi.unstubAllGlobals() + }) + + it('keeps server loader data while refreshing context on client navigation', async () => { + const serverRootBeforeLoad = vi.fn(() => ({ auth: 'server' })) + const serverRootLoader = vi.fn(() => ({ root: 'server' })) + const serverARouteLoader = vi.fn(() => 'a server data') + const serverRootRoute = new BaseRootRoute({ + beforeLoad: serverRootBeforeLoad, + loader: serverRootLoader, + }) + const serverARoute = new BaseRoute({ + getParentRoute: () => serverRootRoute, + path: '/a', + ssr: false, + loader: serverARouteLoader, + }) + const serverBRoute = new BaseRoute({ + getParentRoute: () => serverRootRoute, + path: '/b', + }) + const serverRouter = createTestRouter({ + routeTree: serverRootRoute.addChildren([serverARoute, serverBRoute]), + history: createMemoryHistory({ initialEntries: ['/a'] }), + isServer: true, + }) + + const bootstrap = await dehydrateToBootstrap(serverRouter) + + expect(serverRootBeforeLoad).toHaveBeenCalledTimes(1) + expect(serverRootLoader).toHaveBeenCalledTimes(1) + expect(serverARouteLoader).not.toHaveBeenCalled() + expect(serverRouter.state.matches[0]).toMatchObject({ + routeId: serverRootRoute.id, + status: 'success', + context: { auth: 'server' }, + loaderData: { root: 'server' }, + }) + const rootBeforeLoad = vi.fn(() => ({ auth: 'client' })) + const rootLoader = vi.fn(() => ({ root: 'client' })) + const history = createMemoryHistory({ initialEntries: ['/a'] }) + + const rootRoute = new BaseRootRoute({ + beforeLoad: rootBeforeLoad, + loader: rootLoader, + }) + const aRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/a', + ssr: false, + loader: () => 'a client data', + }) + const bRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/b', + loader: () => 'b client data', + }) + + const router = createTestRouter({ + routeTree: rootRoute.addChildren([aRoute, bRoute]), + history, + isServer: false, + }) + + mockWindow.$_TSR = bootstrap + + await hydrate(router) + + // Hydration adopts the server root before client loads can re-enter beforeLoad. + expect(rootBeforeLoad).not.toHaveBeenCalled() + expect(rootLoader).not.toHaveBeenCalled() + expect( + router.state.matches.find((m) => m.routeId === rootRoute.id), + ).toMatchObject({ + context: { auth: 'server' }, + loaderData: { root: 'server' }, + }) + + await router.load() + + expect( + router.state.matches.find((m) => m.routeId === aRoute.id)?.loaderData, + ).toBe('a client data') + expect(router.state.location.pathname).toBe('/a') + expect(router.state.resolvedLocation?.pathname).toBe('/a') + expect(rootLoader).not.toHaveBeenCalled() + expect( + router.state.matches.find((m) => m.routeId === rootRoute.id), + ).toMatchObject({ + context: { auth: 'server' }, + loaderData: { root: 'server' }, + }) + + // The root stay match refreshes beforeLoad context without rerunning its loader. + await router.navigate({ to: '/b' }) + + expect( + router.state.matches.find((m) => m.routeId === bRoute.id)?.loaderData, + ).toBe('b client data') + expect(router.state.location.pathname).toBe('/b') + expect(router.state.resolvedLocation?.pathname).toBe('/b') + expect(router.state.matches.map((match) => match.routeId)).toEqual([ + rootRoute.id, + bRoute.id, + ]) + expect(rootBeforeLoad).toHaveBeenCalledTimes(1) + expect(rootLoader).not.toHaveBeenCalled() + expect( + router.state.matches.find((m) => m.routeId === rootRoute.id), + ).toMatchObject({ + context: { auth: 'client' }, + loaderData: { root: 'server' }, + }) + }) +}) diff --git a/packages/router-core/tests/hydration-asset-context-order.test.ts b/packages/router-core/tests/hydration-asset-context-order.test.ts new file mode 100644 index 0000000000..64ea28971e --- /dev/null +++ b/packages/router-core/tests/hydration-asset-context-order.test.ts @@ -0,0 +1,99 @@ +import { runInNewContext } from 'node:vm' +import { afterEach, expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute } from '../src' +import { hydrate } from '../src/ssr/client' +import { attachRouterServerSsrUtils } from '../src/ssr/ssr-server' +import { createTestRouter } from './routerTestUtils' +import type { AnyRouteMatch, AnyRouter } from '../src' +import type { ServerManifest } from '../src/manifest' +import type { TsrSsrGlobal } from '../src/ssr/types' + +const testManifest: ServerManifest = { routes: {} } + +async function dehydrateToBootstrap(router: AnyRouter): Promise { + attachRouterServerSsrUtils({ router, manifest: testManifest }) + try { + await router.load() + await router.serverSsr!.dehydrate() + + const script = router.serverSsr!.takeBufferedScripts() + expect(script?.children).toBeTruthy() + + const context: Record = { + document: { currentScript: { remove() {} } }, + } + context.self = context + runInNewContext(script!.children!, context) + + expect(context.$_TSR).toBeDefined() + return context.$_TSR + } finally { + router.serverSsr?.cleanup() + } +} + +afterEach(() => { + vi.restoreAllMocks() + vi.unstubAllGlobals() +}) + +test('hydration reconstructs every match context before ancestor head reads the lane', async () => { + const serverBeforeLoad = vi.fn(() => ({ + user: 'server authenticated user', + })) + const serverRootRoute = new BaseRootRoute({}) + const serverChildRoute = new BaseRoute({ + getParentRoute: () => serverRootRoute, + path: '/child', + beforeLoad: serverBeforeLoad, + }) + const serverRouter = createTestRouter({ + routeTree: serverRootRoute.addChildren([serverChildRoute]), + history: createMemoryHistory({ initialEntries: ['/child'] }), + isServer: true, + }) + + const bootstrap = await dehydrateToBootstrap(serverRouter) + + expect(serverBeforeLoad).toHaveBeenCalledTimes(1) + expect(serverRouter.state.matches.at(-1)?.context).toMatchObject({ + user: 'server authenticated user', + }) + let childContextSeenByRootHead: unknown + const rootHead = vi.fn(({ matches }: { matches: Array }) => { + childContextSeenByRootHead = matches[1]?.context + return { meta: [{ title: 'hydrated' }] } + }) + const clientBeforeLoad = vi.fn(() => ({ user: 'client fallback' })) + const rootRoute = new BaseRootRoute({ + head: rootHead, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/child', + beforeLoad: clientBeforeLoad, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([childRoute]), + history: createMemoryHistory({ initialEntries: ['/child'] }), + isServer: false, + }) + + vi.stubGlobal('window', { $_TSR: bootstrap }) + + await hydrate(router) + + expect(childContextSeenByRootHead).toMatchObject({ + user: 'server authenticated user', + }) + expect(clientBeforeLoad).not.toHaveBeenCalled() + expect(rootHead).toHaveBeenCalledTimes(1) + expect(router.state.location.pathname).toBe('/child') + expect(router.state.resolvedLocation?.pathname).toBe('/child') + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: childRoute.id, + context: { user: 'server authenticated user' }, + }) + expect(router.state.matches[0]?.meta).toEqual([{ title: 'hydrated' }]) +}) diff --git a/packages/router-core/tests/hydration-boundary-chunks.test.ts b/packages/router-core/tests/hydration-boundary-chunks.test.ts new file mode 100644 index 0000000000..fe760e074a --- /dev/null +++ b/packages/router-core/tests/hydration-boundary-chunks.test.ts @@ -0,0 +1,235 @@ +import { runInNewContext } from 'node:vm' +import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute, isNotFound, notFound } from '../src' +import { hydrate } from '../src/ssr/client' +import { attachRouterServerSsrUtils } from '../src/ssr/ssr-server' +import { createTestRouter } from './routerTestUtils' +import type { AnyRouter } from '../src' +import type { TsrSsrGlobal } from '../src/ssr/types' +import type { ServerManifest } from '../src/manifest' + +const testManifest: ServerManifest = { routes: {} } + +async function dehydrateToBootstrap(router: AnyRouter): Promise { + attachRouterServerSsrUtils({ router, manifest: testManifest }) + try { + await router.load() + await router.serverSsr!.dehydrate() + + const script = router.serverSsr!.takeBufferedScripts() + expect(script?.children).toBeTruthy() + + const context: Record = { + document: { currentScript: { remove() {} } }, + } + context.self = context + runInNewContext(script!.children!, context) + + expect(context.$_TSR).toBeDefined() + return context.$_TSR + } finally { + router.serverSsr?.cleanup() + } +} + +describe('hydration route chunks below a server boundary', () => { + let mockWindow: { $_TSR?: TsrSsrGlobal } + + beforeEach(() => { + mockWindow = {} + vi.stubGlobal('window', mockWindow) + }) + + afterEach(() => { + vi.restoreAllMocks() + vi.unstubAllGlobals() + }) + + test('hydrates an error boundary without requiring its normal component chunk', async () => { + const serverError = new Error('server beforeLoad failed') + const serverBeforeLoad = vi.fn(() => { + throw serverError + }) + const serverRootRoute = new BaseRootRoute({}) + const serverAppRoute = new BaseRoute({ + getParentRoute: () => serverRootRoute, + path: '/app', + beforeLoad: serverBeforeLoad, + errorComponent: () => null, + }) + const serverRouter = createTestRouter({ + routeTree: serverRootRoute.addChildren([serverAppRoute]), + history: createMemoryHistory({ initialEntries: ['/app'] }), + isServer: true, + }) + + const bootstrap = await dehydrateToBootstrap(serverRouter) + + expect(serverBeforeLoad).toHaveBeenCalledTimes(1) + expect(serverRouter.state.location.pathname).toBe('/app') + expect(serverRouter.state.matches.map((match) => match.routeId)).toEqual([ + serverRootRoute.id, + serverAppRoute.id, + ]) + expect(serverRouter.state.matches.at(-1)).toMatchObject({ + status: 'error', + error: serverError, + }) + const transportedError = bootstrap.router?.matches.at(-1)?.e + expect(transportedError).not.toBe(serverError) + expect(transportedError).toMatchObject({ + name: serverError.name, + message: serverError.message, + }) + + const normalChunkError = new Error('normal component chunk unavailable') + const normalComponentPreload = vi.fn(() => Promise.reject(normalChunkError)) + const errorComponentPreload = vi.fn(() => Promise.resolve()) + const clientBeforeLoad = vi.fn(() => { + throw new Error('client beforeLoad should not run') + }) + const NormalComponent = Object.assign(() => null, { + preload: normalComponentPreload, + }) + const ErrorComponent = Object.assign(() => null, { + preload: errorComponentPreload, + }) + + const rootRoute = new BaseRootRoute({}) + const appRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/app', + beforeLoad: clientBeforeLoad, + component: NormalComponent, + errorComponent: ErrorComponent, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([appRoute]), + history: createMemoryHistory({ initialEntries: ['/app'] }), + isServer: false, + }) + mockWindow.$_TSR = bootstrap + + await expect(hydrate(router)).resolves.toBeUndefined() + + const appMatch = router.state.matches.find( + (match) => match.routeId === appRoute.id, + ) + expect(appMatch?.status).toBe('error') + expect(appMatch?.error).toBe(transportedError) + expect(appMatch?.error).toMatchObject({ + name: serverError.name, + message: serverError.message, + }) + expect(router.state.location.pathname).toBe('/app') + expect(router.state.resolvedLocation?.pathname).toBe('/app') + expect(clientBeforeLoad).not.toHaveBeenCalled() + expect(normalComponentPreload).not.toHaveBeenCalled() + expect(errorComponentPreload).toHaveBeenCalledTimes(1) + }) + + test.each([ + { ssr: undefined, hydratedStatus: 'notFound', resolved: true }, + { + ssr: 'data-only' as const, + hydratedStatus: 'pending', + resolved: false, + }, + ])( + 'hydrates an ancestor notFound boundary with ssr=$ssr without loading an omitted descendant chunk', + async ({ ssr, hydratedStatus, resolved }) => { + const serverRootRoute = new BaseRootRoute({}) + const serverParentRoute = new BaseRoute({ + getParentRoute: () => serverRootRoute, + path: '/parent', + ssr, + notFoundComponent: () => 'not found', + }) + const serverNotFound = notFound({ routeId: serverParentRoute.id }) + const serverChildLoader = vi.fn(() => { + throw serverNotFound + }) + const serverChildRoute = new BaseRoute({ + getParentRoute: () => serverParentRoute, + path: '/child', + loader: serverChildLoader, + }) + const serverRouter = createTestRouter({ + routeTree: serverRootRoute.addChildren([ + serverParentRoute.addChildren([serverChildRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + isServer: true, + }) + + const bootstrap = await dehydrateToBootstrap(serverRouter) + + expect(serverChildLoader).toHaveBeenCalledTimes(1) + expect(serverRouter.state.matches.map((match) => match.routeId)).toEqual([ + serverRootRoute.id, + serverParentRoute.id, + serverChildRoute.id, + ]) + expect(serverRouter.state.matches[1]?.status).toBe('notFound') + expect(isNotFound(serverRouter.state.matches[1]?.error)).toBe(true) + const childChunkError = new Error('omitted child chunk unavailable') + const childComponentPreload = vi.fn(() => Promise.reject(childChunkError)) + const ChildComponent = Object.assign(() => null, { + preload: childComponentPreload, + }) + + const rootRoute = new BaseRootRoute({}) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + ssr, + notFoundComponent: () => 'not found', + }) + const childLoader = vi.fn(() => { + throw new Error('client child loader should not run') + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loader: childLoader, + component: ChildComponent, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + parentRoute.addChildren([childRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + isServer: false, + }) + mockWindow.$_TSR = bootstrap + + await expect(hydrate(router)).resolves.toBeUndefined() + + expect(router.state.matches.map((match) => match.routeId)).toEqual([ + rootRoute.id, + parentRoute.id, + childRoute.id, + ]) + expect(router.state.matches[1]?.status).toBe(hydratedStatus) + expect(isNotFound(router.state.matches[1]?.error)).toBe(true) + expect(router.state.matches[2]?.status).toBe('pending') + expect(router.state.location.pathname).toBe('/parent/child') + expect(router.state.resolvedLocation?.pathname).toBe( + resolved ? '/parent/child' : undefined, + ) + expect(childComponentPreload).not.toHaveBeenCalled() + expect(childLoader).not.toHaveBeenCalled() + + if (ssr === 'data-only') { + await router.load() + + expect(router.state.resolvedLocation?.pathname).toBe('/parent/child') + expect(router.state.matches[1]?.status).toBe('notFound') + expect(isNotFound(router.state.matches[1]?.error)).toBe(true) + expect(childComponentPreload).not.toHaveBeenCalled() + expect(childLoader).not.toHaveBeenCalled() + } + }, + ) +}) diff --git a/packages/router-core/tests/hydration-currentness.test.ts b/packages/router-core/tests/hydration-currentness.test.ts new file mode 100644 index 0000000000..3257b0e201 --- /dev/null +++ b/packages/router-core/tests/hydration-currentness.test.ts @@ -0,0 +1,675 @@ +import { runInNewContext } from 'node:vm' +import { + afterEach, + beforeEach, + describe, + expect, + onTestFinished, + test, + vi, +} from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { + BaseRootRoute, + BaseRoute, + _getAssetMatches, + createControlledPromise, +} from '../src' +import { hydrate } from '../src/ssr/client' +import { attachRouterServerSsrUtils } from '../src/ssr/ssr-server' +import { dehydrateSsrMatchId } from '../src/ssr/ssr-match-id' +import { createTestRouter } from './routerTestUtils' +import type { AnyRouteMatch, AnyRouter, NavigateFn } from '../src' +import type { DehydratedRouter, TsrSsrGlobal } from '../src/ssr/types' +import type { ServerManifest } from '../src/manifest' + +const testManifest: ServerManifest = { routes: {} } + +async function dehydrateToBootstrap(router: AnyRouter): Promise { + attachRouterServerSsrUtils({ router, manifest: testManifest }) + try { + await router.load() + await router.serverSsr!.dehydrate() + + const script = router.serverSsr!.takeBufferedScripts() + expect(script?.children).toBeTruthy() + + const context: Record = { + document: { currentScript: { remove() {} } }, + } + context.self = context + runInNewContext(script!.children!, context) + + expect(context.$_TSR).toBeDefined() + return context.$_TSR + } finally { + router.serverSsr?.cleanup() + } +} + +// These tests install the hydration protocol directly so client-only hooks can +// be paused at deterministic ownership boundaries. +function installHydrationPayload( + mockWindow: { $_TSR?: TsrSsrGlobal }, + matches: DehydratedRouter['matches'], +) { + mockWindow.$_TSR = { + router: { + manifest: testManifest, + dehydratedData: {}, + matches, + }, + h: vi.fn(), + e: vi.fn(), + c: vi.fn(), + p: vi.fn(), + buffer: [], + initialized: false, + } +} + +/** + * Hydration asset work is another private lane generation. If a public + * navigation commits while an old hydration head is pending, the hydration + * continuation must not execute more old-lane hooks or mark its captured + * location as resolved. + */ +describe('hydration asset currentness', () => { + let mockWindow: { $_TSR?: TsrSsrGlobal } + + beforeEach(() => { + mockWindow = {} + vi.stubGlobal('window', mockWindow) + }) + + afterEach(() => { + vi.restoreAllMocks() + vi.unstubAllGlobals() + }) + + test('shell hydration preserves the complete lane and its first pending boundary', async () => { + const childLoader = vi.fn(() => 'client data') + const rootRoute = new BaseRootRoute({}) + const childRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/child', + ssr: false, + loader: childLoader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([childRoute]), + history: createMemoryHistory({ initialEntries: ['/child'] }), + isServer: false, + }) + const matches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload(mockWindow, [ + { + i: dehydrateSsrMatchId(matches[0]!.id), + s: 'success', + ssr: true, + u: Date.now(), + }, + ]) + + await hydrate(router) + + expect(router.state.matches.map((match) => match.routeId)).toEqual([ + rootRoute.id, + childRoute.id, + ]) + expect(router.state.matches[1]).toMatchObject({ + routeId: childRoute.id, + status: 'pending', + ssr: false, + }) + expect(router.state.resolvedLocation).toBeUndefined() + expect(childLoader).not.toHaveBeenCalled() + }) + + test('includes an identity-verified ssr:false boundary in a data-only asset prefix', async () => { + const serverChildLoader = vi.fn(() => 'server child data') + const serverRootRoute = new BaseRootRoute({}) + const serverParentRoute = new BaseRoute({ + getParentRoute: () => serverRootRoute, + path: '/parent', + ssr: 'data-only', + }) + const serverChildRoute = new BaseRoute({ + getParentRoute: () => serverParentRoute, + path: '/child', + ssr: false, + loader: serverChildLoader, + }) + const serverTailRoute = new BaseRoute({ + getParentRoute: () => serverChildRoute, + path: '/tail', + }) + const serverRouter = createTestRouter({ + routeTree: serverRootRoute.addChildren([ + serverParentRoute.addChildren([ + serverChildRoute.addChildren([serverTailRoute]), + ]), + ]), + history: createMemoryHistory({ + initialEntries: ['/parent/child/tail'], + }), + isServer: true, + }) + mockWindow.$_TSR = await dehydrateToBootstrap(serverRouter) + expect(serverChildLoader).not.toHaveBeenCalled() + + const childContext = vi.fn(() => ({ assetSource: 'child context' })) + const childHead = vi.fn(({ match }: { match: AnyRouteMatch }) => ({ + meta: [ + { + name: 'ssr-false-boundary', + content: (match.context as { assetSource?: string }).assetSource, + }, + ], + })) + const rootRoute = new BaseRootRoute({}) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + ssr: 'data-only', + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + ssr: false, + loader: () => 'client child data', + context: childContext, + head: childHead, + }) + const tailRoute = new BaseRoute({ + getParentRoute: () => childRoute, + path: '/tail', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + parentRoute.addChildren([childRoute.addChildren([tailRoute])]), + ]), + history: createMemoryHistory({ + initialEntries: ['/parent/child/tail'], + }), + isServer: false, + }) + + await hydrate(router) + + expect( + _getAssetMatches(router.state.matches).map((match) => match.routeId), + ).toEqual([rootRoute.id, parentRoute.id, childRoute.id]) + expect(childContext).toHaveBeenCalledOnce() + expect(childHead).toHaveBeenCalledOnce() + expect(router.state.matches[2]?.meta).toEqual([ + { name: 'ssr-false-boundary', content: 'child context' }, + ]) + }) + + test('preserves a verified data-only asset prefix through its client continuation', async () => { + const serverRootRoute = new BaseRootRoute({}) + const serverParentRoute = new BaseRoute({ + getParentRoute: () => serverRootRoute, + path: '/parent', + ssr: 'data-only', + }) + const serverChildRoute = new BaseRoute({ + getParentRoute: () => serverParentRoute, + path: '/child', + ssr: false, + loader: () => 'server child data', + }) + const serverRouter = createTestRouter({ + routeTree: serverRootRoute.addChildren([ + serverParentRoute.addChildren([serverChildRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + isServer: true, + }) + mockWindow.$_TSR = await dehydrateToBootstrap(serverRouter) + + const childLoaderResult = createControlledPromise() + let continuationAssetEnd: number | undefined + let router!: AnyRouter + const childLoader = vi.fn(() => { + continuationAssetEnd = router._tx?.[3][1]?._assetEnd + return childLoaderResult + }) + const rootRoute = new BaseRootRoute({}) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + ssr: 'data-only', + pendingComponent: () => null, + pendingMs: 0, + pendingMinMs: 0, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + ssr: false, + loader: childLoader, + }) + router = createTestRouter({ + routeTree: rootRoute.addChildren([parentRoute.addChildren([childRoute])]), + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + isServer: false, + }) + + await hydrate(router) + const load = router.load() + + await vi.waitFor(() => { + expect(childLoader).toHaveBeenCalledOnce() + expect(router.state.status).toBe('pending') + }) + expect(continuationAssetEnd).toBe(3) + expect( + _getAssetMatches(router.state.matches).map((match) => match.routeId), + ).toEqual([rootRoute.id, parentRoute.id, childRoute.id]) + expect(router.state.matches[1]?._assetEnd).toBe(3) + + childLoaderResult.resolve('client child data') + await load + + expect(router.state.matches[1]?._assetEnd).toBeUndefined() + expect(router.state.matches[2]).toMatchObject({ + status: 'success', + loaderData: 'client child data', + }) + }) + + test('settles when navigation supersedes a pending custom hydrate hook', async () => { + const hydrateGate = createControlledPromise() + const hydrateStarted = createControlledPromise() + const rootRoute = new BaseRootRoute({}) + const oldRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/old', + }) + const newRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/new', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([oldRoute, newRoute]), + history: createMemoryHistory({ initialEntries: ['/old'] }), + isServer: false, + }) + router.options.hydrate = () => { + hydrateStarted.resolve() + return hydrateGate + } + const oldMatches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload( + mockWindow, + oldMatches.map((match) => ({ + i: dehydrateSsrMatchId(match.id), + s: 'success' as const, + ssr: true, + u: Date.now(), + })), + ) + + const hydration = hydrate(router) + onTestFinished(async () => { + hydrateGate.resolve() + await hydration + }) + + await hydrateStarted + await router.navigate({ to: '/new' }) + await expect(hydration).resolves.toBeUndefined() + + expect(router.state.resolvedLocation?.pathname).toBe('/new') + expect(router.state.matches.at(-1)?.routeId).toBe(newRoute.id) + }) + + test('does not publish an old hydration lane after a newer navigation commits', async () => { + const oldChunkGate = createControlledPromise() + const oldChunkStarted = createControlledPromise() + const OldComponent = Object.assign(() => null, { + preload: () => { + oldChunkStarted.resolve() + return oldChunkGate + }, + }) + const rootRoute = new BaseRootRoute({}) + const oldRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/old', + component: OldComponent, + }) + const newRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/new', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([oldRoute, newRoute]), + history: createMemoryHistory({ initialEntries: ['/old'] }), + isServer: false, + }) + const oldMatches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload( + mockWindow, + oldMatches.map((match) => ({ + i: dehydrateSsrMatchId(match.id), + s: 'success' as const, + ssr: true, + u: Date.now(), + })), + ) + + const hydration = hydrate(router) + onTestFinished(async () => { + oldChunkGate.resolve() + await hydration + }) + + await oldChunkStarted + await router.navigate({ to: '/new' }) + + expect(router.state.resolvedLocation?.pathname).toBe('/new') + expect(router.state.matches.at(-1)?.routeId).toBe(newRoute.id) + + await hydration + + expect(router.state.location.pathname).toBe('/new') + expect(router.state.resolvedLocation?.pathname).toBe('/new') + expect(router.state.matches.at(-1)?.routeId).toBe(newRoute.id) + }) + + test('an aborted hydration handoff falls back to a fresh client continuation', async () => { + let hydrationController: AbortController | undefined + const childLoader = vi.fn(() => 'client data') + const rootRoute = new BaseRootRoute({ + context: ({ abortController }: { abortController: AbortController }) => { + hydrationController ??= abortController + }, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/child', + ssr: false, + loader: childLoader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([childRoute]), + history: createMemoryHistory({ initialEntries: ['/child'] }), + isServer: false, + }) + const matches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload(mockWindow, [ + { + i: dehydrateSsrMatchId(matches[0]!.id), + s: 'success', + ssr: true, + u: Date.now(), + }, + { + i: dehydrateSsrMatchId(matches[1]!.id), + s: 'pending', + ssr: false, + u: Date.now(), + }, + ]) + + await hydrate(router) + expect(hydrationController).toBeDefined() + + const unsubscribe = router.subscribe('onBeforeLoad', () => { + hydrationController!.abort() + }) + onTestFinished(unsubscribe) + + await router.load() + + expect(router.state.resolvedLocation?.pathname).toBe('/child') + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: childRoute.id, + status: 'success', + loaderData: 'client data', + }) + expect(childLoader).toHaveBeenCalledTimes(1) + }) + + test('failed HMR restores a partial hydration presentation and handoff', async () => { + let hydrationController: AbortController | undefined + const childLoader = vi.fn(() => 'client child data') + const rootRoute = new BaseRootRoute({ + context: ({ abortController }: { abortController: AbortController }) => { + hydrationController ??= abortController + }, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/child', + ssr: false, + loader: childLoader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([childRoute]), + history: createMemoryHistory({ initialEntries: ['/child'] }), + isServer: false, + }) + const matches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload(mockWindow, [ + { + i: dehydrateSsrMatchId(matches[0]!.id), + s: 'success', + ssr: true, + u: Date.now(), + }, + ]) + + await hydrate(router) + const handoff = router._handoff + const startTransition = router.startTransition + router.startTransition = async (fn) => { + fn() + throw new Error('HMR render failed') + } + + await router._refreshRoute!() + + expect(router.state.matches.map((match) => match.routeId)).toEqual([ + rootRoute.id, + childRoute.id, + ]) + expect(router.state.matches[1]).toMatchObject({ + status: 'pending', + ssr: false, + }) + expect(router._handoff).toBe(handoff) + expect(hydrationController?.signal.aborted).toBe(false) + + router.startTransition = startTransition + await router.load() + expect(router.state.matches[1]).toMatchObject({ + status: 'success', + loaderData: 'client child data', + }) + expect(childLoader).toHaveBeenCalledTimes(2) + }) + + test('a synchronous context navigation prevents stale hydration assets from running', async () => { + const oldHead = vi.fn(() => ({ meta: [{ title: 'Old route' }] })) + const oldScripts = vi.fn(() => [ + { children: 'window.oldHydrationLaneRan = true' }, + ]) + const newHead = vi.fn(() => ({ meta: [{ title: 'New route' }] })) + + let navigation: Promise | undefined + let navigateDuringHydration = false + const navigationStarted = vi.fn() + const oldContext = vi.fn(({ navigate }: { navigate: NavigateFn }) => { + if (navigateDuringHydration) { + navigateDuringHydration = false + navigationStarted() + navigation = navigate({ to: '/new' }) + } + return { source: 'old' } + }) + const rootRoute = new BaseRootRoute({}) + const oldRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/old', + context: oldContext, + head: oldHead, + scripts: oldScripts, + }) + const newRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/new', + head: newHead, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([oldRoute, newRoute]), + history: createMemoryHistory({ initialEntries: ['/old'] }), + isServer: false, + }) + const oldMatches = router.matchRoutes(router.stores.location.get()) + + installHydrationPayload( + mockWindow, + oldMatches.map((match) => ({ + i: dehydrateSsrMatchId(match.id), + s: 'success' as const, + ssr: true, + u: Date.now(), + })), + ) + + navigateDuringHydration = true + await hydrate(router) + expect(navigationStarted).toHaveBeenCalled() + expect(navigation).toBeDefined() + await navigation + + expect(router.state.location.pathname).toBe('/new') + expect(router.state.resolvedLocation?.pathname).toBe('/new') + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: newRoute.id, + status: 'success', + meta: [{ title: 'New route' }], + }) + expect(oldContext).toHaveBeenCalled() + expect(oldHead).not.toHaveBeenCalled() + expect(oldScripts).not.toHaveBeenCalled() + expect(newHead).toHaveBeenCalled() + }) + + test('an adopted data-only error does not run its server-omitted descendants', async () => { + const serverError = new Error('server failed') + const serverBeforeLoad = vi.fn(() => { + throw serverError + }) + const serverRootRoute = new BaseRootRoute({}) + const serverParentRoute = new BaseRoute({ + getParentRoute: () => serverRootRoute, + path: '/parent', + ssr: 'data-only', + beforeLoad: serverBeforeLoad, + }) + const serverChildLoader = vi.fn() + const serverChildRoute = new BaseRoute({ + getParentRoute: () => serverParentRoute, + path: '/child', + loader: serverChildLoader, + }) + const serverRouter = createTestRouter({ + routeTree: serverRootRoute.addChildren([ + serverParentRoute.addChildren([serverChildRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + isServer: true, + }) + + const bootstrap = await dehydrateToBootstrap(serverRouter) + + expect(serverBeforeLoad).toHaveBeenCalledTimes(1) + expect(serverChildLoader).not.toHaveBeenCalled() + expect(serverRouter.state.matches.map((match) => match.routeId)).toEqual([ + serverRootRoute.id, + serverParentRoute.id, + serverChildRoute.id, + ]) + expect(serverRouter.state.matches[1]).toMatchObject({ + routeId: serverParentRoute.id, + status: 'error', + ssr: 'data-only', + error: serverError, + }) + const transportedError = bootstrap.router?.matches.at(-1)?.e + expect(transportedError).not.toBe(serverError) + expect(transportedError).toMatchObject({ + name: serverError.name, + message: serverError.message, + }) + + const parentBeforeLoad = vi.fn() + const childBeforeLoad = vi.fn() + const childLoader = vi.fn() + const rootRoute = new BaseRootRoute({}) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + ssr: 'data-only', + beforeLoad: parentBeforeLoad, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + beforeLoad: childBeforeLoad, + loader: childLoader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([parentRoute.addChildren([childRoute])]), + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + isServer: false, + }) + mockWindow.$_TSR = bootstrap + + await hydrate(router) + + expect(router.state.matches.map((match) => match.routeId)).toEqual([ + rootRoute.id, + parentRoute.id, + childRoute.id, + ]) + expect( + router.state.matches.find((match) => match.routeId === parentRoute.id), + ).toMatchObject({ + status: 'pending', + error: transportedError, + }) + expect(parentBeforeLoad).not.toHaveBeenCalled() + expect(childBeforeLoad).not.toHaveBeenCalled() + expect(childLoader).not.toHaveBeenCalled() + expect( + _getAssetMatches(router.state.matches).map((match) => match.routeId), + ).toEqual([rootRoute.id, parentRoute.id]) + + await router.load() + + expect(router.state.location.pathname).toBe('/parent/child') + expect(router.state.resolvedLocation?.pathname).toBe('/parent/child') + expect(router.state.matches.map((match) => match.routeId)).toEqual([ + rootRoute.id, + parentRoute.id, + childRoute.id, + ]) + expect(router.state.matches[1]).toMatchObject({ + status: 'error', + error: transportedError, + }) + expect(router.state.matches[1]?.error).toMatchObject({ + name: serverError.name, + message: serverError.message, + }) + expect(parentBeforeLoad).not.toHaveBeenCalled() + expect(childBeforeLoad).not.toHaveBeenCalled() + expect(childLoader).not.toHaveBeenCalled() + }) +}) diff --git a/packages/router-core/tests/hydration-terminal-error-child-head.test.ts b/packages/router-core/tests/hydration-terminal-error-child-head.test.ts new file mode 100644 index 0000000000..0e607f7a1c --- /dev/null +++ b/packages/router-core/tests/hydration-terminal-error-child-head.test.ts @@ -0,0 +1,175 @@ +import { runInNewContext } from 'node:vm' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute } from '../src' +import { hydrate } from '../src/ssr/client' +import { attachRouterServerSsrUtils } from '../src/ssr/ssr-server' +import { createTestRouter } from './routerTestUtils' +import type { AnyRouter } from '../src' +import type { TsrSsrGlobal } from '../src/ssr/types' +import type { ServerManifest } from '../src/manifest' + +const testManifest: ServerManifest = { routes: {} } + +async function dehydrateToBootstrap(router: AnyRouter): Promise { + attachRouterServerSsrUtils({ router, manifest: testManifest }) + try { + await router.load() + await router.serverSsr!.dehydrate() + + const script = router.serverSsr!.takeBufferedScripts() + expect(script?.children).toBeTruthy() + + const context: Record = { + document: { currentScript: { remove() {} } }, + } + context.self = context + runInNewContext(script!.children!, context) + + expect(context.$_TSR).toBeDefined() + return context.$_TSR + } finally { + router.serverSsr?.cleanup() + } +} + +// This terminal-prefix case is an internal hydration protocol invariant, not +// an issue #7635 reproduction. The user-visible same-route navigation +// regression is covered in react-router, where the rendered boundary and +// document title can be observed directly. +describe('hydrated terminal error prefix', () => { + let mockWindow: { $_TSR?: TsrSsrGlobal } + + beforeEach(() => { + mockWindow = {} + vi.stubGlobal('window', mockWindow) + }) + + afterEach(() => { + vi.restoreAllMocks() + vi.unstubAllGlobals() + }) + + it('adopts the terminal prefix without running omitted child work', async () => { + const serverError = new Error('App beforeLoad failed') + const serverBeforeLoad = vi.fn(() => { + throw serverError + }) + const serverChildLoader = vi.fn(() => ({ child: 'server data' })) + const serverRootRoute = new BaseRootRoute({}) + const serverAppRoute = new BaseRoute({ + getParentRoute: () => serverRootRoute, + path: '/app', + beforeLoad: serverBeforeLoad, + errorComponent: () => 'App error', + }) + const serverChildRoute = new BaseRoute({ + getParentRoute: () => serverAppRoute, + path: '/child', + loader: serverChildLoader, + }) + const serverRouter = createTestRouter({ + routeTree: serverRootRoute.addChildren([ + serverAppRoute.addChildren([serverChildRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/app/child'] }), + isServer: true, + }) + + const bootstrap = await dehydrateToBootstrap(serverRouter) + + expect(serverBeforeLoad).toHaveBeenCalledTimes(1) + expect(serverChildLoader).not.toHaveBeenCalled() + expect(serverRouter.state.matches.map((match) => match.routeId)).toEqual([ + serverRootRoute.id, + serverAppRoute.id, + serverChildRoute.id, + ]) + expect(serverRouter.state.matches[1]).toMatchObject({ + routeId: serverAppRoute.id, + status: 'error', + error: serverError, + }) + const transportedError = bootstrap.router?.matches.at(-1)?.e + expect(transportedError).not.toBe(serverError) + expect(transportedError).toMatchObject({ + name: serverError.name, + message: serverError.message, + }) + + const appHead = vi.fn(({ match }: any) => ({ + meta: [{ title: match.error ? 'App error title' : 'App success title' }], + })) + const childHead = vi.fn(() => ({ + meta: [{ title: 'Child success title' }], + })) + const childLoader = vi.fn(() => ({ child: 'data' })) + + const rootRoute = new BaseRootRoute({}) + const appRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/app', + head: appHead, + errorComponent: () => 'App error', + }) + const childRoute = new BaseRoute({ + getParentRoute: () => appRoute, + path: '/child', + loader: childLoader, + head: childHead, + }) + + const router = createTestRouter({ + routeTree: rootRoute.addChildren([appRoute.addChildren([childRoute])]), + history: createMemoryHistory({ initialEntries: ['/app/child'] }), + isServer: false, + }) + + // Route matching confirms that the location has a child suffix which the + // terminal server payload omits. + const matches = router.matchRoutes(router.stores.location.get()) + expect(matches.map((match) => match.routeId)).toEqual([ + rootRoute.id, + appRoute.id, + childRoute.id, + ]) + mockWindow.$_TSR = bootstrap + + await hydrate(router) + + // Hydration reconstructs the full matched branch while adopting only the + // terminal prefix represented by the payload. + expect(router.state.matches.map((match) => match.routeId)).toEqual([ + rootRoute.id, + appRoute.id, + childRoute.id, + ]) + expect(router.state.matches[2]?.status).toBe('pending') + + const committedApp = router.state.matches.find( + (match) => match.routeId === appRoute.id, + ) + expect(router.state.location.pathname).toBe('/app/child') + expect(router.state.isLoading).toBe(false) + expect(committedApp?.status).toBe('error') + expect(committedApp?.error).toBe(transportedError) + + // No callback for the omitted suffix may run while adopting the prefix. + expect(childLoader).not.toHaveBeenCalled() + expect(childHead).not.toHaveBeenCalled() + + // Core projects the error match's metadata; document ownership is covered + // by the framework-level test. + expect(appHead).toHaveBeenCalledOnce() + expect(appHead).toHaveBeenCalledWith( + expect.objectContaining({ + match: expect.objectContaining({ + routeId: appRoute.id, + status: 'error', + error: transportedError, + }), + }), + ) + expect(committedApp?.meta).toEqual([{ title: 'App error title' }]) + }) +}) diff --git a/packages/router-core/tests/invalidate-pre-rematch-failure.test.ts b/packages/router-core/tests/invalidate-pre-rematch-failure.test.ts new file mode 100644 index 0000000000..084fbe8d31 --- /dev/null +++ b/packages/router-core/tests/invalidate-pre-rematch-failure.test.ts @@ -0,0 +1,63 @@ +import { expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute } from '../src' +import { createTestRouter } from './routerTestUtils' + +/** + * A planning failure is a route error generation and leaves the router usable + * for a later retry. + */ +test('a fatal pre-rematch failure publishes an error and permits retry', async () => { + const boom = new Error('loaderDeps failed during invalidation rematch') + let failLoaderDeps = false + const loaderSignals: Array = [] + + const rootRoute = new BaseRootRoute({}) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + loaderDeps: () => { + if (failLoaderDeps) { + throw boom + } + return {} + }, + loader: ({ abortController }) => { + loaderSignals.push(abortController.signal) + return 'target data' + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([targetRoute]), + history: createMemoryHistory({ initialEntries: ['/target'] }), + }) + + await router.load() + const activeSignal = loaderSignals[0] + expect(router.state.matches.at(-1)?.status).toBe('success') + + failLoaderDeps = true + await router.invalidate({ forcePending: true }) + + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: targetRoute.id, + status: 'error', + error: boom, + }) + expect(loaderSignals).toHaveLength(1) + expect(activeSignal?.aborted).toBe(true) + + failLoaderDeps = false + await router.invalidate() + const retrySignal = loaderSignals[1] + + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: targetRoute.id, + status: 'success', + loaderData: 'target data', + }) + expect(loaderSignals).toHaveLength(2) + expect(retrySignal).not.toBe(activeSignal) + expect(activeSignal?.aborted).toBe(true) + expect(retrySignal?.aborted).toBe(false) +}) diff --git a/packages/router-core/tests/issue-3179-preload-cached-cause.test.ts b/packages/router-core/tests/issue-3179-preload-cached-cause.test.ts new file mode 100644 index 0000000000..c465961dc2 --- /dev/null +++ b/packages/router-core/tests/issue-3179-preload-cached-cause.test.ts @@ -0,0 +1,59 @@ +import { expect, test } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute } from '../src' +import { createTestRouter } from './routerTestUtils' + +// https://github.com/TanStack/router/issues/3179 +test('#3179: preloading the active route does not replay its load callbacks', async () => { + const calls: Array<{ + phase: 'beforeLoad' | 'loader' + cause: string + preload: boolean + }> = [] + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + staleTime: 0, + preloadStaleTime: 0, + beforeLoad: ({ cause, preload }) => { + calls.push({ phase: 'beforeLoad', cause, preload }) + }, + loader: ({ cause, preload }) => { + calls.push({ phase: 'loader', cause, preload }) + }, + }) + const aboutRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/about', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, aboutRoute]), + history: createMemoryHistory({ initialEntries: ['/about'] }), + }) + + await router.load() + + // First hover Home from About, then click it. This is the sequence from + // the issue that used to cache the navigation's flags on the match. + await router.preloadRoute({ to: '/' }) + expect(calls).toEqual([ + { phase: 'beforeLoad', cause: 'preload', preload: true }, + { phase: 'loader', cause: 'preload', preload: true }, + ]) + + calls.length = 0 + await router.navigate({ to: '/' }) + expect(calls).toEqual([ + { phase: 'beforeLoad', cause: 'enter', preload: false }, + { phase: 'loader', cause: 'enter', preload: false }, + ]) + + // beforeLoad is contextualization, not loader cache data, so a later preload + // reruns it with fresh flags while retaining the accepted loader generation. + calls.length = 0 + await router.preloadRoute({ to: '/' }) + expect(calls).toEqual([ + { phase: 'beforeLoad', cause: 'preload', preload: true }, + ]) +}) diff --git a/packages/router-core/tests/issue-3920-on-before-navigate-after-initial-redirect.test.ts b/packages/router-core/tests/issue-3920-on-before-navigate-after-initial-redirect.test.ts new file mode 100644 index 0000000000..f0de5df8dd --- /dev/null +++ b/packages/router-core/tests/issue-3920-on-before-navigate-after-initial-redirect.test.ts @@ -0,0 +1,40 @@ +import { createMemoryHistory } from '@tanstack/history' +import { expect, test } from 'vitest' +import { BaseRootRoute, BaseRoute, redirect } from '../src' +import { createTestRouter } from './routerTestUtils' + +// https://github.com/TanStack/router/issues/3920 +test('onBeforeNavigate fires after the initial load redirects', async () => { + const rootRoute = new BaseRootRoute({}) + const sourceRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/source', + beforeLoad: () => { + throw redirect({ to: '/target' }) + }, + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + }) + const thirdRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/third', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([sourceRoute, targetRoute, thirdRoute]), + history: createMemoryHistory({ initialEntries: ['/source'] }), + }) + + await router.load() + expect(router.state.location.pathname).toBe('/target') + + const navigatedPaths: Array = [] + router.subscribe('onBeforeNavigate', (event) => { + navigatedPaths.push(event.toLocation.pathname) + }) + + await router.navigate({ to: '/third' }) + + expect(navigatedPaths).toEqual(['/third']) +}) diff --git a/packages/router-core/tests/issue-3928-rapid-reload-abort.test.ts b/packages/router-core/tests/issue-3928-rapid-reload-abort.test.ts new file mode 100644 index 0000000000..a16258be0b --- /dev/null +++ b/packages/router-core/tests/issue-3928-rapid-reload-abort.test.ts @@ -0,0 +1,138 @@ +import { describe, expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute } from '../src' +import { createTestRouter } from './routerTestUtils' + +type LoaderInvocation = { + resolve: (value: string) => void + signal: AbortSignal +} + +const createAbortableInvocation = ( + abortController: AbortController, + onAbort?: () => void, +) => { + let resolve!: (value: string) => void + const promise = new Promise((promiseResolve, reject) => { + const handleAbort = () => { + onAbort?.() + reject(new DOMException('signal is aborted without reason', 'AbortError')) + } + resolve = (value) => { + abortController.signal.removeEventListener('abort', handleAbort) + promiseResolve(value) + } + if (abortController.signal.aborted) { + handleAbort() + } else { + abortController.signal.addEventListener('abort', handleAbort, { + once: true, + }) + } + }) + return { + invocation: { resolve, signal: abortController.signal }, + promise, + } +} + +// https://github.com/TanStack/router/issues/3928 +describe('issue #3928: rapid reloads of a reused parent', () => { + test('one parent flight can serve rapid successors without clearing the latest child load', async () => { + const indexAbort = vi.fn() + const rootInvocations = new Map() + const indexInvocations = new Map() + + const rootRoute = new BaseRootRoute({ + shouldReload: true, + // Search is deliberately not part of this loader's key. Its active + // same-ID flight may serve successors while the child keys each filter. + loader: ({ abortController, location }) => { + const { invocation, promise } = + createAbortableInvocation(abortController) + const search = location.search as Record + const filter = typeof search.filter === 'string' ? search.filter : '' + rootInvocations.set(filter, invocation) + return promise + }, + }) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + validateSearch: (search: Record) => ({ + filter: typeof search.filter === 'string' ? search.filter : '', + }), + loaderDeps: ({ search }: { search: { filter: string } }) => ({ + filter: search.filter, + }), + loader: ({ + deps, + abortController, + }: { + deps: { filter: string } + abortController: AbortController + }) => { + const { invocation, promise } = createAbortableInvocation( + abortController, + indexAbort, + ) + indexInvocations.set(deps.filter, invocation) + return promise + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + const initialLoad = router.load() + await vi.waitFor(() => { + expect(rootInvocations.has('')).toBe(true) + expect(indexInvocations.has('')).toBe(true) + }) + rootInvocations.get('')!.resolve('root:initial') + indexInvocations.get('')!.resolve('') + await initialLoad + + const navA = router.navigate({ to: '/', search: { filter: 'a' } }) + await vi.waitFor(() => { + expect(rootInvocations.has('a')).toBe(true) + expect(indexInvocations.has('a')).toBe(true) + }) + + const navAB = router.navigate({ to: '/', search: { filter: 'ab' } }) + await vi.waitFor(() => { + expect(indexInvocations.has('ab')).toBe(true) + expect(indexInvocations.get('a')!.signal.aborted).toBe(true) + expect(rootInvocations.get('a')!.signal.aborted).toBe(false) + }) + + const navABC = router.navigate({ to: '/', search: { filter: 'abc' } }) + await vi.waitFor(() => { + expect(indexInvocations.has('abc')).toBe(true) + expect(indexInvocations.get('ab')!.signal.aborted).toBe(true) + expect(rootInvocations.get('a')!.signal.aborted).toBe(false) + }) + + rootInvocations.get('a')!.resolve('root:a') + indexInvocations.get('abc')!.resolve('abc') + await Promise.all([navA, navAB, navABC]) + + expect(indexAbort).toHaveBeenCalledTimes(2) + expect(rootInvocations.size).toBe(2) + expect(rootInvocations.get('a')!.signal.aborted).toBe(false) + expect(indexInvocations.get('abc')!.signal.aborted).toBe(false) + expect(router.state.location.search).toEqual({ filter: 'abc' }) + expect(router.state.matches[0]).toMatchObject({ + status: 'success', + loaderData: 'root:a', + }) + expect(router.state.matches[1]).toMatchObject({ + status: 'success', + loaderData: 'abc', + }) + expect(router.state.matches.some((match) => match.status === 'error')).toBe( + false, + ) + }) +}) diff --git a/packages/router-core/tests/issue-4078-loader-notfound-root-boundary.test.ts b/packages/router-core/tests/issue-4078-loader-notfound-root-boundary.test.ts index 45085a8e1f..c33521597b 100644 --- a/packages/router-core/tests/issue-4078-loader-notfound-root-boundary.test.ts +++ b/packages/router-core/tests/issue-4078-loader-notfound-root-boundary.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from 'vitest' +import { describe, expect, it, onTestFinished } from 'vitest' import { createMemoryHistory } from '@tanstack/history' import { BaseRootRoute, @@ -18,7 +18,7 @@ import { createTestRouter } from './routerTestUtils' // root component + notFoundComponent, but a loader-thrown notFound did not. // // At Router Core's boundary, root attribution is represented by the root match -// with globalNotFound. These assertions do not distinguish the +// with _notFound. These assertions do not distinguish the // configured root notFoundComponent from the router default; that reported // rendering behavior requires framework-level coverage. describe('#4078 / #2255 existing Core root-boundary attribution', () => { @@ -63,7 +63,7 @@ describe('#4078 / #2255 existing Core root-boundary attribution', () => { return { routeId: rootMatch?.routeId, - globalNotFound: rootMatch?.globalNotFound, + _notFound: rootMatch?._notFound, } } @@ -72,19 +72,19 @@ describe('#4078 / #2255 existing Core root-boundary attribution', () => { await router.load() const navigation = router.navigate({ to: '/about' }) - await loaderStarted - try { - expect(loaderResponse.status).toBe('pending') - } finally { + onTestFinished(() => { loaderResponse.resolve() - } + }) + await loaderStarted + expect(loaderResponse.status).toBe('pending') + loaderResponse.resolve() await navigation expect(router.state.location.pathname).toBe('/about') expect(getRootBoundaryProjection(router)).toEqual({ routeId: rootRouteId, - globalNotFound: true, + _notFound: true, }) }) @@ -94,7 +94,7 @@ describe('#4078 / #2255 existing Core root-boundary attribution', () => { const unmatchedProjection = getRootBoundaryProjection(unmatched.router) expect(unmatchedProjection).toEqual({ routeId: rootRouteId, - globalNotFound: true, + _notFound: true, }) const loaderNotFound = setup(['/']) diff --git a/packages/router-core/tests/issue-4444-param-parse-error-lazy-child.test.ts b/packages/router-core/tests/issue-4444-param-parse-error-lazy-child.test.ts new file mode 100644 index 0000000000..9a996f9e4a --- /dev/null +++ b/packages/router-core/tests/issue-4444-param-parse-error-lazy-child.test.ts @@ -0,0 +1,74 @@ +import { describe, expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute, PathParamError } from '../src' +import { createTestRouter } from './routerTestUtils' + +/** + * Issue #4444: when a route's params.parse throws and one of its children is + * lazily loaded, the router used to stay "pending" forever: the lazy child's + * chunk was never requested, its match never settled, and framework + * transitioners could never reach idle. + * + * Desired behavior: the param parse error is a serial failure — the failing + * route commits status 'error' for its error boundary, descendants below the + * boundary are trimmed out of the lane with their load promises settled, and + * router.load() resolves with no match left pending. + */ +describe('issue #4444: param parse error on a route with a lazy child', () => { + test('load settles, commits the error boundary, and leaves no match stuck pending', async () => { + const rootRoute = new BaseRootRoute({}) + const langRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/lang/$lang', + params: { + parse: ({ lang }: { lang: string }) => { + if (lang !== 'en') { + throw new Error(`Unsupported language: ${lang}`) + } + return { lang } + }, + stringify: ({ lang }: { lang: string }) => ({ lang }), + }, + errorComponent: () => null, + }) + const lazyChildRoute = new BaseRoute({ + getParentRoute: () => langRoute, + path: '/lazy', + }) + const lazyFn = vi.fn(() => + Promise.resolve({ + options: { + id: lazyChildRoute.id, + component: () => null, + }, + }), + ) + lazyChildRoute.lazy(lazyFn) + + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + langRoute.addChildren([lazyChildRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/lang/es/lazy'] }), + }) + + // The reported bug: this load never reached a settled render state. + await router.load() + expect(router.state.status).toBe('idle') + + // The failing route commits the parse error for its error boundary… + const langMatch = router.state.matches.find( + (match) => match.routeId === langRoute.id, + ) + expect(langMatch?.status).toBe('error') + expect(langMatch?.error).toBeInstanceOf(PathParamError) + + // …while the structurally matched lazy child remains unresolved and hidden + // below that boundary without starting its chunk. + expect( + router.state.matches.find((match) => match.routeId === lazyChildRoute.id), + ).toMatchObject({ status: 'pending', isFetching: false }) + expect(lazyFn).not.toHaveBeenCalled() + expect(router.state.isLoading).toBe(false) + }) +}) diff --git a/packages/router-core/tests/issue-4572-preload-root-beforeload-flags.test.ts b/packages/router-core/tests/issue-4572-preload-root-beforeload-flags.test.ts new file mode 100644 index 0000000000..23495e5001 --- /dev/null +++ b/packages/router-core/tests/issue-4572-preload-root-beforeload-flags.test.ts @@ -0,0 +1,173 @@ +import { expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute } from '../src' +import { createTestRouter } from './routerTestUtils' + +/** + * Issue #4572: an intent preload used to re-trigger active beforeLoad + * callbacks with stale navigation flags. Direct preloadRoute calls provide + * the core-level reduction of the reported Link hover behavior. + * + * A completed beforeLoad result is never cached. Every new preload therefore + * contextualizes its full route lane with fresh preload flags. + */ + +function createPreloadFixture() { + const rootBeforeLoad = vi.fn() + const indexBeforeLoad = vi.fn() + const aboutBeforeLoad = vi.fn() + const bagBeforeLoad = vi.fn() + const nestedBeforeLoad = vi.fn() + + const rootRoute = new BaseRootRoute({ beforeLoad: rootBeforeLoad }) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + beforeLoad: indexBeforeLoad, + }) + const aboutRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/about', + beforeLoad: aboutBeforeLoad, + }) + const bagRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '_bag', + beforeLoad: bagBeforeLoad, + }) + const nestedRoute = new BaseRoute({ + getParentRoute: () => bagRoute, + path: '/nested', + beforeLoad: nestedBeforeLoad, + }) + + return { + rootBeforeLoad, + indexBeforeLoad, + aboutBeforeLoad, + bagBeforeLoad, + nestedBeforeLoad, + nestedRoute, + router: createTestRouter({ + routeTree: rootRoute.addChildren([ + indexRoute, + aboutRoute, + bagRoute.addChildren([nestedRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }), + } +} + +test('#4572: sibling preload contextualizes the full target lane with preload flags', async () => { + const { + rootBeforeLoad, + indexBeforeLoad, + aboutBeforeLoad, + bagBeforeLoad, + nestedBeforeLoad, + router, + } = createPreloadFixture() + + await router.load() + expect(rootBeforeLoad).toHaveBeenCalledTimes(1) + expect(indexBeforeLoad).toHaveBeenCalledTimes(1) + expect(rootBeforeLoad).toHaveBeenCalledWith( + expect.objectContaining({ cause: 'enter', preload: false }), + ) + + await router.preloadRoute({ to: '/about' }) + expect(rootBeforeLoad).toHaveBeenCalledTimes(2) + expect(indexBeforeLoad).toHaveBeenCalledTimes(1) + expect(aboutBeforeLoad).toHaveBeenCalledTimes(1) + expect(aboutBeforeLoad).toHaveBeenCalledWith( + expect.objectContaining({ cause: 'preload', preload: true }), + ) + expect(rootBeforeLoad).toHaveBeenLastCalledWith( + expect.objectContaining({ cause: 'preload', preload: true }), + ) + expect(bagBeforeLoad).not.toHaveBeenCalled() + expect(nestedBeforeLoad).not.toHaveBeenCalled() +}) + +test('#4572: active-route preload reruns beforeLoad with preload flags', async () => { + const { + rootBeforeLoad, + indexBeforeLoad, + aboutBeforeLoad, + bagBeforeLoad, + nestedBeforeLoad, + router, + } = createPreloadFixture() + + await router.load() + expect(rootBeforeLoad).toHaveBeenCalledTimes(1) + expect(indexBeforeLoad).toHaveBeenCalledTimes(1) + expect(rootBeforeLoad).toHaveBeenCalledWith( + expect.objectContaining({ cause: 'enter', preload: false }), + ) + + rootBeforeLoad.mockClear() + indexBeforeLoad.mockClear() + await router.preloadRoute({ to: '/' }) + expect(rootBeforeLoad).toHaveBeenCalledOnce() + expect(indexBeforeLoad).toHaveBeenCalledOnce() + expect(rootBeforeLoad).toHaveBeenCalledWith( + expect.objectContaining({ cause: 'preload', preload: true }), + ) + expect(indexBeforeLoad).toHaveBeenCalledWith( + expect.objectContaining({ cause: 'preload', preload: true }), + ) + expect(aboutBeforeLoad).not.toHaveBeenCalled() + expect(bagBeforeLoad).not.toHaveBeenCalled() + expect(nestedBeforeLoad).not.toHaveBeenCalled() +}) + +test('#4572: active nested preload reruns its full pathless lane', async () => { + const { + rootBeforeLoad, + indexBeforeLoad, + aboutBeforeLoad, + bagBeforeLoad, + nestedBeforeLoad, + nestedRoute, + router, + } = createPreloadFixture() + + await router.load() + await router.navigate({ to: '/_bag/nested' }) + expect(rootBeforeLoad).toHaveBeenCalledTimes(2) + expect(rootBeforeLoad).toHaveBeenLastCalledWith( + expect.objectContaining({ cause: 'stay', preload: false }), + ) + expect(indexBeforeLoad).toHaveBeenCalledTimes(1) + expect(aboutBeforeLoad).not.toHaveBeenCalled() + expect(bagBeforeLoad).toHaveBeenCalledTimes(1) + expect(bagBeforeLoad).toHaveBeenCalledWith( + expect.objectContaining({ cause: 'enter', preload: false }), + ) + expect(nestedBeforeLoad).toHaveBeenCalledTimes(1) + expect(nestedBeforeLoad).toHaveBeenCalledWith( + expect.objectContaining({ cause: 'enter', preload: false }), + ) + expect( + router.state.matches.some((match) => match.routeId === nestedRoute.id), + ).toBe(true) + rootBeforeLoad.mockClear() + indexBeforeLoad.mockClear() + aboutBeforeLoad.mockClear() + bagBeforeLoad.mockClear() + nestedBeforeLoad.mockClear() + + await router.preloadRoute({ to: '/_bag/nested' }) + expect(rootBeforeLoad).toHaveBeenCalledOnce() + expect(indexBeforeLoad).not.toHaveBeenCalled() + expect(aboutBeforeLoad).not.toHaveBeenCalled() + expect(bagBeforeLoad).toHaveBeenCalledOnce() + expect(nestedBeforeLoad).toHaveBeenCalledOnce() + for (const beforeLoad of [rootBeforeLoad, bagBeforeLoad, nestedBeforeLoad]) { + expect(beforeLoad).toHaveBeenCalledWith( + expect.objectContaining({ cause: 'preload', preload: true }), + ) + } +}) diff --git a/packages/router-core/tests/issue-4684-head-on-beforeload-error.test.ts b/packages/router-core/tests/issue-4684-head-on-beforeload-error.test.ts index 05960c8290..6bf84e75ce 100644 --- a/packages/router-core/tests/issue-4684-head-on-beforeload-error.test.ts +++ b/packages/router-core/tests/issue-4684-head-on-beforeload-error.test.ts @@ -1,25 +1,23 @@ import { createMemoryHistory } from '@tanstack/history' -import { describe, expect, test, vi } from 'vitest' +import { describe, expect, test } from 'vitest' import { BaseRootRoute, BaseRoute } from '../src' -import { createTestRouter } from './routerTestUtils' +import { createTestRouter, loadServerResponse } from './routerTestUtils' +// Existing Core projection coverage related to: // https://github.com/TanStack/router/issues/4684 // // When beforeLoad throws, head() must still execute for the retained lane — // the root head owns global stylesheets/title that the errorComponent page // depends on, and the failing route's own head can provide error-specific -// head content. -describe('issue #4684: head executes when beforeLoad throws', () => { +// head content. Rendering those projections through HeadContent is a framework +// concern; this suite asserts the Core-owned match projections. +describe('#4684 existing Core behavior: head executes when beforeLoad throws', () => { const setup = (isServer: boolean) => { const beforeLoadError = new Error('beforeLoad failed') - const rootHead = vi.fn(() => ({ + const rootHead = () => ({ meta: [{ title: 'Root title' }], links: [{ rel: 'stylesheet', href: '/global.css' }], - })) - const failingHead = vi.fn(({ match }: any) => ({ - meta: [{ title: match.error ? 'Error title' : 'Success title' }], - })) - + }) const rootRoute = new BaseRootRoute({ head: rootHead }) const failingRoute = new BaseRoute({ getParentRoute: () => rootRoute, @@ -27,7 +25,9 @@ describe('issue #4684: head executes when beforeLoad throws', () => { beforeLoad: () => { throw beforeLoadError }, - head: failingHead, + head: ({ match }) => ({ + meta: [{ title: match.error ? 'Error title' : 'Success title' }], + }), errorComponent: () => 'error', }) @@ -37,18 +37,15 @@ describe('issue #4684: head executes when beforeLoad throws', () => { isServer, }) - return { router, rootRoute, failingRoute, rootHead, failingHead } + return { router, rootRoute, failingRoute, beforeLoadError } } test('server load projects root and failing-route heads for the error lane', async () => { - const { router, rootRoute, failingRoute, rootHead, failingHead } = - setup(true) + const { router, rootRoute, failingRoute, beforeLoadError } = setup(true) - await router.load() + const response = await loadServerResponse(router, '/fail') - expect(router.state.statusCode).toBe(500) - expect(rootHead).toHaveBeenCalledTimes(1) - expect(failingHead).toHaveBeenCalledTimes(1) + expect(response.status).toBe(500) const rootMatch = router.state.matches.find( (match) => match.routeId === rootRoute.id, @@ -61,18 +58,15 @@ describe('issue #4684: head executes when beforeLoad throws', () => { { rel: 'stylesheet', href: '/global.css' }, ]) expect(failingMatch?.status).toBe('error') + expect(failingMatch?.error).toBe(beforeLoadError) expect(failingMatch?.meta).toEqual([{ title: 'Error title' }]) }) test('client load projects root and failing-route heads for the error lane', async () => { - const { router, rootRoute, failingRoute, rootHead, failingHead } = - setup(false) + const { router, rootRoute, failingRoute, beforeLoadError } = setup(false) await router.load() - expect(rootHead).toHaveBeenCalledTimes(1) - expect(failingHead).toHaveBeenCalledTimes(1) - const rootMatch = router.state.matches.find( (match) => match.routeId === rootRoute.id, ) @@ -80,7 +74,11 @@ describe('issue #4684: head executes when beforeLoad throws', () => { (match) => match.routeId === failingRoute.id, ) expect(rootMatch?.meta).toEqual([{ title: 'Root title' }]) + expect(rootMatch?.links).toEqual([ + { rel: 'stylesheet', href: '/global.css' }, + ]) expect(failingMatch?.status).toBe('error') + expect(failingMatch?.error).toBe(beforeLoadError) expect(failingMatch?.meta).toEqual([{ title: 'Error title' }]) }) }) diff --git a/packages/router-core/tests/issue-5106-hydrated-notfound-boundary.test.ts b/packages/router-core/tests/issue-5106-hydrated-notfound-boundary.test.ts index b9e9a637d8..5ee450ee35 100644 --- a/packages/router-core/tests/issue-5106-hydrated-notfound-boundary.test.ts +++ b/packages/router-core/tests/issue-5106-hydrated-notfound-boundary.test.ts @@ -9,10 +9,11 @@ import type { Manifest } from '../src/manifest' const testManifest: Manifest = { routes: {} } -// Supplemental Core state coverage for +// The child-owned case is supplemental Core state coverage for // https://github.com/TanStack/router/issues/5106. The reported rendered React -// hydration symptom is covered by the React Start E2E test. -describe('hydrated child-owned notFound boundary coverage', () => { +// hydration symptom is not observable at this layer. The ancestor-owned case +// is a generic terminal-prefix invariant, not an issue #5106 reproduction. +describe('hydrated notFound boundary coverage', () => { let mockWindow: { $_TSR?: TsrSsrGlobal } beforeEach(() => { @@ -149,4 +150,125 @@ describe('hydrated child-owned notFound boundary coverage', () => { expect(postsLoader).not.toHaveBeenCalled() expect(postLoader).not.toHaveBeenCalled() }) + + it('generic terminal-prefix invariant: adopts an ancestor-owned boundary without presenting the omitted child', async () => { + const postsLoader = vi.fn(() => 'posts-data') + const postLoader = vi.fn(() => 'post-data') + const history = createMemoryHistory({ + initialEntries: ['/posts/i-do-not-exist'], + }) + + const rootRoute = new BaseRootRoute({}) + const postsRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/posts', + loader: postsLoader, + component: () => 'Posts', + notFoundComponent: () => 'Posts not found', + }) + // The child has no notFoundComponent, so the synthetic payload below + // represents the parent as the selected boundary and omits the child. + const postRoute = new BaseRoute({ + getParentRoute: () => postsRoute, + path: '/$postId', + loader: postLoader, + component: () => 'Post', + }) + const safeLoader = vi.fn(() => 'safe-data') + const safeRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/safe', + loader: safeLoader, + }) + + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + postsRoute.addChildren([postRoute]), + safeRoute, + ]), + history, + isServer: false, + }) + + const matches = router.matchRoutes(router.stores.location.get()) + expect(matches.map((match) => match.routeId)).toEqual([ + rootRoute.id, + postsRoute.id, + postRoute.id, + ]) + + mockWindow.$_TSR = { + router: { + manifest: testManifest, + dehydratedData: {}, + // The represented terminal lane is capped at the parent boundary. + matches: [ + { + i: dehydrateSsrMatchId(matches[0]!.id), + s: 'success' as const, + ssr: true, + u: Date.now(), + }, + { + i: dehydrateSsrMatchId(matches[1]!.id), + s: 'notFound' as const, + l: 'posts-data', + e: { isNotFound: true }, + ssr: true, + u: Date.now(), + }, + ], + }, + h: vi.fn(), + e: vi.fn(), + c: vi.fn(), + p: vi.fn(), + buffer: [], + initialized: false, + } + + await hydrate(router) + + // Hydration reconstructs the complete branch but executes only the + // represented terminal prefix. + const stateMatches = router.state.matches + expect(router.state.location.pathname).toBe('/posts/i-do-not-exist') + expect(router.state.isLoading).toBe(false) + expect(stateMatches.map((match) => match.routeId)).toEqual([ + rootRoute.id, + postsRoute.id, + postRoute.id, + ]) + expect(stateMatches[1]!.routeId).toBe(postsRoute.id) + expect(stateMatches[1]!.status).toBe('notFound') + expect(isNotFound(stateMatches[1]!.error)).toBe(true) + // The boundary keeps the parent loader data supplied by the payload. + expect(stateMatches[1]!.loaderData).toBe('posts-data') + expect(stateMatches[2]).toMatchObject({ + routeId: postRoute.id, + status: 'pending', + }) + + // Neither the omitted child nor the represented parent runs client-side. + expect(postLoader).not.toHaveBeenCalled() + expect(postsLoader).not.toHaveBeenCalled() + expect(safeLoader).not.toHaveBeenCalled() + + // A later client navigation remains usable after adopting the prefix. + await router.navigate({ to: '/safe' }) + expect(router.state.location.pathname).toBe('/safe') + expect(router.state.isLoading).toBe(false) + expect(router.state.matches.map((match) => match.routeId)).toEqual([ + rootRoute.id, + safeRoute.id, + ]) + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: safeRoute.id, + status: 'success', + loaderData: 'safe-data', + }) + expect(safeLoader).toHaveBeenCalledTimes(1) + expect(postLoader).not.toHaveBeenCalled() + expect(postsLoader).not.toHaveBeenCalled() + }) }) diff --git a/packages/router-core/tests/issue-5427-hydration-root-global-not-found.test.ts b/packages/router-core/tests/issue-5427-hydration-root-global-not-found.test.ts new file mode 100644 index 0000000000..0a6119b349 --- /dev/null +++ b/packages/router-core/tests/issue-5427-hydration-root-global-not-found.test.ts @@ -0,0 +1,136 @@ +import { createMemoryHistory } from '@tanstack/history' +import { describe, expect, onTestFinished, test, vi } from 'vitest' +import { BaseRootRoute, BaseRoute } from '../src' +import { hydrate } from '../src/ssr/client' +import { dehydrateMatch } from '../src/ssr/ssr-server' +import { createTestRouter } from './routerTestUtils' +import type { TsrSsrGlobal } from '../src/ssr/types' + +function createRouter(initialEntry: string, isServer: boolean) { + const rootRoute = new BaseRootRoute({ + notFoundComponent: () => 'Root not found', + }) + const notFoundRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/404', + component: () => 'Not found route', + }) + + const router = createTestRouter({ + routeTree: rootRoute.addChildren([notFoundRoute]), + history: createMemoryHistory({ initialEntries: [initialEntry] }), + isServer, + }) + + return { rootRoute, router } +} + +function createLayoutRouter(initialEntry: string, isServer: boolean) { + const rootRoute = new BaseRootRoute({}) + const layoutRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + id: '_layout', + notFoundComponent: () => 'Layout not found', + }) + const notFoundRoute = new BaseRoute({ + getParentRoute: () => layoutRoute, + path: '/404', + }) + const notFoundIndexRoute = new BaseRoute({ + getParentRoute: () => notFoundRoute, + path: '/', + }) + const agentsRoute = new BaseRoute({ + getParentRoute: () => layoutRoute, + path: '/agents', + }) + const skillAgentRoute = new BaseRoute({ + getParentRoute: () => agentsRoute, + path: '/skill-agent', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + layoutRoute.addChildren([ + notFoundRoute.addChildren([notFoundIndexRoute]), + agentsRoute.addChildren([skillAgentRoute]), + ]), + ]), + history: createMemoryHistory({ initialEntries: [initialEntry] }), + isServer, + }) + + return { layoutRoute, router } +} + +describe('issue #5427: root-only global not-found hydration', () => { + test('resolves the client URL when the dehydrated /404 lane has a child match', async () => { + const { router: serverRouter } = createRouter('/404', true) + await serverRouter.load() + + const bootstrap: TsrSsrGlobal = { + router: { + manifest: { routes: {} }, + dehydratedData: {}, + matches: serverRouter.state.matches.map(dehydrateMatch), + }, + h: () => {}, + e: () => {}, + c: () => {}, + p: () => {}, + buffer: [], + } + const previousBootstrap = window.$_TSR + window.$_TSR = bootstrap + onTestFinished(() => { + window.$_TSR = previousBootstrap + }) + + const { rootRoute, router } = createRouter('/missing', false) + + await hydrate(router) + await vi.waitFor(() => { + expect(router.state.resolvedLocation?.pathname).toBe('/missing') + }) + + expect(router.state.matches).toHaveLength(1) + expect(router.state.matches[0]).toMatchObject({ + routeId: rootRoute.id, + _notFound: true, + }) + }) + + test('resolves a fuzzy client URL capped by an ancestor layout boundary', async () => { + const { router: serverRouter } = createLayoutRouter('/404', true) + await serverRouter.load() + expect(serverRouter.state.matches).toHaveLength(4) + + const bootstrap: TsrSsrGlobal = { + router: { + manifest: { routes: {} }, + dehydratedData: {}, + matches: serverRouter.state.matches.map(dehydrateMatch), + }, + h: () => {}, + e: () => {}, + c: () => {}, + p: () => {}, + buffer: [], + } + const previousBootstrap = window.$_TSR + window.$_TSR = bootstrap + onTestFinished(() => { + window.$_TSR = previousBootstrap + }) + + const { layoutRoute, router } = createLayoutRouter('/agents/missing', false) + + await hydrate(router) + + expect(router.state.resolvedLocation?.pathname).toBe('/agents/missing') + expect(router.state.matches).toHaveLength(3) + expect(router.state.matches[1]).toMatchObject({ + routeId: layoutRoute.id, + _notFound: true, + }) + }) +}) diff --git a/packages/router-core/tests/issue-6107-preload-chunk-failure.test.ts b/packages/router-core/tests/issue-6107-preload-chunk-failure.test.ts new file mode 100644 index 0000000000..32539b3676 --- /dev/null +++ b/packages/router-core/tests/issue-6107-preload-chunk-failure.test.ts @@ -0,0 +1,47 @@ +import { describe, expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute } from '../src' +import { createTestRouter } from './routerTestUtils' + +/** + * Issue #6107: going offline and hovering a link (preload intent) made the + * failed dynamic import surface as an unhandled rejection in the console, and + * the error never reached the route's error component. + * + * Desired behavior: + * - a preload whose lazy chunk rejects resolves non-fatally, and + * - the failed chunk is evicted, so the subsequent real navigation retries + * the import and commits the chunk error onto the match (status 'error'), + * which is what framework error components render. + */ +describe('issue #6107: failed dynamic import of a lazy route chunk', () => { + test('preloadRoute failure is non-fatal and navigation retries and commits the chunk error', async () => { + const chunkError = new TypeError( + 'Failed to fetch dynamically imported module: /assets/posts.lazy-abc123.js', + ) + const lazyRoute = vi.fn(() => Promise.reject(chunkError)) + const rootRoute = new BaseRootRoute({}) + const postsRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/posts', + }).lazy(lazyRoute) + + const router = createTestRouter({ + routeTree: rootRoute.addChildren([postsRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + await router.load() + + await router.preloadRoute({ to: '/posts' }) + expect(lazyRoute).toHaveBeenCalled() + const lazyCallsAfterPreload = lazyRoute.mock.calls.length + + await router.navigate({ to: '/posts' }) + const postsMatch = router.state.matches.find( + (match) => match.routeId === postsRoute.id, + ) + expect(lazyRoute.mock.calls.length).toBeGreaterThan(lazyCallsAfterPreload) + expect(postsMatch?.status).toBe('error') + expect(postsMatch?.error).toBe(chunkError) + }) +}) diff --git a/packages/router-core/tests/issue-6221-head-waits-for-loader.test.ts b/packages/router-core/tests/issue-6221-head-waits-for-loader.test.ts index 0024ca29a0..a9f56ba8d3 100644 --- a/packages/router-core/tests/issue-6221-head-waits-for-loader.test.ts +++ b/packages/router-core/tests/issue-6221-head-waits-for-loader.test.ts @@ -1,5 +1,5 @@ import { createMemoryHistory } from '@tanstack/history' -import { describe, expect, test } from 'vitest' +import { describe, expect, onTestFinished, test, vi } from 'vitest' import { BaseRootRoute, BaseRoute, @@ -9,6 +9,13 @@ import { import { createTestRouter } from './routerTestUtils' // https://github.com/TanStack/router/issues/6221 +// +// head() must never compute from loaderData older than the data the lane +// commits. Two reported shapes: +// 1. Revisiting a route whose previous visit ended in notFound — the head +// must see the fresh successful loaderData, not run before the loader. +// 2. Revisiting a cached success match that gets a stale reload — the head +// title must not lag one loaderData run behind. describe('issue #6221: head does not run before loader data is ready', () => { test('existing-behavior control: head sees fresh loaderData after a notFound visit', async () => { let authed = false @@ -88,4 +95,68 @@ describe('issue #6221: head does not run before loader data is ready', () => { unsubscribe?.() } }) + + test('head title does not lag one loaderData run behind on revisits', async () => { + const secondLoaderStarted = createControlledPromise() + const secondLoaderResponse = createControlledPromise<{ author: string }>() + let version = 0 + const quoteLoader = () => { + version += 1 + if (version === 2) { + secondLoaderStarted.resolve() + return secondLoaderResponse + } + + return { author: 'author-1' } + } + + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const quoteRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/quote', + loader: quoteLoader, + staleTime: 0, + head: ({ loaderData }) => ({ + meta: [{ title: `Quote by ${loaderData?.author ?? '...'}` }], + }), + }) + + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, quoteRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + const getQuoteMatch = () => + router.state.matches.find((match) => match.routeId === quoteRoute.id) + onTestFinished(() => { + secondLoaderResponse.resolve({ author: 'author-2' }) + }) + + await router.load() + + await router.navigate({ to: '/quote' }) + expect(getQuoteMatch()?.loaderData).toEqual({ author: 'author-1' }) + expect(getQuoteMatch()?.meta).toEqual([{ title: 'Quote by author-1' }]) + + await router.navigate({ to: '/' }) + const revisit = router.navigate({ to: '/quote' }) + await secondLoaderStarted + await revisit + + // The cached generation remains internally consistent while its stale + // loader is pending in the background. + expect(secondLoaderResponse.status).toBe('pending') + expect(getQuoteMatch()?.loaderData).toEqual({ author: 'author-1' }) + expect(getQuoteMatch()?.meta).toEqual([{ title: 'Quote by author-1' }]) + + secondLoaderResponse.resolve({ author: 'author-2' }) + await vi.waitFor(() => { + expect(getQuoteMatch()?.loaderData).toEqual({ author: 'author-2' }) + expect(getQuoteMatch()?.meta).toEqual([{ title: 'Quote by author-2' }]) + }) + }) }) diff --git a/packages/router-core/tests/issue-6351-fuzzy-notfound-layout.test.ts b/packages/router-core/tests/issue-6351-fuzzy-notfound-layout.test.ts new file mode 100644 index 0000000000..9fec0992a4 --- /dev/null +++ b/packages/router-core/tests/issue-6351-fuzzy-notfound-layout.test.ts @@ -0,0 +1,142 @@ +import { describe, expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute } from '../src' +import { createTestRouter } from './routerTestUtils' + +/** + * https://github.com/TanStack/router/issues/6351 + * + * A fuzzy (unmatched-URL) 404 must be attributed to the nearest matched + * ancestor that can actually render it: the deepest matched route with + * children that defines a `notFoundComponent`. Attributing it to the + * deepest matched route with children regardless of whether it has a + * `notFoundComponent` skips pathless-layout/ancestor `notFoundComponent`s + * and makes the framework fall back to `defaultNotFoundComponent`. + * + * When no matched route defines a `notFoundComponent`, the previous + * behavior (deepest matched route with children) is preserved, and + * `notFoundMode: 'root'` still attributes the 404 to the root route. + * This Core suite asserts boundary ownership; rendered fallback output needs + * framework-level coverage. + */ + +function setup(opts: { + layoutNotFoundComponent?: () => unknown + agentsNotFoundComponent?: () => unknown + agentsContext?: () => Record + agentsBeforeLoad?: () => Record + agentsLoader?: () => null + notFoundMode?: 'fuzzy' | 'root' + isServer?: boolean +}) { + const rootRoute = new BaseRootRoute({}) + const layoutRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + id: '_authenticated', + notFoundComponent: opts.layoutNotFoundComponent, + }) + const agentsRoute = new BaseRoute({ + getParentRoute: () => layoutRoute, + path: '/agents', + notFoundComponent: opts.agentsNotFoundComponent, + context: opts.agentsContext, + beforeLoad: opts.agentsBeforeLoad, + loader: opts.agentsLoader, + }) + const skillAgentRoute = new BaseRoute({ + getParentRoute: () => agentsRoute, + path: '/skill-agent', + }) + + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + layoutRoute.addChildren([agentsRoute.addChildren([skillAgentRoute])]), + ]), + history: createMemoryHistory({ initialEntries: ['/agents/skill-agen'] }), + notFoundMode: opts.notFoundMode, + isServer: opts.isServer, + }) + + return { router, rootRoute, layoutRoute, agentsRoute } +} + +function _notFoundRouteIds(router: { + state: { matches: Array<{ routeId: string; _notFound?: boolean }> } +}) { + return router.state.matches + .filter((match) => match._notFound) + .map((match) => match.routeId) +} + +describe('issue #6351: fuzzy notFound honors pathless layout boundaries', () => { + test('lands on the pathless layout when the deeper route has no boundary', async () => { + const { router, rootRoute, layoutRoute, agentsRoute } = setup({ + layoutNotFoundComponent: () => 'layout not found', + }) + + await router.load() + + expect(router.state.location.pathname).toBe('/agents/skill-agen') + expect(router.state.matches.map((match) => match.routeId)).toEqual([ + rootRoute.id, + layoutRoute.id, + agentsRoute.id, + ]) + expect(_notFoundRouteIds(router)).toEqual([layoutRoute.id]) + }) + + test('boundary-enabled control: prefers the deeper matched route', async () => { + const { router, agentsRoute } = setup({ + layoutNotFoundComponent: () => 'layout not found', + agentsNotFoundComponent: () => 'agents not found', + }) + + await router.load() + + expect(_notFoundRouteIds(router)).toEqual([agentsRoute.id]) + }) + + test.each(['client', 'server'] as const)( + 'fuzzy ancestor 404 skips hidden descendant lifecycle callbacks on the %s', + async (environment) => { + const context = vi.fn(() => ({})) + const beforeLoad = vi.fn(() => ({})) + const loader = vi.fn(() => null) + const { router } = setup({ + layoutNotFoundComponent: () => 'layout not found', + agentsContext: context, + agentsBeforeLoad: beforeLoad, + agentsLoader: loader, + notFoundMode: 'fuzzy', + isServer: environment === 'server', + }) + + await router.load() + + expect(context).not.toHaveBeenCalled() + expect(beforeLoad).not.toHaveBeenCalled() + expect(loader).not.toHaveBeenCalled() + }, + ) +}) + +describe('generic global notFound attribution controls', () => { + test('fuzzy mode falls back to the deepest matched route with children when no boundary exists', async () => { + const { router, agentsRoute } = setup({}) + + await router.load() + + expect(_notFoundRouteIds(router)).toEqual([agentsRoute.id]) + }) + + test('root mode attributes the notFound to root even when a layout boundary exists', async () => { + const { router, rootRoute } = setup({ + layoutNotFoundComponent: () => 'layout not found', + notFoundMode: 'root', + }) + + await router.load() + + expect(_notFoundRouteIds(router)).toEqual([rootRoute.id]) + }) +}) diff --git a/packages/router-core/tests/issue-6371-search-default-normalization-abort.test.ts b/packages/router-core/tests/issue-6371-search-default-normalization-abort.test.ts index a52a7f183d..6f039b45da 100644 --- a/packages/router-core/tests/issue-6371-search-default-normalization-abort.test.ts +++ b/packages/router-core/tests/issue-6371-search-default-normalization-abort.test.ts @@ -9,18 +9,11 @@ import { createTestRouter } from './routerTestUtils' * Entering a route directly via URL without search params, while * validateSearch supplies defaults, makes the framework Transitioner commit a * canonical replace-navigation (URL with the defaulted search) right after - * the mount load already started. That replace supersedes the first load - * pass and aborts its match's abortController. A loader that forwarded that - * signal to fetch() re-throws an AbortError ("signal is aborted without - * reason"): it belongs to the abandoned pass and must not surface as a route - * error; the follow-up canonical load must complete normally. + * the mount load already started. The canonical pass may reuse that loader + * generation, and the URL-only replacement must not abort it or surface an + * AbortError. */ -const waitForMacrotask = () => - new Promise((resolve) => { - setTimeout(resolve, 0) - }) - describe('issue #6371: search default normalization aborts the mount load silently', () => { test('canonical replace after validateSearch defaults does not surface AbortError', async () => { const invocations: Array<{ resolve: () => void; signal: AbortSignal }> = [] @@ -74,26 +67,20 @@ describe('issue #6371: search default normalization aborts the mount load silent // In core (no history subscribers) commitLocation starts the second load. void router.commitLocation({ ...nextLocation, replace: true } as any) - await vi.waitFor(() => expect(invocations.length).toBe(2)) - - // The superseded mount-load generation was aborted (its fetch canceled). - expect(invocations[0]!.signal.aborted).toBe(true) - - // Give the aborted rejection time to (incorrectly) commit as an error. - await waitForMacrotask() - await waitForMacrotask() + await Promise.resolve() + expect(invocations).toHaveLength(1) + expect(invocations[0]!.signal.aborted).toBe(false) for (const match of router.state.matches) { expect(match.status).not.toBe('error') expect((match.error as Error | undefined)?.name).not.toBe('AbortError') } - invocations[1]!.resolve() + invocations[0]!.resolve() // Awaiting the superseded mount load also awaits the superseding chain. await initialLoad - // No runaway normalization loop: exactly one aborted + one canonical run. - expect(invocations.length).toBe(2) + expect(invocations).toHaveLength(1) const aboutMatch = router.state.matches.find( (match) => match.routeId === aboutRoute.id, diff --git a/packages/router-core/tests/issue-7379-head-matches-direct-load.test.ts b/packages/router-core/tests/issue-7379-head-matches-direct-load.test.ts new file mode 100644 index 0000000000..1768988e47 --- /dev/null +++ b/packages/router-core/tests/issue-7379-head-matches-direct-load.test.ts @@ -0,0 +1,81 @@ +import { createMemoryHistory } from '@tanstack/history' +import { describe, expect, test } from 'vitest' +import { BaseRootRoute, BaseRoute } from '../src' +import { createTestRouter } from './routerTestUtils' + +// https://github.com/TanStack/router/issues/7379 +// +// On a direct (initial) load, head({ matches }) must observe loaderData and +// context on every ancestor match so a breadcrumb-style title can be built. +describe('issue #7379: head({ matches }) has loaderData and context on direct load', () => { + test('initial load executes head after loaders with populated matches', async () => { + const seenMatches: Array< + Array<{ routeId: string; loaderData: unknown; context: unknown }> + > = [] + + const rootRoute = new BaseRootRoute({}) + const nestedRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/nested', + beforeLoad: async () => { + await Promise.resolve() + return { section: 'nested-section' } + }, + loader: async () => { + await Promise.resolve() + return { crumb: 'Nested' } + }, + }) + const evenRoute = new BaseRoute({ + getParentRoute: () => nestedRoute, + path: 'even', + loader: async () => { + await Promise.resolve() + return { crumb: 'Even' } + }, + head: ({ matches }) => { + seenMatches.push( + matches.map((match) => ({ + routeId: match.routeId, + loaderData: match.loaderData, + context: match.context, + })), + ) + const crumbs = matches + .map((match) => match.loaderData?.crumb) + .filter(Boolean) + return { + meta: [{ title: [...crumbs, 'Company Inc.'].join(' - ') }], + } + }, + }) + + const router = createTestRouter({ + routeTree: rootRoute.addChildren([nestedRoute.addChildren([evenRoute])]), + history: createMemoryHistory({ initialEntries: ['/nested/even'] }), + }) + + // Direct load: this is the first and only load of the router. + await router.load() + + expect(seenMatches.length).toBeGreaterThan(0) + const seen = seenMatches.at(-1)! + + const seenNested = seen.find((entry) => entry.routeId === nestedRoute.id) + const seenEven = seen.find((entry) => entry.routeId === evenRoute.id) + + // loaderData is present on every match handed to head(). + expect(seenNested?.loaderData).toEqual({ crumb: 'Nested' }) + expect(seenEven?.loaderData).toEqual({ crumb: 'Even' }) + + // context (incl. beforeLoad context) is present as well. + expect(seenNested?.context).toMatchObject({ section: 'nested-section' }) + expect(seenEven?.context).toMatchObject({ section: 'nested-section' }) + + // The breadcrumb title is complete on the committed match. + const evenMatch = router.state.matches.find( + (match) => match.routeId === evenRoute.id, + ) + expect(evenMatch?.meta).toEqual([{ title: 'Nested - Even - Company Inc.' }]) + }) +}) diff --git a/packages/router-core/tests/issue-7602-parent-beforeload-context-child-loader.test.ts b/packages/router-core/tests/issue-7602-parent-beforeload-context-child-loader.test.ts new file mode 100644 index 0000000000..41549b58fd --- /dev/null +++ b/packages/router-core/tests/issue-7602-parent-beforeload-context-child-loader.test.ts @@ -0,0 +1,108 @@ +import { expect, onTestFinished, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute, createControlledPromise } from '../src' +import { createTestRouter } from './routerTestUtils' + +// https://github.com/TanStack/router/issues/7602 +test('#7602: browser Back republishes a cached child with fresh parent beforeLoad context', async () => { + const loaderContexts: Array<{ + number?: number + generation?: number + }> = [] + const reloadGate = createControlledPromise() + let loaderRuns = 0 + let beforeLoadRuns = 0 + + const rootRoute = new BaseRootRoute({}) + const homeRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const reproRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/repro', + beforeLoad: async () => { + await Promise.resolve() + return { number: 42, generation: ++beforeLoadRuns } + }, + }) + const reproIndexRoute = new BaseRoute({ + getParentRoute: () => reproRoute, + path: '/', + loader: async ({ + context, + }: { + context: { number?: number; generation?: number } + }) => { + loaderRuns++ + loaderContexts.push({ + number: context.number, + generation: context.generation, + }) + if (loaderRuns === 2) { + await reloadGate + } + return { visit: loaderRuns } + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + homeRoute, + reproRoute.addChildren([reproIndexRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/repro'] }), + }) + const getChildMatch = () => + router.state.matches.find((match) => match.routeId === reproIndexRoute.id) + // RouterProvider's Transitioner installs this subscription in applications. + // It is what turns a browser Back history event into a router load. + const unsubscribe = router.history.subscribe(router.load) + onTestFinished(() => { + reloadGate.resolve() + unsubscribe() + }) + + await router.load() + expect(router.state.location.pathname).toBe('/repro') + expect(getChildMatch()).toMatchObject({ + routeId: reproIndexRoute.id, + status: 'success', + loaderData: { visit: 1 }, + context: { number: 42, generation: 1 }, + }) + expect(loaderContexts).toEqual([{ number: 42, generation: 1 }]) + + await router.navigate({ to: '/' }) + expect(router.state.location.pathname).toBe('/') + expect(router.state.isLoading).toBe(false) + expect(getChildMatch()).toBeUndefined() + + router.history.back() + await vi.waitFor(() => expect(loaderContexts).toHaveLength(2)) + + // The cached success is visible while its stale loader reloads. It must + // never be published with the parent's context contribution missing. + expect(router.state.location.pathname).toBe('/repro') + expect(getChildMatch()).toMatchObject({ + status: 'success', + loaderData: { visit: 1 }, + context: { number: 42, generation: 2 }, + }) + expect(loaderContexts).toEqual([ + { number: 42, generation: 1 }, + { number: 42, generation: 2 }, + ]) + + reloadGate.resolve() + await vi.waitFor(() => + expect(getChildMatch()?.loaderData).toEqual({ visit: 2 }), + ) + expect(router.state.location.pathname).toBe('/repro') + expect(router.state.isLoading).toBe(false) + expect(getChildMatch()?.context).toMatchObject({ + number: 42, + generation: 2, + }) + expect(beforeLoadRuns).toBe(2) + expect(loaderRuns).toBe(2) +}) diff --git a/packages/router-core/tests/issue-7759-in-flight-preload-eviction.test.ts b/packages/router-core/tests/issue-7759-in-flight-preload-eviction.test.ts new file mode 100644 index 0000000000..66f76aaa33 --- /dev/null +++ b/packages/router-core/tests/issue-7759-in-flight-preload-eviction.test.ts @@ -0,0 +1,43 @@ +import { expect, onTestFinished, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute } from '../src' +import { createTestRouter } from './routerTestUtils' + +test('issue #7759: evicting an in-flight preload finishes without an internal error', async () => { + let signalLoaderStarted!: () => void + let resolveLoader!: (value: string) => void + + const loaderStarted = new Promise((resolve) => { + signalLoaderStarted = resolve + }) + const loaderResult = new Promise((resolve) => { + resolveLoader = resolve + }) + + const rootRoute = new BaseRootRoute({}) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + loader: () => { + signalLoaderStarted() + return loaderResult + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([targetRoute]), + history: createMemoryHistory(), + }) + const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {}) + onTestFinished(() => { + consoleError.mockRestore() + }) + + const preload = router.preloadRoute({ to: '/target' }) + await loaderStarted + + router.clearCache() + resolveLoader('loaded') + + await preload + expect(consoleError).not.toHaveBeenCalled() +}) diff --git a/packages/router-core/tests/load-client-wait-for.test.ts b/packages/router-core/tests/load-client-wait-for.test.ts new file mode 100644 index 0000000000..5ef9febb59 --- /dev/null +++ b/packages/router-core/tests/load-client-wait-for.test.ts @@ -0,0 +1,209 @@ +import { describe, expect, test, vi } from 'vitest' +import { waitForReason } from '../src/await-signal' +import { waitFor } from '../src/load-client' +import { waitForRequest } from '../src/ssr/createRequestHandler' + +describe('waitFor', () => { + test('observes a rejected value when the signal is already aborted', async () => { + const controller = new AbortController() + const error = new Error('late failure') + controller.abort() + + await expect(waitFor(undefined, controller.signal)).rejects.toBe( + controller.signal, + ) + await expect( + waitFor(Promise.reject(error), controller.signal), + ).rejects.toBe(controller.signal) + + await Promise.resolve() + }) + + test('preserves reason-based cancellation and observes a late value', async () => { + const controller = new AbortController() + const reason = new Error('request canceled') + const late = Promise.resolve('late response') + const onLate = vi.fn() + controller.abort(reason) + + await expect(waitForReason(late, controller.signal, onLate)).rejects.toBe( + reason, + ) + await vi.waitFor(() => expect(onLate).toHaveBeenCalledWith('late response')) + }) +}) + +describe('waitForRequest', () => { + test('shares one abort listener across concurrent and sequential waits', async () => { + const controller = new AbortController() + const addEventListener = vi.spyOn(controller.signal, 'addEventListener') + let resolveFirst!: (value: string) => void + let resolveSecond!: (value: string) => void + let resolveThird!: (value: string) => void + const first = new Promise((resolve) => { + resolveFirst = resolve + }) + const second = new Promise((resolve) => { + resolveSecond = resolve + }) + const third = new Promise((resolve) => { + resolveThird = resolve + }) + + const firstResult = waitForRequest(first, controller.signal) + const secondResult = waitForRequest(second, controller.signal) + + expect(addEventListener).toHaveBeenCalledTimes(1) + resolveFirst('first') + resolveSecond('second') + await expect(firstResult).resolves.toBe('first') + await expect(secondResult).resolves.toBe('second') + + const thirdResult = waitForRequest(third, controller.signal) + expect(addEventListener).toHaveBeenCalledTimes(1) + + const reason = new Error('request canceled') + controller.abort(reason) + await expect(thirdResult).rejects.toBe(reason) + resolveThird('third') + await Promise.resolve() + }) + + test('rejects all active waits and observes their late values', async () => { + const controller = new AbortController() + const reason = new Error('request canceled') + const onFirstLate = vi.fn() + const onSecondLate = vi.fn() + let resolveFirst!: (value: string) => void + let resolveSecond!: (value: string) => void + const first = new Promise((resolve) => { + resolveFirst = resolve + }) + const second = new Promise((resolve) => { + resolveSecond = resolve + }) + const firstResult = waitForRequest(first, controller.signal, onFirstLate) + const secondResult = waitForRequest(second, controller.signal, onSecondLate) + + controller.abort(reason) + await expect(firstResult).rejects.toBe(reason) + await expect(secondResult).rejects.toBe(reason) + + resolveFirst('first') + resolveSecond('second') + await vi.waitFor(() => { + expect(onFirstLate).toHaveBeenCalledWith('first') + expect(onSecondLate).toHaveBeenCalledWith('second') + }) + }) + + test('aborts remaining waits after an out-of-order rejection', async () => { + const controller = new AbortController() + const reason = new Error('request canceled') + const failure = new Error('wait failed') + const onLate = vi.fn() + let resolveFirst!: (value: string) => void + let rejectSecond!: (error: Error) => void + let rejectThird!: (error: Error) => void + const first = new Promise((resolve) => { + resolveFirst = resolve + }) + const second = new Promise((_, reject) => { + rejectSecond = reject + }) + const third = new Promise((_, reject) => { + rejectThird = reject + }) + const firstResult = waitForRequest(first, controller.signal, onLate) + const secondResult = waitForRequest(second, controller.signal) + const thirdResult = waitForRequest(third, controller.signal) + + rejectSecond(failure) + await expect(secondResult).rejects.toBe(failure) + + controller.abort(reason) + await expect(firstResult).rejects.toBe(reason) + await expect(thirdResult).rejects.toBe(reason) + + resolveFirst('late') + rejectThird(new Error('late failure')) + await vi.waitFor(() => expect(onLate).toHaveBeenCalledWith('late')) + }) + + test('isolates waits for different request signals', async () => { + const firstController = new AbortController() + const secondController = new AbortController() + const firstReason = new Error('first request canceled') + const firstAddEventListener = vi.spyOn( + firstController.signal, + 'addEventListener', + ) + const secondAddEventListener = vi.spyOn( + secondController.signal, + 'addEventListener', + ) + let resolveFirst!: (value: string) => void + let resolveSecond!: (value: string) => void + const first = new Promise((resolve) => { + resolveFirst = resolve + }) + const second = new Promise((resolve) => { + resolveSecond = resolve + }) + const firstResult = waitForRequest(first, firstController.signal) + const secondResult = waitForRequest(second, secondController.signal) + + expect(firstAddEventListener).toHaveBeenCalledOnce() + expect(secondAddEventListener).toHaveBeenCalledOnce() + + firstController.abort(firstReason) + await expect(firstResult).rejects.toBe(firstReason) + + resolveSecond('second result') + await expect(secondResult).resolves.toBe('second result') + resolveFirst('late first result') + await Promise.resolve() + }) + + test('observes fulfilled and rejected values when already aborted', async () => { + const controller = new AbortController() + const reason = new Error('request canceled') + const lateError = new Error('late failure') + const onLate = vi.fn() + controller.abort(reason) + + await expect( + waitForRequest(Promise.resolve('late'), controller.signal, onLate), + ).rejects.toBe(reason) + await expect( + waitForRequest(Promise.reject(lateError), controller.signal), + ).rejects.toBe(reason) + + await vi.waitFor(() => expect(onLate).toHaveBeenCalledWith('late')) + }) + + test('handles an abort during listener registration', async () => { + const controller = new AbortController() + const reason = new Error('request canceled') + const onLate = vi.fn() + const addEventListener = controller.signal.addEventListener.bind( + controller.signal, + ) + vi.spyOn(controller.signal, 'addEventListener').mockImplementation( + (type, listener, options) => { + addEventListener(type, listener, options) + controller.abort(reason) + }, + ) + let resolve!: (value: string) => void + const value = new Promise((resolveValue) => { + resolve = resolveValue + }) + + const result = waitForRequest(value, controller.signal, onLate) + await expect(result).rejects.toBe(reason) + + resolve('late') + await vi.waitFor(() => expect(onLate).toHaveBeenCalledWith('late')) + }) +}) diff --git a/packages/router-core/tests/load-route-chunk.test.ts b/packages/router-core/tests/load-route-chunk.test.ts new file mode 100644 index 0000000000..73179f9f32 --- /dev/null +++ b/packages/router-core/tests/load-route-chunk.test.ts @@ -0,0 +1,30 @@ +import { expect, test } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute } from '../src' +import { createTestRouter } from './routerTestUtils' + +test('router.loadRouteChunk exposes a failed lazy import and remains retryable', async () => { + const chunkError = new TypeError('Failed to fetch lazy route') + const component = () => null + let fail = true + const rootRoute = new BaseRootRoute({}) + const route = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/route', + }).lazy(() => { + if (fail) { + fail = false + return Promise.reject(chunkError) + } + return Promise.resolve({ options: { component } } as any) + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([route]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await expect(router.loadRouteChunk(route as any)).rejects.toBe(chunkError) + await router.loadRouteChunk(route as any) + + expect(route.options.component).toBe(component) +}) diff --git a/packages/router-core/tests/load.test.ts b/packages/router-core/tests/load.test.ts index eda35f3fa2..53a6444f0b 100644 --- a/packages/router-core/tests/load.test.ts +++ b/packages/router-core/tests/load.test.ts @@ -3,14 +3,13 @@ import { createMemoryHistory } from '@tanstack/history' import { BaseRootRoute, BaseRoute, + createControlledPromise, notFound, redirect, rootRouteId, } from '../src' -import { createTestRouter } from './routerTestUtils' -import { loadMatches } from '../src/load-matches' +import { createTestRouter, loadServerResponse } from './routerTestUtils' import type { - AnyRouter, LoaderStaleReloadMode, RootRouteOptions, RouterCore, @@ -70,14 +69,10 @@ describe('redirect resolution', () => { isServer: true, }) - await router.load() + const response = await loadServerResponse(router, initialPath) - expect(router.state.redirect).toEqual( - expect.objectContaining({ - options: expect.objectContaining({ href: '/undefined' }), - }), - ) - expect(router.state.redirect?.headers.get('Location')).toBe('/undefined') + expect(response.status).toBe(307) + expect(response.headers.get('Location')).toBe('/undefined') }, ) }) @@ -218,11 +213,9 @@ describe('beforeLoad skip or exec', () => { const router = setup({ beforeLoad }) router.preloadRoute({ to: '/foo' }) await Promise.resolve() - expect(router.stores.cachedMatches.get()).toEqual( - expect.arrayContaining([expect.objectContaining({ id: '/foo/foo' })]), - ) await router.navigate({ to: '/foo' }) + expect(router.state.location.pathname).toBe('/foo') expect(beforeLoad).toHaveBeenCalledTimes(2) }) @@ -283,15 +276,10 @@ describe('beforeLoad skip or exec', () => { }) router.preloadRoute({ to: '/foo' }) await Promise.resolve() - expect( - router.stores.cachedMatches.get().some((d) => d.status === 'redirected'), - ).toBe(false) await router.navigate({ to: '/foo' }) expect(router.state.location.pathname).toBe('/foo') - expect( - router.stores.cachedMatches.get().some((d) => d.status === 'redirected'), - ).toBe(false) + expect(router.state.matches.at(-1)?.status).toBe('success') expect(beforeLoad).toHaveBeenCalledTimes(2) }) @@ -426,13 +414,11 @@ describe('loader skip or exec', () => { const loader = vi.fn() const router = setup({ loader }) await router.preloadRoute({ to: '/foo' }) - expect(router.stores.cachedMatches.get()).toEqual( - expect.arrayContaining([expect.objectContaining({ id: '/foo/foo' })]), - ) await sleep(10) await router.navigate({ to: '/foo' }) - expect(loader).toHaveBeenCalledTimes(2) + expect(router.state.location.pathname).toBe('/foo') + expect(loader).toHaveBeenCalledTimes(1) }) test('skip if resolved preload (success) within staleTime duration', async () => { @@ -472,19 +458,35 @@ describe('loader skip or exec', () => { expect(loader).toHaveBeenCalledTimes(2) }) - test('skip if pending preload (notFound)', async () => { + test('navigation shares a pending preload loader notFound', async () => { + const loaderGate = createControlledPromise() const loader: Loader = vi.fn(async ({ preload }) => { - await sleep(100) + await loaderGate if (preload) throw notFound() }) const router = setup({ loader, }) - router.preloadRoute({ to: '/foo' }) + const preload = router.preloadRoute({ to: '/foo' }) + await vi.waitFor(() => expect(loader).toHaveBeenCalledOnce()) + + const navigation = router.navigate({ to: '/foo' }) await Promise.resolve() - await router.navigate({ to: '/foo' }) + expect(loader).toHaveBeenCalledOnce() - expect(loader).toHaveBeenCalledTimes(1) + loaderGate.resolve() + await Promise.all([preload, navigation]) + + expect(router.state.location.pathname).toBe('/foo') + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: '/foo', + status: 'notFound', + error: { + isNotFound: true, + routeId: '/foo', + }, + }) + expect(loader).toHaveBeenCalledOnce() }) test('exec if rejected preload (redirect)', async () => { @@ -521,28 +523,6 @@ describe('loader skip or exec', () => { expect(loader).toHaveBeenCalledTimes(1) }) - test('updateMatch removes redirected matches from cachedMatches', async () => { - const loader = vi.fn() - const router = setup({ loader }) - - await router.preloadRoute({ to: '/foo' }) - expect(router.stores.cachedMatches.get()).toEqual( - expect.arrayContaining([expect.objectContaining({ id: '/foo/foo' })]), - ) - - router.updateMatch('/foo/foo', (prev) => ({ - ...prev, - status: 'redirected', - })) - - expect( - router.stores.cachedMatches.get().some((d) => d.id === '/foo/foo'), - ).toBe(false) - expect( - router.stores.cachedMatches.get().some((d) => d.status === 'redirected'), - ).toBe(false) - }) - test('exec if rejected preload (error)', async () => { const loader: Loader = vi.fn(async ({ preload }) => { if (preload) throw new Error('error') @@ -558,19 +538,33 @@ describe('loader skip or exec', () => { expect(loader).toHaveBeenCalledTimes(2) }) - test('skip if pending preload (error)', async () => { + test('navigation shares a pending preload loader error', async () => { + const loaderGate = createControlledPromise() + const failure = new Error('preload failed') const loader: Loader = vi.fn(async ({ preload }) => { - await sleep(100) - if (preload) throw new Error('error') + await loaderGate + if (preload) throw failure }) const router = setup({ loader, }) - router.preloadRoute({ to: '/foo' }) + const preload = router.preloadRoute({ to: '/foo' }) + await vi.waitFor(() => expect(loader).toHaveBeenCalledOnce()) + + const navigation = router.navigate({ to: '/foo' }) await Promise.resolve() - await router.navigate({ to: '/foo' }) + expect(loader).toHaveBeenCalledOnce() - expect(loader).toHaveBeenCalledTimes(1) + loaderGate.resolve() + await Promise.all([preload, navigation]) + + expect(router.state.location.pathname).toBe('/foo') + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: '/foo', + status: 'error', + error: failure, + }) + expect(loader).toHaveBeenCalledOnce() }) }) @@ -665,22 +659,13 @@ describe('stale loader reload triggers', () => { const getMatchById = ( router: RouterCore, id: string, - ) => - router.state.matches.find((match) => match.id === id) ?? - router.stores.pendingMatches.get().find((match) => match.id === id) ?? - router.stores.cachedMatches.get().find((match) => match.id === id) + ) => router.state.matches.find((match) => match.id === id) const hasActiveMatch = ( router: RouterCore, id: string, ) => router.state.matches.some((match) => match.id === id) - const hasPendingMatch = ( - router: RouterCore, - id: string, - ) => - router.stores.pendingMatches.get().some((match) => match.id === id) ?? false - const setup = ({ loader, staleTime, @@ -750,24 +735,30 @@ describe('stale loader reload triggers', () => { await router.navigate({ to: '/bar' }) await vi.advanceTimersByTimeAsync(1) - const revisit = router.navigate({ to: '/foo' }) - await Promise.resolve() + let revisitSettled = false + const revisit = router.navigate({ to: '/foo' }).then(() => { + revisitSettled = true + }) - expect(loader).toHaveBeenCalledTimes(2) + await vi.waitFor(() => { + expect(loader).toHaveBeenCalledTimes(2) + }) + expect(revisitSettled).toBe(false) + expect(router.state.status).toBe('pending') + expect(router.state.isLoading).toBe(true) expect(hasActiveMatch(router, '/bar/bar')).toBe(true) expect(hasActiveMatch(router, '/foo/foo')).toBe(false) - expect(hasPendingMatch(router, '/foo/foo')).toBe(true) - expect(getMatchById(router, '/foo/foo')?.loaderData).toEqual({ - value: 'first', - }) resolveStaleReload() await revisit + expect(revisitSettled).toBe(true) + expect(router.state.status).toBe('idle') + expect(router.state.isLoading).toBe(false) expect(loader).toHaveBeenCalledTimes(2) expect(hasActiveMatch(router, '/foo/foo')).toBe(true) - expect(hasPendingMatch(router, '/foo/foo')).toBe(false) expect(router.state.location.pathname).toBe('/foo') + expect(router.state.resolvedLocation?.pathname).toBe('/foo') expect(getMatchById(router, '/foo/foo')?.loaderData).toEqual({ value: 'second', }) @@ -785,27 +776,28 @@ describe('stale loader reload triggers', () => { await router.navigate({ to: '/bar' }) await vi.advanceTimersByTimeAsync(1) - const revisit = router.navigate({ to: '/foo' }) - - expect(loader).toHaveBeenCalledTimes(2) - + let revisitSettled = false + const revisit = router.navigate({ to: '/foo' }).then(() => { + revisitSettled = true + }) await revisit - const backgroundReloadPromise = getMatchById(router, '/foo/foo') - ?._nonReactive.loaderPromise - expect(backgroundReloadPromise).toBeDefined() + expect(revisitSettled).toBe(true) + expect(router.state.status).toBe('idle') + expect(router.state.isLoading).toBe(false) + expect(loader).toHaveBeenCalledTimes(2) expect(hasActiveMatch(router, '/foo/foo')).toBe(true) - expect(hasPendingMatch(router, '/foo/foo')).toBe(false) expect(router.state.location.pathname).toBe('/foo') + expect(router.state.resolvedLocation?.pathname).toBe('/foo') expect(getMatchById(router, '/foo/foo')?.loaderData).toEqual({ value: 'first', }) resolveStaleReload() - await backgroundReloadPromise - - expect(getMatchById(router, '/foo/foo')?.loaderData).toEqual({ - value: 'second', + await vi.waitFor(() => { + expect(getMatchById(router, '/foo/foo')?.loaderData).toEqual({ + value: 'second', + }) }) } @@ -867,7 +859,7 @@ describe('stale loader reload triggers', () => { expect(loader).toHaveBeenCalledTimes(2) }) - test('reloads a stale preloaded loader when switching to a different match id of the same route', async () => { + test('reuses a fresh preloaded loader when switching to a different match id of the same route', async () => { const rootRoute = new BaseRootRoute({}) const rootLoader = vi.fn(() => ({ ok: true })) const childLoader = vi.fn(() => ({ ok: true })) @@ -925,8 +917,8 @@ describe('stale loader reload triggers', () => { search: { page: '2' }, }) - expect(rootLoader).toHaveBeenCalledTimes(3) - expect(childLoader).toHaveBeenCalledTimes(3) + expect(rootLoader).toHaveBeenCalledTimes(2) + expect(childLoader).toHaveBeenCalledTimes(2) }) test('skips stale ancestor loader when only a child path param changes', async () => { @@ -1121,24 +1113,23 @@ describe('stale loader reload triggers', () => { }) }) -test('cancelMatches after pending timeout', async () => { - const WAIT_TIME = 5 - const onAbortMock = vi.fn() +test('navigating away from a pending route aborts its loader', async () => { + let loaderSignal: AbortSignal | undefined const rootRoute = new BaseRootRoute({}) const fooRoute = new BaseRoute({ getParentRoute: () => rootRoute, path: '/foo', - pendingMs: WAIT_TIME * 20, + pendingMs: 0, loader: async ({ abortController }) => { + loaderSignal = abortController.signal await new Promise((resolve) => { - const timer = setTimeout(() => { - resolve() - }, WAIT_TIME * 40) - abortController.signal.addEventListener('abort', () => { - onAbortMock() - clearTimeout(timer) - resolve() - }) + abortController.signal.addEventListener( + 'abort', + () => { + resolve() + }, + { once: true }, + ) }) }, pendingComponent: {}, @@ -1151,23 +1142,30 @@ test('cancelMatches after pending timeout', async () => { const router = createTestRouter({ routeTree, history: createMemoryHistory() }) await router.load() - router.navigate({ to: '/foo' }) - await sleep(WAIT_TIME * 30) + const fooNavigation = router.navigate({ to: '/foo' }) - // At this point, pending timeout should have triggered - const fooMatch = router.getMatch('/foo/foo') - expect(fooMatch).toBeDefined() + await vi.waitFor(() => { + expect(loaderSignal).toBeDefined() + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: fooRoute.id, + status: 'pending', + }) + }) - // Navigate away, which should cancel the pending match await router.navigate({ to: '/bar' }) - await router.latestLoadPromise + await fooNavigation expect(router.state.location.pathname).toBe('/bar') - - // Verify that abort was called and pending timeout was cleared - expect(onAbortMock).toHaveBeenCalled() - const cancelledFooMatch = router.getMatch('/foo/foo') - expect(cancelledFooMatch?._nonReactive.pendingTimeout).toBeUndefined() + expect(router.state.resolvedLocation?.pathname).toBe('/bar') + expect(router.state.status).toBe('idle') + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: barRoute.id, + status: 'success', + }) + expect( + router.state.matches.some((match) => match.routeId === fooRoute.id), + ).toBe(false) + expect(loaderSignal?.aborted).toBe(true) }) describe('head execution', () => { @@ -1430,40 +1428,47 @@ describe('head execution', () => { name: string throwAtIndex: ThrowAtIndex parentFailures: ParentFailureMap - expectedErrorKind: 'notFound' | 'redirect' | 'error' + expectedErrorKind: 'notFound' | 'redirect' expectedErrorSource?: string - expectedErrorRouteIndex?: 0 | 1 | 2 | 3 - expectedLoaderMaxIndex: number - expectedRenderedHeadMaxIndex: number - withDefaultNotFoundComponent?: boolean + expectedBoundaryIndex?: 0 | 1 | 2 | 3 + expectedLoaderCount: number + expectedHeadTitles: Array beforeLoadNotFoundFactory?: ( routes: readonly [any, any, any, any], ) => ReturnType - expectRootNotFoundComponentAssigned?: boolean } const setupScenario = ({ throwAtIndex, parentFailures, beforeLoadNotFoundFactory, - withDefaultNotFoundComponent, + skipRootLoaderOnReload = false, }: { throwAtIndex: ThrowAtIndex parentFailures: ParentFailureMap beforeLoadNotFoundFactory?: Scenario['beforeLoadNotFoundFactory'] - withDefaultNotFoundComponent?: boolean + skipRootLoaderOnReload?: boolean }) => { const makeHead = (label: string) => vi.fn(() => ({ meta: [{ title: label }] })) const makeLoader = (index: number) => - vi.fn(() => { - const failure = parentFailures[index as 0 | 1 | 2] - if (failure === 'notFound') { - throw notFound({ data: { source: `loader-${index}` } }) - } - if (failure === 'redirect') { - throw redirect({ to: '/redirect-target' }) + vi.fn(({ location }: { location: { pathname: string } }) => { + // Keep failures scoped to the route under test. A root loader which + // redirects unconditionally would also redirect the destination and + // describe an infinite redirect loop rather than precedence between + // the original lane's settled outcomes. + if (location.pathname !== '/redirect-target') { + const failure = parentFailures[index as 0 | 1 | 2] + if (failure === 'notFound') { + throw notFound({ data: { source: `loader-${index}` } }) + } + if (failure === 'redirect') { + throw redirect({ to: '/redirect-target' }) + } + if (failure === 'error') { + throw new Error(`loader-${index}-error`) + } } return { level: index } }) @@ -1471,6 +1476,9 @@ describe('head execution', () => { const rootRoute = new BaseRootRoute({ loader: makeLoader(0), head: makeHead('Root'), + ...(skipRootLoaderOnReload + ? { staleTime: Infinity, shouldReload: () => false } + : {}), }) const level1Route = new BaseRoute({ @@ -1505,14 +1513,12 @@ describe('head execution', () => { ]) const routes = [rootRoute, level1Route, level2Route, level3Route] as const - - ;([0, 1, 2] as const).forEach((index) => { - if (parentFailures[index] === 'error') { - ;(routes[index].options as any).shouldReload = () => { - throw new Error(`loader-${index}-error`) - } - } - }) + const loaders = routes.map( + (route) => route.options.loader as ReturnType, + ) + const heads = routes.map( + (route) => route.options.head as ReturnType, + ) const throwRoute = routes[throwAtIndex]! throwRoute.options.beforeLoad = () => { @@ -1527,18 +1533,8 @@ describe('head execution', () => { history: createMemoryHistory({ initialEntries: ['/level-1/level-2/level-3'], }), - ...(withDefaultNotFoundComponent - ? { defaultNotFoundComponent: () => null } - : {}), }) - const loaders = routes.map( - (route) => route.options.loader as ReturnType, - ) - const heads = routes.map( - (route) => route.options.head as ReturnType, - ) - return { router, routes, @@ -1547,24 +1543,6 @@ describe('head execution', () => { } } - const runLoadMatchesAndCapture = async (router: AnyRouter) => { - const location = router.latestLocation - const matches = router.matchRoutes(location) - router.stores.setPending(matches) - - try { - await loadMatches({ - router, - location, - matches, - updateMatch: router.updateMatch, - }) - return { error: undefined, matches } - } catch (error) { - return { error, matches } - } - } - const scenarios = [ { name: 'throws beforeLoad notFound when parent loaders succeed', @@ -1572,35 +1550,39 @@ describe('head execution', () => { parentFailures: {} as ParentFailureMap, expectedErrorKind: 'notFound' as const, expectedErrorSource: 'beforeLoad-3', - expectedLoaderMaxIndex: 2, - expectedRenderedHeadMaxIndex: 3, + expectedBoundaryIndex: 3, + expectedLoaderCount: 3, + expectedHeadTitles: ['Root', 'Level 1', 'Level 2', 'Level 3'], }, { - name: 'uses parent loader notFound when parent loader throws notFound', + name: 'promotes a fresh parent loader notFound above a serial child notFound', throwAtIndex: 3 as const, parentFailures: { 1: 'notFound' } as ParentFailureMap, expectedErrorKind: 'notFound' as const, expectedErrorSource: 'loader-1', - expectedLoaderMaxIndex: 2, - expectedRenderedHeadMaxIndex: 1, + expectedBoundaryIndex: 1, + expectedLoaderCount: 3, + expectedHeadTitles: ['Root', 'Level 1'], }, { - name: 'uses first parent loader notFound when multiple parent loaders throw notFound', + name: 'promotes the first fresh parent loader failure above a serial child notFound', throwAtIndex: 3 as const, parentFailures: { 1: 'notFound', 2: 'notFound' } as ParentFailureMap, expectedErrorKind: 'notFound' as const, expectedErrorSource: 'loader-1', - expectedLoaderMaxIndex: 2, - expectedRenderedHeadMaxIndex: 1, + expectedBoundaryIndex: 1, + expectedLoaderCount: 3, + expectedHeadTitles: ['Root', 'Level 1'], }, { - name: 'uses parent loader notFound when root loader throws notFound', + name: 'promotes a fresh root loader notFound above a serial child notFound', throwAtIndex: 2 as const, parentFailures: { 0: 'notFound' } as ParentFailureMap, expectedErrorKind: 'notFound' as const, expectedErrorSource: 'loader-0', - expectedLoaderMaxIndex: 1, - expectedRenderedHeadMaxIndex: 0, + expectedBoundaryIndex: 0, + expectedLoaderCount: 2, + expectedHeadTitles: ['Root'], }, { name: 'uses explicit routeId from beforeLoad notFound to target ancestor boundary', @@ -1608,9 +1590,9 @@ describe('head execution', () => { parentFailures: {} as ParentFailureMap, expectedErrorKind: 'notFound' as const, expectedErrorSource: 'beforeLoad-explicit-level1', - expectedErrorRouteIndex: 1, - expectedLoaderMaxIndex: 1, - expectedRenderedHeadMaxIndex: 1, + expectedBoundaryIndex: 1, + expectedLoaderCount: 2, + expectedHeadTitles: ['Root', 'Level 1'], beforeLoadNotFoundFactory: (routes) => notFound({ routeId: routes[1].id as never, @@ -1623,8 +1605,9 @@ describe('head execution', () => { parentFailures: {} as ParentFailureMap, expectedErrorKind: 'notFound' as const, expectedErrorSource: 'beforeLoad-invalid-route', - expectedLoaderMaxIndex: 0, - expectedRenderedHeadMaxIndex: 0, + expectedBoundaryIndex: 0, + expectedLoaderCount: 1, + expectedHeadTitles: ['Root'], beforeLoadNotFoundFactory: () => notFound({ routeId: '/does-not-exist' as never, @@ -1637,38 +1620,23 @@ describe('head execution', () => { parentFailures: {} as ParentFailureMap, expectedErrorKind: 'notFound' as const, expectedErrorSource: 'beforeLoad-non-exact-route', - expectedLoaderMaxIndex: 0, - expectedRenderedHeadMaxIndex: 0, + expectedBoundaryIndex: 0, + expectedLoaderCount: 1, + expectedHeadTitles: ['Root'], beforeLoadNotFoundFactory: (routes) => notFound({ routeId: `${routes[1].id}/` as never, data: { source: 'beforeLoad-non-exact-route' }, }), }, - { - name: 'assigns defaultNotFoundComponent on root when unknown routeId falls back to root', - throwAtIndex: 3 as const, - parentFailures: {} as ParentFailureMap, - expectedErrorKind: 'notFound' as const, - expectedErrorSource: 'beforeLoad-invalid-route-default', - expectedLoaderMaxIndex: 0, - expectedRenderedHeadMaxIndex: 0, - withDefaultNotFoundComponent: true, - expectRootNotFoundComponentAssigned: true, - beforeLoadNotFoundFactory: () => - notFound({ - routeId: '/does-not-exist' as never, - data: { source: 'beforeLoad-invalid-route-default' }, - }), - }, { name: 'prioritizes redirect when parent loader throws redirect', throwAtIndex: 3 as const, parentFailures: { 0: 'redirect' } as ParentFailureMap, expectedErrorKind: 'redirect' as const, expectedErrorSource: undefined, - expectedLoaderMaxIndex: 2, - expectedRenderedHeadMaxIndex: -1, + expectedLoaderCount: 3, + expectedHeadTitles: ['Root'], }, { name: 'prioritizes redirect over root-loader notFound when both appear in settled loaders', @@ -1676,17 +1644,18 @@ describe('head execution', () => { parentFailures: { 0: 'notFound', 1: 'redirect' } as ParentFailureMap, expectedErrorKind: 'redirect' as const, expectedErrorSource: undefined, - expectedLoaderMaxIndex: 2, - expectedRenderedHeadMaxIndex: -1, + expectedLoaderCount: 3, + expectedHeadTitles: ['Root'], }, { - name: 'propagates regular loader error when mixed with loader notFound in settled loaders', + name: 'promotes the first fresh parent failure when later loaders fail differently', throwAtIndex: 3 as const, parentFailures: { 1: 'notFound', 2: 'error' } as ParentFailureMap, - expectedErrorKind: 'error' as const, - expectedErrorSource: 'loader-2-error', - expectedLoaderMaxIndex: 1, - expectedRenderedHeadMaxIndex: -1, + expectedErrorKind: 'notFound' as const, + expectedErrorSource: 'loader-1', + expectedBoundaryIndex: 1, + expectedLoaderCount: 3, + expectedHeadTitles: ['Root', 'Level 1'], }, ] satisfies Array @@ -1695,60 +1664,68 @@ describe('head execution', () => { throwAtIndex: scenario.throwAtIndex, parentFailures: scenario.parentFailures, beforeLoadNotFoundFactory: scenario.beforeLoadNotFoundFactory, - withDefaultNotFoundComponent: scenario.withDefaultNotFoundComponent, }) - const { error, matches } = await runLoadMatchesAndCapture(router) - - for (let i = 0; i < routes.length; i++) { - const loader = loaders[i]! - const expectedCalls = i <= scenario.expectedLoaderMaxIndex ? 1 : 0 - expect(loader).toHaveBeenCalledTimes(expectedCalls) - } - - for (let i = 0; i < heads.length; i++) { - const head = heads[i]! - const expectedCalls = i <= scenario.expectedRenderedHeadMaxIndex ? 1 : 0 - expect(head).toHaveBeenCalledTimes(expectedCalls) - } + await router.load() + const matches = router.state.matches if (scenario.expectedErrorKind === 'redirect') { - expect(error).toEqual( + expect(router.state.location.pathname).toBe('/redirect-target') + expect(matches.at(-1)).toEqual( expect.objectContaining({ - redirectHandled: true, - options: expect.objectContaining({ - to: '/redirect-target', - }), + pathname: '/redirect-target', + status: 'success', }), ) - return - } - - if (scenario.expectedErrorKind === 'error') { - expect(error).toBeInstanceOf(Error) - expect((error as Error).message).toBe(scenario.expectedErrorSource) - return - } - - expect(error).toEqual( - expect.objectContaining({ - isNotFound: true, - data: { source: scenario.expectedErrorSource }, - }), - ) + expect(matches.some((match) => match.error !== undefined)).toBe(false) + expect(matches.some((match) => match._notFound)).toBe(false) + } else { + const boundary = matches.find( + (match) => match.status === 'notFound' || match._notFound, + )! + expect(boundary.routeId).toBe( + routes[scenario.expectedBoundaryIndex!]!.id, + ) - if (scenario.expectedErrorRouteIndex !== undefined) { - expect((error as { routeId?: string }).routeId).toBe( - routes[scenario.expectedErrorRouteIndex]!.id, + expect(boundary.error).toEqual( + expect.objectContaining({ + isNotFound: true, + data: { source: scenario.expectedErrorSource }, + }), ) - } - if (scenario.expectRootNotFoundComponentAssigned) { - expect(routes[0].options.notFoundComponent).toBeTypeOf('function') + if (scenario.expectedBoundaryIndex === 0) { + expect(boundary.status).toBe('success') + expect(boundary._notFound).toBe(true) + } else { + expect(boundary.status).toBe('notFound') + expect(boundary.error).toEqual( + expect.objectContaining({ routeId: boundary.routeId }), + ) + } } + + loaders.forEach((loader, index) => { + const originalLaneCalls = loader.mock.calls.filter( + ([options]) => + options.location.pathname === '/level-1/level-2/level-3', + ) + expect(originalLaneCalls).toHaveLength( + index < scenario.expectedLoaderCount ? 1 : 0, + ) + }) + heads.forEach((head, index) => { + expect(head).toHaveBeenCalledTimes( + index < scenario.expectedHeadTitles.length ? 1 : 0, + ) + }) + expect( + matches.map(getTitle).filter((title) => title !== undefined), + ).toEqual(scenario.expectedHeadTitles) + expect(router.state.status).toBe('idle') }) - test('sets globalNotFound on root match when beforeLoad notFound targets root boundary', async () => { + test('sets _notFound on root match when beforeLoad notFound targets root boundary', async () => { const { router, routes } = setupScenario({ throwAtIndex: 3, parentFailures: {}, @@ -1759,28 +1736,27 @@ describe('head execution', () => { }), }) - const { error, matches } = await runLoadMatchesAndCapture(router) + await router.load() - expect(error).toEqual( + const rootMatch = router.state.matches.find( + (m) => m.routeId === routes[0].id, + ) + + expect(rootMatch?._notFound).toBe(true) + expect(rootMatch?.status).toBe('success') + expect(rootMatch?.error).toEqual( expect.objectContaining({ isNotFound: true, data: { source: 'beforeLoad-root-explicit' }, }), ) - - const rootMatch = router.stores.pendingMatches - .get() - .find((m) => m.routeId === routes[0].id) - - expect(rootMatch?.globalNotFound).toBe(true) - expect(rootMatch?.status).toBe('success') - expect(rootMatch?.error).toBeUndefined() }) - test('clears stale root globalNotFound on subsequent successful load', async () => { - const { router, routes } = setupScenario({ + test('clears stale root _notFound when the root loader is skipped on a successful reload', async () => { + const { router, routes, loaders } = setupScenario({ throwAtIndex: 3, parentFailures: {}, + skipRootLoaderOnReload: true, beforeLoadNotFoundFactory: (innerRoutes) => notFound({ routeId: innerRoutes[0].id as never, @@ -1788,87 +1764,31 @@ describe('head execution', () => { }), }) - const first = await runLoadMatchesAndCapture(router) - expect(first.error).toEqual(expect.objectContaining({ isNotFound: true })) + await router.load() + const initialRootMatch = router.state.matches.find( + (match) => match.routeId === routes[0].id, + ) + expect(initialRootMatch?._notFound).toBe(true) + expect(initialRootMatch?.error).toEqual( + expect.objectContaining({ + isNotFound: true, + data: { source: 'beforeLoad-root-explicit' }, + }), + ) + expect(loaders[0]).toHaveBeenCalledTimes(1) const throwingRoute = routes[3] throwingRoute.options.beforeLoad = undefined - const second = await runLoadMatchesAndCapture(router) - expect(second.error).toBeUndefined() - - const rootMatch = router.stores.pendingMatches - .get() - .find((m) => m.routeId === routes[0].id) - - expect(rootMatch?.globalNotFound).toBe(false) - }) - - test('clears stale root globalNotFound when root loader is skipped', async () => { - const rootLoader = vi.fn(() => ({ level: 0 })) - const rootRoute = new BaseRootRoute({ - loader: rootLoader, - staleTime: Infinity, - shouldReload: () => false, - }) - - const childLoader = vi.fn(() => ({ level: 1 })) - const childRoute = new BaseRoute({ - getParentRoute: () => rootRoute, - path: '/test', - loader: childLoader, - staleTime: Infinity, - shouldReload: () => false, - }) - - const routeTree = rootRoute.addChildren([childRoute]) - - const router = createTestRouter({ - routeTree, - history: createMemoryHistory({ initialEntries: ['/test'] }), - }) - - const first = await runLoadMatchesAndCapture(router) - expect(first.error).toBeUndefined() - expect(rootLoader).toHaveBeenCalledTimes(1) - - const staleRootNotFound = notFound({ data: { source: 'stale-root' } }) - const currentRootMatchId = router.stores.pendingMatches - .get() - .find((m) => m.routeId === rootRoute.id)!.id - - router.updateMatch(currentRootMatchId, (prev) => ({ - ...prev, - status: 'success', - globalNotFound: true, - error: staleRootNotFound, - })) - - const location = router.latestLocation - const matches = router.matchRoutes(location) - const pendingRootMatch = matches.find((m) => m.routeId === rootRoute.id)! - pendingRootMatch.status = 'success' - pendingRootMatch.globalNotFound = false - pendingRootMatch.error = undefined - router.stores.setPending(matches) - - await expect( - loadMatches({ - router, - location, - matches, - updateMatch: router.updateMatch, - }), - ).resolves.toBe(matches) - - expect(rootLoader).toHaveBeenCalledTimes(1) + await router.load() - const rootMatch = router.stores.pendingMatches - .get() - .find((m) => m.routeId === rootRoute.id) + const rootMatch = router.state.matches.find( + (m) => m.routeId === routes[0].id, + ) - expect(rootMatch?.globalNotFound).toBe(false) - expect(rootMatch?.error).toBeUndefined() + expect(rootMatch?._notFound).toBe(false) + expect(rootMatch?.status).toBe('success') + expect(loaders[0]).toHaveBeenCalledTimes(1) }) }) }) diff --git a/packages/router-core/tests/loader-architecture-regressions.test.ts b/packages/router-core/tests/loader-architecture-regressions.test.ts index 31ada9cc43..d849305244 100644 --- a/packages/router-core/tests/loader-architecture-regressions.test.ts +++ b/packages/router-core/tests/loader-architecture-regressions.test.ts @@ -4,101 +4,300 @@ import { BaseRootRoute, BaseRoute, createControlledPromise, + notFound, redirect, } from '../src' -import { createTestRouter } from './routerTestUtils' +import { createTestRouter, loadServerResponse } from './routerTestUtils' -test('a child can redirect after observing its parent loader error', async () => { +test.each([ + ['error', () => new Error('parent failed')], + ['notFound', () => notFound()], +] as const)( + 'a child can redirect after observing its parent loader %s', + async (expectedStatus, createFailure) => { + const rootRoute = new BaseRootRoute({}) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + loader: () => { + throw createFailure() + }, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loader: async ({ parentMatchPromise }) => { + const parent = await parentMatchPromise + if (parent.status === expectedStatus) { + throw redirect({ to: '/target' }) + } + return parent.status + }, + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + parentRoute.addChildren([childRoute]), + targetRoute, + ]), + history: createMemoryHistory({ + initialEntries: ['/parent/child'], + }), + }) + + await router.load() + + expect(router.state.location.pathname).toBe('/target') + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: targetRoute.id, + status: 'success', + }) + }, +) + +test.each(['client', 'server'] as const)( + 'a descendant self-abort cannot hide redirecting onError on the %s', + async (environment) => { + const parentGate = createControlledPromise() + const childGate = createControlledPromise() + const parentOnError = vi.fn() + const childOnError = vi.fn(() => { + throw redirect({ to: '/target' }) + }) + const rootRoute = new BaseRootRoute({}) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + loader: async () => { + await parentGate + throw new Error('parent failed') + }, + onError: parentOnError, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loader: async ({ abortController }) => { + await childGate + abortController.abort() + throw new Error('child failed') + }, + onError: childOnError, + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + parentRoute.addChildren([childRoute]), + targetRoute, + ]), + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + isServer: environment === 'server', + }) + + const loading = + environment === 'server' + ? loadServerResponse(router, '/parent/child') + : router.load() + parentGate.resolve() + await vi.waitFor(() => expect(parentOnError).toHaveBeenCalledOnce()) + childGate.resolve() + await vi.waitFor(() => expect(childOnError).toHaveBeenCalledOnce()) + + if (environment === 'server') { + const response = await loading + expect(response).toBeDefined() + if (!response) { + return + } + expect(response.status).toBe(307) + expect(response.headers.get('Location')).toBe('/target') + } else { + await loading + expect(router.state.location.pathname).toBe('/target') + } + }, +) + +test('superseding a load clears fetching state from the still-presented lane', async () => { + const firstGate = createControlledPromise() + const secondGate = createControlledPromise() + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + validateSearch: (search: Record) => ({ + revision: Number(search.revision ?? 0), + }), + beforeLoad: ({ search }) => (search.revision === 1 ? firstGate : undefined), + }) + const otherRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/other', + loader: () => secondGate, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute, otherRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + await router.load() + const firstNavigation = router.navigate({ + to: '/page', + search: { revision: 1 }, + }) + await vi.waitFor(() => + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: pageRoute.id, + isFetching: 'beforeLoad', + }), + ) + + const secondNavigation = router.navigate({ to: '/other' }) + await vi.waitFor(() => { + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: pageRoute.id, + isFetching: false, + }) + }) + + firstGate.resolve() + secondGate.resolve() + await Promise.all([firstNavigation, secondNavigation]) +}) + +test('an ignored preload flight is released after a navigation has planned its loaders', async () => { + const preloadFailure = createControlledPromise() + const childGate = createControlledPromise() + let preloadSignal: AbortSignal | undefined + const parentLoader = vi.fn( + ({ abortController }: { abortController: AbortController }) => { + const generation = parentLoader.mock.calls.length + if (generation === 1) { + return 'initial parent data' + } + preloadSignal = abortController.signal + return preloadFailure + }, + ) + const childLoader = vi.fn(() => childGate) const rootRoute = new BaseRootRoute({}) const parentRoute = new BaseRoute({ getParentRoute: () => rootRoute, path: '/parent', - loader: () => { - throw new Error('parent failed') - }, + shouldReload: ({ preload }) => preload, + loader: parentLoader, }) const childRoute = new BaseRoute({ getParentRoute: () => parentRoute, path: '/child', - loader: async ({ parentMatchPromise }) => { - const parent = await parentMatchPromise - if (parent.status === 'error') { - throw redirect({ to: '/target' }) - } - return parent.status - }, + loader: childLoader, }) - const targetRoute = new BaseRoute({ - getParentRoute: () => rootRoute, - path: '/target', + const siblingRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/sibling', }) const router = createTestRouter({ routeTree: rootRoute.addChildren([ - parentRoute.addChildren([childRoute]), - targetRoute, + parentRoute.addChildren([childRoute, siblingRoute]), ]), - history: createMemoryHistory({ - initialEntries: ['/parent/child'], - }), + history: createMemoryHistory({ initialEntries: ['/parent'] }), }) await router.load() + const preload = router.preloadRoute({ to: '/parent/sibling' }) + await vi.waitFor(() => expect(parentLoader).toHaveBeenCalledTimes(2)) - expect(router.state.location.pathname).toBe('/target') - expect(router.state.matches.at(-1)).toMatchObject({ - routeId: targetRoute.id, - status: 'success', - }) + const navigation = router.navigate({ to: '/parent/child' }) + await vi.waitFor(() => expect(childLoader).toHaveBeenCalledOnce()) + + preloadFailure.reject(new Error('discarded preload generation')) + await preload + expect(preloadSignal?.aborted).toBe(true) + + childGate.resolve('child data') + await navigation }) -test('a descendant self-abort cannot hide redirecting onError on the client', async () => { - const parentGate = createControlledPromise() - const childGate = createControlledPromise() - const parentOnError = vi.fn() - const childOnError = vi.fn(() => { - throw redirect({ to: '/target' }) - }) +test('an ordinary route-context failure still permits ancestor loaders', async () => { + const failure = new Error('child context failed') + const parentLoader = vi.fn(() => 'parent data') const rootRoute = new BaseRootRoute({}) const parentRoute = new BaseRoute({ getParentRoute: () => rootRoute, path: '/parent', - loader: async () => { - await parentGate - throw new Error('parent failed') - }, - onError: parentOnError, + loader: parentLoader, }) const childRoute = new BaseRoute({ getParentRoute: () => parentRoute, path: '/child', - loader: async ({ abortController }) => { - await childGate - abortController.abort() - throw new Error('child failed') + context: () => { + throw failure }, - onError: childOnError, }) - const targetRoute = new BaseRoute({ + const router = createTestRouter({ + routeTree: rootRoute.addChildren([parentRoute.addChildren([childRoute])]), + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + }) + + await router.load() + + expect(parentLoader).toHaveBeenCalledOnce() + expect( + router.state.matches.find((match) => match.routeId === parentRoute.id), + ).toMatchObject({ + status: 'success', + loaderData: 'parent data', + }) + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: childRoute.id, + status: 'error', + error: failure, + }) +}) + +test('a failed route context is recomputed when the route is retried', async () => { + const failure = new Error('context failed') + let shouldFail = true + let generation = 0 + const rootRoute = new BaseRootRoute({}) + const route = new BaseRoute({ getParentRoute: () => rootRoute, - path: '/target', + path: '/', + context: () => { + generation++ + if (shouldFail) { + throw failure + } + return { generation } + }, + loader: ({ context }) => context.generation, }) const router = createTestRouter({ - routeTree: rootRoute.addChildren([ - parentRoute.addChildren([childRoute]), - targetRoute, - ]), - history: createMemoryHistory({ initialEntries: ['/parent/child'] }), - isServer: false, + routeTree: rootRoute.addChildren([route]), + history: createMemoryHistory({ initialEntries: ['/'] }), }) - const loading = router.load() - parentGate.resolve() - await vi.waitFor(() => expect(parentOnError).toHaveBeenCalledOnce()) - childGate.resolve() - await vi.waitFor(() => expect(childOnError).toHaveBeenCalledOnce()) + await router.load() + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: route.id, + status: 'error', + error: failure, + }) - await loading - expect(router.state.location.pathname).toBe('/target') + shouldFail = false + await router.invalidate() + + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: route.id, + status: 'success', + loaderData: 2, + }) }) test('invalidation reruns the loader with same-id route context', async () => { @@ -133,6 +332,298 @@ test('invalidation reruns the loader with same-id route context', async () => { }) }) +test("a settled preload cannot abort a navigation's beforeLoad controller", async () => { + const suspended = createControlledPromise() + let navigationSignal: AbortSignal | undefined + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const guardedRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/guarded', + beforeLoad: ({ abortController, preload }) => { + if (!preload) { + navigationSignal = abortController.signal + } + throw suspended + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, guardedRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + const preload = router.preloadRoute({ to: '/guarded' }) + const navigation = router.navigate({ to: '/guarded' }) + + await preload + expect(navigationSignal?.aborted).toBe(false) + + await navigation +}) + +test('a fast background candidate remains private when a reentrant navigation wins', async () => { + let parentLoads = 0 + let childLoads = 0 + let activeSignal: AbortSignal | undefined + let replacementSignal: AbortSignal | undefined + let reentrantNavigation: Promise | undefined + + const rootRoute = new BaseRootRoute({}) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + validateSearch: (search: Record) => ({ + reload: Number(search.reload ?? 0), + }), + shouldReload: ({ location }) => + (location.search as { reload: number }).reload === 1, + loader: ({ abortController }) => { + parentLoads++ + if (parentLoads === 1) { + activeSignal = abortController.signal + } else { + replacementSignal = abortController.signal + } + return { revision: parentLoads } + }, + onStay: (match) => { + if (match.search.reload === 1 && !reentrantNavigation) { + reentrantNavigation = router.navigate({ + to: '/parent/child', + search: { reload: 2 }, + }) + } + }, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + shouldReload: ({ location }) => + (location.search as { reload: number }).reload === 1, + loader: () => { + childLoads++ + if (childLoads === 2) { + throw redirect({ to: '/target' }) + } + return 'child data' + }, + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + parentRoute.addChildren([childRoute]), + targetRoute, + ]), + history: createMemoryHistory({ + initialEntries: ['/parent/child?reload=0'], + }), + }) + + await router.load() + await router.navigate({ + to: '/parent/child', + search: { reload: 1 }, + }) + await reentrantNavigation + + expect(router.state.location.search).toEqual({ reload: 2 }) + expect( + router.state.matches.find((match) => match.routeId === parentRoute.id), + ).toMatchObject({ loaderData: { revision: 1 } }) + expect(parentLoads).toBe(2) + expect(activeSignal?.aborted).toBe(false) + expect(replacementSignal?.aborted).toBe(true) +}) + +test('a superseding navigation reuses a pending background loader after async beforeLoad', async () => { + const backgroundResult = createControlledPromise() + const beforeLoadStarted = createControlledPromise() + const beforeLoadGate = createControlledPromise() + const signals: Array = [] + let generation = 0 + + const rootRoute = new BaseRootRoute({}) + const route = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + validateSearch: (search: Record) => ({ + reload: Number(search.reload ?? 0), + }), + shouldReload: ({ location }) => + (location.search as { reload: number }).reload === 1 ? true : undefined, + beforeLoad: async ({ search }) => { + if (search.reload === 2) { + beforeLoadStarted.resolve() + await beforeLoadGate + } + }, + loader: { + staleReloadMode: 'background', + handler: ({ abortController }) => { + signals.push(abortController.signal) + generation++ + return generation === 2 ? backgroundResult : generation + }, + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([route]), + history: createMemoryHistory({ + initialEntries: ['/page?reload=0'], + }), + }) + + await router.load() + await router.navigate({ + to: '/page', + search: { reload: 1 }, + }) + await vi.waitFor(() => expect(generation).toBe(2)) + + const successor = router.navigate({ + to: '/page', + search: { reload: 2 }, + }) + await beforeLoadStarted + + backgroundResult.resolve(2) + beforeLoadGate.resolve() + await successor + + await vi.waitFor(() => { + expect(router.state.matches.at(-1)?.loaderData).toBe(2) + }) + expect(generation).toBe(2) + expect(signals[1]?.aborted).toBe(false) + expect(router.state.matches.at(-1)?.preload).toBe(false) +}) + +test('an ancestor beforeLoad failure releases an unconsumed background loader', async () => { + const backgroundResult = createControlledPromise() + const signals: Array = [] + let generation = 0 + + const rootRoute = new BaseRootRoute({}) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + validateSearch: (search: Record) => ({ + phase: String(search.phase ?? 'initial'), + }), + beforeLoad: ({ search }) => { + if (search.phase === 'fail') { + throw new Error('parent failed') + } + }, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + shouldReload: ({ location }) => + (location.search as { phase: string }).phase === 'refresh' + ? true + : undefined, + staleTime: Infinity, + loader: { + staleReloadMode: 'background', + handler: ({ abortController }) => { + signals.push(abortController.signal) + generation++ + return generation === 2 ? backgroundResult : generation + }, + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([parentRoute.addChildren([childRoute])]), + history: createMemoryHistory({ + initialEntries: ['/parent/child?phase=initial'], + }), + }) + + await router.load() + await router.navigate({ + to: '/parent/child', + search: { phase: 'refresh' }, + }) + await vi.waitFor(() => expect(generation).toBe(2)) + + await router.navigate({ + to: '/parent/child', + search: { phase: 'fail' }, + }) + + expect( + router.state.matches.find((match) => match.routeId === parentRoute.id), + ).toMatchObject({ status: 'error' }) + expect(signals[1]?.aborted).toBe(true) + + backgroundResult.resolve(2) + await router.navigate({ + to: '/parent/child', + search: { phase: 'recovered' }, + }) + + expect(generation).toBe(2) + expect(router.state.matches.at(-1)?.loaderData).toBe(1) +}) + +test('reentrant navigation from onError cancels stale serial work', async () => { + const failure = new Error('stale context failed') + const parentLoader = vi.fn(() => 'parent data') + let successor: Promise | undefined + + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + loader: parentLoader, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + context: () => { + throw failure + }, + onError: () => { + successor ??= router.navigate({ to: '/target' }) + }, + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + indexRoute, + parentRoute.addChildren([childRoute]), + targetRoute, + ]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + await router.navigate({ to: '/parent/child' }) + await successor + + expect(router.state.location.pathname).toBe('/target') + expect(router.state.matches.at(-1)?.routeId).toBe(targetRoute.id) + expect(router.state.matches.some((match) => match.error === failure)).toBe( + false, + ) + expect(parentLoader).not.toHaveBeenCalled() +}) + test('a joined descendant loader redirect still wins after an ancestor loader fails', async () => { const childStarted = createControlledPromise() const childRedirect = createControlledPromise() @@ -207,6 +698,157 @@ test('a joined descendant loader redirect still wins after an ancestor loader fa expect(childLoads).toBe(1) }) +test('an unrelated preload redirect does not override a navigation error', async () => { + const preloadChildStarted = createControlledPromise() + const releasePreloadRedirect = createControlledPromise() + const navigationFailure = new Error('navigation parent failed') + + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + validateSearch: (search: Record) => ({ + mode: String(search.mode ?? ''), + }), + loaderDeps: ({ search }) => ({ mode: search.mode }), + loader: ({ deps }) => { + if (deps.mode === 'navigation') { + throw navigationFailure + } + return 'preload parent data' + }, + errorComponent: () => null, + }) + const childLoader = vi.fn(async ({ deps }: { deps: { mode: string } }) => { + if (deps.mode === 'preload') { + preloadChildStarted.resolve() + await releasePreloadRedirect + throw redirect({ to: '/target' }) + } + return 'navigation child data' + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loaderDeps: ({ search }) => ({ mode: search.mode }), + loader: childLoader, + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + indexRoute, + parentRoute.addChildren([childRoute]), + targetRoute, + ]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + const preload = router.preloadRoute({ + to: '/parent/child', + search: { mode: 'preload' }, + }) + await preloadChildStarted + + await router.navigate({ + to: '/parent/child', + search: { mode: 'navigation' }, + }) + + expect(router.state.location).toMatchObject({ + pathname: '/parent/child', + search: { mode: 'navigation' }, + }) + expect( + router.state.matches.find((match) => match.routeId === parentRoute.id), + ).toMatchObject({ + status: 'error', + error: navigationFailure, + }) + expect(childLoader).toHaveBeenCalledTimes(2) + + releasePreloadRedirect.resolve() + const preloaded = await preload + + expect(preloaded?.at(-1)?.routeId).toBe(targetRoute.id) + expect(router.state.location).toMatchObject({ + pathname: '/parent/child', + search: { mode: 'navigation' }, + }) + expect( + router.state.matches.find((match) => match.routeId === parentRoute.id), + ).toMatchObject({ + status: 'error', + error: navigationFailure, + }) +}) + +test('a loaderDeps error is handled by the route error boundary', async () => { + const failure = new Error('loader deps failed') + const rootRoute = new BaseRootRoute({}) + const brokenRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/broken', + loaderDeps: (): Record => { + throw failure + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([brokenRoute]), + history: createMemoryHistory({ initialEntries: ['/broken'] }), + }) + + await router.load() + + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: brokenRoute.id, + status: 'error', + error: failure, + }) +}) + +test('loaderDeps redirects use the bounded navigation redirect policy', async () => { + const rootRoute = new BaseRootRoute({}) + const routes = Array.from( + { length: 23 }, + (_, index) => + new BaseRoute({ + getParentRoute: () => rootRoute, + path: `/step-${index}`, + loaderDeps: + index === 22 + ? undefined + : () => { + throw redirect({ to: `/step-${index + 1}` }) + }, + }), + ) + const router = createTestRouter({ + routeTree: rootRoute.addChildren(routes as any) as any, + history: createMemoryHistory({ initialEntries: ['/step-0'] }), + }) + + await router.load() + + expect(router.state.location.pathname).toBe('/step-20') + expect( + router.state.matches.find((match) => match.status !== 'success'), + ).toMatchObject({ + routeId: rootRoute.id, + status: 'error', + error: expect.objectContaining({ + message: 'Too many redirects', + }), + }) +}) + test('a reentrant user navigation starts a fresh redirect chain', async () => { let navigateFresh: () => void const rootRoute = new BaseRootRoute({}) @@ -255,3 +897,291 @@ test('a reentrant user navigation starts a fresh redirect chain', async () => { status: 'success', }) }) + +test('a superseded load settles without waiting for stale asset projection', async () => { + const slowHeadStarted = createControlledPromise() + const slowHead = createControlledPromise<{ + meta: Array<{ title: string }> + }>() + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const slowRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/slow', + head: () => { + slowHeadStarted.resolve() + return slowHead + }, + scripts: () => [{ children: 'window.route = "slow"' }], + headers: () => ({ 'x-route': 'slow' }), + }) + const fastRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/fast', + head: () => ({ meta: [{ title: 'fast' }] }), + scripts: () => [{ children: 'window.route = "fast"' }], + headers: () => ({ 'x-route': 'fast' }), + }) + const history = createMemoryHistory({ initialEntries: ['/'] }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, slowRoute, fastRoute]), + history, + }) + + await router.load() + history.push('/slow') + const slowLoad = router.load() + await slowHeadStarted + + history.push('/fast') + const fastLoad = router.load() + await fastLoad + + const settledBeforeProjection = await Promise.race([ + slowLoad.then(() => true), + new Promise((resolve) => setTimeout(() => resolve(false), 100)), + ]) + + slowHead.resolve({ meta: [{ title: 'slow' }] }) + await slowLoad + await Promise.resolve() + + expect(settledBeforeProjection).toBe(true) + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: fastRoute.id, + meta: [{ title: 'fast' }], + scripts: [{ children: 'window.route = "fast"' }], + }) +}) + +test('server response headers stop at the terminal render boundary', async () => { + let failParent = false + const rootRoute = new BaseRootRoute({ + headers: () => ({ 'x-root': 'root' }), + }) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + loader: () => { + if (failParent) { + throw new Error('parent failed') + } + }, + headers: () => ({ 'x-parent': 'parent' }), + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + headers: () => ({ 'x-child': 'child' }), + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([parentRoute.addChildren([childRoute])]), + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + isServer: true, + }) + + await router.load() + expect(router.state.matches.at(-1)?.headers).toEqual({ 'x-child': 'child' }) + + failParent = true + const response = await loadServerResponse(router, '/parent/child') + + expect(router.state.matches).toHaveLength(3) + expect(router.state.matches[1]).toMatchObject({ + routeId: parentRoute.id, + status: 'error', + }) + expect(response.headers.get('x-root')).toBe('root') + expect(response.headers.get('x-parent')).toBe('parent') + expect(response.headers.get('x-child')).toBeNull() +}) + +test('a superseded load settles without waiting for its normal component chunk', async () => { + const chunkStarted = createControlledPromise() + const chunk = createControlledPromise() + const onError = vi.fn() + const component = Object.assign(() => null, { + preload: () => { + chunkStarted.resolve() + return chunk + }, + }) + const rootRoute = new BaseRootRoute({}) + const slowRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/slow', + component, + onError, + }) + const fastRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/fast', + }) + const history = createMemoryHistory({ initialEntries: ['/slow'] }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([slowRoute, fastRoute]), + history, + }) + + const slowLoad = router.load() + await chunkStarted + history.push('/fast') + await router.load() + + expect( + await Promise.race([ + slowLoad.then(() => true), + new Promise((resolve) => setTimeout(() => resolve(false), 100)), + ]), + ).toBe(true) + expect(router.state.matches.at(-1)?.routeId).toBe(fastRoute.id) + + chunk.reject(new Error('obsolete chunk failed')) + await Promise.resolve() + expect(onError).not.toHaveBeenCalled() +}) + +test('a superseded load settles without waiting for its terminal boundary chunk', async () => { + const chunkStarted = createControlledPromise() + const chunk = createControlledPromise() + const loaderError = new Error('loader failed') + const onError = vi.fn() + const errorComponent = Object.assign(() => null, { + preload: () => { + chunkStarted.resolve() + return chunk + }, + }) + const rootRoute = new BaseRootRoute({}) + const slowRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/slow', + loader: () => { + throw loaderError + }, + errorComponent, + onError, + }) + const fastRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/fast', + }) + const history = createMemoryHistory({ initialEntries: ['/slow'] }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([slowRoute, fastRoute]), + history, + }) + + const slowLoad = router.load() + await chunkStarted + history.push('/fast') + await router.load() + + expect( + await Promise.race([ + slowLoad.then(() => true), + new Promise((resolve) => setTimeout(() => resolve(false), 100)), + ]), + ).toBe(true) + expect(router.state.matches.at(-1)?.routeId).toBe(fastRoute.id) + + chunk.reject(new Error('obsolete boundary chunk failed')) + await Promise.resolve() + expect(onError).toHaveBeenCalledOnce() + expect(onError).toHaveBeenCalledWith(loaderError) +}) + +test('a stale shouldReload cannot continue a superseded preload', async () => { + const loader = vi.fn(() => 'cached') + const onError = vi.fn() + let reenter = false + let winner: Promise | undefined + let router: ReturnType + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const cachedRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/cached', + preloadStaleTime: 0, + shouldReload: () => { + if (reenter) { + router.history.push('/winner') + winner = router.load() + throw new Error('obsolete shouldReload failure') + } + return true + }, + loader, + onError, + }) + const winnerRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/winner', + }) + router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, cachedRoute, winnerRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + await router.preloadRoute({ to: '/cached' }) + reenter = true + await router.preloadRoute({ to: '/cached' }) + await winner + + expect(router.state.location.pathname).toBe('/winner') + expect(router.state.matches.at(-1)?.routeId).toBe(winnerRoute.id) + expect(loader).toHaveBeenCalledOnce() + // Preload lanes run to completion despite the navigation, so the stale + // shouldReload's failure surfaces through onError (matching pre-rewrite + // behavior for preload errors) — but it cannot restart the loader or + // disturb the winning navigation. + expect(onError).toHaveBeenCalledOnce() +}) + +test('an onLeave navigation to the in-flight destination runs a fresh lane', async () => { + const beforeLoad = vi.fn() + const firstEnter = vi.fn() + const firstStay = vi.fn() + let successor: Promise | undefined + let redirected = false + let router: ReturnType + + const rootRoute = new BaseRootRoute({}) + const fromRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/from', + onLeave: () => { + if (!redirected) { + redirected = true + successor = router.navigate({ to: '/first' }) + } + }, + }) + const firstRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/first', + beforeLoad, + onEnter: firstEnter, + onStay: firstStay, + }) + router = createTestRouter({ + routeTree: rootRoute.addChildren([fromRoute, firstRoute]), + history: createMemoryHistory({ initialEntries: ['/from'] }), + }) + + await router.load() + await router.navigate({ to: '/first' }) + await successor + + expect(router.state.location.pathname).toBe('/first') + expect(router.state.matches.at(-1)?.routeId).toBe(firstRoute.id) + expect(beforeLoad).toHaveBeenCalledTimes(2) + expect(firstEnter.mock.calls.length + firstStay.mock.calls.length).toBe(1) +}) diff --git a/packages/router-core/tests/loader-self-abort.test.ts b/packages/router-core/tests/loader-self-abort.test.ts index c1c1885759..68916b1d68 100644 --- a/packages/router-core/tests/loader-self-abort.test.ts +++ b/packages/router-core/tests/loader-self-abort.test.ts @@ -1,59 +1,73 @@ import { expect, test, vi } from 'vitest' import { createMemoryHistory } from '@tanstack/history' import { BaseRootRoute, BaseRoute } from '../src' -import { createTestRouter } from './routerTestUtils' +import { createTestRouter, loadServerResponse } from './routerTestUtils' -test('a loader can fulfill after aborting its controller with isServer=false', async () => { - const rootRoute = new BaseRootRoute({}) - const pageRoute = new BaseRoute({ - getParentRoute: () => rootRoute, - path: '/page', - loader: ({ abortController }) => { - abortController.abort() - return 'accepted data' - }, - }) - const router = createTestRouter({ - routeTree: rootRoute.addChildren([pageRoute]), - history: createMemoryHistory({ initialEntries: ['/page'] }), - isServer: false, - }) +test.each([false, true])( + 'a loader can fulfill after aborting its controller with isServer=%s', + async (isServer) => { + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + loader: ({ abortController }) => { + abortController.abort() + return 'accepted data' + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + isServer, + }) - await router.load() + if (isServer) { + expect((await loadServerResponse(router, '/page')).status).toBe(200) + } else { + await router.load() + } - expect(router.state.matches.at(-1)).toMatchObject({ - routeId: pageRoute.id, - status: 'success', - loaderData: 'accepted data', - }) -}) + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: pageRoute.id, + status: 'success', + loaderData: 'accepted data', + }) + }, +) -test('a loader rejection remains an error after aborting its controller with isServer=false', async () => { - const failure = new Error('loader failed after aborting its controller') - const onError = vi.fn() - const rootRoute = new BaseRootRoute({}) - const pageRoute = new BaseRoute({ - getParentRoute: () => rootRoute, - path: '/page', - loader: ({ abortController }) => { - abortController.abort() - throw failure - }, - errorComponent: () => null, - onError, - }) - const router = createTestRouter({ - routeTree: rootRoute.addChildren([pageRoute]), - history: createMemoryHistory({ initialEntries: ['/page'] }), - isServer: false, - }) +test.each([false, true])( + 'a loader rejection remains an error after aborting its controller with isServer=%s', + async (isServer) => { + const failure = new Error('loader failed after aborting its controller') + const onError = vi.fn() + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + loader: ({ abortController }) => { + abortController.abort() + throw failure + }, + errorComponent: () => null, + onError, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + isServer, + }) - await router.load() + if (isServer) { + expect((await loadServerResponse(router, '/page')).status).toBe(500) + } else { + await router.load() + } - expect(router.state.matches.at(-1)).toMatchObject({ - routeId: pageRoute.id, - status: 'error', - error: failure, - }) - expect(onError).toHaveBeenCalledWith(failure) -}) + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: pageRoute.id, + status: 'error', + error: failure, + }) + expect(onError).toHaveBeenCalledWith(failure) + }, +) diff --git a/packages/router-core/tests/loader-thrown-promise.test.ts b/packages/router-core/tests/loader-thrown-promise.test.ts new file mode 100644 index 0000000000..e45eede84f --- /dev/null +++ b/packages/router-core/tests/loader-thrown-promise.test.ts @@ -0,0 +1,57 @@ +import { expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute } from '../src' +import { createTestRouter, loadServerResponse } from './routerTestUtils' + +test.each([ + ['beforeLoad', false], + ['beforeLoad', true], + ['loader', false], + ['loader', true], +] as const)( + 'a synchronously thrown %s Promise has the same meaning with isServer=%s', + async (hook, isServer) => { + const thrown = Promise.resolve('not loader data') + const onError = vi.fn() + const rootRoute = new BaseRootRoute({}) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + beforeLoad: + hook === 'beforeLoad' + ? () => { + throw thrown + } + : undefined, + loader: + hook === 'loader' + ? () => { + throw thrown + } + : undefined, + onError, + errorComponent: () => null, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([targetRoute]), + history: createMemoryHistory({ initialEntries: ['/target'] }), + isServer, + }) + + if (isServer) { + const response = await loadServerResponse(router, '/target') + expect(response.status).toBe(500) + } else { + await router.load() + } + + const error = router.state.matches.at(-1)?.error + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: targetRoute.id, + status: 'error', + }) + expect(error).toBeInstanceOf(Error) + expect((error as Error).cause).toBe(thrown) + expect(onError).toHaveBeenCalledWith(error) + }, +) diff --git a/packages/router-core/tests/masked-location-state-commit.test.ts b/packages/router-core/tests/masked-location-state-commit.test.ts index 78e40856a2..9738b42261 100644 --- a/packages/router-core/tests/masked-location-state-commit.test.ts +++ b/packages/router-core/tests/masked-location-state-commit.test.ts @@ -1,49 +1,55 @@ -import { expect, test } from 'vitest' +import { afterEach, describe, expect, test, vi } from 'vitest' import { createMemoryHistory } from '@tanstack/history' import { BaseRootRoute, BaseRoute } from '../src' import { createTestRouter } from './routerTestUtils' -test('a same-href navigation clears an expired mask from history state', async () => { - const makeRoutes = () => { - const rootRoute = new BaseRootRoute({}) - const indexRoute = new BaseRoute({ - getParentRoute: () => rootRoute, - path: '/', - }) - const realRoute = new BaseRoute({ - getParentRoute: () => rootRoute, - path: '/real', - }) - const prettyRoute = new BaseRoute({ - getParentRoute: () => rootRoute, - path: '/pretty', +afterEach(() => { + vi.restoreAllMocks() +}) + +describe('masked location remnants in history state', () => { + test('a same-href navigation clears an expired mask from history state', async () => { + const makeRoutes = () => { + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const realRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/real', + }) + const prettyRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/pretty', + }) + return rootRoute.addChildren([indexRoute, realRoute, prettyRoute]) + } + const history = createMemoryHistory({ initialEntries: ['/'] }) + + const router1 = createTestRouter({ + routeTree: makeRoutes(), + history, + unmaskOnReload: true, }) - return rootRoute.addChildren([indexRoute, realRoute, prettyRoute]) - } - const history = createMemoryHistory({ initialEntries: ['/'] }) + await router1.load() + await router1.navigate({ to: '/real', mask: { to: '/pretty' } }) + expect(history.location.state.__tempLocation).toBeDefined() - const router1 = createTestRouter({ - routeTree: makeRoutes(), - history, - unmaskOnReload: true, - }) - await router1.load() - await router1.navigate({ to: '/real', mask: { to: '/pretty' } }) - expect(history.location.state.__tempLocation).toBeDefined() + // A fresh router over the same history models a page reload: the + // per-session temp key no longer matches, so the mask has expired and the + // literal URL resolves. + const router2 = createTestRouter({ + routeTree: makeRoutes(), + history, + unmaskOnReload: true, + }) + await router2.load() + expect(router2.state.location.pathname).toBe('/pretty') - // A fresh router over the same history models a page reload: the - // per-session temp key no longer matches, so the mask has expired and the - // literal URL resolves. - const router2 = createTestRouter({ - routeTree: makeRoutes(), - history, - unmaskOnReload: true, + // Navigating to the same href must still commit so the expired mask + // payload is dropped from history state, matching pre-lane behavior. + await router2.navigate({ to: '/pretty' }) + expect(history.location.state.__tempLocation).toBeUndefined() }) - await router2.load() - expect(router2.state.location.pathname).toBe('/pretty') - - // Navigating to the same href must still commit so the expired mask - // payload is dropped from history state, matching pre-lane behavior. - await router2.navigate({ to: '/pretty' }) - expect(history.location.state.__tempLocation).toBeUndefined() }) diff --git a/packages/router-core/tests/parent-match-promise.test.ts b/packages/router-core/tests/parent-match-promise.test.ts index 1e235cd9f5..5513067027 100644 --- a/packages/router-core/tests/parent-match-promise.test.ts +++ b/packages/router-core/tests/parent-match-promise.test.ts @@ -1,4 +1,4 @@ -import { expect, test } from 'vitest' +import { expect, test, vi } from 'vitest' import { createMemoryHistory } from '@tanstack/history' import { BaseRootRoute, BaseRoute } from '../src' import { createTestRouter } from './routerTestUtils' @@ -40,6 +40,49 @@ test.each([false, true])( }, ) +test('client preload=false parentMatchPromise exposes the skipped pending parent', async () => { + const seen: Array = [] + const parentLoader = vi.fn() + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + preload: false, + loader: parentLoader, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loader: async ({ parentMatchPromise }) => { + seen.push(await parentMatchPromise) + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + indexRoute, + parentRoute.addChildren([childRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/'] }), + isServer: false, + }) + + await router.preloadRoute({ to: '/parent/child' }) + + expect(parentLoader).not.toHaveBeenCalled() + expect(seen).toHaveLength(1) + expect(seen[0]).toMatchObject({ + routeId: parentRoute.id, + status: 'pending', + invalid: false, + preload: false, + isFetching: false, + }) +}) + test('server parentMatchPromise exposes the error selected by onError', async () => { const original = new Error('original') const selected = new Error('selected') diff --git a/packages/router-core/tests/preflight-reentrant-context.test.ts b/packages/router-core/tests/preflight-reentrant-context.test.ts new file mode 100644 index 0000000000..415e5f0e50 --- /dev/null +++ b/packages/router-core/tests/preflight-reentrant-context.test.ts @@ -0,0 +1,101 @@ +import { expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute } from '../src' +import { createTestRouter } from './routerTestUtils' + +test('synchronous navigation from route context stops abandoned descendant context work', async () => { + let redirected = false + let redirectNavigation: Promise | undefined + const childContext = vi.fn(() => ({ child: true })) + + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + context: ({ navigate }) => { + if (!redirected) { + redirected = true + redirectNavigation = navigate({ to: '/target' }) + } + return { parent: true } + }, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + context: childContext, + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + indexRoute, + parentRoute.addChildren([childRoute]), + targetRoute, + ]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + await router.navigate({ to: '/parent/child' }) + await redirectNavigation + + expect(router.state.location.pathname).toBe('/target') + expect(childContext).not.toHaveBeenCalled() +}) + +test('synchronous navigation from preloaded route context lets the preload finish quietly', async () => { + let redirected = false + let redirectNavigation: Promise | undefined + const childContext = vi.fn(() => ({ child: true })) + + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + context: ({ navigate, preload }) => { + if (preload && !redirected) { + redirected = true + redirectNavigation = navigate({ to: '/target' }) + } + return { parent: true } + }, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + context: childContext, + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + indexRoute, + parentRoute.addChildren([childRoute]), + targetRoute, + ]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + await router.preloadRoute({ to: '/parent/child' }) + await redirectNavigation + + expect(router.state.location.pathname).toBe('/target') + // Preload lanes are not cancelled by navigations: the descendant context + // still computes (and may seed the cache), while the navigation wins the + // presented state. Only navigation lanes stop abandoned descendant work. + expect(childContext).toHaveBeenCalledOnce() +}) diff --git a/packages/router-core/tests/preflight-signal-currentness.test.ts b/packages/router-core/tests/preflight-signal-currentness.test.ts new file mode 100644 index 0000000000..23519edfec --- /dev/null +++ b/packages/router-core/tests/preflight-signal-currentness.test.ts @@ -0,0 +1,49 @@ +import { expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute, redirect } from '../src' +import { createTestRouter } from './routerTestUtils' + +test('a route-context redirect retires its matching generation before following the redirect', async () => { + let sourceSignal: AbortSignal | undefined + const sourceBeforeLoad = vi.fn() + const sourceLoader = vi.fn() + const targetLoader = vi.fn(() => 'target') + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const sourceRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/source', + context: ({ abortController }) => { + sourceSignal = abortController.signal + throw redirect({ to: '/target' }) + }, + beforeLoad: sourceBeforeLoad, + loader: sourceLoader, + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + loader: targetLoader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, sourceRoute, targetRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + await router.navigate({ to: '/source' }) + + expect(router.state.location.pathname).toBe('/target') + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: targetRoute.id, + status: 'success', + loaderData: 'target', + }) + expect(sourceSignal?.aborted).toBe(true) + expect(sourceBeforeLoad).not.toHaveBeenCalled() + expect(sourceLoader).not.toHaveBeenCalled() + expect(targetLoader).toHaveBeenCalledOnce() +}) diff --git a/packages/router-core/tests/preload-adoption.test.ts b/packages/router-core/tests/preload-adoption.test.ts index 4742278aca..94d31e2e94 100644 --- a/packages/router-core/tests/preload-adoption.test.ts +++ b/packages/router-core/tests/preload-adoption.test.ts @@ -7,6 +7,16 @@ afterEach(() => { vi.useRealTimers() }) +/** + * Preload adoption edge cases. The happy path (navigation adopts an + * in-flight preload's successful loader run) and the control-flow + * non-leakage path are pinned in load.test.ts; this file pins the + * adoption boundary: a donor must have its loader genuinely in flight. A + * preload still in its serial phase can itself be waiting on the navigation + * through the borrow protocol, while a stale successful donor can still have + * fresh loader work pending behind its cached snapshot. + */ + describe('preload adoption', () => { test('navigation waits for fresh data from an in-flight stale preload revalidation', async () => { vi.useFakeTimers() @@ -86,6 +96,110 @@ describe('preload adoption', () => { ).toEqual({ notifications: ['fresh'] }) }) + test('navigation runs beforeLoad independently of a preload serial phase', async () => { + const beforeLoadGate = createControlledPromise() + const preloadSerialStarted = createControlledPromise() + let beforeLoadCalls = 0 + const loader = vi.fn(() => 'data') + + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const fooRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/foo', + beforeLoad: async () => { + beforeLoadCalls++ + if (beforeLoadCalls === 1) { + // Keep the shared lane's serial phase in flight. + preloadSerialStarted.resolve() + await beforeLoadGate + } + }, + loader, + }) + + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, fooRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + + // Preload is stuck in its serial phase — its loader has NOT started. + const preload = router.preloadRoute({ to: '/foo' } as any) + await preloadSerialStarted + expect(beforeLoadCalls).toBe(1) + + const navigation = router.navigate({ to: '/foo' }) + await vi.waitFor(() => expect(beforeLoadCalls).toBe(2)) + expect(loader).toHaveBeenCalledTimes(1) + + beforeLoadGate.resolve() + await Promise.all([navigation, preload]) + expect(beforeLoadCalls).toBe(2) + expect(loader).toHaveBeenCalledTimes(1) + expect( + router.state.matches.find((match) => match.routeId === fooRoute.id) + ?.status, + ).toBe('success') + }) + + test("a sibling preload shares another preload lane's in-flight loader", async () => { + const loaderGate = createControlledPromise() + const loaderStarted = createControlledPromise() + let preloadBeforeLoadCalls = 0 + const loader = vi.fn(() => { + loaderStarted.resolve() + return loaderGate + }) + + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const fooRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/foo', + preloadStaleTime: Infinity, + beforeLoad: ({ preload }) => { + if (preload) { + preloadBeforeLoadCalls++ + } + }, + loader, + }) + + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, fooRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + + const first = router.preloadRoute({ to: '/foo' } as any) + await loaderStarted + + const second = router.preloadRoute({ to: '/foo' } as any) + await vi.waitFor(() => expect(preloadBeforeLoadCalls).toBe(2)) + expect(loader).toHaveBeenCalledTimes(1) + + loaderGate.resolve('once') + await Promise.all([first, second]) + expect(loader).toHaveBeenCalledTimes(1) + + await router.navigate({ to: '/foo' }) + + expect(loader).toHaveBeenCalledTimes(1) + expect( + router.state.matches.find((match) => match.routeId === fooRoute.id) + ?.loaderData, + ).toBe('once') + }) + test('a fulfilled undefined loader result is adopted as success', async () => { const loaderGate = createControlledPromise() const loaderStarted = createControlledPromise() @@ -130,4 +244,124 @@ describe('preload adoption', () => { expect(match?.status).toBe('success') expect(match?.loaderData).toBeUndefined() }) + + test('navigation shares a failed pending preload without starving sibling work', async () => { + const preloadParentStarted = createControlledPromise() + const preloadFailureGate = createControlledPromise() + const childStarted = createControlledPromise() + const childGate = createControlledPromise() + const parentFailure = new Error('preload failed') + let parentLoads = 0 + let childLoads = 0 + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + loader: async ({ preload }) => { + parentLoads++ + if (preload) { + preloadParentStarted.resolve() + await preloadFailureGate + throw parentFailure + } + return 'navigation data' + }, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loader: async () => { + childLoads++ + childStarted.resolve() + return childGate + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + indexRoute, + parentRoute.addChildren([childRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + const preload = router.preloadRoute({ to: '/parent/child' } as any) + await Promise.all([preloadParentStarted, childStarted]) + expect(parentLoads).toBe(1) + expect(childLoads).toBe(1) + + const navigation = router.navigate({ to: '/parent/child' }) + await Promise.resolve() + expect(preloadFailureGate.status).toBe('pending') + expect(parentLoads).toBe(1) + expect(childLoads).toBe(1) + + preloadFailureGate.resolve() + childGate.resolve('child data') + await Promise.all([navigation, preload]) + + expect(parentLoads).toBe(1) + expect(childLoads).toBe(1) + expect(router.state.location.pathname).toBe('/parent/child') + expect( + router.state.matches.find((match) => match.routeId === parentRoute.id), + ).toMatchObject({ + status: 'error', + error: parentFailure, + }) + expect( + router.state.matches.find((match) => match.routeId === childRoute.id), + ).toMatchObject({ + status: 'success', + loaderData: 'child data', + }) + }) + + test('a route with preload disabled does not discard its preloaded ancestor', async () => { + const parentLoader = vi.fn(() => 'parent data') + const childLoader = vi.fn(() => 'child data') + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + preloadStaleTime: Infinity, + loader: parentLoader, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + preload: false, + loader: childLoader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + indexRoute, + parentRoute.addChildren([childRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + await router.preloadRoute({ to: '/parent/child' } as any) + await router.navigate({ to: '/parent/child' }) + + expect(parentLoader).toHaveBeenCalledTimes(1) + expect(childLoader).toHaveBeenCalledTimes(1) + expect( + router.state.matches.find((match) => match.routeId === parentRoute.id) + ?.loaderData, + ).toBe('parent data') + expect( + router.state.matches.find((match) => match.routeId === childRoute.id) + ?.loaderData, + ).toBe('child data') + }) }) diff --git a/packages/router-core/tests/preload-beforeload-reuse.test.ts b/packages/router-core/tests/preload-beforeload-reuse.test.ts index 9a15d00282..c9d70bae5d 100644 --- a/packages/router-core/tests/preload-beforeload-reuse.test.ts +++ b/packages/router-core/tests/preload-beforeload-reuse.test.ts @@ -1,7 +1,7 @@ import { afterEach, describe, expect, test, vi } from 'vitest' import { createMemoryHistory } from '@tanstack/history' import { BaseRootRoute, BaseRoute, createControlledPromise } from '../src' -import { createTestRouter } from './routerTestUtils' +import { createTestRouter, loadServerResponse } from './routerTestUtils' afterEach(() => { vi.useRealTimers() @@ -90,6 +90,49 @@ describe('preloaded loader reuse with fresh beforeLoad context', () => { }) }) + test('reruns beforeLoad while sharing an identical active preload loader', async () => { + const loaderGate = createControlledPromise() + const beforeLoad = vi.fn(({ preload }: { preload: boolean }) => ({ + guard: preload ? 'preloaded' : 'loaded', + })) + const loader = vi.fn(() => loaderGate) + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const guardedRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/guarded', + preloadStaleTime: Infinity, + beforeLoad, + loader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, guardedRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + const preload = router.preloadRoute({ to: '/guarded' }) + await vi.waitFor(() => expect(loader).toHaveBeenCalledTimes(1)) + + const navigation = router.navigate({ to: '/guarded' }) + await vi.waitFor(() => expect(beforeLoad).toHaveBeenCalledTimes(2)) + expect(beforeLoad.mock.calls.map(([context]) => context.preload)).toEqual([ + true, + false, + ]) + expect(loader).toHaveBeenCalledTimes(1) + + loaderGate.resolve('shared loader data') + await Promise.all([preload, navigation]) + + expect(loader).toHaveBeenCalledTimes(1) + expect(router.state.matches.at(-1)?.context).toEqual({ guard: 'loaded' }) + expect(router.state.matches.at(-1)?.loaderData).toBe('shared loader data') + }) + test.each([ { age: 50, expected: [false, true, false], guard: 'loaded' }, { age: 100, expected: [false, true, false], guard: 'loaded' }, @@ -364,7 +407,9 @@ describe('preloaded loader reuse with fresh beforeLoad context', () => { generation, preload, })) - const loader = vi.fn(() => loaderGate) + const loader = vi.fn(() => + loader.mock.calls.length === 1 ? loaderGate : 'new loader data', + ) const rootRoute = new BaseRootRoute({}) const indexRoute = new BaseRoute({ getParentRoute: () => rootRoute, @@ -398,12 +443,12 @@ describe('preloaded loader reuse with fresh beforeLoad context', () => { true, false, ]) - expect(loader).toHaveBeenCalledOnce() + expect(loader).toHaveBeenCalledTimes(2) expect(router.state.matches.at(-1)?.context).toEqual({ generation: 2, preload: false, }) - expect(router.state.matches.at(-1)?.loaderData).toBe('shared loader data') + expect(router.state.matches.at(-1)?.loaderData).toBe('new loader data') }) test('preload false does not cache beforeLoad context for navigation', async () => { @@ -440,4 +485,185 @@ describe('preloaded loader reuse with fresh beforeLoad context', () => { expect(seen).toEqual([true, false]) expect(loader).toHaveBeenCalledTimes(1) }) + + test('shouldReload remains scoped to loader data', async () => { + const beforeLoad = vi.fn(({ preload }: { preload: boolean }) => ({ + guard: preload ? 'preloaded' : 'loaded', + })) + const loader = vi.fn(({ preload }: { preload: boolean }) => preload) + const shouldReload = vi.fn(() => true) + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const guardedRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/guarded', + staleTime: Infinity, + beforeLoad, + shouldReload, + loader: { + staleReloadMode: 'blocking', + handler: loader, + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, guardedRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + await router.preloadRoute({ to: '/guarded' }) + await router.navigate({ to: '/guarded' }) + + expect(beforeLoad).toHaveBeenCalledTimes(2) + expect(loader).toHaveBeenCalledTimes(2) + expect(loader.mock.calls.map(([context]) => context.preload)).toEqual([ + true, + false, + ]) + expect(shouldReload).toHaveBeenCalledTimes(1) + expect(shouldReload).toHaveBeenCalledWith( + expect.objectContaining({ + context: expect.objectContaining({ guard: 'loaded' }), + preload: false, + }), + ) + }) + + test('shouldReload false does not keep stale beforeLoad context', async () => { + vi.useFakeTimers() + vi.setSystemTime(1_000) + const beforeLoad = vi.fn(({ preload }: { preload: boolean }) => ({ + guard: preload ? 'preloaded' : 'loaded', + })) + const loader = vi.fn(({ preload }: { preload: boolean }) => preload) + const shouldReload = vi.fn(() => false) + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const guardedRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/guarded', + staleTime: Infinity, + preloadStaleTime: 100, + beforeLoad, + shouldReload, + loader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, guardedRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + await router.preloadRoute({ to: '/guarded' }) + vi.setSystemTime(1_100) + await router.navigate({ to: '/guarded' }) + + expect(beforeLoad.mock.calls.map(([context]) => context.preload)).toEqual([ + true, + false, + ]) + expect(loader.mock.calls.map(([context]) => context.preload)).toEqual([ + true, + ]) + expect(shouldReload).toHaveBeenCalledTimes(1) + expect(router.state.matches.at(-1)?.context).toEqual({ guard: 'loaded' }) + }) + + test.each([false, true])( + 'a beforeLoad failure clears context from the previous generation with isServer=%s', + async (isServer) => { + const failure = new Error('guard failed') + let fail = false + const rootRoute = new BaseRootRoute({}) + const guardedRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/guarded', + context: () => ({ routeContext: true }), + beforeLoad: () => { + if (fail) { + throw failure + } + return { guard: 'accepted' } + }, + errorComponent: () => null, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([guardedRoute]), + history: createMemoryHistory({ initialEntries: ['/guarded'] }), + isServer, + }) + + await router.load() + expect(router.state.matches.at(-1)?.context).toEqual({ + routeContext: true, + guard: 'accepted', + }) + + fail = true + if (isServer) { + const response = await loadServerResponse(router, '/guarded') + expect(response.status).toBe(500) + } else { + await router.load() + } + + const failedMatch = router.state.matches.at(-1) + expect(failedMatch).toMatchObject({ + status: 'error', + error: failure, + }) + expect(failedMatch?.context).toEqual({ routeContext: true }) + }, + ) + + test.each([false, true])( + 'a validation failure clears context from the previous generation with isServer=%s', + async (isServer) => { + let fail = false + const rootRoute = new BaseRootRoute({}) + const guardedRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/guarded', + validateSearch: () => { + if (fail) { + throw new Error('invalid search') + } + return {} + }, + context: () => ({ routeContext: true }), + beforeLoad: () => ({ guard: 'accepted' }), + errorComponent: () => null, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([guardedRoute]), + history: createMemoryHistory({ initialEntries: ['/guarded'] }), + isServer, + }) + + await router.load() + expect(router.state.matches.at(-1)?.context).toEqual({ + routeContext: true, + guard: 'accepted', + }) + fail = true + if (isServer) { + const response = await loadServerResponse(router, '/guarded') + expect(response.status).toBe(500) + } else { + await router.load() + } + + const failedMatch = router.state.matches.at(-1) + expect(failedMatch).toMatchObject({ + status: 'error', + }) + expect(failedMatch?.context).toEqual({ routeContext: true }) + }, + ) }) diff --git a/packages/router-core/tests/preload-navigation-adoption.test.ts b/packages/router-core/tests/preload-navigation-adoption.test.ts index 4d656bb16d..058c401f30 100644 --- a/packages/router-core/tests/preload-navigation-adoption.test.ts +++ b/packages/router-core/tests/preload-navigation-adoption.test.ts @@ -3,7 +3,85 @@ import { createMemoryHistory } from '@tanstack/history' import { BaseRootRoute, BaseRoute, createControlledPromise } from '../src' import { createTestRouter } from './routerTestUtils' +// A navigation that adopts an in-flight preload's loader run must reuse that +// run: it must not abort the preload's loader signal or rerun the loader. This +// is generic router adoption coverage; issue #4476 is covered separately with +// the React Query observer-unmount sequence from its reproduction. describe('navigation adopting an in-flight preload', () => { + test('adopted preload loader runs once and its signal is not aborted', async () => { + const loaderGate = createControlledPromise() + const loaderStarted = createControlledPromise() + const beforeLoad = vi.fn() + let preloadSignal: AbortSignal | undefined + const loader = vi.fn( + ({ abortController }: { abortController: AbortController }) => { + preloadSignal = abortController.signal + loaderStarted.resolve() + return loaderGate + }, + ) + + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const fooRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/foo', + beforeLoad, + loader, + }) + + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, fooRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + + // Start the preload and wait until its loader is actually in flight. + const preload = router.preloadRoute({ to: '/foo' }) + await loaderStarted + expect(beforeLoad.mock.calls.map(([context]) => context.preload)).toEqual([ + true, + ]) + expect(loader).toHaveBeenCalledTimes(1) + expect(preloadSignal).toBeDefined() + expect(preloadSignal?.aborted).toBe(false) + + // The navigation runs its own guard but shares the loader generation. + const navigation = router.navigate({ to: '/foo' }) + await vi.waitFor(() => expect(beforeLoad).toHaveBeenCalledTimes(2)) + expect(beforeLoad.mock.calls.map(([context]) => context.preload)).toEqual([ + true, + false, + ]) + expect(loaderGate.status).toBe('pending') + expect(loader).toHaveBeenCalledTimes(1) + expect(preloadSignal?.aborted).toBe(false) + + // Resolve the shared loader and let both settle. + loaderGate.resolve('adopted') + await Promise.all([navigation, preload]) + + // Loader ran exactly once; the adopted run's signal was never aborted. + expect(loader).toHaveBeenCalledTimes(1) + expect(preloadSignal?.aborted).toBe(false) + + // Navigation committed with the adopted loaderData. + const committed = router.state.matches.find( + (match) => match.routeId === fooRoute.id, + ) + expect(committed?.status).toBe('success') + expect(committed?.loaderData).toBe('adopted') + + // Adoption transfers loader-data lifetime ownership to the active match. + // The shared request remains alive while rendered, then aborts on unload. + await router.navigate({ to: '/' }) + expect(preloadSignal?.aborted).toBe(true) + }) + test('reruns beforeLoad when router context changes during an active preload', async () => { const loaderGate = createControlledPromise() const beforeLoad = vi.fn( diff --git a/packages/router-core/tests/preload-public-cache-behavior.test.ts b/packages/router-core/tests/preload-public-cache-behavior.test.ts index 2d74c32da8..2ad8017ee5 100644 --- a/packages/router-core/tests/preload-public-cache-behavior.test.ts +++ b/packages/router-core/tests/preload-public-cache-behavior.test.ts @@ -1,8 +1,118 @@ -import { expect, test, vi } from 'vitest' +import { afterEach, expect, test, vi } from 'vitest' import { createMemoryHistory } from '@tanstack/history' import { BaseRootRoute, BaseRoute, createControlledPromise } from '../src' import { createTestRouter } from './routerTestUtils' +afterEach(() => { + vi.restoreAllMocks() + vi.useRealTimers() +}) + +// https://github.com/TanStack/router/issues/2980 +// Repeated child preloads must borrow a stale active parent instead of rerunning +// its loader. +test('#2980: repeated child preloads do not rerun a stale active parent loader', async () => { + vi.useFakeTimers() + vi.setSystemTime(1_000) + + const layoutLoader = vi.fn(() => 'layout data') + const childLoader = vi.fn((childId: string) => `child ${childId}`) + const rootRoute = new BaseRootRoute({}) + const layoutRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/layout', + staleTime: 10, + loader: layoutLoader, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => layoutRoute, + path: '$childId', + loader: ({ params }) => childLoader(params.childId), + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([layoutRoute.addChildren([childRoute])]), + history: createMemoryHistory({ initialEntries: ['/layout'] }), + }) + + await router.load() + expect(layoutLoader).toHaveBeenCalledTimes(1) + vi.setSystemTime(2_000) + + await router.preloadRoute({ + to: '/layout/$childId', + params: { childId: 'one' }, + }) + expect(layoutLoader).toHaveBeenCalledTimes(1) + expect(childLoader).toHaveBeenNthCalledWith(1, 'one') + + await router.preloadRoute({ + to: '/layout/$childId', + params: { childId: 'two' }, + }) + + expect(layoutLoader).toHaveBeenCalledTimes(1) + expect(childLoader).toHaveBeenCalledTimes(2) + expect(childLoader).toHaveBeenNthCalledWith(2, 'two') +}) + +// Public contract: the public stale/gc options determine whether user loader +// code runs. A read-only preload must not silently shorten a navigation-owned +// cache entry's lifetime. +test('fresh navigation data keeps its gc policy when a preload reuses it', async () => { + vi.useFakeTimers() + vi.setSystemTime(1_000) + + let revision = 0 + const loader = vi.fn(() => ++revision) + const rootRoute = new BaseRootRoute({}) + const homeRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const reportsRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/reports', + gcTime: 30 * 60_000, + preloadGcTime: 30_000, + preloadStaleTime: 10 * 60_000, + staleTime: Infinity, + loader, + }) + const otherRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/other', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([homeRoute, reportsRoute, otherRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + await router.navigate({ to: '/reports' }) + await router.navigate({ to: '/' }) + expect(loader).toHaveBeenCalledTimes(1) + + vi.setSystemTime(61_000) + await router.preloadRoute({ to: '/reports' }) + await router.navigate({ to: '/other' }) + await router.navigate({ to: '/reports' }) + expect(loader).toHaveBeenCalledTimes(1) + expect( + router.state.matches.find((match) => match.routeId === reportsRoute.id) + ?.loaderData, + ).toBe(1) + + await router.navigate({ to: '/' }) + vi.setSystemTime(1_000 + 31 * 60_000) + await router.navigate({ to: '/other' }) + await router.navigate({ to: '/reports' }) + expect(loader).toHaveBeenCalledTimes(2) + expect( + router.state.matches.find((match) => match.routeId === reportsRoute.id) + ?.loaderData, + ).toBe(2) +}) + // Public contract: clearCache must remain authoritative even if an older // preload finishes unrelated asset work after the clear. test('clearCache during an in-flight preload cannot resurrect its borrowed data', async () => { @@ -92,12 +202,178 @@ test('clearCache cancels a brand-new in-flight preload before it can populate th ).toBe('navigation data') }) -// Probe, not a required cancellation policy: invalidating the accepted route -// need not cancel unrelated private preload work, but the public operations -// must both settle and leave navigation usable. -test('invalidate and an unrelated in-flight preload both settle cleanly', async () => { +test('clearCache does not reserve a discarded preload for a navigation in beforeLoad', async () => { + const navigationBeforeLoad = createControlledPromise() + let preloadSignal: AbortSignal | undefined + const beforeLoad = vi.fn(({ preload }: { preload: boolean }) => + preload ? undefined : navigationBeforeLoad, + ) + const loader = vi.fn( + ({ abortController }: { abortController: AbortController }) => { + const generation = loader.mock.calls.length + if (generation !== 1) { + return 'fresh navigation data' + } + preloadSignal = abortController.signal + return new Promise((_resolve, reject) => { + abortController.signal.addEventListener( + 'abort', + () => reject(abortController.signal.reason), + { once: true }, + ) + }) + }, + ) + const rootRoute = new BaseRootRoute({}) + const homeRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + beforeLoad, + loader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([homeRoute, targetRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + const preload = router.preloadRoute({ to: '/target' }) + await vi.waitFor(() => expect(loader).toHaveBeenCalledOnce()) + const navigation = router.navigate({ to: '/target' }) + await vi.waitFor(() => expect(beforeLoad).toHaveBeenCalledTimes(2)) + + router.clearCache({ + filter: (match) => match.routeId === targetRoute.id, + }) + await vi.waitFor(() => expect(preloadSignal?.aborted).toBe(true)) + await preload + + navigationBeforeLoad.resolve() + await navigation + expect(loader).toHaveBeenCalledTimes(2) + expect(router.state.matches.at(-1)?.loaderData).toBe('fresh navigation data') +}) + +test('clearCache does not abort loader work still used by a navigation', async () => { const loaderGate = createControlledPromise() - const loader = vi.fn(() => loaderGate) + let loaderSignal: AbortSignal | undefined + const beforeLoad = vi.fn() + const loader = vi.fn( + ({ abortController }: { abortController: AbortController }) => { + loaderSignal = abortController.signal + return loaderGate + }, + ) + const rootRoute = new BaseRootRoute({}) + const homeRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + beforeLoad, + loader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([homeRoute, targetRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + const preload = router.preloadRoute({ to: '/target' }) + await vi.waitFor(() => expect(loader).toHaveBeenCalledOnce()) + const navigation = router.navigate({ to: '/target' }) + await vi.waitFor(() => expect(beforeLoad).toHaveBeenCalledTimes(2)) + + router.clearCache({ + filter: (match) => match.routeId === targetRoute.id, + }) + expect(loaderSignal?.aborted).toBe(false) + + loaderGate.resolve('shared loader data') + await Promise.all([preload, navigation]) + + expect(loader).toHaveBeenCalledOnce() + expect(router.state.matches.at(-1)?.loaderData).toBe('shared loader data') +}) + +test('clearCache leaves cache authority intact when its filter throws', async () => { + let firstSignal: AbortSignal | undefined + let secondSignal: AbortSignal | undefined + const rootRoute = new BaseRootRoute({}) + const firstRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/first', + loader: ({ abortController }) => { + firstSignal = abortController.signal + return 'first data' + }, + }) + const secondRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/second', + loader: ({ abortController }) => { + secondSignal = abortController.signal + return 'second data' + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([firstRoute, secondRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + await router.preloadRoute({ to: '/first' }) + await router.preloadRoute({ to: '/second' }) + const filterError = new Error('filter failed') + + expect(() => + router.clearCache({ + filter: (match) => { + if (match.routeId === firstRoute.id) { + return true + } + if (match.routeId === secondRoute.id) { + throw filterError + } + return false + }, + }), + ).toThrow(filterError) + expect(firstSignal?.aborted).toBe(false) + expect(secondSignal?.aborted).toBe(false) + + router.clearCache() + expect(firstSignal?.aborted).toBe(true) + expect(secondSignal?.aborted).toBe(true) +}) + +test('clearCache installs replacement authorities before abort listeners reenter', async () => { + let reentrantNavigation: Promise | undefined + let router: ReturnType + const loader = vi.fn( + ({ abortController }: { abortController: AbortController }) => { + const generation = loader.mock.calls.length + if (generation !== 2) { + return `generation ${generation}` + } + return new Promise((_resolve, reject) => { + abortController.signal.addEventListener( + 'abort', + () => { + reentrantNavigation = router.navigate({ to: '/target' }) + reject(abortController.signal.reason) + }, + { once: true }, + ) + }) + }, + ) const rootRoute = new BaseRootRoute({}) const homeRoute = new BaseRoute({ getParentRoute: () => rootRoute, @@ -106,7 +382,277 @@ test('invalidate and an unrelated in-flight preload both settle cleanly', async const targetRoute = new BaseRoute({ getParentRoute: () => rootRoute, path: '/target', + staleTime: Infinity, + preloadStaleTime: 0, + loader, + }) + router = createTestRouter({ + routeTree: rootRoute.addChildren([homeRoute, targetRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + await router.navigate({ to: '/target' }) + await router.navigate({ to: '/' }) + + const preload = router.preloadRoute({ to: '/target' }) + await vi.waitFor(() => expect(loader).toHaveBeenCalledTimes(2)) + router.clearCache() + + await reentrantNavigation + await preload + expect(loader).toHaveBeenCalledTimes(3) + expect(router.state.location.pathname).toBe('/target') + expect(router.state.matches.at(-1)?.loaderData).toBe('generation 3') +}) + +test('clearCache detaches every discarded flight before abort listeners reenter', async () => { + let reentrantNavigation: Promise | undefined + let router: ReturnType + const firstBeforeLoad = vi.fn( + ({ abortController }: { abortController: AbortController }) => + new Promise((_resolve, reject) => { + abortController.signal.addEventListener( + 'abort', + () => { + reentrantNavigation = router.navigate({ to: '/second' }) + reject(abortController.signal.reason) + }, + { once: true }, + ) + }), + ) + const secondLoader = vi.fn( + ({ abortController }: { abortController: AbortController }) => { + const generation = secondLoader.mock.calls.length + if (generation > 1) { + return `generation ${generation}` + } + return new Promise((_resolve, reject) => { + abortController.signal.addEventListener( + 'abort', + () => reject(abortController.signal.reason), + { once: true }, + ) + }) + }, + ) + const rootRoute = new BaseRootRoute({}) + const homeRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const firstRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/first', + beforeLoad: firstBeforeLoad, + }) + const secondRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/second', + loader: secondLoader, + }) + router = createTestRouter({ + routeTree: rootRoute.addChildren([homeRoute, firstRoute, secondRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + const firstPreload = router.preloadRoute({ to: '/first' }) + await vi.waitFor(() => expect(firstBeforeLoad).toHaveBeenCalledOnce()) + const secondPreload = router.preloadRoute({ to: '/second' }) + await vi.waitFor(() => expect(secondLoader).toHaveBeenCalledOnce()) + + router.clearCache() + + await reentrantNavigation + await Promise.all([firstPreload, secondPreload]) + expect(secondLoader).toHaveBeenCalledTimes(2) + expect(router.state.location.pathname).toBe('/second') + expect(router.state.matches.at(-1)?.loaderData).toBe('generation 2') +}) + +test('filtered clearCache keeps shared loader work discoverable through a retained preload', async () => { + const layoutGate = createControlledPromise() + const layoutLoader = vi.fn(() => layoutGate) + const rootRoute = new BaseRootRoute({}) + const layoutRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/layout', + loader: layoutLoader, + }) + const firstBeforeLoad = vi.fn() + const firstRoute = new BaseRoute({ + getParentRoute: () => layoutRoute, + path: '/first', + beforeLoad: firstBeforeLoad, + }) + const secondBeforeLoad = vi.fn() + const secondLoader = vi.fn(() => 'second data') + const secondRoute = new BaseRoute({ + getParentRoute: () => layoutRoute, + path: '/second', + beforeLoad: secondBeforeLoad, + loader: secondLoader, + }) + const thirdBeforeLoad = vi.fn() + const thirdLoader = vi.fn(() => 'third data') + const thirdRoute = new BaseRoute({ + getParentRoute: () => layoutRoute, + path: '/third', + beforeLoad: thirdBeforeLoad, + loader: thirdLoader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + layoutRoute.addChildren([firstRoute, secondRoute, thirdRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + const firstPreload = router.preloadRoute({ to: '/layout/first' }) + const secondPreload = router.preloadRoute({ to: '/layout/second' }) + await vi.waitFor(() => { + expect(firstBeforeLoad).toHaveBeenCalledOnce() + expect(secondBeforeLoad).toHaveBeenCalledOnce() + expect(layoutLoader).toHaveBeenCalledOnce() + expect(secondLoader).toHaveBeenCalledOnce() + }) + + router.clearCache({ + filter: (match) => match.routeId === firstRoute.id, + }) + const thirdPreload = router.preloadRoute({ to: '/layout/third' }) + await vi.waitFor(() => { + expect(thirdBeforeLoad).toHaveBeenCalledOnce() + expect(thirdLoader).toHaveBeenCalledOnce() + }) + + expect(layoutLoader).toHaveBeenCalledOnce() + layoutGate.resolve('layout data') + await Promise.all([firstPreload, secondPreload, thirdPreload]) +}) + +test('independent concurrent preloads both populate the cache', async () => { + const firstGate = createControlledPromise() + const secondGate = createControlledPromise() + const firstLoader = vi.fn(() => firstGate) + const secondLoader = vi.fn(() => secondGate) + const rootRoute = new BaseRootRoute({}) + const homeRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const firstRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/first', preloadStaleTime: Infinity, + loader: firstLoader, + }) + const secondRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/second', + preloadStaleTime: Infinity, + loader: secondLoader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([homeRoute, firstRoute, secondRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + const firstPreload = router.preloadRoute({ to: '/first' }) + const secondPreload = router.preloadRoute({ to: '/second' }) + await vi.waitFor(() => { + expect(firstLoader).toHaveBeenCalledOnce() + expect(secondLoader).toHaveBeenCalledOnce() + }) + + firstGate.resolve('first data') + await firstPreload + secondGate.resolve('second data') + await secondPreload + + await router.navigate({ to: '/first' }) + await router.navigate({ to: '/second' }) + expect(firstLoader).toHaveBeenCalledOnce() + expect(secondLoader).toHaveBeenCalledOnce() +}) + +test.each([ + ['unfiltered', undefined, true], + ['filtered affected', 'target', true], + ['filtered unaffected', 'home', false], +] as const)( + '%s invalidation preserves the correct preload generation', + async (_mode, selected, shouldReload) => { + const oldLoaderGate = createControlledPromise() + let generation = 1 + const loader = vi.fn(() => + loader.mock.calls.length === 1 + ? oldLoaderGate + : `generation ${generation}`, + ) + const rootRoute = new BaseRootRoute({}) + const homeRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + preloadStaleTime: Infinity, + staleTime: Infinity, + loader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([homeRoute, targetRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + const preload = router.preloadRoute({ to: '/target' }) + await vi.waitFor(() => expect(loader).toHaveBeenCalledOnce()) + + generation = 2 + await (selected + ? router.invalidate({ + filter: (match) => + match.routeId === + (selected === 'target' ? targetRoute.id : homeRoute.id), + }) + : router.invalidate()) + + oldLoaderGate.resolve('generation 1') + await preload + await router.navigate({ to: '/target' }) + + expect(loader).toHaveBeenCalledTimes(shouldReload ? 2 : 1) + expect(router.state.matches.at(-1)?.loaderData).toBe( + `generation ${shouldReload ? 2 : 1}`, + ) + }, +) + +test('filtering only an active preload invalidates seeded same-ID cache data', async () => { + const replacementGate = createControlledPromise() + const loader = vi.fn(() => { + const generation = loader.mock.calls.length + return generation === 2 ? replacementGate : `generation ${generation}` + }) + const rootRoute = new BaseRootRoute({}) + const homeRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + validateSearch: (search: Record) => ({ + revision: Number(search.revision ?? 1), + }), + preloadStaleTime: 0, staleTime: Infinity, loader, }) @@ -116,21 +662,74 @@ test('invalidate and an unrelated in-flight preload both settle cleanly', async }) await router.load() - const preload = router.preloadRoute({ to: '/target' }) - await vi.waitFor(() => expect(loader).toHaveBeenCalledTimes(1)) - expect(loaderGate.status).toBe('pending') - await router.invalidate() - expect(loaderGate.status).toBe('pending') - loaderGate.resolve('target data') + await router.navigate({ to: '/target', search: { revision: 1 } }) + await router.navigate({ to: '/' }) + const preload = router.preloadRoute({ + to: '/target', + search: { revision: 2 }, + }) + await vi.waitFor(() => expect(loader).toHaveBeenCalledTimes(2)) + + await router.invalidate({ + filter: (match) => + match.routeId === targetRoute.id && + (match.search as { revision?: number }).revision === 2, + }) + replacementGate.resolve('obsolete generation 2') await preload + await router.navigate({ to: '/target' }) + + await vi.waitFor(() => { + expect(loader).toHaveBeenCalledTimes(3) + expect(router.state.matches.at(-1)?.loaderData).toBe('generation 3') + }) +}) + +test('invalidation wins after a preload loader settles but before it publishes', async () => { + let invalidation: Promise | undefined + let generation = 0 + let router: ReturnType + const loader = vi.fn(() => { + const data = `generation ${++generation}` + if (generation === 1) { + // Schedule invalidation after the loader outcome has fulfilled while the + // preload lane's cache publication is still queued. + let turn = Promise.resolve() + for (let index = 0; index < 4; index++) { + turn = turn.then() + } + invalidation = turn.then(() => router.invalidate()) + } + return data + }) + const rootRoute = new BaseRootRoute({}) + const homeRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + preloadStaleTime: Infinity, + staleTime: Infinity, + loader, + }) + router = createTestRouter({ + routeTree: rootRoute.addChildren([homeRoute, targetRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + await router.load() + const preload = router.preloadRoute({ to: '/target' }) + await vi.waitFor(() => expect(invalidation).toBeDefined()) + await invalidation + await preload await router.navigate({ to: '/target' }) - expect(router.state.location.pathname).toBe('/target') - expect( - router.state.matches.find((match) => match.routeId === targetRoute.id) - ?.loaderData, - ).toBe('target data') - expect(loader).toHaveBeenCalledTimes(1) + + await vi.waitFor(() => { + expect(loader).toHaveBeenCalledTimes(2) + expect(router.state.matches.at(-1)?.loaderData).toBe('generation 2') + }) }) // Preload lanes are not cancelled by unrelated navigations: an in-flight diff --git a/packages/router-core/tests/preload-public-signal-lifetime.test.ts b/packages/router-core/tests/preload-public-signal-lifetime.test.ts new file mode 100644 index 0000000000..b884a3b9ad --- /dev/null +++ b/packages/router-core/tests/preload-public-signal-lifetime.test.ts @@ -0,0 +1,155 @@ +import { expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute, createControlledPromise } from '../src' +import { createTestRouter } from './routerTestUtils' + +// Public contract: the AbortSignal supplied to loader code belongs to the +// accepted data generation. Adopting preloaded data keeps it alive; accepting +// replacement data aborts it; unloading aborts the replacement generation. +test('an adopted loader signal lives until public replacement and unload', async () => { + const replacementGate = createControlledPromise<{ generation: number }>() + let generation = 0 + let adoptedSignal: AbortSignal | undefined + let replacementSignal: AbortSignal | undefined + const loader = vi.fn( + ({ abortController }: { abortController: AbortController }) => { + generation++ + if (generation === 1) { + adoptedSignal = abortController.signal + return { generation } + } + + replacementSignal = abortController.signal + return replacementGate + }, + ) + + const rootRoute = new BaseRootRoute({}) + const homeRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const reportsRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/reports', + staleTime: Infinity, + preloadStaleTime: Infinity, + loader: { + staleReloadMode: 'blocking', + handler: loader, + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([homeRoute, reportsRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + await router.preloadRoute({ to: '/reports' }) + expect(adoptedSignal?.aborted).toBe(false) + + await router.navigate({ to: '/reports' }) + expect(loader).toHaveBeenCalledTimes(1) + expect( + router.state.matches.find((match) => match.routeId === reportsRoute.id) + ?.loaderData, + ).toEqual({ generation: 1 }) + expect(adoptedSignal?.aborted).toBe(false) + + const replacement = router.invalidate({ forcePending: true }) + await vi.waitFor(() => expect(loader).toHaveBeenCalledTimes(2)) + expect(replacementGate.status).toBe('pending') + + // Until replacement data is accepted, the currently rendered data still + // owns the adopted signal. + expect(adoptedSignal?.aborted).toBe(false) + expect(replacementSignal?.aborted).toBe(false) + + replacementGate.resolve({ generation: 2 }) + await replacement + expect( + router.state.matches.find((match) => match.routeId === reportsRoute.id) + ?.loaderData, + ).toEqual({ generation: 2 }) + expect(adoptedSignal?.aborted).toBe(true) + expect(replacementSignal?.aborted).toBe(false) + + await router.navigate({ to: '/' }) + expect(replacementSignal?.aborted).toBe(true) +}) + +test('a superseded preload releases its borrowed loader signal lease', async () => { + let parentSignal: AbortSignal | undefined + let navigation!: Promise + + const rootRoute = new BaseRootRoute({}) + const homeRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + loader: ({ abortController }) => { + parentSignal = abortController.signal + return 'parent data' + }, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + beforeLoad: ({ navigate, preload }) => { + if (preload) { + navigation = navigate({ to: '/' }) + throw navigation + } + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + homeRoute, + parentRoute.addChildren([childRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/parent'] }), + }) + + await router.load() + expect(parentSignal?.aborted).toBe(false) + + await router.preloadRoute({ to: '/parent/child' }) + await navigation + + expect(router.state.location.pathname).toBe('/') + expect(parentSignal?.aborted).toBe(true) +}) + +test('a terminal preload aborts its loader generation', async () => { + const routeError = new Error('preload failed') + let signal: AbortSignal | undefined + + const rootRoute = new BaseRootRoute({}) + const homeRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const reportsRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/reports', + loader: ({ abortController }) => { + signal = abortController.signal + throw routeError + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([homeRoute, reportsRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + const matches = await router.preloadRoute({ to: '/reports' }) + + expect( + matches?.find((match) => match.routeId === reportsRoute.id), + ).toMatchObject({ status: 'error', error: routeError }) + expect(signal?.aborted).toBe(true) +}) diff --git a/packages/router-core/tests/public-client-loading-contract.test.ts b/packages/router-core/tests/public-client-loading-contract.test.ts index 50b152ac6a..c3cb0c7577 100644 --- a/packages/router-core/tests/public-client-loading-contract.test.ts +++ b/packages/router-core/tests/public-client-loading-contract.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, test, vi } from 'vitest' +import { afterEach, describe, expect, test, vi } from 'vitest' import { createMemoryHistory } from '@tanstack/history' import { BaseRootRoute, @@ -8,6 +8,11 @@ import { } from '../src' import { createTestRouter } from './routerTestUtils' +afterEach(() => { + vi.useRealTimers() + vi.restoreAllMocks() +}) + describe('public client loading contracts', () => { test('blocking loading is observable through match isFetching', async () => { const loaderGate = createControlledPromise() @@ -50,6 +55,270 @@ describe('public client loading contracts', () => { }) }) + test('background loading is observable while retaining committed data', async () => { + const reloadGate = createControlledPromise<{ generation: number }>() + let loaderCalls = 0 + const loader = vi.fn(() => + ++loaderCalls === 1 ? { generation: 1 } : reloadGate, + ) + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + staleTime: Infinity, + loader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + await router.load() + const invalidation = router.invalidate() + await vi.waitFor(() => expect(loader).toHaveBeenCalledTimes(2)) + + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: pageRoute.id, + status: 'success', + isFetching: 'loader', + loaderData: { generation: 1 }, + }) + + reloadGate.resolve({ generation: 2 }) + await invalidation + await vi.waitFor(() => + expect(router.state.matches.at(-1)).toMatchObject({ + status: 'success', + isFetching: false, + loaderData: { generation: 2 }, + }), + ) + }) + + test('a successor does not fall back to older data while a background reload is in flight', async () => { + const backgroundResult = createControlledPromise() + const successorPlanned = createControlledPromise() + let loaderCalls = 0 + const loader = vi.fn(() => { + loaderCalls++ + if (loaderCalls === 1) { + return 'cached data' + } + if (loaderCalls === 2) { + return backgroundResult + } + return 'updated data' + }) + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + validateSearch: (search: Record) => ({ + phase: String(search.phase ?? 'initial'), + }), + shouldReload: ({ location }) => { + const phase = (location.search as { phase: string }).phase + if (phase === 'successor') { + successorPlanned.resolve() + } + return phase === 'reload' ? true : undefined + }, + staleTime: Infinity, + loader: { + staleReloadMode: 'background', + handler: loader, + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ + initialEntries: ['/page?phase=initial'], + }), + }) + + await router.load() + await router.navigate({ + to: '/page', + search: { phase: 'reload' }, + }) + await vi.waitFor(() => expect(loader).toHaveBeenCalledTimes(2)) + + const successor = router.navigate({ + to: '/page', + search: { phase: 'successor' }, + }) + await successorPlanned + backgroundResult.resolve('updated data') + await successor + + await vi.waitFor(() => { + expect(router.state.matches.at(-1)?.loaderData).toBe('updated data') + }) + }) + + test('a background loader can fulfill after aborting its controller', async () => { + let loaderCalls = 0 + const loader = vi.fn(({ abortController }) => { + loaderCalls++ + if (loaderCalls === 1) { + return { generation: 1 } + } + abortController.abort() + return { generation: 2 } + }) + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + staleTime: Infinity, + loader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + await router.load() + await router.invalidate() + + expect(loader).toHaveBeenCalledTimes(2) + await vi.waitFor(() => + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: pageRoute.id, + status: 'success', + isFetching: false, + loaderData: { generation: 2 }, + }), + ) + expect(router.state.status).toBe('idle') + }) + + test('a current loader can fulfill after aborting its controller', async () => { + const loader = vi.fn(({ abortController }) => { + abortController.abort() + return 'accepted data' + }) + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + staleTime: Infinity, + loader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + await router.load() + await router.load() + + expect(loader).toHaveBeenCalledTimes(1) + expect(router.state.status).toBe('idle') + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: pageRoute.id, + status: 'success', + loaderData: 'accepted data', + }) + }) + + test('background redirects use the navigation redirect limit', async () => { + let loaderCalls = 0 + const loader = vi.fn(() => { + loaderCalls++ + if (loaderCalls === 1) { + return 'initial data' + } + if (loaderCalls < 30) { + throw redirect({ to: '/page' }) + } + return 'redirect limit was bypassed' + }) + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + shouldReload: true, + loader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + await router.load() + await router.invalidate() + + expect(loader).toHaveBeenCalledTimes(22) + await vi.waitFor(() => + expect( + router.state.matches.find((match) => match.status !== 'success'), + ).toMatchObject({ + routeId: rootRoute.id, + status: 'error', + error: expect.objectContaining({ message: 'Too many redirects' }), + }), + ) + }) + + test('a blocking child observes the fresh semantic parent generation', async () => { + const parentReload = createControlledPromise<{ revision: number }>() + let parentCalls = 0 + const parentLoader = vi.fn(() => + ++parentCalls === 1 ? { revision: 1 } : parentReload, + ) + const childLoader = vi.fn(async ({ parentMatchPromise }) => { + const parent = await parentMatchPromise + return { + parentRevision: (parent.loaderData as { revision: number }).revision, + } + }) + + const rootRoute = new BaseRootRoute({}) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + staleTime: Infinity, + shouldReload: true, + loader: parentLoader, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loader: { + staleReloadMode: 'blocking', + handler: childLoader, + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([parentRoute.addChildren([childRoute])]), + history: createMemoryHistory({ initialEntries: ['/parent'] }), + }) + + await router.load() + + let settled = false + const navigation = router.navigate({ to: '/parent/child' }).then(() => { + settled = true + }) + await vi.waitFor(() => expect(parentLoader).toHaveBeenCalledTimes(2)) + + expect(parentReload.status).toBe('pending') + expect(settled).toBe(false) + + parentReload.resolve({ revision: 2 }) + await navigation + + await vi.waitFor(() => + expect( + router.state.matches.find((match) => match.routeId === parentRoute.id), + ).toMatchObject({ loaderData: { revision: 2 } }), + ) + expect( + router.state.matches.find((match) => match.routeId === childRoute.id), + ).toMatchObject({ loaderData: { parentRevision: 2 } }) + }) + test('a background descendant redirect wins after a blocking ancestor loader fails', async () => { const childStarted = createControlledPromise() const childRedirect = createControlledPromise() @@ -112,4 +381,318 @@ describe('public client loading contracts', () => { router.state.matches.some((match) => match.error === parentError), ).toBe(false) }) + + test('a hidden background descendant failure cannot replace an ancestor failure', async () => { + const childStarted = createControlledPromise() + const parentError = new Error('parent failed') + const childError = new Error('child failed') + let parentLoads = 0 + let childLoads = 0 + + const rootRoute = new BaseRootRoute({}) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + loader: { + staleReloadMode: 'blocking', + handler: async () => { + if (++parentLoads === 1) { + return 'parent data' + } + await childStarted + throw parentError + }, + }, + errorComponent: () => null, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loader: () => { + if (++childLoads === 1) { + return 'child data' + } + childStarted.resolve() + throw childError + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([parentRoute.addChildren([childRoute])]), + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + }) + + await router.load() + await router.invalidate() + + expect(router.state.matches.map((match) => match.routeId)).toEqual([ + rootRoute.id, + parentRoute.id, + childRoute.id, + ]) + expect( + router.state.matches.find((match) => match.routeId === parentRoute.id), + ).toMatchObject({ status: 'error', error: parentError }) + expect( + router.state.matches.find((match) => match.routeId === childRoute.id), + ).toMatchObject({ + status: 'success', + error: undefined, + isFetching: false, + loaderData: 'child data', + }) + expect( + router.state.matches.some((match) => match.error === childError), + ).toBe(false) + }) + + test('background projection stays private until its lane wins publication', async () => { + const reloadGate = createControlledPromise<{ title: string }>() + const headGate = createControlledPromise() + const freshHeadStarted = createControlledPromise() + let loaderCalls = 0 + let allowReload = true + const loader = vi.fn(() => + ++loaderCalls === 1 ? { title: 'old' } : reloadGate, + ) + const head = vi.fn(async ({ loaderData }) => { + if (loaderData?.title === 'fresh') { + freshHeadStarted.resolve() + await headGate + } + return { meta: [{ title: loaderData?.title }] } + }) + + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + staleTime: Infinity, + shouldReload: () => allowReload, + loader, + head, + }) + const otherRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/other', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute, otherRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + allowReload = false + await router.load() + expect(router.state.matches.at(-1)).toMatchObject({ + loaderData: { title: 'old' }, + meta: [{ title: 'old' }], + }) + + allowReload = true + const invalidation = router.invalidate() + await vi.waitFor(() => expect(loader).toHaveBeenCalledTimes(2)) + reloadGate.resolve({ title: 'fresh' }) + await freshHeadStarted + + expect(router.state.matches.at(-1)).toMatchObject({ + loaderData: { title: 'old' }, + meta: [{ title: 'old' }], + }) + + await router.navigate({ to: '/other' }) + allowReload = false + headGate.resolve() + await invalidation + await Promise.resolve() + + await router.navigate({ to: '/page' }) + expect(router.state.matches.at(-1)).toMatchObject({ + loaderData: { title: 'old' }, + meta: [{ title: 'old' }], + isFetching: false, + }) + }) + + test('redirect depth bookkeeping is cleared after a redirect chain settles', async () => { + const hopBeforeLoad = vi.fn(({ params }) => { + const hop = Number(params.hop) + if (hop < 20) { + throw redirect({ + to: '/hop/$hop', + params: { hop: String(hop + 1) }, + } as any) + } + }) + const rootRoute = new BaseRootRoute({}) + const hopRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/hop/$hop', + beforeLoad: hopBeforeLoad, + }) + const againRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/again', + beforeLoad: () => { + throw redirect({ to: '/final' }) + }, + }) + const finalRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/final', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([hopRoute, againRoute, finalRoute]), + history: createMemoryHistory({ initialEntries: ['/hop/0'] }), + }) + + await router.load() + expect(router.state.location.pathname).toBe('/hop/20') + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: hopRoute.id, + status: 'success', + }) + + await router.navigate({ to: '/again' }) + expect(router.state.location.pathname).toBe('/final') + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: finalRoute.id, + status: 'success', + }) + expect(router.state.status).toBe('idle') + }) + + test('a redirect-limit error aborts concurrent work in its final lane', async () => { + const finalRootAborted = createControlledPromise() + const finalRootData = createControlledPromise() + const errorComponentStarted = createControlledPromise() + const errorComponentGate = createControlledPromise() + let finalRootSignal: AbortSignal | undefined + const ErrorComponent = Object.assign(() => null, { + preload: () => { + errorComponentStarted.resolve() + return errorComponentGate + }, + }) + + const rootRoute = new BaseRootRoute({ + errorComponent: ErrorComponent as any, + loader: ({ location, abortController }) => { + if (location.pathname !== '/hop/20/child') { + return 'root data' + } + finalRootSignal = abortController.signal + finalRootSignal.addEventListener( + 'abort', + () => finalRootAborted.resolve(), + { once: true }, + ) + return finalRootData + }, + }) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/hop/$hop', + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loader: ({ params }) => { + throw redirect({ + to: '/hop/$hop/child', + params: { hop: String(Number(params.hop) + 1) }, + } as any) + }, + }) + const cleanupRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/cleanup', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + parentRoute.addChildren([childRoute]), + cleanupRoute, + ]), + history: createMemoryHistory({ + initialEntries: ['/hop/0/child'], + }), + }) + const loading = router.load() + + try { + await errorComponentStarted + expect(finalRootData.status).toBe('pending') + errorComponentGate.resolve() + await finalRootAborted + await expect(loading).resolves.toBeUndefined() + + expect(router.state.location.pathname).toBe('/hop/20/child') + const firstTerminalMatch = router.state.matches.find( + (match) => match.status !== 'success', + ) + expect(firstTerminalMatch).toMatchObject({ + routeId: rootRoute.id, + status: 'error', + isFetching: false, + error: expect.objectContaining({ message: 'Too many redirects' }), + }) + expect(finalRootData.status).toBe('pending') + expect(finalRootSignal?.aborted).toBe(true) + expect( + router.state.matches.every((match) => match.isFetching === false), + ).toBe(true) + expect(router.state.status).toBe('idle') + expect(router.state.isLoading).toBe(false) + } finally { + finalRootData.resolve('late root data') + errorComponentGate.resolve() + await Promise.allSettled([loading]) + } + + await router.navigate({ to: '/cleanup' }) + expect(router.state.location.pathname).toBe('/cleanup') + expect( + router.state.matches.every((match) => match.status === 'success'), + ).toBe(true) + expect(router.state.status).toBe('idle') + }) + + test('a redirect is not blocked by an abort-ignoring sibling loader', async () => { + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/source', + loader: () => new Promise(() => {}), + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loader: () => { + throw redirect({ to: '/target' }) + }, + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + indexRoute, + parentRoute.addChildren([childRoute]), + targetRoute, + ]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + await router.navigate({ to: '/source/child' }) + + expect(router.state.location.pathname).toBe('/target') + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: targetRoute.id, + status: 'success', + }) + }) }) diff --git a/packages/router-core/tests/public-hydration-contract.test.ts b/packages/router-core/tests/public-hydration-contract.test.ts new file mode 100644 index 0000000000..836931ee91 --- /dev/null +++ b/packages/router-core/tests/public-hydration-contract.test.ts @@ -0,0 +1,1628 @@ +import { runInNewContext } from 'node:vm' +import { + afterEach, + beforeEach, + describe, + expect, + onTestFinished, + test, + vi, +} from 'vitest' +import { createBrowserHistory, createMemoryHistory } from '@tanstack/history' +import { + BaseRootRoute, + BaseRoute, + createControlledPromise, + redirect, +} from '../src' +import { hydrate } from '../src/ssr/client' +import { attachRouterServerSsrUtils } from '../src/ssr/ssr-server' +import { dehydrateSsrMatchId } from '../src/ssr/ssr-match-id' +import { createTestRouter } from './routerTestUtils' +import type { AnyRouteMatch, AnyRouter, LocationRewrite } from '../src' +import type { DehydratedRouter, TsrSsrGlobal } from '../src/ssr/types' +import type { ServerManifest } from '../src/manifest' + +const testManifest: ServerManifest = { routes: {} } +const browserWindow = window + +async function dehydrateToBootstrap(router: AnyRouter): Promise { + attachRouterServerSsrUtils({ router, manifest: testManifest }) + try { + await router.load() + await router.serverSsr!.dehydrate() + + const script = router.serverSsr!.takeBufferedScripts() + expect(script?.children).toBeTruthy() + + const context: Record = { + document: { currentScript: { remove() {} } }, + } + context.self = context + runInNewContext(script!.children!, context) + + return context.$_TSR + } finally { + router.serverSsr?.cleanup() + } +} + +function installHydrationPayload( + mockWindow: { $_TSR?: TsrSsrGlobal }, + matches: DehydratedRouter['matches'], +) { + mockWindow.$_TSR = { + router: { + manifest: testManifest, + dehydratedData: {}, + matches, + }, + h() {}, + e() {}, + c() {}, + p() {}, + buffer: [], + } +} + +describe('public hydration contracts', () => { + let mockWindow: { $_TSR?: TsrSsrGlobal } + + beforeEach(() => { + mockWindow = {} + vi.stubGlobal('window', mockWindow) + }) + + afterEach(() => { + vi.restoreAllMocks() + vi.unstubAllGlobals() + }) + + test('starts head and scripts together and swallows a head failure', async () => { + const error = new Error('head failed') + const headGate = createControlledPromise() + const head = vi.fn(async () => { + await headGate + throw error + }) + const scripts = vi.fn(() => [ + { children: 'window.hydrationScriptRan = true' }, + ]) + const consoleError = vi + .spyOn(console, 'error') + .mockImplementation(() => undefined) + + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + head, + scripts, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + isServer: false, + }) + const matches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload( + mockWindow, + matches.map((match) => ({ + i: dehydrateSsrMatchId(match.id), + s: 'success' as const, + ssr: true, + u: Date.now(), + })), + ) + + const hydration = hydrate(router) + await vi.waitFor(() => { + expect(head).toHaveBeenCalledTimes(1) + expect(scripts).toHaveBeenCalledTimes(1) + }) + + headGate.resolve() + await expect(hydration).resolves.toBeUndefined() + + expect(consoleError).toHaveBeenCalledWith(error) + expect(router.state.resolvedLocation?.pathname).toBe('/page') + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: pageRoute.id, + status: 'success', + }) + }) + + test('adopts a shorter selective-SSR prefix ending at a pending client-only match', async () => { + const serverLoader = vi.fn(() => 'server data') + const serverRootRoute = new BaseRootRoute({}) + const serverClientOnlyRoute = new BaseRoute({ + getParentRoute: () => serverRootRoute, + path: '/client-only', + ssr: false, + loader: serverLoader, + }) + const serverChildRoute = new BaseRoute({ + getParentRoute: () => serverClientOnlyRoute, + path: '/child', + ssr: true, + }) + const serverRouter = createTestRouter({ + routeTree: serverRootRoute.addChildren([ + serverClientOnlyRoute.addChildren([serverChildRoute]), + ]), + history: createMemoryHistory({ + initialEntries: ['/client-only/child'], + }), + isServer: true, + }) + const bootstrap = await dehydrateToBootstrap(serverRouter) + expect(bootstrap.router?.matches).toHaveLength(2) + expect(bootstrap.router?.matches.at(-1)).toMatchObject({ + s: 'pending', + ssr: false, + }) + expect(serverLoader).not.toHaveBeenCalled() + + const routeContext = vi.fn(() => ({ routeContext: true })) + const beforeLoad = vi.fn(() => ({ clientContext: true })) + const loader = vi.fn( + ({ context }) => context.clientContext && context.routeContext, + ) + const rootRoute = new BaseRootRoute({}) + const clientOnlyRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/client-only', + ssr: false, + context: routeContext, + beforeLoad, + loader, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => clientOnlyRoute, + path: '/child', + ssr: true, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + clientOnlyRoute.addChildren([childRoute]), + ]), + history: createMemoryHistory({ + initialEntries: ['/client-only/child'], + }), + isServer: false, + }) + mockWindow.$_TSR = bootstrap + + await hydrate(router) + + expect(router.state.matches.map((match) => match.routeId)).toEqual([ + rootRoute.id, + clientOnlyRoute.id, + childRoute.id, + ]) + expect(router.state.matches.slice(1)).toMatchObject([ + { routeId: clientOnlyRoute.id, status: 'pending', ssr: false }, + { routeId: childRoute.id, ssr: true }, + ]) + expect(router.state.resolvedLocation).toBeUndefined() + expect(router.state.matches[1]?.context).toEqual({ routeContext: true }) + expect(routeContext).toHaveBeenCalledTimes(1) + expect(beforeLoad).not.toHaveBeenCalled() + expect(loader).not.toHaveBeenCalled() + + await router.load() + + expect(beforeLoad).toHaveBeenCalledTimes(1) + expect(loader).toHaveBeenCalledTimes(1) + expect(routeContext).toHaveBeenCalledTimes(1) + expect(router.state.matches[1]).toMatchObject({ + routeId: clientOnlyRoute.id, + status: 'success', + loaderData: true, + }) + expect(router.state.resolvedLocation?.href).toBe('/client-only/child') + }) + + test('selective SSR exposes the complete accepted lane to route context and head hooks', async () => { + const serverRootRoute = new BaseRootRoute({}) + const serverClientOnlyRoute = new BaseRoute({ + getParentRoute: () => serverRootRoute, + path: '/client-only', + ssr: false, + }) + const serverChildRoute = new BaseRoute({ + getParentRoute: () => serverClientOnlyRoute, + path: '/child', + }) + const serverRouter = createTestRouter({ + routeTree: serverRootRoute.addChildren([ + serverClientOnlyRoute.addChildren([serverChildRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/client-only/child'] }), + isServer: true, + }) + const bootstrap = await dehydrateToBootstrap(serverRouter) + + const contextLanes: Array> = [] + const headLanes: Array> = [] + const rootRoute = new BaseRootRoute({ + context: ({ matches }) => { + contextLanes.push(matches.map((match) => match.routeId)) + return { hydrated: true } + }, + head: ({ matches }) => { + headLanes.push(matches.map((match) => match.routeId)) + return { + meta: [ + { + title: String(matches[1]?.loaderData), + }, + ], + } + }, + }) + const clientOnlyRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/client-only', + ssr: false, + loader: () => 'client data', + }) + const childRoute = new BaseRoute({ + getParentRoute: () => clientOnlyRoute, + path: '/child', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + clientOnlyRoute.addChildren([childRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/client-only/child'] }), + isServer: false, + }) + mockWindow.$_TSR = bootstrap + + await hydrate(router) + + const fullLane = [rootRoute.id, clientOnlyRoute.id, childRoute.id] + expect(contextLanes).toEqual([fullLane]) + expect(headLanes).toEqual([fullLane]) + expect(router.state.matches.map((match) => match.routeId)).toEqual(fullLane) + expect(router.state.matches[0]?.context).toMatchObject({ hydrated: true }) + expect(router.state.matches[0]?.meta).toEqual([{ title: 'undefined' }]) + + await router.load() + + expect(router.state.matches[0]?.meta).toEqual([{ title: 'client data' }]) + }) + + test('shell hydration exposes the complete accepted lane to route context and head hooks', async () => { + const contextLanes: Array> = [] + const headLanes: Array> = [] + const rootRoute = new BaseRootRoute({ + context: ({ matches }) => { + contextLanes.push(matches.map((match) => match.routeId)) + return { hydrated: true } + }, + head: ({ matches }) => { + headLanes.push(matches.map((match) => match.routeId)) + return { meta: [{ title: 'Shell hydration' }] } + }, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/child', + ssr: false, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([childRoute]), + history: createMemoryHistory({ initialEntries: ['/child'] }), + isServer: false, + }) + const matches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload(mockWindow, [ + { + i: dehydrateSsrMatchId(matches[0]!.id), + s: 'success', + ssr: true, + u: Date.now(), + }, + ]) + + await hydrate(router) + + const fullLane = [rootRoute.id, childRoute.id] + expect(contextLanes).toEqual([fullLane]) + expect(headLanes).toEqual([fullLane]) + expect(router.state.matches.map((match) => match.routeId)).toEqual(fullLane) + expect(router.state.matches[0]?.context).toMatchObject({ hydrated: true }) + expect(router.state.matches[0]?.meta).toEqual([ + { title: 'Shell hydration' }, + ]) + }) + + test('terminal hydration exposes the full structural lane to route context and head hooks', async () => { + const contextLanes: Array> = [] + const headLanes: Array> = [] + const observeContext = ({ matches }: { matches: Array }) => { + contextLanes.push(matches.map((match) => match.routeId)) + return {} + } + const observeHead = ({ matches }: { matches: Array }) => { + headLanes.push(matches.map((match) => match.routeId)) + return { meta: [{ title: 'Terminal hydration' }] } + } + const rootRoute = new BaseRootRoute({ + context: observeContext, + head: observeHead, + }) + const boundaryRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/app', + context: observeContext, + head: observeHead, + errorComponent: () => 'App error', + }) + const childRoute = new BaseRoute({ + getParentRoute: () => boundaryRoute, + path: '/child', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + boundaryRoute.addChildren([childRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/app/child'] }), + isServer: false, + }) + const matches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload(mockWindow, [ + { + i: dehydrateSsrMatchId(matches[0]!.id), + s: 'success', + ssr: true, + u: Date.now(), + }, + { + i: dehydrateSsrMatchId(matches[1]!.id), + s: 'error', + e: new Error('App failed on the server'), + ssr: true, + u: Date.now(), + }, + ]) + + await hydrate(router) + + const fullLane = [rootRoute.id, boundaryRoute.id, childRoute.id] + expect(contextLanes).toEqual([fullLane, fullLane]) + expect(headLanes).toEqual([fullLane, fullLane]) + expect(router.state.matches.map((match) => match.routeId)).toEqual(fullLane) + expect(router.state.matches[1]).toMatchObject({ + routeId: boundaryRoute.id, + status: 'error', + }) + expect(router.state.matches[2]?.status).toBe('success') + }) + + test('does not cache missing loader data when retrying a hydrated terminal boundary', async () => { + const serverError = new Error('Server boundary failed') + const chunkError = new Error('Boundary chunk failed during hydration') + const errorComponentPreload = vi + .fn<() => Promise>() + .mockRejectedValueOnce(chunkError) + .mockResolvedValue(undefined) + const ErrorComponent = Object.assign(() => 'App error', { + preload: errorComponentPreload, + }) + const beforeLoad = vi.fn() + const loader = vi.fn(() => 'client data') + const rootRoute = new BaseRootRoute({}) + const boundaryRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/app', + beforeLoad, + loader, + errorComponent: ErrorComponent, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => boundaryRoute, + path: '/child', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + boundaryRoute.addChildren([childRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/app/child'] }), + isServer: false, + }) + const matches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload(mockWindow, [ + { + i: dehydrateSsrMatchId(matches[0]!.id), + s: 'success', + ssr: true, + u: Date.now(), + }, + { + i: dehydrateSsrMatchId(matches[1]!.id), + s: 'error', + e: serverError, + ssr: true, + u: Date.now(), + }, + ]) + + await hydrate(router) + + expect(errorComponentPreload).toHaveBeenCalledTimes(1) + expect(beforeLoad).not.toHaveBeenCalled() + expect(router.state.resolvedLocation).toBeUndefined() + expect(router.state.matches.map((match) => match.routeId)).toEqual([ + rootRoute.id, + boundaryRoute.id, + childRoute.id, + ]) + expect(router.state.matches[1]).toMatchObject({ + status: 'error', + error: serverError, + }) + + await router.load() + + expect(beforeLoad).toHaveBeenCalledTimes(1) + expect(loader).toHaveBeenCalledTimes(1) + expect(router.state.resolvedLocation?.href).toBe('/app/child') + expect(router.state.matches[1]).toMatchObject({ + status: 'success', + error: undefined, + loaderData: 'client data', + }) + }) + + test('retries a terminal lane after ancestor context reconstruction fails', async () => { + const serverError = new Error('Server boundary failed') + const clientError = new Error('Client boundary failed') + const contextError = new Error('Root context failed during hydration') + const context = vi + .fn<() => { clientContext: true }>() + .mockImplementationOnce(() => { + throw contextError + }) + .mockReturnValue({ clientContext: true }) + const beforeLoad = vi.fn(() => { + throw clientError + }) + const rootRoute = new BaseRootRoute({ context }) + const boundaryRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/app', + beforeLoad, + errorComponent: () => 'App error', + }) + const childRoute = new BaseRoute({ + getParentRoute: () => boundaryRoute, + path: '/child', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + boundaryRoute.addChildren([childRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/app/child'] }), + isServer: false, + }) + const matches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload(mockWindow, [ + { + i: dehydrateSsrMatchId(matches[0]!.id), + s: 'success', + ssr: true, + u: Date.now(), + }, + { + i: dehydrateSsrMatchId(matches[1]!.id), + s: 'error', + e: serverError, + ssr: true, + u: Date.now(), + }, + ]) + + await hydrate(router) + + expect(context).toHaveBeenCalledTimes(1) + expect(beforeLoad).not.toHaveBeenCalled() + expect(router.state.resolvedLocation).toBeUndefined() + expect(router.state.matches.map((match) => match.routeId)).toEqual([ + rootRoute.id, + boundaryRoute.id, + childRoute.id, + ]) + expect(router.state.matches[1]).toMatchObject({ + status: 'error', + error: serverError, + }) + + await router.load() + + expect(context).toHaveBeenCalledTimes(2) + expect(beforeLoad).toHaveBeenCalledTimes(1) + expect(router.state.resolvedLocation?.href).toBe('/app/child') + expect(router.state.matches[0]?.context).toMatchObject({ + clientContext: true, + }) + expect(router.state.matches[1]).toMatchObject({ + status: 'error', + error: clientError, + }) + }) + + test('does not repeat data-only assets during client continuation', async () => { + const serverRootRoute = new BaseRootRoute({}) + const serverPageRoute = new BaseRoute({ + getParentRoute: () => serverRootRoute, + path: '/page', + ssr: 'data-only', + loader: () => 'server data', + }) + const serverRouter = createTestRouter({ + routeTree: serverRootRoute.addChildren([serverPageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + isServer: true, + }) + const bootstrap = await dehydrateToBootstrap(serverRouter) + + const head = vi.fn(({ loaderData }) => ({ + meta: [{ title: String(loaderData) }], + })) + const scripts = vi.fn(({ loaderData }) => [ + { children: `window.pageData = ${JSON.stringify(loaderData)}` }, + ]) + const loader = vi.fn(() => 'client data') + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + ssr: 'data-only', + loader, + head, + scripts, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + isServer: false, + }) + mockWindow.$_TSR = bootstrap + + await hydrate(router) + + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: pageRoute.id, + status: 'pending', + loaderData: 'server data', + meta: [{ title: 'server data' }], + scripts: [{ children: 'window.pageData = "server data"' }], + }) + expect(head).toHaveBeenCalledTimes(1) + expect(scripts).toHaveBeenCalledTimes(1) + expect(loader).not.toHaveBeenCalled() + + await router.load() + + expect(router.state.resolvedLocation?.pathname).toBe('/page') + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: pageRoute.id, + status: 'success', + loaderData: 'server data', + meta: [{ title: 'server data' }], + scripts: [{ children: 'window.pageData = "server data"' }], + }) + expect(head).toHaveBeenCalledTimes(1) + expect(scripts).toHaveBeenCalledTimes(1) + expect(loader).not.toHaveBeenCalled() + }) + + test('adopts the complete nested data-only server prefix', async () => { + const serverParentBeforeLoad = vi.fn(() => ({ source: 'server' })) + const serverParentLoader = vi.fn(() => 'server parent') + const serverChildBeforeLoad = vi.fn() + const serverChildLoader = vi.fn(() => 'server child') + const serverRootRoute = new BaseRootRoute({}) + const serverParentRoute = new BaseRoute({ + getParentRoute: () => serverRootRoute, + path: '/parent', + ssr: 'data-only', + beforeLoad: serverParentBeforeLoad, + loader: serverParentLoader, + }) + const serverChildRoute = new BaseRoute({ + getParentRoute: () => serverParentRoute, + path: '/child', + beforeLoad: serverChildBeforeLoad, + loader: serverChildLoader, + }) + const serverRouter = createTestRouter({ + routeTree: serverRootRoute.addChildren([ + serverParentRoute.addChildren([serverChildRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + isServer: true, + }) + const bootstrap = await dehydrateToBootstrap(serverRouter) + + expect(serverParentBeforeLoad).toHaveBeenCalledTimes(1) + expect(serverChildBeforeLoad).toHaveBeenCalledTimes(1) + expect(serverParentLoader).toHaveBeenCalledTimes(1) + expect(serverChildLoader).toHaveBeenCalledTimes(1) + + const parentBeforeLoad = vi.fn(() => ({ source: 'client' })) + const parentLoader = vi.fn(() => 'client parent') + const childBeforeLoad = vi.fn() + const childLoader = vi.fn(() => 'client child') + const rootRoute = new BaseRootRoute({}) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + ssr: 'data-only', + beforeLoad: parentBeforeLoad, + loader: parentLoader, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + beforeLoad: childBeforeLoad, + loader: childLoader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([parentRoute.addChildren([childRoute])]), + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + isServer: false, + }) + mockWindow.$_TSR = bootstrap + + await hydrate(router) + + expect(router.state.matches[1]).toMatchObject({ + routeId: parentRoute.id, + status: 'pending', + loaderData: 'server parent', + }) + expect(router.state.matches[2]).toMatchObject({ + routeId: childRoute.id, + status: 'success', + loaderData: 'server child', + }) + expect(parentBeforeLoad).not.toHaveBeenCalled() + expect(childBeforeLoad).not.toHaveBeenCalled() + expect(parentLoader).not.toHaveBeenCalled() + expect(childLoader).not.toHaveBeenCalled() + + await router.load() + + expect(router.state.resolvedLocation?.pathname).toBe('/parent/child') + expect(router.state.matches[1]).toMatchObject({ + status: 'success', + context: { source: 'server' }, + loaderData: 'server parent', + }) + expect(router.state.matches[2]).toMatchObject({ + status: 'success', + loaderData: 'server child', + }) + expect(parentBeforeLoad).not.toHaveBeenCalled() + expect(childBeforeLoad).not.toHaveBeenCalled() + expect(parentLoader).not.toHaveBeenCalled() + expect(childLoader).not.toHaveBeenCalled() + }) + + test('keeps an ssr-false route-context error authoritative', async () => { + const serverError = new Error('server route context failed') + const serverRootRoute = new BaseRootRoute({}) + const serverPageRoute = new BaseRoute({ + getParentRoute: () => serverRootRoute, + path: '/page', + ssr: false, + context: () => { + throw serverError + }, + }) + const serverRouter = createTestRouter({ + routeTree: serverRootRoute.addChildren([serverPageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + isServer: true, + }) + const bootstrap = await dehydrateToBootstrap(serverRouter) + const transportedError = bootstrap.router?.matches.at(-1)?.e + + expect(transportedError).toMatchObject({ + name: serverError.name, + message: serverError.message, + }) + + const context = vi.fn(() => ({ source: 'client' })) + const beforeLoad = vi.fn() + const loader = vi.fn() + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + ssr: false, + context, + beforeLoad, + loader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + isServer: false, + }) + mockWindow.$_TSR = bootstrap + + await hydrate(router) + + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: pageRoute.id, + status: 'pending', + error: transportedError, + }) + expect(context).toHaveBeenCalledTimes(1) + expect(beforeLoad).not.toHaveBeenCalled() + expect(loader).not.toHaveBeenCalled() + + await router.load() + + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: pageRoute.id, + status: 'error', + error: transportedError, + }) + expect(context).toHaveBeenCalledTimes(1) + expect(beforeLoad).not.toHaveBeenCalled() + expect(loader).not.toHaveBeenCalled() + }) + + test('aborts the hydrated route context generation when the route is later left', async () => { + const capturedSignals: Array = [] + const rootRoute = new BaseRootRoute({}) + const oldRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/old', + context: ({ abortController }) => { + capturedSignals.push(abortController.signal) + return { hydrated: true } + }, + }) + const newRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/new', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([oldRoute, newRoute]), + history: createMemoryHistory({ initialEntries: ['/old'] }), + isServer: false, + }) + const matches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload( + mockWindow, + matches.map((match) => ({ + i: dehydrateSsrMatchId(match.id), + s: 'success' as const, + ssr: true, + u: Date.now(), + })), + ) + + await hydrate(router) + const hydratedSignal = capturedSignals.at(-1)! + await router.load() + const reloadedSignal = router.state.matches.find( + (match) => match.routeId === oldRoute.id, + )!.abortController.signal + expect(hydratedSignal.aborted).toBe(true) + expect(reloadedSignal).not.toBe(hydratedSignal) + expect(reloadedSignal.aborted).toBe(false) + + await router.navigate({ to: '/new' }) + + expect(reloadedSignal.aborted).toBe(true) + expect(router.state.resolvedLocation?.pathname).toBe('/new') + }) + + test('keeps hydrated route context alive when the initial load is synchronously superseded', async () => { + let capturedSignal: AbortSignal | undefined + const beforeLoadGate = createControlledPromise() + const rootRoute = new BaseRootRoute({ + context: ({ abortController }) => { + capturedSignal = abortController.signal + return { hydrated: true } + }, + }) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + ssr: false, + beforeLoad: async () => { + await beforeLoadGate + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + isServer: false, + }) + const matches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload( + mockWindow, + matches.map((match, index) => ({ + i: dehydrateSsrMatchId(match.id), + s: index ? ('pending' as const) : ('success' as const), + ssr: index ? false : true, + u: Date.now(), + })), + ) + + await hydrate(router) + expect(router.state.resolvedLocation).toBeUndefined() + let winningLoad: Promise | undefined + const unsubscribe = router.subscribe('onBeforeNavigate', () => { + unsubscribe() + winningLoad = router.load() + }) + + const supersededLoad = router.load() + await vi.waitFor(() => { + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: pageRoute.id, + status: 'pending', + }) + }) + expect(router.state.matches.at(-1)!.abortController.signal.aborted).toBe( + false, + ) + + beforeLoadGate.resolve() + await supersededLoad + await winningLoad + + expect(capturedSignal?.aborted).toBe(false) + expect(router.state.resolvedLocation?.pathname).toBe('/page') + }) + + test('does not hand transported context to a reentrant navigation for another location', async () => { + const rootBeforeLoad = vi.fn(() => ({ source: 'client' })) + const newLoader = vi.fn( + ({ context }: { context: { source?: string } }) => context.source, + ) + const rootRoute = new BaseRootRoute({ beforeLoad: rootBeforeLoad }) + const oldRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/old', + ssr: false, + }) + const newRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/new', + loader: newLoader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([oldRoute, newRoute]), + history: createMemoryHistory({ initialEntries: ['/old'] }), + isServer: false, + }) + const matches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload(mockWindow, [ + { + i: dehydrateSsrMatchId(matches[0]!.id), + s: 'success', + b: { source: 'server old' }, + ssr: true, + u: Date.now(), + }, + { + i: dehydrateSsrMatchId(matches[1]!.id), + s: 'pending', + ssr: false, + u: Date.now(), + }, + ]) + + await hydrate(router) + let winningNavigation: Promise | undefined + const unsubscribe = router.subscribe('onBeforeNavigate', () => { + unsubscribe() + winningNavigation = router.navigate({ to: '/new' }) + }) + + await router.load() + await winningNavigation + + expect(router.state.resolvedLocation?.pathname).toBe('/new') + expect(rootBeforeLoad).toHaveBeenCalledTimes(1) + expect(newLoader).toHaveBeenCalledTimes(1) + expect(router.state.matches.at(-1)?.loaderData).toBe('client') + }) + + test('does not hand transported context to a reentrant history-state change', async () => { + const rootBeforeLoad = vi.fn(({ location }: { location: any }) => ({ + auth: location.state.auth, + })) + const pageLoader = vi.fn( + ({ context }: { context: { auth?: string } }) => context.auth, + ) + const rootRoute = new BaseRootRoute({ beforeLoad: rootBeforeLoad }) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + ssr: false, + loader: pageLoader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + isServer: false, + }) + const matches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload(mockWindow, [ + { + i: dehydrateSsrMatchId(matches[0]!.id), + s: 'success', + b: { auth: 'server old' }, + ssr: true, + u: Date.now(), + }, + { + i: dehydrateSsrMatchId(matches[1]!.id), + s: 'pending', + ssr: false, + u: Date.now(), + }, + ]) + + await hydrate(router) + let winningNavigation: Promise | undefined + const unsubscribe = router.subscribe('onBeforeNavigate', () => { + unsubscribe() + winningNavigation = router.navigate({ + to: '/page', + state: { auth: 'client new' } as any, + }) + }) + + await router.load() + await winningNavigation + + expect(router.state.resolvedLocation?.pathname).toBe('/page') + expect(rootBeforeLoad).toHaveBeenCalledTimes(1) + expect(pageLoader).toHaveBeenCalledTimes(1) + expect(router.state.matches.at(-1)?.loaderData).toBe('client new') + }) + + test('does not hand transported context to a replaced public URL generation', async () => { + vi.spyOn(browserWindow, 'scrollTo').mockImplementation(() => undefined) + browserWindow.history.replaceState({}, '', '/public-a') + const history = createBrowserHistory({ window: browserWindow }) + const rewrite: LocationRewrite = { + input: ({ url }) => { + if (url.pathname === '/public-a' || url.pathname === '/public-b') { + url.pathname = '/internal' + } + return url + }, + output: ({ url }) => url, + } + const beforeLoad = vi.fn(({ location }: { location: any }) => ({ + publicHref: location.publicHref, + })) + const loader = vi.fn( + ({ context: routeContext }: { context: { publicHref: string } }) => + routeContext.publicHref, + ) + const rootRoute = new BaseRootRoute({ beforeLoad }) + const internalRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/internal', + ssr: false, + loader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([internalRoute]), + history, + rewrite, + isServer: false, + }) + const matches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload(mockWindow, [ + { + i: dehydrateSsrMatchId(matches[0]!.id), + s: 'success', + b: { publicHref: '/public-a' }, + ssr: true, + u: Date.now(), + }, + { + i: dehydrateSsrMatchId(matches[1]!.id), + s: 'pending', + ssr: false, + u: Date.now(), + }, + ]) + onTestFinished(() => { + history.destroy() + browserWindow.history.replaceState({}, '', '/') + }) + + await hydrate(router) + browserWindow.history.replaceState( + browserWindow.history.state, + '', + '/public-b', + ) + + await router.load() + + expect( + beforeLoad.mock.calls.map(([options]) => options.location.publicHref), + ).toEqual(['/public-b']) + expect(loader).toHaveBeenCalledTimes(1) + expect(router.state.matches.at(-1)?.loaderData).toBe('/public-b') + }) + + test('keeps a hydration handoff when a provider initializes an empty context', async () => { + const rootBeforeLoad = vi.fn(() => ({ source: 'client' })) + const rootLoader = vi.fn(() => 'client root') + const pageLoader = vi.fn( + ({ context }: { context: { source?: string } }) => context.source, + ) + const rootRoute = new BaseRootRoute({ + beforeLoad: rootBeforeLoad, + loader: rootLoader, + }) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + ssr: false, + loader: pageLoader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + isServer: false, + }) + const matches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload(mockWindow, [ + { + i: dehydrateSsrMatchId(matches[0]!.id), + s: 'success', + b: { source: 'server' }, + l: 'server root', + ssr: true, + u: Date.now(), + }, + { + i: dehydrateSsrMatchId(matches[1]!.id), + s: 'pending', + ssr: false, + u: Date.now(), + }, + ]) + + await hydrate(router) + router.update({ + ...router.options, + context: { ...router.options.context }, + }) + await router.load() + + expect(rootBeforeLoad).not.toHaveBeenCalled() + expect(rootLoader).not.toHaveBeenCalled() + expect(pageLoader).toHaveBeenCalledTimes(1) + expect(router.state.matches[0]).toMatchObject({ + context: { source: 'server' }, + loaderData: 'server root', + }) + expect(router.state.matches[1]).toMatchObject({ loaderData: 'server' }) + }) + + test('keeps transported hydration context across a cyclic router-context replacement', async () => { + const rootBeforeLoad = vi.fn(({ context }: { context: any }) => ({ + value: context.value, + })) + const rootLoader = vi.fn(() => 'client root') + const pageContext = vi.fn(({ context }: { context: any }) => ({ + value: context.value, + })) + const pageLoader = vi.fn(({ context }: { context: any }) => context.value) + const rootRoute = new BaseRootRoute({ + beforeLoad: rootBeforeLoad, + loader: rootLoader, + }) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + ssr: false, + context: pageContext, + loader: pageLoader, + }) + const initialValue: Record = {} + initialValue.self = initialValue + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + isServer: false, + context: { value: initialValue }, + }) + const matches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload(mockWindow, [ + { + i: dehydrateSsrMatchId(matches[0]!.id), + s: 'success', + b: { value: initialValue }, + l: 'server root', + ssr: true, + u: Date.now(), + }, + { + i: dehydrateSsrMatchId(matches[1]!.id), + s: 'pending', + ssr: false, + u: Date.now(), + }, + ]) + + await hydrate(router) + const nextValue: Record = {} + nextValue.self = nextValue + router.update({ + ...router.options, + context: { value: nextValue }, + }) + + await expect(router.load()).resolves.toBeUndefined() + + expect(rootBeforeLoad).not.toHaveBeenCalled() + expect(pageContext).toHaveBeenCalledTimes(1) + expect(rootLoader).not.toHaveBeenCalled() + expect(pageLoader).toHaveBeenCalledTimes(1) + expect(router.state.matches[0]?.loaderData).toBe('server root') + expect(router.state.matches[1]?.loaderData).toBe(initialValue) + }) + + test('does not hand transported context across an invalidation', async () => { + const rootBeforeLoad = vi.fn(({ context }: { context: any }) => ({ + auth: context.auth, + })) + const pageLoader = vi.fn( + ({ context }: { context: { auth?: string } }) => context.auth, + ) + const rootRoute = new BaseRootRoute({ beforeLoad: rootBeforeLoad }) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + ssr: false, + loader: pageLoader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + isServer: false, + context: { auth: 'server old' }, + }) + const matches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload(mockWindow, [ + { + i: dehydrateSsrMatchId(matches[0]!.id), + s: 'success', + b: { auth: 'server old' }, + ssr: true, + u: Date.now(), + }, + { + i: dehydrateSsrMatchId(matches[1]!.id), + s: 'pending', + ssr: false, + u: Date.now(), + }, + ]) + + await hydrate(router) + router.update({ + ...router.options, + context: { auth: 'client new' }, + }) + await router.invalidate() + + expect(rootBeforeLoad).toHaveBeenCalledTimes(1) + expect(pageLoader).toHaveBeenCalledTimes(1) + expect(router.state.matches.at(-1)?.loaderData).toBe('client new') + }) + + test('rejects a hydration handoff when its prefix identity changes before finish', async () => { + let generation = 'server' + const rootBeforeLoad = vi.fn(() => ({ source: 'client' })) + const pageLoader = vi.fn( + ({ context }: { context: { source?: string } }) => context.source, + ) + const rootRoute = new BaseRootRoute({ + loaderDeps: () => ({ generation }), + beforeLoad: rootBeforeLoad, + }) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + ssr: false, + loader: pageLoader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + isServer: false, + }) + const matches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload(mockWindow, [ + { + i: dehydrateSsrMatchId(matches[0]!.id), + s: 'success', + b: { source: 'server' }, + ssr: true, + u: Date.now(), + }, + { + i: dehydrateSsrMatchId(matches[1]!.id), + s: 'pending', + ssr: false, + u: Date.now(), + }, + ]) + + await hydrate(router) + const unsubscribe = router.subscribe('onBeforeNavigate', () => { + unsubscribe() + generation = 'client' + }) + + await router.load() + + expect(rootBeforeLoad).toHaveBeenCalledTimes(1) + expect(pageLoader).toHaveBeenCalledTimes(1) + expect(router.state.matches.at(-1)?.loaderData).toBe('client') + }) + + test('turns a client route-context reconstruction failure into a route error', async () => { + const contextError = new Error('client context failed') + const onError = vi.fn() + const loader = vi.fn(() => 'client data') + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + context: () => { + throw contextError + }, + loader, + onError, + errorComponent: () => 'Page error', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + isServer: false, + }) + const matches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload( + mockWindow, + matches.map((match) => ({ + i: dehydrateSsrMatchId(match.id), + s: 'success' as const, + l: match.routeId === pageRoute.id ? 'server data' : undefined, + ssr: true, + u: Date.now(), + })), + ) + + await expect(hydrate(router)).resolves.toBeUndefined() + + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: pageRoute.id, + status: 'success', + error: undefined, + loaderData: 'server data', + }) + expect(router.state.resolvedLocation).toBeUndefined() + expect(onError).not.toHaveBeenCalled() + expect(loader).not.toHaveBeenCalled() + + await router.load() + + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: pageRoute.id, + status: 'error', + error: contextError, + loaderData: 'server data', + }) + expect(onError).toHaveBeenCalledTimes(1) + expect(onError).toHaveBeenCalledWith(contextError) + expect(loader).not.toHaveBeenCalled() + }) + + test('preserves route-context redirect control during hydration reconstruction', async () => { + const rootRoute = new BaseRootRoute({}) + const sourceRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/source', + context: () => { + throw redirect({ to: '/target' }) + }, + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([sourceRoute, targetRoute]), + history: createMemoryHistory({ initialEntries: ['/source'] }), + isServer: false, + }) + const matches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload( + mockWindow, + matches.map((match) => ({ + i: dehydrateSsrMatchId(match.id), + s: 'success' as const, + ssr: true, + u: Date.now(), + })), + ) + + await expect(hydrate(router)).resolves.toBeUndefined() + + expect(router.state.location.pathname).toBe('/source') + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: sourceRoute.id, + status: 'success', + }) + expect(router.state.resolvedLocation).toBeUndefined() + + await router.load() + + expect(router.state.location.pathname).toBe('/target') + expect(router.state.resolvedLocation?.pathname).toBe('/target') + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: targetRoute.id, + status: 'success', + }) + }) + + test('keeps a transported terminal route-context error authoritative', async () => { + const serverError = new Error('server context failed') + const serverRootRoute = new BaseRootRoute({}) + const serverPageRoute = new BaseRoute({ + getParentRoute: () => serverRootRoute, + path: '/page', + context: () => { + throw serverError + }, + errorComponent: () => 'Page error', + }) + const serverRouter = createTestRouter({ + routeTree: serverRootRoute.addChildren([serverPageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + isServer: true, + }) + const bootstrap = await dehydrateToBootstrap(serverRouter) + const transportedError = bootstrap.router?.matches.at(-1)?.e + + const clientContextError = new Error('client context failed') + const beforeLoad = vi.fn() + const loader = vi.fn( + ({ context }: { context: { recovered?: boolean } }) => context.recovered, + ) + const onError = vi.fn() + const clientContext = vi.fn((): { recovered: boolean } => { + throw clientContextError + }) + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + context: clientContext, + beforeLoad, + loader, + onError, + errorComponent: () => 'Page error', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + isServer: false, + }) + mockWindow.$_TSR = bootstrap + + await expect(hydrate(router)).resolves.toBeUndefined() + + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: pageRoute.id, + status: 'error', + error: transportedError, + }) + expect(router.state.matches.at(-1)?.error).not.toBe(clientContextError) + expect(router.state.resolvedLocation?.pathname).toBe('/page') + expect(onError).not.toHaveBeenCalled() + expect(beforeLoad).not.toHaveBeenCalled() + expect(loader).not.toHaveBeenCalled() + + clientContext.mockImplementation(() => ({ recovered: true })) + await router.invalidate() + + expect(clientContext).toHaveBeenCalledTimes(2) + expect(router.state.matches.at(-1)).toMatchObject({ + status: 'success', + context: { recovered: true }, + loaderData: true, + }) + }) + + test('turns a hydrated normal-component chunk failure into a route error without rerunning loader data', async () => { + const chunkError = new Error('page component failed to load') + const componentPreload = vi.fn(() => Promise.reject(chunkError)) + const Page = Object.assign(() => 'Page', { preload: componentPreload }) + const loader = vi.fn(() => 'client data') + const onError = vi.fn() + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + component: Page, + errorComponent: () => 'Page error', + loader, + onError, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + isServer: false, + }) + const matches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload( + mockWindow, + matches.map((match) => ({ + i: dehydrateSsrMatchId(match.id), + s: 'success' as const, + l: match.routeId === pageRoute.id ? 'server data' : undefined, + ssr: true, + u: Date.now(), + })), + ) + + await expect(hydrate(router)).resolves.toBeUndefined() + + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: pageRoute.id, + status: 'success', + error: undefined, + loaderData: 'server data', + }) + expect(router.state.resolvedLocation).toBeUndefined() + expect(onError).not.toHaveBeenCalled() + expect(loader).not.toHaveBeenCalled() + + await router.load() + + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: pageRoute.id, + status: 'error', + error: chunkError, + loaderData: 'server data', + }) + expect(onError).toHaveBeenCalledTimes(1) + expect(onError).toHaveBeenCalledWith(chunkError) + expect(loader).not.toHaveBeenCalled() + }) + + test('retries from the earliest failed hydration chunk regardless of rejection order', async () => { + const rootChunkError = new Error('root component failed to load') + const childChunkError = new Error('child component failed to load') + const rootChunkGate = createControlledPromise() + const rootChunkStarted = createControlledPromise() + const RootComponent = Object.assign(() => 'Root', { + preload: () => { + rootChunkStarted.resolve() + return rootChunkGate + }, + }) + const childComponentPreload = vi.fn(() => Promise.reject(childChunkError)) + const ChildComponent = Object.assign(() => 'Child', { + preload: childComponentPreload, + }) + const rootContext = vi.fn(() => ({})) + const rootRoute = new BaseRootRoute({ + component: RootComponent, + context: rootContext, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/child', + component: ChildComponent, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([childRoute]), + history: createMemoryHistory({ initialEntries: ['/child'] }), + isServer: false, + }) + const matches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload( + mockWindow, + matches.map((match) => ({ + i: dehydrateSsrMatchId(match.id), + s: 'success' as const, + ssr: true, + u: Date.now(), + })), + ) + + const hydration = hydrate(router) + await rootChunkStarted + await vi.waitFor(() => { + expect(childComponentPreload).toHaveBeenCalledTimes(1) + }) + rootChunkGate.reject(rootChunkError) + await expect(hydration).resolves.toBeUndefined() + + expect(rootContext).not.toHaveBeenCalled() + expect(router.state.resolvedLocation).toBeUndefined() + }) + + test('does not wait for descendant chunks after an earlier chunk fails', async () => { + const rootChunkError = new Error('root component failed to load') + const childChunkGate = createControlledPromise() + const childChunkStarted = createControlledPromise() + const RootComponent = Object.assign(() => 'Root', { + preload: () => Promise.reject(rootChunkError), + }) + const ChildComponent = Object.assign(() => 'Child', { + preload: () => { + childChunkStarted.resolve() + return childChunkGate + }, + }) + const rootContext = vi.fn(() => ({})) + const rootRoute = new BaseRootRoute({ + component: RootComponent, + context: rootContext, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/child', + component: ChildComponent, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([childRoute]), + history: createMemoryHistory({ initialEntries: ['/child'] }), + isServer: false, + }) + const matches = router.matchRoutes(router.stores.location.get()) + installHydrationPayload( + mockWindow, + matches.map((match) => ({ + i: dehydrateSsrMatchId(match.id), + s: 'success' as const, + ssr: true, + u: Date.now(), + })), + ) + + let settled = false + const hydration = hydrate(router).then(() => { + settled = true + }) + try { + await childChunkStarted + await vi.waitFor(() => { + expect(settled).toBe(true) + }) + } finally { + childChunkGate.resolve() + await hydration + } + + expect(rootContext).not.toHaveBeenCalled() + expect(router.state.resolvedLocation).toBeUndefined() + }) +}) diff --git a/packages/router-core/tests/public-preload-lane-contract.test.ts b/packages/router-core/tests/public-preload-lane-contract.test.ts index b57905548e..9be86c1b03 100644 --- a/packages/router-core/tests/public-preload-lane-contract.test.ts +++ b/packages/router-core/tests/public-preload-lane-contract.test.ts @@ -1,14 +1,246 @@ -import { describe, expect, test, vi } from 'vitest' +import { afterEach, describe, expect, onTestFinished, test, vi } from 'vitest' import { createMemoryHistory } from '@tanstack/history' import { BaseRootRoute, BaseRoute, createControlledPromise, + notFound, redirect, } from '../src' import { createTestRouter } from './routerTestUtils' +afterEach(() => { + vi.useRealTimers() +}) + describe('public preload lane contracts', () => { + test('completed preload matches retain context without caching beforeLoad context', async () => { + let beforeLoadGeneration = 0 + const loader = vi.fn(() => 'loader data') + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + context: () => ({ routeContext: true }), + beforeLoad: () => ({ generation: ++beforeLoadGeneration }), + loader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, targetRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + const preloaded = await router.preloadRoute({ to: '/target' }) + + expect(preloaded?.at(-1)?.context).toEqual({ + routeContext: true, + generation: 1, + }) + + await router.navigate({ to: '/target' }) + + expect(router.state.matches.at(-1)?.context).toEqual({ + routeContext: true, + generation: 2, + }) + expect(loader).toHaveBeenCalledTimes(1) + }) + + test('every active preload runs beforeLoad even for an identical location', async () => { + const gates = new Map< + number, + ReturnType> + >() + const beforeLoad = vi.fn(({ search }: { search: { version: number } }) => { + let gate = gates.get(search.version) + if (!gate) { + gate = createControlledPromise() + gates.set(search.version, gate) + } + return gate + }) + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + validateSearch: (search: Record) => ({ + version: Number(search.version), + }), + beforeLoad, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, targetRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + stringifySearch: () => '', + }) + + await router.load() + const first = router.preloadRoute({ + to: '/target', + search: { version: 1 }, + }) + await vi.waitFor(() => expect(beforeLoad).toHaveBeenCalledTimes(1)) + const second = router.preloadRoute({ + to: '/target', + search: { version: 2 }, + }) + await vi.waitFor(() => expect(beforeLoad).toHaveBeenCalledTimes(2)) + gates.get(1)!.resolve(undefined) + gates.get(2)!.resolve(undefined) + await Promise.all([first, second]) + + const third = router.preloadRoute({ + to: '/target', + search: { version: 3 }, + }) + await vi.waitFor(() => expect(beforeLoad).toHaveBeenCalledTimes(3)) + const identical = router.preloadRoute({ + to: '/target', + search: { version: 3 }, + }) + await vi.waitFor(() => expect(beforeLoad).toHaveBeenCalledTimes(4)) + gates.get(3)!.resolve(undefined) + await Promise.all([third, identical]) + }) + + test('navigation reruns beforeLoad while sharing an identical preload loader', async () => { + const beforeLoadGate = createControlledPromise() + const beforeLoad = vi.fn(async ({ preload }: { preload: boolean }) => { + await beforeLoadGate + return { source: preload ? 'preload' : 'navigation' } + }) + const loader = vi.fn(() => 'loader data') + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + beforeLoad, + loader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, targetRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + const preload = router.preloadRoute({ to: '/target' }) + await vi.waitFor(() => expect(beforeLoad).toHaveBeenCalledTimes(1)) + + const navigation = router.navigate({ to: '/target' }) + await vi.waitFor(() => expect(beforeLoad).toHaveBeenCalledTimes(2)) + + beforeLoadGate.resolve() + await Promise.all([preload, navigation]) + + expect(beforeLoad.mock.calls.map(([context]) => context.preload)).toEqual([ + true, + false, + ]) + expect(loader).toHaveBeenCalledTimes(1) + expect(router.state.matches.at(-1)).toMatchObject({ + context: { source: 'navigation' }, + loaderData: 'loader data', + }) + }) + + test('a depth-20 navigation does not adopt a depth-0 active preload redirect', async () => { + const redirectGate = createControlledPromise() + const sharedBeforeLoad = vi.fn(async (_context: { preload: boolean }) => { + await redirectGate + throw redirect({ to: '/target' }) + }) + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const hopRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/hop/$hop', + beforeLoad: ({ params }) => { + const hop = Number(params.hop) + if (hop < 19) { + throw redirect({ + to: '/hop/$hop', + params: { hop: String(hop + 1) }, + } as any) + } + throw redirect({ to: '/shared' }) + }, + }) + const sharedRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/shared', + beforeLoad: sharedBeforeLoad, + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + indexRoute, + hopRoute, + sharedRoute, + targetRoute, + ]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + let preload: Promise | undefined + let navigation: Promise | undefined + onTestFinished(async () => { + redirectGate.resolve() + const activeWork: Array> = [] + if (preload) { + activeWork.push(preload) + } + if (navigation) { + activeWork.push(navigation) + } + await Promise.allSettled(activeWork) + }) + + await router.load() + preload = router.preloadRoute({ to: '/shared' }) + await vi.waitFor(() => expect(sharedBeforeLoad).toHaveBeenCalledOnce()) + + navigation = router.navigate({ + to: '/hop/$hop', + params: { hop: '0' }, + } as any) + await vi.waitFor(() => expect(sharedBeforeLoad).toHaveBeenCalledTimes(2)) + + redirectGate.resolve() + await Promise.all([preload, navigation]) + + expect( + sharedBeforeLoad.mock.calls.map(([context]) => context.preload), + ).toEqual([true, false]) + expect(router.state.location.pathname).toBe('/shared') + expect( + router.state.matches.find((match) => match.status !== 'success'), + ).toMatchObject({ + routeId: rootRoute.id, + status: 'error', + error: expect.objectContaining({ message: 'Too many redirects' }), + }) + expect(router.state.status).toBe('idle') + }) + test.each([ { difference: 'pathname', @@ -142,63 +374,136 @@ describe('public preload lane contracts', () => { }, ) - test('navigation does not adopt beforeLoad from an active preload with different params', async () => { - const beforeLoadGate = createControlledPromise() - const beforeLoad = vi.fn(async ({ preload }: { preload: boolean }) => { - await beforeLoadGate - return { preload } - }) + test('identical active preloads await the same redirect chain', async () => { + const redirectGate = createControlledPromise() + const loaderGate = createControlledPromise() + const redirectedLoader = vi.fn(() => loaderGate) const rootRoute = new BaseRootRoute({}) const indexRoute = new BaseRoute({ getParentRoute: () => rootRoute, path: '/', }) - const itemsRoute = new BaseRoute({ + const sourceRoute = new BaseRoute({ getParentRoute: () => rootRoute, - path: '/items/$itemId', - beforeLoad, - }) - const firstRoute = new BaseRoute({ - getParentRoute: () => itemsRoute, - path: '/first', + path: '/source', + beforeLoad: async () => { + await redirectGate + throw redirect({ to: '/target' }) + }, }) - const secondRoute = new BaseRoute({ - getParentRoute: () => itemsRoute, - path: '/second', + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + loader: redirectedLoader, }) const router = createTestRouter({ - routeTree: rootRoute.addChildren([ - indexRoute, - itemsRoute.addChildren([firstRoute, secondRoute]), - ]), + routeTree: rootRoute.addChildren([indexRoute, sourceRoute, targetRoute]), history: createMemoryHistory({ initialEntries: ['/'] }), }) await router.load() - const preload = router.preloadRoute({ - to: '/items/$itemId/first', - params: { itemId: 'one' }, - }) - await vi.waitFor(() => expect(beforeLoad).toHaveBeenCalledTimes(1)) - - const navigation = router.navigate({ - to: '/items/$itemId/first', - params: { itemId: 'two' }, + const first = router.preloadRoute({ to: '/source' }) + const second = router.preloadRoute({ to: '/source' }) + let secondSettled = false + void second.then(() => { + secondSettled = true }) - await vi.waitFor(() => expect(beforeLoad).toHaveBeenCalledTimes(2)) - beforeLoadGate.resolve() - await Promise.all([preload, navigation]) + redirectGate.resolve() + await vi.waitFor(() => expect(redirectedLoader).toHaveBeenCalledOnce()) + expect(secondSettled).toBe(false) - expect(beforeLoad.mock.calls.map(([context]) => context.preload)).toEqual([ - true, - false, - ]) - expect(router.state.location.pathname).toBe('/items/two/first') - expect(router.state.location.search).toEqual({}) - expect(router.state.matches.at(-1)?.context).toEqual({ preload: false }) + loaderGate.resolve() + await Promise.all([first, second]) + expect(secondSettled).toBe(true) }) + test.each([ + { + name: 'route suffix', + preload: { to: '/items/one/first' }, + navigation: { to: '/items/one/second' }, + pathname: '/items/one/second', + search: {}, + }, + { + name: 'params', + preload: { to: '/items/one/first' }, + navigation: { to: '/items/two/first' }, + pathname: '/items/two/first', + search: {}, + }, + { + name: 'search', + preload: { + to: '/items/one/first', + search: { view: 'summary' }, + }, + navigation: { + to: '/items/one/first', + search: { view: 'detail' }, + }, + pathname: '/items/one/first', + search: { view: 'detail' }, + }, + ])( + 'navigation does not adopt beforeLoad from an active preload with different $name', + async ({ + preload: preloadOptions, + navigation: navigationOptions, + pathname, + search, + }) => { + const beforeLoadGate = createControlledPromise() + const beforeLoad = vi.fn(async ({ preload }: { preload: boolean }) => { + await beforeLoadGate + return { preload } + }) + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const itemsRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/items/$itemId', + beforeLoad, + }) + const firstRoute = new BaseRoute({ + getParentRoute: () => itemsRoute, + path: '/first', + }) + const secondRoute = new BaseRoute({ + getParentRoute: () => itemsRoute, + path: '/second', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + indexRoute, + itemsRoute.addChildren([firstRoute, secondRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + const preload = router.preloadRoute(preloadOptions as any) + await vi.waitFor(() => expect(beforeLoad).toHaveBeenCalledTimes(1)) + + const navigation = router.navigate(navigationOptions as any) + await vi.waitFor(() => expect(beforeLoad).toHaveBeenCalledTimes(2)) + + beforeLoadGate.resolve() + await Promise.all([preload, navigation]) + + expect(beforeLoad.mock.calls.map(([context]) => context.preload)).toEqual( + [true, false], + ) + expect(router.state.location.pathname).toBe(pathname) + expect(router.state.location.search).toEqual(search) + expect(router.state.matches.at(-1)?.context).toEqual({ preload: false }) + }, + ) + test('a different full navigation lane reruns beforeLoad but reuses active same-id loader work', async () => { const loaderGate = createControlledPromise() const beforeLoad = vi.fn(({ preload }: { preload: boolean }) => ({ @@ -257,9 +562,15 @@ describe('public preload lane contracts', () => { }) }) - test('different same-href preload lanes rerun beforeLoad but share loader work by match id', async () => { + test('reserved preload loader work survives preload completion while navigation beforeLoad is pending', async () => { const loaderGate = createControlledPromise() - const beforeLoad = vi.fn() + const navigationBeforeLoadGate = createControlledPromise() + const beforeLoad = vi.fn(async ({ preload }: { preload: boolean }) => { + if (!preload) { + await navigationBeforeLoadGate + } + return { source: preload ? 'preload' : 'navigation' } + }) const loader = vi.fn(() => loaderGate) const rootRoute = new BaseRootRoute({}) const indexRoute = new BaseRoute({ @@ -269,9 +580,6 @@ describe('public preload lane contracts', () => { const parentRoute = new BaseRoute({ getParentRoute: () => rootRoute, path: '/parent', - validateSearch: (search: Record) => ({ - version: Number(search.version), - }), beforeLoad, loader, }) @@ -289,35 +597,192 @@ describe('public preload lane contracts', () => { parentRoute.addChildren([firstRoute, secondRoute]), ]), history: createMemoryHistory({ initialEntries: ['/'] }), - stringifySearch: () => '', }) await router.load() - const first = router.preloadRoute({ - to: '/parent/first', - search: { version: 1 }, - }) + const preload = router.preloadRoute({ to: '/parent/first' }) await vi.waitFor(() => expect(loader).toHaveBeenCalledTimes(1)) - const second = router.preloadRoute({ - to: '/parent/first', - search: { version: 2 }, - }) + + const navigation = router.navigate({ to: '/parent/second' }) await vi.waitFor(() => expect(beforeLoad).toHaveBeenCalledTimes(2)) + loaderGate.resolve('reserved parent data') + await preload expect(loader).toHaveBeenCalledTimes(1) - loaderGate.resolve('parent data') - await Promise.all([first, second]) - expect(beforeLoad).toHaveBeenCalledTimes(2) + navigationBeforeLoadGate.resolve() + await navigation + + expect(beforeLoad.mock.calls.map(([context]) => context.preload)).toEqual([ + true, + false, + ]) expect(loader).toHaveBeenCalledTimes(1) + expect(router.state.matches.at(-1)?.routeId).toBe(secondRoute.id) + expect( + router.state.matches.find((match) => match.routeId === parentRoute.id), + ).toMatchObject({ + context: { source: 'navigation' }, + loaderData: 'reserved parent data', + }) }) - test('completed preloads cache loader data but not beforeLoad context', async () => { - const beforeLoad = vi.fn(({ preload }: { preload: boolean }) => ({ - source: preload ? 'preload' : 'navigation', - })) - const loader = vi.fn( - ({ context }: { context: { source: string } }) => context.source, + test('a background refresh consumes reserved preload work after that preload completes', async () => { + const refreshGate = createControlledPromise() + const navigationBeforeLoadGate = createControlledPromise() + const beforeLoad = vi.fn( + async ({ + preload, + search, + }: { + preload: boolean + search: { revision: number } + }) => { + if (!preload && search.revision === 2) { + await navigationBeforeLoadGate + } + return { source: preload ? 'preload' : 'navigation' } + }, + ) + const loader = vi.fn(() => + loader.mock.calls.length === 1 ? 'initial data' : refreshGate, + ) + const rootRoute = new BaseRootRoute({}) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + validateSearch: (search: Record) => ({ + revision: Number(search.revision ?? 0), + }), + beforeLoad, + shouldReload: ({ location }) => + (location.search as { revision: number }).revision > 0, + loader: { + handler: loader, + staleReloadMode: 'background', + }, + }) + const firstRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/first', + }) + const secondRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/second', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + parentRoute.addChildren([firstRoute, secondRoute]), + ]), + history: createMemoryHistory({ + initialEntries: ['/parent/first?revision=0'], + }), + }) + + await router.load() + const preload = router.preloadRoute({ + to: '/parent/first', + search: { revision: 1 }, + }) + await vi.waitFor(() => expect(loader).toHaveBeenCalledTimes(2)) + + let navigationSettled = false + const navigation = router + .navigate({ + to: '/parent/second', + search: { revision: 2 }, + }) + .then(() => { + navigationSettled = true + }) + await vi.waitFor(() => expect(beforeLoad).toHaveBeenCalledTimes(3)) + + refreshGate.resolve('refreshed data') + await preload + expect(loader).toHaveBeenCalledTimes(2) + expect(navigationSettled).toBe(false) + + navigationBeforeLoadGate.resolve() + await navigation + + expect(beforeLoad.mock.calls.map(([context]) => context.preload)).toEqual([ + false, + true, + false, + ]) + expect(loader).toHaveBeenCalledTimes(2) + expect(router.state.matches.at(-1)?.routeId).toBe(secondRoute.id) + expect( + router.state.matches.find((match) => match.routeId === parentRoute.id), + ).toMatchObject({ + context: { source: 'navigation' }, + loaderData: 'refreshed data', + preload: false, + isFetching: false, + }) + }) + + test('different same-href preload lanes rerun beforeLoad but share loader work by match id', async () => { + const loaderGate = createControlledPromise() + const beforeLoad = vi.fn() + const loader = vi.fn(() => loaderGate) + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + validateSearch: (search: Record) => ({ + version: Number(search.version), + }), + beforeLoad, + loader, + }) + const firstRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/first', + }) + const secondRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/second', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + indexRoute, + parentRoute.addChildren([firstRoute, secondRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/'] }), + stringifySearch: () => '', + }) + + await router.load() + const first = router.preloadRoute({ + to: '/parent/first', + search: { version: 1 }, + }) + await vi.waitFor(() => expect(loader).toHaveBeenCalledTimes(1)) + const second = router.preloadRoute({ + to: '/parent/first', + search: { version: 2 }, + }) + await vi.waitFor(() => expect(beforeLoad).toHaveBeenCalledTimes(2)) + + expect(loader).toHaveBeenCalledTimes(1) + loaderGate.resolve('parent data') + await Promise.all([first, second]) + + expect(beforeLoad).toHaveBeenCalledTimes(2) + expect(loader).toHaveBeenCalledTimes(1) + }) + + test('completed preloads cache loader data but not beforeLoad context', async () => { + const beforeLoad = vi.fn(({ preload }: { preload: boolean }) => ({ + source: preload ? 'preload' : 'navigation', + })) + const loader = vi.fn( + ({ context }: { context: { source: string } }) => context.source, ) const rootRoute = new BaseRootRoute({}) const indexRoute = new BaseRoute({ @@ -352,7 +817,43 @@ describe('public preload lane contracts', () => { }) }) - test('navigation retries an adopted preload lane that failed', async () => { + test('completed preloads reuse same-id route context and loader data', async () => { + const context = ({ preload }: { preload: boolean }) => ({ + source: preload ? 'preload' : 'navigation', + }) + const loader = vi.fn( + ({ context: value }: { context: { source: string } }) => value.source, + ) + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + staleTime: Infinity, + preloadStaleTime: Infinity, + context, + loader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, targetRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + await router.preloadRoute({ to: '/target' }) + await router.navigate({ to: '/target' }) + + expect(loader).toHaveBeenCalledTimes(1) + expect(router.state.matches.at(-1)).toMatchObject({ + context: { source: 'preload' }, + loaderData: 'preload', + }) + }) + + test('navigation runs independently when an active preload fails', async () => { const preloadGate = createControlledPromise() const beforeLoad = vi.fn(async ({ preload }: { preload: boolean }) => { if (preload) { @@ -399,6 +900,234 @@ describe('public preload lane contracts', () => { }) }) + test('a later retry reuses successful loader data from the failed preload lane', async () => { + const childGate = createControlledPromise() + const childFailure = new Error('preload child failed') + const beforeLoad = vi.fn(({ preload }: { preload: boolean }) => ({ + source: preload ? 'preload' : 'navigation', + })) + let parentSignal: AbortSignal | undefined + const parentLoader = vi.fn(({ abortController }) => { + parentSignal = abortController.signal + return 'parent data' + }) + const childLoader = vi.fn(async ({ preload }: { preload: boolean }) => { + if (preload) { + await childGate + throw childFailure + } + return 'child data' + }) + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + beforeLoad, + loader: parentLoader, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loader: childLoader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + indexRoute, + parentRoute.addChildren([childRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + const preload = router.preloadRoute({ to: '/parent/child' }) + await vi.waitFor(() => expect(childLoader).toHaveBeenCalledTimes(1)) + + childGate.resolve() + const terminal = await preload + + expect( + terminal?.find((match) => match.routeId === parentRoute.id), + ).toMatchObject({ + status: 'success', + loaderData: 'parent data', + }) + expect( + terminal?.find((match) => match.routeId === childRoute.id), + ).toMatchObject({ + status: 'error', + error: childFailure, + }) + + await router.navigate({ to: '/parent/child' }) + + expect(beforeLoad.mock.calls.map(([context]) => context.preload)).toEqual([ + true, + false, + ]) + expect(parentLoader).toHaveBeenCalledTimes(1) + expect(parentSignal?.aborted).toBe(false) + expect(childLoader).toHaveBeenCalledTimes(2) + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: childRoute.id, + status: 'success', + loaderData: 'child data', + }) + }) + + test('a terminal preload stays terminal publicly while caching its successful loader generation', async () => { + const loader = vi.fn( + ({ deps }: { deps: { version: number } }) => deps.version, + ) + const rootRoute = new BaseRootRoute({ + validateSearch: (search: Record) => ({ + version: Number(search.version ?? 0), + }), + loaderDeps: ({ search }) => ({ version: search.version }), + shouldReload: false, + loader, + }) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + beforeLoad: () => { + throw notFound({ routeId: rootRoute.id as never }) + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, targetRoute]), + history: createMemoryHistory({ + initialEntries: ['/?version=0'], + }), + }) + + await router.load() + const terminal = await router.preloadRoute({ + to: '/target', + search: { version: 1 }, + }) + + expect(loader).toHaveBeenCalledTimes(2) + expect(terminal).toHaveLength(2) + expect(terminal?.[0]).toMatchObject({ + routeId: rootRoute.id, + status: 'success', + _notFound: true, + error: { isNotFound: true }, + loaderData: 1, + }) + expect(terminal?.[1]).toMatchObject({ + routeId: targetRoute.id, + status: 'pending', + }) + + await router.navigate({ + to: '/target', + search: { version: 1 }, + }) + + expect(loader).toHaveBeenCalledTimes(2) + expect(router.state.matches[0]).toMatchObject({ + status: 'success', + _notFound: true, + loaderData: 1, + }) + expect(router.state.matches[1]).toMatchObject({ + routeId: targetRoute.id, + status: 'pending', + }) + }) + + test('a terminal preload started during navigation borrows its loader generation', async () => { + const loaderGate = createControlledPromise() + const loader = vi.fn(() => + loader.mock.calls.length === 1 ? 'initial data' : loaderGate, + ) + const rootRoute = new BaseRootRoute({ + shouldReload: true, + loader, + }) + const beforeLoad = vi.fn(() => { + throw notFound({ routeId: rootRoute.id as never }) + }) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + beforeLoad, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, targetRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + const navigation = router.navigate({ to: '/target' }) + await vi.waitFor(() => expect(loader).toHaveBeenCalledTimes(2)) + const preload = router.preloadRoute({ to: '/target' }) + await vi.waitFor(() => expect(beforeLoad).toHaveBeenCalledTimes(2)) + + expect(loader).toHaveBeenCalledTimes(2) + loaderGate.resolve('navigation data') + const [, terminal] = await Promise.all([navigation, preload]) + + expect(loader).toHaveBeenCalledTimes(2) + expect(terminal?.[0]).toMatchObject({ + routeId: rootRoute.id, + _notFound: true, + loaderData: 'navigation data', + }) + }) + + test('leaving a route preserves its newer same-id preload generation', async () => { + const loader = vi.fn(() => `loader data ${loader.mock.calls.length}`) + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + validateSearch: (search: Record) => ({ + revision: Number(search.revision ?? 1), + }), + shouldReload: ({ location }) => + (location.search as { revision: number }).revision === 2, + loader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, targetRoute]), + history: createMemoryHistory({ + initialEntries: ['/target?revision=1'], + }), + }) + + await router.load() + await router.preloadRoute({ + to: '/target', + search: { revision: 2 }, + }) + await router.navigate({ to: '/' }) + await router.navigate({ + to: '/target', + search: { revision: 1 }, + }) + + expect(loader).toHaveBeenCalledTimes(2) + expect(router.state.matches.at(-1)?.loaderData).toBe('loader data 2') + }) + test('an expired same-id preload does not displace fresh unloaded data', async () => { const loader = vi.fn(() => `loader data ${loader.mock.calls.length}`) const rootRoute = new BaseRootRoute({}) @@ -475,6 +1204,77 @@ describe('public preload lane contracts', () => { expect(router.state.matches.at(-1)?.loaderData).toBe('loader data 3') }) + test('a hidden terminal suffix does not evict a newer same-id preload', async () => { + const failingBeforeLoadStarted = createControlledPromise() + const failingBeforeLoadGate = createControlledPromise() + const loader = vi.fn(() => 'preloaded child data') + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + validateSearch: (search: Record) => ({ + fail: search.fail === true, + }), + beforeLoad: async ({ search }) => { + if (search.fail) { + failingBeforeLoadStarted.resolve() + await failingBeforeLoadGate + throw new Error('parent failed') + } + }, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + staleTime: Infinity, + preloadStaleTime: Infinity, + loader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + indexRoute, + parentRoute.addChildren([childRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + await router.load() + const failingNavigation = router.navigate({ + to: '/parent/child', + search: { fail: true }, + }) + await failingBeforeLoadStarted + + await router.preloadRoute({ + to: '/parent/child', + search: { fail: false }, + }) + expect(loader).toHaveBeenCalledTimes(1) + + failingBeforeLoadGate.resolve() + await failingNavigation + expect(router.state.matches).toHaveLength(3) + expect(router.state.matches[1]).toMatchObject({ + routeId: parentRoute.id, + status: 'error', + }) + + await router.navigate({ + to: '/parent/child', + search: { fail: false }, + }) + + expect(loader).toHaveBeenCalledTimes(1) + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: childRoute.id, + loaderData: 'preloaded child data', + }) + }) + test('preload false skips speculation but performs a blocking navigation load', async () => { const loaderGate = createControlledPromise() const loader = vi.fn(() => loaderGate) @@ -515,6 +1315,76 @@ describe('public preload lane contracts', () => { }) }) + test('preloads the target of a twenty-hop redirect chain', async () => { + const beforeLoad = vi.fn(({ params }) => { + const hop = Number(params.hop) + if (hop < 20) { + throw redirect({ + to: '/hop/$hop', + params: { hop: String(hop + 1) }, + } as any) + } + }) + const rootRoute = new BaseRootRoute({}) + const hopRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/hop/$hop', + beforeLoad, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([hopRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + const matches = await router.preloadRoute({ + to: '/hop/$hop', + params: { hop: '0' }, + } as any) + + expect(beforeLoad).toHaveBeenCalledTimes(21) + expect(matches?.at(-1)).toMatchObject({ + routeId: hopRoute.id, + status: 'success', + params: { hop: '20' }, + }) + }) + + test('stops a preload after the twenty-hop redirect limit', async () => { + const beforeLoad = vi.fn(({ params }) => { + const hop = Number(params.hop) + if (hop < 22) { + throw redirect({ + to: '/hop/$hop', + params: { hop: String(hop + 1) }, + } as any) + } + }) + const rootRoute = new BaseRootRoute({}) + const hopRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/hop/$hop', + beforeLoad, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([hopRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + const matches = await router.preloadRoute({ + to: '/hop/$hop', + params: { hop: '0' }, + } as any) + + expect(beforeLoad).toHaveBeenCalledTimes(21) + expect(matches?.find((match) => match.status !== 'success')).toMatchObject({ + routeId: rootRoute.id, + status: 'error', + error: expect.objectContaining({ + message: 'Too many redirects', + }), + }) + }) + test('forwards a document redirect at the redirect limit', async () => { const beforeLoad = vi.fn(({ params }) => { const hop = Number(params.hop) @@ -543,4 +1413,29 @@ describe('public preload lane contracts', () => { expect(beforeLoad).toHaveBeenCalledTimes(21) expect(matches).toBeUndefined() }) + + test('reports a chunk redirect limit at root', async () => { + const chunkError = new Error('route chunk failed') + const rootRoute = new BaseRootRoute({}) + const loopRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/loop', + onError: () => { + throw redirect({ to: '/loop' }) + }, + }).lazy(() => Promise.reject(chunkError)) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([loopRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + const matches = await router.preloadRoute({ to: '/loop' }) + + const terminal = matches?.find((match) => match.status !== 'success') + expect(terminal).toMatchObject({ + routeId: rootRoute.id, + status: 'error', + error: expect.objectContaining({ message: 'Too many redirects' }), + }) + }) }) diff --git a/packages/router-core/tests/rendered-matches.test.ts b/packages/router-core/tests/rendered-matches.test.ts new file mode 100644 index 0000000000..2a2ea362ae --- /dev/null +++ b/packages/router-core/tests/rendered-matches.test.ts @@ -0,0 +1,30 @@ +import { describe, expect, test } from 'vitest' +import { _getAssetMatches } from '../src' +import type { AnyRouteMatch } from '../src' + +describe('_getAssetMatches', () => { + test('does not cross an ordinary data-only pending boundary', () => { + const matches = [ + { status: 'success' }, + { status: 'pending', ssr: 'data-only' }, + { status: 'success' }, + ] as Array + + expect(_getAssetMatches(matches)).toEqual(matches.slice(0, 2)) + }) + + test('limits a hydration handoff to its verified asset prefix', () => { + const matches = [ + { status: 'success' }, + { + status: 'pending', + ssr: 'data-only', + _assetEnd: 3, + }, + { status: 'success' }, + { status: 'success' }, + ] as Array + + expect(_getAssetMatches(matches)).toEqual(matches.slice(0, 3)) + }) +}) diff --git a/packages/router-core/tests/routerTestUtils.ts b/packages/router-core/tests/routerTestUtils.ts index b71c54e347..413b810b95 100644 --- a/packages/router-core/tests/routerTestUtils.ts +++ b/packages/router-core/tests/routerTestUtils.ts @@ -5,8 +5,10 @@ import { createNonReactiveMutableStore, createNonReactiveReadonlyStore, } from '../src' +import { createRequestHandler } from '../src/ssr/createRequestHandler' import type { RouterHistory } from '@tanstack/history' import type { + AnyRouter, AnyRoute, GetStoreConfig, RouterConstructorOptions, @@ -46,3 +48,24 @@ export function createTestRouter< ) { return new RouterCore(options, getStoreConfig) } + +/** Materialize the request-local server result as the HTTP response users see. */ +export function loadServerResponse( + router: AnyRouter, + path: string, + signal?: AbortSignal, +) { + return createRequestHandler({ + createRouter: () => router, + request: new Request(`http://localhost${path}`, { signal }), + })(({ router: loadedRouter, responseHeaders }) => { + const result = loadedRouter._serverResult + return new Response(null, { + status: + result?.type === 'redirect' + ? result.redirect.status + : (result?.status ?? 500), + headers: responseHeaders, + }) + }) +} diff --git a/packages/router-core/tests/same-destination-navigation-join.test.ts b/packages/router-core/tests/same-destination-navigation-join.test.ts index cf61a43053..c6d65fe525 100644 --- a/packages/router-core/tests/same-destination-navigation-join.test.ts +++ b/packages/router-core/tests/same-destination-navigation-join.test.ts @@ -1,11 +1,16 @@ -import { describe, expect, test, vi } from 'vitest' +import { afterEach, describe, expect, test, vi } from 'vitest' import { createMemoryHistory } from '@tanstack/history' import { BaseRootRoute, BaseRoute, createControlledPromise } from '../src' import { createTestRouter } from './routerTestUtils' +afterEach(() => { + vi.restoreAllMocks() +}) + describe('same-destination navigation while one is in flight', () => { function setup() { const gate = createControlledPromise() + const beforeLoad = vi.fn() const loader = vi.fn(() => gate) const rootRoute = new BaseRootRoute({}) const indexRoute = new BaseRoute({ @@ -15,17 +20,18 @@ describe('same-destination navigation while one is in flight', () => { const targetRoute = new BaseRoute({ getParentRoute: () => rootRoute, path: '/target', + beforeLoad, loader, }) const router = createTestRouter({ routeTree: rootRoute.addChildren([indexRoute, targetRoute]), history: createMemoryHistory({ initialEntries: ['/'] }), }) - return { router, loader, gate, targetRoute } + return { router, beforeLoad, loader, gate, targetRoute } } test('a second navigation to the same destination joins the in-flight load', async () => { - const { router, loader, gate, targetRoute } = setup() + const { router, beforeLoad, loader, gate, targetRoute } = setup() await router.load() const first = router.navigate({ to: '/target' }) @@ -35,6 +41,7 @@ describe('same-destination navigation while one is in flight', () => { gate.resolve('once') await Promise.all([first, second]) + expect(beforeLoad).toHaveBeenCalledTimes(2) expect(loader).toHaveBeenCalledTimes(1) expect(router.state.matches.at(-1)).toMatchObject({ routeId: targetRoute.id, @@ -61,6 +68,172 @@ describe('same-destination navigation while one is in flight', () => { }) }) + test('a successor joining a loader error normalizes the shared generation once', async () => { + const gate = createControlledPromise() + const failure = new Error('shared failure') + const loader = vi.fn(async () => { + await gate + throw failure + }) + const onError = vi.fn() + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + loader, + onError, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, targetRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + await router.load() + + const first = router.navigate({ to: '/target' }) + await vi.waitFor(() => expect(loader).toHaveBeenCalledOnce()) + const second = router.navigate({ to: '/target' }) + await Promise.resolve() + expect(loader).toHaveBeenCalledOnce() + + gate.resolve() + await Promise.all([first, second]) + + expect(loader).toHaveBeenCalledOnce() + expect(onError).toHaveBeenCalledOnce() + expect(onError).toHaveBeenCalledWith(failure) + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: targetRoute.id, + status: 'error', + error: failure, + }) + }) + + test('a navigation started by onError runs a fresh loader generation', async () => { + const gate = createControlledPromise() + const failure = new Error('first generation failed') + let successor: Promise | undefined + let router: ReturnType + const loader = vi.fn(async () => { + if (loader.mock.calls.length === 1) { + await gate + throw failure + } + return 'recovered' + }) + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + loader, + onError: () => { + successor ??= router.navigate({ to: '/target' }) + }, + }) + router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, targetRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + await router.load() + + const first = router.navigate({ to: '/target' }) + await vi.waitFor(() => expect(loader).toHaveBeenCalledOnce()) + gate.resolve() + await first + await successor + + expect(loader).toHaveBeenCalledTimes(2) + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: targetRoute.id, + status: 'success', + loaderData: 'recovered', + }) + }) + + test('a superseded loader failure cannot poison a successor delayed in beforeLoad', async () => { + const firstResult = createControlledPromise() + const successorBeforeLoadStarted = createControlledPromise() + const successorBeforeLoadGate = createControlledPromise() + const failure = new Error('reserved generation failed') + const signals: Array = [] + const onError = vi.fn() + const loader = vi.fn( + ({ abortController }: { abortController: AbortController }) => { + signals.push(abortController.signal) + return loader.mock.calls.length === 1 ? firstResult : 'fresh' + }, + ) + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + validateSearch: (search: Record) => ({ + phase: String(search.phase ?? ''), + }), + beforeLoad: async ({ search }) => { + if (search.phase === 'successor') { + successorBeforeLoadStarted.resolve() + await successorBeforeLoadGate + } + }, + loader, + onError, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, targetRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + await router.load() + + const first = router.navigate({ + to: '/target', + search: { phase: 'predecessor' }, + }) + await vi.waitFor(() => expect(loader).toHaveBeenCalledOnce()) + + const second = router.navigate({ + to: '/target', + search: { phase: 'successor' }, + }) + await successorBeforeLoadStarted + + expect(loader).toHaveBeenCalledOnce() + expect(signals[0]?.aborted).toBe(false) + + firstResult.reject(failure) + await vi.waitFor(() => expect(signals[0]?.aborted).toBe(true)) + + expect(onError).not.toHaveBeenCalled() + expect(loader).toHaveBeenCalledOnce() + + successorBeforeLoadGate.resolve() + await Promise.all([first, second]) + + expect(loader).toHaveBeenCalledTimes(2) + expect(onError).not.toHaveBeenCalled() + expect(signals[1]?.aborted).toBe(false) + expect(router.state.location).toMatchObject({ + pathname: '/target', + search: { phase: 'successor' }, + }) + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: targetRoute.id, + status: 'success', + loaderData: 'fresh', + }) + }) + test('invalidate reruns the loader after the navigation settles', async () => { const { router, loader, gate } = setup() gate.resolve('data') @@ -73,6 +246,72 @@ describe('same-destination navigation while one is in flight', () => { expect(loader).toHaveBeenCalledTimes(2) }) + test('invalidate during an in-flight navigation still reruns the loader', async () => { + const { router, loader, gate } = setup() + await router.load() + + const first = router.navigate({ to: '/target' }) + await vi.waitFor(() => expect(loader).toHaveBeenCalledTimes(1)) + const second = router.invalidate() + + gate.resolve('data') + await Promise.all([first, second]) + + expect(loader).toHaveBeenCalledTimes(2) + }) + + test('filtered invalidate does not adopt same-id work from an active preload', async () => { + const secondGate = createControlledPromise() + const loader = vi.fn( + ({ abortController }: { abortController: AbortController }) => { + const generation = loader.mock.calls.length + return generation === 2 + ? secondGate + : `generation ${generation}:${abortController.signal.aborted}` + }, + ) + const rootRoute = new BaseRootRoute({}) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + validateSearch: (search: Record) => ({ + revision: Number(search.revision ?? 1), + }), + shouldReload: ({ preload }) => (preload ? true : undefined), + loader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([targetRoute]), + history: createMemoryHistory({ + initialEntries: ['/target?revision=1'], + }), + }) + + await router.load() + const preload = router.preloadRoute({ + to: '/target', + search: { revision: 2 }, + }) + await vi.waitFor(() => expect(loader).toHaveBeenCalledTimes(2)) + + const invalidation = router.invalidate({ + filter: (match) => + match.routeId === targetRoute.id && + (match.search as { revision: number }).revision === 1, + }) + await vi.waitFor(() => expect(loader).toHaveBeenCalledTimes(3)) + await invalidation + + expect(router.state.location.search).toEqual({ revision: 1 }) + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: targetRoute.id, + loaderData: 'generation 3:false', + }) + + secondGate.resolve('preload data') + await preload + }) + test('a repeat navigation after settle reloads instead of joining', async () => { const { router, loader, gate, targetRoute } = setup() gate.resolve('data') diff --git a/packages/router-core/tests/server-async-headers-decorative-hang.test.ts b/packages/router-core/tests/server-async-headers-decorative-hang.test.ts new file mode 100644 index 0000000000..454e6a6bb8 --- /dev/null +++ b/packages/router-core/tests/server-async-headers-decorative-hang.test.ts @@ -0,0 +1,52 @@ +import { afterEach, expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute, createControlledPromise } from '../src' +import { createTestRouter, loadServerResponse } from './routerTestUtils' + +afterEach(() => { + vi.restoreAllMocks() +}) + +test('a rejected projection hook is logged without failing the response', async () => { + const projectionError = new Error('scripts failed') + const log = vi.spyOn(console, 'error').mockImplementation(() => {}) + const headGate = createControlledPromise<{ + meta: Array<{ title: string }> + }>() + const headersGate = createControlledPromise>() + const head = vi.fn(() => headGate) + const scripts = vi.fn(async () => { + throw projectionError + }) + const headers = vi.fn(() => headersGate) + + const rootRoute = new BaseRootRoute({}) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + head, + scripts, + headers, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([targetRoute]), + history: createMemoryHistory({ initialEntries: ['/target'] }), + isServer: true, + }) + + const response = await loadServerResponse(router, '/target') + + expect(headGate.status).toBe('pending') + expect(headersGate.status).toBe('pending') + expect(response.status).toBe(200) + expect(response.headers.get('x-response-header')).toBeNull() + expect(head).toHaveBeenCalledTimes(1) + expect(scripts).toHaveBeenCalledTimes(1) + expect(headers).toHaveBeenCalledTimes(1) + expect(log).toHaveBeenCalledTimes(1) + expect(log).toHaveBeenCalledWith(projectionError) + + headGate.resolve({ meta: [{ title: 'discarded' }] }) + headersGate.resolve({ 'x-response-header': 'discarded' }) + await Promise.all([headGate, headersGate]) +}) diff --git a/packages/router-core/tests/server-beforeload-preload-flag.test.ts b/packages/router-core/tests/server-beforeload-preload-flag.test.ts index 22509a86c7..4779001305 100644 --- a/packages/router-core/tests/server-beforeload-preload-flag.test.ts +++ b/packages/router-core/tests/server-beforeload-preload-flag.test.ts @@ -1,5 +1,5 @@ import { createMemoryHistory } from '@tanstack/history' -import { expect, test } from 'vitest' +import { expect, test, vi } from 'vitest' import { BaseRootRoute, BaseRoute } from '../src' import { createTestRouter } from './routerTestUtils' @@ -23,3 +23,66 @@ test('server beforeLoad receives preload false', async () => { expect(seen).toEqual([false]) }) + +test('server preloadRoute stays speculative and receives preload true', async () => { + const beforeLoad = vi.fn() + const loader = vi.fn(() => 'preloaded data') + const rootRoute = new BaseRootRoute({}) + const indexRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/', + }) + const childRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/child', + beforeLoad, + loader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([indexRoute, childRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + isServer: true, + }) + + await router.load() + const matches = await router.preloadRoute({ to: '/child' }) + + expect(beforeLoad).toHaveBeenCalledWith( + expect.objectContaining({ preload: true }), + ) + expect(loader).toHaveBeenCalledOnce() + expect(matches?.at(-1)).toMatchObject({ + routeId: childRoute.id, + loaderData: 'preloaded data', + }) + expect(router.state.location.pathname).toBe('/') + expect(router.state.matches.at(-1)?.routeId).toBe(indexRoute.id) +}) + +test('server child context receives its parent beforeLoad context', async () => { + const rootRoute = new BaseRootRoute({ + beforeLoad: () => ({ parent: 'ready' }), + }) + const childContext = vi.fn(({ context }) => ({ + child: `${context.parent}:child`, + })) + const childRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/child', + context: childContext, + loader: ({ context }) => context.child, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([childRoute]), + history: createMemoryHistory({ initialEntries: ['/child'] }), + isServer: true, + }) + + await router.load() + + expect(router.state.matches.at(-1)).toMatchObject({ + context: { parent: 'ready', child: 'ready:child' }, + loaderData: 'ready:child', + }) + expect(childContext).toHaveBeenCalledTimes(1) +}) diff --git a/packages/router-core/tests/server-chunk-failure-lifecycle.test.ts b/packages/router-core/tests/server-chunk-failure-lifecycle.test.ts new file mode 100644 index 0000000000..662fab95f9 --- /dev/null +++ b/packages/router-core/tests/server-chunk-failure-lifecycle.test.ts @@ -0,0 +1,238 @@ +import { describe, expect, onTestFinished, test } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { + BaseRootRoute, + BaseRoute, + createControlledPromise, + notFound, + redirect, +} from '../src' +import { createTestRouter, loadServerResponse } from './routerTestUtils' + +/** + * A primary route chunk failure observed by the server loader phase (a route + * component preload or lazyFn rejection) must go through the same route + * failure lifecycle as loader failures: the route's onError runs, + * redirect/notFound conversion applies, and the errorComponent boundary + * chunk is loaded. Only boundary component chunk failures commit directly + * without recursing into another boundary chunk. + * + * These tests use real server router loads and public component preload/lazy + * route APIs. + */ + +describe('server route chunk failure lifecycle', () => { + test('calls route onError once and loads the errorComponent chunk when the route component chunk rejects', async () => { + const chunkError = new Error('component chunk failed') + const capturedErrors: Array = [] + const events: Array = [] + + const FailingComponent = Object.assign(() => null, { + preload: () => Promise.reject(chunkError), + }) + const ErrorBoundary = Object.assign(() => null, { + preload: () => { + events.push('errorComponent-preload') + return Promise.resolve() + }, + }) + + const rootRoute = new BaseRootRoute({}) + const chunkedRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/chunked', + onError: (error: unknown) => { + capturedErrors.push(error) + events.push('onError') + }, + component: FailingComponent as any, + errorComponent: ErrorBoundary as any, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([chunkedRoute]), + history: createMemoryHistory({ initialEntries: ['/chunked'] }), + isServer: true, + }) + + const response = await loadServerResponse(router, '/chunked') + + const match = router.state.matches.find( + (item) => item.routeId === chunkedRoute.id, + ) + expect(capturedErrors).toEqual([chunkError]) + expect(match?.status).toBe('error') + expect(match?.error).toBe(chunkError) + expect(events).toEqual(['onError', 'errorComponent-preload']) + expect(response.status).toBe(500) + }) + + test('a notFound thrown from onError for a route chunk failure sets 404 instead of 500', async () => { + const chunkError = new Error('component chunk failed') + + const FailingComponent = Object.assign(() => null, { + preload: () => Promise.reject(chunkError), + }) + + const rootRoute = new BaseRootRoute({}) + const chunkedRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/chunked', + onError: () => { + throw notFound() + }, + component: FailingComponent as any, + notFoundComponent: () => null, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([chunkedRoute]), + history: createMemoryHistory({ initialEntries: ['/chunked'] }), + isServer: true, + }) + + const response = await loadServerResponse(router, '/chunked') + + const match = router.state.matches.find( + (item) => item.routeId === chunkedRoute.id, + ) + expect(response.status).toBe(404) + expect(match?.status).toBe('notFound') + expect(match?.error).toEqual(expect.objectContaining({ isNotFound: true })) + }) + + test('the first route-order chunk failure determines the response', async () => { + const rootError = new Error('root component failed') + const rootChunkGate = createControlledPromise() + const childConverted = createControlledPromise() + const RootComponent = Object.assign(() => null, { + preload: () => rootChunkGate, + }) + const ChildComponent = Object.assign(() => null, { + preload: () => Promise.reject(new Error('child component failed')), + }) + + const rootRoute = new BaseRootRoute({ + component: RootComponent as any, + errorComponent: () => null, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/child', + component: ChildComponent as any, + onError: () => { + childConverted.resolve() + throw redirect({ to: '/elsewhere' }) + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([childRoute]), + history: createMemoryHistory({ initialEntries: ['/child'] }), + isServer: true, + }) + + const responsePromise = loadServerResponse(router, '/child') + await childConverted + rootChunkGate.reject(rootError) + const response = await responsePromise + + expect(response.status).toBe(500) + expect(response.headers.get('Location')).toBeNull() + expect(router.state.matches).toHaveLength(2) + expect(router.state.matches[0]).toMatchObject({ + routeId: rootRoute.id, + status: 'error', + error: rootError, + }) + }) + + test('does not leave later chunk rejections unhandled after request abort', async () => { + const rootError = new Error('root component failed') + const abortError = new Error('request closed') + const rootChunkGate = createControlledPromise() + const childChunkGate = createControlledPromise() + const rootChunkStarted = createControlledPromise() + const childChunkStarted = createControlledPromise() + const RootComponent = Object.assign(() => null, { + preload: () => { + rootChunkStarted.resolve() + return rootChunkGate + }, + }) + const ChildComponent = Object.assign(() => null, { + preload: () => { + childChunkStarted.resolve() + return childChunkGate + }, + }) + const rootRoute = new BaseRootRoute({ + component: RootComponent as any, + errorComponent: () => null, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/child', + component: ChildComponent as any, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([childRoute]), + history: createMemoryHistory({ initialEntries: ['/child'] }), + isServer: true, + }) + const controller = new AbortController() + const unhandled: Array = [] + const onUnhandled = (error: unknown) => { + unhandled.push(error) + } + process.on('unhandledRejection', onUnhandled) + onTestFinished(() => { + process.off('unhandledRejection', onUnhandled) + }) + + const responsePromise = loadServerResponse( + router, + '/child', + controller.signal, + ) + await Promise.all([rootChunkStarted, childChunkStarted]) + + rootChunkGate.reject(rootError) + await expect(responsePromise).resolves.toMatchObject({ status: 500 }) + + controller.abort(abortError) + childChunkGate.resolve() + await new Promise((resolve) => setTimeout(resolve, 0)) + + expect(unhandled).not.toContain(abortError) + }) + + test('a required ancestor lazy chunk failure wins over a deeper loader notFound', async () => { + const chunkError = new Error('ancestor lazy chunk failed') + const rootRoute = new BaseRootRoute({}) + const ancestorRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/ancestor', + errorComponent: () => null, + }).lazy(() => Promise.reject(chunkError)) + const childRoute = new BaseRoute({ + getParentRoute: () => ancestorRoute, + path: '/child', + loader: () => { + throw notFound() + }, + notFoundComponent: () => null, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + ancestorRoute.addChildren([childRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/ancestor/child'] }), + isServer: true, + }) + + const response = await loadServerResponse(router, '/ancestor/child') + + expect(response.status).toBe(500) + expect( + router.state.matches.find((match) => match.routeId === ancestorRoute.id), + ).toMatchObject({ status: 'error', error: chunkError }) + }) +}) diff --git a/packages/router-core/tests/server-concurrent-error-notfound.test.ts b/packages/router-core/tests/server-concurrent-error-notfound.test.ts new file mode 100644 index 0000000000..5fc3cc63f9 --- /dev/null +++ b/packages/router-core/tests/server-concurrent-error-notfound.test.ts @@ -0,0 +1,467 @@ +import { describe, expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { + BaseRootRoute, + BaseRoute, + createControlledPromise, + notFound, + redirect, +} from '../src' +import { createTestRouter, loadServerResponse } from './routerTestUtils' + +const requiredPrefixModes = [ + ['fresh', 'error', false, 'child-first'], + ['fresh', 'error', true, 'child-first'], + ['fresh', 'notFound', false, 'child-first'], + ['fresh', 'notFound', true, 'child-first'], + ['retained', 'error', false, 'child-first'], + ['retained', 'notFound', false, 'child-first'], + ['retained', 'error', false, 'parent-first'], +] as const + +describe('concurrent route failure ordering', () => { + test.each(requiredPrefixModes)( + 'selects the reachable failure (parentData=%s, parent=%s, isServer=%s, order=%s)', + async (parentData, parentOutcome, isServer, settlementOrder) => { + const parentFailure = + parentOutcome === 'error' + ? new Error('parent loader failed') + : notFound() + const childFailure = + parentOutcome === 'error' + ? notFound() + : new Error('child loader failed') + const childOutcome = parentOutcome === 'error' ? 'notFound' : 'error' + const parentGate = createControlledPromise() + const childGate = createControlledPromise() + const parentStarted = createControlledPromise() + const childStarted = createControlledPromise() + const parentSettled = createControlledPromise() + const childSettled = createControlledPromise() + const settlements: Array = [] + let failing = parentData === 'fresh' + const rootRoute = new BaseRootRoute({}) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + ssr: 'data-only', + loader: async () => { + if (!failing) { + return undefined + } + parentStarted.resolve() + await parentGate + settlements.push('parent') + parentSettled.resolve() + throw parentFailure + }, + errorComponent: () => null, + notFoundComponent: () => null, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loader: async () => { + if (!failing) { + return 'child data' + } + childStarted.resolve() + await childGate + settlements.push('child') + childSettled.resolve() + throw childFailure + }, + errorComponent: () => null, + notFoundComponent: () => null, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + parentRoute.addChildren([childRoute]), + ]), + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + isServer, + defaultStaleReloadMode: 'blocking', + }) + + if (parentData === 'retained') { + await router.load() + const parentMatch = router.state.matches.find( + (match) => match.routeId === parentRoute.id, + ) + expect(parentMatch).toMatchObject({ + routeId: parentRoute.id, + status: 'success', + loaderData: undefined, + }) + failing = true + } + + const responsePromise = isServer + ? loadServerResponse(router, '/parent/child') + : (parentData === 'retained' + ? router.invalidate() + : router.load() + ).then(() => undefined) + await Promise.all([parentStarted, childStarted]) + if (settlementOrder === 'child-first') { + childGate.resolve() + await childSettled + parentGate.resolve() + await parentSettled + } else { + parentGate.resolve() + await parentSettled + childGate.resolve() + await childSettled + } + const response = await responsePromise + + const parentWon = + parentData === 'fresh' || settlementOrder === 'parent-first' + const selectedOutcome = parentWon ? parentOutcome : childOutcome + const selectedFailure = parentWon ? parentFailure : childFailure + if (isServer) { + expect(response?.status).toBe(selectedOutcome === 'error' ? 500 : 404) + } + expect(settlements).toEqual( + settlementOrder === 'child-first' + ? ['child', 'parent'] + : ['parent', 'child'], + ) + const terminalMatch = router.state.matches.find( + (match) => match.status === 'error' || match.status === 'notFound', + ) + expect(terminalMatch).toMatchObject({ + routeId: parentWon ? parentRoute.id : childRoute.id, + status: selectedOutcome, + error: selectedFailure, + }) + expect(router.state.status).toBe('idle') + }, + ) + + test.each([false, true])( + 'recomputes chunk readiness after a notFound ancestor promotes to root (isServer=%s)', + async (isServer) => { + const chunkError = new Error('intermediate lazy chunk failed') + const childError = new Error('child loader failed') + const rootNotFound = notFound() + const ancestorGate = createControlledPromise() + const childGate = createControlledPromise() + const ancestorStarted = createControlledPromise() + const childStarted = createControlledPromise() + const ancestorSettled = createControlledPromise() + const childSettled = createControlledPromise() + const settlements: Array = [] + const intermediateLazy = vi.fn(() => Promise.reject(chunkError)) + const rootRoute = new BaseRootRoute({ + notFoundComponent: () => null, + }) + const intermediateRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/intermediate', + errorComponent: () => null, + }).lazy(intermediateLazy) + const ancestorRoute = new BaseRoute({ + getParentRoute: () => intermediateRoute, + path: '/ancestor', + loader: async () => { + ancestorStarted.resolve() + await ancestorGate + settlements.push('ancestor') + ancestorSettled.resolve() + throw rootNotFound + }, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => ancestorRoute, + path: '/child', + loader: async () => { + childStarted.resolve() + await childGate + settlements.push('child') + childSettled.resolve() + throw childError + }, + errorComponent: () => null, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + intermediateRoute.addChildren([ + ancestorRoute.addChildren([childRoute]), + ]), + ]), + history: createMemoryHistory({ + initialEntries: ['/intermediate/ancestor/child'], + }), + isServer, + }) + + const responsePromise = isServer + ? loadServerResponse(router, '/intermediate/ancestor/child') + : router.load().then(() => undefined) + await Promise.all([ancestorStarted, childStarted]) + childGate.resolve() + await childSettled + ancestorGate.resolve() + await ancestorSettled + const response = await responsePromise + + if (isServer) { + expect(response?.status).toBe(404) + } + expect(settlements).toEqual(['child', 'ancestor']) + expect(intermediateLazy).toHaveBeenCalled() + expect( + router.state.matches.find((match) => match.routeId === rootRoute.id), + ).toMatchObject({ + status: 'success', + error: rootNotFound, + }) + expect( + router.state.matches.some((match) => match.error === chunkError), + ).toBe(false) + }, + ) + + test.each([ + [false, 'beforeLoad'], + [false, 'loader'], + [true, 'beforeLoad'], + [true, 'loader'], + ] as const)( + 'keeps a resolved notFound boundary stable within one load (isServer=%s, phase=%s)', + async (isServer, phase) => { + const lazyError = new Error('lazy boundary lookup failed') + const notFoundError = notFound() + const resolveAt = phase === 'beforeLoad' ? 2 : 3 + let lazyCalls = 0 + const rootRoute = new BaseRootRoute({ + notFoundComponent: () => null, + }) + const intermediateRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/intermediate', + }).lazy(() => { + lazyCalls++ + if (lazyCalls < resolveAt) { + return Promise.reject(lazyError) + } + return Promise.resolve({ + options: { notFoundComponent: () => null }, + } as any) + }) + function throwNotFound() { + throw notFoundError + } + const childRoute = new BaseRoute({ + getParentRoute: () => intermediateRoute, + path: '/child', + beforeLoad: phase === 'beforeLoad' ? throwNotFound : undefined, + loader: phase === 'loader' ? throwNotFound : undefined, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + intermediateRoute.addChildren([childRoute]), + ]), + history: createMemoryHistory({ + initialEntries: ['/intermediate/child'], + }), + isServer, + }) + + const response = isServer + ? await loadServerResponse(router, '/intermediate/child') + : await router.load().then(() => undefined) + + if (isServer) { + expect(response?.status).toBe(404) + } + expect(lazyCalls).toBe(!isServer && phase === 'loader' ? 2 : 1) + expect( + router.state.matches.find((match) => match.error === notFoundError) + ?.routeId, + ).toBe(rootRoute.id) + }, + ) + + test('a redirect aborts every generation in its discarded server lane', async () => { + const signals: Array = [] + const rootRoute = new BaseRootRoute({ + loader: ({ abortController }) => { + signals.push(abortController.signal) + return 'root data' + }, + }) + const redirectRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/redirect', + loader: ({ abortController }) => { + signals.push(abortController.signal) + throw redirect({ to: '/target' }) + }, + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([redirectRoute, targetRoute]), + history: createMemoryHistory({ initialEntries: ['/redirect'] }), + isServer: true, + }) + + const response = await loadServerResponse(router, '/redirect') + + expect(response.status).toBe(307) + expect(response.headers.get('Location')).toBe('/target') + expect(signals).toHaveLength(2) + expect(signals.every((signal) => signal.aborted)).toBe(true) + }) + + test('a started descendant can redirect after its ancestor fails', async () => { + const childStarted = createControlledPromise() + const childRedirect = createControlledPromise() + const parentError = new Error('parent failed') + const rootRoute = new BaseRootRoute({}) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + loader: async () => { + await childStarted + throw parentError + }, + errorComponent: () => null, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loader: async () => { + childStarted.resolve() + await childRedirect + throw redirect({ to: '/target' }) + }, + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + parentRoute.addChildren([childRoute]), + targetRoute, + ]), + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + isServer: true, + }) + + const responsePromise = loadServerResponse(router, '/parent/child') + await childStarted + childRedirect.resolve() + const response = await responsePromise + + expect(response.status).toBe(307) + expect(response.headers.get('Location')).toBe('/target') + }) + + test('a redirect does not abort a later server generation on the same router', async () => { + let generation = 0 + const rootRoute = new BaseRootRoute({}) + const pageRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/page', + loader: () => { + generation++ + if (generation === 2) { + throw redirect({ to: '/target' }) + } + return `data ${generation}` + }, + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([pageRoute, targetRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + isServer: true, + }) + + expect((await loadServerResponse(router, '/page')).status).toBe(200) + expect((await loadServerResponse(router, '/page')).status).toBe(307) + expect((await loadServerResponse(router, '/page')).status).toBe(200) + + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: pageRoute.id, + status: 'success', + loaderData: 'data 3', + }) + }) + + test.each([false, true])( + 'an aborted child does not report its cancellation through onError (isServer=%s)', + async (isServer) => { + const childStarted = createControlledPromise() + const childLoader = createControlledPromise() + const cancellation = new Error('discarded request aborted') + const childOnError = vi.fn() + let childSignal: AbortSignal | undefined + const rootRoute = new BaseRootRoute({}) + const sourceRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/source', + loader: async () => { + await childStarted + throw redirect({ to: '/target' }) + }, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => sourceRoute, + path: '/child', + loader: ({ abortController }) => { + childSignal = abortController.signal + abortController.signal.addEventListener( + 'abort', + () => childLoader.reject(cancellation), + { once: true }, + ) + childStarted.resolve() + return childLoader + }, + onError: childOnError, + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + sourceRoute.addChildren([childRoute]), + targetRoute, + ]), + history: createMemoryHistory({ initialEntries: ['/source/child'] }), + isServer, + }) + + const childCancellation = expect(childLoader).rejects.toBe(cancellation) + const loadPromise = isServer + ? loadServerResponse(router, '/source/child') + : router.load().then(() => undefined) + const [response] = await Promise.all([loadPromise, childCancellation]) + + expect(childSignal?.aborted).toBe(true) + expect(childOnError).not.toHaveBeenCalled() + if (isServer) { + expect(response?.status).toBe(307) + expect(response?.headers.get('Location')).toBe('/target') + } else { + expect(router.state.location.pathname).toBe('/target') + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: targetRoute.id, + status: 'success', + }) + } + }, + ) +}) diff --git a/packages/router-core/tests/server-loader-abort-error.test.ts b/packages/router-core/tests/server-loader-abort-error.test.ts index eeab2cb3ca..bc079ce239 100644 --- a/packages/router-core/tests/server-loader-abort-error.test.ts +++ b/packages/router-core/tests/server-loader-abort-error.test.ts @@ -1,33 +1,289 @@ -import { describe, expect, test } from 'vitest' +import { describe, expect, test, vi } from 'vitest' import { createMemoryHistory } from '@tanstack/history' -import { BaseRootRoute, BaseRoute } from '../src' -import { createTestRouter } from './routerTestUtils' +import { BaseRootRoute, BaseRoute, createControlledPromise } from '../src' +import { createRequestHandler } from '../src/ssr/server' +import { createTestRouter, loadServerResponse } from './routerTestUtils' describe('loader user-thrown abort values', () => { - test('treats a user-thrown AbortSignal as an ordinary route error (isServer=false)', async () => { - let matchSignal: AbortSignal | undefined + test.each( + ([false, true] as const).flatMap((isServer) => [ + { + isServer, + thrownType: 'AbortSignal', + createThrownValue: (signal: AbortSignal) => signal, + }, + { + isServer, + thrownType: 'AbortError', + createThrownValue: () => + new DOMException('The operation was aborted.', 'AbortError'), + }, + ]), + )( + 'treats a user-thrown $thrownType as an ordinary route error (isServer=$isServer)', + async ({ isServer, createThrownValue }) => { + let matchSignal: AbortSignal | undefined + let thrownValue: unknown + const rootRoute = new BaseRootRoute({}) + const abortingRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/aborting', + loader: ({ abortController }) => { + matchSignal = abortController.signal + thrownValue = createThrownValue(matchSignal) + throw thrownValue + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([abortingRoute]), + history: createMemoryHistory({ initialEntries: ['/aborting'] }), + isServer, + }) + + if (isServer) { + const response = await loadServerResponse(router, '/aborting') + expect(response.status).toBe(500) + } else { + await router.load() + } + + const match = router.state.matches.find( + (item) => item.routeId === abortingRoute.id, + ) + // A server match remains ordinary route failure during reduction, then + // its controller is retired when the request response is cleaned up. + expect(matchSignal?.aborted).toBe(isServer) + expect(match?.status).toBe('error') + expect(match?.error).toBe(thrownValue) + }, + ) + + test('request cancellation reaches the controller shared by route hooks', async () => { + const loaderStarted = createControlledPromise() + const controllers: Array = [] + const onError = vi.fn() const rootRoute = new BaseRootRoute({}) - const abortingRoute = new BaseRoute({ + const route = new BaseRoute({ getParentRoute: () => rootRoute, - path: '/aborting', + path: '/work', + context: ({ abortController }) => { + controllers.push(abortController) + }, + beforeLoad: ({ abortController }) => { + controllers.push(abortController) + }, loader: ({ abortController }) => { - matchSignal = abortController.signal - throw matchSignal + controllers.push(abortController) + loaderStarted.resolve() + return new Promise((_, reject) => { + abortController.signal.addEventListener( + 'abort', + () => reject(abortController.signal.reason), + { once: true }, + ) + }) }, + onError, }) const router = createTestRouter({ - routeTree: rootRoute.addChildren([abortingRoute]), - history: createMemoryHistory({ initialEntries: ['/aborting'] }), - isServer: false, + routeTree: rootRoute.addChildren([route]), + history: createMemoryHistory({ initialEntries: ['/work'] }), + isServer: true, + }) + const requestController = new AbortController() + const render = vi.fn(() => new Response('must not render')) + const handler = createRequestHandler({ + createRouter: () => router, + request: new Request('http://localhost/work', { + signal: requestController.signal, + }), + }) + const response = handler(render) + await loaderStarted + + const cancellation = new Error('request disconnected') + requestController.abort(cancellation) + + await expect(response).rejects.toBe(cancellation) + expect(controllers).toHaveLength(3) + expect( + controllers.every((controller) => controller === controllers[0]), + ).toBe(true) + expect(controllers[0]?.signal.aborted).toBe(true) + expect(controllers[0]?.signal.reason).toBe(cancellation) + expect(onError).not.toHaveBeenCalled() + expect(render).not.toHaveBeenCalled() + }) + + test('response cleanup aborts the controller retained by deferred loader data', async () => { + const deferred = createControlledPromise() + let loaderSignal: AbortSignal | undefined + const rootRoute = new BaseRootRoute({}) + const route = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/work', + loader: ({ abortController }) => { + loaderSignal = abortController.signal + return { deferred } + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([route]), + history: createMemoryHistory({ initialEntries: ['/work'] }), + isServer: true, + }) + + const response = await loadServerResponse(router, '/work') + + expect(response.status).toBe(200) + expect(loaderSignal?.aborted).toBe(true) + deferred.resolve('late data') + }) + + test('request cancellation thrown from route context does not call onError', async () => { + const cancellation = new Error('request disconnected in context') + const requestController = new AbortController() + const beforeLoad = vi.fn() + const loader = vi.fn() + const onError = vi.fn() + const rootRoute = new BaseRootRoute({}) + const route = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/work', + context: ({ abortController }) => { + requestController.abort(cancellation) + abortController.signal.throwIfAborted() + }, + beforeLoad, + loader, + onError, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([route]), + history: createMemoryHistory({ initialEntries: ['/work'] }), + isServer: true, + }) + const render = vi.fn(() => new Response('must not render')) + const handler = createRequestHandler({ + createRouter: () => router, + request: new Request('http://localhost/work', { + signal: requestController.signal, + }), + }) + + await expect(handler(render)).rejects.toBe(cancellation) + + expect(onError).not.toHaveBeenCalled() + expect(beforeLoad).not.toHaveBeenCalled() + expect(loader).not.toHaveBeenCalled() + expect(render).not.toHaveBeenCalled() + }) + + test('request cancellation during beforeLoad does not start loaders', async () => { + const beforeLoadStarted = createControlledPromise() + const loader = vi.fn() + const rootRoute = new BaseRootRoute({}) + const route = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/work', + beforeLoad: ({ abortController }) => { + beforeLoadStarted.resolve() + return new Promise((resolve) => { + abortController.signal.addEventListener('abort', () => resolve(), { + once: true, + }) + }) + }, + loader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([route]), + history: createMemoryHistory({ initialEntries: ['/work'] }), + isServer: true, + }) + const requestController = new AbortController() + const handler = createRequestHandler({ + createRouter: () => router, + request: new Request('http://localhost/work', { + signal: requestController.signal, + }), + }) + const response = handler(() => new Response('must not render')) + await beforeLoadStarted + + const cancellation = new Error('request disconnected') + requestController.abort(cancellation) + + await expect(response).rejects.toBe(cancellation) + expect(loader).not.toHaveBeenCalled() + }) + + test('request cancellation does not wait for manifest lookup', async () => { + const manifestStarted = createControlledPromise() + const manifest = createControlledPromise() + const rootRoute = new BaseRootRoute({}) + const loader = vi.fn() + const route = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/work', + loader, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([route]), + history: createMemoryHistory({ initialEntries: ['/work'] }), + isServer: true, + }) + const requestController = new AbortController() + const render = vi.fn(() => new Response('must not render')) + const handler = createRequestHandler({ + createRouter: () => router, + request: new Request('http://localhost/work', { + signal: requestController.signal, + }), + getRouterManifest: () => { + manifestStarted.resolve() + return manifest + }, + }) + + const response = handler(render) + await manifestStarted + const cancellation = new Error('request disconnected') + requestController.abort(cancellation) + + await expect(response).rejects.toBe(cancellation) + expect(loader).not.toHaveBeenCalled() + expect(render).not.toHaveBeenCalled() + }) + + test('request cancellation does not wait for custom dehydration', async () => { + const dehydrateStarted = createControlledPromise() + const dehydrate = createControlledPromise() + const rootRoute = new BaseRootRoute({}) + const router = createTestRouter({ + routeTree: rootRoute, + history: createMemoryHistory({ initialEntries: ['/'] }), + isServer: true, + dehydrate: () => { + dehydrateStarted.resolve() + return dehydrate + }, + }) + const requestController = new AbortController() + const render = vi.fn(() => new Response('must not render')) + const handler = createRequestHandler({ + createRouter: () => router, + request: new Request('http://localhost/', { + signal: requestController.signal, + }), }) - await router.load() + const response = handler(render) + await dehydrateStarted + const cancellation = new Error('request disconnected') + requestController.abort(cancellation) - const match = router.state.matches.find( - (item) => item.routeId === abortingRoute.id, - ) - expect(matchSignal?.aborted).toBe(false) - expect(match?.status).toBe('error') - expect(match?.error).toBe(matchSignal) + await expect(response).rejects.toBe(cancellation) + expect(render).not.toHaveBeenCalled() }) }) diff --git a/packages/router-core/tests/server-planning-redirect.test.ts b/packages/router-core/tests/server-planning-redirect.test.ts new file mode 100644 index 0000000000..95a3a50317 --- /dev/null +++ b/packages/router-core/tests/server-planning-redirect.test.ts @@ -0,0 +1,79 @@ +import { expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute, redirect } from '../src' +import { createRequestHandler } from '../src/ssr/server' +import { createTestRouter, loadServerResponse } from './routerTestUtils' + +test.each([false, true])( + 'a route-context planning redirect is control with isServer=%s', + async (isServer) => { + const rootRoute = new BaseRootRoute({}) + const sourceRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/source', + context: () => { + throw redirect({ to: '/target' }) + }, + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([sourceRoute, targetRoute]), + history: createMemoryHistory({ initialEntries: ['/source'] }), + isServer, + }) + + if (isServer) { + const response = await loadServerResponse(router, '/source') + + expect(response.status).toBe(307) + expect(response.headers.get('Location')).toBe('/target') + } else { + await router.load() + + expect(router.state.location.pathname).toBe('/target') + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: targetRoute.id, + status: 'success', + }) + } + }, +) + +test('the generic request handler returns a redirect without rendering', async () => { + const rootRoute = new BaseRootRoute({}) + const sourceRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/source', + loader: () => + redirect({ + to: '/target', + statusCode: 308, + headers: { 'x-redirect': 'route' }, + }), + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([sourceRoute, targetRoute]), + history: createMemoryHistory({ initialEntries: ['/source'] }), + isServer: true, + }) + const render = vi.fn(() => new Response('must not render')) + const handler = createRequestHandler({ + createRouter: () => router, + request: new Request('http://localhost/source'), + }) + + const response = await handler(render) + + expect(response.status).toBe(308) + expect(response.headers.get('Location')).toBe('/target') + expect(response.headers.get('x-redirect')).toBe('route') + expect(await response.text()).toBe('') + expect(render).not.toHaveBeenCalled() +}) diff --git a/packages/router-core/tests/server-route-lifecycle.test.ts b/packages/router-core/tests/server-route-lifecycle.test.ts new file mode 100644 index 0000000000..7c90f87b60 --- /dev/null +++ b/packages/router-core/tests/server-route-lifecycle.test.ts @@ -0,0 +1,76 @@ +import { expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute } from '../src' +import { createTestRouter, loadServerResponse } from './routerTestUtils' + +test('server route lifecycle callbacks run after loaded matches are published', async () => { + const events: Array = [] + const onEnter = vi.fn((match) => { + events.push(`enter:${match.routeId}:${match.loaderData}`) + }) + const onStay = vi.fn((match) => { + events.push(`stay:${match.routeId}:${match.search.step}`) + }) + const onLeave = vi.fn((match) => { + events.push(`leave:${match.routeId}`) + }) + const barEnter = vi.fn((match) => { + events.push(`enter:${match.routeId}`) + }) + + const rootRoute = new BaseRootRoute({}) + const fooRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/foo', + beforeLoad: () => { + events.push('beforeLoad:/foo') + return { ready: true } + }, + loader: ({ context }) => { + events.push(`loader:/foo:${context.ready}`) + return 'foo data' + }, + onEnter, + onStay, + onLeave, + }) + const barRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/bar', + onEnter: barEnter, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([fooRoute, barRoute]), + history: createMemoryHistory(), + isServer: true, + }) + + expect((await loadServerResponse(router, '/foo?step=one')).status).toBe(200) + expect(events).toEqual([ + 'beforeLoad:/foo', + 'loader:/foo:true', + `enter:${fooRoute.id}:foo data`, + ]) + expect(onEnter).toHaveBeenCalledWith( + expect.objectContaining({ + routeId: fooRoute.id, + status: 'success', + loaderData: 'foo data', + context: expect.objectContaining({ ready: true }), + }), + ) + + events.length = 0 + expect((await loadServerResponse(router, '/foo?step=two')).status).toBe(200) + expect(events).toEqual([ + 'beforeLoad:/foo', + 'loader:/foo:true', + `stay:${fooRoute.id}:two`, + ]) + + events.length = 0 + expect((await loadServerResponse(router, '/bar')).status).toBe(200) + expect(events).toEqual([`leave:${fooRoute.id}`, `enter:${barRoute.id}`]) + expect(onLeave).toHaveBeenCalledTimes(1) + expect(barEnter).toHaveBeenCalledTimes(1) +}) diff --git a/packages/router-core/tests/server-serial-ssr-notfound.test.ts b/packages/router-core/tests/server-serial-ssr-notfound.test.ts new file mode 100644 index 0000000000..6f8c92b6ca --- /dev/null +++ b/packages/router-core/tests/server-serial-ssr-notfound.test.ts @@ -0,0 +1,71 @@ +import { describe, expect, test } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute, notFound } from '../src' +import { createTestRouter, loadServerResponse } from './routerTestUtils' + +/** + * A notFound thrown from a route's ssr() option happens during the serial + * phase, before beforeLoad has run for that route and its descendants. It + * must cap the loader prefix like a beforeLoad notFound: the throwing route + * and its descendants must not run their loaders (their context is + * incomplete and their ssr status unresolved), and the response must be a + * 404 — a descendant loader failure must not be able to turn the outcome + * into a 200/500. + */ + +function setupRouter() { + const loaderCalls: Array = [] + + const rootRoute = new BaseRootRoute({}) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + notFoundComponent: () => null, + ssr: () => { + throw notFound() + }, + loader: () => { + loaderCalls.push('parent') + }, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loader: () => { + loaderCalls.push('child') + throw new Error('child loader failed') + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([parentRoute.addChildren([childRoute])]), + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + isServer: true, + }) + + return { router, rootRoute, parentRoute, childRoute, loaderCalls } +} + +describe('server serial ssr() notFound', () => { + test('caps the loader prefix at the boundary and sets 404', async () => { + const { router, rootRoute, parentRoute, childRoute, loaderCalls } = + setupRouter() + + const response = await loadServerResponse(router, '/parent/child') + + const parentMatch = router.state.matches.find( + (item) => item.routeId === parentRoute.id, + ) + expect(loaderCalls).toEqual([]) + expect(response.status).toBe(404) + expect(parentMatch?.status).toBe('notFound') + expect(parentMatch?.error).toEqual( + expect.objectContaining({ isNotFound: true }), + ) + expect(router.state.matches.map((match) => match.routeId)).toEqual([ + rootRoute.id, + parentRoute.id, + childRoute.id, + ]) + expect(router.state.matches[1]?.status).toBe('notFound') + }) +}) diff --git a/packages/router-core/tests/server-ssr-false-assets.test.ts b/packages/router-core/tests/server-ssr-false-assets.test.ts new file mode 100644 index 0000000000..5ee27fa232 --- /dev/null +++ b/packages/router-core/tests/server-ssr-false-assets.test.ts @@ -0,0 +1,192 @@ +import { describe, expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute } from '../src' +import { createTestRouter, loadServerResponse } from './routerTestUtils' + +/** + * The first `ssr: false` route contributes the assets needed for its server + * shell. Descendants inherit the client-only restriction and do not execute + * their route asset callbacks, even if they request `ssr: true` themselves. + */ +describe('server assets for ssr:false routes', () => { + test('executes assets at the client-only boundary but not below it', async () => { + const rootHead = vi.fn(() => ({ meta: [{ title: 'server root' }] })) + const rootScripts = vi.fn(() => [ + { + children: 'window.rootAssetRan = true', + }, + ]) + const rootHeaders = vi.fn(() => ({ + 'x-server-root': 'projected', + })) + const parentHead = vi.fn(() => ({ + meta: [{ title: 'client-only parent' }], + })) + const parentScripts = vi.fn(() => [ + { + children: 'window.parentAssetRan = true', + }, + ]) + const parentHeaders = vi.fn(() => ({ + 'x-client-only-parent': 'projected', + })) + const parentBeforeLoad = vi.fn(() => ({})) + const parentLoader = vi.fn(() => 'unexpected') + const childHead = vi.fn(() => ({ + meta: [{ title: 'client-only child' }], + })) + const childScripts = vi.fn(() => [ + { + children: 'window.childAssetRan = true', + }, + ]) + const childHeaders = vi.fn(() => ({ + 'x-client-only-child': 'unexpected', + })) + + const rootRoute = new BaseRootRoute({ + head: rootHead, + scripts: rootScripts, + headers: rootHeaders, + }) + const clientOnlyRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/client-only', + ssr: false, + beforeLoad: parentBeforeLoad, + loader: parentLoader, + head: parentHead, + scripts: parentScripts, + headers: parentHeaders, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => clientOnlyRoute, + path: '/child', + // A child cannot relax its parent's client-only restriction. + ssr: true, + head: childHead, + scripts: childScripts, + headers: childHeaders, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + clientOnlyRoute.addChildren([childRoute]), + ]), + history: createMemoryHistory({ + initialEntries: ['/client-only/child'], + }), + isServer: true, + }) + + const response = await loadServerResponse(router, '/client-only/child') + + expect(response.status).toBe(200) + expect(rootHead).toHaveBeenCalledTimes(1) + expect(rootScripts).toHaveBeenCalledTimes(1) + expect(rootHeaders).toHaveBeenCalledTimes(1) + expect(response.headers.get('x-server-root')).toBe('projected') + expect(parentHead).toHaveBeenCalledTimes(1) + expect(parentScripts).toHaveBeenCalledTimes(1) + expect(parentHeaders).toHaveBeenCalledTimes(1) + expect(parentBeforeLoad).not.toHaveBeenCalled() + expect(parentLoader).not.toHaveBeenCalled() + expect(response.headers.get('x-client-only-parent')).toBe('projected') + expect( + router.state.matches.find( + (match) => match.routeId === clientOnlyRoute.id, + ), + ).toMatchObject({ + meta: [{ title: 'client-only parent' }], + scripts: [{ children: 'window.parentAssetRan = true' }], + headers: { 'x-client-only-parent': 'projected' }, + }) + expect(childHead).not.toHaveBeenCalled() + expect(childScripts).not.toHaveBeenCalled() + expect(childHeaders).not.toHaveBeenCalled() + expect(response.headers.get('x-client-only-child')).toBeNull() + }) + + test('executes assets when the root route is client-only', async () => { + const rootHead = vi.fn(() => ({ meta: [{ title: 'root shell' }] })) + const rootScripts = vi.fn(() => [ + { children: 'window.rootShellRan = true' }, + ]) + const rootHeaders = vi.fn(() => ({ 'x-root-shell': 'projected' })) + const childHead = vi.fn(() => ({ meta: [{ title: 'child' }] })) + const childScripts = vi.fn(() => [{ children: 'window.childRan = true' }]) + const childHeaders = vi.fn(() => ({ 'x-child': 'unexpected' })) + const rootRoute = new BaseRootRoute({ + ssr: false, + head: rootHead, + scripts: rootScripts, + headers: rootHeaders, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/child', + ssr: true, + head: childHead, + scripts: childScripts, + headers: childHeaders, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([childRoute]), + history: createMemoryHistory({ initialEntries: ['/child'] }), + isServer: true, + }) + + const response = await loadServerResponse(router, '/child') + + expect(response.status).toBe(200) + expect(rootHead).toHaveBeenCalledTimes(1) + expect(rootScripts).toHaveBeenCalledTimes(1) + expect(rootHeaders).toHaveBeenCalledTimes(1) + expect(response.headers.get('x-root-shell')).toBe('projected') + expect(childHead).not.toHaveBeenCalled() + expect(childScripts).not.toHaveBeenCalled() + expect(childHeaders).not.toHaveBeenCalled() + expect(response.headers.get('x-child')).toBeNull() + }) + + test('projects assets for a data-only branch', async () => { + const head = vi.fn(({ loaderData }) => ({ + meta: [{ title: `report: ${loaderData}` }], + })) + const scripts = vi.fn(({ loaderData }) => [ + { children: `window.report = ${JSON.stringify(loaderData)}` }, + ]) + const headers = vi.fn(({ loaderData }) => ({ + 'x-report': String(loaderData), + })) + const rootRoute = new BaseRootRoute({}) + const reportRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/report', + ssr: 'data-only', + loader: () => 'server data', + head, + scripts, + headers, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([reportRoute]), + history: createMemoryHistory({ initialEntries: ['/report'] }), + isServer: true, + }) + + const response = await loadServerResponse(router, '/report') + const match = router.state.matches.at(-1) + + expect(response.status).toBe(200) + expect(response.headers.get('x-report')).toBe('server data') + expect(match).toMatchObject({ + ssr: 'data-only', + meta: [{ title: 'report: server data' }], + scripts: [{ children: 'window.report = "server data"' }], + headers: { 'x-report': 'server data' }, + }) + expect(head).toHaveBeenCalledTimes(1) + expect(scripts).toHaveBeenCalledTimes(1) + expect(headers).toHaveBeenCalledTimes(1) + }) +}) diff --git a/packages/router-core/tests/server-ssr-option-error.test.ts b/packages/router-core/tests/server-ssr-option-error.test.ts new file mode 100644 index 0000000000..f1dcf64e14 --- /dev/null +++ b/packages/router-core/tests/server-ssr-option-error.test.ts @@ -0,0 +1,178 @@ +import { describe, expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { BaseRootRoute, BaseRoute, redirect } from '../src' +import { createTestRouter, loadServerResponse } from './routerTestUtils' + +/** + * A functional ssr() option is user route code in the server serial phase. + * If it throws an ordinary error, the route must not be committed as a + * successful 200 response with missing loader data. It follows the same + * renderable route-error lifecycle as beforeLoad and loader failures. + */ +describe('server functional ssr() errors', () => { + test.each(['sync', 'async'] as const)( + 'commits a %s failure with route context and returns 500', + async (failureMode) => { + const boom = new Error('feature flag lookup failed') + const loader = vi.fn(() => 'reports data') + const onError = vi.fn() + const beforeLoad = vi.fn() + + const rootRoute = new BaseRootRoute({ + context: () => ({ rootContext: true }), + beforeLoad: () => ({ rootBeforeLoadContext: true }), + }) + const reportsRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/reports', + context: ({ context }) => ({ + reportsContext: context.rootBeforeLoadContext, + }), + ssr: () => { + if (failureMode === 'sync') { + throw boom + } + return Promise.reject(boom) + }, + beforeLoad, + loader, + onError, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([reportsRoute]), + history: createMemoryHistory({ initialEntries: ['/reports'] }), + isServer: true, + context: { routerContext: true }, + }) + + const response = await loadServerResponse(router, '/reports') + + const reportsMatch = router.state.matches.find( + (match) => match.routeId === reportsRoute.id, + ) + expect(loader).not.toHaveBeenCalled() + expect(beforeLoad).not.toHaveBeenCalled() + expect(response.status).toBe(500) + expect(onError).toHaveBeenCalledTimes(1) + expect(onError).toHaveBeenCalledWith(boom) + expect(reportsMatch).toMatchObject({ + status: 'error', + error: boom, + context: { + routerContext: true, + rootContext: true, + rootBeforeLoadContext: true, + reportsContext: true, + }, + }) + }, + ) + + test('retains inherited renderability and loads the lazy error boundary', async () => { + const boom = new Error('SSR policy failed') + const errorBoundaryPreload = vi.fn(() => Promise.resolve()) + const ErrorBoundary = Object.assign(() => null, { + preload: errorBoundaryPreload, + }) + const rootRoute = new BaseRootRoute({}) + const reportsRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/reports', + ssr: async () => { + await Promise.resolve() + throw boom + }, + }) + const lazy = vi.fn(() => + Promise.resolve({ + options: { + id: reportsRoute.id, + errorComponent: ErrorBoundary, + }, + }), + ) + reportsRoute.lazy(lazy as any) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([reportsRoute]), + history: createMemoryHistory({ initialEntries: ['/reports'] }), + isServer: true, + }) + + const response = await loadServerResponse(router, '/reports') + + expect(response.status).toBe(500) + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: reportsRoute.id, + status: 'error', + error: boom, + ssr: true, + }) + expect(lazy).toHaveBeenCalledTimes(1) + expect(errorBoundaryPreload).toHaveBeenCalledTimes(1) + }) + + test('does not run route context after an SSR policy redirect', async () => { + const context = vi.fn(() => ({ reportsContext: true })) + const loader = vi.fn(() => 'reports data') + const rootRoute = new BaseRootRoute({}) + const reportsRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/reports', + ssr: () => { + throw redirect({ to: '/target' }) + }, + context, + loader, + }) + const targetRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/target', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([reportsRoute, targetRoute]), + history: createMemoryHistory({ initialEntries: ['/reports'] }), + isServer: true, + }) + + const response = await loadServerResponse(router, '/reports') + + expect(response.status).toBe(307) + expect(response.headers.get('Location')).toBe('/target') + expect(context).not.toHaveBeenCalled() + expect(loader).not.toHaveBeenCalled() + }) + + test('preserves the SSR policy failure when route context also throws', async () => { + const policyError = new Error('SSR policy failed') + const contextError = new Error('route context failed') + const onError = vi.fn() + const rootRoute = new BaseRootRoute({}) + const reportsRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/reports', + ssr: () => { + throw policyError + }, + context: () => { + throw contextError + }, + onError, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([reportsRoute]), + history: createMemoryHistory({ initialEntries: ['/reports'] }), + isServer: true, + }) + + const response = await loadServerResponse(router, '/reports') + + expect(response.status).toBe(500) + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: reportsRoute.id, + status: 'error', + error: policyError, + }) + expect(onError).toHaveBeenCalledWith(policyError) + expect(onError).not.toHaveBeenCalledWith(contextError) + }) +}) diff --git a/packages/router-core/tests/ssr-match-id.bench.ts b/packages/router-core/tests/ssr-match-id.bench.ts new file mode 100644 index 0000000000..3f6a7c0a6e --- /dev/null +++ b/packages/router-core/tests/ssr-match-id.bench.ts @@ -0,0 +1,53 @@ +import { bench, describe, expect } from 'vitest' +import { dehydrateSsrMatchId, hydrateSsrMatchId } from '../src/ssr/ssr-match-id' + +const typicalIds = Array.from( + { length: 100 }, + (_, index) => + `/$orgId/projects/$projectId/acme/projects/project-${index}{"page":${index}}`, +) +const deepIds = Array.from( + { length: 100 }, + (_, index) => + `${Array.from({ length: 32 }, (__, depth) => `/route-${depth}`).join('')}/${index}`, +) +const reservedIds = Array.from( + { length: 100 }, + (_, index) => `~/\0/\uFFFD/~0/~r/${index}`, +) +const normalizedDeepIds = deepIds.map((id) => + dehydrateSsrMatchId(id).replaceAll('\0', '\uFFFD'), +) +let benchmarkSink = 0 + +for (const id of [...typicalIds, ...deepIds, ...reservedIds]) { + expect(hydrateSsrMatchId(dehydrateSsrMatchId(id))).toBe(id) +} + +describe('SSR match ID codec', () => { + bench('encode 100 typical match IDs', () => { + let size = 0 + for (const id of typicalIds) { + size += dehydrateSsrMatchId(id).length + } + benchmarkSink = size + }) + + bench('decode 100 normalized deep match IDs', () => { + let size = 0 + for (const id of normalizedDeepIds) { + size += hydrateSsrMatchId(id).length + } + benchmarkSink = size + }) + + bench('round-trip 100 reserved-character match IDs', () => { + let size = 0 + for (const id of reservedIds) { + size += hydrateSsrMatchId(dehydrateSsrMatchId(id)).length + } + benchmarkSink = size + }) +}) + +void benchmarkSink diff --git a/packages/router-core/tests/ssr-match-id.test.ts b/packages/router-core/tests/ssr-match-id.test.ts index c6a7437535..65e1cc4ebb 100644 --- a/packages/router-core/tests/ssr-match-id.test.ts +++ b/packages/router-core/tests/ssr-match-id.test.ts @@ -20,6 +20,17 @@ describe('ssr match id codec', () => { expect(hydrateSsrMatchId(id)).toBe(id) }) + it('round-trips reserved and browser-normalized characters', () => { + const id = '~/\0/\uFFFD/~0/~r' + const dehydratedId = dehydrateSsrMatchId(id) + const normalize = (value: string) => value.replaceAll('\0', '\uFFFD') + + expect(hydrateSsrMatchId(normalize(dehydratedId))).toBe(id) + expect(normalize(dehydrateSsrMatchId('/r'))).not.toBe( + normalize(dehydrateSsrMatchId('\uFFFDr')), + ) + }) + it('decodes browser-normalized replacement chars back to slashes', () => { const normalized = dehydrateSsrMatchId('/posts/1').replaceAll( '\0', diff --git a/packages/router-core/tests/ssr-server-cleanup.test.ts b/packages/router-core/tests/ssr-server-cleanup.test.ts index 0cc8832abd..a4d0b96588 100644 --- a/packages/router-core/tests/ssr-server-cleanup.test.ts +++ b/packages/router-core/tests/ssr-server-cleanup.test.ts @@ -1,8 +1,11 @@ import { createMemoryHistory } from '@tanstack/history' -import { describe, expect, test, vi } from 'vitest' +import { afterEach, describe, expect, onTestFinished, test, vi } from 'vitest' import { BaseRootRoute, BaseRoute } from '../src' import { createRequestHandler } from '../src/ssr/createRequestHandler' -import { createSsrStreamResponse } from '../src/ssr/handlerCallback' +import { + bindSsrResponseToRequest, + createSsrStreamResponse, +} from '../src/ssr/handlerCallback' import { attachRouterServerSsrUtils } from '../src/ssr/ssr-server' import { transformStreamWithRouter } from '../src/ssr/transformStreamWithRouter' import { createTestRouter } from './routerTestUtils' @@ -38,6 +41,10 @@ function deferred() { return { promise, resolve } } +afterEach(() => { + vi.restoreAllMocks() +}) + describe('serverSsr.cleanup', () => { test('onCleanup listeners run exactly once', () => { const router = buildRouter() @@ -338,6 +345,226 @@ describe('serverSsr.cleanup', () => { expect(router.serverSsr).toBeUndefined() }) + test('request abort settles while the render callback is still pending', async () => { + const router = buildRouter() + const requestController = new AbortController() + const renderStarted = deferred() + const renderResult = deferred>() + let cleanupCalls = 0 + let cancelCalls = 0 + let lateStreamResponse!: ReturnType + const handler = createRequestHandler({ + createRouter: () => router, + request: new Request('http://localhost/', { + signal: requestController.signal, + }), + }) + + const response = handler(({ router: requestRouter }) => { + const serverSsr = requestRouter.serverSsr! + const cleanup = serverSsr.cleanup + serverSsr.cleanup = () => { + cleanupCalls++ + cleanup() + } + lateStreamResponse = createSsrStreamResponse( + requestRouter, + new Response( + new ReadableStream({ + cancel() { + cancelCalls++ + return new Promise(() => {}) + }, + }), + ), + ) + renderStarted.resolve() + return renderResult.promise + }) + + await renderStarted.promise + const cancellation = new Error('request disconnected') + requestController.abort(cancellation) + + await expect(response).rejects.toBe(cancellation) + expect(cleanupCalls).toBe(1) + expect(router.serverSsr).toBeUndefined() + + renderResult.resolve(lateStreamResponse) + await Promise.resolve() + await Promise.resolve() + expect(cleanupCalls).toBe(1) + expect(cancelCalls).toBe(1) + expect(router.serverSsr).toBeUndefined() + }) + + test('request abort cancels a plain response resolved by the callback later', async () => { + const router = buildRouter() + const requestController = new AbortController() + const callbackStarted = deferred() + const callbackResult = deferred() + const cancel = vi.fn((_reason: unknown) => new Promise(() => {})) + const handler = createRequestHandler({ + createRouter: () => router, + request: new Request('http://localhost/', { + signal: requestController.signal, + }), + }) + + const response = handler(() => { + callbackStarted.resolve() + return callbackResult.promise + }) + + await callbackStarted.promise + const cancellation = new Error('request disconnected') + requestController.abort(cancellation) + + await expect(response).rejects.toBe(cancellation) + callbackResult.resolve(new Response(new ReadableStream({ cancel }))) + await vi.waitFor(() => { + expect(cancel).toHaveBeenCalledTimes(1) + expect(cancel).toHaveBeenCalledWith(cancellation) + }) + }) + + test.each(['throw', 'reject'] as const)( + 'reports a %s from disposal of a late render response', + async (failureMode) => { + const router = buildRouter() + const requestController = new AbortController() + const renderStarted = deferred() + const renderResult = deferred() + const cleanupError = new Error('late stream cleanup failed') + const dispose = vi.fn(() => { + if (failureMode === 'throw') { + throw cleanupError + } + return Promise.reject(cleanupError) + }) + const consoleError = vi + .spyOn(console, 'error') + .mockImplementation(() => undefined) + const handler = createRequestHandler({ + createRouter: () => router, + request: new Request('http://localhost/', { + signal: requestController.signal, + }), + }) + onTestFinished(() => { + consoleError.mockRestore() + }) + onTestFinished(() => { + router.serverSsr?.cleanup() + }) + + const response = handler(() => { + renderStarted.resolve() + return renderResult.promise + }) + + await renderStarted.promise + const cancellation = new Error('request disconnected') + requestController.abort(cancellation) + await expect(response).rejects.toBe(cancellation) + + renderResult.resolve({ + response: new Response('stream'), + serverSsrCleanup: 'stream', + dispose, + }) + await vi.waitFor(() => { + expect(consoleError).toHaveBeenCalledWith(cleanupError) + }) + expect(dispose).toHaveBeenCalledOnce() + }, + ) + + test('request abort disposes a stream after response handoff', async () => { + const router = buildRouter() + const requestController = new AbortController() + let cleanupCalls = 0 + let cancelCalls = 0 + const handler = createRequestHandler({ + createRouter: () => router, + request: new Request('http://localhost/', { + signal: requestController.signal, + }), + }) + + const response = await handler(({ router: requestRouter }) => { + const serverSsr = requestRouter.serverSsr! + const cleanup = serverSsr.cleanup + serverSsr.cleanup = () => { + cleanupCalls++ + cleanup() + } + return createSsrStreamResponse( + requestRouter, + new Response( + new ReadableStream({ + cancel() { + cancelCalls++ + return new Promise(() => {}) + }, + }), + ), + ) + }) + + expect(response.body).not.toBeNull() + expect(cleanupCalls).toBe(0) + requestController.abort(new Error('request disconnected')) + await Promise.resolve() + + expect(cleanupCalls).toBe(1) + expect(cancelCalls).toBe(1) + expect(router.serverSsr).toBeUndefined() + }) + + test.each(['throw', 'reject'] as const)( + 'reports a custom stream disposal %s after request abort', + async (failureMode) => { + const router = buildRouter() + attachRouterServerSsrUtils({ router, manifest: undefined }) + let cleanupCalls = 0 + const cleanup = router.serverSsr!.cleanup + router.serverSsr!.cleanup = () => { + cleanupCalls++ + cleanup() + } + const cleanupError = new Error('custom stream cleanup failed') + const dispose = vi.fn(() => { + if (failureMode === 'throw') { + throw cleanupError + } + return Promise.reject(cleanupError) + }) + const consoleError = vi + .spyOn(console, 'error') + .mockImplementation(() => undefined) + const requestController = new AbortController() + + bindSsrResponseToRequest( + router, + { + response: new Response('stream'), + serverSsrCleanup: 'stream', + dispose, + }, + requestController.signal, + ) + requestController.abort(new Error('request disconnected')) + + await vi.waitFor(() => { + expect(consoleError).toHaveBeenCalledWith(cleanupError) + }) + expect(dispose).toHaveBeenCalledOnce() + expect(cleanupCalls).toBe(1) + expect(router.serverSsr).toBeUndefined() + }, + ) + test('request handler defers cleanup for stream response metadata', async () => { const router = buildRouter() let cleanupCalls = 0 diff --git a/packages/router-core/tests/ssr-server-manifest.test.ts b/packages/router-core/tests/ssr-server-manifest.test.ts index f76535622f..6cdc98a87b 100644 --- a/packages/router-core/tests/ssr-server-manifest.test.ts +++ b/packages/router-core/tests/ssr-server-manifest.test.ts @@ -401,4 +401,56 @@ describe('attachRouterServerSsrUtils manifest dehydration', () => { '/assets/index.js', ]) }) + + test('omits descendant assets past a terminal parent boundary', async () => { + const rootRoute = new BaseRootRoute({}) + const parentRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + loader: () => { + throw new Error('parent failed') + }, + }) + const childRoute = new BaseRoute({ + getParentRoute: () => parentRoute, + path: '/child', + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([parentRoute.addChildren([childRoute])]), + history: createMemoryHistory({ initialEntries: ['/parent/child'] }), + isServer: true, + }) + const manifest: ServerManifest = { + inlineCss: { + styles: { + '/assets/root.css': '.root{}', + '/assets/parent.css': '.parent{}', + '/assets/child.css': '.child{}', + }, + }, + routes: { + [rootRoute.id]: { css: ['/assets/root.css'] }, + [parentRoute.id]: { css: ['/assets/parent.css'] }, + [childRoute.id]: { + css: ['/assets/child.css'], + preloads: ['/assets/child.js'], + }, + }, + } + + attachRouterServerSsrUtils({ router, manifest }) + await router.load() + + expect(router.state.matches).toHaveLength(3) + expect(router.ssr!.manifest?.inlineStyle?.children).toBe('.root{}.parent{}') + + await router.serverSsr!.dehydrate() + const script = router.serverSsr!.takeBufferedScripts() + expect(script?.children).toBeTruthy() + const dehydratedManifest = parseSerializedRouter( + script!.children!, + ).manifest! + + expect(dehydratedManifest.routes[childRoute.id]).toBeUndefined() + }) }) diff --git a/packages/router-core/tests/stay-match-abort.test.ts b/packages/router-core/tests/stay-match-abort.test.ts new file mode 100644 index 0000000000..a389d2be76 --- /dev/null +++ b/packages/router-core/tests/stay-match-abort.test.ts @@ -0,0 +1,247 @@ +import { afterEach, describe, expect, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { + BaseRootRoute, + BaseRoute, + createControlledPromise, + notFound, +} from '../src' +import { createTestRouter } from './routerTestUtils' + +afterEach(() => { + vi.restoreAllMocks() +}) + +/** + * A settled success stay-match must keep its abortController un-aborted + * across navigations and invalidations: loaders can hand that signal to + * still-streaming deferred data (fetch(url, { signal }) consumed via + * ), and the documented contract only cancels the signal when the + * route unloads or the loader call becomes outdated. Only pending or + * actively-fetching matches are cancelled at load start. + */ + +function setup(reloadGate?: Promise) { + let capturedSignal: AbortSignal | undefined + let deferredRequestAbortCount = 0 + let loaderCalls = 0 + + const rootRoute = new BaseRootRoute({}) + const dashboardRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/dashboard', + staleTime: Infinity, + loader: ({ abortController }: { abortController: AbortController }) => { + loaderCalls++ + capturedSignal = abortController.signal + + // A common streaming pattern is to return immediately-available data + // alongside a request consumed later by . That request must stay + // alive while this layout is reused, then be cancelled when it unloads. + const deferredRequest = new Promise<'aborted'>((resolve) => { + abortController.signal.addEventListener( + 'abort', + () => { + deferredRequestAbortCount++ + resolve('aborted') + }, + { once: true }, + ) + }) + + const data = { critical: 'dashboard data', deferredRequest } + return reloadGate && loaderCalls > 1 ? reloadGate.then(() => data) : data + }, + }) + const settingsRoute = new BaseRoute({ + getParentRoute: () => dashboardRoute, + path: '/settings', + }) + const loginRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/login', + }) + + const router = createTestRouter({ + routeTree: rootRoute.addChildren([ + dashboardRoute.addChildren([settingsRoute]), + loginRoute, + ]), + history: createMemoryHistory({ initialEntries: ['/dashboard'] }), + }) + + return { + router, + dashboardRoute, + getSignal: () => capturedSignal, + getDeferredRequestAbortCount: () => deferredRequestAbortCount, + } +} + +describe('stay-match abort scope', () => { + test('unloading a reused match aborts the signal exposed to its loader', async () => { + const { router, getSignal, getDeferredRequestAbortCount } = setup() + + await router.load() + const dashboardLoaderSignal = getSignal() + + // Reusing the dashboard layout for a child must preserve the lifetime of + // its loader's deferred request. + await router.navigate({ to: '/dashboard/settings' }) + expect(dashboardLoaderSignal?.aborted).toBe(false) + expect(getDeferredRequestAbortCount()).toBe(0) + + // Leaving the layout altogether must now cancel that same request. + await router.navigate({ to: '/login' }) + expect(dashboardLoaderSignal?.aborted).toBe(true) + expect(getDeferredRequestAbortCount()).toBe(1) + }) + + test('background invalidation keeps the old loader signal until fresh data commits', async () => { + const reloadGate = createControlledPromise() + const { router, dashboardRoute, getSignal } = setup(reloadGate) + + await router.load() + const firstSignal = getSignal() + expect(firstSignal?.aborted).toBe(false) + + const invalidation = router.invalidate() + await vi.waitFor(() => expect(getSignal()).not.toBe(firstSignal)) + const replacementSignal = getSignal() + + // The old loader data is still the published generation while the + // background replacement is private. + expect(firstSignal?.aborted).toBe(false) + expect(replacementSignal?.aborted).toBe(false) + + reloadGate.resolve() + await invalidation + await vi.waitFor(() => expect(firstSignal?.aborted).toBe(true)) + expect(router.state.matches.at(-1)).toMatchObject({ + routeId: dashboardRoute.id, + status: 'success', + }) + expect(replacementSignal?.aborted).toBe(false) + }) + + test.each(['beforeLoad', 'shouldReload'] as const)( + '%s failure replacing a successful stay aborts its loader signal', + async (failureStage) => { + const failure = new Error(`${failureStage} failed`) + const onAbort = vi.fn() + let shouldFail = false + let loaderSignal: AbortSignal | undefined + + const rootRoute = new BaseRootRoute({}) + const accountRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/account', + staleTime: Infinity, + beforeLoad: () => { + if (shouldFail && failureStage === 'beforeLoad') { + throw failure + } + }, + shouldReload: () => { + if (shouldFail && failureStage === 'shouldReload') { + throw failure + } + return false + }, + loader: ({ abortController }: { abortController: AbortController }) => { + loaderSignal = abortController.signal + loaderSignal.addEventListener('abort', onAbort, { once: true }) + return { user: 'Flo' } + }, + errorComponent: () => null, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([accountRoute]), + history: createMemoryHistory({ initialEntries: ['/account'] }), + }) + + await router.load() + expect(loaderSignal?.aborted).toBe(false) + + shouldFail = true + await router.load() + + const match = router.state.matches.find( + (candidate) => candidate.routeId === accountRoute.id, + ) + expect(match?.status).toBe('error') + expect(match?.error).toBe(failure) + expect(loaderSignal?.aborted).toBe(true) + expect(onAbort).toHaveBeenCalledTimes(1) + }, + ) + + test('background notFound replacing successful data aborts its old loader signal', async () => { + let loaderCalls = 0 + let initialSignal: AbortSignal | undefined + + const rootRoute = new BaseRootRoute({}) + const accountRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/account', + staleTime: Infinity, + loader: ({ abortController }: { abortController: AbortController }) => { + loaderCalls++ + if (loaderCalls === 1) { + initialSignal = abortController.signal + return { user: 'Flo' } + } + throw notFound() + }, + notFoundComponent: () => null, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([accountRoute]), + history: createMemoryHistory({ initialEntries: ['/account'] }), + }) + + await router.load() + expect(initialSignal?.aborted).toBe(false) + + await router.invalidate() + await vi.waitFor(() => + expect(router.state.matches.at(-1)?.status).toBe('notFound'), + ) + expect(initialSignal?.aborted).toBe(true) + }) + + test('decorative background asset failure still transfers loader signal ownership', async () => { + vi.spyOn(console, 'error').mockImplementation(() => undefined) + let loaderCalls = 0 + const loaderSignals: Array = [] + + const rootRoute = new BaseRootRoute({}) + const accountRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/account', + staleTime: Infinity, + loader: ({ abortController }: { abortController: AbortController }) => { + loaderSignals.push(abortController.signal) + return { revision: ++loaderCalls } + }, + head: ({ loaderData }) => { + if (loaderData?.revision === 2) { + throw new Error('fresh head failed') + } + return { meta: [{ title: 'account' }] } + }, + }) + const router = createTestRouter({ + routeTree: rootRoute.addChildren([accountRoute]), + history: createMemoryHistory({ initialEntries: ['/account'] }), + }) + + await router.load() + await router.invalidate() + + expect(router.state.matches.at(-1)?.loaderData).toEqual({ revision: 2 }) + expect(loaderSignals).toHaveLength(2) + expect(loaderSignals[0]?.aborted).toBe(true) + expect(loaderSignals[1]?.aborted).toBe(false) + }) +}) diff --git a/packages/router-core/tests/transformStreamWithRouter.test.ts b/packages/router-core/tests/transformStreamWithRouter.test.ts index 13b0072e24..903efeb0c9 100644 --- a/packages/router-core/tests/transformStreamWithRouter.test.ts +++ b/packages/router-core/tests/transformStreamWithRouter.test.ts @@ -436,6 +436,38 @@ describe('transformStreamWithRouter — real SSR scripts', () => { }) describe('transformStreamWithRouter — cleanup side-effects', () => { + test.each([ + ['fast', true], + ['main', false], + ] as const)( + 'request abort cleans the %s transform without consuming its response body', + async (_path, fastPath) => { + const { router, cleanupCalls } = makeRouter({ + isSerializationFinished: () => fastPath, + reserveStreamFastPath: () => fastPath, + takeBufferedHtml: () => undefined, + }) + const upstream = makeManualUpstream() + const request = new AbortController() + + const responseBody = transformStreamWithRouter( + router as any, + upstream.stream as any, + { signal: request.signal }, + ) + + expect(responseBody.locked).toBe(false) + + const reason = new Error('request-gone') + request.abort(reason) + await flush() + + expect(responseBody.locked).toBe(false) + expect(upstream.cancelled).toEqual({ value: true, reason }) + expect(cleanupCalls.count).toBe(1) + }, + ) + test('downstream cancel propagates upstream and calls serverSsr.cleanup once', async () => { // Fast path: simpler, no scanner. Verifies cancel + cleanup contract. const { router, cleanupCalls } = makeRouter({ diff --git a/packages/router-devtools-core/package.json b/packages/router-devtools-core/package.json index 9dd2165884..043626815e 100644 --- a/packages/router-devtools-core/package.json +++ b/packages/router-devtools-core/package.json @@ -25,7 +25,8 @@ ], "scripts": { "clean": "rimraf ./dist && rimraf ./coverage", - "test:eslint": "eslint ./src", + "test:eslint": "eslint ./src ./tests", + "test:unit": "vitest", "test:types": "pnpm run \"/^test:types:ts[0-9]{2}$/\"", "test:types:ts56": "node ../../node_modules/typescript56/lib/tsc.js", "test:types:ts57": "node ../../node_modules/typescript57/lib/tsc.js", @@ -66,6 +67,7 @@ "goober": "^2.1.16" }, "devDependencies": { + "@testing-library/jest-dom": "^6.6.3", "solid-js": "^1.9.10", "vite": "*", "vite-plugin-solid": "^2.11.10" diff --git a/packages/router-devtools-core/src/AgeTicker.tsx b/packages/router-devtools-core/src/AgeTicker.tsx index 27d179fda1..e3bd657358 100644 --- a/packages/router-devtools-core/src/AgeTicker.tsx +++ b/packages/router-devtools-core/src/AgeTicker.tsx @@ -1,6 +1,6 @@ import { clsx as cx } from 'clsx' import { useStyles } from './useStyles' -import type { AnyRouteMatch, AnyRouter } from '@tanstack/router-core' +import type { AnyRoute, AnyRouteMatch, AnyRouter } from '@tanstack/router-core' import type { Accessor } from 'solid-js' function formatTime(ms: number) { @@ -35,7 +35,9 @@ export function AgeTicker({ return null } - const route = router().looseRoutesById[match.routeId]! + const route = (router().routesById as Record)[ + match.routeId + ]! if (!route.options.loader) { return null diff --git a/packages/router-devtools-core/src/BaseTanStackRouterDevtoolsPanel.tsx b/packages/router-devtools-core/src/BaseTanStackRouterDevtoolsPanel.tsx index 79566656b0..966eb32bc5 100644 --- a/packages/router-devtools-core/src/BaseTanStackRouterDevtoolsPanel.tsx +++ b/packages/router-devtools-core/src/BaseTanStackRouterDevtoolsPanel.tsx @@ -282,46 +282,40 @@ export const BaseTanStackRouterDevtoolsPanel = const [history, setHistory] = createSignal>([]) const [hasHistoryOverflowed, setHasHistoryOverflowed] = createSignal(false) - let pendingMatches: Accessor> - let cachedMatches: Accessor> - // subscribable implementation - if ('subscribe' in router().stores.pendingMatches) { - const [_pendingMatches, setPending] = createSignal>( - [], - ) - pendingMatches = _pendingMatches - - const [_cachedMatches, setCached] = createSignal>([]) - cachedMatches = _cachedMatches - - type Subscribe = (fn: () => void) => { unsubscribe: () => void } - createEffect(() => { - const pendingMatchesStore = router().stores.pendingMatches - setPending(pendingMatchesStore.get()) - const subscription = ( - (pendingMatchesStore as any).subscribe as Subscribe - )(() => { - setPending(pendingMatchesStore.get()) - }) - onCleanup(() => subscription.unsubscribe()) - }) - - createEffect(() => { - const cachedMatchesStore = router().stores.cachedMatches - setCached(cachedMatchesStore.get()) - const subscription = ( - (cachedMatchesStore as any).subscribe as Subscribe - )(() => { - setCached(cachedMatchesStore.get()) - }) - onCleanup(() => subscription.unsubscribe()) - }) - } - // signal implementation - else { - pendingMatches = () => router().stores.pendingMatches.get() - cachedMatches = () => router().stores.cachedMatches.get() + const pendingMatches = () => { + const matches = routerState().matches + return matches.some((match: AnyRouteMatch) => match.status === 'pending') + ? matches + : [] } + let cache = router()._cache + let cacheMatches = [...cache.values()] + const [cachedMatches, setCachedMatches] = createSignal(cacheMatches) + + createEffect(() => { + const refreshCache = () => { + const next = router()._cache + let valuesChanged = next.size !== cacheMatches.length + if (!valuesChanged) { + let index = 0 + for (const match of next.values()) { + if (match !== cacheMatches[index++]) { + valuesChanged = true + break + } + } + } + + if (next !== cache || valuesChanged) { + cache = next + cacheMatches = [...next.values()] + setCachedMatches(cacheMatches) + } + } + refreshCache() + const interval = setInterval(refreshCache, 500) + onCleanup(() => clearInterval(interval)) + }) createEffect(() => { const matches = routerState().matches @@ -392,7 +386,6 @@ export const BaseTanStackRouterDevtoolsPanel = 'stores', 'basepath', 'subscribers', - 'latestLoadPromise', '_scroll', 'tempLocationKey', 'latestLocation', @@ -719,7 +712,9 @@ export const BaseTanStackRouterDevtoolsPanel =
    State:
    - {pendingMatches().find((d) => d.id === activeMatch()?.id) + {pendingMatches().find( + (d: AnyRouteMatch) => d.id === activeMatch()?.id, + ) ? 'Pending' : routerState().matches.find( (d: any) => d.id === activeMatch()?.id, diff --git a/packages/router-devtools-core/src/useStyles.tsx b/packages/router-devtools-core/src/useStyles.tsx index 5524ed0ae8..32908c524d 100644 --- a/packages/router-devtools-core/src/useStyles.tsx +++ b/packages/router-devtools-core/src/useStyles.tsx @@ -428,7 +428,7 @@ const stylesFactory = (shadowDOMTarget?: ShadowRoot) => { line-height: ${tokens.font.lineHeight.sm}; `, matchStatus: ( - status: 'pending' | 'success' | 'error' | 'notFound' | 'redirected', + status: 'pending' | 'success' | 'error' | 'notFound', isFetching: false | 'beforeLoad' | 'loader', ) => { const colorMap = { @@ -436,7 +436,6 @@ const stylesFactory = (shadowDOMTarget?: ShadowRoot) => { success: 'green', error: 'red', notFound: 'purple', - redirected: 'gray', } as const const color = diff --git a/packages/router-devtools-core/src/utils.tsx b/packages/router-devtools-core/src/utils.tsx index c14bfd80be..962cdaf0f3 100644 --- a/packages/router-devtools-core/src/utils.tsx +++ b/packages/router-devtools-core/src/utils.tsx @@ -25,7 +25,6 @@ export function getStatusColor(match: AnyRouteMatch) { success: 'green', error: 'red', notFound: 'purple', - redirected: 'gray', } as const return match.isFetching && match.status === 'success' diff --git a/packages/router-devtools-core/tests/cache-replacement.test.ts b/packages/router-devtools-core/tests/cache-replacement.test.ts new file mode 100644 index 0000000000..1da523a1fb --- /dev/null +++ b/packages/router-devtools-core/tests/cache-replacement.test.ts @@ -0,0 +1,104 @@ +import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest' +import { TanStackRouterDevtoolsPanelCore } from '../src/TanStackRouterDevtoolsPanelCore' +import type { AnyRouteMatch, AnyRouter } from '@tanstack/router-core' + +function createCachedMatch(loaderData: string): AnyRouteMatch { + return { + id: 'cached-match', + routeId: '/cached', + pathname: '/cached', + params: {}, + search: {}, + status: 'success', + isFetching: false, + updatedAt: Date.now(), + loaderData, + } as AnyRouteMatch +} + +describe('cached matches', () => { + let panel: TanStackRouterDevtoolsPanelCore | undefined + + // Warm Vite's lazy transform of the panel chunk outside any test so its + // cost never counts against a test's timeout budget on slow CI runners. + beforeAll(async () => { + await import('../src/BaseTanStackRouterDevtoolsPanel') + }, 30_000) + + afterEach(() => { + panel?.unmount() + panel = undefined + document.body.innerHTML = '' + try { + window.localStorage.clear() + } catch {} + vi.useRealTimers() + }) + + it('refreshes when an existing cache entry is replaced', async () => { + vi.useFakeTimers() + + const route = { + id: '/cached', + path: 'cached', + fullPath: '/cached', + rank: 0, + children: [], + options: { loader: () => undefined }, + } + const routeTree = { + id: '__root__', + path: '/', + fullPath: '/', + rank: 0, + children: [route], + options: {}, + } + const cache = new Map([['cached-match', createCachedMatch('old data')]]) + const router = { + _cache: cache, + routeTree, + routesById: { + __root__: routeTree, + '/cached': route, + }, + options: {}, + navigate: vi.fn(), + } as unknown as AnyRouter + const routerState = { + location: { + href: '/', + pathname: '/', + search: {}, + searchStr: '', + hash: '', + }, + matches: [], + } + const container = document.createElement('div') + document.body.append(container) + + panel = new TanStackRouterDevtoolsPanelCore({ router, routerState }) + panel.mount(container) + + await vi.waitFor(() => { + expect( + container.querySelector( + '[aria-label="Open match details for cached-match"]', + ), + ).not.toBeNull() + }) + + const cachedMatch = container.querySelector( + '[aria-label="Open match details for cached-match"]', + ) as HTMLElement + cachedMatch.click() + expect(container.textContent).toContain('old data') + + cache.set('cached-match', createCachedMatch('new data')) + await vi.advanceTimersByTimeAsync(500) + + expect(container.textContent).toContain('new data') + expect(container.textContent).not.toContain('old data') + }, 10_000) +}) diff --git a/packages/router-devtools-core/tsconfig.json b/packages/router-devtools-core/tsconfig.json index 1b802b64d1..e24672b5de 100644 --- a/packages/router-devtools-core/tsconfig.json +++ b/packages/router-devtools-core/tsconfig.json @@ -4,5 +4,5 @@ "jsx": "preserve", "jsxImportSource": "solid-js" }, - "include": ["src", "vite.config.ts"] + "include": ["src", "tests", "vite.config.ts"] } diff --git a/packages/router-devtools-core/vite.config.ts b/packages/router-devtools-core/vite.config.ts index 53da7f8839..b14dd2bdf5 100644 --- a/packages/router-devtools-core/vite.config.ts +++ b/packages/router-devtools-core/vite.config.ts @@ -1,9 +1,23 @@ import { defineConfig, mergeConfig } from 'vitest/config' import { tanstackViteConfig } from '@tanstack/vite-config' import solid from 'vite-plugin-solid' +import packageJson from './package.json' const config = defineConfig({ plugins: [solid()], + test: { + name: packageJson.name, + dir: './tests', + watch: false, + environment: 'jsdom', + typecheck: { enabled: true }, + setupFiles: [], + server: { + deps: { + inline: [/solid-js/], + }, + }, + }, }) const merged = mergeConfig( diff --git a/packages/router-plugin/src/core/hmr/handle-route-update.ts b/packages/router-plugin/src/core/hmr/handle-route-update.ts index c0325b06e9..23b51d2432 100644 --- a/packages/router-plugin/src/core/hmr/handle-route-update.ts +++ b/packages/router-plugin/src/core/hmr/handle-route-update.ts @@ -1,15 +1,9 @@ -import type { - AnyRoute, - AnyRouteMatch, - AnyRouter, - RouterWritableStore, -} from '@tanstack/router-core' +import type { AnyRoute, AnyRouter } from '@tanstack/router-core' type AnyRouteWithPrivateProps = AnyRoute & { options: Record parentRoute: AnyRoute - _componentsPromise?: Promise - _lazyPromise?: Promise + _lazy?: Promise | true update: (options: Record) => unknown _path: string _id: string @@ -17,37 +11,19 @@ type AnyRouteWithPrivateProps = AnyRoute & { _to: string } -type AnyRouterWithPrivateMaps = AnyRouter & { +type AnyRouterWithPrivateState = AnyRouter & { routesById: Record buildRouteTree: () => Parameters[0] setRoutes: AnyRouter['setRoutes'] - stores: AnyRouter['stores'] & { - cachedMatchStores: Map< - string, - Pick, 'get' | 'set'> - > - pendingMatchStores: Map< - string, - Pick, 'get' | 'set'> - > - matchStores: Map< - string, - Pick, 'get' | 'set'> - > - } -} - -type AnyRouteMatchWithPrivateProps = AnyRouteMatch & { - __beforeLoadContext?: unknown - __routeContext?: Record - context?: Record + _refreshRoute?: () => Promise + _replaceRouteChunk: (route: AnyRoute, lazyFn: AnyRoute['lazyFn']) => void } function handleRouteUpdate( routeId: string, newRoute: AnyRouteWithPrivateProps, ) { - const router = window.__TSR_ROUTER__ as AnyRouterWithPrivateMaps + const router = window.__TSR_ROUTER__ as AnyRouterWithPrivateState const oldRoute = router.routesById[routeId] as | AnyRouteWithPrivateProps | undefined @@ -66,14 +42,6 @@ function handleRouteUpdate( } }) - const removedKeys = new Set() - Object.keys(oldRoute.options).forEach((key) => { - if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) { - removedKeys.add(key) - delete oldRoute.options[key] - } - }) - const oldHasShellComponent = 'shellComponent' in oldRoute.options const newHasShellComponent = 'shellComponent' in newRoute.options const preserveComponentIdentity = @@ -106,60 +74,12 @@ function handleRouteUpdate( oldRoute.options = nextOptions oldRoute.update(nextOptions) - oldRoute._componentsPromise = undefined - oldRoute._lazyPromise = undefined + router._replaceRouteChunk(oldRoute, newRoute.lazyFn) router.setRoutes(router.buildRouteTree()) syncHotRouteExport(oldRoute) router.resolvePathCache.clear() - - const filter = (m: AnyRouteMatch) => m.routeId === oldRoute.id - const activeMatch = router.stores.matches.get().find(filter) - const pendingMatch = router.stores.pendingMatches.get().find(filter) - const cachedMatches = router.stores.cachedMatches.get().filter(filter) - - if (activeMatch || pendingMatch || cachedMatches.length > 0) { - // Clear stale match data for removed route options BEFORE invalidating. - // Without this, router.invalidate() -> matchRoutes() reuses the existing - // match from the store (via ...existingMatch spread) and the stale - // loaderData / __beforeLoadContext survives the reload cycle. - // - // We must update the store directly (not via router.updateMatch) because - // updateMatch wraps in startTransition which may defer the state update, - // and we need the clear to be visible before invalidate reads the store. - if (removedKeys.has('loader') || removedKeys.has('beforeLoad')) { - const matchIds = [ - activeMatch?.id, - pendingMatch?.id, - ...cachedMatches.map((match) => match.id), - ].filter(Boolean) as Array - router.batch(() => { - for (const matchId of matchIds) { - const store = - router.stores.pendingMatchStores.get(matchId) || - router.stores.matchStores.get(matchId) || - router.stores.cachedMatchStores.get(matchId) - if (store) { - store.set((prev) => { - const next: AnyRouteMatchWithPrivateProps = { ...prev } - - if (removedKeys.has('loader')) { - next.loaderData = undefined - } - if (removedKeys.has('beforeLoad')) { - next.__beforeLoadContext = undefined - next.context = rebuildMatchContextWithoutBeforeLoad(next) - } - - return next - }) - } - } - }) - } - - router.invalidate({ filter, sync: true }) - } + void router._refreshRoute?.() function syncHotRouteExport(liveRoute: AnyRouteWithPrivateProps) { // routeTree.gen.ts mutates the original module export with generated @@ -172,66 +92,6 @@ function handleRouteUpdate( newRoute._fullPath = liveRoute._fullPath newRoute._to = liveRoute._to } - - function getStoreMatch(matchId: string) { - return ( - router.stores.pendingMatchStores.get(matchId)?.get() || - router.stores.matchStores.get(matchId)?.get() || - router.stores.cachedMatchStores.get(matchId)?.get() - ) - } - - function getMatchList(matchId: string) { - const pendingMatches = router.stores.pendingMatches.get() - if (pendingMatches.some((match) => match.id === matchId)) { - return pendingMatches - } - - const activeMatches = router.stores.matches.get() - if (activeMatches.some((match) => match.id === matchId)) { - return activeMatches - } - - const cachedMatches = router.stores.cachedMatches.get() - if (cachedMatches.some((match) => match.id === matchId)) { - return cachedMatches - } - - return [] - } - - function getParentMatch(match: AnyRouteMatch) { - const matchList = getMatchList(match.id) - const matchIndex = matchList.findIndex((item) => item.id === match.id) - - if (matchIndex <= 0) { - return undefined - } - - const parentMatch = matchList[matchIndex - 1]! - return getStoreMatch(parentMatch.id) || parentMatch - } - - function rebuildMatchContextWithoutBeforeLoad( - match: AnyRouteMatchWithPrivateProps, - ) { - const parentMatch = getParentMatch(match) - const getParentContext = ( - router as unknown as { - getParentContext?: ( - parentMatch?: AnyRouteMatch, - ) => Record | undefined - } - ).getParentContext - const parentContext = getParentContext - ? getParentContext.call(router, parentMatch) - : (parentMatch?.context ?? router.options.context) - - return { - ...(parentContext ?? {}), - ...(match.__routeContext ?? {}), - } - } } const handleRouteUpdateStr = handleRouteUpdate.toString() diff --git a/packages/router-plugin/tests/add-hmr.test.ts b/packages/router-plugin/tests/add-hmr.test.ts index b0481bb7f8..98b3e61a5f 100644 --- a/packages/router-plugin/tests/add-hmr.test.ts +++ b/packages/router-plugin/tests/add-hmr.test.ts @@ -120,6 +120,36 @@ describe('add-hmr works', () => { expect(output).toContain('newModule') }) + it('generates lazy reset and scoped route refresh without route context mutation', async () => { + const filename = 'arrow-function.tsx' + const framework = 'react' + const file = await readFile( + path.join(getFrameworkDir(framework).files, filename), + ) + const code = file.toString() + const compileResult = compileCodeSplitReferenceRoute({ + code, + filename, + id: filename, + addHmr: true, + codeSplitGroupings: defaultCodeSplitGroupings, + targetFramework: framework, + compilerPlugins: getFrameworkHmrCompilerPlugins({ + targetFramework: framework, + }), + }) + if (!compileResult) { + throw new Error('Expected the reference route to be transformed') + } + const output = compileResult.code + + expect(output).toContain( + 'router._replaceRouteChunk(oldRoute, newRoute.lazyFn);', + ) + expect(output).toContain('void router._refreshRoute?.();') + expect(output).not.toContain('__routeContext') + }) + it('uses a generated route id fallback for Vite HMR', async () => { const statement = createRouteHmrStatement([], { hmrStyle: 'vite', diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/arrow-function@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/arrow-function@true.tsx index a463d9ab22..4f32369e8f 100644 --- a/packages/router-plugin/tests/add-hmr/snapshots/react/arrow-function@true.tsx +++ b/packages/router-plugin/tests/add-hmr/snapshots/react/arrow-function@true.tsx @@ -52,13 +52,6 @@ if (import.meta.hot) { generatedRouteOptions[key] = oldRoute.options[key]; } }); - const removedKeys = new Set(); - Object.keys(oldRoute.options).forEach(key => { - if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) { - removedKeys.add(key); - delete oldRoute.options[key]; - } - }); const oldHasShellComponent = "shellComponent" in oldRoute.options; const newHasShellComponent = "shellComponent" in newRoute.options; const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent; @@ -77,48 +70,11 @@ if (import.meta.hot) { }; oldRoute.options = nextOptions; oldRoute.update(nextOptions); - oldRoute._componentsPromise = undefined; - oldRoute._lazyPromise = undefined; + router._replaceRouteChunk(oldRoute, newRoute.lazyFn); router.setRoutes(router.buildRouteTree()); syncHotRouteExport(oldRoute); router.resolvePathCache.clear(); - const filter = m => m.routeId === oldRoute.id; - const activeMatch = router.stores.matches.get().find(filter); - const pendingMatch = router.stores.pendingMatches.get().find(filter); - const cachedMatches = router.stores.cachedMatches.get().filter(filter); - if (activeMatch || pendingMatch || cachedMatches.length > 0) { - if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) { - const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean); - router.batch(() => { - for (const matchId of matchIds) { - const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId); - if (store) { - store.set(prev => { - const next = { - ...prev - }; - if (removedKeys.has("loader")) { - next.loaderData = undefined; - } - ; - if (removedKeys.has("beforeLoad")) { - next.__beforeLoadContext = undefined; - next.context = rebuildMatchContextWithoutBeforeLoad(next); - } - ; - return next; - }); - } - } - }); - } - ; - router.invalidate({ - filter, - sync: true - }); - } - ; + void router._refreshRoute?.(); function syncHotRouteExport(liveRoute) { newRoute.options = liveRoute.options; newRoute.parentRoute = liveRoute.parentRoute; @@ -127,46 +83,6 @@ if (import.meta.hot) { newRoute._fullPath = liveRoute._fullPath; newRoute._to = liveRoute._to; } - function getStoreMatch(matchId) { - return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get(); - } - function getMatchList(matchId) { - const pendingMatches = router.stores.pendingMatches.get(); - if (pendingMatches.some(match => match.id === matchId)) { - return pendingMatches; - } - ; - const activeMatches = router.stores.matches.get(); - if (activeMatches.some(match => match.id === matchId)) { - return activeMatches; - } - ; - const cachedMatches = router.stores.cachedMatches.get(); - if (cachedMatches.some(match => match.id === matchId)) { - return cachedMatches; - } - ; - return []; - } - function getParentMatch(match) { - const matchList = getMatchList(match.id); - const matchIndex = matchList.findIndex(item => item.id === match.id); - if (matchIndex <= 0) { - return undefined; - } - ; - const parentMatch = matchList[matchIndex - 1]; - return getStoreMatch(parentMatch.id) || parentMatch; - } - function rebuildMatchContextWithoutBeforeLoad(match) { - const parentMatch = getParentMatch(match); - const getParentContext = router.getParentContext; - const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context; - return { - ...(parentContext ?? {}), - ...(match.__routeContext ?? {}) - }; - } }; const initialRouteId = Route.id ?? hotData['tsr-route-id']; if (initialRouteId) { diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/arrow-function@webpack-hot.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/arrow-function@webpack-hot.tsx index 44908e32a9..12e47f429e 100644 --- a/packages/router-plugin/tests/add-hmr/snapshots/react/arrow-function@webpack-hot.tsx +++ b/packages/router-plugin/tests/add-hmr/snapshots/react/arrow-function@webpack-hot.tsx @@ -38,13 +38,6 @@ if (import.meta.webpackHot) { generatedRouteOptions[key] = oldRoute.options[key]; } }); - const removedKeys = new Set(); - Object.keys(oldRoute.options).forEach(key => { - if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) { - removedKeys.add(key); - delete oldRoute.options[key]; - } - }); const oldHasShellComponent = "shellComponent" in oldRoute.options; const newHasShellComponent = "shellComponent" in newRoute.options; const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent; @@ -63,48 +56,11 @@ if (import.meta.webpackHot) { }; oldRoute.options = nextOptions; oldRoute.update(nextOptions); - oldRoute._componentsPromise = undefined; - oldRoute._lazyPromise = undefined; + router._replaceRouteChunk(oldRoute, newRoute.lazyFn); router.setRoutes(router.buildRouteTree()); syncHotRouteExport(oldRoute); router.resolvePathCache.clear(); - const filter = m => m.routeId === oldRoute.id; - const activeMatch = router.stores.matches.get().find(filter); - const pendingMatch = router.stores.pendingMatches.get().find(filter); - const cachedMatches = router.stores.cachedMatches.get().filter(filter); - if (activeMatch || pendingMatch || cachedMatches.length > 0) { - if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) { - const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean); - router.batch(() => { - for (const matchId of matchIds) { - const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId); - if (store) { - store.set(prev => { - const next = { - ...prev - }; - if (removedKeys.has("loader")) { - next.loaderData = undefined; - } - ; - if (removedKeys.has("beforeLoad")) { - next.__beforeLoadContext = undefined; - next.context = rebuildMatchContextWithoutBeforeLoad(next); - } - ; - return next; - }); - } - } - }); - } - ; - router.invalidate({ - filter, - sync: true - }); - } - ; + void router._refreshRoute?.(); function syncHotRouteExport(liveRoute) { newRoute.options = liveRoute.options; newRoute.parentRoute = liveRoute.parentRoute; @@ -113,46 +69,6 @@ if (import.meta.webpackHot) { newRoute._fullPath = liveRoute._fullPath; newRoute._to = liveRoute._to; } - function getStoreMatch(matchId) { - return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get(); - } - function getMatchList(matchId) { - const pendingMatches = router.stores.pendingMatches.get(); - if (pendingMatches.some(match => match.id === matchId)) { - return pendingMatches; - } - ; - const activeMatches = router.stores.matches.get(); - if (activeMatches.some(match => match.id === matchId)) { - return activeMatches; - } - ; - const cachedMatches = router.stores.cachedMatches.get(); - if (cachedMatches.some(match => match.id === matchId)) { - return cachedMatches; - } - ; - return []; - } - function getParentMatch(match) { - const matchList = getMatchList(match.id); - const matchIndex = matchList.findIndex(item => item.id === match.id); - if (matchIndex <= 0) { - return undefined; - } - ; - const parentMatch = matchList[matchIndex - 1]; - return getStoreMatch(parentMatch.id) || parentMatch; - } - function rebuildMatchContextWithoutBeforeLoad(match) { - const parentMatch = getParentMatch(match); - const getParentContext = router.getParentContext; - const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context; - return { - ...(parentContext ?? {}), - ...(match.__routeContext ?? {}) - }; - } })(routeId, Route); try { const tsrReactRefreshUtils = typeof __react_refresh_utils__ !== 'undefined' ? __react_refresh_utils__ : undefined; diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/createFileRoute-lowercase-components@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/createFileRoute-lowercase-components@true.tsx index b04afe83f4..70c428363e 100644 --- a/packages/router-plugin/tests/add-hmr/snapshots/react/createFileRoute-lowercase-components@true.tsx +++ b/packages/router-plugin/tests/add-hmr/snapshots/react/createFileRoute-lowercase-components@true.tsx @@ -63,13 +63,6 @@ if (import.meta.hot) { generatedRouteOptions[key] = oldRoute.options[key]; } }); - const removedKeys = new Set(); - Object.keys(oldRoute.options).forEach(key => { - if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) { - removedKeys.add(key); - delete oldRoute.options[key]; - } - }); const oldHasShellComponent = "shellComponent" in oldRoute.options; const newHasShellComponent = "shellComponent" in newRoute.options; const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent; @@ -88,48 +81,11 @@ if (import.meta.hot) { }; oldRoute.options = nextOptions; oldRoute.update(nextOptions); - oldRoute._componentsPromise = undefined; - oldRoute._lazyPromise = undefined; + router._replaceRouteChunk(oldRoute, newRoute.lazyFn); router.setRoutes(router.buildRouteTree()); syncHotRouteExport(oldRoute); router.resolvePathCache.clear(); - const filter = m => m.routeId === oldRoute.id; - const activeMatch = router.stores.matches.get().find(filter); - const pendingMatch = router.stores.pendingMatches.get().find(filter); - const cachedMatches = router.stores.cachedMatches.get().filter(filter); - if (activeMatch || pendingMatch || cachedMatches.length > 0) { - if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) { - const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean); - router.batch(() => { - for (const matchId of matchIds) { - const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId); - if (store) { - store.set(prev => { - const next = { - ...prev - }; - if (removedKeys.has("loader")) { - next.loaderData = undefined; - } - ; - if (removedKeys.has("beforeLoad")) { - next.__beforeLoadContext = undefined; - next.context = rebuildMatchContextWithoutBeforeLoad(next); - } - ; - return next; - }); - } - } - }); - } - ; - router.invalidate({ - filter, - sync: true - }); - } - ; + void router._refreshRoute?.(); function syncHotRouteExport(liveRoute) { newRoute.options = liveRoute.options; newRoute.parentRoute = liveRoute.parentRoute; @@ -138,46 +94,6 @@ if (import.meta.hot) { newRoute._fullPath = liveRoute._fullPath; newRoute._to = liveRoute._to; } - function getStoreMatch(matchId) { - return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get(); - } - function getMatchList(matchId) { - const pendingMatches = router.stores.pendingMatches.get(); - if (pendingMatches.some(match => match.id === matchId)) { - return pendingMatches; - } - ; - const activeMatches = router.stores.matches.get(); - if (activeMatches.some(match => match.id === matchId)) { - return activeMatches; - } - ; - const cachedMatches = router.stores.cachedMatches.get(); - if (cachedMatches.some(match => match.id === matchId)) { - return cachedMatches; - } - ; - return []; - } - function getParentMatch(match) { - const matchList = getMatchList(match.id); - const matchIndex = matchList.findIndex(item => item.id === match.id); - if (matchIndex <= 0) { - return undefined; - } - ; - const parentMatch = matchList[matchIndex - 1]; - return getStoreMatch(parentMatch.id) || parentMatch; - } - function rebuildMatchContextWithoutBeforeLoad(match) { - const parentMatch = getParentMatch(match); - const getParentContext = router.getParentContext; - const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context; - return { - ...(parentContext ?? {}), - ...(match.__routeContext ?? {}) - }; - } }; const initialRouteId = Route.id ?? hotData['tsr-route-id']; if (initialRouteId) { diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute-inline-component@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute-inline-component@true.tsx index 9ef182f4b7..e5a473dc14 100644 --- a/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute-inline-component@true.tsx +++ b/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute-inline-component@true.tsx @@ -41,13 +41,6 @@ if (import.meta.hot) { generatedRouteOptions[key] = oldRoute.options[key]; } }); - const removedKeys = new Set(); - Object.keys(oldRoute.options).forEach(key => { - if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) { - removedKeys.add(key); - delete oldRoute.options[key]; - } - }); const oldHasShellComponent = "shellComponent" in oldRoute.options; const newHasShellComponent = "shellComponent" in newRoute.options; const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent; @@ -66,48 +59,11 @@ if (import.meta.hot) { }; oldRoute.options = nextOptions; oldRoute.update(nextOptions); - oldRoute._componentsPromise = undefined; - oldRoute._lazyPromise = undefined; + router._replaceRouteChunk(oldRoute, newRoute.lazyFn); router.setRoutes(router.buildRouteTree()); syncHotRouteExport(oldRoute); router.resolvePathCache.clear(); - const filter = m => m.routeId === oldRoute.id; - const activeMatch = router.stores.matches.get().find(filter); - const pendingMatch = router.stores.pendingMatches.get().find(filter); - const cachedMatches = router.stores.cachedMatches.get().filter(filter); - if (activeMatch || pendingMatch || cachedMatches.length > 0) { - if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) { - const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean); - router.batch(() => { - for (const matchId of matchIds) { - const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId); - if (store) { - store.set(prev => { - const next = { - ...prev - }; - if (removedKeys.has("loader")) { - next.loaderData = undefined; - } - ; - if (removedKeys.has("beforeLoad")) { - next.__beforeLoadContext = undefined; - next.context = rebuildMatchContextWithoutBeforeLoad(next); - } - ; - return next; - }); - } - } - }); - } - ; - router.invalidate({ - filter, - sync: true - }); - } - ; + void router._refreshRoute?.(); function syncHotRouteExport(liveRoute) { newRoute.options = liveRoute.options; newRoute.parentRoute = liveRoute.parentRoute; @@ -116,46 +72,6 @@ if (import.meta.hot) { newRoute._fullPath = liveRoute._fullPath; newRoute._to = liveRoute._to; } - function getStoreMatch(matchId) { - return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get(); - } - function getMatchList(matchId) { - const pendingMatches = router.stores.pendingMatches.get(); - if (pendingMatches.some(match => match.id === matchId)) { - return pendingMatches; - } - ; - const activeMatches = router.stores.matches.get(); - if (activeMatches.some(match => match.id === matchId)) { - return activeMatches; - } - ; - const cachedMatches = router.stores.cachedMatches.get(); - if (cachedMatches.some(match => match.id === matchId)) { - return cachedMatches; - } - ; - return []; - } - function getParentMatch(match) { - const matchList = getMatchList(match.id); - const matchIndex = matchList.findIndex(item => item.id === match.id); - if (matchIndex <= 0) { - return undefined; - } - ; - const parentMatch = matchList[matchIndex - 1]; - return getStoreMatch(parentMatch.id) || parentMatch; - } - function rebuildMatchContextWithoutBeforeLoad(match) { - const parentMatch = getParentMatch(match); - const getParentContext = router.getParentContext; - const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context; - return { - ...(parentContext ?? {}), - ...(match.__routeContext ?? {}) - }; - } }; const initialRouteId = Route.id ?? hotData['tsr-route-id']; if (initialRouteId) { diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute-lowercase-components@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute-lowercase-components@true.tsx index d3eae5cb36..cb5014cd35 100644 --- a/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute-lowercase-components@true.tsx +++ b/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute-lowercase-components@true.tsx @@ -59,13 +59,6 @@ if (import.meta.hot) { generatedRouteOptions[key] = oldRoute.options[key]; } }); - const removedKeys = new Set(); - Object.keys(oldRoute.options).forEach(key => { - if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) { - removedKeys.add(key); - delete oldRoute.options[key]; - } - }); const oldHasShellComponent = "shellComponent" in oldRoute.options; const newHasShellComponent = "shellComponent" in newRoute.options; const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent; @@ -84,48 +77,11 @@ if (import.meta.hot) { }; oldRoute.options = nextOptions; oldRoute.update(nextOptions); - oldRoute._componentsPromise = undefined; - oldRoute._lazyPromise = undefined; + router._replaceRouteChunk(oldRoute, newRoute.lazyFn); router.setRoutes(router.buildRouteTree()); syncHotRouteExport(oldRoute); router.resolvePathCache.clear(); - const filter = m => m.routeId === oldRoute.id; - const activeMatch = router.stores.matches.get().find(filter); - const pendingMatch = router.stores.pendingMatches.get().find(filter); - const cachedMatches = router.stores.cachedMatches.get().filter(filter); - if (activeMatch || pendingMatch || cachedMatches.length > 0) { - if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) { - const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean); - router.batch(() => { - for (const matchId of matchIds) { - const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId); - if (store) { - store.set(prev => { - const next = { - ...prev - }; - if (removedKeys.has("loader")) { - next.loaderData = undefined; - } - ; - if (removedKeys.has("beforeLoad")) { - next.__beforeLoadContext = undefined; - next.context = rebuildMatchContextWithoutBeforeLoad(next); - } - ; - return next; - }); - } - } - }); - } - ; - router.invalidate({ - filter, - sync: true - }); - } - ; + void router._refreshRoute?.(); function syncHotRouteExport(liveRoute) { newRoute.options = liveRoute.options; newRoute.parentRoute = liveRoute.parentRoute; @@ -134,46 +90,6 @@ if (import.meta.hot) { newRoute._fullPath = liveRoute._fullPath; newRoute._to = liveRoute._to; } - function getStoreMatch(matchId) { - return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get(); - } - function getMatchList(matchId) { - const pendingMatches = router.stores.pendingMatches.get(); - if (pendingMatches.some(match => match.id === matchId)) { - return pendingMatches; - } - ; - const activeMatches = router.stores.matches.get(); - if (activeMatches.some(match => match.id === matchId)) { - return activeMatches; - } - ; - const cachedMatches = router.stores.cachedMatches.get(); - if (cachedMatches.some(match => match.id === matchId)) { - return cachedMatches; - } - ; - return []; - } - function getParentMatch(match) { - const matchList = getMatchList(match.id); - const matchIndex = matchList.findIndex(item => item.id === match.id); - if (matchIndex <= 0) { - return undefined; - } - ; - const parentMatch = matchList[matchIndex - 1]; - return getStoreMatch(parentMatch.id) || parentMatch; - } - function rebuildMatchContextWithoutBeforeLoad(match) { - const parentMatch = getParentMatch(match); - const getParentContext = router.getParentContext; - const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context; - return { - ...(parentContext ?? {}), - ...(match.__routeContext ?? {}) - }; - } }; const initialRouteId = Route.id ?? hotData['tsr-route-id']; if (initialRouteId) { diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute@true.tsx index 9789370efc..7ba271c212 100644 --- a/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute@true.tsx +++ b/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute@true.tsx @@ -43,13 +43,6 @@ if (import.meta.hot) { generatedRouteOptions[key] = oldRoute.options[key]; } }); - const removedKeys = new Set(); - Object.keys(oldRoute.options).forEach(key => { - if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) { - removedKeys.add(key); - delete oldRoute.options[key]; - } - }); const oldHasShellComponent = "shellComponent" in oldRoute.options; const newHasShellComponent = "shellComponent" in newRoute.options; const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent; @@ -68,48 +61,11 @@ if (import.meta.hot) { }; oldRoute.options = nextOptions; oldRoute.update(nextOptions); - oldRoute._componentsPromise = undefined; - oldRoute._lazyPromise = undefined; + router._replaceRouteChunk(oldRoute, newRoute.lazyFn); router.setRoutes(router.buildRouteTree()); syncHotRouteExport(oldRoute); router.resolvePathCache.clear(); - const filter = m => m.routeId === oldRoute.id; - const activeMatch = router.stores.matches.get().find(filter); - const pendingMatch = router.stores.pendingMatches.get().find(filter); - const cachedMatches = router.stores.cachedMatches.get().filter(filter); - if (activeMatch || pendingMatch || cachedMatches.length > 0) { - if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) { - const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean); - router.batch(() => { - for (const matchId of matchIds) { - const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId); - if (store) { - store.set(prev => { - const next = { - ...prev - }; - if (removedKeys.has("loader")) { - next.loaderData = undefined; - } - ; - if (removedKeys.has("beforeLoad")) { - next.__beforeLoadContext = undefined; - next.context = rebuildMatchContextWithoutBeforeLoad(next); - } - ; - return next; - }); - } - } - }); - } - ; - router.invalidate({ - filter, - sync: true - }); - } - ; + void router._refreshRoute?.(); function syncHotRouteExport(liveRoute) { newRoute.options = liveRoute.options; newRoute.parentRoute = liveRoute.parentRoute; @@ -118,46 +74,6 @@ if (import.meta.hot) { newRoute._fullPath = liveRoute._fullPath; newRoute._to = liveRoute._to; } - function getStoreMatch(matchId) { - return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get(); - } - function getMatchList(matchId) { - const pendingMatches = router.stores.pendingMatches.get(); - if (pendingMatches.some(match => match.id === matchId)) { - return pendingMatches; - } - ; - const activeMatches = router.stores.matches.get(); - if (activeMatches.some(match => match.id === matchId)) { - return activeMatches; - } - ; - const cachedMatches = router.stores.cachedMatches.get(); - if (cachedMatches.some(match => match.id === matchId)) { - return cachedMatches; - } - ; - return []; - } - function getParentMatch(match) { - const matchList = getMatchList(match.id); - const matchIndex = matchList.findIndex(item => item.id === match.id); - if (matchIndex <= 0) { - return undefined; - } - ; - const parentMatch = matchList[matchIndex - 1]; - return getStoreMatch(parentMatch.id) || parentMatch; - } - function rebuildMatchContextWithoutBeforeLoad(match) { - const parentMatch = getParentMatch(match); - const getParentContext = router.getParentContext; - const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context; - return { - ...(parentContext ?? {}), - ...(match.__routeContext ?? {}) - }; - } }; const initialRouteId = Route.id ?? hotData['tsr-route-id']; if (initialRouteId) { diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRouteWithContext-type-args@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRouteWithContext-type-args@true.tsx index 6c7073a8f3..b66449aebf 100644 --- a/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRouteWithContext-type-args@true.tsx +++ b/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRouteWithContext-type-args@true.tsx @@ -46,13 +46,6 @@ if (import.meta.hot) { generatedRouteOptions[key] = oldRoute.options[key]; } }); - const removedKeys = new Set(); - Object.keys(oldRoute.options).forEach(key => { - if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) { - removedKeys.add(key); - delete oldRoute.options[key]; - } - }); const oldHasShellComponent = "shellComponent" in oldRoute.options; const newHasShellComponent = "shellComponent" in newRoute.options; const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent; @@ -71,48 +64,11 @@ if (import.meta.hot) { }; oldRoute.options = nextOptions; oldRoute.update(nextOptions); - oldRoute._componentsPromise = undefined; - oldRoute._lazyPromise = undefined; + router._replaceRouteChunk(oldRoute, newRoute.lazyFn); router.setRoutes(router.buildRouteTree()); syncHotRouteExport(oldRoute); router.resolvePathCache.clear(); - const filter = m => m.routeId === oldRoute.id; - const activeMatch = router.stores.matches.get().find(filter); - const pendingMatch = router.stores.pendingMatches.get().find(filter); - const cachedMatches = router.stores.cachedMatches.get().filter(filter); - if (activeMatch || pendingMatch || cachedMatches.length > 0) { - if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) { - const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean); - router.batch(() => { - for (const matchId of matchIds) { - const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId); - if (store) { - store.set(prev => { - const next = { - ...prev - }; - if (removedKeys.has("loader")) { - next.loaderData = undefined; - } - ; - if (removedKeys.has("beforeLoad")) { - next.__beforeLoadContext = undefined; - next.context = rebuildMatchContextWithoutBeforeLoad(next); - } - ; - return next; - }); - } - } - }); - } - ; - router.invalidate({ - filter, - sync: true - }); - } - ; + void router._refreshRoute?.(); function syncHotRouteExport(liveRoute) { newRoute.options = liveRoute.options; newRoute.parentRoute = liveRoute.parentRoute; @@ -121,46 +77,6 @@ if (import.meta.hot) { newRoute._fullPath = liveRoute._fullPath; newRoute._to = liveRoute._to; } - function getStoreMatch(matchId) { - return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get(); - } - function getMatchList(matchId) { - const pendingMatches = router.stores.pendingMatches.get(); - if (pendingMatches.some(match => match.id === matchId)) { - return pendingMatches; - } - ; - const activeMatches = router.stores.matches.get(); - if (activeMatches.some(match => match.id === matchId)) { - return activeMatches; - } - ; - const cachedMatches = router.stores.cachedMatches.get(); - if (cachedMatches.some(match => match.id === matchId)) { - return cachedMatches; - } - ; - return []; - } - function getParentMatch(match) { - const matchList = getMatchList(match.id); - const matchIndex = matchList.findIndex(item => item.id === match.id); - if (matchIndex <= 0) { - return undefined; - } - ; - const parentMatch = matchList[matchIndex - 1]; - return getStoreMatch(parentMatch.id) || parentMatch; - } - function rebuildMatchContextWithoutBeforeLoad(match) { - const parentMatch = getParentMatch(match); - const getParentContext = router.getParentContext; - const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context; - return { - ...(parentContext ?? {}), - ...(match.__routeContext ?? {}) - }; - } }; const initialRouteId = Route.id ?? hotData['tsr-route-id']; if (initialRouteId) { diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/explicit-undefined-component@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/explicit-undefined-component@true.tsx index bee793d372..fee0b2f2e6 100644 --- a/packages/router-plugin/tests/add-hmr/snapshots/react/explicit-undefined-component@true.tsx +++ b/packages/router-plugin/tests/add-hmr/snapshots/react/explicit-undefined-component@true.tsx @@ -40,13 +40,6 @@ if (import.meta.hot) { generatedRouteOptions[key] = oldRoute.options[key]; } }); - const removedKeys = new Set(); - Object.keys(oldRoute.options).forEach(key => { - if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) { - removedKeys.add(key); - delete oldRoute.options[key]; - } - }); const oldHasShellComponent = "shellComponent" in oldRoute.options; const newHasShellComponent = "shellComponent" in newRoute.options; const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent; @@ -65,48 +58,11 @@ if (import.meta.hot) { }; oldRoute.options = nextOptions; oldRoute.update(nextOptions); - oldRoute._componentsPromise = undefined; - oldRoute._lazyPromise = undefined; + router._replaceRouteChunk(oldRoute, newRoute.lazyFn); router.setRoutes(router.buildRouteTree()); syncHotRouteExport(oldRoute); router.resolvePathCache.clear(); - const filter = m => m.routeId === oldRoute.id; - const activeMatch = router.stores.matches.get().find(filter); - const pendingMatch = router.stores.pendingMatches.get().find(filter); - const cachedMatches = router.stores.cachedMatches.get().filter(filter); - if (activeMatch || pendingMatch || cachedMatches.length > 0) { - if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) { - const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean); - router.batch(() => { - for (const matchId of matchIds) { - const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId); - if (store) { - store.set(prev => { - const next = { - ...prev - }; - if (removedKeys.has("loader")) { - next.loaderData = undefined; - } - ; - if (removedKeys.has("beforeLoad")) { - next.__beforeLoadContext = undefined; - next.context = rebuildMatchContextWithoutBeforeLoad(next); - } - ; - return next; - }); - } - } - }); - } - ; - router.invalidate({ - filter, - sync: true - }); - } - ; + void router._refreshRoute?.(); function syncHotRouteExport(liveRoute) { newRoute.options = liveRoute.options; newRoute.parentRoute = liveRoute.parentRoute; @@ -115,46 +71,6 @@ if (import.meta.hot) { newRoute._fullPath = liveRoute._fullPath; newRoute._to = liveRoute._to; } - function getStoreMatch(matchId) { - return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get(); - } - function getMatchList(matchId) { - const pendingMatches = router.stores.pendingMatches.get(); - if (pendingMatches.some(match => match.id === matchId)) { - return pendingMatches; - } - ; - const activeMatches = router.stores.matches.get(); - if (activeMatches.some(match => match.id === matchId)) { - return activeMatches; - } - ; - const cachedMatches = router.stores.cachedMatches.get(); - if (cachedMatches.some(match => match.id === matchId)) { - return cachedMatches; - } - ; - return []; - } - function getParentMatch(match) { - const matchList = getMatchList(match.id); - const matchIndex = matchList.findIndex(item => item.id === match.id); - if (matchIndex <= 0) { - return undefined; - } - ; - const parentMatch = matchList[matchIndex - 1]; - return getStoreMatch(parentMatch.id) || parentMatch; - } - function rebuildMatchContextWithoutBeforeLoad(match) { - const parentMatch = getParentMatch(match); - const getParentContext = router.getParentContext; - const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context; - return { - ...(parentContext ?? {}), - ...(match.__routeContext ?? {}) - }; - } }; const initialRouteId = Route.id ?? hotData['tsr-route-id']; if (initialRouteId) { diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/function-declaration@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/function-declaration@true.tsx index 75b457b778..4a925828e2 100644 --- a/packages/router-plugin/tests/add-hmr/snapshots/react/function-declaration@true.tsx +++ b/packages/router-plugin/tests/add-hmr/snapshots/react/function-declaration@true.tsx @@ -52,13 +52,6 @@ if (import.meta.hot) { generatedRouteOptions[key] = oldRoute.options[key]; } }); - const removedKeys = new Set(); - Object.keys(oldRoute.options).forEach(key => { - if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) { - removedKeys.add(key); - delete oldRoute.options[key]; - } - }); const oldHasShellComponent = "shellComponent" in oldRoute.options; const newHasShellComponent = "shellComponent" in newRoute.options; const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent; @@ -77,48 +70,11 @@ if (import.meta.hot) { }; oldRoute.options = nextOptions; oldRoute.update(nextOptions); - oldRoute._componentsPromise = undefined; - oldRoute._lazyPromise = undefined; + router._replaceRouteChunk(oldRoute, newRoute.lazyFn); router.setRoutes(router.buildRouteTree()); syncHotRouteExport(oldRoute); router.resolvePathCache.clear(); - const filter = m => m.routeId === oldRoute.id; - const activeMatch = router.stores.matches.get().find(filter); - const pendingMatch = router.stores.pendingMatches.get().find(filter); - const cachedMatches = router.stores.cachedMatches.get().filter(filter); - if (activeMatch || pendingMatch || cachedMatches.length > 0) { - if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) { - const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean); - router.batch(() => { - for (const matchId of matchIds) { - const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId); - if (store) { - store.set(prev => { - const next = { - ...prev - }; - if (removedKeys.has("loader")) { - next.loaderData = undefined; - } - ; - if (removedKeys.has("beforeLoad")) { - next.__beforeLoadContext = undefined; - next.context = rebuildMatchContextWithoutBeforeLoad(next); - } - ; - return next; - }); - } - } - }); - } - ; - router.invalidate({ - filter, - sync: true - }); - } - ; + void router._refreshRoute?.(); function syncHotRouteExport(liveRoute) { newRoute.options = liveRoute.options; newRoute.parentRoute = liveRoute.parentRoute; @@ -127,46 +83,6 @@ if (import.meta.hot) { newRoute._fullPath = liveRoute._fullPath; newRoute._to = liveRoute._to; } - function getStoreMatch(matchId) { - return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get(); - } - function getMatchList(matchId) { - const pendingMatches = router.stores.pendingMatches.get(); - if (pendingMatches.some(match => match.id === matchId)) { - return pendingMatches; - } - ; - const activeMatches = router.stores.matches.get(); - if (activeMatches.some(match => match.id === matchId)) { - return activeMatches; - } - ; - const cachedMatches = router.stores.cachedMatches.get(); - if (cachedMatches.some(match => match.id === matchId)) { - return cachedMatches; - } - ; - return []; - } - function getParentMatch(match) { - const matchList = getMatchList(match.id); - const matchIndex = matchList.findIndex(item => item.id === match.id); - if (matchIndex <= 0) { - return undefined; - } - ; - const parentMatch = matchList[matchIndex - 1]; - return getStoreMatch(parentMatch.id) || parentMatch; - } - function rebuildMatchContextWithoutBeforeLoad(match) { - const parentMatch = getParentMatch(match); - const getParentContext = router.getParentContext; - const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context; - return { - ...(parentContext ?? {}), - ...(match.__routeContext ?? {}) - }; - } }; const initialRouteId = Route.id ?? hotData['tsr-route-id']; if (initialRouteId) { diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/multi-component@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/multi-component@true.tsx index 7f28b7b3d3..ca2a7ab2e2 100644 --- a/packages/router-plugin/tests/add-hmr/snapshots/react/multi-component@true.tsx +++ b/packages/router-plugin/tests/add-hmr/snapshots/react/multi-component@true.tsx @@ -62,13 +62,6 @@ if (import.meta.hot) { generatedRouteOptions[key] = oldRoute.options[key]; } }); - const removedKeys = new Set(); - Object.keys(oldRoute.options).forEach(key => { - if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) { - removedKeys.add(key); - delete oldRoute.options[key]; - } - }); const oldHasShellComponent = "shellComponent" in oldRoute.options; const newHasShellComponent = "shellComponent" in newRoute.options; const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent; @@ -87,48 +80,11 @@ if (import.meta.hot) { }; oldRoute.options = nextOptions; oldRoute.update(nextOptions); - oldRoute._componentsPromise = undefined; - oldRoute._lazyPromise = undefined; + router._replaceRouteChunk(oldRoute, newRoute.lazyFn); router.setRoutes(router.buildRouteTree()); syncHotRouteExport(oldRoute); router.resolvePathCache.clear(); - const filter = m => m.routeId === oldRoute.id; - const activeMatch = router.stores.matches.get().find(filter); - const pendingMatch = router.stores.pendingMatches.get().find(filter); - const cachedMatches = router.stores.cachedMatches.get().filter(filter); - if (activeMatch || pendingMatch || cachedMatches.length > 0) { - if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) { - const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean); - router.batch(() => { - for (const matchId of matchIds) { - const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId); - if (store) { - store.set(prev => { - const next = { - ...prev - }; - if (removedKeys.has("loader")) { - next.loaderData = undefined; - } - ; - if (removedKeys.has("beforeLoad")) { - next.__beforeLoadContext = undefined; - next.context = rebuildMatchContextWithoutBeforeLoad(next); - } - ; - return next; - }); - } - } - }); - } - ; - router.invalidate({ - filter, - sync: true - }); - } - ; + void router._refreshRoute?.(); function syncHotRouteExport(liveRoute) { newRoute.options = liveRoute.options; newRoute.parentRoute = liveRoute.parentRoute; @@ -137,46 +93,6 @@ if (import.meta.hot) { newRoute._fullPath = liveRoute._fullPath; newRoute._to = liveRoute._to; } - function getStoreMatch(matchId) { - return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get(); - } - function getMatchList(matchId) { - const pendingMatches = router.stores.pendingMatches.get(); - if (pendingMatches.some(match => match.id === matchId)) { - return pendingMatches; - } - ; - const activeMatches = router.stores.matches.get(); - if (activeMatches.some(match => match.id === matchId)) { - return activeMatches; - } - ; - const cachedMatches = router.stores.cachedMatches.get(); - if (cachedMatches.some(match => match.id === matchId)) { - return cachedMatches; - } - ; - return []; - } - function getParentMatch(match) { - const matchList = getMatchList(match.id); - const matchIndex = matchList.findIndex(item => item.id === match.id); - if (matchIndex <= 0) { - return undefined; - } - ; - const parentMatch = matchList[matchIndex - 1]; - return getStoreMatch(parentMatch.id) || parentMatch; - } - function rebuildMatchContextWithoutBeforeLoad(match) { - const parentMatch = getParentMatch(match); - const getParentContext = router.getParentContext; - const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context; - return { - ...(parentContext ?? {}), - ...(match.__routeContext ?? {}) - }; - } }; const initialRouteId = Route.id ?? hotData['tsr-route-id']; if (initialRouteId) { diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/string-literal-keys@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/string-literal-keys@true.tsx index 6bee992948..b6f67e67b1 100644 --- a/packages/router-plugin/tests/add-hmr/snapshots/react/string-literal-keys@true.tsx +++ b/packages/router-plugin/tests/add-hmr/snapshots/react/string-literal-keys@true.tsx @@ -62,13 +62,6 @@ if (import.meta.hot) { generatedRouteOptions[key] = oldRoute.options[key]; } }); - const removedKeys = new Set(); - Object.keys(oldRoute.options).forEach(key => { - if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) { - removedKeys.add(key); - delete oldRoute.options[key]; - } - }); const oldHasShellComponent = "shellComponent" in oldRoute.options; const newHasShellComponent = "shellComponent" in newRoute.options; const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent; @@ -87,48 +80,11 @@ if (import.meta.hot) { }; oldRoute.options = nextOptions; oldRoute.update(nextOptions); - oldRoute._componentsPromise = undefined; - oldRoute._lazyPromise = undefined; + router._replaceRouteChunk(oldRoute, newRoute.lazyFn); router.setRoutes(router.buildRouteTree()); syncHotRouteExport(oldRoute); router.resolvePathCache.clear(); - const filter = m => m.routeId === oldRoute.id; - const activeMatch = router.stores.matches.get().find(filter); - const pendingMatch = router.stores.pendingMatches.get().find(filter); - const cachedMatches = router.stores.cachedMatches.get().filter(filter); - if (activeMatch || pendingMatch || cachedMatches.length > 0) { - if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) { - const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean); - router.batch(() => { - for (const matchId of matchIds) { - const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId); - if (store) { - store.set(prev => { - const next = { - ...prev - }; - if (removedKeys.has("loader")) { - next.loaderData = undefined; - } - ; - if (removedKeys.has("beforeLoad")) { - next.__beforeLoadContext = undefined; - next.context = rebuildMatchContextWithoutBeforeLoad(next); - } - ; - return next; - }); - } - } - }); - } - ; - router.invalidate({ - filter, - sync: true - }); - } - ; + void router._refreshRoute?.(); function syncHotRouteExport(liveRoute) { newRoute.options = liveRoute.options; newRoute.parentRoute = liveRoute.parentRoute; @@ -137,46 +93,6 @@ if (import.meta.hot) { newRoute._fullPath = liveRoute._fullPath; newRoute._to = liveRoute._to; } - function getStoreMatch(matchId) { - return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get(); - } - function getMatchList(matchId) { - const pendingMatches = router.stores.pendingMatches.get(); - if (pendingMatches.some(match => match.id === matchId)) { - return pendingMatches; - } - ; - const activeMatches = router.stores.matches.get(); - if (activeMatches.some(match => match.id === matchId)) { - return activeMatches; - } - ; - const cachedMatches = router.stores.cachedMatches.get(); - if (cachedMatches.some(match => match.id === matchId)) { - return cachedMatches; - } - ; - return []; - } - function getParentMatch(match) { - const matchList = getMatchList(match.id); - const matchIndex = matchList.findIndex(item => item.id === match.id); - if (matchIndex <= 0) { - return undefined; - } - ; - const parentMatch = matchList[matchIndex - 1]; - return getStoreMatch(parentMatch.id) || parentMatch; - } - function rebuildMatchContextWithoutBeforeLoad(match) { - const parentMatch = getParentMatch(match); - const getParentContext = router.getParentContext; - const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context; - return { - ...(parentContext ?? {}), - ...(match.__routeContext ?? {}) - }; - } }; const initialRouteId = Route.id ?? hotData['tsr-route-id']; if (initialRouteId) { diff --git a/packages/router-plugin/tests/add-hmr/snapshots/solid/arrow-function@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/solid/arrow-function@true.tsx index 2d1be7e32b..754da5c902 100644 --- a/packages/router-plugin/tests/add-hmr/snapshots/solid/arrow-function@true.tsx +++ b/packages/router-plugin/tests/add-hmr/snapshots/solid/arrow-function@true.tsx @@ -23,13 +23,6 @@ if (import.meta.hot) { generatedRouteOptions[key] = oldRoute.options[key]; } }); - const removedKeys = new Set(); - Object.keys(oldRoute.options).forEach(key => { - if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) { - removedKeys.add(key); - delete oldRoute.options[key]; - } - }); const oldHasShellComponent = "shellComponent" in oldRoute.options; const newHasShellComponent = "shellComponent" in newRoute.options; const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent; @@ -48,48 +41,11 @@ if (import.meta.hot) { }; oldRoute.options = nextOptions; oldRoute.update(nextOptions); - oldRoute._componentsPromise = undefined; - oldRoute._lazyPromise = undefined; + router._replaceRouteChunk(oldRoute, newRoute.lazyFn); router.setRoutes(router.buildRouteTree()); syncHotRouteExport(oldRoute); router.resolvePathCache.clear(); - const filter = m => m.routeId === oldRoute.id; - const activeMatch = router.stores.matches.get().find(filter); - const pendingMatch = router.stores.pendingMatches.get().find(filter); - const cachedMatches = router.stores.cachedMatches.get().filter(filter); - if (activeMatch || pendingMatch || cachedMatches.length > 0) { - if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) { - const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean); - router.batch(() => { - for (const matchId of matchIds) { - const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId); - if (store) { - store.set(prev => { - const next = { - ...prev - }; - if (removedKeys.has("loader")) { - next.loaderData = undefined; - } - ; - if (removedKeys.has("beforeLoad")) { - next.__beforeLoadContext = undefined; - next.context = rebuildMatchContextWithoutBeforeLoad(next); - } - ; - return next; - }); - } - } - }); - } - ; - router.invalidate({ - filter, - sync: true - }); - } - ; + void router._refreshRoute?.(); function syncHotRouteExport(liveRoute) { newRoute.options = liveRoute.options; newRoute.parentRoute = liveRoute.parentRoute; @@ -98,46 +54,6 @@ if (import.meta.hot) { newRoute._fullPath = liveRoute._fullPath; newRoute._to = liveRoute._to; } - function getStoreMatch(matchId) { - return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get(); - } - function getMatchList(matchId) { - const pendingMatches = router.stores.pendingMatches.get(); - if (pendingMatches.some(match => match.id === matchId)) { - return pendingMatches; - } - ; - const activeMatches = router.stores.matches.get(); - if (activeMatches.some(match => match.id === matchId)) { - return activeMatches; - } - ; - const cachedMatches = router.stores.cachedMatches.get(); - if (cachedMatches.some(match => match.id === matchId)) { - return cachedMatches; - } - ; - return []; - } - function getParentMatch(match) { - const matchList = getMatchList(match.id); - const matchIndex = matchList.findIndex(item => item.id === match.id); - if (matchIndex <= 0) { - return undefined; - } - ; - const parentMatch = matchList[matchIndex - 1]; - return getStoreMatch(parentMatch.id) || parentMatch; - } - function rebuildMatchContextWithoutBeforeLoad(match) { - const parentMatch = getParentMatch(match); - const getParentContext = router.getParentContext; - const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context; - return { - ...(parentContext ?? {}), - ...(match.__routeContext ?? {}) - }; - } }; const initialRouteId = Route.id ?? hotData['tsr-route-id']; if (initialRouteId) { diff --git a/packages/router-plugin/tests/handle-route-update.test.ts b/packages/router-plugin/tests/handle-route-update.test.ts index d6d1b4c766..db371746af 100644 --- a/packages/router-plugin/tests/handle-route-update.test.ts +++ b/packages/router-plugin/tests/handle-route-update.test.ts @@ -8,8 +8,15 @@ import { createNonReactiveReadonlyStore, trimPathRight, } from '@tanstack/router-core' -import { describe, expect, it, vi } from 'vitest' +import { describe, expect, it, onTestFinished, vi } from 'vitest' +import { Fragment, act, createElement } from 'react' +import { createPortal } from 'react-dom' +import { createRoot } from 'react-dom/client' import { createMemoryHistory } from '../../history/src' +import { HeadContent } from '../../react-router/src/HeadContent' +import { RouterContextProvider } from '../../react-router/src/RouterProvider' +import { Scripts } from '../../react-router/src/Scripts' +import { Router as ReactRouter } from '../../react-router/src/router' import { getHandleRouteUpdateCode } from '../src/core/hmr' import type { AnyRoute, GetStoreConfig } from '@tanstack/router-core' @@ -89,6 +96,18 @@ function createClientTestRouter( ) } +function createReactClientTestRouter( + routeTree: TRouteTree, + initialEntry: string, +) { + return new ReactRouter({ + routeTree: asAnyRoute(routeTree), + history: createMemoryHistory({ initialEntries: [initialEntry] }), + isServer: false, + origin: 'http://localhost', + }) +} + function withWindowRouter(router: RouterCore) { const testWindow = globalThis.window as any const hadOwnRouter = Object.prototype.hasOwnProperty.call( @@ -271,6 +290,52 @@ describe('handleRouteUpdate', () => { expect((newRoute as any).options).toBe((itemRoute as any).options) }) + it('clears lazy ownership and delegates rematerialization to the router', () => { + const rootRoute = new BaseRootRoute({}) + const itemRoute = new BaseRoute({ + getParentRoute: () => asAnyRoute(rootRoute), + path: '/items/$itemId', + }) + const router = createTestRouter( + rootRoute.addChildren([asAnyRoute(itemRoute)]), + ) + const lazyOwner = Promise.resolve() + const refreshRoute = vi.fn(async () => {}) + ;(itemRoute as any)._lazy = lazyOwner + ;(router as any)._refreshRoute = refreshRoute + + expect((itemRoute as any)._lazy).toBe(lazyOwner) + + runHandleRouteUpdate(router, itemRoute.id, new BaseRoute({} as any)) + + expect((itemRoute as any)._lazy).toBeUndefined() + expect(refreshRoute).toHaveBeenCalledTimes(1) + expect(refreshRoute).toHaveBeenCalledWith() + }) + + it('uses a fresh source lazy importer without erasing an omitted one', () => { + const oldImporter = vi.fn(async () => ({ options: {} }) as any) + const newImporter = vi.fn(async () => ({ options: {} }) as any) + const rootRoute = new BaseRootRoute({}) + const itemRoute = new BaseRoute({ + getParentRoute: () => asAnyRoute(rootRoute), + path: '/items/$itemId', + }).lazy(oldImporter) + const router = createTestRouter( + rootRoute.addChildren([asAnyRoute(itemRoute)]), + ) + + runHandleRouteUpdate( + router, + itemRoute.id, + new BaseRoute({} as any).lazy(newImporter), + ) + expect(itemRoute.lazyFn).toBe(newImporter) + + runHandleRouteUpdate(router, itemRoute.id, new BaseRoute({} as any)) + expect(itemRoute.lazyFn).toBe(newImporter) + }) + it('removes stale loader data when a hot route removes its loader', async () => { const rootRoute = new BaseRootRoute({}) const hotRoute = new BaseRoute({ @@ -305,6 +370,167 @@ describe('handleRouteUpdate', () => { } }) + it('keeps hot loader data when an older refresh settles later', async () => { + let resolveStaleRefresh!: (value: string) => void + const staleRefresh = new Promise((resolve) => { + resolveStaleRefresh = resolve + }) + const oldLoader = vi + .fn<() => string | Promise>() + .mockReturnValueOnce('initial') + .mockReturnValueOnce(staleRefresh) + const newLoader = vi.fn(() => 'hot') + const rootRoute = new BaseRootRoute({ loader: oldLoader }) + const router = createClientTestRouter(rootRoute, '/') + + await router.load() + expect(router.state.matches[0]?.loaderData).toBe('initial') + + const oldRefresh = router.invalidate() + await vi.waitFor(() => expect(oldLoader).toHaveBeenCalledTimes(2)) + + runHandleRouteUpdate( + router, + rootRoute.id, + new BaseRootRoute({ loader: newLoader }), + ) + + await vi.waitFor(() => { + expect(router.state.matches[0]?.loaderData).toBe('hot') + }) + + resolveStaleRefresh('stale') + await oldRefresh + + expect(newLoader).toHaveBeenCalledTimes(1) + expect(router.state.matches[0]!.routeId).toBe(rootRoute.id) + expect(router.state.matches[0]!.loaderData).toBe('hot') + }) + + it('does not cache an obsolete inactive preload that settles after a hot update', async () => { + let resolveOldLoader!: (value: string) => void + const oldLoaderResult = new Promise((resolve) => { + resolveOldLoader = resolve + }) + const oldLoader = vi.fn(() => oldLoaderResult) + const newLoader = vi.fn(() => 'hot') + const rootRoute = new BaseRootRoute({}) + const hotRoute = new BaseRoute({ + getParentRoute: () => asAnyRoute(rootRoute), + path: '/hot', + loader: oldLoader, + preloadStaleTime: Infinity, + }) + const router = createClientTestRouter( + rootRoute.addChildren([asAnyRoute(hotRoute)]), + '/', + ) + + await router.load() + const preload = router.preloadRoute({ to: '/hot' }) + await vi.waitFor(() => expect(oldLoader).toHaveBeenCalledOnce()) + + runHandleRouteUpdate( + router, + hotRoute.id, + new BaseRoute({ loader: newLoader } as any), + ) + + resolveOldLoader('obsolete') + await preload + await router.navigate({ to: '/hot' }) + + expect(newLoader).toHaveBeenCalledOnce() + expect(router.state.matches.at(-1)?.loaderData).toBe('hot') + }) + + it('observes hot parser and route-context changes on the active route', async () => { + const rootRoute = new BaseRootRoute({}) + const itemRoute = new BaseRoute({ + getParentRoute: () => asAnyRoute(rootRoute), + path: '/items/$itemId', + params: { + parse: ({ itemId }: { itemId: string }) => ({ + itemId, + parser: 'old', + }), + }, + context: () => ({ source: 'old' }), + }) + const router = createClientTestRouter( + rootRoute.addChildren([asAnyRoute(itemRoute)]), + '/items/abc', + ) + + await router.load() + expect(router.state.matches[1]!.routeId).toBe(itemRoute.id) + expect(router.state.matches[1]!.params).toEqual({ + itemId: 'abc', + parser: 'old', + }) + expect(router.state.matches[1]!.context).toMatchObject({ source: 'old' }) + + runHandleRouteUpdate( + router, + itemRoute.id, + new BaseRoute({ + params: { + parse: ({ itemId }: { itemId: string }) => ({ + itemId, + parser: 'new', + }), + }, + context: () => ({ source: 'new' }), + } as any), + ) + + await vi.waitFor(() => { + expect(router.state.matches[1]!.routeId).toBe(itemRoute.id) + expect(router.state.matches[1]!.params).toEqual({ + itemId: 'abc', + parser: 'new', + }) + expect(router.state.matches[1]!.context).toMatchObject({ source: 'new' }) + }) + }) + + it('rematerializes descendants when a parent route definition changes', async () => { + const rootRoute = new BaseRootRoute({}) + const parentRoute = new BaseRoute({ + getParentRoute: () => asAnyRoute(rootRoute), + path: '/parent', + context: () => ({ source: 'old' }), + }) + const childRoute = new BaseRoute({ + getParentRoute: () => asAnyRoute(parentRoute), + path: '/child', + context: ({ context }) => ({ inheritedSource: context.source }), + }) + const router = createClientTestRouter( + rootRoute.addChildren([ + parentRoute.addChildren([asAnyRoute(childRoute)]), + ]), + '/parent/child', + ) + + await router.load() + expect(router.state.matches[2]?.context).toMatchObject({ + inheritedSource: 'old', + }) + + runHandleRouteUpdate( + router, + parentRoute.id, + new BaseRoute({ context: () => ({ source: 'new' }) } as any), + ) + + await vi.waitFor(() => { + expect(router.state.matches[2]?.context).toMatchObject({ + inheritedSource: 'new', + }) + }) + }) + it('does not run removed loader or beforeLoad callbacks on a later visit', async () => { const beforeLoad = vi.fn(() => ({ hotBeforeLoad: true })) const loader = vi.fn(() => 'hot loader data') @@ -344,40 +570,85 @@ describe('handleRouteUpdate', () => { expect(match!.context).not.toHaveProperty('hotBeforeLoad') }) - it('does not cache an obsolete inactive preload that settles after a hot update', async () => { - let resolveOldLoader!: (value: string) => void - const oldLoaderResult = new Promise((resolve) => { - resolveOldLoader = resolve - }) - const oldLoader = vi.fn(() => oldLoaderResult) - const newLoader = vi.fn(() => 'hot') + it('removes rendered HeadContent and Scripts output after a hot update', async () => { const rootRoute = new BaseRootRoute({}) const hotRoute = new BaseRoute({ getParentRoute: () => asAnyRoute(rootRoute), path: '/hot', - loader: oldLoader, - preloadStaleTime: Infinity, + head: () => ({ + meta: [{ title: 'hot title' }], + scripts: [{ src: '/hot-head.js' }], + }), + scripts: () => [{ src: '/hot-body.js' }], }) - const router = createClientTestRouter( + const router = createReactClientTestRouter( rootRoute.addChildren([asAnyRoute(hotRoute)]), - '/', + '/hot', ) await router.load() - const preload = router.preloadRoute({ to: '/hot' }) - await vi.waitFor(() => expect(oldLoader).toHaveBeenCalledOnce()) + expect(router.state.matches[1]!).toMatchObject({ + routeId: hotRoute.id, + meta: [{ title: 'hot title' }], + headScripts: [{ src: '/hot-head.js' }], + scripts: [{ src: '/hot-body.js' }], + }) - runHandleRouteUpdate( - router, - hotRoute.id, - new BaseRoute({ loader: newLoader } as any), - ) + const container = document.createElement('div') + document.body.appendChild(container) + const reactRoot = createRoot(container) + const previousActEnvironment = (globalThis as any).IS_REACT_ACT_ENVIRONMENT + ;(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true + onTestFinished(() => { + if (previousActEnvironment === undefined) { + delete (globalThis as any).IS_REACT_ACT_ENVIRONMENT + } else { + ;(globalThis as any).IS_REACT_ACT_ENVIRONMENT = previousActEnvironment + } + }) + onTestFinished(() => { + container.remove() + }) + onTestFinished(async () => { + await act(async () => { + reactRoot.unmount() + }) + }) - resolveOldLoader('obsolete') - await preload - await router.navigate({ to: '/hot' }) + await act(async () => { + reactRoot.render( + createElement(RouterContextProvider, { + router, + children: createElement( + Fragment, + null, + createPortal(createElement(HeadContent), document.head), + createElement(Scripts), + ), + }), + ) + }) - expect(newLoader).toHaveBeenCalledOnce() - expect(router.state.matches.at(-1)?.loaderData).toBe('hot') + expect(document.head.querySelector('title')?.textContent).toBe('hot title') + expect( + document.head.querySelector('script[src="/hot-head.js"]'), + ).not.toBeNull() + expect( + document.head.querySelector('script[src="/hot-body.js"]'), + ).not.toBeNull() + + await act(async () => { + runHandleRouteUpdate(router, hotRoute.id, new BaseRoute({} as any)) + await vi.waitFor(() => { + expect(router.state.matches[1]!.routeId).toBe(hotRoute.id) + expect(router.state.matches[1]!.meta).toBeUndefined() + expect(router.state.matches[1]!.headScripts).toBeUndefined() + expect(router.state.matches[1]!.scripts).toBeUndefined() + }) + }) + + expect(document.head.querySelector('title')).toBeNull() + expect(document.head.querySelector('script[src="/hot-head.js"]')).toBeNull() + expect(document.head.querySelector('script[src="/hot-body.js"]')).toBeNull() }) }) diff --git a/packages/solid-router/src/CatchBoundary.tsx b/packages/solid-router/src/CatchBoundary.tsx index 10d5e83ca6..12e4446581 100644 --- a/packages/solid-router/src/CatchBoundary.tsx +++ b/packages/solid-router/src/CatchBoundary.tsx @@ -4,7 +4,7 @@ import type { ErrorRouteComponent } from './route' export function CatchBoundary( props: { - getResetKey: () => number | string + getResetKey: () => unknown children: Solid.JSX.Element errorComponent?: ErrorRouteComponent onCatch?: (error: Error) => void @@ -16,7 +16,7 @@ export function CatchBoundary( props.onCatch?.(error) Solid.createEffect( - Solid.on([props.getResetKey], () => reset(), { defer: true }), + Solid.on(props.getResetKey, () => reset(), { defer: true }), ) return ( diff --git a/packages/solid-router/src/ClientOnly.tsx b/packages/solid-router/src/ClientOnly.tsx index a7c4b71819..621d41974a 100644 --- a/packages/solid-router/src/ClientOnly.tsx +++ b/packages/solid-router/src/ClientOnly.tsx @@ -59,7 +59,9 @@ export function ClientOnly(props: ClientOnlyProps) { let globalHydrated = false export function useHydrated(): Solid.Accessor { - const [hydrated, setHydrated] = Solid.createSignal(globalHydrated) + const [hydrated, setHydrated] = Solid.createSignal( + globalHydrated && !Solid.sharedConfig.context, + ) Solid.onMount(() => { globalHydrated = true diff --git a/packages/solid-router/src/Match.tsx b/packages/solid-router/src/Match.tsx index 205e778689..8a400a8580 100644 --- a/packages/solid-router/src/Match.tsx +++ b/packages/solid-router/src/Match.tsx @@ -1,12 +1,5 @@ import * as Solid from 'solid-js' -import { - createControlledPromise, - getLocationChangeInfo, - invariant, - isNotFound, - isRedirect, - rootRouteId, -} from '@tanstack/router-core' +import { rootRouteId } from '@tanstack/router-core' import { isServer } from '@tanstack/router-core/isServer' import { Dynamic } from 'solid-js/web' import { CatchBoundary, ErrorComponent } from './CatchBoundary' @@ -16,436 +9,221 @@ import { nearestMatchContext } from './matchContext' import { SafeFragment } from './SafeFragment' import { renderRouteNotFound } from './renderRouteNotFound' import { ScrollRestoration } from './scroll-restoration' -import type { AnyRoute, RootRouteOptions } from '@tanstack/router-core' +import { ClientOnly } from './ClientOnly' +import type { + AnyRoute, + AnyRouter, + RootRouteOptions, +} from '@tanstack/router-core' -export const Match = (props: { matchId: string }) => { +// Keep the client constant undefined so the server-only script can be dropped. +const renderScrollRestoration = + isServer === false + ? undefined + : (router: AnyRouter, route: AnyRoute) => + (isServer ?? router.isServer) && + route.parentRoute?.id === rootRouteId && + router.options.scrollRestoration ? ( + + ) : null + +export const Match = (props: { routeId: string }) => { const router = useRouter() - const match = Solid.createMemo(() => { - const id = props.matchId - if (!id) return undefined - return router.stores.matchStores.get(id)?.get() - }) - - const rawMatchState = Solid.createMemo(() => { - const currentMatch = match() - if (!currentMatch) { - return null - } - - const routeId = currentMatch.routeId as string - const parentRouteId = (router.routesById[routeId] as AnyRoute)?.parentRoute - ?.id - - return { - matchId: currentMatch.id, - routeId, - ssr: currentMatch.ssr, - _displayPending: currentMatch._displayPending, - parentRouteId: parentRouteId as string | undefined, - } - }) + const currentMatch = Solid.createMemo( + () => router.stores.byRoute.get(props.routeId)!.get()!, + ) - const hasPendingMatch = Solid.createMemo(() => { - const currentRouteId = rawMatchState()?.routeId - return currentRouteId - ? Boolean(router.stores.pendingRouteIds.get()[currentRouteId]) - : false - }) const nearestMatch = { - matchId: () => rawMatchState()?.matchId, - routeId: () => rawMatchState()?.routeId, - match, - hasPending: hasPendingMatch, + routeId: () => props.routeId, + match: currentMatch, } - return ( - - {(currentMatchState) => { - const route: () => AnyRoute = () => - router.routesById[currentMatchState().routeId] - - const resolvePendingComponent = () => - route().options.pendingComponent ?? - router.options.defaultPendingComponent - - const routeErrorComponent = () => - route().options.errorComponent ?? router.options.defaultErrorComponent - - const routeOnCatch = () => - route().options.onCatch ?? router.options.defaultOnCatch - - const routeNotFoundComponent = () => - route().isRoot - ? // If it's the root route, use the globalNotFound option, with fallback to the notFoundRoute's component - (route().options.notFoundComponent ?? - router.options.notFoundRoute?.options.component) - : route().options.notFoundComponent + const route: AnyRoute = router.routesById[props.routeId] - const resolvedNoSsr = - currentMatchState().ssr === false || - currentMatchState().ssr === 'data-only' + // Lazy route option mutations become observable with the next client match + // publication. Server stores are non-reactive and options load before render. + const routeOptions = + (isServer ?? router.isServer) + ? () => route.options + : () => { + currentMatch() + return route.options + } - const shouldSkipSuspenseFallback = - (isServer ?? router.isServer) - ? resolvedNoSsr - : currentMatchState().ssr === 'data-only' + const resolvePendingComponent = () => + routeOptions().pendingComponent ?? router.options.defaultPendingComponent - const ResolvedSuspenseBoundary = () => Solid.Suspense + const routeErrorComponent = () => + routeOptions().errorComponent ?? router.options.defaultErrorComponent - const ResolvedCatchBoundary = () => - routeErrorComponent() ? CatchBoundary : SafeFragment + const routeNotFoundComponent = () => + route.isRoot + ? // If it's the root route, use the _notFound option, with fallback to the notFoundRoute's component + (routeOptions().notFoundComponent ?? + router.options.notFoundRoute?.options.component) + : routeOptions().notFoundComponent - const ResolvedNotFoundBoundary = () => - routeNotFoundComponent() ? CatchNotFound : SafeFragment + const resolvedNoSsr = () => + currentMatch().ssr === false || currentMatch().ssr === 'data-only' - const ShellComponent = route().isRoot - ? ((route().options as RootRouteOptions).shellComponent ?? - SafeFragment) - : SafeFragment + const shouldSkipSuspenseFallback = () => + (isServer ?? router.isServer) + ? resolvedNoSsr() + : currentMatch().ssr === 'data-only' - return ( - - - - ) - } - > - router.stores.loadedAt.get()} - errorComponent={routeErrorComponent() || ErrorComponent} - onCatch={(error: Error) => { - // Forward not found errors (we don't want to show the error component for these) - const notFoundError = getNotFound(error) - if (notFoundError) { - notFoundError.routeId ??= currentMatchState() - .routeId as any - throw notFoundError - } - if (process.env.NODE_ENV !== 'production') { - console.warn( - `Warning: Error in route match: ${currentMatchState().routeId}`, - ) - } - routeOnCatch()?.(error) - }} - > - { - const notFoundError = getNotFound(error) ?? error + const ShellComponent = route.isRoot + ? ((route.options as RootRouteOptions).shellComponent ?? SafeFragment) + : SafeFragment - notFoundError.routeId ??= currentMatchState() - .routeId as any - - // If the current not found handler doesn't exist or it has a - // route ID which doesn't match the current route, rethrow the error - if ( - !routeNotFoundComponent() || - (notFoundError.routeId && - notFoundError.routeId !== - currentMatchState().routeId) || - (!notFoundError.routeId && !route().isRoot) - ) - throw notFoundError - - return ( - - ) - }} - > - - - - } - > - - - - - - - - - - - - - {currentMatchState().parentRouteId === rootRouteId ? ( - <> - - {router.options.scrollRestoration && - (isServer ?? router.isServer) ? ( - - ) : null} - - ) : null} - - ) - }} + const MatchContent = () => ( + } + > + ) -} - -// On Rendered can't happen above the root layout because it needs to run -// after the app has committed below the root layout. Keeping it here lets us -// fire onRendered even after a hydration mismatch above the root layout -// (like bad head/link tags, which is common). -function OnRendered() { - const router = useRouter() - - const location = Solid.createMemo( - () => router.stores.resolvedLocation.get()?.state.__TSR_key, - ) - Solid.createEffect( - Solid.on([location], () => { - router.emit({ - type: 'onRendered', - ...getLocationChangeInfo( - router.stores.location.get(), - router.stores.resolvedLocation.get(), - ), - }) - }), - ) - return null -} - -export const MatchInner = (): any => { - const router = useRouter() - const match = Solid.useContext(nearestMatchContext).match - - const rawMatchState = Solid.createMemo(() => { - const currentMatch = match() - if (!currentMatch) { - return null - } - - const routeId = currentMatch.routeId as string - - const remountFn = - (router.routesById[routeId] as AnyRoute).options.remountDeps ?? - router.options.defaultRemountDeps - const remountDeps = remountFn?.({ - routeId, - loaderDeps: currentMatch.loaderDeps, - params: currentMatch._strictParams, - search: currentMatch._strictSearch, - }) - const key = remountDeps ? JSON.stringify(remountDeps) : undefined - - return { - key, - routeId, - match: { - id: currentMatch.id, - status: currentMatch.status, - error: currentMatch.error, - _forcePending: currentMatch._forcePending ?? false, - _displayPending: currentMatch._displayPending ?? false, - }, - } - }) return ( - - {(currentMatchState) => { - const route = () => router.routesById[currentMatchState().routeId]! - - const currentMatch = () => currentMatchState().match - - const componentKey = () => - currentMatchState().key ?? currentMatchState().match.id - - const out = () => { - const Comp = - route().options.component ?? router.options.defaultComponent - if (Comp) { - return + + + + ) } - return - } - - const getLoadPromise = ( - matchId: string, - fallbackMatch: - | { - _nonReactive: { - loadPromise?: Promise - } + > + { + // Forward not found errors (we don't want to show the error component for these) + const notFoundError = getNotFound(error) + if (notFoundError) { + notFoundError.routeId ??= currentMatch().routeId + throw notFoundError } - | undefined, - ) => { - return ( - router.getMatch(matchId)?._nonReactive.loadPromise ?? - fallbackMatch?._nonReactive.loadPromise - ) - } - - const keyedOut = () => ( - - {(_key) => out()} - - ) - - return ( - - - {(_) => { - const [displayPendingResult] = Solid.createResource( - () => - router.getMatch(currentMatch().id)?._nonReactive - .displayPendingPromise, + if (process.env.NODE_ENV !== 'production') { + console.warn( + `Warning: Error in route match: ${currentMatch().routeId}`, ) + } + ;(route.options.onCatch ?? router.options.defaultOnCatch)?.(error) + }} + > + { + const notFoundError = getNotFound(error) ?? error - return <>{displayPendingResult()} - }} - - - {(_) => { - const [minPendingResult] = Solid.createResource( - () => - router.getMatch(currentMatch().id)?._nonReactive - .minPendingPromise, - ) - - return <>{minPendingResult()} - }} - - - {(_) => { - const pendingMinMs = - route().options.pendingMinMs ?? - router.options.defaultPendingMinMs - - if (pendingMinMs) { - const routerMatch = router.getMatch(currentMatch().id) - if ( - routerMatch && - !routerMatch._nonReactive.minPendingPromise - ) { - // Create a promise that will resolve after the minPendingMs - if (!(isServer ?? router.isServer)) { - const minPendingPromise = createControlledPromise() - - routerMatch._nonReactive.minPendingPromise = - minPendingPromise - - setTimeout(() => { - minPendingPromise.resolve() - // We've handled the minPendingPromise, so we can delete it - routerMatch._nonReactive.minPendingPromise = undefined - }, pendingMinMs) - } - } - } - - const [loaderResult] = Solid.createResource(async () => { - await Promise.resolve() - return router.getMatch(currentMatch().id)?._nonReactive - .loadPromise - }) - - const FallbackComponent = - route().options.pendingComponent ?? - router.options.defaultPendingComponent - - return ( - <> - {FallbackComponent && pendingMinMs > 0 ? ( - - ) : null} - {loaderResult()} - - ) - }} - - - {(_) => { - if (!isNotFound(currentMatch().error)) { - if (process.env.NODE_ENV !== 'production') { - throw new Error( - 'Invariant failed: Expected a notFound error', - ) - } + notFoundError.routeId ??= currentMatch().routeId - invariant() + if (notFoundError.routeId !== currentMatch().routeId) { + throw notFoundError } - // Use Show with keyed to ensure re-render when routeId changes return ( - - {(_routeId) => - renderRouteNotFound(router, route(), currentMatch().error) - } - + ) }} - - - {(_) => { - const matchId = currentMatch().id - const routerMatch = router.getMatch(matchId) - - if (!isRedirect(currentMatch().error)) { - if (process.env.NODE_ENV !== 'production') { - throw new Error( - 'Invariant failed: Expected a redirect error', - ) - } + > + + + } + > + + + + + + + + + + + + + {renderScrollRestoration?.(router, route)} + + ) +} - invariant() - } +export const MatchInner = (): any => { + const router = useRouter() + const nearestMatch = Solid.useContext(nearestMatchContext) + const match = nearestMatch.match + const routeId = nearestMatch.routeId + const route = router.routesById[routeId()!]! + const currentMatch = () => match()! + + const componentKey = () => { + const current = currentMatch() + const remount = + route.options.remountDeps ?? router.options.defaultRemountDeps + const deps = remount?.({ + routeId: routeId()!, + loaderDeps: current.loaderDeps, + params: current._strictParams, + search: current._strictSearch, + }) + return deps ? JSON.stringify(deps) : current.id + } - const [loaderResult] = Solid.createResource(async () => { - await Promise.resolve() - return getLoadPromise(matchId, routerMatch) - }) + const out = () => { + const Comp = route.options.component ?? router.options.defaultComponent + if (Comp) { + return + } + return + } - return <>{loaderResult()} - }} - - - {(_) => { - if (isServer ?? router.isServer) { - const RouteErrorComponent = - (route().options.errorComponent ?? - router.options.defaultErrorComponent) || - ErrorComponent + const keyedOut = () => ( + + {(_key) => out()} + + ) - return ( - - ) - } + return ( + + + {(_) => renderRouteNotFound(router, route, currentMatch().error)} + + + {(_) => { + if (isServer ?? router.isServer) { + const RouteErrorComponent = + (route.options.errorComponent ?? + router.options.defaultErrorComponent) || + ErrorComponent + + return ( + + ) + } - throw currentMatch().error - }} - - - {keyedOut()} - - - ) - }} - + throw currentMatch().error + }} + + + {keyedOut()} + + ) } @@ -454,61 +232,38 @@ export const Outlet = () => { const nearestParentMatch = Solid.useContext(nearestMatchContext) const parentMatch = nearestParentMatch.match const routeId = nearestParentMatch.routeId - const route = Solid.createMemo(() => - routeId() ? router.routesById[routeId()!] : undefined, - ) - - const parentGlobalNotFound = Solid.createMemo( - () => parentMatch()?.globalNotFound ?? false, - ) + const route = router.routesById[routeId()!]! - const childMatchId = Solid.createMemo(() => { - const currentRouteId = routeId() - return currentRouteId - ? router.stores.childMatchIdByRouteId.get()[currentRouteId] - : undefined - }) - - const childMatchStatus = Solid.createMemo(() => { - const id = childMatchId() - if (!id) return undefined - return router.stores.matchStores.get(id)?.get().status - }) - - const shouldShowNotFound = () => - childMatchStatus() !== 'redirected' && parentGlobalNotFound() - - const childRouteKey = Solid.createMemo(() => { - if (shouldShowNotFound()) return undefined - const cid = childMatchId() - if (!cid) return undefined - return router.stores.matchStores.get(cid)?.routeId ?? cid - }) + const childRouteId = () => { + if (parentMatch()!._notFound) { + return + } + const ids = router.stores.ids.get() + return ids[ids.indexOf(routeId()!) + 1] + } return ( - {(resolvedRoute) => - renderRouteNotFound(router, resolvedRoute(), undefined) - } - + parentMatch()!._notFound + ? renderRouteNotFound(router, route, parentMatch()!.error) + : undefined } > {(_routeKey: string) => { return ( } + fallback={} > } > - + ) diff --git a/packages/solid-router/src/Matches.tsx b/packages/solid-router/src/Matches.tsx index 75586f862d..ff270ecbd9 100644 --- a/packages/solid-router/src/Matches.tsx +++ b/packages/solid-router/src/Matches.tsx @@ -3,7 +3,7 @@ import { replaceEqualDeep, rootRouteId } from '@tanstack/router-core' import { isServer } from '@tanstack/router-core/isServer' import { CatchBoundary, ErrorComponent } from './CatchBoundary' import { useRouter } from './useRouter' -import { Transitioner } from './Transitioner' +import { Rendered, Transitioner } from './Transitioner' import { nearestMatchContext } from './matchContext' import { SafeFragment } from './SafeFragment' import { Match } from './Match' @@ -55,6 +55,7 @@ export function Matches() { > + ) @@ -62,26 +63,18 @@ export function Matches() { function MatchesInner() { const router = useRouter() - const matchId = () => router.stores.firstId.get() - const routeId = () => (matchId() ? rootRouteId : undefined) + const routeId = () => router.stores.ids.get()[0] const match = () => - routeId() ? router.stores.getRouteMatchStore(rootRouteId).get() : undefined - const hasPendingMatch = () => - routeId() - ? Boolean(router.stores.pendingRouteIds.get()[rootRouteId]) - : false - const resetKey = () => router.stores.loadedAt.get() + routeId() ? router.stores.byRoute.get(routeId()!)?.get() : undefined const nearestMatch = { - matchId, routeId, match, - hasPending: hasPendingMatch, } const matchComponent = () => { return ( - - + + ) } @@ -92,7 +85,7 @@ function MatchesInner() { matchComponent() ) : ( resetKey()} + getResetKey={match} errorComponent={ErrorComponent} onCatch={ process.env.NODE_ENV !== 'production' @@ -140,7 +133,9 @@ export function useMatchRoute() { return Solid.createMemo(() => { const { pending, caseSensitive, fuzzy, includeSearch, ...rest } = opts - router.stores.matchRouteDeps.get() + router.stores.location.get() + router.stores.resolvedLocation.get() + router.stores.status.get() return router.matchRoute(rest as any, { pending, caseSensitive, @@ -224,13 +219,13 @@ export function useParentMatches< >( opts?: UseMatchesBaseOptions, ): Solid.Accessor> { - const contextMatchId = Solid.useContext(nearestMatchContext).matchId + const contextRouteId = Solid.useContext(nearestMatchContext).routeId return useMatches({ select: (matches: Array>) => { matches = matches.slice( 0, - matches.findIndex((d) => d.id === contextMatchId()), + matches.findIndex((d) => d.routeId === contextRouteId()), ) return opts?.select ? opts.select(matches) : matches }, @@ -243,12 +238,12 @@ export function useChildMatches< >( opts?: UseMatchesBaseOptions, ): Solid.Accessor> { - const contextMatchId = Solid.useContext(nearestMatchContext).matchId + const contextRouteId = Solid.useContext(nearestMatchContext).routeId return useMatches({ select: (matches: Array>) => { matches = matches.slice( - matches.findIndex((d) => d.id === contextMatchId()) + 1, + matches.findIndex((d) => d.routeId === contextRouteId()) + 1, ) return opts?.select ? opts.select(matches) : matches }, diff --git a/packages/solid-router/src/Scripts.tsx b/packages/solid-router/src/Scripts.tsx index 4c1ece99c6..0a547521e2 100644 --- a/packages/solid-router/src/Scripts.tsx +++ b/packages/solid-router/src/Scripts.tsx @@ -1,84 +1,53 @@ import * as Solid from 'solid-js' +import { _getAssetMatches, replaceEqualDeep } from '@tanstack/router-core' import { isServer } from '@tanstack/router-core/isServer' import { Asset } from './Asset' import { useRouter } from './useRouter' -import type { AnyRouteMatch, RouterManagedTag } from '@tanstack/router-core' +import type { RouterManagedTag } from '@tanstack/router-core' export const Scripts = () => { const router = useRouter() const nonce = router.options.ssr?.nonce - const getAssetScripts = (matches: Array) => { - const assetScripts: Array = [] - const manifest = router.ssr?.manifest - - if (!manifest) { - return [] - } - - for (const match of matches) { - const scripts = manifest.routes[match.routeId]?.scripts - - if (!scripts) { - continue + const scripts = Solid.createMemo( + (previous: Array | undefined) => { + const matches = _getAssetMatches(router.stores.matches.get()) + const next: Array = [] + const assets: Array = [] + const manifest = router.ssr?.manifest + for (const match of matches) { + for (const script of match.scripts ?? []) { + if (!script) { + continue + } + const { children, ...attrs } = script + next.push({ + tag: 'script', + attrs: { ...attrs, nonce }, + children: children as string | undefined, + }) + } + for (const asset of manifest?.routes[match.routeId]?.scripts ?? []) { + assets.push({ + tag: 'script', + attrs: { ...asset.attrs, nonce }, + children: asset.children, + }) + } } - - for (const asset of scripts) { - assetScripts.push({ - tag: 'script', - attrs: { ...asset.attrs, nonce }, - children: asset.children, - }) - } - } - - return assetScripts - } - - const getScripts = (matches: Array): Array => - ( - matches - .map((match) => match.scripts!) - .flat(1) - .filter(Boolean) as Array - ).map( - ({ children, ...script }) => - ({ - tag: 'script', - attrs: { - ...script, - nonce, - }, - children, - }) satisfies RouterManagedTag, - ) - - const activeMatches = Solid.createMemo(() => router.stores.matches.get()) - const assetScripts = Solid.createMemo(() => getAssetScripts(activeMatches())) - const scripts = Solid.createMemo(() => getScripts(activeMatches())) - - return renderScripts(router, scripts(), assetScripts()) -} - -function renderScripts( - router: ReturnType, - scripts: Array, - assetScripts: Array, -) { - const allScripts = [...scripts, ...assetScripts] as Array - - if ((isServer ?? router.isServer) && router.serverSsr) { - const serverBufferedScript = router.serverSsr.takeBufferedScripts() - if (serverBufferedScript) { - allScripts.unshift(serverBufferedScript) - } - } + next.push(...assets) + return previous ? replaceEqualDeep(previous, next) : next + }, + ) + const serverBufferedScript = + (isServer ?? router.isServer) && router.serverSsr + ? router.serverSsr.takeBufferedScripts() + : undefined return ( <> - {allScripts.map((asset) => ( - - ))} + {serverBufferedScript && } + {(asset) => } ) } diff --git a/packages/solid-router/src/Transitioner.tsx b/packages/solid-router/src/Transitioner.tsx index 331aff5864..58b3dca076 100644 --- a/packages/solid-router/src/Transitioner.tsx +++ b/packages/solid-router/src/Transitioner.tsx @@ -3,36 +3,33 @@ import { getLocationChangeInfo, trimPathRight } from '@tanstack/router-core' import { isServer } from '@tanstack/router-core/isServer' import { useRouter } from './useRouter' +function getResolvedLocation(router: ReturnType) { + const resolvedLocation = router.stores.resolvedLocation.get() + if ( + resolvedLocation?.href === router.latestLocation.href && + resolvedLocation.state.__TSR_key === router.latestLocation.state.__TSR_key + ) { + return resolvedLocation + } + return +} + export function Transitioner() { const router = useRouter() - let mountLoadForRouter = { router, mounted: false } - const isLoading = Solid.createMemo(() => router.stores.isLoading.get()) if (isServer ?? router.isServer) { return null } - const [isSolidTransitioning, startSolidTransition] = Solid.useTransition() - - // Track pending state changes - const hasPending = Solid.createMemo(() => router.stores.hasPending.get()) - - const isAnyPending = Solid.createMemo( - () => isLoading() || isSolidTransitioning() || hasPending(), - ) - - const isPagePending = Solid.createMemo(() => isLoading() || hasPending()) - - router.startTransition = (fn: () => void | Promise) => { - Solid.startTransition(() => { - startSolidTransition(fn) - }) + router.startTransition = async (fn) => { + await Solid.startTransition(fn) + return true } // Subscribe to location changes // and try to load the new location Solid.onMount(() => { - const unsub = router.history.subscribe(router.load) + Solid.onCleanup(router.history.subscribe(router.load)) const nextLocation = router.buildLocation({ to: router.latestLocation.pathname, @@ -44,95 +41,38 @@ export function Transitioner() { }) // Check if the current URL matches the canonical form. - // Compare publicHref (browser-facing URL) for consistency with - // the server-side redirect check in router.beforeLoad. + // Compare publicHref (browser-facing URL) consistently with server + // canonicalization. if ( trimPathRight(router.latestLocation.publicHref) !== trimPathRight(nextLocation.publicHref) ) { - router.commitLocation({ ...nextLocation, replace: true }) - } - - Solid.onCleanup(() => { - unsub() - }) - }) - - // Try to load the initial location - Solid.createRenderEffect(() => { - Solid.untrack(() => { - if ( - // if we are hydrating from SSR, loading is triggered in ssr-client - (typeof window !== 'undefined' && router.ssr) || - (mountLoadForRouter.router === router && mountLoadForRouter.mounted) - ) { - return - } - mountLoadForRouter = { router, mounted: true } - const tryLoad = async () => { - try { - await router.load() - } catch (err) { - console.error(err) - } - } - tryLoad() - }) - }) - - Solid.createRenderEffect((previousIsLoading = false) => { - const currentIsLoading = isLoading() - - if (previousIsLoading && !currentIsLoading) { - router.emit({ - type: 'onLoad', - ...getLocationChangeInfo( - router.stores.location.get(), - router.stores.resolvedLocation.get(), - ), + router.commitLocation({ + ...nextLocation, + replace: true, + ignoreBlocker: true, }) + return } - return currentIsLoading - }) - - Solid.createComputed((previousIsPagePending = false) => { - const currentIsPagePending = isPagePending() - - if (previousIsPagePending && !currentIsPagePending) { - router.emit({ - type: 'onBeforeRouteMount', - ...getLocationChangeInfo( - router.stores.location.get(), - router.stores.resolvedLocation.get(), - ), - }) + if (!getResolvedLocation(router) && !router._tx) { + router.load().catch(console.error) } - - return currentIsPagePending }) - Solid.createRenderEffect((previousIsAnyPending = false) => { - const currentIsAnyPending = isAnyPending() + return null +} - if (previousIsAnyPending && !currentIsAnyPending) { - const changeInfo = getLocationChangeInfo( - router.stores.location.get(), - router.stores.resolvedLocation.get(), - ) +export function Rendered() { + const router = useRouter() + Solid.onMount(() => { + const resolvedLocation = getResolvedLocation(router) + if (resolvedLocation) { router.emit({ - type: 'onResolved', - ...changeInfo, - }) - - Solid.batch(() => { - router.stores.status.set('idle') - router.stores.resolvedLocation.set(router.stores.location.get()) + type: 'onRendered', + ...getLocationChangeInfo(resolvedLocation, resolvedLocation), }) } - - return currentIsAnyPending }) - return null } diff --git a/packages/solid-router/src/awaited.tsx b/packages/solid-router/src/awaited.tsx index fb1edb14e0..148f45edda 100644 --- a/packages/solid-router/src/awaited.tsx +++ b/packages/solid-router/src/awaited.tsx @@ -10,7 +10,7 @@ export type AwaitOptions = { export function useAwaited({ promise: _promise, -}: AwaitOptions): [T, DeferredPromise] { +}: AwaitOptions): [data: T, promise: DeferredPromise] { const promise = defer(_promise) if (promise[TSR_DEFERRED_PROMISE].status === 'pending') { diff --git a/packages/solid-router/src/headContentUtils.tsx b/packages/solid-router/src/headContentUtils.tsx index b4ca3e8a4e..9dcad352fb 100644 --- a/packages/solid-router/src/headContentUtils.tsx +++ b/packages/solid-router/src/headContentUtils.tsx @@ -1,5 +1,6 @@ import * as Solid from 'solid-js' import { + _getAssetMatches, appendUniqueUserTags, escapeHtml, getAssetCrossOrigin, @@ -13,29 +14,27 @@ import type { } from '@tanstack/router-core' /** - * Build the list of head/link/meta/script tags to render for active matches. + * Build the head/link/meta/script tags from the renderable presented prefix. * Used internally by `HeadContent`. */ export const useTags = (assetCrossOrigin?: AssetCrossOriginConfig) => { const router = useRouter() const nonce = router.options.ssr?.nonce - const activeMatches = Solid.createMemo(() => router.stores.matches.get()) - const routeMeta = Solid.createMemo(() => - activeMatches() - .map((match) => match.meta) - .filter((meta) => meta !== undefined), - ) - - const meta: Solid.Accessor> = Solid.createMemo(() => { + return Solid.createMemo((prev: Array | undefined) => { + const matches = _getAssetMatches(router.stores.matches.get()) const resultMeta: Array = [] const metaByAttribute: Record = {} let title: RouterManagedTag | undefined - const routeMetasArray = routeMeta() - for (let i = routeMetasArray.length - 1; i >= 0; i--) { - const metas = routeMetasArray[i]! + for (let i = matches.length - 1; i >= 0; i--) { + const metas = matches[i]!.meta + if (!metas) { + continue + } for (let j = metas.length - 1; j >= 0; j--) { const m = metas[j] - if (!m) continue + if (!m) { + continue + } if (m.title) { if (!title) { @@ -95,126 +94,98 @@ export const useTags = (assetCrossOrigin?: AssetCrossOriginConfig) => { } resultMeta.reverse() - return resultMeta - }) - - const links = Solid.createMemo(() => { - const matches = activeMatches() - const constructed = matches - .flatMap((match) => match.links ?? []) - .filter((link) => link !== undefined) - .map((link) => ({ - tag: 'link', - attrs: { - ...link, - nonce, - }, - })) satisfies Array - - return constructed - }) - - const manifestCssTags = Solid.createMemo(() => { + const next: Array = [] + appendUniqueUserTags(next, resultMeta) const manifest = router.ssr?.manifest - const tags: Array = [] - - if (!manifest) { - return tags - } - - for (const match of activeMatches()) { - manifest.routes[match.routeId]?.css?.forEach((link) => { - const resolvedLink = resolveManifestCssLink(link) - tags.push({ + const preloads: Array = [] + for (const match of matches) { + for (const preload of manifest?.routes[match.routeId]?.preloads ?? []) { + if (!preload) { + continue + } + preloads.push({ tag: 'link', attrs: { - rel: 'stylesheet', - ...resolvedLink, - crossOrigin: - getAssetCrossOrigin(assetCrossOrigin, 'stylesheet') ?? - resolvedLink.crossOrigin, + ...getScriptPreloadAttrs(manifest, preload, assetCrossOrigin), nonce, }, }) - }) + } } + next.push(...preloads) - if (manifest.inlineStyle) { - tags.push({ - tag: 'style', - attrs: { - ...manifest.inlineStyle.attrs, - nonce, - }, - children: manifest.inlineStyle.children, - inlineCss: true, - }) + const links: Array = [] + for (const match of matches) { + for (const link of match.links ?? []) { + if (link === undefined) { + continue + } + links.push({ tag: 'link', attrs: { ...link, nonce } }) + } } + appendUniqueUserTags(next, links) - return tags - }) - - const preloadLinks = Solid.createMemo(() => { - const matches = activeMatches() - const preloadLinks: Array = [] - - matches.forEach((match) => - router.ssr?.manifest?.routes[match.routeId]?.preloads - ?.filter(Boolean) - .forEach((preload) => { - preloadLinks.push({ + if (manifest) { + for (const match of matches) { + for (const link of manifest.routes[match.routeId]?.css ?? []) { + const resolvedLink = resolveManifestCssLink(link) + next.push({ tag: 'link', attrs: { - ...getScriptPreloadAttrs( - router.ssr?.manifest, - preload, - assetCrossOrigin, - ), + rel: 'stylesheet', + ...resolvedLink, + crossOrigin: + getAssetCrossOrigin(assetCrossOrigin, 'stylesheet') ?? + resolvedLink.crossOrigin, nonce, }, }) - }), - ) - - return preloadLinks - }) - - const styles = Solid.createMemo(() => { - return activeMatches() - .flatMap((match) => match.styles ?? []) - .filter((style) => style !== undefined) - .map(({ children, ...style }) => ({ - tag: 'style', - attrs: { - ...style, - nonce, - }, - children: children as string | undefined, - })) satisfies Array - }) - - const headScripts = Solid.createMemo(() => { - return activeMatches() - .flatMap((match) => match.headScripts ?? []) - .filter((script) => script !== undefined) - .map(({ children, ...script }) => ({ - tag: 'script', - attrs: { - ...script, - nonce, - }, - children: children as string | undefined, - })) satisfies Array - }) + } + } + if (manifest.inlineStyle) { + next.push({ + tag: 'style', + attrs: { + ...manifest.inlineStyle.attrs, + nonce, + }, + children: manifest.inlineStyle.children, + inlineCss: true, + }) + } + } - return Solid.createMemo((prev: Array | undefined) => { - const next: Array = [] - appendUniqueUserTags(next, meta()) - next.push(...preloadLinks()) - appendUniqueUserTags(next, links()) - next.push(...manifestCssTags()) - appendUniqueUserTags(next, styles()) - appendUniqueUserTags(next, headScripts()) + const styles: Array = [] + const headScripts: Array = [] + for (const match of matches) { + for (const style of match.styles ?? []) { + if (style === undefined) { + continue + } + const { children, ...attrs } = style + styles.push({ + tag: 'style', + attrs: { ...attrs, nonce }, + children: children as string | undefined, + }) + } + for (const script of match.headScripts ?? []) { + if (script === undefined) { + continue + } + const { children, ...attrs } = script + headScripts.push({ + tag: 'script', + attrs: { + ...attrs, + nonce, + }, + children: children as string | undefined, + }) + } + } + appendUniqueUserTags(next, styles) + appendUniqueUserTags(next, headScripts) if (prev === undefined) { return next diff --git a/packages/solid-router/src/lazyRouteComponent.tsx b/packages/solid-router/src/lazyRouteComponent.tsx index 424940522b..64bf129337 100644 --- a/packages/solid-router/src/lazyRouteComponent.tsx +++ b/packages/solid-router/src/lazyRouteComponent.tsx @@ -1,6 +1,7 @@ import { Dynamic } from 'solid-js/web' import { createResource } from 'solid-js' import { isModuleNotFoundError } from '@tanstack/router-core' +import { isServer } from '@tanstack/router-core/isServer' import type { AsyncRouteComponent } from './route' export function lazyRouteComponent< @@ -18,13 +19,18 @@ export function lazyRouteComponent< const load = () => { if (!loadPromise) { + error = undefined loadPromise = importer() .then((res) => { - loadPromise = undefined + // Keep browser preload behavior unchanged; SSR can reuse the import. + if (!(isServer ?? typeof window === 'undefined')) { + loadPromise = undefined + } comp = res[exportName ?? 'default'] return comp }) .catch((err) => { + loadPromise = undefined error = err }) } diff --git a/packages/solid-router/src/link.tsx b/packages/solid-router/src/link.tsx index 295b8ec230..935d0a3d2b 100644 --- a/packages/solid-router/src/link.tsx +++ b/packages/solid-router/src/link.tsx @@ -131,18 +131,16 @@ export function useLinkProps< { equals: (prev, next) => prev.href === next.href }, ) - const _options = () => options - const next = Solid.createMemo(() => { // Rebuild when inherited search/hash or the current route context changes. const _fromLocation = currentLocation() - const options = { _fromLocation, ..._options() } as any + const nextOptions = { _fromLocation, ...options } as any // untrack because router-core will also access stores, which are signals in solid - return Solid.untrack(() => router.buildLocation(options)) + return Solid.untrack(() => router.buildLocation(nextOptions)) }) const hrefOption = Solid.createMemo(() => { - if (_options().disabled) return undefined + if (options.disabled) return undefined // Use publicHref - it contains the correct href for display // When a rewrite changes the origin, publicHref is the full URL // Otherwise it's the origin-stripped path @@ -173,7 +171,7 @@ export function useLinkProps< } return _href.href } - const to = _options().to + const to = options.to const safeInternal = isSafeInternal(to) if (safeInternal) return undefined if (typeof to !== 'string' || to.indexOf(':') === -1) return undefined @@ -192,7 +190,7 @@ export function useLinkProps< }) const preload = Solid.createMemo(() => { - if (_options().reloadDocument || externalLink()) { + if (options.reloadDocument || externalLink()) { return false } return local.preload ?? router.options.defaultPreload @@ -251,7 +249,7 @@ export function useLinkProps< const doPreload = () => router - .preloadRoute({ ..._options(), _builtLocation: next() } as any) + .preloadRoute({ ...options, _builtLocation: next() } as any) .catch((err: any) => { console.warn(err) console.warn(preloadWarning) @@ -288,7 +286,7 @@ export function useLinkProps< return Solid.mergeProps( propsSafeToSpread, { - ref: mergeRefs(setRef, _options().ref), + ref: mergeRefs(setRef, options.ref), href: externalLink(), }, Solid.splitProps(local, [ @@ -336,7 +334,7 @@ export function useLinkProps< // All is well? Navigate! // N.B. we don't call `router.commitLocation(next) here because we want to run `validateSearch` before committing router.navigate({ - ..._options(), + ...options, replace: local.replace, resetScroll: local.resetScroll, hashScrollIntoView: local.hashScrollIntoView, @@ -425,7 +423,7 @@ export function useLinkProps< const base = { href: hrefOption()?.href, - ref: mergeRefs(setRef, _options().ref), + ref: mergeRefs(setRef, options.ref), onClick, onBlur, onFocus, diff --git a/packages/solid-router/src/matchContext.tsx b/packages/solid-router/src/matchContext.tsx index f6efc37c6b..50ab0d4b76 100644 --- a/packages/solid-router/src/matchContext.tsx +++ b/packages/solid-router/src/matchContext.tsx @@ -2,17 +2,13 @@ import * as Solid from 'solid-js' import type { AnyRouteMatch } from '@tanstack/router-core' export type NearestMatchContextValue = { - matchId: Solid.Accessor routeId: Solid.Accessor match: Solid.Accessor - hasPending: Solid.Accessor } const defaultNearestMatchContext: NearestMatchContextValue = { - matchId: () => undefined, routeId: () => undefined, match: () => undefined, - hasPending: () => false, } export const nearestMatchContext = diff --git a/packages/solid-router/src/routerStores.ts b/packages/solid-router/src/routerStores.ts index 11c06a35ef..93c8b5ea8f 100644 --- a/packages/solid-router/src/routerStores.ts +++ b/packages/solid-router/src/routerStores.ts @@ -5,53 +5,11 @@ import { } from '@tanstack/router-core' import { isServer } from '@tanstack/router-core/isServer' import type { - AnyRoute, GetStoreConfig, RouterReadableStore, - RouterStores, RouterWritableStore, } from '@tanstack/router-core' -declare module '@tanstack/router-core' { - export interface RouterStores { - /** Maps each active routeId to the matchId of its child in the match tree. */ - childMatchIdByRouteId: RouterReadableStore> - /** Maps each pending routeId to true for quick lookup. */ - pendingRouteIds: RouterReadableStore> - } -} - -function initRouterStores( - stores: RouterStores, - createReadonlyStore: ( - read: () => TValue, - ) => RouterReadableStore, -) { - stores.childMatchIdByRouteId = createReadonlyStore(() => { - const ids = stores.matchesId.get() - const obj: Record = {} - for (let i = 0; i < ids.length - 1; i++) { - const parentStore = stores.matchStores.get(ids[i]!) - if (parentStore?.routeId) { - obj[parentStore.routeId] = ids[i + 1]! - } - } - return obj - }) - - stores.pendingRouteIds = createReadonlyStore(() => { - const ids = stores.pendingIds.get() - const obj: Record = {} - for (const id of ids) { - const store = stores.pendingMatchStores.get(id) - if (store?.routeId) { - obj[store.routeId] = true - } - } - return obj - }) -} - function createSolidMutableStore( initialValue: TValue, ): RouterWritableStore { @@ -60,22 +18,10 @@ function createSolidMutableStore( return { get: signal, set: setSignal } } -let finalizationRegistry: FinalizationRegistry<() => void> | null = null -if (typeof globalThis !== 'undefined' && 'FinalizationRegistry' in globalThis) { - finalizationRegistry = new FinalizationRegistry((cb) => cb()) -} - function createSolidReadonlyStore( read: () => TValue, ): RouterReadableStore { - let dispose!: () => void - const memo = Solid.createRoot((d) => { - dispose = d - return Solid.createMemo(read) - }) - const store = { get: memo } - finalizationRegistry?.register(store, dispose) - return store + return { get: Solid.createRoot(() => Solid.createMemo(read)) } } export const getStoreFactory: GetStoreConfig = (opts) => { @@ -84,8 +30,6 @@ export const getStoreFactory: GetStoreConfig = (opts) => { createMutableStore: createNonReactiveMutableStore, createReadonlyStore: createNonReactiveReadonlyStore, batch: (fn) => fn(), - init: (stores) => - initRouterStores(stores, createNonReactiveReadonlyStore), } } @@ -93,6 +37,5 @@ export const getStoreFactory: GetStoreConfig = (opts) => { createMutableStore: createSolidMutableStore, createReadonlyStore: createSolidReadonlyStore, batch: Solid.batch, - init: (stores) => initRouterStores(stores, createSolidReadonlyStore), } } diff --git a/packages/solid-router/src/ssr/RouterClient.tsx b/packages/solid-router/src/ssr/RouterClient.tsx index 15e0dcc39f..c2389495db 100644 --- a/packages/solid-router/src/ssr/RouterClient.tsx +++ b/packages/solid-router/src/ssr/RouterClient.tsx @@ -5,21 +5,16 @@ import { RouterProvider } from '../RouterProvider' import type { AnyRouter } from '@tanstack/router-core' import type { JSXElement } from 'solid-js' -let hydrationPromise: Promise>> | undefined +let hydrationPromise: Promise | undefined const Dummy = (props: { children?: JSXElement }) => <>{props.children} export function RouterClient(props: { router: AnyRouter }) { - if (!hydrationPromise) { - if (!props.router.stores.matchesId.get().length) { - hydrationPromise = hydrate(props.router) - } else { - hydrationPromise = Promise.resolve() - } - } + hydrationPromise ??= hydrate(props.router).finally(() => window.$_TSR!.h()) + return ( true)} children={() => ( diff --git a/packages/solid-router/src/ssr/renderRouterToStream.tsx b/packages/solid-router/src/ssr/renderRouterToStream.tsx index 8914c117d9..7432f19510 100644 --- a/packages/solid-router/src/ssr/renderRouterToStream.tsx +++ b/packages/solid-router/src/ssr/renderRouterToStream.tsx @@ -167,12 +167,15 @@ export const renderRouterToStream = async ({ const responseStream = transformReadableStreamWithRouter( router, readable as unknown as ReadableStream, - { onAbort: abortSolidPipe }, + { signal: request.signal, onAbort: abortSolidPipe }, ) return createSsrStreamResponse( router, new Response(responseStream as any, { - status: router.stores.statusCode.get(), + status: + router._serverResult?.type === 'render' + ? router._serverResult.status + : 200, headers: responseHeaders, }), ) diff --git a/packages/solid-router/src/ssr/renderRouterToString.tsx b/packages/solid-router/src/ssr/renderRouterToString.tsx index 7da982b392..421ac234bc 100644 --- a/packages/solid-router/src/ssr/renderRouterToString.tsx +++ b/packages/solid-router/src/ssr/renderRouterToString.tsx @@ -32,7 +32,10 @@ export const renderRouterToString = ({ html = html.replace(``, () => `${injectedHtml}`) } return new Response(`${html}`, { - status: router.stores.statusCode.get(), + status: + router._serverResult?.type === 'render' + ? router._serverResult.status + : 200, headers: responseHeaders, }) } catch (error) { diff --git a/packages/solid-router/src/useMatch.tsx b/packages/solid-router/src/useMatch.tsx index 858d75be6b..97880329db 100644 --- a/packages/solid-router/src/useMatch.tsx +++ b/packages/solid-router/src/useMatch.tsx @@ -70,13 +70,12 @@ export function useMatch< ThrowOrOptional, TThrow> > { const router = useRouter() - const nearestMatch = opts.from - ? undefined - : Solid.useContext(nearestMatchContext) + const contextMatch = Solid.useContext(nearestMatchContext) + const nearestMatch = opts.from ? undefined : contextMatch const match = () => { if (opts.from) { - return router.stores.getRouteMatchStore(opts.from).get() + return router.stores.getMatchStore(opts.from).get() } return nearestMatch?.match() @@ -87,15 +86,7 @@ export function useMatch< return } - const hasPendingMatch = opts.from - ? Boolean(router.stores.pendingRouteIds.get()[opts.from!]) - : (nearestMatch?.hasPending() ?? false) - - if ( - !hasPendingMatch && - !router.stores.isTransitioning.get() && - (opts.shouldThrow ?? true) - ) { + if (opts.shouldThrow ?? true) { if (process.env.NODE_ENV !== 'production') { throw new Error( `Invariant failed: Could not find ${opts.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`, @@ -110,17 +101,6 @@ export function useMatch< const selectedMatch = match() if (selectedMatch === undefined) { - const hasPendingMatch = opts.from - ? Boolean(router.stores.pendingRouteIds.get()[opts.from!]) - : (nearestMatch?.hasPending() ?? false) - - if ( - prev !== undefined && - (hasPendingMatch || router.stores.isTransitioning.get()) - ) { - return prev - } - return undefined } diff --git a/packages/solid-router/tests/Scripts.test.tsx b/packages/solid-router/tests/Scripts.test.tsx index b15faaef19..29f6553d56 100644 --- a/packages/solid-router/tests/Scripts.test.tsx +++ b/packages/solid-router/tests/Scripts.test.tsx @@ -1,4 +1,4 @@ -import { afterEach, describe, expect, test } from 'vitest' +import { afterEach, describe, expect, test, vi } from 'vitest' import { cleanup, fireEvent, @@ -6,11 +6,14 @@ import { screen, waitFor, } from '@solidjs/testing-library' +import { hydrate } from '@tanstack/router-core/ssr/client' +import { dehydrateSsrMatchId } from '../../router-core/src/ssr/ssr-match-id' import { HeadContent, Link, Outlet, + RouterContextProvider, RouterProvider, createBrowserHistory, createMemoryHistory, @@ -47,9 +50,64 @@ afterEach(() => { cleanup() browserHistories.splice(0).forEach((history) => history.destroy()) window.history.replaceState(null, 'root', '/') + delete window.$_TSR }) describe('ssr scripts', () => { + test('updates route data scripts after client navigation', async () => { + const rootRoute = createRootRoute({ + component: () => ( + <> + + + + ), + }) + const firstRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/first', + scripts: () => [ + { + id: 'first-route-data', + type: 'application/json', + children: 'first', + }, + ], + component: () => Second, + }) + const secondRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/second', + scripts: () => [ + { + id: 'second-route-data', + type: 'application/json', + children: 'second', + }, + ], + component: () =>
    Second route
    , + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([firstRoute, secondRoute]), + history: createMemoryHistory({ initialEntries: ['/first'] }), + }) + + const { container } = render(() => ) + await screen.findByRole('link', { name: 'Second' }) + expect(container.querySelector('#first-route-data')?.textContent).toBe( + 'first', + ) + + fireEvent.click(screen.getByRole('link', { name: 'Second' })) + expect(await screen.findByText('Second route')).toBeInTheDocument() + await waitFor(() => { + expect(container.querySelector('#first-route-data')).toBeNull() + expect(container.querySelector('#second-route-data')?.textContent).toBe( + 'second', + ) + }) + }) + test('it works', async () => { const rootRoute = createRootRoute({ // loader: () => new Promise((r) => setTimeout(r, 1)), @@ -453,6 +511,104 @@ describe('ssr scripts', () => { }) describe('ssr HeadContent', () => { + test('renders descendant assets during a data-only hydration handoff', async () => { + const rootRoute = createRootRoute({}) + const dataOnlyRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/report', + ssr: 'data-only', + loader: () => 'report', + }) + const childRoute = createRoute({ + getParentRoute: () => dataOnlyRoute, + path: '/details', + loader: () => 'details', + head: () => ({ + meta: [{ name: 'solid-data-only-child', content: 'visible' }], + }), + scripts: () => [ + { + id: 'solid-data-only-body-script', + type: 'application/json', + children: '{"source":"body"}', + }, + ], + }) + const router = createRouter({ + history: createMemoryHistory({ + initialEntries: ['/report/details'], + }), + routeTree: rootRoute.addChildren([ + dataOnlyRoute.addChildren([childRoute]), + ]), + }) + const matches = router.matchRoutes(router.latestLocation) + window.$_TSR = { + router: { + dehydratedData: {}, + manifest: { + routes: { + [childRoute.id]: { + preloads: ['/solid-data-only-manifest.js'], + scripts: [ + { + attrs: { + id: 'solid-data-only-manifest-script', + type: 'application/json', + }, + children: '{"source":"manifest"}', + }, + ], + }, + }, + }, + matches: matches.map((match, index) => ({ + i: dehydrateSsrMatchId(match.id), + s: 'success', + ssr: index === 1 ? 'data-only' : true, + l: index ? (index === 1 ? 'report' : 'details') : undefined, + u: Date.now(), + })), + }, + h: vi.fn(), + e: vi.fn(), + c: vi.fn(), + p: vi.fn(), + buffer: [], + } + + await hydrate(router) + + expect(router.state.matches.map((match) => match.status)).toEqual([ + 'success', + 'pending', + 'success', + ]) + render(() => ( + + {() => ( + <> + + + + )} + + )) + + expect( + document.querySelector('meta[name="solid-data-only-child"]'), + ).not.toBeNull() + expect( + document.querySelector('link[href="/solid-data-only-manifest.js"]'), + ).not.toBeNull() + expect( + document.querySelector('#solid-data-only-body-script'), + ).not.toBeNull() + expect( + document.querySelector('#solid-data-only-manifest-script'), + ).not.toBeNull() + }) + test('derives title, dedupes meta, and allows non-loader HeadContent', async () => { const rootRoute = createRootRoute({ loader: () => diff --git a/packages/solid-router/tests/component-preload-retry.test.tsx b/packages/solid-router/tests/component-preload-retry.test.tsx index a311a41f26..0a26f0af26 100644 --- a/packages/solid-router/tests/component-preload-retry.test.tsx +++ b/packages/solid-router/tests/component-preload-retry.test.tsx @@ -1,6 +1,15 @@ import { afterEach, expect, test, vi } from 'vitest' -import { cleanup, render, screen } from '@solidjs/testing-library' -import { lazyRouteComponent } from '../src' +import { cleanup, fireEvent, render, screen } from '@solidjs/testing-library' +import { + RouterProvider, + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, + lazyRouteComponent, + useRouter, +} from '../src' +import type { ErrorComponentProps } from '../src' afterEach(() => { cleanup() @@ -8,6 +17,18 @@ afterEach(() => { vi.unstubAllGlobals() }) +test('a successful server component download is reused', async () => { + vi.stubGlobal('window', undefined) + const importer = vi.fn().mockResolvedValue({ default: () => null }) + const Page = lazyRouteComponent(importer) + + const preload = Page.preload?.() + await preload + + expect(Page.preload?.()).toBe(preload) + expect(importer).toHaveBeenCalledTimes(1) +}) + test('a component loads when rendered before preload', async () => { const importer = vi.fn().mockResolvedValue({ default: () =>
    Page content
    , @@ -19,3 +40,47 @@ test('a component loads when rendered before preload', async () => { expect(await screen.findByText('Page content')).toBeInTheDocument() expect(importer).toHaveBeenCalledTimes(1) }) + +test('a failed component download is retried from the route error UI', async () => { + vi.spyOn(console, 'error').mockImplementation(() => {}) + + const PageContent = () =>
    Page content
    + const importer = vi + .fn<() => Promise<{ default: typeof PageContent }>>() + .mockRejectedValueOnce(new Error('component download failed')) + .mockResolvedValue({ default: PageContent }) + const Page = lazyRouteComponent(importer) + + function RouteError(props: ErrorComponentProps) { + const router = useRouter() + return ( + + ) + } + + const rootRoute = createRootRoute() + const pageRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/page', + component: Page, + errorComponent: RouteError, + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + render(() => ) + + fireEvent.click(await screen.findByRole('button', { name: 'Retry' })) + + expect(await screen.findByText('Page content')).toBeInTheDocument() +}) diff --git a/packages/solid-router/tests/createLazyRoute.test.tsx b/packages/solid-router/tests/createLazyRoute.test.tsx index 215ef93a1b..4dcbbbfc2c 100644 --- a/packages/solid-router/tests/createLazyRoute.test.tsx +++ b/packages/solid-router/tests/createLazyRoute.test.tsx @@ -1,4 +1,4 @@ -import { afterEach, describe, expect, it, vi } from 'vitest' +import { afterEach, describe, expect, it, onTestFinished, vi } from 'vitest' import { cleanup, fireEvent, render, screen } from '@solidjs/testing-library' import { Link, @@ -97,6 +97,78 @@ describe('preload: matched routes', { timeout: 20000 }, () => { }) }) +// https://github.com/TanStack/router/issues/4467 +it('replaces default pending UI when delayed lazy options provide pending UI', async () => { + const loader = createControlledPromise() + const componentPreload = createControlledPromise() + const pendingComponentPreload = createControlledPromise() + const preloadComponent = vi.fn(() => componentPreload) + const preloadPendingComponent = vi.fn(() => pendingComponentPreload) + const Page = Object.assign(() =>

    Page

    , { + preload: preloadComponent, + }) + const Pending = Object.assign(() =>

    Loading lazy page

    , { + preload: preloadPendingComponent, + }) + const lazyPageOptions = createLazyRoute('/page')({ + pendingComponent: Pending, + component: Page, + }) + const lazyOptions = createControlledPromise() + const rootRoute = createRootRoute({ component: Outlet }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>

    Index page

    , + }) + const pageRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/page', + loader: () => loader, + }).lazy(() => lazyOptions) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute, pageRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + defaultPendingMs: 0, + defaultPendingMinMs: 0, + defaultPendingComponent: () =>

    Loading default

    , + }) + render(() => ) + expect(await screen.findByText('Index page')).toBeInTheDocument() + + const navigation = router.navigate({ to: '/page' }) + onTestFinished(async () => { + lazyOptions.resolve(lazyPageOptions) + pendingComponentPreload.resolve() + componentPreload.resolve() + loader.resolve() + await Promise.allSettled([navigation]) + }) + + expect(await screen.findByRole('status')).toHaveTextContent('Loading default') + + lazyOptions.resolve(lazyPageOptions) + await vi.waitFor(() => { + expect(preloadComponent).toHaveBeenCalledOnce() + expect(preloadPendingComponent).toHaveBeenCalledOnce() + }) + expect(screen.getByRole('status')).toHaveTextContent('Loading default') + expect(pendingComponentPreload.status).toBe('pending') + + pendingComponentPreload.resolve() + await vi.waitFor(() => + expect(screen.getByRole('status')).toHaveTextContent('Loading lazy page'), + ) + expect(componentPreload.status).toBe('pending') + expect(screen.queryByText('Page')).not.toBeInTheDocument() + + componentPreload.resolve() + loader.resolve() + await navigation + + expect(await screen.findByText('Page')).toBeInTheDocument() +}) + it('renders an eager loader error with a delayed lazy errorComponent', async () => { const loader = createControlledPromise() const loaderErrorHandled = createControlledPromise() diff --git a/packages/solid-router/tests/errorComponent.test.tsx b/packages/solid-router/tests/errorComponent.test.tsx index 4ee1abf3f1..af45fdccee 100644 --- a/packages/solid-router/tests/errorComponent.test.tsx +++ b/packages/solid-router/tests/errorComponent.test.tsx @@ -1,8 +1,10 @@ -import { afterEach, describe, expect, test, vi } from 'vitest' +import { afterEach, describe, expect, onTestFinished, test, vi } from 'vitest' import { cleanup, fireEvent, render, screen } from '@solidjs/testing-library' +import { createControlledPromise } from '@tanstack/router-core' import { Link, + Outlet, RouterProvider, createLazyRoute, createRootRoute, @@ -160,3 +162,101 @@ describe.each([true, false])( ) }, ) + +test('global catch boundary resets when a background child generation recovers', async () => { + const refresh = createControlledPromise() + let loaderCalls = 0 + const rootRoute = createRootRoute({ component: Outlet }) + const childRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + loader: { + staleReloadMode: 'background', + handler: () => (++loaderCalls === 1 ? 1 : refresh), + }, + component: () => { + const revision = childRoute.useLoaderData() + if (revision() === 1) { + throw new Error('stale child render failed') + } + return
    Recovered child revision {revision()}
    + }, + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([childRoute]), + }) + vi.spyOn(console, 'warn').mockImplementation(() => {}) + vi.spyOn(console, 'error').mockImplementation(() => {}) + + render(() => ) + expect( + await screen.findByText('stale child render failed'), + ).toBeInTheDocument() + + const invalidation = router.invalidate() + await vi.waitFor(() => expect(loaderCalls).toBe(2)) + expect(screen.getByText('stale child render failed')).toBeInTheDocument() + expect(screen.queryByText(/Recovered child revision/)).not.toBeInTheDocument() + refresh.resolve(2) + await invalidation + + expect( + await screen.findByText('Recovered child revision 2'), + ).toBeInTheDocument() + expect( + screen.queryByText('stale child render failed'), + ).not.toBeInTheDocument() +}) + +test('ancestor route errorComponent resets when a background child generation recovers', async () => { + const refresh = createControlledPromise() + let loaderCalls = 0 + const rootRoute = createRootRoute({ + component: Outlet, + errorComponent: ({ error }) =>
    Ancestor error: {error.message}
    , + }) + const childRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + loader: { + staleReloadMode: 'background', + handler: () => (++loaderCalls === 1 ? 1 : refresh), + }, + component: () => { + const revision = childRoute.useLoaderData() + if (revision() === 1) { + throw new Error('stale child render failed') + } + return
    Recovered child revision {revision()}
    + }, + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([childRoute]), + }) + vi.spyOn(console, 'warn').mockImplementation(() => {}) + vi.spyOn(console, 'error').mockImplementation(() => {}) + + render(() => ) + expect( + await screen.findByText('Ancestor error: stale child render failed'), + ).toBeInTheDocument() + + const invalidation = router.invalidate({ + filter: (match) => match.routeId === childRoute.id, + }) + onTestFinished(async () => { + refresh.resolve(2) + await Promise.allSettled([invalidation]) + }) + + await vi.waitFor(() => expect(loaderCalls).toBe(2)) + expect( + screen.getByText('Ancestor error: stale child render failed'), + ).toBeInTheDocument() + refresh.resolve(2) + await invalidation + + expect( + await screen.findByText('Recovered child revision 2'), + ).toBeInTheDocument() +}) diff --git a/packages/solid-router/tests/loaders.test.tsx b/packages/solid-router/tests/loaders.test.tsx index b2ace7b43d..63ac8e3e2e 100644 --- a/packages/solid-router/tests/loaders.test.tsx +++ b/packages/solid-router/tests/loaders.test.tsx @@ -11,17 +11,16 @@ import { createRootRoute, createRoute, createRouter, - useLoaderData, useRouter, } from '../src' import { sleep } from './utils' afterEach(() => { + cleanup() vi.useRealTimers() vi.resetAllMocks() window.history.replaceState(null, 'root', '/') - cleanup() }) const WAIT_TIME = 100 @@ -322,31 +321,64 @@ test('throw error from beforeLoad when navigating to route', async () => { expect(indexElement).toBeInTheDocument() }) -test('throw abortError from loader upon initial load with basepath', async () => { +// https://github.com/TanStack/router/pull/7673 +test('#7673: a spontaneous loader AbortError renders the boundary without executing the route component', async () => { window.history.replaceState(null, 'root', '/app') const rootRoute = createRootRoute({}) + const abortError = new DOMException('Aborted', 'AbortError') + const routeComponentRendered = vi.fn() + const renderedError = vi.fn() + let routeSignal: AbortSignal | undefined const indexRoute = createRoute({ getParentRoute: () => rootRoute, path: '/', - loader: async () => { - return Promise.reject(new DOMException('Aborted', 'AbortError')) + loader: async ({ abortController }): Promise<{ value: string }> => { + routeSignal = abortController.signal + return Promise.reject(abortError) + }, + component: () => { + routeComponentRendered() + const data = indexRoute.useLoaderData() + return
    {data().value}
    + }, + errorComponent: ({ error }) => { + renderedError(error) + return
    indexErrorComponent
    }, - component: () =>
    Index route content
    , - errorComponent: () => ( -
    indexErrorComponent
    - ), }) const routeTree = rootRoute.addChildren([indexRoute]) const router = createRouter({ routeTree, basepath: '/app' }) + const rendered = new Promise((resolve) => { + const unsubscribe = router.subscribe('onRendered', () => { + unsubscribe() + resolve() + }) + }) render(() => ) - const indexElement = await screen.findByText('Index route content') - expect(indexElement).toBeInTheDocument() - expect(screen.queryByTestId('index-error')).not.toBeInTheDocument() - expect(window.location.pathname.startsWith('/app')).toBe(true) + expect(await screen.findByTestId('index-error')).toBeInTheDocument() + await rendered + expect(screen.queryByTestId('index-content')).not.toBeInTheDocument() + expect(routeComponentRendered).not.toHaveBeenCalled() + // jsdom creates DOMException in another realm, so Solid wraps the error. + expect(renderedError).toHaveBeenCalledWith( + expect.objectContaining({ + message: 'Unknown error', + cause: abortError, + }), + ) + expect(routeSignal?.aborted).toBe(false) + expect( + router.state.matches.find((match) => match.routeId === indexRoute.id), + ).toMatchObject({ + status: 'error', + error: abortError, + }) + expect(window.location.pathname).toBe('/app') + expect(router.state.status).toBe('idle') }) test('reproducer #4245', async () => { @@ -747,51 +779,6 @@ test('does not show pending UI when loaders finish before their pending delays', expect(fooPendingComponentOnMountMock).not.toHaveBeenCalled() }) -test('useLoaderData retains previous data while route match is pending', async () => { - const history = createMemoryHistory({ initialEntries: ['/app'] }) - const rootRoute = createRootRoute({ - component: () => { - const loaderData = useLoaderData({ from: '/app' }) - - return ( - <> -
    {`${loaderData().length}:0`}
    - - - ) - }, - }) - const appRoute = createRoute({ - getParentRoute: () => rootRoute, - path: '/app', - loader: () => 'loaded', - component: () =>
    App route
    , - }) - const routeTree = rootRoute.addChildren([appRoute]) - const router = createRouter({ routeTree, history }) - - render(() => ) - - expect(await screen.findByTestId('combined')).toHaveTextContent('6:0') - - const appMatch = router.state.matches.find( - (match) => match.routeId === '/app', - ) - - expect(appMatch).toBeDefined() - - if (!appMatch) { - throw new Error('Expected /app match to be active') - } - - router.stores.setPending([{ ...appMatch, id: `${appMatch.id}__pending` }]) - router.stores.setMatches( - router.state.matches.filter((match) => match.routeId !== '/app'), - ) - - expect(screen.getByTestId('combined')).toHaveTextContent('6:0') -}) - test('navigating away from a pending route aborts its loader', async () => { function getPendingComponent(onMount: () => void) { const PendingComponent = () => { diff --git a/packages/solid-router/tests/pending-fallback-promise-replacement.test.tsx b/packages/solid-router/tests/pending-fallback-promise-replacement.test.tsx new file mode 100644 index 0000000000..1b1ac2268c --- /dev/null +++ b/packages/solid-router/tests/pending-fallback-promise-replacement.test.tsx @@ -0,0 +1,179 @@ +import { cleanup, render, screen } from '@solidjs/testing-library' +import { afterEach, expect, test, vi } from 'vitest' +import { createControlledPromise } from '@tanstack/router-core' +import { + Outlet, + RouterProvider, + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, +} from '../src' +import type { AnyRouter } from '../src' + +const testCleanups: Array<() => void | Promise> = [] + +afterEach(async () => { + const pendingCleanups = testCleanups + .splice(0) + .reverse() + .map((testCleanup) => testCleanup()) + if (vi.isFakeTimers()) { + await vi.runAllTimersAsync() + } + await Promise.allSettled(pendingCleanups) + cleanup() + vi.useRealTimers() +}) + +test.each(['child', 'root'] as const)( + 'a mounted %s pending fallback follows an overlapping load generation', + async (routeLevel) => { + const firstReload = createControlledPromise() + const secondReload = createControlledPromise() + const reloads = [firstReload, secondReload] + let loaderCall = 0 + + const routeOptions = { + pendingMs: 0, + pendingMinMs: 100, + pendingComponent: () =>
    Pending
    , + loader: () => { + const generation = ++loaderCall + const gate = reloads[generation - 2] + return gate ? gate.then(() => generation) : generation + }, + } + + const makeRouter = (): AnyRouter => { + if (routeLevel === 'root') { + const rootRoute = createRootRoute({ + ...routeOptions, + component: () =>
    Generation {rootRoute.useLoaderData()()}
    , + }) + return createRouter({ + routeTree: rootRoute, + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + } + + const rootRoute = createRootRoute({ component: () => }) + const pageRoute = createRoute({ + ...routeOptions, + getParentRoute: () => rootRoute, + path: '/page', + component: () =>
    Generation {pageRoute.useLoaderData()()}
    , + }) + return createRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + } + const router = makeRouter() + + render(() => ) + expect(await screen.findByText('Generation 1')).toBeInTheDocument() + + vi.useFakeTimers() + const firstInvalidation = router.invalidate({ forcePending: true }) + const invalidations = [firstInvalidation] + testCleanups.push(async () => { + firstReload.resolve() + secondReload.resolve() + await Promise.allSettled(invalidations) + }) + await vi.advanceTimersByTimeAsync(0) + expect(loaderCall).toBe(2) + expect(screen.getByTestId('pending')).toBeInTheDocument() + expect(screen.queryByText('Generation 1')).not.toBeInTheDocument() + + await vi.advanceTimersByTimeAsync(25) + const secondInvalidation = router.invalidate({ forcePending: true }) + invalidations.push(secondInvalidation) + await vi.advanceTimersByTimeAsync(0) + expect(loaderCall).toBe(3) + + firstReload.resolve() + await vi.advanceTimersByTimeAsync(0) + + expect(screen.getByTestId('pending')).toBeInTheDocument() + expect(screen.queryByText('Generation 2')).not.toBeInTheDocument() + + let secondSettled = false + void secondInvalidation.then(() => { + secondSettled = true + }) + secondReload.resolve() + await vi.advanceTimersByTimeAsync(0) + + expect(secondSettled).toBe(false) + expect(screen.getByTestId('pending')).toBeInTheDocument() + + await vi.advanceTimersByTimeAsync(74) + expect(secondSettled).toBe(false) + expect(screen.getByTestId('pending')).toBeInTheDocument() + + await vi.advanceTimersByTimeAsync(1) + await Promise.all(invalidations) + expect(screen.getByText('Generation 3')).toBeInTheDocument() + expect(screen.queryByText('Generation 1')).not.toBeInTheDocument() + expect(screen.queryByText('Generation 2')).not.toBeInTheDocument() + expect(screen.queryByTestId('pending')).not.toBeInTheDocument() + expect(router.state.status).toBe('idle') + }, +) + +test('forcePending honors pendingMinMs when the reload settles before pendingMs', async () => { + const reload = createControlledPromise() + let loaderCall = 0 + + const rootRoute = createRootRoute({ component: () => }) + const pageRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/page', + pendingMinMs: 100, + pendingComponent: () =>
    Pending
    , + loader: async () => { + if (++loaderCall > 1) { + await reload + } + return loaderCall + }, + component: () =>
    Generation {pageRoute.useLoaderData()()}
    , + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + render(() => ) + expect(await screen.findByText('Generation 1')).toBeInTheDocument() + + vi.useFakeTimers() + const invalidation = router.invalidate({ forcePending: true }) + testCleanups.push(async () => { + reload.resolve() + await Promise.allSettled([invalidation]) + }) + await vi.advanceTimersByTimeAsync(0) + expect(screen.getByTestId('fast-pending')).toBeInTheDocument() + + let settled = false + void invalidation.then(() => { + settled = true + }) + reload.resolve() + await vi.advanceTimersByTimeAsync(0) + + await vi.advanceTimersByTimeAsync(99) + expect(settled).toBe(false) + expect(screen.getByTestId('fast-pending')).toBeInTheDocument() + expect(screen.queryByText('Generation 2')).not.toBeInTheDocument() + + await vi.advanceTimersByTimeAsync(1) + await invalidation + expect(screen.getByText('Generation 2')).toBeInTheDocument() + expect(screen.queryByText('Generation 1')).not.toBeInTheDocument() + expect(screen.queryByTestId('fast-pending')).not.toBeInTheDocument() + expect(router.state.status).toBe('idle') +}) diff --git a/packages/solid-router/tests/redirect.test.tsx b/packages/solid-router/tests/redirect.test.tsx index 34681a8f85..2192f4f962 100644 --- a/packages/solid-router/tests/redirect.test.tsx +++ b/packages/solid-router/tests/redirect.test.tsx @@ -8,7 +8,6 @@ import { Outlet, RouterProvider, createBrowserHistory, - createMemoryHistory, createRootRoute, createRoute, createRouter, @@ -313,116 +312,4 @@ describe('redirect', () => { expect(window.location.pathname).toBe('/final') }) }) - - describe('SSR', () => { - test('when `redirect` is thrown in `beforeLoad`', async () => { - const rootRoute = createRootRoute() - - const indexRoute = createRoute({ - path: '/', - getParentRoute: () => rootRoute, - beforeLoad: () => { - throw redirect({ - to: '/about', - }) - }, - }) - - const aboutRoute = createRoute({ - path: '/about', - getParentRoute: () => rootRoute, - component: () => { - return 'About' - }, - }) - - const router = createRouter({ - routeTree: rootRoute.addChildren([indexRoute, aboutRoute]), - // Mock server mode - isServer: true, - history: createMemoryHistory({ - initialEntries: ['/'], - }), - }) - - await router.load() - - expect(router.state.redirect).toBeDefined() - expect(router.state.redirect).toBeInstanceOf(Response) - const redirectResponse = router.state.redirect! - - expect(redirectResponse.options).toEqual({ - _fromLocation: expect.objectContaining({ - hash: '', - href: '/', - pathname: '/', - search: {}, - searchStr: '', - }), - to: '/about', - href: '/about', - statusCode: 307, - }) - }) - }) - - test('when `redirect` is thrown in `loader`', async () => { - const rootRoute = createRootRoute() - - const indexRoute = createRoute({ - path: '/', - getParentRoute: () => rootRoute, - loader: () => { - throw redirect({ - to: '/about', - }) - }, - }) - - const aboutRoute = createRoute({ - path: '/about', - getParentRoute: () => rootRoute, - component: () => { - return 'About' - }, - }) - - const router = createRouter({ - history: createMemoryHistory({ - initialEntries: ['/'], - }), - routeTree: rootRoute.addChildren([indexRoute, aboutRoute]), - // Mock server mode - isServer: true, - }) - - await router.load() - - const currentRedirect = router.state.redirect - - expect(currentRedirect).toBeDefined() - expect(currentRedirect).toBeInstanceOf(Response) - const redirectResponse = currentRedirect! - expect(redirectResponse.status).toEqual(307) - expect(redirectResponse.headers.get('Location')).toEqual('/about') - expect(redirectResponse.options).toEqual({ - _fromLocation: { - external: false, - publicHref: '/', - hash: '', - href: '/', - pathname: '/', - search: {}, - searchStr: '', - state: { - __TSR_index: 0, - __TSR_key: redirectResponse.options._fromLocation!.state.__TSR_key, - key: redirectResponse.options._fromLocation!.state.key, - }, - }, - href: '/about', - to: '/about', - statusCode: 307, - }) - }) }) diff --git a/packages/solid-router/tests/router-client-stream-cleanup.test.tsx b/packages/solid-router/tests/router-client-stream-cleanup.test.tsx new file mode 100644 index 0000000000..0ea9e57077 --- /dev/null +++ b/packages/solid-router/tests/router-client-stream-cleanup.test.tsx @@ -0,0 +1,39 @@ +import { cleanup, render, screen, waitFor } from '@solidjs/testing-library' +import { afterEach, expect, test, vi } from 'vitest' +import { ErrorBoundary } from 'solid-js' +import { RouterClient } from '../src/ssr/RouterClient' +import { createMemoryHistory, createRootRoute, createRouter } from '../src' + +const hydrate = vi.hoisted(() => vi.fn()) + +vi.mock('@tanstack/router-core/ssr/client', () => ({ hydrate })) + +afterEach(() => { + cleanup() + delete window.$_TSR + hydrate.mockReset() +}) + +test.runIf(typeof window !== 'undefined')( + 'RouterClient signals streaming cleanup without hiding a hydration failure', + async () => { + const error = new Error('hydration failed') + hydrate.mockRejectedValue(error) + const rootRoute = createRootRoute({ component: () =>
    Ready
    }) + const router = createRouter({ + routeTree: rootRoute, + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + const hydrated = vi.fn() + window.$_TSR = { h: hydrated } as any + + render(() => ( + caught.message}> + + + )) + + await waitFor(() => expect(hydrated).toHaveBeenCalledTimes(1)) + expect(await screen.findByText(error.message)).toBeInTheDocument() + }, +) diff --git a/packages/solid-router/tests/store-updates-during-navigation.test.tsx b/packages/solid-router/tests/store-updates-during-navigation.test.tsx index 69b570122d..3ef09b437f 100644 --- a/packages/solid-router/tests/store-updates-during-navigation.test.tsx +++ b/packages/solid-router/tests/store-updates-during-navigation.test.tsx @@ -136,7 +136,7 @@ describe("Store doesn't update *too many* times during navigation", () => { // This number should be as small as possible to minimize the amount of work // that needs to be done during a navigation. // Any change that increases this number should be investigated. - expect(updates).toBe(9) + expect(updates).toBe(6) }) test('redirection in preload', async () => { @@ -172,7 +172,7 @@ describe("Store doesn't update *too many* times during navigation", () => { // that needs to be done during a navigation. // Any change that increases this number should be investigated. // Note: Solid has different update counts than React due to different reactivity - expect(updates).toBe(5) + expect(updates).toBe(4) }) test('nothing', async () => { @@ -183,7 +183,7 @@ describe("Store doesn't update *too many* times during navigation", () => { // This number should be as small as possible to minimize the amount of work // that needs to be done during a navigation. // Any change that increases this number should be investigated. - expect(updates).toBe(3) + expect(updates).toBe(2) }) test('not found in beforeLoad', async () => { @@ -198,7 +198,7 @@ describe("Store doesn't update *too many* times during navigation", () => { // This number should be as small as possible to minimize the amount of work // that needs to be done during a navigation. // Any change that increases this number should be investigated. - expect(updates).toBe(4) + expect(updates).toBe(2) }) test('hover preload, then navigate, w/ async loaders', async () => { @@ -240,7 +240,7 @@ describe("Store doesn't update *too many* times during navigation", () => { // This number should be as small as possible to minimize the amount of work // that needs to be done during a navigation. // Any change that increases this number should be investigated. - expect(updates).toBe(3) + expect(updates).toBe(2) }) test('navigate, w/ preloaded & sync loaders', async () => { @@ -256,7 +256,7 @@ describe("Store doesn't update *too many* times during navigation", () => { // This number should be as small as possible to minimize the amount of work // that needs to be done during a navigation. // Any change that increases this number should be investigated. - expect(updates).toBe(3) + expect(updates).toBe(2) }) test('navigate, w/ previous navigation & async loader', async () => { @@ -272,7 +272,7 @@ describe("Store doesn't update *too many* times during navigation", () => { // This number should be as small as possible to minimize the amount of work // that needs to be done during a navigation. // Any change that increases this number should be investigated. - expect(updates).toBe(3) + expect(updates).toBe(2) }) test('preload a preloaded route w/ async loader', async () => { diff --git a/packages/solid-router/tests/transitioner-listener-errors.test.tsx b/packages/solid-router/tests/transitioner-listener-errors.test.tsx new file mode 100644 index 0000000000..a5c6765d09 --- /dev/null +++ b/packages/solid-router/tests/transitioner-listener-errors.test.tsx @@ -0,0 +1,80 @@ +import { cleanup, render, screen } from '@solidjs/testing-library' +import { afterEach, expect, onTestFinished, test } from 'vitest' +import { + Outlet, + RouterProvider, + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, +} from '../src' + +afterEach(() => { + cleanup() +}) + +test('a throwing load-event listener cannot interrupt route hooks or later navigations', async () => { + const lifecycle: Array = [] + const listenerError = new Error('onLoad listener failed') + + const rootRoute = createRootRoute({ component: () => }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
    Index route
    , + }) + const firstRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/first', + onEnter: () => lifecycle.push('enter:/first'), + component: () =>
    First route
    , + }) + const secondRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/second', + onEnter: () => lifecycle.push('enter:/second'), + component: () =>
    Second route
    , + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute, firstRoute, secondRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + render(() => ) + expect(await screen.findByText('Index route')).toBeInTheDocument() + + const unsubscribers = [ + router.subscribe('onLoad', (event) => { + if (event.toLocation.pathname === '/first') { + lifecycle.push('throw:/first') + throw listenerError + } + }), + router.subscribe('onLoad', (event) => { + if (event.toLocation.pathname !== '/') { + lifecycle.push(`load:${event.toLocation.pathname}`) + } + }), + ] + onTestFinished(() => { + for (const unsubscribe of unsubscribers) { + unsubscribe() + } + }) + + await router.navigate({ to: '/first' }) + expect(await screen.findByText('First route')).toBeInTheDocument() + expect(screen.queryByText('Index route')).not.toBeInTheDocument() + expect(lifecycle).toEqual(['enter:/first', 'throw:/first', 'load:/first']) + + await router.navigate({ to: '/second' }) + expect(await screen.findByText('Second route')).toBeInTheDocument() + expect(screen.queryByText('First route')).not.toBeInTheDocument() + expect(lifecycle).toEqual([ + 'enter:/first', + 'throw:/first', + 'load:/first', + 'enter:/second', + 'load:/second', + ]) +}) diff --git a/packages/solid-router/tests/transitioner-render-ack.test.tsx b/packages/solid-router/tests/transitioner-render-ack.test.tsx new file mode 100644 index 0000000000..03ac23a332 --- /dev/null +++ b/packages/solid-router/tests/transitioner-render-ack.test.tsx @@ -0,0 +1,333 @@ +import { cleanup, render, screen, waitFor } from '@solidjs/testing-library' +import { afterEach, expect, onTestFinished, test } from 'vitest' +import { + Outlet, + RouterProvider, + createControlledPromise, + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, +} from '../src' +import type { HistoryState } from '../src' + +afterEach(() => { + cleanup() +}) + +test('onResolved precedes onRendered and both observe the committed destination DOM', async () => { + const nextLoader = createControlledPromise() + const rootRoute = createRootRoute({ component: () => }) + const firstRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/first', + component: () =>
    First
    , + }) + const nextRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/next', + loader: () => nextLoader, + component: () =>
    Next
    , + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([firstRoute, nextRoute]), + history: createMemoryHistory({ initialEntries: ['/first'] }), + }) + + render(() => ) + expect(await screen.findByText('First')).toBeInTheDocument() + await waitFor(() => expect(router.state.status).toBe('idle')) + + const lifecycle: Array<{ + event: 'onResolved' | 'onRendered' + pathname: string + destination: boolean + outgoing: boolean + }> = [] + const unsubscribers = [ + router.subscribe('onResolved', (event) => { + lifecycle.push({ + event: 'onResolved', + pathname: event.toLocation.pathname, + destination: screen.queryByText('Next') !== null, + outgoing: screen.queryByText('First') !== null, + }) + }), + router.subscribe('onRendered', (event) => { + lifecycle.push({ + event: 'onRendered', + pathname: event.toLocation.pathname, + destination: screen.queryByText('Next') !== null, + outgoing: screen.queryByText('First') !== null, + }) + }), + ] + onTestFinished(() => { + nextLoader.resolve() + for (const unsubscribe of unsubscribers) { + unsubscribe() + } + }) + + const navigation = router.navigate({ to: '/next' }) + + expect(screen.queryByText('Next')).toBeNull() + expect(screen.getByText('First')).toBeInTheDocument() + expect(lifecycle).toEqual([]) + + nextLoader.resolve() + await navigation + await waitFor(() => + expect(lifecycle).toEqual([ + { + event: 'onResolved', + pathname: '/next', + destination: true, + outgoing: false, + }, + { + event: 'onRendered', + pathname: '/next', + destination: true, + outgoing: false, + }, + ]), + ) +}) + +test('onRendered describes each committed navigation from the previously rendered location', async () => { + type SameHrefTestState = HistoryState & { sameHrefState: true } + const isSameHrefTestState = ( + state: HistoryState | undefined, + ): state is SameHrefTestState => + state !== undefined && + 'sameHrefState' in state && + state.sameHrefState === true + + const rootRoute = createRootRoute({ component: () => }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
    Index
    , + }) + const nextRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/next', + component: () =>
    Next
    , + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute, nextRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + render(() => ) + expect(await screen.findByText('Index')).toBeInTheDocument() + await waitFor(() => expect(router.state.status).toBe('idle')) + + const renderedChanges: Array<{ + fromPathname: string | undefined + toPathname: string + fromHref: string | undefined + toHref: string + pathChanged: boolean + hrefChanged: boolean + fromSameHrefState: boolean + toSameHrefState: boolean + renderedRoute: 'Index' | 'Next' | undefined + }> = [] + const unsubscribe = router.subscribe('onRendered', (event) => { + renderedChanges.push({ + fromPathname: event.fromLocation?.pathname, + toPathname: event.toLocation.pathname, + fromHref: event.fromLocation?.href, + toHref: event.toLocation.href, + pathChanged: event.pathChanged, + hrefChanged: event.hrefChanged, + fromSameHrefState: isSameHrefTestState(event.fromLocation?.state), + toSameHrefState: isSameHrefTestState(event.toLocation.state), + renderedRoute: screen.queryByText('Next') + ? 'Next' + : screen.queryByText('Index') + ? 'Index' + : undefined, + }) + }) + onTestFinished(unsubscribe) + + await router.navigate({ to: '/next' }) + expect(await screen.findByText('Next')).toBeInTheDocument() + await waitFor(() => + expect(renderedChanges).toEqual([ + { + fromPathname: '/', + toPathname: '/next', + fromHref: '/', + toHref: '/next', + pathChanged: true, + hrefChanged: true, + fromSameHrefState: false, + toSameHrefState: false, + renderedRoute: 'Next', + }, + ]), + ) + + const sameHrefState: SameHrefTestState = { sameHrefState: true } + await router.navigate({ + to: '/next', + state: sameHrefState, + }) + await waitFor(() => + expect(renderedChanges).toEqual([ + { + fromPathname: '/', + toPathname: '/next', + fromHref: '/', + toHref: '/next', + pathChanged: true, + hrefChanged: true, + fromSameHrefState: false, + toSameHrefState: false, + renderedRoute: 'Next', + }, + { + fromPathname: '/next', + toPathname: '/next', + fromHref: '/next', + toHref: '/next', + pathChanged: false, + hrefChanged: false, + fromSameHrefState: false, + toSameHrefState: true, + renderedRoute: 'Next', + }, + ]), + ) +}) + +test('an older rendered destination cannot resolve a superseding navigation', async () => { + const nextLoader = createControlledPromise() + const rootRoute = createRootRoute({ component: () => }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
    Index
    , + }) + const firstRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/first', + component: () =>
    First
    , + }) + const nextRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/next', + loader: () => nextLoader, + component: () =>
    Next
    , + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute, firstRoute, nextRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + render(() => ) + expect(await screen.findByText('Index')).toBeInTheDocument() + await waitFor(() => expect(router.state.status).toBe('idle')) + + const lifecycle: Array<{ + event: 'onResolved' | 'onRendered' + pathname: string + destination: boolean + superseded: boolean + }> = [] + let nextNavigation: Promise | undefined + const unsubscribers = [ + router.subscribe('onLoad', (event) => { + if (event.toLocation.pathname === '/first') { + nextNavigation = router.navigate({ to: '/next' }) + } + }), + router.subscribe('onResolved', (event) => { + lifecycle.push({ + event: 'onResolved', + pathname: event.toLocation.pathname, + destination: screen.queryByText('Next') !== null, + superseded: screen.queryByText('First') !== null, + }) + }), + router.subscribe('onRendered', (event) => { + lifecycle.push({ + event: 'onRendered', + pathname: event.toLocation.pathname, + destination: screen.queryByText('Next') !== null, + superseded: screen.queryByText('First') !== null, + }) + }), + ] + onTestFinished(() => { + nextLoader.resolve() + for (const unsubscribe of unsubscribers) { + unsubscribe() + } + }) + + const firstNavigation = router.navigate({ to: '/first' }) + + await waitFor(() => { + expect(nextNavigation).toBeDefined() + }) + expect(screen.getByText('First')).toBeInTheDocument() + expect(screen.queryByText('Next')).not.toBeInTheDocument() + expect(lifecycle).toEqual([]) + + nextLoader.resolve() + await Promise.all([firstNavigation, nextNavigation!]) + + await waitFor(() => { + expect(screen.getByText('Next')).toBeInTheDocument() + }) + expect(lifecycle).toEqual([ + { + event: 'onResolved', + pathname: '/next', + destination: true, + superseded: false, + }, + { + event: 'onRendered', + pathname: '/next', + destination: true, + superseded: false, + }, + ]) +}) + +test('same-location invalidation resolves after its refreshed DOM commits', async () => { + let generation = 0 + const rootRoute = createRootRoute({ component: () => }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + loader: { + staleReloadMode: 'blocking', + handler: () => ++generation, + }, + component: () =>
    Generation {indexRoute.useLoaderData()()}
    , + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + render(() => ) + expect(await screen.findByText('Generation 1')).toBeInTheDocument() + + const refreshedDomWasVisible: Array = [] + const unsubscribe = router.subscribe('onResolved', () => { + refreshedDomWasVisible.push(screen.queryByText('Generation 2') !== null) + }) + onTestFinished(unsubscribe) + + await router.invalidate() + expect(await screen.findByText('Generation 2')).toBeInTheDocument() + expect(refreshedDomWasVisible).toEqual([true]) +}) diff --git a/packages/solid-start-client/src/hydrateStart.ts b/packages/solid-start-client/src/hydrateStart.ts index e4299cb9c7..cb353576c0 100644 --- a/packages/solid-start-client/src/hydrateStart.ts +++ b/packages/solid-start-client/src/hydrateStart.ts @@ -4,9 +4,6 @@ import type { AnyRouter } from '@tanstack/router-core' /** * Solid-specific wrapper for hydrateStart that signals hydration completion */ -export async function hydrateStart(): Promise { - const router = await coreHydrateStart() - // Signal that router hydration is complete so cleanup can happen if stream has ended - window.$_TSR?.h() - return router +export function hydrateStart(): Promise { + return coreHydrateStart().finally(() => window.$_TSR?.h()) } diff --git a/packages/solid-start-client/src/tests/hydrateStart.test.ts b/packages/solid-start-client/src/tests/hydrateStart.test.ts index 0610a8fa0c..2d34400009 100644 --- a/packages/solid-start-client/src/tests/hydrateStart.test.ts +++ b/packages/solid-start-client/src/tests/hydrateStart.test.ts @@ -21,3 +21,13 @@ test('signals streaming cleanup after hydration succeeds', async () => { await expect(hydrateStart()).resolves.toBe(router) expect(hydrated).toHaveBeenCalledTimes(1) }) + +test('signals streaming cleanup without hiding a hydration failure', async () => { + const error = new Error('hydration failed') + coreHydrateStart.mockRejectedValue(error) + const hydrated = vi.fn() + window.$_TSR = { h: hydrated } as any + + await expect(hydrateStart()).rejects.toBe(error) + expect(hydrated).toHaveBeenCalledTimes(1) +}) diff --git a/packages/start-client-core/src/client/hydrateStart.ts b/packages/start-client-core/src/client/hydrateStart.ts index 83b9c468af..d3742e5ff6 100644 --- a/packages/start-client-core/src/client/hydrateStart.ts +++ b/packages/start-client-core/src/client/hydrateStart.ts @@ -52,7 +52,7 @@ async function hydrateStart(): Promise { basepath: process.env.TSS_ROUTER_BASEPATH, ...{ serializationAdapters }, }) - if (!router.stores.matchesId.get().length) { + if (!router.stores.ids.get().length) { await hydrate(router) } diff --git a/packages/start-plugin-core/src/vite/dev-server-plugin/plugin.ts b/packages/start-plugin-core/src/vite/dev-server-plugin/plugin.ts index 18d3e54e2e..064c7623d4 100644 --- a/packages/start-plugin-core/src/vite/dev-server-plugin/plugin.ts +++ b/packages/start-plugin-core/src/vite/dev-server-plugin/plugin.ts @@ -63,7 +63,7 @@ export function devServerPlugin({ // Parse route IDs from query param const urlObj = new URL(url, 'http://localhost') const routesParam = urlObj.searchParams.get('routes') - const routeIds = routesParam ? routesParam.split(',') : [] + const ids = routesParam ? routesParam.split(',') : [] // Build entries list from route file paths const entries: Array = [] @@ -74,8 +74,8 @@ export function devServerPlugin({ | Record }> | undefined - if (routesManifest && routeIds.length > 0) { - for (const routeId of routeIds) { + if (routesManifest && ids.length > 0) { + for (const routeId of ids) { const route = routesManifest[routeId] if (route?.filePath) { entries.push(route.filePath) diff --git a/packages/start-server-core/src/createStartHandler.ts b/packages/start-server-core/src/createStartHandler.ts index 90d4e706f9..20c4763137 100644 --- a/packages/start-server-core/src/createStartHandler.ts +++ b/packages/start-server-core/src/createStartHandler.ts @@ -8,18 +8,22 @@ import { safeObjectMerge, } from '@tanstack/start-client-core' import { + _getRenderedMatches, executeRewriteInput, isRedirect, isResolvedRedirect, } from '@tanstack/router-core' import { attachRouterServerSsrUtils, + bindSsrResponseToRequest, + disposeSsrResponseDetached, getNormalizedURL, getOrigin, isSsrResponse, normalizeSsrResponse, replaceSsrResponse, stripSsrResponseBody, + waitForRequest, } from '@tanstack/router-core/ssr/server' import { getStartContext, @@ -74,7 +78,7 @@ function getStartResponseHeaders(opts: { router: AnyRouter }) { { 'Content-Type': 'text/html; charset=utf-8', }, - ...opts.router.stores.matches.get().map((match) => { + ..._getRenderedMatches(opts.router.stores.matches.get()).map((match) => { return match.headers }), ) @@ -208,17 +212,36 @@ function handleCtxResult(result: TODO) { return result } +function disposeLateResponse(result: TODO, signal: AbortSignal): void { + const response = handleCtxResult(result)?.response + if (isSsrResponse(response) || isSpecialResponse(response)) { + disposeSsrResponseDetached(response, signal.reason) + } +} + +function isSignalAborted(signal: AbortSignal): boolean { + return signal.aborted +} + /** * Execute a middleware chain */ async function executeMiddleware( middlewares: Array, ctx: TODO, + signal: AbortSignal, ): Promise<{ ctx: TODO; response: HandlerCallbackResult }> { let index = -1 let streamResponse: | Extract | undefined + let retiredStreamIdentities: WeakSet | undefined + + const isResponseAlias = (candidate: unknown, response: Response) => + candidate === response || + (candidate instanceof Response && + response.body !== null && + candidate.body === response.body) const setResponse = (response: TODO) => { if (isSsrResponse(response)) { @@ -232,25 +255,44 @@ async function executeMiddleware( ctx.response = response } - const disposeStreamResponse = async (reason: string) => { + const disposeStreamResponse = async (reason: unknown) => { const response = streamResponse if (!response) { return } streamResponse = undefined + retiredStreamIdentities ??= new WeakSet() + retiredStreamIdentities.add(response.response) + if (response.response.body) { + retiredStreamIdentities.add(response.response.body) + } const currentResponse = ctx.response - if ( - currentResponse === response.response || - (currentResponse instanceof Response && - response.response.body !== null && - currentResponse.body === response.response.body) - ) { + if (isResponseAlias(currentResponse, response.response)) { ctx.response = undefined } await response.dispose(reason) } + const disposeAbandonedResult = (result: TODO) => { + const exposed = handleCtxResult(result)?.response + const response = isSsrResponse(exposed) ? exposed.response : exposed + if (streamResponse && isResponseAlias(response, streamResponse.response)) { + void disposeStreamResponse(signal.reason).catch(console.error) + return + } + if ( + response instanceof Response && + retiredStreamIdentities && + (retiredStreamIdentities.has(response) || + (response.body !== null && retiredStreamIdentities.has(response.body))) + ) { + return + } + + disposeLateResponse(result, signal) + } + const getFinalResponse = async (): Promise => { const response = ctx.response if (!response) { @@ -276,7 +318,19 @@ async function executeMiddleware( return response } - const next = async (nextCtx?: TODO): Promise => { + let nextPromise: Promise | undefined + + function next(nextCtx?: TODO): Promise { + const result = runNext(nextCtx) + nextPromise = result + return result + } + + async function runNext(nextCtx?: TODO): Promise { + if (signal.aborted) { + throw signal.reason + } + // Merge context if provided using safeObjectMerge for prototype pollution prevention if (nextCtx) { if (nextCtx.context) { @@ -298,13 +352,26 @@ async function executeMiddleware( let result: TODO try { - result = await middleware({ ...ctx, next }) + const pending = middleware({ ...ctx, next }) + // A directly returned next() promise already propagates request aborts. + if (pending === nextPromise) { + nextPromise = undefined + result = await pending + if (isSignalAborted(signal)) { + disposeAbandonedResult(result) + throw signal.reason + } + } else { + result = await waitForRequest(pending, signal, disposeAbandonedResult) + } } catch (err) { + if (isSignalAborted(signal)) { + throw signal.reason + } if (isSpecialResponse(err)) { setResponse(err) return ctx } - await disposeStreamResponse('middleware error') throw err } @@ -321,8 +388,27 @@ async function executeMiddleware( return ctx } - await next() - return { ctx, response: await getFinalResponse() } + try { + await runNext() + const response = await waitForRequest( + getFinalResponse(), + signal, + disposeAbandonedResult, + ) + if (signal.aborted) { + disposeAbandonedResult(response) + throw signal.reason + } + return { ctx, response } + } catch (err) { + const disposal = disposeStreamResponse(signal.aborted ? signal.reason : err) + if (signal.aborted) { + void disposal.catch(console.error) + } else { + await disposal + } + throw err + } } /** @@ -404,6 +490,7 @@ export function createStartHandler( let responseOwnsCleanup = false as boolean try { + request.signal.throwIfAborted() // normalizing and sanitizing the pathname here for server, so we always deal with the same format during SSR. // during normalization paths like '//posts' are flattened to '/posts'. // in these cases we would prefer to redirect to the new path @@ -415,11 +502,13 @@ export function createStartHandler( return Response.redirect(url, 308) } - const entries = await getEntries() + const entries = await waitForRequest(getEntries(), request.signal) const hasStartInstance = !!entries.startEntry.startInstance const startOptions: AnyStartInstanceOptions = - (await entries.startEntry.startInstance?.getOptions()) || - ({} as AnyStartInstanceOptions) + (await waitForRequest( + entries.startEntry.startInstance?.getOptions(), + request.signal, + )) || ({} as AnyStartInstanceOptions) const { hasPluginAdapters, pluginSerializationAdapters } = entries.pluginAdapters @@ -452,7 +541,10 @@ export function createStartHandler( const getRouter = async (): Promise => { if (router) return router - router = await entries.routerEntry.getRouter() + router = await waitForRequest( + entries.routerEntry.getRouter(), + request.signal, + ) let isShell = IS_SHELL_ENV if (IS_PRERENDERING && !isShell) { @@ -529,13 +621,17 @@ export function createStartHandler( handlerType: 'serverFn', context: createNullProtoObject(requestOpts?.context), }, + request.signal, ) const result = await handleRedirectResponse( middlewareResponse, request, getRouter, + request.signal, ) + bindSsrResponseToRequest(router ?? undefined, result, request.signal) + request.signal.throwIfAborted() responseOwnsCleanup = result.serverSsrCleanup === 'stream' return result.response } @@ -562,11 +658,14 @@ export function createStartHandler( ) } - const manifest = await resolveManifestForRequest({ - request, - requestInlineCss: requestOpts?.inlineCss, - getBaseManifest: () => getBaseManifest(matchedRoutes), - }) + const manifest = await waitForRequest( + resolveManifestForRequest({ + request, + requestInlineCss: requestOpts?.inlineCss, + getBaseManifest: () => getBaseManifest(matchedRoutes), + }), + request.signal, + ) const earlyHints = createEarlyHintsForRequest({ onEarlyHints: requestOpts?.onEarlyHints, @@ -587,29 +686,41 @@ export function createStartHandler( // `additionalContext` is request-scoped and only read from router.options // during load; avoid a full router.update() and redundant location parse. routerInstance.options.additionalContext = { serverContext } - await routerInstance.load() + await routerInstance.load({ _signal: request.signal }) + request.signal.throwIfAborted() - if (routerInstance.state.redirect) { - return normalizeSsrResponse(routerInstance.state.redirect) + if (routerInstance._serverResult?.type === 'redirect') { + return normalizeSsrResponse(routerInstance._serverResult.redirect) } - earlyHints?.collectDynamic(routerInstance.stores.matches.get()) + earlyHints?.collectDynamic( + _getRenderedMatches(routerInstance.stores.matches.get()), + ) // Pass request-scoped assets to dehydrate for manifest injection const ctx = getStartContext({ throwIfNotFound: false }) - await routerInstance.serverSsr!.dehydrate({ - requestAssets: ctx?.requestAssets, - }) + await waitForRequest( + routerInstance.serverSsr!.dehydrate({ + requestAssets: ctx?.requestAssets, + }), + request.signal, + ) + request.signal.throwIfAborted() const responseHeaders = getStartResponseHeaders({ router: routerInstance, }) earlyHints?.appendResponseHeaders(responseHeaders) - const response = await cb({ - request, - router: routerInstance, - responseHeaders, - }) + request.signal.throwIfAborted() + const response = await waitForRequest( + cb({ + request, + router: routerInstance, + responseHeaders, + }), + request.signal, + (late) => disposeLateResponse(late, request.signal), + ) return normalizeSsrResponse(response) } @@ -655,13 +766,17 @@ export function createStartHandler( handlerType: 'router', context: createNullProtoObject(requestOpts?.context), }, + request.signal, ) const response = await handleRedirectResponse( middlewareResponse, request, getRouter, + request.signal, ) + bindSsrResponseToRequest(router ?? undefined, response, request.signal) + request.signal.throwIfAborted() responseOwnsCleanup = response.serverSsrCleanup === 'stream' return response.response } finally { @@ -682,7 +797,9 @@ async function handleRedirectResponse( response: HandlerCallbackResult, request: Request, getRouter: () => Promise, + signal: AbortSignal, ): Promise { + signal.throwIfAborted() const ssrResponse = normalizeSsrResponse(response) if (!isRedirect(ssrResponse.response)) { return ssrResponse @@ -690,13 +807,16 @@ async function handleRedirectResponse( if (isResolvedRedirect(ssrResponse.response)) { if (request.headers.get('x-tsr-serverFn') === 'true') { - return replaceSsrResponse( - ssrResponse, - Response.json( - { ...ssrResponse.response.options, isSerializedRedirect: true }, - { headers: ssrResponse.response.headers }, + return waitForRequest( + replaceSsrResponse( + ssrResponse, + Response.json( + { ...ssrResponse.response.options, isSerializedRedirect: true }, + { headers: ssrResponse.response.headers }, + ), + 'redirect response replaced', ), - 'redirect response replaced', + signal, ) } return ssrResponse @@ -724,21 +844,29 @@ async function handleRedirectResponse( ) } - const router = await getRouter() + signal.throwIfAborted() + const router = await waitForRequest(getRouter(), signal) + signal.throwIfAborted() const redirect = router.resolveRedirect(ssrResponse.response) if (request.headers.get('x-tsr-serverFn') === 'true') { - return replaceSsrResponse( - ssrResponse, - Response.json( - { ...ssrResponse.response.options, isSerializedRedirect: true }, - { headers: ssrResponse.response.headers }, + return waitForRequest( + replaceSsrResponse( + ssrResponse, + Response.json( + { ...ssrResponse.response.options, isSerializedRedirect: true }, + { headers: ssrResponse.response.headers }, + ), + 'redirect response replaced', ), - 'redirect response replaced', + signal, ) } - return replaceSsrResponse(ssrResponse, redirect, 'redirect response replaced') + return waitForRequest( + replaceSsrResponse(ssrResponse, redirect, 'redirect response replaced'), + signal, + ) } async function handleServerRoutes({ @@ -831,13 +959,17 @@ async function handleServerRoutes({ routeMiddlewares.push(((ctx: TODO) => executeRouter(ctx.context, matchedRoutes)) as TODO) - const { ctx, response } = await executeMiddleware(routeMiddlewares, { - request, - context, - params: routeParams, - pathname, - handlerType: 'router', - }) + const { ctx, response } = await executeMiddleware( + routeMiddlewares, + { + request, + context, + params: routeParams, + pathname, + handlerType: 'router', + }, + request.signal, + ) // RFC 9110 §9.3.2: HEAD must carry the same header fields as GET but no body. // Resolve any redirect before stripping so the Location header survives. @@ -846,8 +978,16 @@ async function handleServerRoutes({ throwRouteHandlerError() } - const resolved = await handleRedirectResponse(response, request, getRouter) - return stripSsrResponseBody(resolved, 'HEAD body stripped') + const resolved = await handleRedirectResponse( + response, + request, + getRouter, + request.signal, + ) + return waitForRequest( + stripSsrResponseBody(resolved, 'HEAD body stripped'), + request.signal, + ) } return normalizeSsrResponse(response) diff --git a/packages/start-server-core/tests/createStartHandler.test.ts b/packages/start-server-core/tests/createStartHandler.test.ts index 2e0011ac0b..dbcc169cb5 100644 --- a/packages/start-server-core/tests/createStartHandler.test.ts +++ b/packages/start-server-core/tests/createStartHandler.test.ts @@ -1,9 +1,22 @@ // @vitest-environment node -import { afterAll, afterEach, describe, expect, it, vi } from 'vitest' +import { + afterAll, + afterEach, + describe, + expect, + it, + onTestFinished, + vi, +} from 'vitest' import { createMemoryHistory } from '@tanstack/history' import { createMiddleware } from '@tanstack/start-client-core' -import { BaseRootRoute, BaseRoute, RouterCore } from '@tanstack/router-core' +import { + BaseRootRoute, + BaseRoute, + RouterCore, + type AnyRouter, +} from '@tanstack/router-core' import { createNonReactiveMutableStore, createNonReactiveReadonlyStore, @@ -25,7 +38,7 @@ const startMocks = vi.hoisted(() => { previousServerFnBase, requestMiddleware: [] as Array, serverFnResult: undefined as undefined | Response | object, - router: undefined as undefined | ReturnType, + router: undefined as undefined | AnyRouter, } }) @@ -70,6 +83,38 @@ function makeRouter() { return router } +function makeRouterWithRouteWork(routeWork: { + beforeLoad?: (ctx: { abortController: AbortController }) => unknown + loader?: (ctx: { abortController: AbortController }) => unknown +}) { + const rootRoute = new BaseRootRoute({}) + const workRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/work', + component: () => null, + ...routeWork, + }) + const router = new RouterCore( + { + history: createMemoryHistory({ initialEntries: ['/work'] }), + routeTree: rootRoute.addChildren([workRoute]), + }, + getStoreConfig, + ) + router.isServer = true + return router +} + +function waitForAbortOrRelease(signal: AbortSignal) { + return new Promise((resolve) => { + const release = () => { + signal.removeEventListener('abort', release) + resolve() + } + signal.addEventListener('abort', release, { once: true }) + }) +} + function makeStreamResponse(router: ReturnType) { attachRouterServerSsrUtils({ router: router as any, manifest: undefined }) const stream = new ReadableStream({ @@ -327,6 +372,632 @@ describe('createStartHandler SSR cleanup ownership', () => { }) }) +describe('createStartHandler request cancellation', () => { + it.each(['beforeLoad', 'loader'] as const)( + 'aborts route %s work and does not render HTML', + async (hook) => { + let routeSignal: AbortSignal | undefined + let notifyStarted: (() => void) | undefined + const started = new Promise((resolve) => { + notifyStarted = resolve + }) + const routeWork = ({ + abortController, + }: { + abortController: AbortController + }) => { + routeSignal = abortController.signal + notifyStarted?.() + return waitForAbortOrRelease(abortController.signal) + } + const router = makeRouterWithRouteWork({ [hook]: routeWork }) + startMocks.router = router + const requestController = new AbortController() + const render = vi.fn(() => new Response('must not render')) + const handler = createStartHandler(render) + const response = handler( + new Request('http://localhost/work', { + signal: requestController.signal, + }), + {}, + ) + + await started + const cancellation = new Error('request disconnected') + requestController.abort(cancellation) + + expect((await response).status).toBe(500) + expect(routeSignal?.aborted).toBe(true) + expect(routeSignal?.reason).toBe(cancellation) + expect(render).not.toHaveBeenCalled() + }, + ) + + it('settles and cleans up while the render callback is still pending', async () => { + const router = makeRouter() + startMocks.router = router + const requestController = new AbortController() + let notifyRenderStarted!: () => void + const renderStarted = new Promise((resolve) => { + notifyRenderStarted = resolve + }) + let resolveRender!: ( + value: ReturnType, + ) => void + const renderResult = new Promise< + ReturnType + >((resolve) => { + resolveRender = resolve + }) + let cleanupCalls = 0 + let cancelCalls = 0 + let lateStreamResponse!: ReturnType + const handler = createStartHandler(({ router: requestRouter }) => { + const serverSsr = requestRouter.serverSsr! + const cleanup = serverSsr.cleanup + serverSsr.cleanup = () => { + cleanupCalls++ + cleanup() + } + lateStreamResponse = createSsrStreamResponse( + requestRouter, + new Response( + new ReadableStream({ + cancel() { + cancelCalls++ + return new Promise(() => {}) + }, + }), + ), + ) + notifyRenderStarted() + return renderResult + }) + const response = handler( + new Request('http://localhost/', { + signal: requestController.signal, + }), + {}, + ) + + await renderStarted + requestController.abort(new Error('request disconnected')) + + expect((await response).status).toBe(500) + expect(cleanupCalls).toBe(1) + expect(router.serverSsr).toBeUndefined() + + resolveRender(lateStreamResponse) + await Promise.resolve() + await Promise.resolve() + expect(cleanupCalls).toBe(1) + expect(cancelCalls).toBe(1) + expect(router.serverSsr).toBeUndefined() + }) + + it('cancels a plain response resolved by the render callback later', async () => { + const router = makeRouter() + startMocks.router = router + const requestController = new AbortController() + let notifyRenderStarted!: () => void + const renderStarted = new Promise((resolve) => { + notifyRenderStarted = resolve + }) + let resolveRender!: (value: Response) => void + const renderResult = new Promise((resolve) => { + resolveRender = resolve + }) + const cancel = vi.fn((_reason: unknown) => new Promise(() => {})) + const handler = createStartHandler(() => { + notifyRenderStarted() + return renderResult + }) + const response = handler( + new Request('http://localhost/', { + signal: requestController.signal, + }), + {}, + ) + + await renderStarted + const cancellation = new Error('request disconnected') + requestController.abort(cancellation) + + expect((await response).status).toBe(500) + resolveRender(new Response(new ReadableStream({ cancel }))) + await vi.waitFor(() => { + expect(cancel).toHaveBeenCalledTimes(1) + expect(cancel).toHaveBeenCalledWith(cancellation) + }) + }) + + it('cancels a plain response resolved by request middleware later', async () => { + const router = makeRouter() + startMocks.router = router + const requestController = new AbortController() + let notifyMiddlewareStarted!: () => void + const middlewareStarted = new Promise((resolve) => { + notifyMiddlewareStarted = resolve + }) + let resolveMiddleware!: (value: Response) => void + const middlewareResult = new Promise((resolve) => { + resolveMiddleware = resolve + }) + const cancel = vi.fn((_reason: unknown) => new Promise(() => {})) + startMocks.requestMiddleware = [ + createMiddleware().server(() => { + notifyMiddlewareStarted() + return middlewareResult + }), + ] + const handler = createStartHandler(() => new Response('must not render')) + const response = handler( + new Request('http://localhost/', { + signal: requestController.signal, + }), + {}, + ) + + await middlewareStarted + const cancellation = new Error('request disconnected') + requestController.abort(cancellation) + + expect((await response).status).toBe(500) + resolveMiddleware(new Response(new ReadableStream({ cancel }))) + await vi.waitFor(() => { + expect(cancel).toHaveBeenCalledTimes(1) + expect(cancel).toHaveBeenCalledWith(cancellation) + }) + }) + + it.each(['throw', 'reject'] as const)( + 'reports a %s from disposal of a late render response', + async (failureMode) => { + const router = makeRouter() + startMocks.router = router + const requestController = new AbortController() + const cleanupError = new Error('late stream cleanup failed') + const dispose = vi.fn(() => { + if (failureMode === 'throw') { + throw cleanupError + } + return Promise.reject(cleanupError) + }) + const consoleError = vi + .spyOn(console, 'error') + .mockImplementation(() => undefined) + let notifyRenderStarted!: () => void + const renderStarted = new Promise((resolve) => { + notifyRenderStarted = resolve + }) + let resolveRender!: (value: any) => void + const renderResult = new Promise((resolve) => { + resolveRender = resolve + }) + onTestFinished(() => { + consoleError.mockRestore() + }) + onTestFinished(() => { + router.serverSsr?.cleanup() + }) + + const handler = createStartHandler(() => { + notifyRenderStarted() + return renderResult + }) + const response = handler( + new Request('http://localhost/', { + signal: requestController.signal, + }), + {}, + ) + + await renderStarted + requestController.abort(new Error('request disconnected')) + expect((await response).status).toBe(500) + + resolveRender({ + response: new Response('stream'), + serverSsrCleanup: 'stream', + dispose, + }) + await vi.waitFor(() => { + expect(consoleError).toHaveBeenCalledWith(cleanupError) + }) + expect(dispose).toHaveBeenCalledOnce() + }, + ) + + it.each(['throw', 'reject'] as const)( + 'reports a stream disposal %s when middleware is aborted', + async (failureMode) => { + const router = makeRouter() + startMocks.router = router + const requestController = new AbortController() + const cleanupError = new Error('custom stream cleanup failed') + const consoleError = vi + .spyOn(console, 'error') + .mockImplementation(() => undefined) + const ssrResponse = makeStreamResponse(router) + const dispose = vi.fn(() => { + if (failureMode === 'throw') { + throw cleanupError + } + return Promise.reject(cleanupError) + }) + ;(ssrResponse as any).dispose = dispose + startMocks.serverFnResult = ssrResponse + let notifyMiddlewareStarted!: () => void + const middlewareStarted = new Promise((resolve) => { + notifyMiddlewareStarted = resolve + }) + startMocks.requestMiddleware = [ + createMiddleware().server(async ({ next }) => { + await next() + notifyMiddlewareStarted() + return new Promise(() => {}) + }), + ] + onTestFinished(() => { + consoleError.mockRestore() + }) + onTestFinished(() => { + router.serverSsr?.cleanup() + }) + + const handler = createStartHandler(() => new Response('unused')) + const response = handler( + new Request('http://localhost/_serverFn/test', { + headers: { 'x-tsr-serverFn': 'true' }, + signal: requestController.signal, + }), + {}, + ) + + await middlewareStarted + requestController.abort(new Error('request disconnected')) + + expect((await response).status).toBe(500) + await vi.waitFor(() => { + expect(consoleError).toHaveBeenCalledWith(cleanupError) + }) + expect(dispose).toHaveBeenCalledOnce() + }, + ) + + it('disposes a stream when the request aborts after response handoff', async () => { + const router = makeRouter() + startMocks.router = router + const requestController = new AbortController() + let cancelCalls = 0 + const handler = createStartHandler(({ router: requestRouter }) => + createSsrStreamResponse( + requestRouter, + new Response( + new ReadableStream({ + cancel() { + cancelCalls++ + return new Promise(() => {}) + }, + }), + ), + ), + ) + + const response = await handler( + new Request('http://localhost/', { + signal: requestController.signal, + }), + {}, + ) + expect(response.body).not.toBeNull() + expect(router.serverSsr).toBeDefined() + + requestController.abort(new Error('request disconnected')) + await Promise.resolve() + + expect(cancelCalls).toBe(1) + expect(router.serverSsr).toBeUndefined() + }) + + it('settles when request middleware ignores cancellation', async () => { + const router = makeRouter() + startMocks.router = router + const requestController = new AbortController() + let notifyMiddlewareStarted!: () => void + const middlewareStarted = new Promise((resolve) => { + notifyMiddlewareStarted = resolve + }) + const dispose = vi.fn(() => Promise.resolve()) + let releaseMiddleware!: (response: any) => void + const middlewareResult = new Promise((resolve) => { + releaseMiddleware = resolve + }) + startMocks.requestMiddleware = [ + createMiddleware().server(() => { + notifyMiddlewareStarted() + return middlewareResult + }), + ] + const render = vi.fn(() => new Response('must not render')) + const handler = createStartHandler(render) + const response = handler( + new Request('http://localhost/', { + signal: requestController.signal, + }), + {}, + ) + + await middlewareStarted + requestController.abort(new Error('request disconnected')) + + expect((await response).status).toBe(500) + expect(render).not.toHaveBeenCalled() + + releaseMiddleware({ + response: new Response('late'), + serverSsrCleanup: 'stream', + dispose, + }) + await vi.waitFor(() => expect(dispose).toHaveBeenCalledOnce()) + expect(render).not.toHaveBeenCalled() + }) + + it('unwinds nested middleware when an inner operation ignores cancellation', async () => { + const router = makeRouter() + startMocks.router = router + const requestController = new AbortController() + const outerFinally = vi.fn() + let notifyInnerStarted!: () => void + const innerStarted = new Promise((resolve) => { + notifyInnerStarted = resolve + }) + const pending = new Promise(() => {}) + startMocks.requestMiddleware = [ + createMiddleware().server(({ next }) => next()), + createMiddleware().server(async ({ next }) => { + try { + return await next() + } finally { + outerFinally() + } + }), + createMiddleware().server(() => { + notifyInnerStarted() + return pending + }), + ] + const render = vi.fn(() => new Response('must not render')) + const handler = createStartHandler(render) + const response = handler( + new Request('http://localhost/', { + signal: requestController.signal, + }), + {}, + ) + + await innerStarted + requestController.abort(new Error('request disconnected')) + + expect((await response).status).toBe(500) + expect(outerFinally).toHaveBeenCalledOnce() + expect(render).not.toHaveBeenCalled() + }) + + it('cancels an all-synchronous direct next chain', async () => { + const router = makeRouter() + startMocks.router = router + const requestController = new AbortController() + let notifyInnerStarted!: () => void + const innerStarted = new Promise((resolve) => { + notifyInnerStarted = resolve + }) + startMocks.requestMiddleware = [ + createMiddleware().server(({ next }) => next()), + createMiddleware().server(({ next }) => next()), + createMiddleware().server(() => { + notifyInnerStarted() + return new Promise(() => {}) + }), + ] + const render = vi.fn(() => new Response('must not render')) + const handler = createStartHandler(render) + const response = handler( + new Request('http://localhost/', { + signal: requestController.signal, + }), + {}, + ) + + await innerStarted + requestController.abort(new Error('request disconnected')) + + expect((await response).status).toBe(500) + expect(render).not.toHaveBeenCalled() + }) + + it('preserves the abort reason when direct next rejects during abort', async () => { + const router = makeRouter() + startMocks.router = router + const requestController = new AbortController() + const reason = new Error('request disconnected') + startMocks.requestMiddleware = [ + createMiddleware().server(({ next }) => next()), + createMiddleware().server(() => { + requestController.abort(reason) + throw new Response('must not escape', { status: 418 }) + }), + ] + const render = vi.fn(() => new Response('must not render')) + const handler = createStartHandler(render) + + const response = await handler( + new Request('http://localhost/', { + signal: requestController.signal, + }), + {}, + ) + + expect(response.status).toBe(500) + expect(render).not.toHaveBeenCalled() + }) + + it('preserves aborts that race with a fulfilled direct next promise', async () => { + const router = makeRouter() + startMocks.router = router + const requestController = new AbortController() + const reason = new Error('request disconnected') + const observedErrors: Array = [] + const afterNext = vi.fn() + const ssrResponse = makeStreamResponse(router) + const dispose = vi.spyOn(ssrResponse as any, 'dispose') + const cancel = vi.spyOn(ssrResponse.response.body!, 'cancel') + startMocks.requestMiddleware = [ + createMiddleware().server(async ({ next }) => { + try { + const result = await next() + afterNext() + return result + } catch (error) { + observedErrors.push(error) + throw error + } + }), + createMiddleware().server(({ next }) => { + const pending = next() + void Promise.resolve(pending).then(() => + requestController.abort(reason), + ) + return pending + }), + createMiddleware().server(() => ssrResponse as any), + ] + const render = vi.fn(() => new Response('must not render')) + const handler = createStartHandler(render) + + const response = await handler( + new Request('http://localhost/', { + signal: requestController.signal, + }), + {}, + ) + + expect(response.status).toBe(500) + await vi.waitFor(() => expect(observedErrors).toEqual([reason])) + await vi.waitFor(() => { + expect(dispose).toHaveBeenCalledOnce() + expect(dispose).toHaveBeenCalledWith(reason) + expect(cancel).toHaveBeenCalledOnce() + expect(cancel).toHaveBeenCalledWith(reason) + }) + expect(afterNext).not.toHaveBeenCalled() + expect(render).not.toHaveBeenCalled() + }) + + it('disposes a tagged final response once when abort wins handoff', async () => { + const router = makeRouter() + startMocks.router = router + const requestController = new AbortController() + const reason = new Error('request disconnected') + const ssrResponse = makeStreamResponse(router) + const dispose = vi.spyOn(ssrResponse as any, 'dispose') + const cancel = vi.spyOn(ssrResponse.response.body!, 'cancel') + startMocks.requestMiddleware = [ + createMiddleware().server(() => { + queueMicrotask(() => { + queueMicrotask(() => requestController.abort(reason)) + }) + return ssrResponse as any + }), + ] + const render = vi.fn(() => new Response('must not render')) + const handler = createStartHandler(render) + + const response = await handler( + new Request('http://localhost/', { + signal: requestController.signal, + }), + {}, + ) + + expect(response.status).toBe(500) + await vi.waitFor(() => { + expect(dispose).toHaveBeenCalledOnce() + expect(dispose).toHaveBeenCalledWith(reason) + expect(cancel).toHaveBeenCalledOnce() + expect(cancel).toHaveBeenCalledWith(reason) + }) + expect(router.serverSsr).toBeUndefined() + expect(render).not.toHaveBeenCalled() + }) + + it('ignores a late same-body alias after catch disposes its owner', async () => { + const router = makeRouter() + startMocks.router = router + const requestController = new AbortController() + const reason = new Error('request disconnected') + let notifyResponseCaptured!: () => void + const responseCaptured = new Promise((resolve) => { + notifyResponseCaptured = resolve + }) + let releaseMiddleware!: () => void + const middlewareRelease = new Promise((resolve) => { + releaseMiddleware = resolve + }) + let notifyLateResultDelivered!: () => void + const lateResultDelivered = new Promise((resolve) => { + notifyLateResultDelivered = resolve + }) + startMocks.requestMiddleware = [ + createMiddleware().server(async ({ next }) => { + const result = await next() + const wrapped = new Response(result.response.body, result.response) + notifyResponseCaptured() + await middlewareRelease + queueMicrotask(() => { + queueMicrotask(notifyLateResultDelivered) + }) + return wrapped + }), + ] + const response = new Response( + new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode('stream')) + }, + }), + ) + const cancel = vi.spyOn(response.body!, 'cancel') + let ssrResponse!: ReturnType + const render = vi.fn(({ router: requestRouter }) => { + ssrResponse = createSsrStreamResponse(requestRouter, response) + return ssrResponse + }) + const handler = createStartHandler(render) + const result = handler( + new Request('http://localhost/', { + signal: requestController.signal, + }), + {}, + ) + + await responseCaptured + const dispose = vi.spyOn(ssrResponse as any, 'dispose') + requestController.abort(reason) + + expect((await result).status).toBe(500) + releaseMiddleware() + await lateResultDelivered + await vi.waitFor(() => { + expect(dispose).toHaveBeenCalledOnce() + expect(dispose).toHaveBeenCalledWith(reason) + expect(cancel).toHaveBeenCalledOnce() + expect(cancel).toHaveBeenCalledWith(reason) + }) + expect(router.serverSsr).toBeUndefined() + }) +}) + describe('createStartHandler inlineCss option', () => { const request = new Request('https://example.com/') diff --git a/packages/vue-router/src/CatchBoundary.tsx b/packages/vue-router/src/CatchBoundary.tsx index 5d75de8d6d..e912445158 100644 --- a/packages/vue-router/src/CatchBoundary.tsx +++ b/packages/vue-router/src/CatchBoundary.tsx @@ -1,13 +1,8 @@ import * as Vue from 'vue' import type { ErrorRouteComponent } from './route' -interface ErrorComponentProps { - error: Error - reset: () => void -} - type CatchBoundaryProps = { - getResetKey: () => number | string + getResetKey: () => unknown children: Vue.VNode errorComponent?: ErrorRouteComponent | Vue.Component onCatch?: (error: Error) => void @@ -17,12 +12,12 @@ const VueErrorBoundary = Vue.defineComponent({ name: 'VueErrorBoundary', props: { onError: Function, - resetKey: [String, Number], + resetKey: [String, Number, Object], + children: null, + errorComponent: null, }, - emits: ['catch'], - setup(props, { slots }) { + setup(props) { const error = Vue.ref(null) - const resetFn = Vue.ref<(() => void) | null>(null) const reset = () => { error.value = null @@ -46,7 +41,6 @@ const VueErrorBoundary = Vue.defineComponent({ } error.value = err - resetFn.value = reset if (props.onError) { props.onError(err) @@ -55,56 +49,26 @@ const VueErrorBoundary = Vue.defineComponent({ return false }) - return () => { - if (error.value && slots.fallback) { - const fallbackContent = slots.fallback({ - error: error.value, - reset, - }) - return Array.isArray(fallbackContent) && fallbackContent.length === 1 - ? fallbackContent[0] - : fallbackContent - } - - const defaultContent = slots.default && slots.default() - return Array.isArray(defaultContent) && defaultContent.length === 1 - ? defaultContent[0] - : defaultContent - } - }, -}) - -const CatchBoundaryWrapper = Vue.defineComponent({ - name: 'CatchBoundary', - inheritAttrs: false, - props: ['getResetKey', 'children', 'errorComponent', 'onCatch'] as any, - setup(props: CatchBoundaryProps) { - const resetKey = Vue.computed(() => props.getResetKey()) - - return () => { - return Vue.h( - VueErrorBoundary, - { - resetKey: resetKey.value, - onError: props.onCatch, - }, - { - default: () => props.children, - fallback: ({ error, reset }: ErrorComponentProps) => { - if (props.errorComponent) { - return Vue.h(props.errorComponent, { error, reset }) - } - return Vue.h(ErrorComponent, { error, reset }) - }, - }, - ) - } + return () => + error.value + ? Vue.h(props.errorComponent ?? ErrorComponent, { + error: error.value, + reset, + }) + : (props.children as Vue.VNode) }, }) export function CatchBoundary(props: CatchBoundaryProps) { - return Vue.h(CatchBoundaryWrapper, props as any) + return Vue.h(VueErrorBoundary, { + resetKey: props.getResetKey() as any, + onError: props.onCatch, + children: props.children, + errorComponent: props.errorComponent, + }) } +CatchBoundary.inheritAttrs = false +CatchBoundary.props = ['getResetKey', 'children', 'errorComponent', 'onCatch'] export const ErrorComponent = Vue.defineComponent({ name: 'ErrorComponent', diff --git a/packages/vue-router/src/Match.tsx b/packages/vue-router/src/Match.tsx index 44e9aca0cd..96d81970a5 100644 --- a/packages/vue-router/src/Match.tsx +++ b/packages/vue-router/src/Match.tsx @@ -1,23 +1,12 @@ import * as Vue from 'vue' -import { - createControlledPromise, - getLocationChangeInfo, - invariant, - isNotFound, - isRedirect, - rootRouteId, -} from '@tanstack/router-core' +import { isNotFound, rootRouteId } from '@tanstack/router-core' import { isServer } from '@tanstack/router-core/isServer' import { useStore } from '@tanstack/vue-store' -import { CatchBoundary, ErrorComponent } from './CatchBoundary' +import { CatchBoundary } from './CatchBoundary' import { ClientOnly } from './ClientOnly' import { useRouter } from './useRouter' import { CatchNotFound } from './not-found' -import { - matchContext, - pendingMatchContext, - routeIdContext, -} from './matchContext' +import { routeIdContext } from './matchContext' import { renderRouteNotFound } from './renderRouteNotFound' import { ScrollRestoration } from './scroll-restoration' import type { VNode } from 'vue' @@ -26,7 +15,7 @@ import type { AnyRoute, RootRouteOptions } from '@tanstack/router-core' export const Match = Vue.defineComponent({ name: 'Match', props: { - matchId: { + routeId: { type: String, required: true, }, @@ -34,127 +23,47 @@ export const Match = Vue.defineComponent({ setup(props) { const router = useRouter() - // Derive routeId from initial props.matchId — stable for this component's - // lifetime. The routeId never changes for a given route position in the - // tree, even when matchId changes (loaderDepsHash, etc). - const routeId = router.stores.matchStores.get(props.matchId)?.routeId + const routeId = props.routeId - if (!routeId) { - if (process.env.NODE_ENV !== 'production') { - throw new Error( - `Invariant failed: Could not find routeId for matchId "${props.matchId}". Please file an issue!`, - ) - } - - invariant() - } - - // Static route-tree check: is this route a direct child of the root? - // parentRoute is set at build time, so no reactive tracking needed. - const isChildOfRoot = - (router.routesById[routeId] as AnyRoute)?.parentRoute?.id === rootRouteId - - // Single stable store subscription — getRouteMatchStore returns a - // cached computed store that resolves routeId → current match state - // through the signal graph. No bridge needed. const activeMatch = useStore( - router.stores.getRouteMatchStore(routeId), + router.stores.getMatchStore(routeId), (value) => value, - ) - const isPendingMatchRef = useStore( - router.stores.pendingRouteIds, - (pendingRouteIds) => Boolean(pendingRouteIds[routeId]), { equal: Object.is }, ) - const loadedAt = useStore(router.stores.loadedAt, (value) => value) - - const matchData = Vue.computed(() => { - const match = activeMatch.value - if (!match) { - return null - } - - return { - matchId: match.id, - routeId, - loadedAt: loadedAt.value, - ssr: match.ssr, - _displayPending: match._displayPending, - } - }) - - const route = Vue.computed(() => - matchData.value ? router.routesById[matchData.value.routeId] : null, - ) - - const PendingComponent = Vue.computed( - () => - route.value?.options?.pendingComponent ?? - router?.options?.defaultPendingComponent, - ) - - const pendingElement = Vue.computed(() => - PendingComponent.value ? Vue.h(PendingComponent.value) : undefined, - ) - - const routeErrorComponent = Vue.computed( - () => - route.value?.options?.errorComponent ?? - router?.options?.defaultErrorComponent, - ) - - const routeOnCatch = Vue.computed( - () => route.value?.options?.onCatch ?? router?.options?.defaultOnCatch, - ) - - const routeNotFoundComponent = Vue.computed(() => - route.value?.isRoot - ? // If it's the root route, use the globalNotFound option, with fallback to the notFoundRoute's component - (route.value?.options?.notFoundComponent ?? - router?.options?.notFoundRoute?.options?.component) - : route.value?.options?.notFoundComponent, - ) - - const hasShellComponent = Vue.computed(() => { - if (!route.value?.isRoot) return false - return !!(route.value.options as RootRouteOptions).shellComponent - }) - - const ShellComponent = Vue.computed(() => - hasShellComponent.value - ? ((route.value!.options as RootRouteOptions).shellComponent as any) - : null, - ) - // Provide routeId context (stable string) for children. // MatchInner, Outlet, and useMatch all consume this. Vue.provide(routeIdContext, routeId) - // Provide reactive nearest-match context for hooks that slice the active - // matches array relative to the current match. - const matchIdRef = Vue.computed( - () => activeMatch.value?.id ?? props.matchId, - ) - Vue.provide(matchContext, matchIdRef) - - Vue.provide(pendingMatchContext, isPendingMatchRef) - return (): VNode => { - const actualMatchId = matchData.value?.matchId ?? props.matchId - - const resolvedNoSsr = - matchData.value?.ssr === false || matchData.value?.ssr === 'data-only' - const shouldClientOnly = - resolvedNoSsr || !!matchData.value?._displayPending + const match = activeMatch.value + const route = match ? (router.routesById[routeId] as AnyRoute) : undefined + const PendingComponent = + route?.options.pendingComponent ?? + router.options.defaultPendingComponent + const pendingElement = PendingComponent + ? Vue.h(PendingComponent) + : undefined + const routeErrorComponent = + route?.options.errorComponent ?? router.options.defaultErrorComponent + const routeOnCatch = + route?.options.onCatch ?? router.options.defaultOnCatch + const routeNotFoundComponent = route?.isRoot + ? (route.options.notFoundComponent ?? + router.options.notFoundRoute?.options.component) + : route?.options.notFoundComponent + const ShellComponent = route?.isRoot + ? ((route.options as RootRouteOptions).shellComponent as any) + : undefined + const resolvedNoSsr = match?.ssr === false || match?.ssr === 'data-only' const renderMatchContent = (): VNode => { - const matchInner = Vue.h(MatchInner, { matchId: actualMatchId }) + const matchInner = Vue.h(MatchInner) - let content: VNode = shouldClientOnly + let content: VNode = resolvedNoSsr ? Vue.h( ClientOnly, { - fallback: pendingElement.value, + fallback: pendingElement, }, { default: () => matchInner, @@ -163,73 +72,60 @@ export const Match = Vue.defineComponent({ : matchInner // Wrap in NotFound boundary if needed - if (routeNotFoundComponent.value) { + if (routeNotFoundComponent) { content = Vue.h(CatchNotFound, { fallback: (error: any) => { - error.routeId ??= matchData.value?.routeId - - // If the current not found handler doesn't exist or it has a - // route ID which doesn't match the current route, rethrow the error - if ( - !routeNotFoundComponent.value || - (error.routeId && error.routeId !== matchData.value?.routeId) || - (!error.routeId && route.value && !route.value.isRoot) - ) + error.routeId ??= routeId + + if (error.routeId !== routeId) { throw error + } - return Vue.h(routeNotFoundComponent.value, error) + return Vue.h(routeNotFoundComponent, error) }, children: content, }) } // Wrap in error boundary if needed - if (routeErrorComponent.value) { + if (routeErrorComponent) { content = CatchBoundary({ - getResetKey: () => matchData.value?.loadedAt ?? 0, - errorComponent: routeErrorComponent.value || ErrorComponent, + getResetKey: () => activeMatch.value, + errorComponent: routeErrorComponent, onCatch: (error: Error) => { // Forward not found errors (we don't want to show the error component for these) if (isNotFound(error)) { - error.routeId ??= matchData.value?.routeId + error.routeId ??= routeId throw error } if (process.env.NODE_ENV !== 'production') { - console.warn(`Warning: Error in route match: ${actualMatchId}`) + console.warn(`Warning: Error in route match: ${match?.id}`) } - routeOnCatch.value?.(error) + routeOnCatch?.(error) }, children: content, }) } - // Add scroll restoration if needed - const withScrollRestoration: Array = [ - content, - isChildOfRoot - ? Vue.h(Vue.Fragment, null, [ - Vue.h(OnRendered), - router.options.scrollRestoration && - (isServer ?? router.isServer) - ? Vue.h(ScrollRestoration) - : null, - ]) - : null, - ].filter(Boolean) as Array - - // Return single child directly to avoid Fragment wrapper that causes hydration mismatch - if (withScrollRestoration.length === 1) { - return withScrollRestoration[0]! + if (route?.parentRoute?.id !== rootRouteId) { + return content } - return Vue.h(Vue.Fragment, null, withScrollRestoration) + return Vue.h(Vue.Fragment, null, [ + content, + Vue.h(Vue.Fragment, null, [ + (isServer ?? router.isServer) && router.options.scrollRestoration + ? Vue.h(ScrollRestoration) + : null, + ]), + ]) } - if (!hasShellComponent.value) { + if (!ShellComponent) { return renderMatchContent() } - return Vue.h(ShellComponent.value, null, { + return Vue.h(ShellComponent, null, { // Important: return a fresh VNode on each slot invocation so that shell // components can re-render without reusing a cached VNode instance. default: () => renderMatchContent(), @@ -239,65 +135,14 @@ export const Match = Vue.defineComponent({ }) // On Rendered can't happen above the root layout because it actually -// renders a dummy dom element to track the rendered state of the app. -// We render a script tag with a key that changes based on the current -// location state.__TSR_key. Also, because it's below the root layout, it -// allows us to fire onRendered events even after a hydration mismatch -// error that occurred above the root layout (like bad head/link tags, -// which is common). -const OnRendered = Vue.defineComponent({ - name: 'OnRendered', - setup() { - const router = useRouter() - - const location = useStore( - router.stores.resolvedLocation, - (resolvedLocation) => resolvedLocation?.state.__TSR_key, - ) - - let prevHref: string | undefined - - Vue.watch( - location, - () => { - if (location.value) { - const currentHref = router.latestLocation.href - if (prevHref === undefined || prevHref !== currentHref) { - router.emit({ - type: 'onRendered', - ...getLocationChangeInfo( - router.stores.location.get(), - router.stores.resolvedLocation.get(), - ), - }) - prevHref = currentHref - } - } - }, - { immediate: true }, - ) - - return () => null - }, -}) - export const MatchInner = Vue.defineComponent({ name: 'MatchInner', - props: { - matchId: { - type: String, - required: true, - }, - }, - setup(props) { + setup() { const router = useRouter() // Use routeId from context (provided by parent Match) — stable string. const routeId = Vue.inject(routeIdContext)! - const activeMatch = useStore( - router.stores.getRouteMatchStore(routeId), - (value) => value, - ) + const activeMatch = useStore(router.stores.getMatchStore(routeId)) // Combined selector for match state AND remount key // This ensures both are computed in the same selector call with consistent data @@ -315,110 +160,45 @@ export const MatchInner = Vue.defineComponent({ (router.routesById[matchRouteId] as AnyRoute).options.remountDeps ?? router.options.defaultRemountDeps - let remountKey: string | undefined - if (remountFn) { - const remountDeps = remountFn({ - routeId: matchRouteId, - loaderDeps: match.loaderDeps, - params: match._strictParams, - search: match._strictSearch, - }) - remountKey = remountDeps ? JSON.stringify(remountDeps) : undefined - } - - return { - routeId: matchRouteId, - match: { - id: match.id, - status: match.status, - error: match.error, - ssr: match.ssr, - _forcePending: match._forcePending, - _displayPending: match._displayPending, - _nonReactive: match._nonReactive, - }, - remountKey, - } - }) + const remountDeps = remountFn + ? remountFn({ + routeId: matchRouteId, + loaderDeps: match.loaderDeps, + params: match._strictParams, + search: match._strictSearch, + }) + : undefined - const route = Vue.computed(() => { - if (!combinedState.value) return null - return router.routesById[combinedState.value.routeId]! + return [ + match, + remountDeps ? JSON.stringify(remountDeps) : undefined, + ] as const }) - const match = Vue.computed(() => combinedState.value?.match) - const remountKey = Vue.computed(() => combinedState.value?.remountKey) - - const getMatchPromise = ( - match: { - id: string - _nonReactive: { - displayPendingPromise?: Promise - minPendingPromise?: Promise - loadPromise?: Promise - } - }, - key: 'displayPendingPromise' | 'minPendingPromise' | 'loadPromise', - ) => { - return ( - router.getMatch(match.id)?._nonReactive[key] ?? match._nonReactive[key] - ) - } - return (): VNode | null => { // If match doesn't exist, return null (component is being unmounted or not ready) - if (!combinedState.value || !match.value || !route.value) return null - - // Handle different match statuses - if (match.value._displayPending) { - const PendingComponent = - route.value.options.pendingComponent ?? - router.options.defaultPendingComponent - - return PendingComponent ? Vue.h(PendingComponent) : null - } - - if (match.value._forcePending) { - const PendingComponent = - route.value.options.pendingComponent ?? - router.options.defaultPendingComponent - - return PendingComponent ? Vue.h(PendingComponent) : null - } - - if (match.value.status === 'notFound') { - if (!isNotFound(match.value.error)) { - if (process.env.NODE_ENV !== 'production') { - throw new Error('Invariant failed: Expected a notFound error') - } - - invariant() - } - return renderRouteNotFound(router, route.value, match.value.error) + const state = combinedState.value + if (!state) { + return null } + const [match, remountKey] = state + const route = router.routesById[match.routeId]! - if (match.value.status === 'redirected') { - if (!isRedirect(match.value.error)) { - if (process.env.NODE_ENV !== 'production') { - throw new Error('Invariant failed: Expected a redirect error') - } - - invariant() - } - throw getMatchPromise(match.value, 'loadPromise') + // Handle different match statuses + if (match.status === 'notFound') { + return renderRouteNotFound(router, route, match.error) } - if (match.value.status === 'error') { + if (match.status === 'error') { // Check if this route or any parent has an error component const RouteErrorComponent = - route.value.options.errorComponent ?? - router.options.defaultErrorComponent + route.options.errorComponent ?? router.options.defaultErrorComponent // If this route has an error component, render it directly // This is more reliable than relying on Vue's error boundary if (RouteErrorComponent) { return Vue.h(RouteErrorComponent, { - error: match.value.error, + error: match.error, reset: () => { router.invalidate() }, @@ -430,37 +210,14 @@ export const MatchInner = Vue.defineComponent({ // If there's no error component for this route, throw the error // so it can bubble up to the nearest parent with an error component - throw match.value.error + throw match.error } - if (match.value.status === 'pending') { - const pendingMinMs = - route.value.options.pendingMinMs ?? router.options.defaultPendingMinMs - - const routerMatch = router.getMatch(match.value.id) - if ( - pendingMinMs && - routerMatch && - !routerMatch._nonReactive.minPendingPromise - ) { - // Create a promise that will resolve after the minPendingMs - if (!(isServer ?? router.isServer)) { - const minPendingPromise = createControlledPromise() - - routerMatch._nonReactive.minPendingPromise = minPendingPromise - - setTimeout(() => { - minPendingPromise.resolve() - // We've handled the minPendingPromise, so we can delete it - routerMatch._nonReactive.minPendingPromise = undefined - }, pendingMinMs) - } - } - + if (match.status === 'pending') { // In Vue, we render the pending component directly instead of throwing a promise // because Vue's Suspense doesn't catch thrown promises like React does const PendingComponent = - route.value.options.pendingComponent ?? + route.options.pendingComponent ?? router.options.defaultPendingComponent if (PendingComponent) { @@ -472,16 +229,19 @@ export const MatchInner = Vue.defineComponent({ } // Success status - render the component with remount key - const Comp = - route.value.options.component ?? router.options.defaultComponent - const key = remountKey.value - + const Comp = route.options.component ?? router.options.defaultComponent if (Comp) { // Pass key as a prop - Vue.h properly handles 'key' as a special prop - return Vue.h(Comp, key !== undefined ? { key } : undefined) + return Vue.h( + Comp, + remountKey !== undefined ? { key: remountKey } : undefined, + ) } - return Vue.h(Outlet, key !== undefined ? { key } : undefined) + return Vue.h( + Outlet, + remountKey !== undefined ? { key: remountKey } : undefined, + ) } }, }) @@ -490,66 +250,41 @@ export const Outlet = Vue.defineComponent({ name: 'Outlet', setup() { const router = useRouter() - const parentRouteId = Vue.inject(routeIdContext) - - if (!parentRouteId) { - return (): VNode | null => null - } - - // Parent state via stable routeId store — single subscription - const parentMatch = useStore( - router.stores.getRouteMatchStore(parentRouteId), - (v) => v, - ) + const parentRouteId = Vue.inject(routeIdContext)! - const route = Vue.computed(() => - parentMatch.value - ? router.routesById[parentMatch.value.routeId as string]! - : undefined, - ) - - const parentGlobalNotFound = Vue.computed( - () => parentMatch.value?.globalNotFound ?? false, - ) + const parentMatch = useStore(router.stores.getMatchStore(parentRouteId)) - // Child match lookup: read the child matchId from the shared derived - // map (one reactive node for the whole tree), then grab match state - // directly from the pool. - const childMatchIdMap = useStore( - router.stores.childMatchIdByRouteId, - (v) => v, - ) + const route = router.routesById[parentRouteId]! - const childMatchData = Vue.computed(() => { - const childId = childMatchIdMap.value[parentRouteId] - if (!childId) return null - const child = router.stores.matchStores.get(childId)?.get() - if (!child) return null - - return { - id: child.id, - // Key based on routeId + params only (not loaderDeps) - // This ensures component recreates when params change, - // but NOT when only loaderDeps change - paramsKey: child.routeId + JSON.stringify(child._strictParams), - } + const childMatch = useStore(router.stores.matches, (matches) => { + const index = matches.findIndex( + (match) => match.routeId === parentRouteId, + ) + const child = matches[index + 1] + return child + ? ([ + child.routeId, + child.routeId + JSON.stringify(child._strictParams), + ] as const) + : undefined }) return (): VNode | null => { - if (parentGlobalNotFound.value) { - if (!route.value) { - return null - } - return renderRouteNotFound(router, route.value, undefined) + if (parentMatch.value?._notFound) { + return renderRouteNotFound(router, route, parentMatch.value.error) } - if (!childMatchData.value) { + const child = childMatch.value + if (!child) { return null } const nextMatch = Vue.h(Match, { - matchId: childMatchData.value.id, - key: childMatchData.value.paramsKey, + routeId: child[0 /* routeId */], + // Key based on routeId + params only (not loaderDeps) + // This ensures component recreates when params change, + // but NOT when only loaderDeps change + key: child[1 /* key */], }) // Note: We intentionally do NOT wrap in Suspense here. diff --git a/packages/vue-router/src/Matches.tsx b/packages/vue-router/src/Matches.tsx index da8220db29..15799d2a5b 100644 --- a/packages/vue-router/src/Matches.tsx +++ b/packages/vue-router/src/Matches.tsx @@ -4,7 +4,7 @@ import { useStore } from '@tanstack/vue-store' import { CatchBoundary } from './CatchBoundary' import { useRouter } from './useRouter' import { useTransitionerSetup } from './Transitioner' -import { matchContext } from './matchContext' +import { routeIdContext } from './matchContext' import { Match } from './Match' import type { AnyRouter, @@ -32,51 +32,31 @@ declare module '@tanstack/router-core' { } } -// Create a component that renders MatchesInner with Transitioner's setup logic inlined. -// This is critical for proper hydration - we call useTransitionerSetup() as a composable -// rather than rendering it as a component, which avoids Fragment/element mismatches. -const MatchesContent = Vue.defineComponent({ - name: 'MatchesContent', - setup() { - // IMPORTANT: We need to ensure Transitioner's setup() runs. - // Transitioner sets up critical functionality: - // - router.startTransition - // - History subscription via router.history.subscribe(router.load) - // - Watchers for router events - // - // We inline Transitioner's setup logic here. Since Transitioner returns null, - // we can call its setup function directly without affecting the render tree. - // This is done by importing and calling useTransitionerSetup. - useTransitionerSetup() - - return () => Vue.h(MatchesInner) - }, -}) - export const Matches = Vue.defineComponent({ name: 'Matches', setup() { const router = useRouter() + useTransitionerSetup() return () => { - const pendingElement = router?.options?.defaultPendingComponent + const pendingElement = router.options.defaultPendingComponent ? Vue.h(router.options.defaultPendingComponent) : null // Do not render a root Suspense during SSR or hydrating from SSR const inner = - (isServer ?? router?.isServer ?? false) || - (typeof document !== 'undefined' && router?.ssr) - ? Vue.h(MatchesContent) + (isServer ?? router.isServer) || + (typeof document !== 'undefined' && router.ssr) + ? Vue.h(MatchesInner) : Vue.h( Vue.Suspense, { fallback: pendingElement }, { - default: () => Vue.h(MatchesContent), + default: () => Vue.h(MatchesInner), }, ) - return router?.options?.InnerWrap + return router.options.InnerWrap ? Vue.h(router.options.InnerWrap, null, { default: () => inner }) : inner } @@ -99,19 +79,13 @@ const MatchesInner = Vue.defineComponent({ setup() { const router = useRouter() - const matchId = useStore(router.stores.firstId, (id) => id) - const resetKey = useStore(router.stores.loadedAt, (loadedAt) => loadedAt) - - // Create a ref for the match id to provide - const matchIdRef = Vue.computed(() => matchId.value) - - // Provide the matchId for child components using the InjectionKey - Vue.provide(matchContext, matchIdRef) + const matches = useStore(router.stores.matches) + const routeId = Vue.computed(() => matches.value[0]?.routeId) return () => { - // Generate a placeholder element if matchId.value is not present - const childElement = matchId.value - ? Vue.h(Match, { matchId: matchId.value }) + // Generate a placeholder element if routeId.value is not present + const childElement = routeId.value + ? Vue.h(Match, { routeId: routeId.value }) : Vue.h('div') // If disableGlobalCatchBoundary is true, don't wrap in CatchBoundary @@ -120,7 +94,7 @@ const MatchesInner = Vue.defineComponent({ } return Vue.h(CatchBoundary, { - getResetKey: () => resetKey.value, + getResetKey: () => matches.value, errorComponent: errorComponentFn, onCatch: process.env.NODE_ENV !== 'production' @@ -152,7 +126,12 @@ export type UseMatchRouteOptions< export function useMatchRoute() { const router = useRouter() - const routerState = useStore(router.stores.matchRouteDeps, (value) => value) + const location = useStore(router.stores.location, (value) => value.href) + const resolvedLocation = useStore( + router.stores.resolvedLocation, + (value) => value?.href, + ) + const status = useStore(router.stores.status) return < const TFrom extends string = string, @@ -164,12 +143,11 @@ export function useMatchRoute() { ): Vue.Ref< false | ResolveRoute['types']['allParams'] > => { - const { pending, caseSensitive, fuzzy, includeSearch, ...rest } = opts - const matchRoute = Vue.computed(() => { - // Access routerState to establish dependency - - routerState.value + location.value + resolvedLocation.value + status.value + const { pending, caseSensitive, fuzzy, includeSearch, ...rest } = opts return router.matchRoute(rest as any, { pending, caseSensitive, @@ -248,27 +226,20 @@ export const MatchRoute = Vue.defineComponent({ }, }, setup(props, { slots }) { - const router = useRouter() - const status = useStore( - router.stores.matchRouteDeps, - (value) => value.status, - ) + const params = useMatchRoute()(props) return () => { - if (!status.value) return null - - const matchRoute = useMatchRoute() - const params = matchRoute(props).value as boolean + const match = params.value as boolean // Create a component that renders the slot in a reactive manner - if (!params || !slots.default) { + if (!match || !slots.default) { return null } // For function slots, pass the params if (typeof slots.default === 'function') { // Use h to create a wrapper component that will call the slot function - return Vue.h(Vue.Fragment, null, slots.default(params)) + return Vue.h(Vue.Fragment, null, slots.default(match)) } // For normal slots, just render them @@ -306,15 +277,13 @@ export function useParentMatches< >( opts?: UseMatchesBaseOptions, ): Vue.Ref> { - // Use matchContext with proper type - const contextMatchId = Vue.inject>(matchContext) - const safeMatchId = Vue.computed(() => contextMatchId?.value || '') + const contextRouteId = Vue.inject(routeIdContext) return useMatches({ select: (matches: Array>) => { matches = matches.slice( 0, - matches.findIndex((d) => d.id === safeMatchId.value), + matches.findIndex((d) => d.routeId === contextRouteId), ) return opts?.select ? opts.select(matches) : matches }, @@ -327,14 +296,12 @@ export function useChildMatches< >( opts?: UseMatchesBaseOptions, ): Vue.Ref> { - // Use matchContext with proper type - const contextMatchId = Vue.inject>(matchContext) - const safeMatchId = Vue.computed(() => contextMatchId?.value || '') + const contextRouteId = Vue.inject(routeIdContext) return useMatches({ select: (matches: Array>) => { matches = matches.slice( - matches.findIndex((d) => d.id === safeMatchId.value) + 1, + matches.findIndex((d) => d.routeId === contextRouteId) + 1, ) return opts?.select ? opts.select(matches) : matches }, diff --git a/packages/vue-router/src/Scripts.tsx b/packages/vue-router/src/Scripts.tsx index 32c9eef8b5..8c0a1f87f3 100644 --- a/packages/vue-router/src/Scripts.tsx +++ b/packages/vue-router/src/Scripts.tsx @@ -1,4 +1,5 @@ import * as Vue from 'vue' +import { _getAssetMatches } from '@tanstack/router-core' import { useStore } from '@tanstack/vue-store' import { isServer } from '@tanstack/router-core/isServer' import { Asset } from './Asset' @@ -17,68 +18,49 @@ export const Scripts = Vue.defineComponent({ setup() { const router = useRouter() const nonce = router.options.ssr?.nonce - const matches = useStore(router.stores.matches, (value) => value) + const matches = useStore(router.stores.matches, _getAssetMatches) - const getAssetScripts = (matches: Array) => { + const scripts = Vue.computed(() => { + const userScripts: Array = [] const assetScripts: Array = [] const manifest = router.ssr?.manifest - - if (!manifest) { - return [] - } - - matches.forEach((match) => { - const routeManifest = manifest.routes[match.routeId] - - routeManifest?.scripts?.forEach((asset) => { + for (const match of matches.value) { + for (const script of match.scripts ?? []) { + if (!script) { + continue + } + const { children, ...attrs } = script + userScripts.push({ + tag: 'script', + attrs: { ...attrs, nonce }, + children, + }) + } + for (const asset of manifest?.routes[match.routeId]?.scripts ?? []) { assetScripts.push({ tag: 'script', attrs: { ...asset.attrs, nonce }, children: asset.children, }) - }) - }) - - return assetScripts - } - - const getScripts = (matches: Array): Array => - ( - matches - .map((match) => match.scripts!) - .flat(1) - .filter(Boolean) as Array - ).map( - ({ children, ...script }) => - ({ - tag: 'script', - attrs: { - ...script, - nonce, - }, - children, - }) satisfies RouterManagedTag, - ) - - const assetScripts = Vue.computed>(() => - getAssetScripts(matches.value), - ) - const scripts = Vue.computed>(() => - getScripts(matches.value), - ) + } + } + return [userScripts, assetScripts] as const + }) const mounted = Vue.ref(false) Vue.onMounted(() => { mounted.value = true }) - return () => - renderScripts(router, { - scripts: scripts.value, - assetScripts: assetScripts.value, + return () => { + const [userScripts, assetScripts] = scripts.value + return renderScripts(router, { + scripts: userScripts, + assetScripts, mounted: mounted.value, nonce, }) + } }, }) diff --git a/packages/vue-router/src/Transitioner.tsx b/packages/vue-router/src/Transitioner.tsx index b0133d965c..96f98b7bf3 100644 --- a/packages/vue-router/src/Transitioner.tsx +++ b/packages/vue-router/src/Transitioner.tsx @@ -1,109 +1,23 @@ import * as Vue from 'vue' import { getLocationChangeInfo, trimPathRight } from '@tanstack/router-core' import { isServer } from '@tanstack/router-core/isServer' -import { batch, useStore } from '@tanstack/vue-store' import { useRouter } from './useRouter' -import { usePrevious } from './utils' -// Track mount state per router to avoid double-loading -let mountLoadForRouter = { router: null as any, mounted: false } - -/** - * Composable that sets up router transition logic. - * This is called from MatchesContent to set up: - * - router.startTransition - * - router.startViewTransition - * - History subscription - * - Router event watchers - * - * Must be called during component setup phase. - */ export function useTransitionerSetup() { const router = useRouter() - - // Skip on server - no transitions needed if (isServer ?? router.isServer) { return } - const isLoading = useStore(router.stores.isLoading, (value) => value) - - // Track if we're in a transition - using a ref to track async transitions - const isTransitioning = Vue.ref(false) - - // Track pending state changes - const hasPending = useStore(router.stores.hasPending, (value) => value) - - const previousIsLoading = usePrevious(() => isLoading.value) - - const isAnyPending = Vue.computed( - () => isLoading.value || isTransitioning.value || hasPending.value, - ) - const previousIsAnyPending = usePrevious(() => isAnyPending.value) - - const isPagePending = Vue.computed(() => isLoading.value || hasPending.value) - const previousIsPagePending = usePrevious(() => isPagePending.value) - - // Implement startTransition similar to React/Solid - // Vue doesn't have a native useTransition like React 18, so we simulate it - // We also update the router state's isTransitioning flag so useMatch can check it - router.startTransition = (fn: () => void | Promise) => { - isTransitioning.value = true - // Also update the router state so useMatch knows we're transitioning - try { - router.stores.isTransitioning.set(true) - } catch { - // Ignore errors if component is unmounted - } - - // Helper to end the transition - const endTransition = () => { - // Use nextTick to ensure Vue has processed all reactive updates - Vue.nextTick(() => { - try { - isTransitioning.value = false - router.stores.isTransitioning.set(false) - } catch { - // Ignore errors if component is unmounted - } - }) - } - - // Execute the function synchronously - // The function internally may call startViewTransition which schedules async work - // via document.startViewTransition, but we don't need to wait for it here - // because Vue's reactivity will trigger re-renders when state changes + const transition = async (fn: () => void) => { fn() - - // End the transition on next tick to allow Vue to process reactive updates - endTransition() + await Vue.nextTick() + return true } - - // Vue updates DOM asynchronously (next tick). The View Transitions API expects the - // update callback promise to resolve only after the DOM has been updated. - // Wrap the router-core implementation to await a Vue flush before resolving. - const originalStartViewTransition: - | undefined - | ((fn: () => Promise) => void) = - (router as any).__tsrOriginalStartViewTransition ?? - router.startViewTransition - - ;(router as any).__tsrOriginalStartViewTransition = - originalStartViewTransition - - router.startViewTransition = (fn: () => Promise) => { - return originalStartViewTransition?.(async () => { - await fn() - await Vue.nextTick() - }) - } - - // Subscribe to location changes - // and try to load the new location - let unsubscribe: (() => void) | undefined + router.startTransition = transition Vue.onMounted(() => { - unsubscribe = router.history.subscribe(router.load) + Vue.onUnmounted(router.history.subscribe(router.load)) const nextLocation = router.buildLocation({ to: router.latestLocation.pathname, @@ -114,130 +28,34 @@ export function useTransitionerSetup() { _includeValidateSearch: true, }) - // Check if the current URL matches the canonical form. - // Compare publicHref (browser-facing URL) for consistency with - // the server-side redirect check in router.beforeLoad. if ( trimPathRight(router.latestLocation.publicHref) !== trimPathRight(nextLocation.publicHref) ) { - router.commitLocation({ ...nextLocation, replace: true }) - } - }) - - // Track if component is mounted to prevent updates after unmount - const isMounted = Vue.ref(false) - - Vue.onMounted(() => { - isMounted.value = true - if (!isAnyPending.value) { - if (router.stores.status.get() === 'pending') { - batch(() => { - router.stores.status.set('idle') - router.stores.resolvedLocation.set(router.stores.location.get()) - }) - } - } - }) - - Vue.onUnmounted(() => { - isMounted.value = false - if (unsubscribe) { - unsubscribe() + router.commitLocation({ + ...nextLocation, + replace: true, + ignoreBlocker: true, + }) + return } - }) - // Try to load the initial location - Vue.onMounted(() => { + const resolvedLocation = router.stores.resolvedLocation.get() if ( - (typeof window !== 'undefined' && router.ssr) || - (mountLoadForRouter.router === router && mountLoadForRouter.mounted) + resolvedLocation?.href === router.latestLocation.href && + resolvedLocation.state.__TSR_key === router.latestLocation.state.__TSR_key ) { - return - } - mountLoadForRouter = { router, mounted: true } - const tryLoad = async () => { - try { - await router.load() - } catch (err) { - console.error(err) - } - } - tryLoad() - }) - - // Setup watchers for emitting events - // All watchers check isMounted to prevent updates after unmount - Vue.watch( - () => isLoading.value, - (newValue) => { - if (!isMounted.value) return - try { - if (previousIsLoading.value.previous && !newValue) { - router.emit({ - type: 'onLoad', - ...getLocationChangeInfo( - router.stores.location.get(), - router.stores.resolvedLocation.get(), - ), - }) - } - } catch { - // Ignore errors if component is unmounted - } - }, - ) - - Vue.watch(isPagePending, (newValue) => { - if (!isMounted.value) return - try { - // emit onBeforeRouteMount - if (previousIsPagePending.value.previous && !newValue) { - router.emit({ - type: 'onBeforeRouteMount', - ...getLocationChangeInfo( - router.stores.location.get(), - router.stores.resolvedLocation.get(), - ), - }) - } - } catch { - // Ignore errors if component is unmounted - } - }) - - Vue.watch(isAnyPending, (newValue) => { - if (!isMounted.value) return - try { - if (!newValue && router.stores.status.get() === 'pending') { - batch(() => { - router.stores.status.set('idle') - router.stores.resolvedLocation.set(router.stores.location.get()) - }) - } - - // The router was pending and now it's not - if (previousIsAnyPending.value.previous && !newValue) { - const changeInfo = getLocationChangeInfo( - router.stores.location.get(), - router.stores.resolvedLocation.get(), - ) - router.emit({ - type: 'onResolved', - ...changeInfo, - }) - } - } catch { - // Ignore errors if component is unmounted + router.emit({ + type: 'onRendered', + ...getLocationChangeInfo(resolvedLocation, resolvedLocation), + }) + } else if (!router._tx) { + router.load().catch(console.error) } }) } -/** - * @deprecated Use useTransitionerSetup() composable instead. - * This component is kept for backwards compatibility but the setup logic - * has been moved to useTransitionerSetup() for better SSR hydration. - */ +/** @deprecated Use useTransitionerSetup() instead. */ export const Transitioner = Vue.defineComponent({ name: 'Transitioner', setup() { diff --git a/packages/vue-router/src/awaited.tsx b/packages/vue-router/src/awaited.tsx index ca20035c7d..136d7bbc6e 100644 --- a/packages/vue-router/src/awaited.tsx +++ b/packages/vue-router/src/awaited.tsx @@ -9,7 +9,7 @@ export type AwaitOptions = { export function useAwaited({ promise: _promise, -}: AwaitOptions): [T, DeferredPromise] { +}: AwaitOptions): [data: T, promise: DeferredPromise] { const promise = defer(_promise) if (promise[TSR_DEFERRED_PROMISE].status === 'pending') { diff --git a/packages/vue-router/src/headContentUtils.tsx b/packages/vue-router/src/headContentUtils.tsx index 1f1f8ff973..adfc156c72 100644 --- a/packages/vue-router/src/headContentUtils.tsx +++ b/packages/vue-router/src/headContentUtils.tsx @@ -1,5 +1,6 @@ import * as Vue from 'vue' import { + _getAssetMatches, appendUniqueUserTags, escapeHtml, getAssetCrossOrigin, @@ -15,132 +16,16 @@ import type { export const useTags = (assetCrossOrigin?: AssetCrossOriginConfig) => { const router = useRouter() - const matches = useStore(router.stores.matches, (value) => value) + const matches = useStore(router.stores.matches, _getAssetMatches) - const meta = Vue.computed>(() => { - const resultMeta: Array = [] - const metaByAttribute: Record = {} - let title: RouterManagedTag | undefined - ;[...matches.value.map((match) => match.meta!).filter(Boolean)] - .reverse() - .forEach((metas) => { - ;[...metas].reverse().forEach((m) => { - if (!m) return - - if (m.title) { - if (!title) { - title = { - tag: 'title', - children: m.title, - } - } - } else if ('script:ld+json' in m) { - // Handle JSON-LD structured data - // Content is HTML-escaped to prevent XSS when injected via innerHTML - try { - const json = JSON.stringify(m['script:ld+json']) - resultMeta.push({ - tag: 'script', - attrs: { - type: 'application/ld+json', - }, - children: escapeHtml(json), - }) - } catch { - // Skip invalid JSON-LD objects - } - } else { - const attribute = m.name ?? m.property - if (attribute) { - if (metaByAttribute[attribute]) { - return - } else { - metaByAttribute[attribute] = true - } - } - - resultMeta.push({ - tag: 'meta', - attrs: { - ...m, - }, - }) - } - }) - }) - - if (title) { - resultMeta.push(title) - } - - resultMeta.reverse() - - return resultMeta - }) - - const links = Vue.computed>( - () => - matches.value - .map((match) => match.links!) - .filter(Boolean) - .flat(1) - .map((link) => ({ - tag: 'link', - attrs: { - ...link, - }, - })) as Array, - ) - - const preloadMeta = Vue.computed>(() => { - const preloadMeta: Array = [] - - matches.value.forEach((match) => { - router.ssr?.manifest?.routes[match.routeId]?.preloads - ?.filter(Boolean) - .forEach((preload) => { - preloadMeta.push({ - tag: 'link', - attrs: { - ...getScriptPreloadAttrs( - router.ssr?.manifest, - preload, - assetCrossOrigin, - ), - }, - }) - }) - }) - - return preloadMeta - }) - - const headScripts = Vue.computed>(() => - ( - matches.value - .map((match) => match.headScripts!) - .flat(1) - .filter(Boolean) as Array - ).map(({ children, ...script }) => ({ - tag: 'script', - attrs: { - ...script, - }, - children, - })), - ) - - const manifestAssets = Vue.computed>(() => { + const tags = Vue.computed>(() => { + const currentMatches = matches.value + const tags: Array = [] const manifest = router.ssr?.manifest - - const assets: Array = [] - - matches.value.forEach((match) => { - const routeManifest = manifest?.routes[match.routeId] - - routeManifest?.css?.forEach((link) => { + for (const match of currentMatches) { + for (const link of manifest?.routes[match.routeId]?.css ?? []) { const resolvedLink = resolveManifestCssLink(link) - assets.push({ + tags.push({ tag: 'link', attrs: { rel: 'stylesheet', @@ -150,11 +35,10 @@ export const useTags = (assetCrossOrigin?: AssetCrossOriginConfig) => { resolvedLink.crossOrigin, }, }) - }) - }) - + } + } if (manifest?.inlineStyle) { - assets.push({ + tags.push({ tag: 'style', attrs: manifest.inlineStyle.attrs, children: manifest.inlineStyle.children, @@ -162,16 +46,97 @@ export const useTags = (assetCrossOrigin?: AssetCrossOriginConfig) => { }) } - return assets - }) + const resultMeta: Array = [] + const metaByAttribute: Record = {} + let title: RouterManagedTag | undefined + for (let i = currentMatches.length - 1; i >= 0; i--) { + const metas = currentMatches[i]!.meta + if (!metas) { + continue + } + for (let j = metas.length - 1; j >= 0; j--) { + const m = metas[j] + if (!m) { + continue + } + + if (m.title) { + if (!title) { + title = { + tag: 'title', + children: m.title, + } + } + } else if ('script:ld+json' in m) { + // Content is HTML-escaped to prevent XSS when injected via innerHTML. + try { + resultMeta.push({ + tag: 'script', + attrs: { type: 'application/ld+json' }, + children: escapeHtml(JSON.stringify(m['script:ld+json'])), + }) + } catch { + // Skip invalid JSON-LD objects. + } + } else { + const attribute = m.name ?? m.property + if (attribute) { + if (metaByAttribute[attribute]) { + continue + } else { + metaByAttribute[attribute] = true + } + } + resultMeta.push({ + tag: 'meta', + attrs: { + ...m, + }, + }) + } + } + } - return () => { - const tags: Array = [] - tags.push(...manifestAssets.value) - appendUniqueUserTags(tags, meta.value) - tags.push(...preloadMeta.value) - appendUniqueUserTags(tags, links.value) - appendUniqueUserTags(tags, headScripts.value) + if (title) { + resultMeta.push(title) + } + resultMeta.reverse() + appendUniqueUserTags(tags, resultMeta) + + const preloads: Array = [] + const links: Array = [] + const headScripts: Array = [] + for (const match of currentMatches) { + for (const preload of manifest?.routes[match.routeId]?.preloads ?? []) { + if (preload) { + preloads.push({ + tag: 'link', + attrs: { + ...getScriptPreloadAttrs(manifest, preload, assetCrossOrigin), + }, + }) + } + } + for (const link of match.links ?? []) { + if (link) { + links.push({ tag: 'link', attrs: { ...link } }) + } + } + for (const script of match.headScripts ?? []) { + if (script) { + const { children, ...attrs } = script + headScripts.push({ + tag: 'script', + attrs: { ...attrs }, + children: children as string | undefined, + }) + } + } + } + tags.push(...preloads) + appendUniqueUserTags(tags, links) + appendUniqueUserTags(tags, headScripts) return tags - } + }) + return () => tags.value } diff --git a/packages/vue-router/src/lazyRouteComponent.tsx b/packages/vue-router/src/lazyRouteComponent.tsx index 77a2233573..527f3d15ad 100644 --- a/packages/vue-router/src/lazyRouteComponent.tsx +++ b/packages/vue-router/src/lazyRouteComponent.tsx @@ -44,6 +44,7 @@ export function lazyRouteComponent< // Use existing promise or create new one if (!loadPromise) { + error = undefined loadPromise = importer() .then((res) => { loadPromise = undefined diff --git a/packages/vue-router/src/matchContext.tsx b/packages/vue-router/src/matchContext.tsx index 1a8b7fb167..01ed6d35de 100644 --- a/packages/vue-router/src/matchContext.tsx +++ b/packages/vue-router/src/matchContext.tsx @@ -1,39 +1,8 @@ -import * as Vue from 'vue' - -// Reactive nearest-match context used by hooks that work relative to the -// current match in the tree. -export const matchContext = Symbol('TanStackRouterMatch') as Vue.InjectionKey< - Vue.Ref -> - -// Pending match context for nearest-match lookups -export const pendingMatchContext = Symbol( - 'TanStackRouterPendingMatch', -) as Vue.InjectionKey> - -// Dummy pending context when nearest pending state is not needed -export const dummyPendingMatchContext = Symbol( - 'TanStackRouterDummyPendingMatch', -) as Vue.InjectionKey> +import type { InjectionKey } from 'vue' // Stable routeId context — a plain string (not reactive) that identifies // which route this component belongs to. Provided by Match, consumed by // MatchInner, Outlet, and useMatch for routeId-based store lookups. export const routeIdContext = Symbol( 'TanStackRouterRouteId', -) as Vue.InjectionKey - -/** - * Retrieves nearest pending-match state from the component tree - */ -export function injectPendingMatch(): Vue.Ref { - return Vue.inject(pendingMatchContext, Vue.ref(false)) -} - -/** - * Retrieves dummy pending-match state from the component tree - * This only exists so we can conditionally inject a value when we are not interested in the nearest pending match - */ -export function injectDummyPendingMatch(): Vue.Ref { - return Vue.inject(dummyPendingMatchContext, Vue.ref(false)) -} +) as InjectionKey diff --git a/packages/vue-router/src/not-found.tsx b/packages/vue-router/src/not-found.tsx index f676c66e99..f0ebf678ea 100644 --- a/packages/vue-router/src/not-found.tsx +++ b/packages/vue-router/src/not-found.tsx @@ -16,7 +16,7 @@ export function CatchNotFound(props: { router.stores.location, (location) => location.pathname, ) - const status = useStore(router.stores.status, (value) => value) + const status = useStore(router.stores.status) // Create a function that returns a VNode to match the SyncRouteComponent signature const errorComponentFn = (componentProps: ErrorComponentProps) => { diff --git a/packages/vue-router/src/routerStores.ts b/packages/vue-router/src/routerStores.ts index da943fe990..a7aafa0287 100644 --- a/packages/vue-router/src/routerStores.ts +++ b/packages/vue-router/src/routerStores.ts @@ -1,19 +1,9 @@ import { batch, createAtom } from '@tanstack/vue-store' -import type { - AnyRoute, - GetStoreConfig, - RouterStores, -} from '@tanstack/router-core' +import type { GetStoreConfig } from '@tanstack/router-core' import type { Readable } from '@tanstack/vue-store' declare module '@tanstack/router-core' { export interface RouterReadableStore extends Readable {} - export interface RouterStores { - /** Maps each active routeId to the matchId of its child in the match tree. */ - childMatchIdByRouteId: RouterReadableStore> - /** Maps each pending routeId to true for quick lookup. */ - pendingRouteIds: RouterReadableStore> - } } export const getStoreFactory: GetStoreConfig = (_opts) => { @@ -21,34 +11,5 @@ export const getStoreFactory: GetStoreConfig = (_opts) => { createMutableStore: createAtom, createReadonlyStore: createAtom, batch, - init: (stores: RouterStores) => { - // Single derived store: one reactive node that maps every active - // routeId to its child's matchId. Depends only on matchesId + - // the pool's routeId tags (which are set during reconciliation). - // Outlet reads the map and then does a direct pool lookup. - stores.childMatchIdByRouteId = createAtom(() => { - const ids = stores.matchesId.get() - const obj: Record = {} - for (let i = 0; i < ids.length - 1; i++) { - const parentStore = stores.matchStores.get(ids[i]!) - if (parentStore?.routeId) { - obj[parentStore.routeId] = ids[i + 1]! - } - } - return obj - }) - - stores.pendingRouteIds = createAtom(() => { - const ids = stores.pendingIds.get() - const obj: Record = {} - for (const id of ids) { - const store = stores.pendingMatchStores.get(id) - if (store?.routeId) { - obj[store.routeId] = true - } - } - return obj - }) - }, } } diff --git a/packages/vue-router/src/ssr/RouterClient.tsx b/packages/vue-router/src/ssr/RouterClient.tsx index 8ed9401669..b21f0fc9f5 100644 --- a/packages/vue-router/src/ssr/RouterClient.tsx +++ b/packages/vue-router/src/ssr/RouterClient.tsx @@ -4,7 +4,7 @@ import { HeadContent } from '../HeadContent' import { RouterProvider } from '../RouterProvider' import type { AnyRouter } from '@tanstack/router-core' -let hydrationPromise: Promise>> | undefined +let hydrationPromise: Promise | undefined export const RouterClient = Vue.defineComponent({ name: 'RouterClient', @@ -17,25 +17,14 @@ export const RouterClient = Vue.defineComponent({ setup(props) { const isHydrated = Vue.ref(false) - if (!hydrationPromise) { - if (!props.router.stores.matchesId.get().length) { - hydrationPromise = hydrate(props.router) - } else { - hydrationPromise = Promise.resolve() - } - } + hydrationPromise ??= hydrate(props.router).finally(() => window.$_TSR!.h()) Vue.onMounted(() => { - hydrationPromise!.then(() => { + return hydrationPromise!.then(() => { isHydrated.value = true }) }) - // For SSR, we're already hydrated - if (typeof window === 'undefined') { - isHydrated.value = true - } - return () => { if (!isHydrated.value) { return null diff --git a/packages/vue-router/src/ssr/renderRouterToStream.tsx b/packages/vue-router/src/ssr/renderRouterToStream.tsx index efad4fc2eb..d352c52497 100644 --- a/packages/vue-router/src/ssr/renderRouterToStream.tsx +++ b/packages/vue-router/src/ssr/renderRouterToStream.tsx @@ -112,7 +112,10 @@ export const renderRouterToStream = async ({ } return new Response(`${fullHtml}`, { - status: router.stores.statusCode.get(), + status: + router._serverResult?.type === 'render' + ? router._serverResult.status + : 200, headers: responseHeaders, }) } finally { @@ -211,13 +214,16 @@ export const renderRouterToStream = async ({ const responseStream = transformReadableStreamWithRouter( router, doctypedStream, - { onAbort: abortVuePipe }, + { signal: request.signal, onAbort: abortVuePipe }, ) return createSsrStreamResponse( router, new Response(responseStream as any, { - status: router.stores.statusCode.get(), + status: + router._serverResult?.type === 'render' + ? router._serverResult.status + : 200, headers: responseHeaders, }), ) diff --git a/packages/vue-router/src/ssr/renderRouterToString.tsx b/packages/vue-router/src/ssr/renderRouterToString.tsx index b551dc5dfc..723aa0b91e 100644 --- a/packages/vue-router/src/ssr/renderRouterToString.tsx +++ b/packages/vue-router/src/ssr/renderRouterToString.tsx @@ -24,7 +24,10 @@ export const renderRouterToString = async ({ } return new Response(`${html}`, { - status: router.stores.statusCode.get(), + status: + router._serverResult?.type === 'render' + ? router._serverResult.status + : 200, headers: responseHeaders, }) } catch (error) { diff --git a/packages/vue-router/src/useMatch.tsx b/packages/vue-router/src/useMatch.tsx index 29047e5d48..a02b5ae109 100644 --- a/packages/vue-router/src/useMatch.tsx +++ b/packages/vue-router/src/useMatch.tsx @@ -2,11 +2,7 @@ import * as Vue from 'vue' import { invariant } from '@tanstack/router-core' import { useStore } from '@tanstack/vue-store' import { isServer } from '@tanstack/router-core/isServer' -import { - injectDummyPendingMatch, - injectPendingMatch, - routeIdContext, -} from './matchContext' +import { routeIdContext } from './matchContext' import { useRouter } from './useRouter' import type { AnyRouter, @@ -83,7 +79,7 @@ export function useMatch< const nearestRouteId = opts.from ? undefined : Vue.inject(routeIdContext) const matchStore = (opts.from ?? nearestRouteId) - ? router.stores.getRouteMatchStore(opts.from ?? nearestRouteId!) + ? router.stores.getMatchStore(opts.from ?? nearestRouteId!) : undefined const match = matchStore?.get() @@ -114,53 +110,37 @@ export function useMatch< > } - const hasPendingNearestMatch = opts.from - ? injectDummyPendingMatch() - : injectPendingMatch() // Set up reactive match value based on lookup strategy. let match: Readonly> + const nearestRouteId = Vue.inject(routeIdContext) if (opts.from) { - // routeId case: single subscription via per-routeId computed store. - // The store reference is stable (cached by routeId). - const matchStore = router.stores.getRouteMatchStore(opts.from) - match = useStore(matchStore, (value) => value) + // routeId case: subscribe to the stable per-route presentation atom. + const matchStore = router.stores.getMatchStore(opts.from) + match = useStore(matchStore) } else { - // matchId case: use routeId from context for stable store lookup. + // Nearest-match case: use the routeId from context for stable lookup. // The routeId is provided by the nearest Match component and doesn't // change for the component's lifetime, so the store is stable. - const nearestRouteId = Vue.inject(routeIdContext) if (nearestRouteId) { - match = useStore( - router.stores.getRouteMatchStore(nearestRouteId), - (value) => value, - ) + match = useStore(router.stores.getMatchStore(nearestRouteId)) } else { // No route context — will fall through to error handling below match = Vue.ref(undefined) as Readonly> } } - const hasPendingRouteMatch = opts.from - ? useStore(router.stores.pendingRouteIds, (ids) => ids) - : undefined - const isTransitioning = useStore( - router.stores.isTransitioning, - (value) => value, - { equal: Object.is }, - ) - - const result = Vue.computed(() => { - const selectedMatch = match.value + const ownsMatch = + nearestRouteId !== undefined && + (opts.from ?? nearestRouteId) === nearestRouteId + let lastOwnedMatch: any + const result = Vue.computed(() => { + if (match.value !== undefined && ownsMatch) { + lastOwnedMatch = match.value + } + const selectedMatch = match.value ?? lastOwnedMatch if (selectedMatch === undefined) { - const hasPendingMatch = opts.from - ? Boolean(hasPendingRouteMatch?.value[opts.from!]) - : hasPendingNearestMatch.value - if ( - !hasPendingMatch && - !isTransitioning.value && - (opts.shouldThrow ?? true) - ) { + if (opts.shouldThrow ?? true) { if (process.env.NODE_ENV !== 'production') { throw new Error( `Invariant failed: Could not find ${opts.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`, diff --git a/packages/vue-router/tests/Scripts.test.tsx b/packages/vue-router/tests/Scripts.test.tsx index 5cd2eacd4f..8d6724076b 100644 --- a/packages/vue-router/tests/Scripts.test.tsx +++ b/packages/vue-router/tests/Scripts.test.tsx @@ -1,4 +1,4 @@ -import { afterEach, describe, expect, test } from 'vitest' +import { afterEach, describe, expect, test, vi } from 'vitest' import { cleanup, fireEvent, @@ -7,11 +7,14 @@ import { waitFor, } from '@testing-library/vue' import { Teleport } from 'vue' +import { hydrate } from '@tanstack/router-core/ssr/client' +import { dehydrateSsrMatchId } from '../../router-core/src/ssr/ssr-match-id' import { HeadContent, Link, Outlet, + RouterContextProvider, RouterProvider, createBrowserHistory, createMemoryHistory, @@ -48,6 +51,7 @@ afterEach(() => { cleanup() browserHistories.splice(0).forEach((history) => history.destroy()) window.history.replaceState(null, 'root', '/') + delete window.$_TSR }) describe('ssr scripts', () => { @@ -148,6 +152,98 @@ describe('ssr scripts', () => { }) describe('ssr HeadContent', () => { + test('renders descendant assets during a data-only hydration handoff', async () => { + const rootRoute = createRootRoute({}) + const dataOnlyRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/report', + ssr: 'data-only', + loader: () => 'report', + }) + const childRoute = createRoute({ + getParentRoute: () => dataOnlyRoute, + path: '/details', + loader: () => 'details', + head: () => ({ + meta: [{ name: 'vue-data-only-child', content: 'visible' }], + }), + scripts: () => [ + { + id: 'vue-data-only-body-script', + type: 'application/json', + children: '{"source":"body"}', + }, + ], + }) + const router = createRouter({ + history: createMemoryHistory({ + initialEntries: ['/report/details'], + }), + routeTree: rootRoute.addChildren([ + dataOnlyRoute.addChildren([childRoute]), + ]), + }) + const matches = router.matchRoutes(router.latestLocation) + window.$_TSR = { + router: { + dehydratedData: {}, + manifest: { + routes: { + [childRoute.id]: { + preloads: ['/vue-data-only-manifest.js'], + scripts: [ + { + attrs: { + id: 'vue-data-only-manifest-script', + type: 'application/json', + }, + children: '{"source":"manifest"}', + }, + ], + }, + }, + }, + matches: matches.map((match, index) => ({ + i: dehydrateSsrMatchId(match.id), + s: 'success', + ssr: index === 1 ? 'data-only' : true, + l: index ? (index === 1 ? 'report' : 'details') : undefined, + u: Date.now(), + })), + }, + h: vi.fn(), + e: vi.fn(), + c: vi.fn(), + p: vi.fn(), + buffer: [], + } + + await hydrate(router) + + expect(router.state.matches.map((match) => match.status)).toEqual([ + 'success', + 'pending', + 'success', + ]) + render( + + + + , + ) + + expect( + document.querySelector('meta[name="vue-data-only-child"]'), + ).not.toBeNull() + expect( + document.querySelector('link[href="/vue-data-only-manifest.js"]'), + ).not.toBeNull() + expect(document.querySelector('#vue-data-only-body-script')).not.toBeNull() + expect( + document.querySelector('#vue-data-only-manifest-script'), + ).not.toBeNull() + }) + test('applies assetCrossOrigin to manifest stylesheets and preloads', async () => { const history = createTestBrowserHistory() diff --git a/packages/vue-router/tests/Transitioner.test.tsx b/packages/vue-router/tests/Transitioner.test.tsx index 6ae0841d14..84adcf8af8 100644 --- a/packages/vue-router/tests/Transitioner.test.tsx +++ b/packages/vue-router/tests/Transitioner.test.tsx @@ -1,7 +1,8 @@ -import { afterEach, describe, expect, it, vi } from 'vitest' +import { afterEach, describe, expect, it, onTestFinished, vi } from 'vitest' import { cleanup, render, waitFor } from '@testing-library/vue' import { RouterProvider, + createControlledPromise, createMemoryHistory, createRootRoute, createRoute, @@ -38,4 +39,38 @@ describe('Transitioner', () => { expect(view.getByText('Index')).toBeInTheDocument() }) }) + + it('reuses a public load already in progress when the provider mounts', async () => { + const loaderGate = createControlledPromise() + const beforeLoad = vi.fn() + const loader = vi.fn(() => loaderGate) + const rootRoute = createRootRoute() + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + beforeLoad, + loader, + component: () =>
    Index
    , + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + const load = router.load() + onTestFinished(async () => { + loaderGate.resolve() + await Promise.allSettled([load]) + }) + + await waitFor(() => expect(loader).toHaveBeenCalledTimes(1)) + + const view = render() + loaderGate.resolve() + await load + + expect(await view.findByText('Index')).toBeInTheDocument() + expect(beforeLoad).toHaveBeenCalledTimes(1) + expect(loader).toHaveBeenCalledTimes(1) + }) }) diff --git a/packages/vue-router/tests/component-preload-retry.test.tsx b/packages/vue-router/tests/component-preload-retry.test.tsx new file mode 100644 index 0000000000..9fb8ad5f5a --- /dev/null +++ b/packages/vue-router/tests/component-preload-retry.test.tsx @@ -0,0 +1,65 @@ +import { afterEach, expect, test, vi } from 'vitest' +import { cleanup, fireEvent, render, screen } from '@testing-library/vue' +import { + RouterProvider, + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, + lazyRouteComponent, + useRouter, +} from '../src' +import type { ErrorComponentProps } from '../src' + +afterEach(() => { + cleanup() + vi.restoreAllMocks() +}) + +test('a failed component download is retried from the route error UI', async () => { + vi.spyOn(console, 'error').mockImplementation(() => {}) + + const PageContent = () =>
    Page content
    + const importer = vi + .fn<() => Promise<{ default: typeof PageContent }>>() + .mockRejectedValueOnce(new Error('component download failed')) + .mockResolvedValue({ default: PageContent }) + const Page = lazyRouteComponent(importer) + + function RouteError(props: ErrorComponentProps) { + const router = useRouter() + return ( + + ) + } + + const rootRoute = createRootRoute() + const pageRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/page', + component: Page, + errorComponent: RouteError, + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([pageRoute]), + history: createMemoryHistory({ initialEntries: ['/page'] }), + }) + + render() + + const retryButton = await screen.findByRole('button', { name: 'Retry' }) + expect(importer).toHaveBeenCalledTimes(1) + + await fireEvent.click(retryButton) + + expect(await screen.findByText('Page content')).toBeInTheDocument() + expect(importer).toHaveBeenCalledTimes(2) +}) diff --git a/packages/vue-router/tests/hydration-capped-boundary-pending.test.tsx b/packages/vue-router/tests/hydration-capped-boundary-pending.test.tsx new file mode 100644 index 0000000000..e8e0db4b67 --- /dev/null +++ b/packages/vue-router/tests/hydration-capped-boundary-pending.test.tsx @@ -0,0 +1,153 @@ +import * as Vue from 'vue' +import { renderToString } from 'vue/server-renderer' +import { afterEach, describe, expect, test, vi } from 'vitest' +import { hydrate as hydrateRouter } from '@tanstack/router-core/ssr/client' +import { + Outlet, + RouterProvider, + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, + notFound, +} from '../src' +import { dehydrateToBootstrap } from './ssr-test-utils' +import type { TsrSsrGlobal } from '@tanstack/router-core/ssr/client' + +declare global { + interface Window { + $_TSR?: TsrSsrGlobal + } +} + +const testCleanups: Array<() => void | Promise> = [] + +afterEach(async () => { + while (testCleanups.length) { + await testCleanups.pop()!() + } + vi.restoreAllMocks() + window.$_TSR = undefined + document.body.innerHTML = '' +}) + +describe('hydrating a server-capped boundary lane', () => { + test.each([ + ['error', 'parent'], + ['notFound', 'parent'], + ['error', 'root'], + ['notFound', 'root'], + ] as const)( + 'keeps the server-rendered %s %s boundary visible through hydration', + async (outcome, boundary) => { + const childLoader = vi.fn(() => 'child data') + const makeRouteTree = () => { + const boundaryOptions = { + beforeLoad: () => { + throw outcome === 'notFound' + ? notFound() + : new Error('server route failure') + }, + pendingComponent: () => ( +
    Boundary pending
    + ), + errorComponent: () => ( +
    Boundary error
    + ), + notFoundComponent: () => ( +
    Boundary not found
    + ), + } + const rootRoute = createRootRoute({ + component: Outlet, + ...(boundary === 'root' ? boundaryOptions : {}), + }) + const parentRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/parent', + component: Outlet, + ...(boundary === 'parent' ? boundaryOptions : {}), + }) + const childRoute = createRoute({ + getParentRoute: () => parentRoute, + path: '/child', + loader: childLoader, + component: () =>
    Child
    , + }) + return rootRoute.addChildren([parentRoute.addChildren([childRoute])]) + } + + const serverRouter = createRouter({ + routeTree: makeRouteTree(), + history: createMemoryHistory({ + initialEntries: ['/parent/child'], + }), + }) + serverRouter.isServer = true + testCleanups.push(() => serverRouter.serverSsr?.cleanup()) + window.$_TSR = await dehydrateToBootstrap(serverRouter) + + const serverMatches = serverRouter.stores.matches.get() + expect(serverMatches).toHaveLength(3) + const serverBoundary = serverMatches[boundary === 'root' ? 0 : 1]! + if (outcome === 'notFound' && boundary === 'root') { + expect(serverBoundary).toMatchObject({ + status: 'success', + _notFound: true, + }) + } else { + expect(serverBoundary.status).toBe(outcome) + } + + const serverApp = Vue.createSSRApp( + Vue.defineComponent({ + setup: () => () => , + }), + ) + const serverHtml = await renderToString(serverApp) + const expectedBoundary = + outcome === 'notFound' ? 'Boundary not found' : 'Boundary error' + expect(serverHtml).toContain(expectedBoundary) + + const clientRouter = createRouter({ + routeTree: makeRouteTree(), + history: createMemoryHistory({ + initialEntries: ['/parent/child'], + }), + }) + + await hydrateRouter(clientRouter) + + const container = document.createElement('div') + container.innerHTML = serverHtml + document.body.appendChild(container) + const consoleError = vi + .spyOn(console, 'error') + .mockImplementation(() => {}) + const consoleWarn = vi.spyOn(console, 'warn').mockImplementation(() => {}) + const clientApp = Vue.createSSRApp( + Vue.defineComponent({ + setup: () => () => , + }), + ) + let clientAppMounted = false + testCleanups.push(() => { + if (clientAppMounted) { + clientApp.unmount() + } + container.remove() + }) + + clientApp.mount(container) + clientAppMounted = true + await Vue.nextTick() + + expect(container).toHaveTextContent(expectedBoundary) + expect(container).not.toHaveTextContent('Boundary pending') + expect( + [consoleError.mock.calls, consoleWarn.mock.calls].flat(2).join(' '), + ).not.toMatch(/hydration|mismatch/i) + expect(childLoader).not.toHaveBeenCalled() + }, + ) +}) diff --git a/packages/vue-router/tests/loaders.test.tsx b/packages/vue-router/tests/loaders.test.tsx index edc3b2a9bd..99ef9ef5d1 100644 --- a/packages/vue-router/tests/loaders.test.tsx +++ b/packages/vue-router/tests/loaders.test.tsx @@ -282,6 +282,44 @@ test('throw error from loader upon initial load', async () => { expect(errorElement).toBeInTheDocument() }) +// https://github.com/TanStack/router/pull/7673 +test('#7673: aborted loader does not render the route component with undefined loaderData', async () => { + const abortError = new DOMException('Aborted', 'AbortError') + const routeComponentRendered = vi.fn() + const renderedError = vi.fn() + const rootRoute = createRootRoute({}) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + loader: (): Promise<{ value: string }> => Promise.reject(abortError), + component: () => { + routeComponentRendered() + const data = indexRoute.useLoaderData() + return
    {data.value.value}
    + }, + errorComponent: ({ error }) => { + renderedError(error) + return
    indexErrorComponent
    + }, + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute]), + }) + + render() + + expect(await screen.findByTestId('index-error')).toBeInTheDocument() + expect(screen.queryByTestId('index-content')).not.toBeInTheDocument() + expect(routeComponentRendered).not.toHaveBeenCalled() + expect(renderedError).toHaveBeenCalledWith(abortError) + expect( + router.state.matches.find((match) => match.routeId === indexRoute.id), + ).toMatchObject({ + status: 'error', + error: abortError, + }) +}) + test('throw error from beforeLoad when navigating to route', async () => { const rootRoute = createRootRoute({}) diff --git a/packages/vue-router/tests/redirect.test.tsx b/packages/vue-router/tests/redirect.test.tsx index 0d7c2c92ef..e26fe9a372 100644 --- a/packages/vue-router/tests/redirect.test.tsx +++ b/packages/vue-router/tests/redirect.test.tsx @@ -302,108 +302,4 @@ describe('redirect', () => { expect(window.location.pathname).toBe('/final') }) }) - - describe('SSR', () => { - test('when `redirect` is thrown in `beforeLoad`', async () => { - const rootRoute = createRootRoute() - - const indexRoute = createRoute({ - path: '/', - getParentRoute: () => rootRoute, - beforeLoad: () => { - throw redirect({ - to: '/about', - }) - }, - }) - - const aboutRoute = createRoute({ - path: '/about', - getParentRoute: () => rootRoute, - component: () => { - return <>About - }, - }) - - const router = createRouter({ - routeTree: rootRoute.addChildren([indexRoute, aboutRoute]), - // Mock server mode - isServer: true, - history: createMemoryHistory({ - initialEntries: ['/'], - }), - }) - - await router.load() - - expect(router.state.redirect).toBeDefined() - expect(router.state.redirect).toBeInstanceOf(Response) - const redirectResponse = router.state.redirect! - - expect(redirectResponse.options).toEqual({ - _fromLocation: expect.objectContaining({ - hash: '', - href: '/', - pathname: '/', - search: {}, - searchStr: '', - }), - to: '/about', - href: '/about', - statusCode: 307, - }) - }) - - test('when `redirect` is thrown in `loader`', async () => { - const rootRoute = createRootRoute() - - const indexRoute = createRoute({ - path: '/', - getParentRoute: () => rootRoute, - loader: () => { - throw redirect({ - to: '/about', - }) - }, - }) - - const aboutRoute = createRoute({ - path: '/about', - getParentRoute: () => rootRoute, - component: () => { - return <>About - }, - }) - - const router = createRouter({ - history: createMemoryHistory({ - initialEntries: ['/'], - }), - routeTree: rootRoute.addChildren([indexRoute, aboutRoute]), - // Mock server mode - isServer: true, - }) - - await router.load() - - const currentRedirect = router.state.redirect - - expect(currentRedirect).toBeDefined() - expect(currentRedirect).toBeInstanceOf(Response) - const redirectResponse = currentRedirect! - - expect(redirectResponse.options).toEqual({ - _fromLocation: expect.objectContaining({ - hash: '', - href: '/', - pathname: '/', - search: {}, - searchStr: '', - }), - to: '/about', - href: '/about', - statusCode: 307, - }) - }) - }) }) diff --git a/packages/vue-router/tests/router-client-stream-cleanup.test.tsx b/packages/vue-router/tests/router-client-stream-cleanup.test.tsx new file mode 100644 index 0000000000..071df9cfce --- /dev/null +++ b/packages/vue-router/tests/router-client-stream-cleanup.test.tsx @@ -0,0 +1,46 @@ +import { afterEach, expect, test, vi } from 'vitest' +import { cleanup, render, screen, waitFor } from '@testing-library/vue' +import { createMemoryHistory } from '@tanstack/history' +import * as Vue from 'vue' +import { RouterClient } from '../src/ssr/RouterClient' +import { createRootRoute, createRouter } from '../src' + +const hydrate = vi.hoisted(() => vi.fn()) + +vi.mock('@tanstack/router-core/ssr/client', () => ({ hydrate })) + +afterEach(() => { + cleanup() + delete window.$_TSR + hydrate.mockReset() +}) + +test('RouterClient signals streaming cleanup without hiding a hydration failure', async () => { + const error = new Error('hydration failed') + hydrate.mockRejectedValue(error) + const rootRoute = createRootRoute({ component: () =>
    Ready
    }) + const router = createRouter({ + routeTree: rootRoute, + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + const hydrated = vi.fn() + window.$_TSR = { h: hydrated } as any + + const Boundary = Vue.defineComponent({ + setup(_, { slots }) { + const caught = Vue.ref() + Vue.onErrorCaptured((value) => { + caught.value = value + return false + }) + return () => caught.value?.message ?? slots.default?.() + }, + }) + + render(Boundary, { + slots: { default: () => Vue.h(RouterClient, { router }) }, + }) + + await waitFor(() => expect(hydrated).toHaveBeenCalledTimes(1)) + expect(await screen.findByText(error.message)).toBeInTheDocument() +}) diff --git a/packages/vue-router/tests/ssr-test-utils.ts b/packages/vue-router/tests/ssr-test-utils.ts new file mode 100644 index 0000000000..7d8c5c80f3 --- /dev/null +++ b/packages/vue-router/tests/ssr-test-utils.ts @@ -0,0 +1,43 @@ +import { runInNewContext } from 'node:vm' +import { attachRouterServerSsrUtils } from '@tanstack/router-core/ssr/server' +import type { AnyRouter } from '@tanstack/router-core' +import type { TsrSsrGlobal } from '@tanstack/router-core/ssr/client' + +export async function dehydrateToBootstrap( + router: AnyRouter, +): Promise { + try { + attachRouterServerSsrUtils({ router, manifest: { routes: {} } }) + await router.load() + await router.serverSsr!.dehydrate() + + const script = router.serverSsr!.takeBufferedScripts() + if (typeof script?.children !== 'string') { + throw new Error( + 'Expected server dehydration to produce a bootstrap script', + ) + } + + const context: { + document: { currentScript: { remove: () => void } } + self?: unknown + $_TSR?: TsrSsrGlobal + } = { + document: { + currentScript: { + remove() {}, + }, + }, + } + context.self = context + runInNewContext(script.children, context) + + if (!context.$_TSR) { + throw new Error('Expected bootstrap script to initialize $_TSR') + } + return context.$_TSR + } catch (error) { + router.serverSsr?.cleanup() + throw error + } +} diff --git a/packages/vue-router/tests/store-updates-during-navigation.test.tsx b/packages/vue-router/tests/store-updates-during-navigation.test.tsx index 1c40bf7be7..8ad9a4b423 100644 --- a/packages/vue-router/tests/store-updates-during-navigation.test.tsx +++ b/packages/vue-router/tests/store-updates-during-navigation.test.tsx @@ -138,7 +138,7 @@ describe("Store doesn't update *too many* times during navigation", () => { // that needs to be done during a navigation. // Any change that increases this number should be investigated. // Note: Vue has different update counts than React/Solid due to different reactivity - expect(updates).toBe(16) + expect(updates).toBe(6) }) test('redirection in preload', async () => { @@ -157,7 +157,7 @@ describe("Store doesn't update *too many* times during navigation", () => { // that needs to be done during a navigation. // Any change that increases this number should be investigated. // Note: Vue has different update counts than React/Solid due to different reactivity - expect(updates).toBe(5) + expect(updates).toBe(2) }) test('sync beforeLoad', async () => { @@ -174,7 +174,7 @@ describe("Store doesn't update *too many* times during navigation", () => { // that needs to be done during a navigation. // Any change that increases this number should be investigated. // Note: Vue has different update counts than React/Solid due to different reactivity - expect(updates).toBe(12) + expect(updates).toBe(4) }) test('nothing', async () => { @@ -186,7 +186,7 @@ describe("Store doesn't update *too many* times during navigation", () => { // that needs to be done during a navigation. // Any change that increases this number should be investigated. // Note: Vue has different update counts than React/Solid due to different reactivity - expect(updates).toBe(6) + expect(updates).toBe(2) }) test('not found in beforeLoad', async () => { @@ -202,7 +202,7 @@ describe("Store doesn't update *too many* times during navigation", () => { // that needs to be done during a navigation. // Any change that increases this number should be investigated. // Note: Vue has different update counts than React/Solid due to different reactivity - expect(updates).toBe(9) + expect(updates).toBe(2) }) test('hover preload, then navigate, w/ async loaders', async () => { @@ -229,7 +229,7 @@ describe("Store doesn't update *too many* times during navigation", () => { // that needs to be done during a navigation. // Any change that increases this number should be investigated. // Note: Vue has different update counts than React/Solid due to different reactivity - expect(updates).toBe(17) + expect(updates).toBe(3) }) test('navigate, w/ preloaded & async loaders', async () => { @@ -246,7 +246,7 @@ describe("Store doesn't update *too many* times during navigation", () => { // that needs to be done during a navigation. // Any change that increases this number should be investigated. // Note: Vue has different update counts than React/Solid due to different reactivity - expect(updates).toBe(10) + expect(updates).toBe(2) }) test('navigate, w/ preloaded & sync loaders', async () => { @@ -263,7 +263,7 @@ describe("Store doesn't update *too many* times during navigation", () => { // that needs to be done during a navigation. // Any change that increases this number should be investigated. // Note: Vue has different update counts than React/Solid due to different reactivity - expect(updates).toBe(6) + expect(updates).toBe(2) }) test('navigate, w/ previous navigation & async loader', async () => { @@ -280,7 +280,7 @@ describe("Store doesn't update *too many* times during navigation", () => { // that needs to be done during a navigation. // Any change that increases this number should be investigated. // Note: Vue has different update counts than React/Solid due to different reactivity - expect(updates).toBe(6) + expect(updates).toBe(2) }) test('preload a preloaded route w/ async loader', async () => { @@ -299,6 +299,6 @@ describe("Store doesn't update *too many* times during navigation", () => { // that needs to be done during a navigation. // Any change that increases this number should be investigated. // Note: Vue has different update counts than React/Solid due to different reactivity - expect(updates).toBe(2) + expect(updates).toBe(0) }) }) diff --git a/packages/vue-router/tests/transitioner-listener-errors.test.tsx b/packages/vue-router/tests/transitioner-listener-errors.test.tsx new file mode 100644 index 0000000000..60e0874f27 --- /dev/null +++ b/packages/vue-router/tests/transitioner-listener-errors.test.tsx @@ -0,0 +1,75 @@ +import { cleanup, render, screen } from '@testing-library/vue' +import { afterEach, expect, onTestFinished, test, vi } from 'vitest' +import { createMemoryHistory } from '@tanstack/history' +import { + Outlet, + RouterProvider, + createRootRoute, + createRoute, + createRouter, +} from '../src' + +afterEach(() => { + cleanup() +}) + +test('a throwing load-event listener cannot interrupt later listeners, route hooks, or navigations', async () => { + const firstOnEnter = vi.fn() + const secondOnEnter = vi.fn() + const listenerError = new Error('onLoad listener failed') + const throwingOnLoad = vi.fn() + const laterOnLoad = vi.fn() + + const rootRoute = createRootRoute({ component: () => }) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
    Index route
    , + }) + const firstRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/first', + onEnter: firstOnEnter, + component: () =>
    First route
    , + }) + const secondRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/second', + onEnter: secondOnEnter, + component: () =>
    Second route
    , + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute, firstRoute, secondRoute]), + history: createMemoryHistory({ initialEntries: ['/'] }), + }) + + render() + expect(await screen.findByText('Index route')).toBeTruthy() + + const unsubscribeThrowing = router.subscribe('onLoad', (event) => { + throwingOnLoad(event.toLocation.pathname) + if (event.toLocation.pathname === '/first') { + throw listenerError + } + }) + const unsubscribeLater = router.subscribe('onLoad', (event) => { + laterOnLoad(event.toLocation.pathname) + }) + onTestFinished(() => { + unsubscribeThrowing() + unsubscribeLater() + }) + + await router.navigate({ to: '/first' }) + expect(await screen.findByText('First route')).toBeTruthy() + expect(firstOnEnter).toHaveBeenCalledTimes(1) + expect(throwingOnLoad.mock.calls).toEqual([['/first']]) + expect(laterOnLoad.mock.calls).toEqual([['/first']]) + + await router.navigate({ to: '/second' }) + expect(await screen.findByText('Second route')).toBeTruthy() + expect(firstOnEnter).toHaveBeenCalledTimes(1) + expect(secondOnEnter).toHaveBeenCalledTimes(1) + expect(throwingOnLoad.mock.calls).toEqual([['/first'], ['/second']]) + expect(laterOnLoad.mock.calls).toEqual([['/first'], ['/second']]) +}) diff --git a/packages/vue-router/tests/transitioner-remount-rendered.test.tsx b/packages/vue-router/tests/transitioner-remount-rendered.test.tsx index 179a7434f7..e39aaeee06 100644 --- a/packages/vue-router/tests/transitioner-remount-rendered.test.tsx +++ b/packages/vue-router/tests/transitioner-remount-rendered.test.tsx @@ -31,11 +31,11 @@ function setup() { routeTree: rootRoute.addChildren([indexRoute, nextRoute]), history, }) - return router + return { history, router } } test('onRendered runs after the destination DOM has committed', async () => { - const router = setup() + const { router } = setup() const initialRendered = vi.fn() const unsubscribeInitial = router.subscribe('onRendered', initialRendered) render() @@ -54,3 +54,28 @@ test('onRendered runs after the destination DOM has committed', async () => { unsubscribe() }) + +test('onRendered fires for a same-href navigation with a new history key', async () => { + const { router } = setup() + const onRendered = vi.fn() + const unsubscribe = router.subscribe('onRendered', onRendered) + render() + expect(await screen.findByText('Index')).toBeTruthy() + await waitFor(() => expect(onRendered).toHaveBeenCalledTimes(1)) + onRendered.mockClear() + + await router.navigate({ + to: '/', + state: { sameHrefState: true } as any, + }) + await waitFor(() => expect(onRendered).toHaveBeenCalledTimes(1)) + + const event = onRendered.mock.calls[0]![0] + expect(event.fromLocation?.state.sameHrefState).toBeUndefined() + expect(event.toLocation.state.sameHrefState).toBe(true) + expect(event.fromLocation?.href).toBe('/') + expect(event.toLocation.href).toBe('/') + expect(event.hrefChanged).toBe(false) + + unsubscribe() +}) diff --git a/packages/vue-router/tests/useMatch.test.tsx b/packages/vue-router/tests/useMatch.test.tsx index 3a802798d2..7a73cdbea7 100644 --- a/packages/vue-router/tests/useMatch.test.tsx +++ b/packages/vue-router/tests/useMatch.test.tsx @@ -235,6 +235,44 @@ describe('useMatch', () => { } }) + test('an outgoing component never observes its own match disappear', async () => { + const observedRouteIds: Array = [] + const rootRoute = createRootRoute({ component: () => }) + const firstRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/first', + component: Vue.defineComponent({ + setup() { + const match = useMatch({ + from: '/first', + shouldThrow: false, + }) + Vue.watchEffect(() => observedRouteIds.push(match.value?.routeId), { + flush: 'sync', + }) + return () =>
    First route
    + }, + }), + }) + const nextRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/next', + component: () =>
    Next route
    , + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([firstRoute, nextRoute]), + history: createMemoryHistory({ initialEntries: ['/first'] }), + }) + + render() + expect(await screen.findByText('First route')).toBeInTheDocument() + + await router.navigate({ to: '/next' }) + + expect(await screen.findByText('Next route')).toBeInTheDocument() + expect(observedRouteIds).not.toContain(undefined) + }) + describe('when match is not found', () => { test.each([undefined, true])( 'throws if shouldThrow = %s', diff --git a/packages/vue-start-client/src/hydrateStart.test.ts b/packages/vue-start-client/src/hydrateStart.test.ts new file mode 100644 index 0000000000..c338122c6c --- /dev/null +++ b/packages/vue-start-client/src/hydrateStart.test.ts @@ -0,0 +1,23 @@ +import { afterEach, expect, test, vi } from 'vitest' +import { hydrateStart } from './hydrateStart' + +const coreHydrateStart = vi.hoisted(() => vi.fn()) + +vi.mock('@tanstack/start-client-core/client', () => ({ + hydrateStart: coreHydrateStart, +})) + +afterEach(() => { + delete window.$_TSR + coreHydrateStart.mockReset() +}) + +test('signals streaming cleanup without hiding a hydration failure', async () => { + const error = new Error('hydration failed') + coreHydrateStart.mockRejectedValue(error) + const hydrated = vi.fn() + window.$_TSR = { h: hydrated } as any + + await expect(hydrateStart()).rejects.toBe(error) + expect(hydrated).toHaveBeenCalledTimes(1) +}) diff --git a/packages/vue-start-client/src/hydrateStart.ts b/packages/vue-start-client/src/hydrateStart.ts index b9e4860b57..7a90e6d27c 100644 --- a/packages/vue-start-client/src/hydrateStart.ts +++ b/packages/vue-start-client/src/hydrateStart.ts @@ -89,9 +89,15 @@ export function configureHydrationSuppressions( * suppression for routes with ssr: false or ssr: 'data-only' before returning */ export async function hydrateStart(): Promise { - const router = await coreHydrateStart() - // Install console suppression before app.mount() is called - // This catches the "Hydration completed but contains mismatches" error - suppressSsrHydrationMismatches(router) - return router + try { + const router = await coreHydrateStart() + // Install console suppression before app.mount() is called + // This catches the "Hydration completed but contains mismatches" error + suppressSsrHydrationMismatches(router) + return router + } catch (error) { + // A failed router hydration never mounts StartClient, so release the stream here. + window.$_TSR?.h() + throw error + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bbbc0e852c..dc45720175 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1457,6 +1457,77 @@ importers: specifier: ^8.0.14 version: 8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0) + e2e/react-router/issue-7120: + dependencies: + '@tanstack/react-router': + specifier: workspace:* + version: link:../../../packages/react-router + '@tanstack/router-plugin': + specifier: workspace:* + version: link:../../../packages/router-plugin + react: + specifier: ^19.2.3 + version: 19.2.3 + react-dom: + specifier: ^19.2.3 + version: 19.2.3(react@19.2.3) + devDependencies: + '@playwright/test': + specifier: ^1.61.0 + version: 1.61.1 + '@tanstack/router-e2e-utils': + specifier: workspace:^ + version: link:../../e2e-utils + '@types/react': + specifier: ^19.2.8 + version: 19.2.9 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.9) + '@vitejs/plugin-react': + specifier: ^6.0.1 + version: 6.0.1(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) + vite: + specifier: ^8.0.14 + version: 8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0) + + e2e/react-router/issue-7457: + dependencies: + '@tanstack/react-query': + specifier: ^5.99.0 + version: 5.99.0(react@19.2.3) + '@tanstack/react-router': + specifier: workspace:* + version: link:../../../packages/react-router + '@tanstack/router-plugin': + specifier: workspace:* + version: link:../../../packages/router-plugin + react: + specifier: ^19.2.3 + version: 19.2.3 + react-dom: + specifier: ^19.2.3 + version: 19.2.3(react@19.2.3) + devDependencies: + '@playwright/test': + specifier: ^1.61.0 + version: 1.61.1 + '@tanstack/router-e2e-utils': + specifier: workspace:^ + version: link:../../e2e-utils + '@types/react': + specifier: ^19.2.8 + version: 19.2.9 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.9) + '@vitejs/plugin-react': + specifier: ^6.0.1 + version: 6.0.1(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) + vite: + specifier: ^8.0.14 + version: 8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0) + e2e/react-router/view-transitions: dependencies: '@tailwindcss/vite': @@ -13299,6 +13370,9 @@ importers: specifier: ^5.1.22 version: 5.1.28 devDependencies: + '@tanstack/react-query': + specifier: ^5.99.0 + version: 5.99.0(react@19.2.3) '@testing-library/jest-dom': specifier: ^6.6.3 version: 6.6.3 @@ -13630,6 +13704,9 @@ importers: specifier: ^2.1.16 version: 2.1.16(csstype@3.2.3) devDependencies: + '@testing-library/jest-dom': + specifier: ^6.6.3 + version: 6.6.3 solid-js: specifier: 1.9.12 version: 1.9.12 From a4942ff68a81116540cb5e08d91e22ffb57ded1b Mon Sep 17 00:00:00 2001 From: Flo Date: Tue, 4 Aug 2026 21:19:20 +0200 Subject: [PATCH 45/51] fix(router): correct lane loader changeset (#7948) * fix(router): correct lane loader changeset * fix(router): keep lane loader release patch * docs(router): clarify removed internal stores --- .changeset/lane-match-loader-rewrite.md | 23 +++++++++++-------- .../router-devtools-core/src/AgeTicker.tsx | 2 +- .../tests/cache-replacement.test.ts | 3 ++- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/.changeset/lane-match-loader-rewrite.md b/.changeset/lane-match-loader-rewrite.md index 911a956864..a7d63e6d34 100644 --- a/.changeset/lane-match-loader-rewrite.md +++ b/.changeset/lane-match-loader-rewrite.md @@ -3,28 +3,33 @@ '@tanstack/react-router': patch '@tanstack/solid-router': patch '@tanstack/vue-router': patch +'@tanstack/router-devtools-core': patch --- Rewrite match loading around a lane-based scheduler that tracks each navigation, preload, and background reload as an ordered unit of work. This fixes pending/redirect/retry state leaking between overlapping navigations, restores correct SSR status codes for redirects, errors, and not-found responses, and closes hydration gaps where the client re-ran work the server had already completed. - Invalidation now retires matching active preloads so older speculative loader results cannot become fresh cache data after invalidation. - Route `headers()` now only runs on the server, matching the documented behavior — it is no longer invoked during client-side asset projection. -- Default `gcTime` and `preloadGcTime` are reduced from 30 minutes (`1_800_000`) to 5 minutes (`300_000`). +- The documented default `gcTime` and `preloadGcTime` now match the existing runtime default of 5 minutes (`300_000`). -**Removed / changed public API** +**Removed / changed exported internals** - `RouterState` no longer includes `loadedAt`, `isTransitioning`, `statusCode`, or `redirect`. Use `match.updatedAt` in place of `loadedAt`; subscribe to `router.state.status` / `router.state.isLoading` in place of `isTransitioning`; server response status and redirect handling are now internal to the server loader and are no longer exposed on `router.state`. - `RouteMatch.fetchCount` has been removed, with no replacement — it was purely informational. - `RouteMatch.status` no longer includes `'redirected'` (it remains `'pending' | 'success' | 'error' | 'notFound'`) — redirected matches are dropped from the match list instead of being rendered. - `RouteMatch.globalNotFound` has been renamed and privatized to the internal `_notFound` field. Use `match.status === 'notFound'` instead. +- The exported React, Solid, and Vue `Match` components now accept `routeId` instead of `matchId`. +- The exported `RouterStores` adapter contract now uses route-keyed presentation stores: `matchesId` is replaced by `ids`, `matchStores` by `byRoute`, and `getRouteMatchStore()` by `getMatchStore()`. The separate `loadedAt`, `isLoading`, `isTransitioning`, `statusCode`, and `redirect` stores have been removed, along with the pending/cache stores and their setters. `StoreConfig.init` has also been removed. Read application-facing state from `router.state`; preload and cache coordination are now internal. - Removed `RouterCore` members `getMatch()`, `updateMatch()`, `cancelMatch()`, and `cancelMatches()` — read matches from `router.state.matches` (e.g. `router.state.matches.find((m) => m.id === id)`); there is no replacement for mutating or cancelling an individual in-flight match from outside the router. -- Removed `hasNotFoundMatch()` — use `router.state.matches.some((m) => m.status === 'notFound')`. -- Removed `looseRoutesById` — use `routesById`. -- Removed `isPrerendering()`, `isViewTransitionTypesSupported`, and `viewTransitionPromise`, with no replacement. -- Removed `getParsedLocationHref()` and `clearExpiredCache()`, with no replacement — expired cache entries are now reconciled automatically as part of match commit. -- Removed `latestLoadPromise` and `beforeLoad()`, with no replacement. -- `commitLocationPromise` and `pendingBuiltLocation` are now private (`_commitPromise`, `_pendingLocation`) and no longer part of the public `RouterCore` surface. +- Removed `RouterCore.hasNotFoundMatch()` — use `router.state.matches.some((m) => m.status === 'notFound')`. +- Removed `RouterCore.looseRoutesById` — use `routesById`. +- Removed `RouterCore.isPrerendering()`, `RouterCore.isViewTransitionTypesSupported`, and `RouterCore.viewTransitionPromise`, with no replacement. +- Removed `RouterCore.getParsedLocationHref()` and `RouterCore.clearExpiredCache()`, with no replacement — expired cache entries are now reconciled automatically as part of match commit. +- Removed `RouterCore.latestLoadPromise` and `RouterCore.beforeLoad()`, with no replacement. +- `RouterCore.commitLocationPromise` and `RouterCore.pendingBuiltLocation` have been replaced by the internal `_commitPromise` and `_pendingLocation` fields. - Removed the exported `GetMatchFn` and `UpdateMatchFn` types, along with the methods they typed. - Removed the standalone `getMatchedRoutes()` export from `@tanstack/router-core` — use the `router.getMatchedRoutes()` instance method instead. +- `RouterCore.loadRouteChunk()` no longer accepts an array of component types as its second argument. One-argument usage is unchanged; the optional second argument is now `'errorComponent'`, `'notFoundComponent'`, or `false` for internal boundary loading. +- Removed `Redirect.redirectHandled`, which was internal redirect bookkeeping. - `MatchRoutesOpts.preload` and `MatchRoutesOpts.dest` have been removed. -- `StartTransitionFn` is now `(fn, expected, urgent?) => Promise` (previously `(fn) => void`). This only affects custom framework adapters that implement `startTransition`. +- `StartTransitionFn` is now `(fn, expected) => Promise` (previously `(fn) => void`). This only affects custom framework adapters that implement `startTransition`. diff --git a/packages/router-devtools-core/src/AgeTicker.tsx b/packages/router-devtools-core/src/AgeTicker.tsx index e3bd657358..5a615be54f 100644 --- a/packages/router-devtools-core/src/AgeTicker.tsx +++ b/packages/router-devtools-core/src/AgeTicker.tsx @@ -47,7 +47,7 @@ export function AgeTicker({ const staleTime = route.options.staleTime ?? router().options.defaultStaleTime ?? 0 const gcTime = - route.options.gcTime ?? router().options.defaultGcTime ?? 30 * 60 * 1000 + route.options.gcTime ?? router().options.defaultGcTime ?? 300_000 return (
    staleTime))}> diff --git a/packages/router-devtools-core/tests/cache-replacement.test.ts b/packages/router-devtools-core/tests/cache-replacement.test.ts index 1da523a1fb..e44e1cce53 100644 --- a/packages/router-devtools-core/tests/cache-replacement.test.ts +++ b/packages/router-devtools-core/tests/cache-replacement.test.ts @@ -35,7 +35,7 @@ describe('cached matches', () => { vi.useRealTimers() }) - it('refreshes when an existing cache entry is replaced', async () => { + it('uses the default gc time and refreshes replaced cache entries', async () => { vi.useFakeTimers() const route = { @@ -88,6 +88,7 @@ describe('cached matches', () => { ), ).not.toBeNull() }) + expect(container.textContent).toContain('5min') const cachedMatch = container.querySelector( '[aria-label="Open match details for cached-match"]', From 3740f447864216adfbe3c0d1c698ecfb0a8063fc Mon Sep 17 00:00:00 2001 From: Flo Date: Tue, 4 Aug 2026 22:23:02 +0200 Subject: [PATCH 46/51] docs: align loader docs with lane rewrite (#7949) * docs: align loader docs with lane rewrite * docs: refine lane loader documentation * docs: expand link preloading guide --- docs/router/api/router/LinkOptionsType.md | 4 +- docs/router/api/router/RouteOptionsType.md | 21 ++++-- docs/router/api/router/RouterType.md | 16 +++-- docs/router/guide/data-loading.md | 31 ++++++--- docs/router/guide/data-mutations.md | 19 +++-- docs/router/guide/navigation.md | 18 +++-- docs/router/guide/preloading.md | 81 ++++++++++++++-------- packages/router-core/src/link.ts | 8 ++- 8 files changed, 135 insertions(+), 63 deletions(-) diff --git a/docs/router/api/router/LinkOptionsType.md b/docs/router/api/router/LinkOptionsType.md index 481a5b4a3d..cf20e0ff19 100644 --- a/docs/router/api/router/LinkOptionsType.md +++ b/docs/router/api/router/LinkOptionsType.md @@ -9,7 +9,7 @@ The `LinkOptions` type extends the [`NavigateOptions`](./NavigateOptionsType.md) type LinkOptions = NavigateOptions & { target?: HTMLAnchorElement['target'] activeOptions?: ActiveOptions - preload?: false | 'intent' + preload?: false | 'intent' | 'viewport' | 'render' preloadDelay?: number disabled?: boolean } @@ -42,7 +42,7 @@ The `LinkOptions` object accepts/contains the following properties: - Type: `number` - Optional -- Delay intent preloading by this many milliseconds. If the intent exits before this delay, the preload will be cancelled. +- Delay focus and hover intent preloading by this many milliseconds. Touch intent preloads immediately. If focus or hover exits before the delay, the preload will be cancelled. ### `disabled` diff --git a/docs/router/api/router/RouteOptionsType.md b/docs/router/api/router/RouteOptionsType.md index a1c0cb42aa..ac35cdc845 100644 --- a/docs/router/api/router/RouteOptionsType.md +++ b/docs/router/api/router/RouteOptionsType.md @@ -115,14 +115,14 @@ type beforeLoad = ( location: ParsedLocation navigate: NavigateFn // @deprecated buildLocation: BuildLocationFn - cause: 'enter' | 'stay' + cause: 'preload' | 'enter' | 'stay' }, ) => Promise | TRouteContext | void ``` - Optional - [`ParsedLocation`](./ParsedLocationType.md) -- This async function is called before a route is loaded. If an error is thrown here, the route's loader will not be called and the route will not render. If thrown during a navigation, the navigation will be canceled and the error will be passed to the `onError` function. If thrown during a preload event, the error will be logged to the console and the preload will fail. +- This async function is called before a route is loaded. If it fails, the route's loader and its descendants will not run. During navigation, ordinary errors become the match's error state and are passed to the `onError` function. During a preload, ordinary errors and not-found results are represented in the returned speculative match lane instead of rejecting the `preloadRoute` promise. - If this function returns a promise, the route will be put into a pending state and cause rendering to suspend until the promise resolves. If this route's pendingMs threshold is reached, the `pendingComponent` will be shown until it resolves. If the promise rejects, the route will be put into an error state and the error will be thrown during render. - If this function returns a `TRouteContext` object, that object will be merged into the route's context and be made available in the `loader` and other related route components/methods. - It's common to use this function to check if a user is authenticated and redirect them to a login page if they are not. To do this, you can either return or throw a `redirect` object from this function. @@ -159,9 +159,9 @@ type loader = - Optional - [`ParsedLocation`](./ParsedLocationType.md) -- This async function is called when a route is matched and passed the route's match object. If an error is thrown here, the route will be put into an error state and the error will be thrown during render. If thrown during a navigation, the navigation will be canceled and the error will be passed to the `onError` function. If thrown during a preload event, the error will be logged to the console and the preload will fail. +- This async function is called when a route is matched and passed the route's match object. During navigation, ordinary errors become the match's error state and are passed to the `onError` function. During a preload, ordinary errors and not-found results are represented in the returned speculative match lane instead of rejecting the `preloadRoute` promise. - If this function returns a promise, the route will be put into a pending state and cause rendering to suspend until the promise resolves. If this route's pendingMs threshold is reached, the `pendingComponent` will be shown until it resolves. If the promise rejects, the route will be put into an error state and the error will be thrown during render. -- If this function returns a `TLoaderData` object, that object will be stored on the route match until the route match is no longer active. It can be accessed using the `useLoaderData` hook in any component that is a child of the route match before another `` is rendered. +- If this function returns a `TLoaderData` object, that object will be stored on the route match and can remain available in the in-memory cache after the match becomes inactive. Navigation-owned data uses `gcTime` for retention, while preload-owned data uses `preloadGcTime`. It can be accessed using the `useLoaderData` hook in any component that is a child of the route match before another `` is rendered. - Deps must be returned by your `loaderDeps` function in order to appear. - Use the object form to configure loader-specific behavior like `staleReloadMode`. - `staleReloadMode: 'background'` preserves stale-while-revalidate behavior for stale successful matches. @@ -190,6 +190,13 @@ type loaderDeps = (opts: { search: TFullSearchSchema }) => Record - Defaults to `routerOptions.defaultStaleTime`, which defaults to `0` - The amount of time in milliseconds that a route match's loader data will be considered fresh. If a route match is matched again within this time frame, its loader data will not be reloaded. +### `preload` property + +- Type: `boolean` +- Optional +- Defaults to `true` +- If `false`, speculative preloads still run this route's `beforeLoad` function but skip its `loader`. A navigation runs both `beforeLoad` and the skipped `loader` normally. + ### `preloadStaleTime` property - Type: `number` @@ -202,7 +209,7 @@ type loaderDeps = (opts: { search: TFullSearchSchema }) => Record - Type: `number` - Optional - Defaults to `routerOptions.defaultGcTime`, which defaults to 5 minutes. -- The amount of time in milliseconds that loader data from an ordinary load will be kept in memory after it is no longer in use. +- The retention window in milliseconds for unused loader data from an ordinary load. Once the data is older than this value, it is eligible for pruning during a later cache reconciliation. ### `shouldReload` property @@ -243,7 +250,7 @@ type loaderDeps = (opts: { search: TFullSearchSchema }) => Record - Type: `number` - Optional - Defaults to `routerOptions.defaultPreloadGcTime`, which defaults to 5 minutes. -- The amount of time in milliseconds that loader data produced by a preload can remain in memory while it is not in use. This controls retention; use `preloadStaleTime` to control whether retained data is fresh enough to reuse without reloading. +- The retention window in milliseconds for unused loader data produced by a preload. Once the data is older than this value, it is eligible for pruning during a later cache reconciliation. Use `preloadStaleTime` to control whether retained data is fresh enough to reuse without reloading. ### `preSearchFilters` property (⚠️ deprecated, use `search.middlewares` instead) @@ -266,7 +273,7 @@ type loaderDeps = (opts: { search: TFullSearchSchema }) => Record - Type: `(error: any) => void` - Optional - A function that will be called when an error is thrown during a navigation or preload event. -- If this function throws a [`redirect`](./redirectFunction.md), then the router will process and apply the redirect immediately. +- If this function throws a [`redirect`](./redirectFunction.md), the redirect replaces the original error and becomes control flow for the current navigation or preload operation. If it throws a not-found result, that result replaces the original error in the current match lane. ### `onEnter` property diff --git a/docs/router/api/router/RouterType.md b/docs/router/api/router/RouterType.md index cf857d5e88..cf8fdebeae 100644 --- a/docs/router/api/router/RouterType.md +++ b/docs/router/api/router/RouterType.md @@ -165,12 +165,16 @@ Loads all of the currently matched route matches and resolves when they are all Preloads all of the matches that match the provided `NavigateOptions`. An active preload is speculative and is not published as the current match -presentation. Successful loader data can enter the normal in-memory route cache -and remain reusable according to `preloadStaleTime` and `preloadGcTime`. - -Every preload and navigation runs its own `beforeLoad` chain. Preloads can -donate cached or in-flight loader work, but never `beforeLoad` context or -control flow. +presentation. Successful loader data can enter the normal in-memory route +cache. Its freshness follows `preloadStaleTime`; once unused and older than +`preloadGcTime`, it is eligible for pruning during a later cache +reconciliation. + +Every preload and navigation runs its own `beforeLoad` chain. A later lane can +reuse successful settled loader data or join loader work that is still in +flight, but it never reuses `beforeLoad` context or an already-settled +redirect, error, or not-found result. If joined loader work later produces a +terminal outcome, all current consumers of that flight observe it. - Type: `(opts: NavigateOptions) => Promise` - Properties diff --git a/docs/router/guide/data-loading.md b/docs/router/guide/data-loading.md index dad97b1e39..26a82eaad6 100644 --- a/docs/router/guide/data-loading.md +++ b/docs/router/guide/data-loading.md @@ -81,7 +81,7 @@ Use the object form when you want to configure loader-specific behavior such as The `loader` function receives a single object with the following properties: -- `abortController` - The route's abortController. Its signal is cancelled when the route is unloaded or when the Route is no longer relevant and the current invocation of the `loader` function becomes outdated. +- `abortController` - The controller for this shareable loader invocation. A preload and navigation can share the same in-flight loader work. Its signal is cancelled after the invocation becomes outdated and no consumer still needs it. - `cause` - The cause of the current route match. Can be either one of the following: - `enter` - When the route is matched and loaded after not being matched in the previous location. - `preload` - When the route is being preloaded. @@ -168,11 +168,11 @@ To control router dependencies and "freshness", TanStack Router provides a pleth ### ⚠️ Some Important Defaults -- By default, the `staleTime` is set to `0`, meaning that the route's data is immediately considered stale. Stale matches are reloaded in the background when the route is entered again, when its loader key changes (path params used by the route or `loaderDeps`), or when `router.load()` is called explicitly. -- By default, a previously preloaded route is considered fresh for **30 seconds**. This means if a route is preloaded, then preloaded again within 30 seconds, the second preload will be ignored. This prevents unnecessary preloads from happening too frequently. **When a route is loaded normally, the standard `staleTime` is used.** -- By default, `gcTime` and `preloadGcTime` are **5 minutes**, meaning unused loader data is removed from the in-memory cache after 5 minutes. They can be configured independently. +- By default, `staleTime` is set to `0`, so reusable successful data is immediately considered stale. When the same loader key is entered again or `router.load()` is called explicitly, stale data revalidates in the background by default. A different loader key identifies a separate cache entry and must load if it has no reusable data. +- By default, loader data produced by a preload is considered fresh for **30 seconds**. Every preload and navigation still runs its own `beforeLoad` chain, but a later preload and the first navigation can reuse the preload's loader data or in-flight loader work during that interval. After navigation accepts that loader generation, subsequent freshness checks use the standard `staleTime`. +- By default, `gcTime` and `preloadGcTime` define **5-minute** retention windows. Once unused data is older than its applicable window, it is eligible for pruning during a later cache reconciliation. The two windows can be configured independently. - By default, `staleReloadMode` is `'background'`, so stale successful matches keep rendering with their existing `loaderData` while the loader revalidates in the background. -- `router.invalidate()` will force all active routes to reload their loaders immediately and mark every cached route's data as stale. +- `router.invalidate()` selects matching committed, cached, and in-flight loader generations for invalidation and retires matching active preload lanes. Current active routes reload through the normal loading protocol; cached inactive data remains marked stale and reloads when it is reused. By default, stale successful loader data revalidates in the background unless `sync: true` is requested. ### Using `loaderDeps` to access search params @@ -221,7 +221,13 @@ export const Route = createFileRoute('/posts')({ ### Using `staleTime` to control how long data is considered fresh -By default, `staleTime` for navigations is set to `0`ms (and 30 seconds for preloads) which means that the route's data will always be considered stale. When a stale route is entered again, its loader key changes, or `router.load()` is called explicitly, the route will reload in the background. +By default, `staleTime` for accepted navigation data is `0`ms, while +`preloadStaleTime` is 30 seconds. A successful preload can therefore provide +loader data to the first navigation during that interval. Once navigation +accepts that loader generation, normal navigation freshness applies. With the +default `staleTime`, the data is immediately stale for a later use of the same +loader key and revalidates in the background while cached data remains visible. +A different loader key identifies a separate cache entry. **This is a good default for most use cases, but you may find that some route data is more static or potentially expensive to load.** In these cases, you can use the `staleTime` option to control how long the route's data is considered fresh for navigations. Let's take a look at an example: @@ -313,7 +319,10 @@ and `preloadStaleTime` for freshness, so the default settings keep a recent preload in memory and let the first navigation reuse it without another loader call. -To opt out of preloading, don't turn it on via the `routerOptions.defaultPreload` or `routeOptions.preload` options. +Use `routerOptions.defaultPreload` to control automatic link preloading. Setting +`routeOptions.preload` to `false` has a narrower effect: a speculative lane +still runs that route's `beforeLoad`, but skips its `loader`. Navigation runs +both normally. ## Passing all loader events to an external cache @@ -326,7 +335,11 @@ const router = createRouter({ }) ``` -This will ensure that every preload, load, and reload event will trigger your `loader` functions, which can then be handled and deduped by your external cache. +This makes settled preload data immediately stale in the Router, allowing your +external cache to decide whether to fetch. Retention still follows +`preloadGcTime`, and overlapping preload or navigation consumers can still +share in-flight loader work. A route's `shouldReload` option can also suppress +a loader call. ## Using Router Context @@ -481,7 +494,7 @@ export const Route = createFileRoute('/posts')({ ## Using the Abort Signal -The `abortController` property of the `loader` function is an [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController). Its signal is cancelled when the route is unloaded or when the `loader` call becomes outdated. This is useful for cancelling network requests when the route is unloaded or when the route's params change. Here is an example using it with a fetch call: +The `abortController` property of the `loader` function is an [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) for that loader invocation. A preload and navigation can share an in-flight invocation, so its signal remains active while any consumer still needs the work. The signal is cancelled after the invocation becomes outdated and has no remaining consumers. Here is an example using it with a fetch call: ```tsx // src/routes/posts.tsx diff --git a/docs/router/guide/data-mutations.md b/docs/router/guide/data-mutations.md index 7ffb403bfb..5fddfffdea 100644 --- a/docs/router/guide/data-mutations.md +++ b/docs/router/guide/data-mutations.md @@ -2,7 +2,11 @@ title: Data Mutations --- -Since TanStack router does not store or cache data, it's role in data mutation is slim to none outside of reacting to potential URL side-effects from external mutation events. That said, we've compiled a list of mutation-related features you might find useful and libraries that implement them. +TanStack Router caches route loader data, but it does not manage mutation or +submission state. Its role in mutation workflows is primarily invalidating +loader data and reacting to potential URL side effects from external mutation +events. That said, we've compiled a list of mutation-related features you might +find useful and libraries that implement them. Look for and use mutation utilities that support: @@ -35,9 +39,14 @@ Similar to data fetching, mutation state isn't a one-size-fits-all solution, so ## Invalidating TanStack Router after a mutation -TanStack Router comes with short-term caching built-in. So even though we're not storing any data after a route match is unmounted, there is a high probability that if any mutations are made related to the data stored in the Router, the current route matches' data could become stale. +TanStack Router comes with short-term caching built in. Loader data can remain +cached after a route match is unmounted, so a mutation can make both active and +cached route data stale. -When mutations related to loader data are made, we can use `router.invalidate` to force the router to reload all of the current route matches: +When mutations related to loader data are made, we can use `router.invalidate` +to invalidate committed, cached, and in-flight loader generations. Matching +active preload lanes are retired, and selected current active matches reload +through the normal loading protocol: ```tsx const router = useRouter() @@ -52,7 +61,9 @@ const addTodo = async (todo: Todo) => { } ``` -Invalidating all of the current route matches happens in the background, so existing data will continue to be served until the new data is ready, just as if you were navigating to a new route. +By default, stale successful loader data revalidates in the background, so +existing data remains visible until the new data is ready. Cached inactive +matches remain marked stale and reload when they are reused. If you want to await the invalidation until all loaders have finished, pass `{sync: true}` into `router.invalidate`: diff --git a/docs/router/guide/navigation.md b/docs/router/guide/navigation.md index dbabc0906a..b2bb03ad0e 100644 --- a/docs/router/guide/navigation.md +++ b/docs/router/guide/navigation.md @@ -127,9 +127,10 @@ export type LinkOptions< includeSearch?: boolean explicitUndefined?: boolean } - // If set, will preload the linked route on hover and cache it for this many milliseconds in hopes that the user will eventually navigate there. - preload?: false | 'intent' - // Delay intent preloading by this many milliseconds. If the intent exits before this delay, the preload will be cancelled. + // Choose the preload strategy for this link. `false` disables preloading; + // `'intent'`, `'viewport'`, and `'render'` select when it begins. + preload?: false | 'intent' | 'viewport' | 'render' + // Delay focus/hover intent by this many milliseconds. Touch intent preloads immediately. preloadDelay?: number // If true, will render the link without the href attribute disabled?: boolean @@ -707,7 +708,14 @@ const link = ( ### Link Preloading -The `Link` component supports automatically preloading routes on intent (hovering or touchstart for now). This can be configured as a default in the router options (which we'll talk more about soon) or by passing a `preload='intent'` prop to the `Link` component. Here's an example: +The `Link` component supports four `preload` values: + +- `false` disables automatic preloading. +- `'intent'` preloads when the link receives focus, is hovered, or is touched. +- `'viewport'` preloads when the link enters the viewport. +- `'render'` preloads as soon as the link renders. + +This can be configured as a default in the router options (which we'll talk more about soon) or by passing the `preload` prop to the `Link` component. Here's an intent-preloading example: ```tsx const link = ( @@ -723,7 +731,7 @@ What's even better is that by using a cache-first library like `@tanstack/query` ### Link Preloading Delay -Along with preloading is a configurable delay which determines how long a user must hover over a link to trigger the intent-based preloading. The default delay is 50 milliseconds, but you can change this by passing a `preloadDelay` prop to the `Link` component with the number of milliseconds you'd like to wait: +For `'intent'` preloading, a configurable delay determines how long a link must remain focused or hovered before preloading begins. If focus or hover ends before the delay, the queued preload is cancelled. Touch intent preloads immediately without waiting for the delay. The default delay is 50 milliseconds, but you can change it by passing a `preloadDelay` prop to the `Link` component: ```tsx const link = ( diff --git a/docs/router/guide/preloading.md b/docs/router/guide/preloading.md index a2f57e78e8..6ae9563f2f 100644 --- a/docs/router/guide/preloading.md +++ b/docs/router/guide/preloading.md @@ -19,12 +19,13 @@ Preloading in TanStack Router is a way to load a route before the user actually ## How long does preloaded data stay in memory? Successful preloaded loader results can enter the router's in-memory cache with -two independent lifetimes: +two independent policies: - **Freshness defaults to 30 seconds.** Configure it with `defaultPreloadStaleTime` or a route's `preloadStaleTime`. -- **Unused retention defaults to 5 minutes.** Configure it with - `defaultPreloadGcTime` or a route's `preloadGcTime`. +- **The unused retention window defaults to 5 minutes.** Configure it with + `defaultPreloadGcTime` or a route's `preloadGcTime`. Older unused entries + are eligible for pruning during a later cache reconciliation. - **The speculative lane is never promoted into router state.** Navigation creates its own presentation and runs its own `beforeLoad` chain. It can reuse cached loader data or join a loader that is still in flight. @@ -99,9 +100,10 @@ If you're using the built-in loaders, you can control how long preloaded data is Freshness and retention are separate. `preloadStaleTime` controls whether the retained loader result can be reused without another loader call. -`preloadGcTime` (or `defaultPreloadGcTime`) controls how long an unused preload -result can remain in the in-memory cache. Both preload GC options default to 5 -minutes. +`preloadGcTime` (or `defaultPreloadGcTime`) controls when an unused preload +result becomes eligible for pruning during a later cache reconciliation; it +does not schedule a timer to evict the result at that exact moment. Both preload +GC options default to 5 minutes. To change this, you can set the `defaultPreloadStaleTime` option on your router: @@ -145,9 +147,11 @@ export const Route = createFileRoute('/posts/$postId')({ Client-side preloading runs each route's `beforeLoad` with `preload: true`. Every later preload or navigation runs its own `beforeLoad` chain, so a navigation observes `preload: false` even when an identical preload is still -active. Preloads can donate cached or in-flight loader work, but not -`beforeLoad` context, redirects, errors, or not-found results. The -`shouldReload` option remains loader-only. +active. A later lane can reuse successful settled loader data or join loader +work that is still in flight, but it never reuses `beforeLoad` context or an +already-settled redirect, error, or not-found result. If joined loader work +later produces a terminal outcome, all current consumers of that flight observe +it. The `shouldReload` option remains loader-only. If a route has `preload: false`, its speculative lane still runs `beforeLoad`, but skips that route's loader. Navigation runs `beforeLoad` again and performs @@ -157,7 +161,12 @@ the skipped loader work. When integrating external caching libraries like React Query, which have their own mechanisms for determining stale data, you may want to override the default preloading and stale-while-revalidate logic of TanStack Router. These libraries often use options like staleTime to control the freshness of data. -To customize the preloading behavior in TanStack Router and fully leverage your external library's caching strategy, you can bypass the built-in caching by setting routerOptions.defaultPreloadStaleTime or routeOptions.preloadStaleTime to 0. This ensures that all preloads are marked as stale internally, and loaders are always invoked, allowing your external library, such as React Query, to manage data loading and caching. +To let an external cache make the freshness decision, set +`routerOptions.defaultPreloadStaleTime` or `routeOptions.preloadStaleTime` to +`0`. Settled preload data then becomes immediately stale in the Router, while +retention still follows `preloadGcTime`. Overlapping preload or navigation +consumers can still share in-flight loader work, and `shouldReload` can still +suppress a loader call. For example: @@ -193,27 +202,36 @@ This would then allow you, for instance, to use an option like React Query's `st If you need to manually preload a route, use the router's `preloadRoute` method. It accepts a standard TanStack `NavigateOptions` object and returns the -speculative match lane. An ordinary error or not-found is represented in that -returned lane; cancellation or control flow that produces no reusable lane can -return `undefined`. +speculative match lane. An ordinary error or not-found thrown while loading is +represented in that returned lane; cancellation or control flow that produces +no reusable lane can return `undefined`. # React ```tsx +import { isNotFound } from '@tanstack/react-router' + function Component() { const router = useRouter() useEffect(() => { async function preload() { - try { - const matches = await router.preloadRoute({ - to: postRoute, - params: { id: 1 }, - }) - } catch (err) { - // Failed to preload route + const matches = await router.preloadRoute({ + to: postRoute, + params: { id: 1 }, + }) + + const routeFailure = matches?.find( + (match) => + match.status === 'error' || + match.status === 'notFound' || + isNotFound(match.error), + ) + + if (routeFailure) { + // Inspect routeFailure.error } } @@ -227,18 +245,27 @@ function Component() { # Solid ```tsx +import { isNotFound } from '@tanstack/solid-router' + function Component() { const router = useRouter() createEffect(() => { async function preload() { - try { - const matches = await router.preloadRoute({ - to: postRoute, - params: { id: 1 }, - }) - } catch (err) { - // Failed to preload route + const matches = await router.preloadRoute({ + to: postRoute, + params: { id: 1 }, + }) + + const routeFailure = matches?.find( + (match) => + match.status === 'error' || + match.status === 'notFound' || + isNotFound(match.error), + ) + + if (routeFailure) { + // Inspect routeFailure.error } } diff --git a/packages/router-core/src/link.ts b/packages/router-core/src/link.ts index 55d5a79ce8..7ec148b8f4 100644 --- a/packages/router-core/src/link.ts +++ b/packages/router-core/src/link.ts @@ -671,13 +671,15 @@ export interface LinkOptionsProps { /** * The preloading strategy for this link * - `false` - No preloading - * - `'intent'` - Preload the linked route on hover and cache it for this many milliseconds in hopes that the user will eventually navigate there. + * - `'intent'` - Preload the linked route when the user focuses, hovers over, or touches the link * - `'viewport'` - Preload the linked route when it enters the viewport + * - `'render'` - Preload the linked route as soon as it renders */ preload?: false | 'intent' | 'viewport' | 'render' /** - * When a preload strategy is set, this delays the preload by this many milliseconds. - * If the user exits the link before this delay, the preload will be cancelled. + * When the intent preload strategy is set, this delays focus and hover + * preloading by this many milliseconds. Touch intent preloads immediately. + * If focus or hover exits before this delay, the preload will be cancelled. */ preloadDelay?: number /** From 704145c6d5f1e4040a021daa8a06a8119b6a1be4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2026 22:25:17 +0200 Subject: [PATCH 47/51] ci: Version Packages (#7946) ci: changeset release Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/lane-match-loader-rewrite.md | 35 --- .../package.json | 6 +- .../react/authenticated-routes/package.json | 6 +- .../basic-default-search-params/package.json | 4 +- .../react/basic-devtools-panel/package.json | 4 +- examples/react/basic-file-based/package.json | 6 +- .../basic-non-nested-devtools/package.json | 4 +- .../basic-react-query-file-based/package.json | 6 +- examples/react/basic-react-query/package.json | 4 +- .../react/basic-ssr-file-based/package.json | 6 +- .../package.json | 6 +- .../basic-virtual-file-based/package.json | 6 +- .../package.json | 6 +- examples/react/basic/package.json | 4 +- examples/react/deferred-data/package.json | 4 +- examples/react/i18n-paraglide/package.json | 4 +- .../kitchen-sink-file-based/package.json | 6 +- .../package.json | 6 +- .../kitchen-sink-react-query/package.json | 4 +- examples/react/kitchen-sink/package.json | 4 +- examples/react/large-file-based/package.json | 6 +- examples/react/location-masking/package.json | 4 +- .../react/navigation-blocking/package.json | 4 +- .../package.json | 6 +- .../react/quickstart-file-based/package.json | 6 +- .../quickstart-rspack-file-based/package.json | 6 +- .../package.json | 6 +- examples/react/quickstart/package.json | 4 +- .../router-monorepo-react-query/package.json | 6 +- .../packages/app/package.json | 2 +- .../packages/router/package.json | 4 +- .../router-monorepo-simple-lazy/package.json | 6 +- .../packages/app/package.json | 2 +- .../packages/router/package.json | 4 +- .../react/router-monorepo-simple/package.json | 6 +- .../packages/app/package.json | 2 +- .../packages/router/package.json | 4 +- .../react/scroll-restoration/package.json | 4 +- .../search-validator-adapters/package.json | 6 +- examples/react/start-bare/package.json | 6 +- examples/react/start-basic-auth/package.json | 6 +- .../react/start-basic-authjs/package.json | 6 +- .../react/start-basic-cloudflare/package.json | 6 +- .../start-basic-react-query/package.json | 6 +- .../react/start-basic-rsbuild/package.json | 6 +- .../react/start-basic-static/package.json | 8 +- examples/react/start-basic/package.json | 6 +- examples/react/start-bun/package.json | 8 +- examples/react/start-clerk-basic/package.json | 6 +- .../react/start-convex-trellaux/package.json | 6 +- examples/react/start-counter/package.json | 6 +- .../react/start-i18n-paraglide/package.json | 6 +- examples/react/start-large/package.json | 6 +- examples/react/start-material-ui/package.json | 6 +- examples/react/start-rscs/package.json | 6 +- .../package.json | 6 +- .../react/start-supabase-basic/package.json | 6 +- examples/react/start-tailwind-v4/package.json | 6 +- examples/react/start-trellaux/package.json | 6 +- examples/react/start-workos/package.json | 6 +- examples/react/view-transitions/package.json | 6 +- .../react/with-framer-motion/package.json | 4 +- .../react/with-trpc-react-query/package.json | 6 +- examples/react/with-trpc/package.json | 6 +- .../package.json | 6 +- .../solid/authenticated-routes/package.json | 6 +- .../basic-default-search-params/package.json | 4 +- .../solid/basic-devtools-panel/package.json | 4 +- examples/solid/basic-file-based/package.json | 6 +- .../basic-non-nested-devtools/package.json | 4 +- .../basic-solid-query-file-based/package.json | 6 +- examples/solid/basic-solid-query/package.json | 6 +- .../solid/basic-ssr-file-based/package.json | 6 +- .../package.json | 6 +- .../basic-virtual-file-based/package.json | 6 +- .../package.json | 6 +- examples/solid/basic/package.json | 4 +- examples/solid/deferred-data/package.json | 4 +- examples/solid/i18n-paraglide/package.json | 4 +- .../kitchen-sink-file-based/package.json | 6 +- .../package.json | 6 +- .../kitchen-sink-solid-query/package.json | 4 +- examples/solid/kitchen-sink/package.json | 4 +- examples/solid/large-file-based/package.json | 6 +- examples/solid/location-masking/package.json | 4 +- .../solid/navigation-blocking/package.json | 4 +- .../package.json | 6 +- .../solid/quickstart-file-based/package.json | 6 +- .../quickstart-rspack-file-based/package.json | 6 +- .../package.json | 6 +- examples/solid/quickstart/package.json | 4 +- .../router-monorepo-simple-lazy/package.json | 6 +- .../packages/app/package.json | 2 +- .../packages/router/package.json | 4 +- .../solid/router-monorepo-simple/package.json | 6 +- .../packages/app/package.json | 2 +- .../packages/router/package.json | 4 +- .../router-monorepo-solid-query/package.json | 6 +- .../packages/app/package.json | 2 +- .../packages/router/package.json | 4 +- .../solid/scroll-restoration/package.json | 4 +- .../search-validator-adapters/package.json | 6 +- examples/solid/start-basic-auth/package.json | 6 +- .../solid/start-basic-authjs/package.json | 6 +- .../solid/start-basic-cloudflare/package.json | 6 +- .../solid/start-basic-netlify/package.json | 6 +- examples/solid/start-basic-nitro/package.json | 6 +- .../start-basic-solid-query/package.json | 6 +- .../solid/start-basic-static/package.json | 8 +- examples/solid/start-basic/package.json | 6 +- examples/solid/start-bun/package.json | 8 +- .../start-convex-better-auth/package.json | 6 +- examples/solid/start-counter/package.json | 6 +- .../solid/start-i18n-paraglide/package.json | 6 +- examples/solid/start-large/package.json | 6 +- .../package.json | 6 +- .../solid/start-supabase-basic/package.json | 6 +- examples/solid/start-tailwind-v4/package.json | 6 +- examples/solid/view-transitions/package.json | 6 +- .../solid/with-framer-motion/package.json | 4 +- examples/solid/with-trpc/package.json | 6 +- .../vue/basic-file-based-jsx/package.json | 6 +- .../vue/basic-file-based-sfc/package.json | 6 +- examples/vue/basic/package.json | 4 +- packages/react-router-devtools/CHANGELOG.md | 9 + packages/react-router-devtools/package.json | 2 +- packages/react-router/CHANGELOG.md | 33 ++ packages/react-router/package.json | 2 +- packages/react-start-client/CHANGELOG.md | 9 + packages/react-start-client/package.json | 2 +- packages/react-start-rsc/CHANGELOG.md | 11 + packages/react-start-rsc/package.json | 2 +- packages/react-start-server/CHANGELOG.md | 9 + packages/react-start-server/package.json | 2 +- packages/react-start/CHANGELOG.md | 13 + packages/react-start/package.json | 2 +- packages/router-cli/CHANGELOG.md | 7 + packages/router-cli/package.json | 2 +- packages/router-core/CHANGELOG.md | 30 ++ packages/router-core/package.json | 2 +- packages/router-devtools-core/CHANGELOG.md | 33 ++ packages/router-devtools-core/package.json | 2 +- packages/router-devtools/CHANGELOG.md | 8 + packages/router-devtools/package.json | 2 +- packages/router-generator/CHANGELOG.md | 7 + packages/router-generator/package.json | 2 +- packages/router-plugin/CHANGELOG.md | 9 + packages/router-plugin/package.json | 2 +- packages/router-vite-plugin/CHANGELOG.md | 7 + packages/router-vite-plugin/package.json | 2 +- packages/solid-router-devtools/CHANGELOG.md | 9 + packages/solid-router-devtools/package.json | 2 +- packages/solid-router/CHANGELOG.md | 33 ++ packages/solid-router/package.json | 2 +- packages/solid-start-client/CHANGELOG.md | 9 + packages/solid-start-client/package.json | 2 +- packages/solid-start-server/CHANGELOG.md | 9 + packages/solid-start-server/package.json | 2 +- packages/solid-start/CHANGELOG.md | 12 + packages/solid-start/package.json | 2 +- packages/start-client-core/CHANGELOG.md | 8 + packages/start-client-core/package.json | 2 +- packages/start-plugin-core/CHANGELOG.md | 10 + packages/start-plugin-core/package.json | 2 +- packages/start-server-core/CHANGELOG.md | 9 + packages/start-server-core/package.json | 2 +- .../CHANGELOG.md | 9 + .../package.json | 2 +- packages/start-storage-context/CHANGELOG.md | 7 + packages/start-storage-context/package.json | 2 +- packages/vue-router-devtools/CHANGELOG.md | 9 + packages/vue-router-devtools/package.json | 2 +- packages/vue-router/CHANGELOG.md | 33 ++ packages/vue-router/package.json | 2 +- packages/vue-start-client/CHANGELOG.md | 9 + packages/vue-start-client/package.json | 2 +- packages/vue-start-server/CHANGELOG.md | 9 + packages/vue-start-server/package.json | 2 +- packages/vue-start/CHANGELOG.md | 12 + packages/vue-start/package.json | 2 +- pnpm-lock.yaml | 282 +++++++++--------- 181 files changed, 872 insertions(+), 527 deletions(-) delete mode 100644 .changeset/lane-match-loader-rewrite.md diff --git a/.changeset/lane-match-loader-rewrite.md b/.changeset/lane-match-loader-rewrite.md deleted file mode 100644 index a7d63e6d34..0000000000 --- a/.changeset/lane-match-loader-rewrite.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -'@tanstack/router-core': patch -'@tanstack/react-router': patch -'@tanstack/solid-router': patch -'@tanstack/vue-router': patch -'@tanstack/router-devtools-core': patch ---- - -Rewrite match loading around a lane-based scheduler that tracks each navigation, preload, and background reload as an ordered unit of work. This fixes pending/redirect/retry state leaking between overlapping navigations, restores correct SSR status codes for redirects, errors, and not-found responses, and closes hydration gaps where the client re-ran work the server had already completed. - -- Invalidation now retires matching active preloads so older speculative loader results cannot become fresh cache data after invalidation. -- Route `headers()` now only runs on the server, matching the documented behavior — it is no longer invoked during client-side asset projection. -- The documented default `gcTime` and `preloadGcTime` now match the existing runtime default of 5 minutes (`300_000`). - -**Removed / changed exported internals** - -- `RouterState` no longer includes `loadedAt`, `isTransitioning`, `statusCode`, or `redirect`. Use `match.updatedAt` in place of `loadedAt`; subscribe to `router.state.status` / `router.state.isLoading` in place of `isTransitioning`; server response status and redirect handling are now internal to the server loader and are no longer exposed on `router.state`. -- `RouteMatch.fetchCount` has been removed, with no replacement — it was purely informational. -- `RouteMatch.status` no longer includes `'redirected'` (it remains `'pending' | 'success' | 'error' | 'notFound'`) — redirected matches are dropped from the match list instead of being rendered. -- `RouteMatch.globalNotFound` has been renamed and privatized to the internal `_notFound` field. Use `match.status === 'notFound'` instead. -- The exported React, Solid, and Vue `Match` components now accept `routeId` instead of `matchId`. -- The exported `RouterStores` adapter contract now uses route-keyed presentation stores: `matchesId` is replaced by `ids`, `matchStores` by `byRoute`, and `getRouteMatchStore()` by `getMatchStore()`. The separate `loadedAt`, `isLoading`, `isTransitioning`, `statusCode`, and `redirect` stores have been removed, along with the pending/cache stores and their setters. `StoreConfig.init` has also been removed. Read application-facing state from `router.state`; preload and cache coordination are now internal. -- Removed `RouterCore` members `getMatch()`, `updateMatch()`, `cancelMatch()`, and `cancelMatches()` — read matches from `router.state.matches` (e.g. `router.state.matches.find((m) => m.id === id)`); there is no replacement for mutating or cancelling an individual in-flight match from outside the router. -- Removed `RouterCore.hasNotFoundMatch()` — use `router.state.matches.some((m) => m.status === 'notFound')`. -- Removed `RouterCore.looseRoutesById` — use `routesById`. -- Removed `RouterCore.isPrerendering()`, `RouterCore.isViewTransitionTypesSupported`, and `RouterCore.viewTransitionPromise`, with no replacement. -- Removed `RouterCore.getParsedLocationHref()` and `RouterCore.clearExpiredCache()`, with no replacement — expired cache entries are now reconciled automatically as part of match commit. -- Removed `RouterCore.latestLoadPromise` and `RouterCore.beforeLoad()`, with no replacement. -- `RouterCore.commitLocationPromise` and `RouterCore.pendingBuiltLocation` have been replaced by the internal `_commitPromise` and `_pendingLocation` fields. -- Removed the exported `GetMatchFn` and `UpdateMatchFn` types, along with the methods they typed. -- Removed the standalone `getMatchedRoutes()` export from `@tanstack/router-core` — use the `router.getMatchedRoutes()` instance method instead. -- `RouterCore.loadRouteChunk()` no longer accepts an array of component types as its second argument. One-argument usage is unchanged; the optional second argument is now `'errorComponent'`, `'notFoundComponent'`, or `false` for internal boundary loading. -- Removed `Redirect.redirectHandled`, which was internal redirect bookkeeping. -- `MatchRoutesOpts.preload` and `MatchRoutesOpts.dest` have been removed. -- `StartTransitionFn` is now `(fn, expected) => Promise` (previously `(fn) => void`). This only affects custom framework adapters that implement `startTransition`. diff --git a/examples/react/authenticated-routes-firebase/package.json b/examples/react/authenticated-routes-firebase/package.json index e2a80d5e70..c6465c1940 100644 --- a/examples/react/authenticated-routes-firebase/package.json +++ b/examples/react/authenticated-routes-firebase/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/router-plugin": "^1.168.24", "firebase": "^11.4.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/authenticated-routes/package.json b/examples/react/authenticated-routes/package.json index ea0fd7f0ce..7e2743b760 100644 --- a/examples/react/authenticated-routes/package.json +++ b/examples/react/authenticated-routes/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/router-plugin": "^1.168.24", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-default-search-params/package.json b/examples/react/basic-default-search-params/package.json index 783d8aefa6..69239de4e0 100644 --- a/examples/react/basic-default-search-params/package.json +++ b/examples/react/basic-default-search-params/package.json @@ -11,8 +11,8 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-query": "^5.90.0", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-devtools-panel/package.json b/examples/react/basic-devtools-panel/package.json index 1f7db96cbd..9f83a68648 100644 --- a/examples/react/basic-devtools-panel/package.json +++ b/examples/react/basic-devtools-panel/package.json @@ -11,8 +11,8 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-query-devtools": "^5.67.2", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-file-based/package.json b/examples/react/basic-file-based/package.json index 4ebbfac1bc..b13da4adca 100644 --- a/examples/react/basic-file-based/package.json +++ b/examples/react/basic-file-based/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/router-plugin": "^1.168.24", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-non-nested-devtools/package.json b/examples/react/basic-non-nested-devtools/package.json index 46a2b351ce..0c747b097e 100644 --- a/examples/react/basic-non-nested-devtools/package.json +++ b/examples/react/basic-non-nested-devtools/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-react-query-file-based/package.json b/examples/react/basic-react-query-file-based/package.json index 9d938052a7..aff759e64c 100644 --- a/examples/react/basic-react-query-file-based/package.json +++ b/examples/react/basic-react-query-file-based/package.json @@ -12,9 +12,9 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-query": "^5.90.0", "@tanstack/react-query-devtools": "^5.90.0", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/router-plugin": "^1.168.24", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-react-query/package.json b/examples/react/basic-react-query/package.json index 530b2e0a22..443c16f8c8 100644 --- a/examples/react/basic-react-query/package.json +++ b/examples/react/basic-react-query/package.json @@ -12,8 +12,8 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-query": "^5.90.0", "@tanstack/react-query-devtools": "^5.90.0", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/basic-ssr-file-based/package.json b/examples/react/basic-ssr-file-based/package.json index a0985005dc..c046f9e6d1 100644 --- a/examples/react/basic-ssr-file-based/package.json +++ b/examples/react/basic-ssr-file-based/package.json @@ -11,8 +11,8 @@ "debug": "node --inspect-brk server" }, "dependencies": { - "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/router-plugin": "^1.168.24", "compression": "^1.8.0", "express": "^5.2.1", "get-port": "^7.1.0", @@ -21,7 +21,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router-devtools": "^1.167.1", "@types/express": "^5.0.6", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.1", diff --git a/examples/react/basic-ssr-streaming-file-based/package.json b/examples/react/basic-ssr-streaming-file-based/package.json index 32a35d2d61..f20318d88d 100644 --- a/examples/react/basic-ssr-streaming-file-based/package.json +++ b/examples/react/basic-ssr-streaming-file-based/package.json @@ -11,8 +11,8 @@ "debug": "node --inspect-brk server" }, "dependencies": { - "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/router-plugin": "^1.168.24", "compression": "^1.8.0", "express": "^5.2.1", "get-port": "^7.1.0", @@ -21,7 +21,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router-devtools": "^1.167.1", "@types/express": "^5.0.6", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.1", diff --git a/examples/react/basic-virtual-file-based/package.json b/examples/react/basic-virtual-file-based/package.json index 8a04df0318..b55c0b1bdf 100644 --- a/examples/react/basic-virtual-file-based/package.json +++ b/examples/react/basic-virtual-file-based/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/router-plugin": "^1.168.24", "@tanstack/virtual-file-routes": "^1.162.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/basic-virtual-inside-file-based/package.json b/examples/react/basic-virtual-inside-file-based/package.json index c6a5a7eb51..473dd8a81c 100644 --- a/examples/react/basic-virtual-inside-file-based/package.json +++ b/examples/react/basic-virtual-inside-file-based/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/router-plugin": "^1.168.24", "@tanstack/virtual-file-routes": "^1.162.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/basic/package.json b/examples/react/basic/package.json index a3e65043f0..c030b321f1 100644 --- a/examples/react/basic/package.json +++ b/examples/react/basic/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/deferred-data/package.json b/examples/react/deferred-data/package.json index 2fb74c14be..c36bb4fafa 100644 --- a/examples/react/deferred-data/package.json +++ b/examples/react/deferred-data/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/i18n-paraglide/package.json b/examples/react/i18n-paraglide/package.json index 3c570610d4..f82d68e8cd 100644 --- a/examples/react/i18n-paraglide/package.json +++ b/examples/react/i18n-paraglide/package.json @@ -11,8 +11,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/router-plugin": "^1.168.24", "react": "^19.1.1", "react-dom": "^19.1.1", "tailwindcss": "^4.2.2" diff --git a/examples/react/kitchen-sink-file-based/package.json b/examples/react/kitchen-sink-file-based/package.json index e4c42006df..c453ff25f6 100644 --- a/examples/react/kitchen-sink-file-based/package.json +++ b/examples/react/kitchen-sink-file-based/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/router-plugin": "^1.168.24", "immer": "^10.1.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/kitchen-sink-react-query-file-based/package.json b/examples/react/kitchen-sink-react-query-file-based/package.json index 5c81bcc151..66bce428ea 100644 --- a/examples/react/kitchen-sink-react-query-file-based/package.json +++ b/examples/react/kitchen-sink-react-query-file-based/package.json @@ -12,9 +12,9 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-query": "^5.90.0", "@tanstack/react-query-devtools": "^5.90.0", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/router-plugin": "^1.168.24", "immer": "^10.1.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/kitchen-sink-react-query/package.json b/examples/react/kitchen-sink-react-query/package.json index efcb6cbb11..3ce98536f4 100644 --- a/examples/react/kitchen-sink-react-query/package.json +++ b/examples/react/kitchen-sink-react-query/package.json @@ -12,8 +12,8 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-query": "^5.90.0", "@tanstack/react-query-devtools": "^5.90.0", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", "immer": "^10.1.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/kitchen-sink/package.json b/examples/react/kitchen-sink/package.json index 624af9997c..7bae6392fe 100644 --- a/examples/react/kitchen-sink/package.json +++ b/examples/react/kitchen-sink/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", "immer": "^10.1.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/large-file-based/package.json b/examples/react/large-file-based/package.json index 4c72e28325..121a73a568 100644 --- a/examples/react/large-file-based/package.json +++ b/examples/react/large-file-based/package.json @@ -13,9 +13,9 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-query": "^5.90.0", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/router-plugin": "^1.168.24", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/location-masking/package.json b/examples/react/location-masking/package.json index e156afff8b..34b52ef958 100644 --- a/examples/react/location-masking/package.json +++ b/examples/react/location-masking/package.json @@ -12,8 +12,8 @@ "@radix-ui/react-dialog": "^1.1.6", "@tailwindcss/vite": "^4.2.2", "@tanstack/react-query": "^5.90.0", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/navigation-blocking/package.json b/examples/react/navigation-blocking/package.json index 228eba8ed6..cb92d28276 100644 --- a/examples/react/navigation-blocking/package.json +++ b/examples/react/navigation-blocking/package.json @@ -11,8 +11,8 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-query": "^5.90.0", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/quickstart-esbuild-file-based/package.json b/examples/react/quickstart-esbuild-file-based/package.json index 7ce6a0d7c2..ad2e79b6ec 100644 --- a/examples/react/quickstart-esbuild-file-based/package.json +++ b/examples/react/quickstart-esbuild-file-based/package.json @@ -9,9 +9,9 @@ "start": "dev" }, "dependencies": { - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/router-plugin": "^1.168.24", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/quickstart-file-based/package.json b/examples/react/quickstart-file-based/package.json index 667de7e875..164630daea 100644 --- a/examples/react/quickstart-file-based/package.json +++ b/examples/react/quickstart-file-based/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/router-plugin": "^1.168.24", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/quickstart-rspack-file-based/package.json b/examples/react/quickstart-rspack-file-based/package.json index 5c6793dda5..b758b8a56c 100644 --- a/examples/react/quickstart-rspack-file-based/package.json +++ b/examples/react/quickstart-rspack-file-based/package.json @@ -9,8 +9,8 @@ }, "dependencies": { "@tailwindcss/postcss": "^4.2.2", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", "postcss": "^8.5.1", "react": "^19.0.0", "react-dom": "^19.0.0", @@ -19,7 +19,7 @@ "devDependencies": { "@rsbuild/core": "^2.1.0", "@rsbuild/plugin-react": "^2.0.0", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/router-plugin": "^1.168.24", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@typescript/native": "npm:typescript@^7.0.2", diff --git a/examples/react/quickstart-webpack-file-based/package.json b/examples/react/quickstart-webpack-file-based/package.json index d8016513e6..99386be401 100644 --- a/examples/react/quickstart-webpack-file-based/package.json +++ b/examples/react/quickstart-webpack-file-based/package.json @@ -7,14 +7,14 @@ "build": "webpack build && tsc --noEmit" }, "dependencies": { - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", "react": "^19.0.0", "react-dom": "^19.0.0" }, "devDependencies": { "@swc/core": "^1.15.33", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/router-plugin": "^1.168.24", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "html-webpack-plugin": "^5.6.3", diff --git a/examples/react/quickstart/package.json b/examples/react/quickstart/package.json index 9c116828f2..8e57454acf 100644 --- a/examples/react/quickstart/package.json +++ b/examples/react/quickstart/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwindcss": "^4.2.2" diff --git a/examples/react/router-monorepo-react-query/package.json b/examples/react/router-monorepo-react-query/package.json index e1176029a6..7dfb8c6be6 100644 --- a/examples/react/router-monorepo-react-query/package.json +++ b/examples/react/router-monorepo-react-query/package.json @@ -12,9 +12,9 @@ "dependencies": { "@tanstack/react-query": "^5.90.0", "@tanstack/react-query-devtools": "^5.90.0", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/router-plugin": "^1.168.24", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/router-monorepo-react-query/packages/app/package.json b/examples/react/router-monorepo-react-query/packages/app/package.json index caea88cb4c..336998abe7 100644 --- a/examples/react/router-monorepo-react-query/packages/app/package.json +++ b/examples/react/router-monorepo-react-query/packages/app/package.json @@ -16,7 +16,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router-devtools": "^1.167.1", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^6.0.1", diff --git a/examples/react/router-monorepo-react-query/packages/router/package.json b/examples/react/router-monorepo-react-query/packages/router/package.json index 19317727a0..8840dea47b 100644 --- a/examples/react/router-monorepo-react-query/packages/router/package.json +++ b/examples/react/router-monorepo-react-query/packages/router/package.json @@ -11,8 +11,8 @@ "@router-mono-react-query/post-query": "workspace:*", "@tanstack/history": "^1.162.0", "@tanstack/react-query": "^5.90.0", - "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/router-plugin": "^1.168.24", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/router-monorepo-simple-lazy/package.json b/examples/react/router-monorepo-simple-lazy/package.json index 6e752dabb4..6297e540de 100644 --- a/examples/react/router-monorepo-simple-lazy/package.json +++ b/examples/react/router-monorepo-simple-lazy/package.json @@ -8,9 +8,9 @@ "dev": "pnpm router build && pnpm post-feature build && pnpm app dev" }, "dependencies": { - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/router-plugin": "^1.168.24", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/router-monorepo-simple-lazy/packages/app/package.json b/examples/react/router-monorepo-simple-lazy/packages/app/package.json index 877e0086b0..a1e792cc3d 100644 --- a/examples/react/router-monorepo-simple-lazy/packages/app/package.json +++ b/examples/react/router-monorepo-simple-lazy/packages/app/package.json @@ -15,7 +15,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router-devtools": "^1.167.1", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^6.0.1", diff --git a/examples/react/router-monorepo-simple-lazy/packages/router/package.json b/examples/react/router-monorepo-simple-lazy/packages/router/package.json index 0f453fa648..ee48da871f 100644 --- a/examples/react/router-monorepo-simple-lazy/packages/router/package.json +++ b/examples/react/router-monorepo-simple-lazy/packages/router/package.json @@ -9,8 +9,8 @@ "types": "./dist/index.d.ts", "dependencies": { "@tanstack/history": "^1.162.0", - "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/router-plugin": "^1.168.24", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/router-monorepo-simple/package.json b/examples/react/router-monorepo-simple/package.json index 16f8a89796..b6df4df4a5 100644 --- a/examples/react/router-monorepo-simple/package.json +++ b/examples/react/router-monorepo-simple/package.json @@ -8,9 +8,9 @@ "dev": "pnpm router build && pnpm post-feature build && pnpm app dev" }, "dependencies": { - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/router-plugin": "^1.168.24", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/router-monorepo-simple/packages/app/package.json b/examples/react/router-monorepo-simple/packages/app/package.json index 795069716b..01da864c38 100644 --- a/examples/react/router-monorepo-simple/packages/app/package.json +++ b/examples/react/router-monorepo-simple/packages/app/package.json @@ -15,7 +15,7 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router-devtools": "^1.167.1", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^6.0.1", diff --git a/examples/react/router-monorepo-simple/packages/router/package.json b/examples/react/router-monorepo-simple/packages/router/package.json index 7103db0cc7..b59ef63fc9 100644 --- a/examples/react/router-monorepo-simple/packages/router/package.json +++ b/examples/react/router-monorepo-simple/packages/router/package.json @@ -9,8 +9,8 @@ "types": "./dist/index.d.ts", "dependencies": { "@tanstack/history": "^1.162.0", - "@tanstack/react-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/router-plugin": "^1.168.24", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/scroll-restoration/package.json b/examples/react/scroll-restoration/package.json index 37ea294774..76081ff3b0 100644 --- a/examples/react/scroll-restoration/package.json +++ b/examples/react/scroll-restoration/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", "@tanstack/react-virtual": "^3.13.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/search-validator-adapters/package.json b/examples/react/search-validator-adapters/package.json index 2a24fde8fc..86700f3b13 100644 --- a/examples/react/search-validator-adapters/package.json +++ b/examples/react/search-validator-adapters/package.json @@ -13,9 +13,9 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/arktype-adapter": "^1.167.0", "@tanstack/react-query": "^5.90.0", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/router-plugin": "^1.168.24", "@tanstack/valibot-adapter": "^1.167.0", "@tanstack/zod-adapter": "^1.167.0", "arktype": "^2.1.7", diff --git a/examples/react/start-bare/package.json b/examples/react/start-bare/package.json index df9095406a..4afcc5b3f6 100644 --- a/examples/react/start-bare/package.json +++ b/examples/react/start-bare/package.json @@ -10,9 +10,9 @@ "start": "pnpx srvx --prod -s ../client dist/server/server.js" }, "dependencies": { - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.35", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/react-start": "^1.168.36", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-basic-auth/package.json b/examples/react/start-basic-auth/package.json index d6837c76aa..c4fab434de 100644 --- a/examples/react/start-basic-auth/package.json +++ b/examples/react/start-basic-auth/package.json @@ -14,9 +14,9 @@ "@libsql/client": "^0.15.15", "@prisma/adapter-libsql": "^7.0.0", "@prisma/client": "^7.0.0", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.35", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/react-start": "^1.168.36", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-basic-authjs/package.json b/examples/react/start-basic-authjs/package.json index bded499b89..7648214f17 100644 --- a/examples/react/start-basic-authjs/package.json +++ b/examples/react/start-basic-authjs/package.json @@ -11,9 +11,9 @@ }, "dependencies": { "@auth/core": "^0.41.1", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.35", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/react-start": "^1.168.36", "react": "^19.0.0", "react-dom": "^19.0.0", "start-authjs": "^1.0.0", diff --git a/examples/react/start-basic-cloudflare/package.json b/examples/react/start-basic-cloudflare/package.json index 4c58962173..ffc5a68c71 100644 --- a/examples/react/start-basic-cloudflare/package.json +++ b/examples/react/start-basic-cloudflare/package.json @@ -12,9 +12,9 @@ "postinstall": "WRANGLER_LOG=warn npm run --silent cf-typegen" }, "dependencies": { - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.35", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/react-start": "^1.168.36", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/examples/react/start-basic-react-query/package.json b/examples/react/start-basic-react-query/package.json index 86bd1bdcdd..eb16a1b7eb 100644 --- a/examples/react/start-basic-react-query/package.json +++ b/examples/react/start-basic-react-query/package.json @@ -12,10 +12,10 @@ "dependencies": { "@tanstack/react-query": "^5.90.0", "@tanstack/react-query-devtools": "^5.90.0", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.35", + "@tanstack/react-start": "^1.168.36", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-basic-rsbuild/package.json b/examples/react/start-basic-rsbuild/package.json index 18c30c8d7f..9de8f02a01 100644 --- a/examples/react/start-basic-rsbuild/package.json +++ b/examples/react/start-basic-rsbuild/package.json @@ -10,9 +10,9 @@ "start": "srvx --prod -s ../client dist/server/index.js" }, "dependencies": { - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.35", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/react-start": "^1.168.36", "react": "^19.2.3", "react-dom": "^19.2.3", "tailwind-merge": "^3.6.0", diff --git a/examples/react/start-basic-static/package.json b/examples/react/start-basic-static/package.json index ceaa0a1c7b..ac369b8faf 100644 --- a/examples/react/start-basic-static/package.json +++ b/examples/react/start-basic-static/package.json @@ -10,10 +10,10 @@ "start": "pnpx srvx --prod -s ../client dist/server/server.js" }, "dependencies": { - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.35", - "@tanstack/start-static-server-functions": "^1.167.19", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/react-start": "^1.168.36", + "@tanstack/start-static-server-functions": "^1.167.20", "@vitejs/plugin-react": "^6.0.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/start-basic/package.json b/examples/react/start-basic/package.json index 23cc748f75..ade898753f 100644 --- a/examples/react/start-basic/package.json +++ b/examples/react/start-basic/package.json @@ -10,9 +10,9 @@ "start": "node .output/server/index.mjs" }, "dependencies": { - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.35", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/react-start": "^1.168.36", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.6.0", diff --git a/examples/react/start-bun/package.json b/examples/react/start-bun/package.json index 80ae09f891..9c9b7e20e2 100644 --- a/examples/react/start-bun/package.json +++ b/examples/react/start-bun/package.json @@ -15,11 +15,11 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/react-devtools": "^0.7.0", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.35", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-start": "^1.168.36", + "@tanstack/router-plugin": "^1.168.24", "react": "^19.1.1", "react-dom": "^19.1.1", "tailwindcss": "^4.2.2" diff --git a/examples/react/start-clerk-basic/package.json b/examples/react/start-clerk-basic/package.json index 96560e9406..eb068eb88d 100644 --- a/examples/react/start-clerk-basic/package.json +++ b/examples/react/start-clerk-basic/package.json @@ -11,9 +11,9 @@ }, "dependencies": { "@clerk/tanstack-react-start": "^0.27.14", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.35", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/react-start": "^1.168.36", "@vitejs/plugin-react": "^6.0.1", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/start-convex-trellaux/package.json b/examples/react/start-convex-trellaux/package.json index 155f2b269b..998eaa26b5 100644 --- a/examples/react/start-convex-trellaux/package.json +++ b/examples/react/start-convex-trellaux/package.json @@ -15,10 +15,10 @@ "@convex-dev/react-query": "0.0.0-alpha.8", "@tanstack/react-query": "^5.90.0", "@tanstack/react-query-devtools": "^5.90.0", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.35", + "@tanstack/react-start": "^1.168.36", "concurrently": "^8.2.2", "convex": "^1.19.0", "ky": "^1.7.4", diff --git a/examples/react/start-counter/package.json b/examples/react/start-counter/package.json index d9e7ec5448..afaedb0b1d 100644 --- a/examples/react/start-counter/package.json +++ b/examples/react/start-counter/package.json @@ -10,9 +10,9 @@ "start": "pnpx srvx --prod -s ../client dist/server/server.js" }, "dependencies": { - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.35", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/react-start": "^1.168.36", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/examples/react/start-i18n-paraglide/package.json b/examples/react/start-i18n-paraglide/package.json index 14199a263b..88d9c81298 100644 --- a/examples/react/start-i18n-paraglide/package.json +++ b/examples/react/start-i18n-paraglide/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tanstack/react-devtools": "^0.7.0", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.35", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/react-start": "^1.168.36", "react": "^19.1.1", "react-dom": "^19.1.1" }, diff --git a/examples/react/start-large/package.json b/examples/react/start-large/package.json index ceaa603188..369d1abe20 100644 --- a/examples/react/start-large/package.json +++ b/examples/react/start-large/package.json @@ -13,9 +13,9 @@ }, "dependencies": { "@tanstack/react-query": "^5.90.0", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.35", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/react-start": "^1.168.36", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/start-material-ui/package.json b/examples/react/start-material-ui/package.json index 4a9f0b4362..3b4588ef3f 100644 --- a/examples/react/start-material-ui/package.json +++ b/examples/react/start-material-ui/package.json @@ -15,9 +15,9 @@ "@emotion/styled": "11.14.0", "@fontsource-variable/roboto": "5.2.5", "@mui/material": "6.4.7", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.35", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/react-start": "^1.168.36", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-rscs/package.json b/examples/react/start-rscs/package.json index 013487a90a..b3e98d6190 100644 --- a/examples/react/start-rscs/package.json +++ b/examples/react/start-rscs/package.json @@ -10,9 +10,9 @@ "start": "node .output/server/index.mjs" }, "dependencies": { - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.35", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/react-start": "^1.168.36", "dexie": "^4.0.10", "react": "^19.2.0", "react-dom": "^19.2.0", diff --git a/examples/react/start-streaming-data-from-server-functions/package.json b/examples/react/start-streaming-data-from-server-functions/package.json index 844af0104f..0cf6b2d40a 100644 --- a/examples/react/start-streaming-data-from-server-functions/package.json +++ b/examples/react/start-streaming-data-from-server-functions/package.json @@ -10,9 +10,9 @@ "start": "pnpx srvx --prod -s ../client dist/server/server.js" }, "dependencies": { - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.35", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/react-start": "^1.168.36", "react": "^19.0.0", "react-dom": "^19.0.0", "zod": "^4.4.3" diff --git a/examples/react/start-supabase-basic/package.json b/examples/react/start-supabase-basic/package.json index 38efef297b..79132b7bf6 100644 --- a/examples/react/start-supabase-basic/package.json +++ b/examples/react/start-supabase-basic/package.json @@ -15,9 +15,9 @@ "dependencies": { "@supabase/ssr": "^0.5.2", "@supabase/supabase-js": "^2.48.1", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.35", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/react-start": "^1.168.36", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1" diff --git a/examples/react/start-tailwind-v4/package.json b/examples/react/start-tailwind-v4/package.json index 9ba1b872b1..bbfa2f7dee 100644 --- a/examples/react/start-tailwind-v4/package.json +++ b/examples/react/start-tailwind-v4/package.json @@ -10,9 +10,9 @@ "start": "node .output/server/index.mjs" }, "dependencies": { - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.35", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/react-start": "^1.168.36", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.6.0", diff --git a/examples/react/start-trellaux/package.json b/examples/react/start-trellaux/package.json index d1f1b717a3..723fe3f70e 100644 --- a/examples/react/start-trellaux/package.json +++ b/examples/react/start-trellaux/package.json @@ -12,10 +12,10 @@ "dependencies": { "@tanstack/react-query": "^5.90.0", "@tanstack/react-query-devtools": "^5.90.0", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", "@tanstack/react-router-ssr-query": "^1.167.1", - "@tanstack/react-start": "^1.168.35", + "@tanstack/react-start": "^1.168.36", "ky": "^1.7.4", "msw": "^2.7.0", "react": "^19.0.0", diff --git a/examples/react/start-workos/package.json b/examples/react/start-workos/package.json index c458ed9959..0fc2bb046c 100644 --- a/examples/react/start-workos/package.json +++ b/examples/react/start-workos/package.json @@ -14,9 +14,9 @@ "license": "MIT", "dependencies": { "@radix-ui/themes": "^3.3.0", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/react-start": "^1.168.35", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/react-start": "^1.168.36", "@workos/authkit-tanstack-react-start": "^0.5.0", "react": "^19.0.0", "react-dom": "^19.0.0" diff --git a/examples/react/view-transitions/package.json b/examples/react/view-transitions/package.json index d53d0da850..a597e63551 100644 --- a/examples/react/view-transitions/package.json +++ b/examples/react/view-transitions/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/router-plugin": "^1.168.24", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", diff --git a/examples/react/with-framer-motion/package.json b/examples/react/with-framer-motion/package.json index 1641fd215c..cbd7e1e24f 100644 --- a/examples/react/with-framer-motion/package.json +++ b/examples/react/with-framer-motion/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", "framer-motion": "^11.18.2", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/examples/react/with-trpc-react-query/package.json b/examples/react/with-trpc-react-query/package.json index c230435543..9e62320366 100644 --- a/examples/react/with-trpc-react-query/package.json +++ b/examples/react/with-trpc-react-query/package.json @@ -13,9 +13,9 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/react-query": "^5.90.0", "@tanstack/react-query-devtools": "^5.90.0", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/router-plugin": "^1.168.24", "@trpc/client": "^11.4.3", "@trpc/server": "^11.4.3", "@trpc/tanstack-react-query": "^11.4.3", diff --git a/examples/react/with-trpc/package.json b/examples/react/with-trpc/package.json index 3281bd02d4..6b87233386 100644 --- a/examples/react/with-trpc/package.json +++ b/examples/react/with-trpc/package.json @@ -11,9 +11,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-router": "^1.170.18", - "@tanstack/react-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/react-router": "^1.170.19", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/router-plugin": "^1.168.24", "@trpc/client": "^11.4.3", "@trpc/server": "^11.4.3", "express": "^5.2.1", diff --git a/examples/solid/authenticated-routes-firebase/package.json b/examples/solid/authenticated-routes-firebase/package.json index c333b08d96..6582e7a627 100644 --- a/examples/solid/authenticated-routes-firebase/package.json +++ b/examples/solid/authenticated-routes-firebase/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.23", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/router-plugin": "^1.168.24", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "firebase": "^11.4.0", "redaxios": "^0.5.1", "simple-icons": "^14.9.0", diff --git a/examples/solid/authenticated-routes/package.json b/examples/solid/authenticated-routes/package.json index 7fa0fbc727..7493d522f2 100644 --- a/examples/solid/authenticated-routes/package.json +++ b/examples/solid/authenticated-routes/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.23", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/router-plugin": "^1.168.24", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2", diff --git a/examples/solid/basic-default-search-params/package.json b/examples/solid/basic-default-search-params/package.json index 01af12b1ee..1445a2da15 100644 --- a/examples/solid/basic-default-search-params/package.json +++ b/examples/solid/basic-default-search-params/package.json @@ -11,8 +11,8 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/solid-query": "^5.90.9", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2", diff --git a/examples/solid/basic-devtools-panel/package.json b/examples/solid/basic-devtools-panel/package.json index 60bfd68727..a6f6f68012 100644 --- a/examples/solid/basic-devtools-panel/package.json +++ b/examples/solid/basic-devtools-panel/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" diff --git a/examples/solid/basic-file-based/package.json b/examples/solid/basic-file-based/package.json index dc6a9d7319..f652d057d4 100644 --- a/examples/solid/basic-file-based/package.json +++ b/examples/solid/basic-file-based/package.json @@ -10,15 +10,15 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2", "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/router-plugin": "^1.168.24", "@typescript/native": "npm:typescript@^7.0.2", "typescript": "npm:@typescript/typescript6@^6.0.2", "vite": "^8.0.14", diff --git a/examples/solid/basic-non-nested-devtools/package.json b/examples/solid/basic-non-nested-devtools/package.json index 802d70ef48..0aa4496d90 100644 --- a/examples/solid/basic-non-nested-devtools/package.json +++ b/examples/solid/basic-non-nested-devtools/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" diff --git a/examples/solid/basic-solid-query-file-based/package.json b/examples/solid/basic-solid-query-file-based/package.json index b386697c82..4229fff6de 100644 --- a/examples/solid/basic-solid-query-file-based/package.json +++ b/examples/solid/basic-solid-query-file-based/package.json @@ -13,15 +13,15 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-query-devtools": "^5.90.0", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2", "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/router-plugin": "^1.168.24", "@typescript/native": "npm:typescript@^7.0.2", "typescript": "npm:@typescript/typescript6@^6.0.2", "vite": "^8.0.14", diff --git a/examples/solid/basic-solid-query/package.json b/examples/solid/basic-solid-query/package.json index d295236208..a5efd67924 100644 --- a/examples/solid/basic-solid-query/package.json +++ b/examples/solid/basic-solid-query/package.json @@ -12,14 +12,14 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-query-devtools": "^5.90.0", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/router-plugin": "^1.168.24", "@typescript/native": "npm:typescript@^7.0.2", "typescript": "npm:@typescript/typescript6@^6.0.2", "vite": "^8.0.14", diff --git a/examples/solid/basic-ssr-file-based/package.json b/examples/solid/basic-ssr-file-based/package.json index ce43f7e42e..b4661b4c6d 100644 --- a/examples/solid/basic-ssr-file-based/package.json +++ b/examples/solid/basic-ssr-file-based/package.json @@ -11,8 +11,8 @@ "debug": "node --inspect-brk server" }, "dependencies": { - "@tanstack/solid-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/router-plugin": "^1.168.24", "compression": "^1.8.0", "express": "^5.2.1", "get-port": "^7.1.0", @@ -20,7 +20,7 @@ "solid-js": "^1.9.10" }, "devDependencies": { - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router-devtools": "^1.167.1", "@types/express": "^5.0.6", "vite-plugin-solid": "^2.11.11", "@typescript/native": "npm:typescript@^7.0.2", diff --git a/examples/solid/basic-ssr-streaming-file-based/package.json b/examples/solid/basic-ssr-streaming-file-based/package.json index 99a9253cea..ba1b36008c 100644 --- a/examples/solid/basic-ssr-streaming-file-based/package.json +++ b/examples/solid/basic-ssr-streaming-file-based/package.json @@ -12,8 +12,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "compression": "^1.8.0", "express": "^5.2.1", "get-port": "^7.1.0", @@ -24,7 +24,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/router-plugin": "^1.168.24", "@types/express": "^5.0.6", "@typescript/native": "npm:typescript@^7.0.2", "typescript": "npm:@typescript/typescript6@^6.0.2", diff --git a/examples/solid/basic-virtual-file-based/package.json b/examples/solid/basic-virtual-file-based/package.json index 6db2fe5819..8d67ea0a1c 100644 --- a/examples/solid/basic-virtual-file-based/package.json +++ b/examples/solid/basic-virtual-file-based/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.23", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/router-plugin": "^1.168.24", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "@tanstack/virtual-file-routes": "^1.162.0", "redaxios": "^0.5.1", "solid-js": "^1.9.10", diff --git a/examples/solid/basic-virtual-inside-file-based/package.json b/examples/solid/basic-virtual-inside-file-based/package.json index 2e4b993db1..7c2c04bbed 100644 --- a/examples/solid/basic-virtual-inside-file-based/package.json +++ b/examples/solid/basic-virtual-inside-file-based/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.23", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/router-plugin": "^1.168.24", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "@tanstack/virtual-file-routes": "^1.162.0", "redaxios": "^0.5.1", "solid-js": "^1.9.10", diff --git a/examples/solid/basic/package.json b/examples/solid/basic/package.json index f004585c1a..6ccabf7cfc 100644 --- a/examples/solid/basic/package.json +++ b/examples/solid/basic/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" diff --git a/examples/solid/deferred-data/package.json b/examples/solid/deferred-data/package.json index 45670d818d..9d4c9f5fbe 100644 --- a/examples/solid/deferred-data/package.json +++ b/examples/solid/deferred-data/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2", diff --git a/examples/solid/i18n-paraglide/package.json b/examples/solid/i18n-paraglide/package.json index 1fda97e77d..551b05555f 100644 --- a/examples/solid/i18n-paraglide/package.json +++ b/examples/solid/i18n-paraglide/package.json @@ -11,8 +11,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/router-plugin": "^1.168.24", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" }, diff --git a/examples/solid/kitchen-sink-file-based/package.json b/examples/solid/kitchen-sink-file-based/package.json index 154e592dca..be5f857287 100644 --- a/examples/solid/kitchen-sink-file-based/package.json +++ b/examples/solid/kitchen-sink-file-based/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "immer": "^10.1.1", "redaxios": "^0.5.1", "solid-js": "^1.9.10", @@ -19,7 +19,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/router-plugin": "^1.168.24", "@typescript/native": "npm:typescript@^7.0.2", "typescript": "npm:@typescript/typescript6@^6.0.2", "vite": "^8.0.14", diff --git a/examples/solid/kitchen-sink-solid-query-file-based/package.json b/examples/solid/kitchen-sink-solid-query-file-based/package.json index 6166dd7bda..5e22d7d2fc 100644 --- a/examples/solid/kitchen-sink-solid-query-file-based/package.json +++ b/examples/solid/kitchen-sink-solid-query-file-based/package.json @@ -10,11 +10,11 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/router-plugin": "^1.168.24", "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-query-devtools": "^5.90.0", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "immer": "^10.1.1", "redaxios": "^0.5.1", "solid-js": "^1.9.10", diff --git a/examples/solid/kitchen-sink-solid-query/package.json b/examples/solid/kitchen-sink-solid-query/package.json index 1c66354899..48600b172a 100644 --- a/examples/solid/kitchen-sink-solid-query/package.json +++ b/examples/solid/kitchen-sink-solid-query/package.json @@ -12,8 +12,8 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-query-devtools": "^5.90.0", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "immer": "^10.1.1", "redaxios": "^0.5.1", "solid-js": "^1.9.10", diff --git a/examples/solid/kitchen-sink/package.json b/examples/solid/kitchen-sink/package.json index fb4658c1eb..01daff0d7f 100644 --- a/examples/solid/kitchen-sink/package.json +++ b/examples/solid/kitchen-sink/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "immer": "^10.1.1", "redaxios": "^0.5.1", "solid-js": "^1.9.10", diff --git a/examples/solid/large-file-based/package.json b/examples/solid/large-file-based/package.json index 6ff299092c..4fc56bda64 100644 --- a/examples/solid/large-file-based/package.json +++ b/examples/solid/large-file-based/package.json @@ -12,10 +12,10 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/router-plugin": "^1.168.24", "@tanstack/solid-query": "^5.90.9", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2", diff --git a/examples/solid/location-masking/package.json b/examples/solid/location-masking/package.json index 086688663e..5b0c9e6b55 100644 --- a/examples/solid/location-masking/package.json +++ b/examples/solid/location-masking/package.json @@ -11,8 +11,8 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/solid-query": "^5.90.9", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "solid-js": "^1.9.10", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2" diff --git a/examples/solid/navigation-blocking/package.json b/examples/solid/navigation-blocking/package.json index e7baf89ac7..9de6f6578d 100644 --- a/examples/solid/navigation-blocking/package.json +++ b/examples/solid/navigation-blocking/package.json @@ -11,8 +11,8 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/solid-query": "^5.90.9", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "solid-js": "^1.9.10", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2" diff --git a/examples/solid/quickstart-esbuild-file-based/package.json b/examples/solid/quickstart-esbuild-file-based/package.json index 118afd0bab..67f308d022 100644 --- a/examples/solid/quickstart-esbuild-file-based/package.json +++ b/examples/solid/quickstart-esbuild-file-based/package.json @@ -9,9 +9,9 @@ "start": "dev" }, "dependencies": { - "@tanstack/router-plugin": "^1.168.23", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/router-plugin": "^1.168.24", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2", diff --git a/examples/solid/quickstart-file-based/package.json b/examples/solid/quickstart-file-based/package.json index 82cc27c9b0..c2c180d68b 100644 --- a/examples/solid/quickstart-file-based/package.json +++ b/examples/solid/quickstart-file-based/package.json @@ -10,15 +10,15 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2", "zod": "^4.4.3" }, "devDependencies": { - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/router-plugin": "^1.168.24", "@typescript/native": "npm:typescript@^7.0.2", "typescript": "npm:@typescript/typescript6@^6.0.2", "vite": "^8.0.14", diff --git a/examples/solid/quickstart-rspack-file-based/package.json b/examples/solid/quickstart-rspack-file-based/package.json index 149edaad33..f7fe418a41 100644 --- a/examples/solid/quickstart-rspack-file-based/package.json +++ b/examples/solid/quickstart-rspack-file-based/package.json @@ -9,8 +9,8 @@ }, "dependencies": { "@tailwindcss/postcss": "^4.2.2", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "postcss": "^8.5.1", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" @@ -19,7 +19,7 @@ "@rsbuild/core": "^2.1.0", "@rsbuild/plugin-babel": "^1.1.2", "@rsbuild/plugin-solid": "^1.1.1", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/router-plugin": "^1.168.24", "@typescript/native": "npm:typescript@^7.0.2", "typescript": "npm:@typescript/typescript6@^6.0.2" } diff --git a/examples/solid/quickstart-webpack-file-based/package.json b/examples/solid/quickstart-webpack-file-based/package.json index 5ccb742b24..4e441133e7 100644 --- a/examples/solid/quickstart-webpack-file-based/package.json +++ b/examples/solid/quickstart-webpack-file-based/package.json @@ -7,15 +7,15 @@ "build": "webpack build && tsc --noEmit" }, "dependencies": { - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" }, "devDependencies": { "@babel/core": "^7.28.5", "@babel/preset-typescript": "^7.27.1", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/router-plugin": "^1.168.24", "babel-loader": "^10.0.0", "babel-preset-solid": "^1.9.10", "css-loader": "^7.1.2", diff --git a/examples/solid/quickstart/package.json b/examples/solid/quickstart/package.json index 59dc16cd4f..f2a45d869c 100644 --- a/examples/solid/quickstart/package.json +++ b/examples/solid/quickstart/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" }, diff --git a/examples/solid/router-monorepo-simple-lazy/package.json b/examples/solid/router-monorepo-simple-lazy/package.json index 57dabb8472..a65183fdc9 100644 --- a/examples/solid/router-monorepo-simple-lazy/package.json +++ b/examples/solid/router-monorepo-simple-lazy/package.json @@ -8,9 +8,9 @@ "dev": "pnpm router build && pnpm post-feature build && pnpm app dev" }, "dependencies": { - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", + "@tanstack/router-plugin": "^1.168.24", "solid-js": "^1.9.10", "redaxios": "^0.5.1" }, diff --git a/examples/solid/router-monorepo-simple-lazy/packages/app/package.json b/examples/solid/router-monorepo-simple-lazy/packages/app/package.json index 814174b07d..3cfa6a1377 100644 --- a/examples/solid/router-monorepo-simple-lazy/packages/app/package.json +++ b/examples/solid/router-monorepo-simple-lazy/packages/app/package.json @@ -20,7 +20,7 @@ "@vitejs/plugin-react": "^6.0.1", "@typescript/native": "npm:typescript@^7.0.2", "typescript": "npm:@typescript/typescript6@^6.0.2", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router-devtools": "^1.167.1", "tailwindcss": "^4.2.2", "vite": "^8.0.14", "vite-plugin-dts": "^4.5.4", diff --git a/examples/solid/router-monorepo-simple-lazy/packages/router/package.json b/examples/solid/router-monorepo-simple-lazy/packages/router/package.json index ecba56af40..922a9663a7 100644 --- a/examples/solid/router-monorepo-simple-lazy/packages/router/package.json +++ b/examples/solid/router-monorepo-simple-lazy/packages/router/package.json @@ -9,8 +9,8 @@ "types": "./dist/index.d.ts", "dependencies": { "@tanstack/history": "^1.162.0", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/router-plugin": "^1.168.24", "redaxios": "^0.5.1", "zod": "^4.4.3", "solid-js": "^1.9.10" diff --git a/examples/solid/router-monorepo-simple/package.json b/examples/solid/router-monorepo-simple/package.json index 090f9677f6..2e25cfd8fc 100644 --- a/examples/solid/router-monorepo-simple/package.json +++ b/examples/solid/router-monorepo-simple/package.json @@ -8,9 +8,9 @@ "dev": "pnpm router build && pnpm post-feature build && pnpm app dev" }, "dependencies": { - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", + "@tanstack/router-plugin": "^1.168.24", "solid-js": "^1.9.10", "redaxios": "^0.5.1" }, diff --git a/examples/solid/router-monorepo-simple/packages/app/package.json b/examples/solid/router-monorepo-simple/packages/app/package.json index 97de75d4ed..937bf9795c 100644 --- a/examples/solid/router-monorepo-simple/packages/app/package.json +++ b/examples/solid/router-monorepo-simple/packages/app/package.json @@ -17,7 +17,7 @@ "vite-plugin-solid": "^2.11.11", "@typescript/native": "npm:typescript@^7.0.2", "typescript": "npm:@typescript/typescript6@^6.0.2", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router-devtools": "^1.167.1", "vite": "^8.0.14", "tailwindcss": "^4.2.2", "vite-plugin-dts": "^4.5.4", diff --git a/examples/solid/router-monorepo-simple/packages/router/package.json b/examples/solid/router-monorepo-simple/packages/router/package.json index 6ed9a77798..c405e637df 100644 --- a/examples/solid/router-monorepo-simple/packages/router/package.json +++ b/examples/solid/router-monorepo-simple/packages/router/package.json @@ -9,8 +9,8 @@ "types": "./dist/index.d.ts", "dependencies": { "@tanstack/history": "^1.162.0", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/router-plugin": "^1.168.24", "redaxios": "^0.5.1", "zod": "^4.4.3", "solid-js": "^1.9.10" diff --git a/examples/solid/router-monorepo-solid-query/package.json b/examples/solid/router-monorepo-solid-query/package.json index 2e0330ad6d..54822bc3bc 100644 --- a/examples/solid/router-monorepo-solid-query/package.json +++ b/examples/solid/router-monorepo-solid-query/package.json @@ -12,9 +12,9 @@ "dependencies": { "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-query-devtools": "^5.90.0", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", + "@tanstack/router-plugin": "^1.168.24", "solid-js": "^1.9.10", "redaxios": "^0.5.1" }, diff --git a/examples/solid/router-monorepo-solid-query/packages/app/package.json b/examples/solid/router-monorepo-solid-query/packages/app/package.json index af7273235b..bf2cc8ab04 100644 --- a/examples/solid/router-monorepo-solid-query/packages/app/package.json +++ b/examples/solid/router-monorepo-solid-query/packages/app/package.json @@ -18,7 +18,7 @@ "vite-plugin-solid": "^2.11.11", "@typescript/native": "npm:typescript@^7.0.2", "typescript": "npm:@typescript/typescript6@^6.0.2", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router-devtools": "^1.167.1", "tailwindcss": "^4.2.2", "vite": "^8.0.14", "vite-plugin-dts": "^4.5.4", diff --git a/examples/solid/router-monorepo-solid-query/packages/router/package.json b/examples/solid/router-monorepo-solid-query/packages/router/package.json index 64be0a0082..5a44bf044b 100644 --- a/examples/solid/router-monorepo-solid-query/packages/router/package.json +++ b/examples/solid/router-monorepo-solid-query/packages/router/package.json @@ -10,8 +10,8 @@ "dependencies": { "@tanstack/history": "^1.162.0", "@tanstack/solid-query": "^5.90.9", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/router-plugin": "^1.168.24", "@router-solid-mono-solid-query/post-query": "workspace:*", "redaxios": "^0.5.1", "zod": "^4.4.3", diff --git a/examples/solid/scroll-restoration/package.json b/examples/solid/scroll-restoration/package.json index 11663a69ac..f4ee50babb 100644 --- a/examples/solid/scroll-restoration/package.json +++ b/examples/solid/scroll-restoration/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "@tanstack/solid-virtual": "^3.13.0", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" diff --git a/examples/solid/search-validator-adapters/package.json b/examples/solid/search-validator-adapters/package.json index 648d925e59..b12a9b8313 100644 --- a/examples/solid/search-validator-adapters/package.json +++ b/examples/solid/search-validator-adapters/package.json @@ -13,9 +13,9 @@ "@tailwindcss/vite": "^4.2.2", "@tanstack/arktype-adapter": "^1.167.0", "@tanstack/solid-query": "^5.90.9", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", + "@tanstack/router-plugin": "^1.168.24", "@tanstack/valibot-adapter": "^1.167.0", "@tanstack/zod-adapter": "^1.167.0", "arktype": "^2.1.7", diff --git a/examples/solid/start-basic-auth/package.json b/examples/solid/start-basic-auth/package.json index ec7539a2bf..02f928a230 100644 --- a/examples/solid/start-basic-auth/package.json +++ b/examples/solid/start-basic-auth/package.json @@ -14,9 +14,9 @@ "@libsql/client": "^0.15.15", "@prisma/adapter-libsql": "^7.0.0", "@prisma/client": "^7.0.0", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.34", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", + "@tanstack/solid-start": "^1.168.35", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-authjs/package.json b/examples/solid/start-basic-authjs/package.json index 914d7e5184..fb9084671e 100644 --- a/examples/solid/start-basic-authjs/package.json +++ b/examples/solid/start-basic-authjs/package.json @@ -11,9 +11,9 @@ }, "dependencies": { "@auth/core": "^0.41.1", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.34", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", + "@tanstack/solid-start": "^1.168.35", "solid-js": "^1.9.10", "start-authjs": "^1.0.0", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-cloudflare/package.json b/examples/solid/start-basic-cloudflare/package.json index a8764bbec9..f8109e68e3 100644 --- a/examples/solid/start-basic-cloudflare/package.json +++ b/examples/solid/start-basic-cloudflare/package.json @@ -12,9 +12,9 @@ "postinstall": "WRANGLER_LOG=warn npm run --silent cf-typegen" }, "dependencies": { - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.34", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", + "@tanstack/solid-start": "^1.168.35", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-netlify/package.json b/examples/solid/start-basic-netlify/package.json index 6e51fff097..0c6ad51c48 100644 --- a/examples/solid/start-basic-netlify/package.json +++ b/examples/solid/start-basic-netlify/package.json @@ -9,9 +9,9 @@ "preview": "vite preview" }, "dependencies": { - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.34", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", + "@tanstack/solid-start": "^1.168.35", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-nitro/package.json b/examples/solid/start-basic-nitro/package.json index 2af6bc93b8..6af3b19d96 100644 --- a/examples/solid/start-basic-nitro/package.json +++ b/examples/solid/start-basic-nitro/package.json @@ -9,9 +9,9 @@ "preview": "vite preview" }, "dependencies": { - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.34", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", + "@tanstack/solid-start": "^1.168.35", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-basic-solid-query/package.json b/examples/solid/start-basic-solid-query/package.json index f960820abc..97a384dd19 100644 --- a/examples/solid/start-basic-solid-query/package.json +++ b/examples/solid/start-basic-solid-query/package.json @@ -12,10 +12,10 @@ "dependencies": { "@tanstack/solid-query": "^5.90.9", "@tanstack/solid-query-devtools": "^5.90.0", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "@tanstack/solid-router-ssr-query": "^1.167.1", - "@tanstack/solid-start": "^1.168.34", + "@tanstack/solid-start": "^1.168.35", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic-static/package.json b/examples/solid/start-basic-static/package.json index babb41b1f0..e3fa505b42 100644 --- a/examples/solid/start-basic-static/package.json +++ b/examples/solid/start-basic-static/package.json @@ -10,10 +10,10 @@ "start": "pnpx srvx --prod -s ../client dist/server/server.js" }, "dependencies": { - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.34", - "@tanstack/start-static-server-functions": "^1.167.19", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", + "@tanstack/solid-start": "^1.168.35", + "@tanstack/start-static-server-functions": "^1.167.20", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-basic/package.json b/examples/solid/start-basic/package.json index 40f2780c29..6db050f088 100644 --- a/examples/solid/start-basic/package.json +++ b/examples/solid/start-basic/package.json @@ -10,9 +10,9 @@ "start": "node .output/server/index.mjs" }, "dependencies": { - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.34", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", + "@tanstack/solid-start": "^1.168.35", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0" diff --git a/examples/solid/start-bun/package.json b/examples/solid/start-bun/package.json index a71a471e65..bf3edcddd3 100644 --- a/examples/solid/start-bun/package.json +++ b/examples/solid/start-bun/package.json @@ -15,11 +15,11 @@ "dependencies": { "@tailwindcss/vite": "^4.2.2", "@tanstack/solid-devtools": "^0.7.0", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "@tanstack/solid-router-ssr-query": "^1.167.1", - "@tanstack/solid-start": "^1.168.34", - "@tanstack/router-plugin": "^1.168.23", + "@tanstack/solid-start": "^1.168.35", + "@tanstack/router-plugin": "^1.168.24", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2" }, diff --git a/examples/solid/start-convex-better-auth/package.json b/examples/solid/start-convex-better-auth/package.json index aa075d1999..dbe24d17a6 100644 --- a/examples/solid/start-convex-better-auth/package.json +++ b/examples/solid/start-convex-better-auth/package.json @@ -13,9 +13,9 @@ "dependencies": { "@convex-dev/better-auth": "^0.9.7", "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.34", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", + "@tanstack/solid-start": "^1.168.35", "better-auth": "^1.6.19", "clsx": "^2.1.1", "convex": "^1.28.2", diff --git a/examples/solid/start-counter/package.json b/examples/solid/start-counter/package.json index f66cd690ec..dcd446787b 100644 --- a/examples/solid/start-counter/package.json +++ b/examples/solid/start-counter/package.json @@ -10,9 +10,9 @@ "start": "pnpx srvx --prod -s ../client dist/server/server.js" }, "dependencies": { - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.34", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", + "@tanstack/solid-start": "^1.168.35", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", diff --git a/examples/solid/start-i18n-paraglide/package.json b/examples/solid/start-i18n-paraglide/package.json index e6875378a3..956fdc4ff4 100644 --- a/examples/solid/start-i18n-paraglide/package.json +++ b/examples/solid/start-i18n-paraglide/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tanstack/solid-devtools": "^0.7.0", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.34", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", + "@tanstack/solid-start": "^1.168.35", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/examples/solid/start-large/package.json b/examples/solid/start-large/package.json index eaecb7db39..1a641b9b2f 100644 --- a/examples/solid/start-large/package.json +++ b/examples/solid/start-large/package.json @@ -13,9 +13,9 @@ }, "dependencies": { "@tanstack/solid-query": "^5.90.9", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.34", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", + "@tanstack/solid-start": "^1.168.35", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", diff --git a/examples/solid/start-streaming-data-from-server-functions/package.json b/examples/solid/start-streaming-data-from-server-functions/package.json index fe2e7fd0e6..11ff02f0c2 100644 --- a/examples/solid/start-streaming-data-from-server-functions/package.json +++ b/examples/solid/start-streaming-data-from-server-functions/package.json @@ -10,9 +10,9 @@ "start": "pnpx srvx --prod -s ../client dist/server/server.js" }, "dependencies": { - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.34", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", + "@tanstack/solid-start": "^1.168.35", "solid-js": "^1.9.10", "zod": "^4.4.3" }, diff --git a/examples/solid/start-supabase-basic/package.json b/examples/solid/start-supabase-basic/package.json index 9ac110bec0..3f62654e66 100644 --- a/examples/solid/start-supabase-basic/package.json +++ b/examples/solid/start-supabase-basic/package.json @@ -14,9 +14,9 @@ "dependencies": { "@supabase/ssr": "^0.5.2", "@supabase/supabase-js": "^2.48.1", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.34", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", + "@tanstack/solid-start": "^1.168.35", "solid-js": "^1.9.9", "redaxios": "^0.5.1" }, diff --git a/examples/solid/start-tailwind-v4/package.json b/examples/solid/start-tailwind-v4/package.json index 24c8294b53..af649cec96 100644 --- a/examples/solid/start-tailwind-v4/package.json +++ b/examples/solid/start-tailwind-v4/package.json @@ -10,9 +10,9 @@ "start": "node .output/server/index.mjs" }, "dependencies": { - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", - "@tanstack/solid-start": "^1.168.34", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", + "@tanstack/solid-start": "^1.168.35", "solid-js": "^1.9.10", "tailwind-merge": "^3.6.0", "zod": "^4.4.3" diff --git a/examples/solid/view-transitions/package.json b/examples/solid/view-transitions/package.json index 0792700723..fb24384531 100644 --- a/examples/solid/view-transitions/package.json +++ b/examples/solid/view-transitions/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.23", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/router-plugin": "^1.168.24", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwindcss": "^4.2.2", diff --git a/examples/solid/with-framer-motion/package.json b/examples/solid/with-framer-motion/package.json index a01d2d4082..6781807eff 100644 --- a/examples/solid/with-framer-motion/package.json +++ b/examples/solid/with-framer-motion/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "solid-motionone": "^1.0.4", diff --git a/examples/solid/with-trpc/package.json b/examples/solid/with-trpc/package.json index 9bcca25387..a315283af6 100644 --- a/examples/solid/with-trpc/package.json +++ b/examples/solid/with-trpc/package.json @@ -11,9 +11,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.23", - "@tanstack/solid-router": "^1.170.18", - "@tanstack/solid-router-devtools": "^1.167.0", + "@tanstack/router-plugin": "^1.168.24", + "@tanstack/solid-router": "^1.170.19", + "@tanstack/solid-router-devtools": "^1.167.1", "@trpc/client": "^11.4.3", "@trpc/server": "^11.4.3", "express": "^5.2.1", diff --git a/examples/vue/basic-file-based-jsx/package.json b/examples/vue/basic-file-based-jsx/package.json index 98424f4f4e..5e94ab51ea 100644 --- a/examples/vue/basic-file-based-jsx/package.json +++ b/examples/vue/basic-file-based-jsx/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.23", - "@tanstack/vue-router": "^1.170.17", - "@tanstack/vue-router-devtools": "^1.167.0", + "@tanstack/router-plugin": "^1.168.24", + "@tanstack/vue-router": "^1.170.18", + "@tanstack/vue-router-devtools": "^1.167.1", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2", "vue": "^3.5.16", diff --git a/examples/vue/basic-file-based-sfc/package.json b/examples/vue/basic-file-based-sfc/package.json index 320c5dacfb..f2a41544cb 100644 --- a/examples/vue/basic-file-based-sfc/package.json +++ b/examples/vue/basic-file-based-sfc/package.json @@ -10,9 +10,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.2.2", - "@tanstack/router-plugin": "^1.168.23", - "@tanstack/vue-router": "^1.170.17", - "@tanstack/vue-router-devtools": "^1.167.0", + "@tanstack/router-plugin": "^1.168.24", + "@tanstack/vue-router": "^1.170.18", + "@tanstack/vue-router-devtools": "^1.167.1", "redaxios": "^0.5.1", "tailwindcss": "^4.2.2", "vue": "^3.5.16", diff --git a/examples/vue/basic/package.json b/examples/vue/basic/package.json index 8dfe217267..16c55a19b5 100644 --- a/examples/vue/basic/package.json +++ b/examples/vue/basic/package.json @@ -9,8 +9,8 @@ "start": "vite" }, "dependencies": { - "@tanstack/vue-router": "^1.170.17", - "@tanstack/vue-router-devtools": "^1.167.0", + "@tanstack/vue-router": "^1.170.18", + "@tanstack/vue-router-devtools": "^1.167.1", "redaxios": "^0.5.1", "vue": "^3.5.13", "tailwindcss": "4.1.18" diff --git a/packages/react-router-devtools/CHANGELOG.md b/packages/react-router-devtools/CHANGELOG.md index 40e065c673..7634f3d19e 100644 --- a/packages/react-router-devtools/CHANGELOG.md +++ b/packages/react-router-devtools/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/react-router-devtools +## 1.167.1 + +### Patch Changes + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/router-core@1.171.16 + - @tanstack/react-router@1.170.19 + - @tanstack/router-devtools-core@1.168.1 + ## 1.167.0 ### Minor Changes diff --git a/packages/react-router-devtools/package.json b/packages/react-router-devtools/package.json index 505a168955..262a9294fb 100644 --- a/packages/react-router-devtools/package.json +++ b/packages/react-router-devtools/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-router-devtools", - "version": "1.167.0", + "version": "1.167.1", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-router/CHANGELOG.md b/packages/react-router/CHANGELOG.md index 46909b1699..49e069817b 100644 --- a/packages/react-router/CHANGELOG.md +++ b/packages/react-router/CHANGELOG.md @@ -1,5 +1,38 @@ # @tanstack/react-router +## 1.170.19 + +### Patch Changes + +- [#7805](https://github.com/TanStack/router/pull/7805) [`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd) - Rewrite match loading around a lane-based scheduler that tracks each navigation, preload, and background reload as an ordered unit of work. This fixes pending/redirect/retry state leaking between overlapping navigations, restores correct SSR status codes for redirects, errors, and not-found responses, and closes hydration gaps where the client re-ran work the server had already completed. + - Invalidation now retires matching active preloads so older speculative loader results cannot become fresh cache data after invalidation. + - Route `headers()` now only runs on the server, matching the documented behavior — it is no longer invoked during client-side asset projection. + - The documented default `gcTime` and `preloadGcTime` now match the existing runtime default of 5 minutes (`300_000`). + + **Removed / changed exported internals** + - `RouterState` no longer includes `loadedAt`, `isTransitioning`, `statusCode`, or `redirect`. Use `match.updatedAt` in place of `loadedAt`; subscribe to `router.state.status` / `router.state.isLoading` in place of `isTransitioning`; server response status and redirect handling are now internal to the server loader and are no longer exposed on `router.state`. + - `RouteMatch.fetchCount` has been removed, with no replacement — it was purely informational. + - `RouteMatch.status` no longer includes `'redirected'` (it remains `'pending' | 'success' | 'error' | 'notFound'`) — redirected matches are dropped from the match list instead of being rendered. + - `RouteMatch.globalNotFound` has been renamed and privatized to the internal `_notFound` field. Use `match.status === 'notFound'` instead. + - The exported React, Solid, and Vue `Match` components now accept `routeId` instead of `matchId`. + - The exported `RouterStores` adapter contract now uses route-keyed presentation stores: `matchesId` is replaced by `ids`, `matchStores` by `byRoute`, and `getRouteMatchStore()` by `getMatchStore()`. The separate `loadedAt`, `isLoading`, `isTransitioning`, `statusCode`, and `redirect` stores have been removed, along with the pending/cache stores and their setters. `StoreConfig.init` has also been removed. Read application-facing state from `router.state`; preload and cache coordination are now internal. + - Removed `RouterCore` members `getMatch()`, `updateMatch()`, `cancelMatch()`, and `cancelMatches()` — read matches from `router.state.matches` (e.g. `router.state.matches.find((m) => m.id === id)`); there is no replacement for mutating or cancelling an individual in-flight match from outside the router. + - Removed `RouterCore.hasNotFoundMatch()` — use `router.state.matches.some((m) => m.status === 'notFound')`. + - Removed `RouterCore.looseRoutesById` — use `routesById`. + - Removed `RouterCore.isPrerendering()`, `RouterCore.isViewTransitionTypesSupported`, and `RouterCore.viewTransitionPromise`, with no replacement. + - Removed `RouterCore.getParsedLocationHref()` and `RouterCore.clearExpiredCache()`, with no replacement — expired cache entries are now reconciled automatically as part of match commit. + - Removed `RouterCore.latestLoadPromise` and `RouterCore.beforeLoad()`, with no replacement. + - `RouterCore.commitLocationPromise` and `RouterCore.pendingBuiltLocation` have been replaced by the internal `_commitPromise` and `_pendingLocation` fields. + - Removed the exported `GetMatchFn` and `UpdateMatchFn` types, along with the methods they typed. + - Removed the standalone `getMatchedRoutes()` export from `@tanstack/router-core` — use the `router.getMatchedRoutes()` instance method instead. + - `RouterCore.loadRouteChunk()` no longer accepts an array of component types as its second argument. One-argument usage is unchanged; the optional second argument is now `'errorComponent'`, `'notFoundComponent'`, or `false` for internal boundary loading. + - Removed `Redirect.redirectHandled`, which was internal redirect bookkeeping. + - `MatchRoutesOpts.preload` and `MatchRoutesOpts.dest` have been removed. + - `StartTransitionFn` is now `(fn, expected) => Promise` (previously `(fn) => void`). This only affects custom framework adapters that implement `startTransition`. + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/router-core@1.171.16 + ## 1.170.18 ### Patch Changes diff --git a/packages/react-router/package.json b/packages/react-router/package.json index 9120fbacbf..fdb6266ef2 100644 --- a/packages/react-router/package.json +++ b/packages/react-router/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-router", - "version": "1.170.18", + "version": "1.170.19", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-start-client/CHANGELOG.md b/packages/react-start-client/CHANGELOG.md index 3407c7af4d..261be673df 100644 --- a/packages/react-start-client/CHANGELOG.md +++ b/packages/react-start-client/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/react-start-client +## 1.168.17 + +### Patch Changes + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/router-core@1.171.16 + - @tanstack/react-router@1.170.19 + - @tanstack/start-client-core@1.170.15 + ## 1.168.16 ### Patch Changes diff --git a/packages/react-start-client/package.json b/packages/react-start-client/package.json index ecffb8737b..1b85f031c3 100644 --- a/packages/react-start-client/package.json +++ b/packages/react-start-client/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start-client", - "version": "1.168.16", + "version": "1.168.17", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-start-rsc/CHANGELOG.md b/packages/react-start-rsc/CHANGELOG.md index f75d31ebc0..90e0807dd2 100644 --- a/packages/react-start-rsc/CHANGELOG.md +++ b/packages/react-start-rsc/CHANGELOG.md @@ -1,5 +1,16 @@ # @tanstack/react-start-rsc +## 0.1.35 + +### Patch Changes + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/router-core@1.171.16 + - @tanstack/react-router@1.170.19 + - @tanstack/start-client-core@1.170.15 + - @tanstack/start-plugin-core@1.171.27 + - @tanstack/start-storage-context@1.167.18 + ## 0.1.34 ### Patch Changes diff --git a/packages/react-start-rsc/package.json b/packages/react-start-rsc/package.json index 8968caac3c..7280c24c39 100644 --- a/packages/react-start-rsc/package.json +++ b/packages/react-start-rsc/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start-rsc", - "version": "0.1.34", + "version": "0.1.35", "description": "React Server Components support for TanStack Start", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-start-server/CHANGELOG.md b/packages/react-start-server/CHANGELOG.md index 10a3a2b7a0..d1ba326d96 100644 --- a/packages/react-start-server/CHANGELOG.md +++ b/packages/react-start-server/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/react-start-server +## 1.167.24 + +### Patch Changes + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/router-core@1.171.16 + - @tanstack/react-router@1.170.19 + - @tanstack/start-server-core@1.169.19 + ## 1.167.23 ### Patch Changes diff --git a/packages/react-start-server/package.json b/packages/react-start-server/package.json index d593cb9237..3e5b0f67db 100644 --- a/packages/react-start-server/package.json +++ b/packages/react-start-server/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start-server", - "version": "1.167.23", + "version": "1.167.24", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/react-start/CHANGELOG.md b/packages/react-start/CHANGELOG.md index 8236580be4..d579571791 100644 --- a/packages/react-start/CHANGELOG.md +++ b/packages/react-start/CHANGELOG.md @@ -1,5 +1,18 @@ # @tanstack/react-start +## 1.168.36 + +### Patch Changes + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/react-router@1.170.19 + - @tanstack/react-start-client@1.168.17 + - @tanstack/react-start-rsc@0.1.35 + - @tanstack/react-start-server@1.167.24 + - @tanstack/start-client-core@1.170.15 + - @tanstack/start-plugin-core@1.171.27 + - @tanstack/start-server-core@1.169.19 + ## 1.168.35 ### Patch Changes diff --git a/packages/react-start/package.json b/packages/react-start/package.json index c2c77d854b..9abee3a86e 100644 --- a/packages/react-start/package.json +++ b/packages/react-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/react-start", - "version": "1.168.35", + "version": "1.168.36", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-cli/CHANGELOG.md b/packages/router-cli/CHANGELOG.md index 41092bfd37..8b9c4c1d71 100644 --- a/packages/router-cli/CHANGELOG.md +++ b/packages/router-cli/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/router-cli +## 1.167.22 + +### Patch Changes + +- Updated dependencies []: + - @tanstack/router-generator@1.167.22 + ## 1.167.21 ### Patch Changes diff --git a/packages/router-cli/package.json b/packages/router-cli/package.json index 8d91797480..313869c287 100644 --- a/packages/router-cli/package.json +++ b/packages/router-cli/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-cli", - "version": "1.167.21", + "version": "1.167.22", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-core/CHANGELOG.md b/packages/router-core/CHANGELOG.md index 360436487b..52db7369b6 100644 --- a/packages/router-core/CHANGELOG.md +++ b/packages/router-core/CHANGELOG.md @@ -1,5 +1,35 @@ # @tanstack/router-core +## 1.171.16 + +### Patch Changes + +- [#7805](https://github.com/TanStack/router/pull/7805) [`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd) - Rewrite match loading around a lane-based scheduler that tracks each navigation, preload, and background reload as an ordered unit of work. This fixes pending/redirect/retry state leaking between overlapping navigations, restores correct SSR status codes for redirects, errors, and not-found responses, and closes hydration gaps where the client re-ran work the server had already completed. + - Invalidation now retires matching active preloads so older speculative loader results cannot become fresh cache data after invalidation. + - Route `headers()` now only runs on the server, matching the documented behavior — it is no longer invoked during client-side asset projection. + - The documented default `gcTime` and `preloadGcTime` now match the existing runtime default of 5 minutes (`300_000`). + + **Removed / changed exported internals** + - `RouterState` no longer includes `loadedAt`, `isTransitioning`, `statusCode`, or `redirect`. Use `match.updatedAt` in place of `loadedAt`; subscribe to `router.state.status` / `router.state.isLoading` in place of `isTransitioning`; server response status and redirect handling are now internal to the server loader and are no longer exposed on `router.state`. + - `RouteMatch.fetchCount` has been removed, with no replacement — it was purely informational. + - `RouteMatch.status` no longer includes `'redirected'` (it remains `'pending' | 'success' | 'error' | 'notFound'`) — redirected matches are dropped from the match list instead of being rendered. + - `RouteMatch.globalNotFound` has been renamed and privatized to the internal `_notFound` field. Use `match.status === 'notFound'` instead. + - The exported React, Solid, and Vue `Match` components now accept `routeId` instead of `matchId`. + - The exported `RouterStores` adapter contract now uses route-keyed presentation stores: `matchesId` is replaced by `ids`, `matchStores` by `byRoute`, and `getRouteMatchStore()` by `getMatchStore()`. The separate `loadedAt`, `isLoading`, `isTransitioning`, `statusCode`, and `redirect` stores have been removed, along with the pending/cache stores and their setters. `StoreConfig.init` has also been removed. Read application-facing state from `router.state`; preload and cache coordination are now internal. + - Removed `RouterCore` members `getMatch()`, `updateMatch()`, `cancelMatch()`, and `cancelMatches()` — read matches from `router.state.matches` (e.g. `router.state.matches.find((m) => m.id === id)`); there is no replacement for mutating or cancelling an individual in-flight match from outside the router. + - Removed `RouterCore.hasNotFoundMatch()` — use `router.state.matches.some((m) => m.status === 'notFound')`. + - Removed `RouterCore.looseRoutesById` — use `routesById`. + - Removed `RouterCore.isPrerendering()`, `RouterCore.isViewTransitionTypesSupported`, and `RouterCore.viewTransitionPromise`, with no replacement. + - Removed `RouterCore.getParsedLocationHref()` and `RouterCore.clearExpiredCache()`, with no replacement — expired cache entries are now reconciled automatically as part of match commit. + - Removed `RouterCore.latestLoadPromise` and `RouterCore.beforeLoad()`, with no replacement. + - `RouterCore.commitLocationPromise` and `RouterCore.pendingBuiltLocation` have been replaced by the internal `_commitPromise` and `_pendingLocation` fields. + - Removed the exported `GetMatchFn` and `UpdateMatchFn` types, along with the methods they typed. + - Removed the standalone `getMatchedRoutes()` export from `@tanstack/router-core` — use the `router.getMatchedRoutes()` instance method instead. + - `RouterCore.loadRouteChunk()` no longer accepts an array of component types as its second argument. One-argument usage is unchanged; the optional second argument is now `'errorComponent'`, `'notFoundComponent'`, or `false` for internal boundary loading. + - Removed `Redirect.redirectHandled`, which was internal redirect bookkeeping. + - `MatchRoutesOpts.preload` and `MatchRoutesOpts.dest` have been removed. + - `StartTransitionFn` is now `(fn, expected) => Promise` (previously `(fn) => void`). This only affects custom framework adapters that implement `startTransition`. + ## 1.171.15 ### Patch Changes diff --git a/packages/router-core/package.json b/packages/router-core/package.json index e26d450010..a146135251 100644 --- a/packages/router-core/package.json +++ b/packages/router-core/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-core", - "version": "1.171.15", + "version": "1.171.16", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-devtools-core/CHANGELOG.md b/packages/router-devtools-core/CHANGELOG.md index 485adc437f..be1a138356 100644 --- a/packages/router-devtools-core/CHANGELOG.md +++ b/packages/router-devtools-core/CHANGELOG.md @@ -1,5 +1,38 @@ # @tanstack/router-devtools-core +## 1.168.1 + +### Patch Changes + +- [#7805](https://github.com/TanStack/router/pull/7805) [`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd) - Rewrite match loading around a lane-based scheduler that tracks each navigation, preload, and background reload as an ordered unit of work. This fixes pending/redirect/retry state leaking between overlapping navigations, restores correct SSR status codes for redirects, errors, and not-found responses, and closes hydration gaps where the client re-ran work the server had already completed. + - Invalidation now retires matching active preloads so older speculative loader results cannot become fresh cache data after invalidation. + - Route `headers()` now only runs on the server, matching the documented behavior — it is no longer invoked during client-side asset projection. + - The documented default `gcTime` and `preloadGcTime` now match the existing runtime default of 5 minutes (`300_000`). + + **Removed / changed exported internals** + - `RouterState` no longer includes `loadedAt`, `isTransitioning`, `statusCode`, or `redirect`. Use `match.updatedAt` in place of `loadedAt`; subscribe to `router.state.status` / `router.state.isLoading` in place of `isTransitioning`; server response status and redirect handling are now internal to the server loader and are no longer exposed on `router.state`. + - `RouteMatch.fetchCount` has been removed, with no replacement — it was purely informational. + - `RouteMatch.status` no longer includes `'redirected'` (it remains `'pending' | 'success' | 'error' | 'notFound'`) — redirected matches are dropped from the match list instead of being rendered. + - `RouteMatch.globalNotFound` has been renamed and privatized to the internal `_notFound` field. Use `match.status === 'notFound'` instead. + - The exported React, Solid, and Vue `Match` components now accept `routeId` instead of `matchId`. + - The exported `RouterStores` adapter contract now uses route-keyed presentation stores: `matchesId` is replaced by `ids`, `matchStores` by `byRoute`, and `getRouteMatchStore()` by `getMatchStore()`. The separate `loadedAt`, `isLoading`, `isTransitioning`, `statusCode`, and `redirect` stores have been removed, along with the pending/cache stores and their setters. `StoreConfig.init` has also been removed. Read application-facing state from `router.state`; preload and cache coordination are now internal. + - Removed `RouterCore` members `getMatch()`, `updateMatch()`, `cancelMatch()`, and `cancelMatches()` — read matches from `router.state.matches` (e.g. `router.state.matches.find((m) => m.id === id)`); there is no replacement for mutating or cancelling an individual in-flight match from outside the router. + - Removed `RouterCore.hasNotFoundMatch()` — use `router.state.matches.some((m) => m.status === 'notFound')`. + - Removed `RouterCore.looseRoutesById` — use `routesById`. + - Removed `RouterCore.isPrerendering()`, `RouterCore.isViewTransitionTypesSupported`, and `RouterCore.viewTransitionPromise`, with no replacement. + - Removed `RouterCore.getParsedLocationHref()` and `RouterCore.clearExpiredCache()`, with no replacement — expired cache entries are now reconciled automatically as part of match commit. + - Removed `RouterCore.latestLoadPromise` and `RouterCore.beforeLoad()`, with no replacement. + - `RouterCore.commitLocationPromise` and `RouterCore.pendingBuiltLocation` have been replaced by the internal `_commitPromise` and `_pendingLocation` fields. + - Removed the exported `GetMatchFn` and `UpdateMatchFn` types, along with the methods they typed. + - Removed the standalone `getMatchedRoutes()` export from `@tanstack/router-core` — use the `router.getMatchedRoutes()` instance method instead. + - `RouterCore.loadRouteChunk()` no longer accepts an array of component types as its second argument. One-argument usage is unchanged; the optional second argument is now `'errorComponent'`, `'notFoundComponent'`, or `false` for internal boundary loading. + - Removed `Redirect.redirectHandled`, which was internal redirect bookkeeping. + - `MatchRoutesOpts.preload` and `MatchRoutesOpts.dest` have been removed. + - `StartTransitionFn` is now `(fn, expected) => Promise` (previously `(fn) => void`). This only affects custom framework adapters that implement `startTransition`. + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/router-core@1.171.16 + ## 1.168.0 ### Minor Changes diff --git a/packages/router-devtools-core/package.json b/packages/router-devtools-core/package.json index 043626815e..89c5f6217c 100644 --- a/packages/router-devtools-core/package.json +++ b/packages/router-devtools-core/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-devtools-core", - "version": "1.168.0", + "version": "1.168.1", "description": "Modern and scalable routing for Web applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-devtools/CHANGELOG.md b/packages/router-devtools/CHANGELOG.md index 38a9a2be35..445e7578dc 100644 --- a/packages/router-devtools/CHANGELOG.md +++ b/packages/router-devtools/CHANGELOG.md @@ -1,5 +1,13 @@ # @tanstack/router-devtools +## 1.167.1 + +### Patch Changes + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/react-router@1.170.19 + - @tanstack/react-router-devtools@1.167.1 + ## 1.167.0 ### Minor Changes diff --git a/packages/router-devtools/package.json b/packages/router-devtools/package.json index 4eca6e1af6..484b722638 100644 --- a/packages/router-devtools/package.json +++ b/packages/router-devtools/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-devtools", - "version": "1.167.0", + "version": "1.167.1", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-generator/CHANGELOG.md b/packages/router-generator/CHANGELOG.md index 64189e0fe8..fd9dc5dcea 100644 --- a/packages/router-generator/CHANGELOG.md +++ b/packages/router-generator/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/router-generator +## 1.167.22 + +### Patch Changes + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/router-core@1.171.16 + ## 1.167.21 ### Patch Changes diff --git a/packages/router-generator/package.json b/packages/router-generator/package.json index 29edf0d150..492c8cc726 100644 --- a/packages/router-generator/package.json +++ b/packages/router-generator/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-generator", - "version": "1.167.21", + "version": "1.167.22", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-plugin/CHANGELOG.md b/packages/router-plugin/CHANGELOG.md index ba440a0ff0..43463e919a 100644 --- a/packages/router-plugin/CHANGELOG.md +++ b/packages/router-plugin/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/router-plugin +## 1.168.24 + +### Patch Changes + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/router-core@1.171.16 + - @tanstack/react-router@1.170.19 + - @tanstack/router-generator@1.167.22 + ## 1.168.23 ### Patch Changes diff --git a/packages/router-plugin/package.json b/packages/router-plugin/package.json index c92015ef18..3f98c7d0c4 100644 --- a/packages/router-plugin/package.json +++ b/packages/router-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-plugin", - "version": "1.168.23", + "version": "1.168.24", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/router-vite-plugin/CHANGELOG.md b/packages/router-vite-plugin/CHANGELOG.md index 2cbfc55ef4..a8abbec3f2 100644 --- a/packages/router-vite-plugin/CHANGELOG.md +++ b/packages/router-vite-plugin/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/router-vite-plugin +## 1.167.24 + +### Patch Changes + +- Updated dependencies []: + - @tanstack/router-plugin@1.168.24 + ## 1.167.23 ### Patch Changes diff --git a/packages/router-vite-plugin/package.json b/packages/router-vite-plugin/package.json index 8f8b07b4e2..f80dd4d53e 100644 --- a/packages/router-vite-plugin/package.json +++ b/packages/router-vite-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/router-vite-plugin", - "version": "1.167.23", + "version": "1.167.24", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/solid-router-devtools/CHANGELOG.md b/packages/solid-router-devtools/CHANGELOG.md index 0a762daf5e..505ab5b756 100644 --- a/packages/solid-router-devtools/CHANGELOG.md +++ b/packages/solid-router-devtools/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/solid-router-devtools +## 1.167.1 + +### Patch Changes + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/router-core@1.171.16 + - @tanstack/solid-router@1.170.19 + - @tanstack/router-devtools-core@1.168.1 + ## 1.167.0 ### Minor Changes diff --git a/packages/solid-router-devtools/package.json b/packages/solid-router-devtools/package.json index 5d3ea31c0c..dbc61dc56d 100644 --- a/packages/solid-router-devtools/package.json +++ b/packages/solid-router-devtools/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-router-devtools", - "version": "1.167.0", + "version": "1.167.1", "description": "Modern and scalable routing for Solid applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/solid-router/CHANGELOG.md b/packages/solid-router/CHANGELOG.md index e63a5a7f25..1205585437 100644 --- a/packages/solid-router/CHANGELOG.md +++ b/packages/solid-router/CHANGELOG.md @@ -1,5 +1,38 @@ # @tanstack/solid-router +## 1.170.19 + +### Patch Changes + +- [#7805](https://github.com/TanStack/router/pull/7805) [`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd) - Rewrite match loading around a lane-based scheduler that tracks each navigation, preload, and background reload as an ordered unit of work. This fixes pending/redirect/retry state leaking between overlapping navigations, restores correct SSR status codes for redirects, errors, and not-found responses, and closes hydration gaps where the client re-ran work the server had already completed. + - Invalidation now retires matching active preloads so older speculative loader results cannot become fresh cache data after invalidation. + - Route `headers()` now only runs on the server, matching the documented behavior — it is no longer invoked during client-side asset projection. + - The documented default `gcTime` and `preloadGcTime` now match the existing runtime default of 5 minutes (`300_000`). + + **Removed / changed exported internals** + - `RouterState` no longer includes `loadedAt`, `isTransitioning`, `statusCode`, or `redirect`. Use `match.updatedAt` in place of `loadedAt`; subscribe to `router.state.status` / `router.state.isLoading` in place of `isTransitioning`; server response status and redirect handling are now internal to the server loader and are no longer exposed on `router.state`. + - `RouteMatch.fetchCount` has been removed, with no replacement — it was purely informational. + - `RouteMatch.status` no longer includes `'redirected'` (it remains `'pending' | 'success' | 'error' | 'notFound'`) — redirected matches are dropped from the match list instead of being rendered. + - `RouteMatch.globalNotFound` has been renamed and privatized to the internal `_notFound` field. Use `match.status === 'notFound'` instead. + - The exported React, Solid, and Vue `Match` components now accept `routeId` instead of `matchId`. + - The exported `RouterStores` adapter contract now uses route-keyed presentation stores: `matchesId` is replaced by `ids`, `matchStores` by `byRoute`, and `getRouteMatchStore()` by `getMatchStore()`. The separate `loadedAt`, `isLoading`, `isTransitioning`, `statusCode`, and `redirect` stores have been removed, along with the pending/cache stores and their setters. `StoreConfig.init` has also been removed. Read application-facing state from `router.state`; preload and cache coordination are now internal. + - Removed `RouterCore` members `getMatch()`, `updateMatch()`, `cancelMatch()`, and `cancelMatches()` — read matches from `router.state.matches` (e.g. `router.state.matches.find((m) => m.id === id)`); there is no replacement for mutating or cancelling an individual in-flight match from outside the router. + - Removed `RouterCore.hasNotFoundMatch()` — use `router.state.matches.some((m) => m.status === 'notFound')`. + - Removed `RouterCore.looseRoutesById` — use `routesById`. + - Removed `RouterCore.isPrerendering()`, `RouterCore.isViewTransitionTypesSupported`, and `RouterCore.viewTransitionPromise`, with no replacement. + - Removed `RouterCore.getParsedLocationHref()` and `RouterCore.clearExpiredCache()`, with no replacement — expired cache entries are now reconciled automatically as part of match commit. + - Removed `RouterCore.latestLoadPromise` and `RouterCore.beforeLoad()`, with no replacement. + - `RouterCore.commitLocationPromise` and `RouterCore.pendingBuiltLocation` have been replaced by the internal `_commitPromise` and `_pendingLocation` fields. + - Removed the exported `GetMatchFn` and `UpdateMatchFn` types, along with the methods they typed. + - Removed the standalone `getMatchedRoutes()` export from `@tanstack/router-core` — use the `router.getMatchedRoutes()` instance method instead. + - `RouterCore.loadRouteChunk()` no longer accepts an array of component types as its second argument. One-argument usage is unchanged; the optional second argument is now `'errorComponent'`, `'notFoundComponent'`, or `false` for internal boundary loading. + - Removed `Redirect.redirectHandled`, which was internal redirect bookkeeping. + - `MatchRoutesOpts.preload` and `MatchRoutesOpts.dest` have been removed. + - `StartTransitionFn` is now `(fn, expected) => Promise` (previously `(fn) => void`). This only affects custom framework adapters that implement `startTransition`. + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/router-core@1.171.16 + ## 1.170.18 ### Patch Changes diff --git a/packages/solid-router/package.json b/packages/solid-router/package.json index 33096663bd..60f7bae7b0 100644 --- a/packages/solid-router/package.json +++ b/packages/solid-router/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-router", - "version": "1.170.18", + "version": "1.170.19", "description": "Modern and scalable routing for Solid applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/solid-start-client/CHANGELOG.md b/packages/solid-start-client/CHANGELOG.md index 7c8f57cdc3..25430e0f23 100644 --- a/packages/solid-start-client/CHANGELOG.md +++ b/packages/solid-start-client/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/solid-start-client +## 1.168.17 + +### Patch Changes + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/router-core@1.171.16 + - @tanstack/solid-router@1.170.19 + - @tanstack/start-client-core@1.170.15 + ## 1.168.16 ### Patch Changes diff --git a/packages/solid-start-client/package.json b/packages/solid-start-client/package.json index f6002b67a3..de38049609 100644 --- a/packages/solid-start-client/package.json +++ b/packages/solid-start-client/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-start-client", - "version": "1.168.16", + "version": "1.168.17", "description": "Modern and scalable routing for Solid applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/solid-start-server/CHANGELOG.md b/packages/solid-start-server/CHANGELOG.md index 25263c1444..75f9cb3593 100644 --- a/packages/solid-start-server/CHANGELOG.md +++ b/packages/solid-start-server/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/solid-start-server +## 1.167.24 + +### Patch Changes + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/router-core@1.171.16 + - @tanstack/solid-router@1.170.19 + - @tanstack/start-server-core@1.169.19 + ## 1.167.23 ### Patch Changes diff --git a/packages/solid-start-server/package.json b/packages/solid-start-server/package.json index 8633af6d11..ccd2adbfc6 100644 --- a/packages/solid-start-server/package.json +++ b/packages/solid-start-server/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-start-server", - "version": "1.167.23", + "version": "1.167.24", "description": "Modern and scalable routing for Solid applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/solid-start/CHANGELOG.md b/packages/solid-start/CHANGELOG.md index a0d11f2e8e..640514226a 100644 --- a/packages/solid-start/CHANGELOG.md +++ b/packages/solid-start/CHANGELOG.md @@ -1,5 +1,17 @@ # @tanstack/solid-start +## 1.168.35 + +### Patch Changes + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/solid-router@1.170.19 + - @tanstack/solid-start-client@1.168.17 + - @tanstack/solid-start-server@1.167.24 + - @tanstack/start-client-core@1.170.15 + - @tanstack/start-plugin-core@1.171.27 + - @tanstack/start-server-core@1.169.19 + ## 1.168.34 ### Patch Changes diff --git a/packages/solid-start/package.json b/packages/solid-start/package.json index 207712cddd..6d4e41d293 100644 --- a/packages/solid-start/package.json +++ b/packages/solid-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/solid-start", - "version": "1.168.34", + "version": "1.168.35", "description": "Modern and scalable routing for Solid applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-client-core/CHANGELOG.md b/packages/start-client-core/CHANGELOG.md index 11bb9192d3..9c4a205bac 100644 --- a/packages/start-client-core/CHANGELOG.md +++ b/packages/start-client-core/CHANGELOG.md @@ -1,5 +1,13 @@ # @tanstack/start-client-core +## 1.170.15 + +### Patch Changes + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/router-core@1.171.16 + - @tanstack/start-storage-context@1.167.18 + ## 1.170.14 ### Patch Changes diff --git a/packages/start-client-core/package.json b/packages/start-client-core/package.json index 6a2eb9eb45..fe83f213fd 100644 --- a/packages/start-client-core/package.json +++ b/packages/start-client-core/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-client-core", - "version": "1.170.14", + "version": "1.170.15", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-plugin-core/CHANGELOG.md b/packages/start-plugin-core/CHANGELOG.md index 5f83e305cd..b7b0ff96da 100644 --- a/packages/start-plugin-core/CHANGELOG.md +++ b/packages/start-plugin-core/CHANGELOG.md @@ -1,5 +1,15 @@ # @tanstack/start-plugin-core +## 1.171.27 + +### Patch Changes + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/router-core@1.171.16 + - @tanstack/router-generator@1.167.22 + - @tanstack/router-plugin@1.168.24 + - @tanstack/start-server-core@1.169.19 + ## 1.171.26 ### Patch Changes diff --git a/packages/start-plugin-core/package.json b/packages/start-plugin-core/package.json index 494a5eb900..3aa6a3ef9a 100644 --- a/packages/start-plugin-core/package.json +++ b/packages/start-plugin-core/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-plugin-core", - "version": "1.171.26", + "version": "1.171.27", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-server-core/CHANGELOG.md b/packages/start-server-core/CHANGELOG.md index aabe68f0e9..f7869e4737 100644 --- a/packages/start-server-core/CHANGELOG.md +++ b/packages/start-server-core/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/start-server-core +## 1.169.19 + +### Patch Changes + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/router-core@1.171.16 + - @tanstack/start-client-core@1.170.15 + - @tanstack/start-storage-context@1.167.18 + ## 1.169.18 ### Patch Changes diff --git a/packages/start-server-core/package.json b/packages/start-server-core/package.json index ebadb3c162..ac8787c47a 100644 --- a/packages/start-server-core/package.json +++ b/packages/start-server-core/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-server-core", - "version": "1.169.18", + "version": "1.169.19", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-static-server-functions/CHANGELOG.md b/packages/start-static-server-functions/CHANGELOG.md index 27584fb74f..6118173acf 100644 --- a/packages/start-static-server-functions/CHANGELOG.md +++ b/packages/start-static-server-functions/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/start-static-server-functions +## 1.167.20 + +### Patch Changes + +- Updated dependencies []: + - @tanstack/start-client-core@1.170.15 + - @tanstack/react-start@1.168.36 + - @tanstack/solid-start@1.168.35 + ## 1.167.19 ### Patch Changes diff --git a/packages/start-static-server-functions/package.json b/packages/start-static-server-functions/package.json index 435340fab6..c828bd7a19 100644 --- a/packages/start-static-server-functions/package.json +++ b/packages/start-static-server-functions/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-static-server-functions", - "version": "1.167.19", + "version": "1.167.20", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/start-storage-context/CHANGELOG.md b/packages/start-storage-context/CHANGELOG.md index d569880484..da8b12f4d4 100644 --- a/packages/start-storage-context/CHANGELOG.md +++ b/packages/start-storage-context/CHANGELOG.md @@ -1,5 +1,12 @@ # @tanstack/start-storage-context +## 1.167.18 + +### Patch Changes + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/router-core@1.171.16 + ## 1.167.17 ### Patch Changes diff --git a/packages/start-storage-context/package.json b/packages/start-storage-context/package.json index 5316edebdc..31c243b74b 100644 --- a/packages/start-storage-context/package.json +++ b/packages/start-storage-context/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/start-storage-context", - "version": "1.167.17", + "version": "1.167.18", "description": "Modern and scalable routing for React applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/vue-router-devtools/CHANGELOG.md b/packages/vue-router-devtools/CHANGELOG.md index 2c444eeaa3..4e20f51a13 100644 --- a/packages/vue-router-devtools/CHANGELOG.md +++ b/packages/vue-router-devtools/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/vue-router-devtools +## 1.167.1 + +### Patch Changes + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/router-core@1.171.16 + - @tanstack/vue-router@1.170.18 + - @tanstack/router-devtools-core@1.168.1 + ## 1.167.0 ### Minor Changes diff --git a/packages/vue-router-devtools/package.json b/packages/vue-router-devtools/package.json index 6b896e8a00..29f2adabdf 100644 --- a/packages/vue-router-devtools/package.json +++ b/packages/vue-router-devtools/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/vue-router-devtools", - "version": "1.167.0", + "version": "1.167.1", "description": "Modern and scalable routing for Vue applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/vue-router/CHANGELOG.md b/packages/vue-router/CHANGELOG.md index 1638058127..a15a0a7eaa 100644 --- a/packages/vue-router/CHANGELOG.md +++ b/packages/vue-router/CHANGELOG.md @@ -1,5 +1,38 @@ # @tanstack/vue-router +## 1.170.18 + +### Patch Changes + +- [#7805](https://github.com/TanStack/router/pull/7805) [`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd) - Rewrite match loading around a lane-based scheduler that tracks each navigation, preload, and background reload as an ordered unit of work. This fixes pending/redirect/retry state leaking between overlapping navigations, restores correct SSR status codes for redirects, errors, and not-found responses, and closes hydration gaps where the client re-ran work the server had already completed. + - Invalidation now retires matching active preloads so older speculative loader results cannot become fresh cache data after invalidation. + - Route `headers()` now only runs on the server, matching the documented behavior — it is no longer invoked during client-side asset projection. + - The documented default `gcTime` and `preloadGcTime` now match the existing runtime default of 5 minutes (`300_000`). + + **Removed / changed exported internals** + - `RouterState` no longer includes `loadedAt`, `isTransitioning`, `statusCode`, or `redirect`. Use `match.updatedAt` in place of `loadedAt`; subscribe to `router.state.status` / `router.state.isLoading` in place of `isTransitioning`; server response status and redirect handling are now internal to the server loader and are no longer exposed on `router.state`. + - `RouteMatch.fetchCount` has been removed, with no replacement — it was purely informational. + - `RouteMatch.status` no longer includes `'redirected'` (it remains `'pending' | 'success' | 'error' | 'notFound'`) — redirected matches are dropped from the match list instead of being rendered. + - `RouteMatch.globalNotFound` has been renamed and privatized to the internal `_notFound` field. Use `match.status === 'notFound'` instead. + - The exported React, Solid, and Vue `Match` components now accept `routeId` instead of `matchId`. + - The exported `RouterStores` adapter contract now uses route-keyed presentation stores: `matchesId` is replaced by `ids`, `matchStores` by `byRoute`, and `getRouteMatchStore()` by `getMatchStore()`. The separate `loadedAt`, `isLoading`, `isTransitioning`, `statusCode`, and `redirect` stores have been removed, along with the pending/cache stores and their setters. `StoreConfig.init` has also been removed. Read application-facing state from `router.state`; preload and cache coordination are now internal. + - Removed `RouterCore` members `getMatch()`, `updateMatch()`, `cancelMatch()`, and `cancelMatches()` — read matches from `router.state.matches` (e.g. `router.state.matches.find((m) => m.id === id)`); there is no replacement for mutating or cancelling an individual in-flight match from outside the router. + - Removed `RouterCore.hasNotFoundMatch()` — use `router.state.matches.some((m) => m.status === 'notFound')`. + - Removed `RouterCore.looseRoutesById` — use `routesById`. + - Removed `RouterCore.isPrerendering()`, `RouterCore.isViewTransitionTypesSupported`, and `RouterCore.viewTransitionPromise`, with no replacement. + - Removed `RouterCore.getParsedLocationHref()` and `RouterCore.clearExpiredCache()`, with no replacement — expired cache entries are now reconciled automatically as part of match commit. + - Removed `RouterCore.latestLoadPromise` and `RouterCore.beforeLoad()`, with no replacement. + - `RouterCore.commitLocationPromise` and `RouterCore.pendingBuiltLocation` have been replaced by the internal `_commitPromise` and `_pendingLocation` fields. + - Removed the exported `GetMatchFn` and `UpdateMatchFn` types, along with the methods they typed. + - Removed the standalone `getMatchedRoutes()` export from `@tanstack/router-core` — use the `router.getMatchedRoutes()` instance method instead. + - `RouterCore.loadRouteChunk()` no longer accepts an array of component types as its second argument. One-argument usage is unchanged; the optional second argument is now `'errorComponent'`, `'notFoundComponent'`, or `false` for internal boundary loading. + - Removed `Redirect.redirectHandled`, which was internal redirect bookkeeping. + - `MatchRoutesOpts.preload` and `MatchRoutesOpts.dest` have been removed. + - `StartTransitionFn` is now `(fn, expected) => Promise` (previously `(fn) => void`). This only affects custom framework adapters that implement `startTransition`. + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/router-core@1.171.16 + ## 1.170.17 ### Patch Changes diff --git a/packages/vue-router/package.json b/packages/vue-router/package.json index 96dc3fde40..5d61f81e72 100644 --- a/packages/vue-router/package.json +++ b/packages/vue-router/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/vue-router", - "version": "1.170.17", + "version": "1.170.18", "description": "Modern and scalable routing for Vue applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/vue-start-client/CHANGELOG.md b/packages/vue-start-client/CHANGELOG.md index 88d3855bbf..3b51c790ae 100644 --- a/packages/vue-start-client/CHANGELOG.md +++ b/packages/vue-start-client/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/vue-start-client +## 1.167.20 + +### Patch Changes + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/router-core@1.171.16 + - @tanstack/vue-router@1.170.18 + - @tanstack/start-client-core@1.170.15 + ## 1.167.19 ### Patch Changes diff --git a/packages/vue-start-client/package.json b/packages/vue-start-client/package.json index 64641c9502..452516147e 100644 --- a/packages/vue-start-client/package.json +++ b/packages/vue-start-client/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/vue-start-client", - "version": "1.167.19", + "version": "1.167.20", "description": "Modern and scalable routing for Vue applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/vue-start-server/CHANGELOG.md b/packages/vue-start-server/CHANGELOG.md index 22ae2bcba3..24a5927481 100644 --- a/packages/vue-start-server/CHANGELOG.md +++ b/packages/vue-start-server/CHANGELOG.md @@ -1,5 +1,14 @@ # @tanstack/vue-start-server +## 1.167.24 + +### Patch Changes + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/router-core@1.171.16 + - @tanstack/vue-router@1.170.18 + - @tanstack/start-server-core@1.169.19 + ## 1.167.23 ### Patch Changes diff --git a/packages/vue-start-server/package.json b/packages/vue-start-server/package.json index 527061f4cc..cd1b816336 100644 --- a/packages/vue-start-server/package.json +++ b/packages/vue-start-server/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/vue-start-server", - "version": "1.167.23", + "version": "1.167.24", "description": "Modern and scalable routing for Vue applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/packages/vue-start/CHANGELOG.md b/packages/vue-start/CHANGELOG.md index dbb185b0c6..0189e65e4e 100644 --- a/packages/vue-start/CHANGELOG.md +++ b/packages/vue-start/CHANGELOG.md @@ -1,5 +1,17 @@ # @tanstack/vue-start +## 1.168.34 + +### Patch Changes + +- Updated dependencies [[`45c4ad8`](https://github.com/TanStack/router/commit/45c4ad8d629e291fab70c37900525449e415ffcd)]: + - @tanstack/vue-router@1.170.18 + - @tanstack/start-client-core@1.170.15 + - @tanstack/start-plugin-core@1.171.27 + - @tanstack/start-server-core@1.169.19 + - @tanstack/vue-start-client@1.167.20 + - @tanstack/vue-start-server@1.167.24 + ## 1.168.33 ### Patch Changes diff --git a/packages/vue-start/package.json b/packages/vue-start/package.json index 87fb9b22ca..260004a338 100644 --- a/packages/vue-start/package.json +++ b/packages/vue-start/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack/vue-start", - "version": "1.168.33", + "version": "1.168.34", "description": "Modern and scalable routing for Vue applications", "author": "Tanner Linsley", "license": "MIT", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dc45720175..7d02e7e862 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -78,7 +78,7 @@ importers: version: 1.26.2(@typescript/typescript6@6.0.2)(eslint@9.22.0(jiti@2.7.0))(ts-api-utils@2.4.0(@typescript/typescript6@6.0.2)) '@nx/devkit': specifier: 22.7.5 - version: 22.7.5(nx@22.7.5(@swc-node/register@1.11.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@swc/core@1.15.33(@swc/helpers@0.5.23))(@swc/types@0.1.26)(@typescript/typescript6@6.0.2))(@swc/core@1.15.33(@swc/helpers@0.5.23))(debug@4.4.3)) + version: 22.7.5(nx@22.7.5(@swc-node/register@1.11.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@swc/core@1.15.33(@swc/helpers@0.5.23))(@swc/types@0.1.26)(@typescript/typescript6@6.0.2))(@swc/core@1.15.33(@swc/helpers@0.5.23))(debug@4.4.3)) '@playwright/test': specifier: ^1.61.0 version: 1.61.1 @@ -87,7 +87,7 @@ importers: version: 1.14.1(@swc/core@1.15.33(@swc/helpers@0.5.23))(@swc/types@0.1.26) '@swc-node/register': specifier: ^1.11.1 - version: 1.11.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@swc/core@1.15.33(@swc/helpers@0.5.23))(@swc/types@0.1.26)(@typescript/typescript6@6.0.2) + version: 1.11.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@swc/core@1.15.33(@swc/helpers@0.5.23))(@swc/types@0.1.26)(@typescript/typescript6@6.0.2) '@tanstack/eslint-config': specifier: 0.4.0 version: 0.4.0(@typescript-eslint/utils@8.57.1(@typescript/typescript6@6.0.2)(eslint@9.22.0(jiti@2.7.0)))(@typescript/typescript6@6.0.2)(eslint@9.22.0(jiti@2.7.0)) @@ -141,7 +141,7 @@ importers: version: 4.0.3 nx: specifier: 22.7.5 - version: 22.7.5(@swc-node/register@1.11.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@swc/core@1.15.33(@swc/helpers@0.5.23))(@swc/types@0.1.26)(@typescript/typescript6@6.0.2))(@swc/core@1.15.33(@swc/helpers@0.5.23))(debug@4.4.3) + version: 22.7.5(@swc-node/register@1.11.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@swc/core@1.15.33(@swc/helpers@0.5.23))(@swc/types@0.1.26)(@typescript/typescript6@6.0.2))(@swc/core@1.15.33(@swc/helpers@0.5.23))(debug@4.4.3) prettier: specifier: ^3.8.0 version: 3.8.1 @@ -1169,6 +1169,77 @@ importers: specifier: ^8.0.14 version: 8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0) + e2e/react-router/issue-7120: + dependencies: + '@tanstack/react-router': + specifier: workspace:* + version: link:../../../packages/react-router + '@tanstack/router-plugin': + specifier: workspace:* + version: link:../../../packages/router-plugin + react: + specifier: ^19.2.3 + version: 19.2.3 + react-dom: + specifier: ^19.2.3 + version: 19.2.3(react@19.2.3) + devDependencies: + '@playwright/test': + specifier: ^1.61.0 + version: 1.61.1 + '@tanstack/router-e2e-utils': + specifier: workspace:^ + version: link:../../e2e-utils + '@types/react': + specifier: ^19.2.8 + version: 19.2.9 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.9) + '@vitejs/plugin-react': + specifier: ^6.0.1 + version: 6.0.1(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) + vite: + specifier: ^8.0.14 + version: 8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0) + + e2e/react-router/issue-7457: + dependencies: + '@tanstack/react-query': + specifier: ^5.99.0 + version: 5.99.0(react@19.2.3) + '@tanstack/react-router': + specifier: workspace:* + version: link:../../../packages/react-router + '@tanstack/router-plugin': + specifier: workspace:* + version: link:../../../packages/router-plugin + react: + specifier: ^19.2.3 + version: 19.2.3 + react-dom: + specifier: ^19.2.3 + version: 19.2.3(react@19.2.3) + devDependencies: + '@playwright/test': + specifier: ^1.61.0 + version: 1.61.1 + '@tanstack/router-e2e-utils': + specifier: workspace:^ + version: link:../../e2e-utils + '@types/react': + specifier: ^19.2.8 + version: 19.2.9 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.9) + '@vitejs/plugin-react': + specifier: ^6.0.1 + version: 6.0.1(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) + vite: + specifier: ^8.0.14 + version: 8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0) + e2e/react-router/js-only-file-based: dependencies: '@tailwindcss/vite': @@ -1457,77 +1528,6 @@ importers: specifier: ^8.0.14 version: 8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0) - e2e/react-router/issue-7120: - dependencies: - '@tanstack/react-router': - specifier: workspace:* - version: link:../../../packages/react-router - '@tanstack/router-plugin': - specifier: workspace:* - version: link:../../../packages/router-plugin - react: - specifier: ^19.2.3 - version: 19.2.3 - react-dom: - specifier: ^19.2.3 - version: 19.2.3(react@19.2.3) - devDependencies: - '@playwright/test': - specifier: ^1.61.0 - version: 1.61.1 - '@tanstack/router-e2e-utils': - specifier: workspace:^ - version: link:../../e2e-utils - '@types/react': - specifier: ^19.2.8 - version: 19.2.9 - '@types/react-dom': - specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.9) - '@vitejs/plugin-react': - specifier: ^6.0.1 - version: 6.0.1(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) - vite: - specifier: ^8.0.14 - version: 8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0) - - e2e/react-router/issue-7457: - dependencies: - '@tanstack/react-query': - specifier: ^5.99.0 - version: 5.99.0(react@19.2.3) - '@tanstack/react-router': - specifier: workspace:* - version: link:../../../packages/react-router - '@tanstack/router-plugin': - specifier: workspace:* - version: link:../../../packages/router-plugin - react: - specifier: ^19.2.3 - version: 19.2.3 - react-dom: - specifier: ^19.2.3 - version: 19.2.3(react@19.2.3) - devDependencies: - '@playwright/test': - specifier: ^1.61.0 - version: 1.61.1 - '@tanstack/router-e2e-utils': - specifier: workspace:^ - version: link:../../e2e-utils - '@types/react': - specifier: ^19.2.8 - version: 19.2.9 - '@types/react-dom': - specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.9) - '@vitejs/plugin-react': - specifier: ^6.0.1 - version: 6.0.1(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) - vite: - specifier: ^8.0.14 - version: 8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0) - e2e/react-router/view-transitions: dependencies: '@tailwindcss/vite': @@ -10914,7 +10914,7 @@ importers: specifier: workspace:* version: link:../../../packages/router-plugin '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -10954,7 +10954,7 @@ importers: specifier: workspace:* version: link:../../../packages/router-plugin '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -10997,7 +10997,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11040,7 +11040,7 @@ importers: specifier: ^5.99.0 version: 5.99.0(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11077,7 +11077,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11111,7 +11111,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11151,7 +11151,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11197,7 +11197,7 @@ importers: specifier: ^5.90.0 version: 5.90.4(@tanstack/solid-query@5.99.0(solid-js@1.9.12))(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11240,7 +11240,7 @@ importers: specifier: ^5.90.0 version: 5.90.4(@tanstack/solid-query@5.99.0(solid-js@1.9.12))(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11280,7 +11280,7 @@ importers: specifier: workspace:* version: link:../../../packages/router-plugin '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router compression: specifier: ^1.8.0 @@ -11323,7 +11323,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11381,7 +11381,7 @@ importers: specifier: workspace:* version: link:../../../packages/router-plugin '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11424,7 +11424,7 @@ importers: specifier: workspace:* version: link:../../../packages/router-plugin '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11464,7 +11464,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11504,7 +11504,7 @@ importers: specifier: workspace:* version: link:../../../packages/router-plugin '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router solid-js: specifier: 1.9.12 @@ -11538,7 +11538,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11578,7 +11578,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11627,7 +11627,7 @@ importers: specifier: ^5.90.0 version: 5.90.4(@tanstack/solid-query@5.99.0(solid-js@1.9.12))(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11676,7 +11676,7 @@ importers: specifier: ^5.90.0 version: 5.90.4(@tanstack/solid-query@5.99.0(solid-js@1.9.12))(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11722,7 +11722,7 @@ importers: specifier: ^5.99.0 version: 5.99.0(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11762,7 +11762,7 @@ importers: specifier: ^5.99.0 version: 5.99.0(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11799,7 +11799,7 @@ importers: specifier: ^5.99.0 version: 5.99.0(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11833,7 +11833,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11864,7 +11864,7 @@ importers: specifier: workspace:* version: link:../../../packages/router-plugin '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11895,7 +11895,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11935,7 +11935,7 @@ importers: specifier: ^4.2.2 version: 4.2.2 '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -11972,7 +11972,7 @@ importers: examples/solid/quickstart-webpack-file-based: dependencies: '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12036,7 +12036,7 @@ importers: specifier: workspace:* version: link:../../../packages/router-plugin '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12073,7 +12073,7 @@ importers: specifier: workspace:* version: link:../../../packages/router-plugin '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12116,7 +12116,7 @@ importers: specifier: ^5.90.0 version: 5.90.4(@tanstack/solid-query@5.99.0(solid-js@1.9.12))(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12153,7 +12153,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12196,7 +12196,7 @@ importers: specifier: ^5.99.0 version: 5.99.0(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12245,7 +12245,7 @@ importers: examples/solid/start-basic: dependencies: '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12300,7 +12300,7 @@ importers: specifier: ^7.0.0 version: 7.0.0(@typescript/typescript6@6.0.2)(prisma@7.0.0(@types/react@19.2.9)(@typescript/typescript6@6.0.2)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12352,7 +12352,7 @@ importers: specifier: ^0.41.1 version: 0.41.1 '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12395,7 +12395,7 @@ importers: examples/solid/start-basic-cloudflare: dependencies: '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12438,7 +12438,7 @@ importers: examples/solid/start-basic-netlify: dependencies: '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12478,7 +12478,7 @@ importers: examples/solid/start-basic-nitro: dependencies: '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12524,7 +12524,7 @@ importers: specifier: ^5.90.0 version: 5.90.4(@tanstack/solid-query@5.99.0(solid-js@1.9.12))(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12570,7 +12570,7 @@ importers: examples/solid/start-basic-static: dependencies: '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12625,7 +12625,7 @@ importers: specifier: ^0.7.0 version: 0.7.14(csstype@3.2.3)(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12692,7 +12692,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12750,7 +12750,7 @@ importers: examples/solid/start-counter: dependencies: '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12796,7 +12796,7 @@ importers: specifier: ^0.7.0 version: 0.7.14(csstype@3.2.3)(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12839,7 +12839,7 @@ importers: specifier: ^5.99.0 version: 5.99.0(solid-js@1.9.12) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12885,7 +12885,7 @@ importers: examples/solid/start-streaming-data-from-server-functions: dependencies: '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12925,7 +12925,7 @@ importers: specifier: ^2.48.1 version: 2.48.1 '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -12965,7 +12965,7 @@ importers: examples/solid/start-tailwind-v4: dependencies: '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -13014,7 +13014,7 @@ importers: specifier: workspace:* version: link:../../../packages/router-plugin '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -13051,7 +13051,7 @@ importers: specifier: ^4.2.2 version: 4.2.2(vite@8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0)) '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -13094,7 +13094,7 @@ importers: specifier: workspace:* version: link:../../../packages/router-plugin '@tanstack/solid-router': - specifier: ^1.170.18 + specifier: ^1.170.19 version: link:../../../packages/solid-router '@tanstack/solid-router-devtools': specifier: workspace:^ @@ -22238,6 +22238,7 @@ packages: cron-parser@4.9.0: resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} engines: {node: '>=12.0.0'} + deprecated: v4 is no longer maintained, upgrade to v5 croner@9.1.0: resolution: {integrity: sha512-p9nwwR4qyT5W996vBZhdvBCnMhicY5ytZkR4D1Xj0wuTDEiMnjwR57Q3RXYY/s0EpX6Ay3vgIcfaR+ewGHsi+g==} @@ -30726,6 +30727,13 @@ snapshots: '@tybys/wasm-util': 0.10.2 optional: true + '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.3 + optional: true + '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: '@emnapi/core': 1.11.1 @@ -31094,13 +31102,13 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.0 - '@nx/devkit@22.7.5(nx@22.7.5(@swc-node/register@1.11.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@swc/core@1.15.33(@swc/helpers@0.5.23))(@swc/types@0.1.26)(@typescript/typescript6@6.0.2))(@swc/core@1.15.33(@swc/helpers@0.5.23))(debug@4.4.3))': + '@nx/devkit@22.7.5(nx@22.7.5(@swc-node/register@1.11.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@swc/core@1.15.33(@swc/helpers@0.5.23))(@swc/types@0.1.26)(@typescript/typescript6@6.0.2))(@swc/core@1.15.33(@swc/helpers@0.5.23))(debug@4.4.3))': dependencies: '@zkochan/js-yaml': 0.0.7 ejs: 5.0.1 enquirer: 2.3.6 minimatch: 10.2.5 - nx: 22.7.5(@swc-node/register@1.11.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@swc/core@1.15.33(@swc/helpers@0.5.23))(@swc/types@0.1.26)(@typescript/typescript6@6.0.2))(@swc/core@1.15.33(@swc/helpers@0.5.23))(debug@4.4.3) + nx: 22.7.5(@swc-node/register@1.11.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@swc/core@1.15.33(@swc/helpers@0.5.23))(@swc/types@0.1.26)(@typescript/typescript6@6.0.2))(@swc/core@1.15.33(@swc/helpers@0.5.23))(debug@4.4.3) semver: 7.8.2 tslib: 2.8.1 yargs-parser: 21.1.1 @@ -31282,9 +31290,9 @@ snapshots: '@oxc-resolver/binding-openharmony-arm64@11.19.1': optional: true - '@oxc-resolver/binding-wasm32-wasi@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + '@oxc-resolver/binding-wasm32-wasi@11.19.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -32472,7 +32480,7 @@ snapshots: dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.9': @@ -33253,14 +33261,14 @@ snapshots: '@swc/core': 1.15.33(@swc/helpers@0.5.23) '@swc/types': 0.1.26 - '@swc-node/register@1.11.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@swc/core@1.15.33(@swc/helpers@0.5.23))(@swc/types@0.1.26)(@typescript/typescript6@6.0.2)': + '@swc-node/register@1.11.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@swc/core@1.15.33(@swc/helpers@0.5.23))(@swc/types@0.1.26)(@typescript/typescript6@6.0.2)': dependencies: '@swc-node/core': 1.14.1(@swc/core@1.15.33(@swc/helpers@0.5.23))(@swc/types@0.1.26) '@swc-node/sourcemap-support': 0.6.1 '@swc/core': 1.15.33(@swc/helpers@0.5.23) colorette: 2.0.20 debug: 4.4.3 - oxc-resolver: 11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + oxc-resolver: 11.19.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) pirates: 4.0.7 tslib: 2.8.1 typescript: '@typescript/typescript6@6.0.2' @@ -39472,7 +39480,7 @@ snapshots: nwsapi@2.2.16: {} - nx@22.7.5(@swc-node/register@1.11.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@swc/core@1.15.33(@swc/helpers@0.5.23))(@swc/types@0.1.26)(@typescript/typescript6@6.0.2))(@swc/core@1.15.33(@swc/helpers@0.5.23))(debug@4.4.3): + nx@22.7.5(@swc-node/register@1.11.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@swc/core@1.15.33(@swc/helpers@0.5.23))(@swc/types@0.1.26)(@typescript/typescript6@6.0.2))(@swc/core@1.15.33(@swc/helpers@0.5.23))(debug@4.4.3): dependencies: '@emnapi/core': 1.4.5 '@emnapi/runtime': 1.4.5 @@ -39595,7 +39603,7 @@ snapshots: '@nx/nx-linux-x64-musl': 22.7.5 '@nx/nx-win32-arm64-msvc': 22.7.5 '@nx/nx-win32-x64-msvc': 22.7.5 - '@swc-node/register': 1.11.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@swc/core@1.15.33(@swc/helpers@0.5.23))(@swc/types@0.1.26)(@typescript/typescript6@6.0.2) + '@swc-node/register': 1.11.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@swc/core@1.15.33(@swc/helpers@0.5.23))(@swc/types@0.1.26)(@typescript/typescript6@6.0.2) '@swc/core': 1.15.33(@swc/helpers@0.5.23) transitivePeerDependencies: - debug @@ -39741,7 +39749,7 @@ snapshots: - '@emnapi/core' - '@emnapi/runtime' - oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): + oxc-resolver@11.19.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1): optionalDependencies: '@oxc-resolver/binding-android-arm-eabi': 11.19.1 '@oxc-resolver/binding-android-arm64': 11.19.1 @@ -39759,7 +39767,7 @@ snapshots: '@oxc-resolver/binding-linux-x64-gnu': 11.19.1 '@oxc-resolver/binding-linux-x64-musl': 11.19.1 '@oxc-resolver/binding-openharmony-arm64': 11.19.1 - '@oxc-resolver/binding-wasm32-wasi': 11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@oxc-resolver/binding-wasm32-wasi': 11.19.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) '@oxc-resolver/binding-win32-arm64-msvc': 11.19.1 '@oxc-resolver/binding-win32-ia32-msvc': 11.19.1 '@oxc-resolver/binding-win32-x64-msvc': 11.19.1 @@ -42424,7 +42432,7 @@ snapshots: browserslist: 4.28.1 chrome-trace-event: 1.0.4 enhanced-resolve: 5.21.6 - es-module-lexer: 2.0.0 + es-module-lexer: 2.3.1 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 From 314098e59e656d0701d8bdc13f933c357a61a0e4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2026 22:55:13 +0200 Subject: [PATCH 48/51] ci: Version Packages (#7950) ci: changeset release Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> From e021c7a352a639810c1a8a11a81be19b0cb19160 Mon Sep 17 00:00:00 2001 From: Brenley Dueck Date: Wed, 5 Aug 2026 12:27:01 -0500 Subject: [PATCH 49/51] fix(solid-router): keep Transitioner hydration ids in sync between server and client Solid 2 derives hydration keys from the reactive owner tree, and its server onSettled stub consumes a child-id slot to mirror the client's effect owner. The server early-return in Transitioner skipped that registration, shifting every subsequent hydration key by one and causing a root-level hydration mismatch that discarded the server-rendered HTML (breaking deferred/streaming content and redirects in the solid-start e2e suites). Run the same code on both sides again: the onSettled callback never executes on the server, so only the owner-tree structure is shared. Co-Authored-By: Claude Fable 5 --- packages/solid-router/src/Transitioner.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/solid-router/src/Transitioner.tsx b/packages/solid-router/src/Transitioner.tsx index 4b943402b9..428a6bb10e 100644 --- a/packages/solid-router/src/Transitioner.tsx +++ b/packages/solid-router/src/Transitioner.tsx @@ -1,6 +1,5 @@ import * as Solid from 'solid-js' import { getLocationChangeInfo, trimPathRight } from '@tanstack/router-core' -import { isServer } from '@tanstack/router-core/isServer' import { useRouter } from './useRouter' function getResolvedLocation(router: ReturnType) { @@ -17,10 +16,10 @@ function getResolvedLocation(router: ReturnType) { export function Transitioner() { const router = useRouter() - if (isServer ?? router.isServer) { - return null - } - + // No server early-return here: Solid 2 derives hydration keys from the + // reactive owner tree, so the server must register the same `onSettled` + // slot as the client or every key after this component shifts by one. + // The callback itself never runs on the server. router.startTransition = async (fn) => { const result = Solid.runWithOwner(null, fn) try { From 99756c0710faadf2ee94faaea858d62b3c9f63f2 Mon Sep 17 00:00:00 2001 From: Brenley Dueck Date: Wed, 5 Aug 2026 13:10:36 -0500 Subject: [PATCH 50/51] fix(solid-start): resolve SSR'd lazy() client assets via vite-plugin-solid's manifest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Solid 2's streaming renderer resolves the modules behind server-rendered lazy() boundaries through its `manifest` render option: it emits stylesheet links and modulepreloads for the lazy chunk and serializes the module→asset map the client needs to preload it before hydration. TanStack Start never provided that manifest — its own start manifest is route-keyed and intentionally excludes dynamic-import chunks — so direct visits to routes with SSR'd lazy() components rendered without the lazy chunk's CSS and logged "Asset manifest returned no client assets" on every request. (Pre-merge this was masked: the old Match tree re-rendered the boundary client-side after the failed hydration, fetching the CSS late via Vite's dynamic-import helper.) Wire up vite-plugin-solid's `virtual:solid-manifest` (dev module-graph resolver in dev, the client build's `.vite/manifest.json` in builds), kept entirely inside the solid packages: - solid-router ships an inert `ssr/clientAssetsManifest` stub that the renderers prefer over the route-keyed manifest when non-undefined - solid-start's vite plugin swaps the stub for `virtual:solid-manifest` in the server environment (with `isEntry` neutralized so Solid doesn't duplicate the entry stylesheet Start already emits) and enables `build.manifest` for the client build - bundlers without the plugin (rsbuild) keep the stub and current behavior Fixes the two shared-widget lazy-CSS tests in the solid-start start-manifest e2e (10/10 passing; selective-ssr and basic suites verified unaffected). Co-Authored-By: Claude Fable 5 --- .../src/ssr/clientAssetsManifest.ts | 21 +++++++ .../src/ssr/renderRouterToStream.tsx | 6 +- .../src/ssr/renderRouterToString.tsx | 6 +- packages/solid-start/src/plugin/vite.ts | 61 ++++++++++++++++++- 4 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 packages/solid-router/src/ssr/clientAssetsManifest.ts diff --git a/packages/solid-router/src/ssr/clientAssetsManifest.ts b/packages/solid-router/src/ssr/clientAssetsManifest.ts new file mode 100644 index 0000000000..377da8efa5 --- /dev/null +++ b/packages/solid-router/src/ssr/clientAssetsManifest.ts @@ -0,0 +1,21 @@ +/** + * Client-assets manifest bridge for server-rendered `lazy()` components. + * + * Solid's streaming renderer resolves the modules behind server-rendered + * `lazy()` boundaries to their client JS/CSS assets through the `manifest` + * render option — emitting stylesheet links for lazy chunks and serializing + * the module→asset map the client needs to preload them before hydration. + * + * This module is a stub on purpose: bundler integrations that can answer + * those lookups (e.g. TanStack Start's vite plugin, which swaps this module + * for vite-plugin-solid's `virtual:solid-manifest`) replace it at build + * time. Everywhere else — plain SSR setups, bundlers without an + * integration — it stays `undefined` and rendering falls back to the + * router's own route manifest. + * + * The swap is keyed off this file's path (`ssr/clientAssetsManifest`), so + * renaming or moving it breaks the integration. + */ +const clientAssetsManifest: unknown = undefined + +export default clientAssetsManifest diff --git a/packages/solid-router/src/ssr/renderRouterToStream.tsx b/packages/solid-router/src/ssr/renderRouterToStream.tsx index 36d6e34dd9..5bc303e2e6 100644 --- a/packages/solid-router/src/ssr/renderRouterToStream.tsx +++ b/packages/solid-router/src/ssr/renderRouterToStream.tsx @@ -5,6 +5,7 @@ import { transformReadableStreamWithRouter, } from '@tanstack/router-core/ssr/server' import { makeSsrSerovalPlugin } from '@tanstack/router-core' +import clientAssetsManifest from './clientAssetsManifest' import type { ReadableStream } from 'node:stream/web' import type { AnyRouter } from '@tanstack/router-core' import type { JSX } from '@solidjs/web' @@ -59,7 +60,10 @@ export const renderRouterToStream = async ({ const stream = Solid.renderToStream(() => children, { nonce: router.options.ssr?.nonce, plugins: serovalPlugins, - manifest: manifest ?? router.ssr?.manifest, + // Prefer the bundler-provided client-assets bridge (module-keyed, the + // shape Solid resolves lazy() assets from); the router's route-keyed + // manifest is a last resort that cannot answer lazy module lookups. + manifest: manifest ?? clientAssetsManifest ?? router.ssr?.manifest, } as any) // Solid's `pipeTo(w)` takes a single arg (no signal overload) and locks diff --git a/packages/solid-router/src/ssr/renderRouterToString.tsx b/packages/solid-router/src/ssr/renderRouterToString.tsx index 8d28ba379b..982c4b886d 100644 --- a/packages/solid-router/src/ssr/renderRouterToString.tsx +++ b/packages/solid-router/src/ssr/renderRouterToString.tsx @@ -1,5 +1,6 @@ import * as Solid from '@solidjs/web' import { makeSsrSerovalPlugin } from '@tanstack/router-core' +import clientAssetsManifest from './clientAssetsManifest' import type { AnyRouter } from '@tanstack/router-core' import type { JSX } from '@solidjs/web' @@ -26,7 +27,10 @@ export const renderRouterToString = ({ let html = Solid.renderToString(children, { nonce: router.options.ssr?.nonce, plugins: serovalPlugins, - manifest: manifest ?? router.ssr?.manifest, + // Prefer the bundler-provided client-assets bridge (module-keyed, the + // shape Solid resolves lazy() assets from); the router's route-keyed + // manifest is a last resort that cannot answer lazy module lookups. + manifest: manifest ?? clientAssetsManifest ?? router.ssr?.manifest, } as any) router.serverSsr!.setRenderFinished() diff --git a/packages/solid-start/src/plugin/vite.ts b/packages/solid-start/src/plugin/vite.ts index cb3f57f703..50e03fc721 100644 --- a/packages/solid-start/src/plugin/vite.ts +++ b/packages/solid-start/src/plugin/vite.ts @@ -2,13 +2,25 @@ import { START_ENVIRONMENT_NAMES, tanStackStartVite, } from '@tanstack/start-plugin-core/vite' +import { solidStartDefaultEntryPaths } from './shared' import type { TanStackStartViteInputConfig, TanStackStartVitePluginCoreOptions, } from '@tanstack/start-plugin-core/vite' -import { solidStartDefaultEntryPaths } from './shared' import type { PluginOption } from 'vite' +// vite-plugin-solid's client-assets manifest: resolves the module keys its +// compiler bakes into lazy() calls to client JS/CSS assets — a live resolver +// over the dev module graph in dev, the client build's `.vite/manifest.json` +// in builds. Solid's server renderer consumes either shape natively. +const SOLID_MANIFEST_ID = 'virtual:solid-manifest' + +// @tanstack/solid-router's inert stand-in for that manifest (see the stub's +// doc comment). We swap its content for the real thing in the server +// environment, matched by path across every dist flavor of the package. +const CLIENT_ASSETS_MANIFEST_STUB_RE = + /solid-router\/(?:src|dist\/[^/]+)\/ssr\/clientAssetsManifest\.[cm]?[jt]sx?$/ + export function tanstackStart( options?: TanStackStartViteInputConfig, ): Array { @@ -48,6 +60,12 @@ export function tanstackStart( }, configEnvironment(environmentName, options) { return { + // The build flavor of `virtual:solid-manifest` reads the client + // build's `.vite/manifest.json`, which vite only emits on demand. + build: + environmentName === START_ENVIRONMENT_NAMES.client + ? { manifest: true } + : undefined, optimizeDeps: environmentName === START_ENVIRONMENT_NAMES.client || (environmentName === START_ENVIRONMENT_NAMES.server && @@ -65,6 +83,47 @@ export function tanstackStart( } }, }, + { + name: 'tanstack-solid-start:client-assets-manifest', + async load(id) { + if (this.environment.name !== START_ENVIRONMENT_NAMES.server) { + return undefined + } + + const cleanId = id.split('?')[0]! + if (!CLIENT_ASSETS_MANIFEST_STUB_RE.test(cleanId)) { + return undefined + } + + // Only swap when the installed vite-plugin-solid actually registers + // the virtual module (older versions don't) — otherwise keep the + // inert stub. + const resolved = await this.resolve(SOLID_MANIFEST_ID, id) + if (!resolved) { + return undefined + } + + // TanStack Start already emits entry/route CSS and preloads through + // its own route manifest, so neutralize `isEntry` before handing the + // built manifest to Solid — otherwise its registerEntryAssets pass + // duplicates the entry stylesheet link. Lazy() module lookups are + // unaffected. The dev flavor is a `{ resolve }` bridge and passes + // through untouched (Solid skips entry registration for resolvers). + return `import manifest from '${SOLID_MANIFEST_ID}' +const clientAssetsManifest = + manifest && typeof manifest === 'object' && typeof manifest.resolve !== 'function' + ? Object.fromEntries( + Object.entries(manifest).map(([key, entry]) => + entry && typeof entry === 'object' && entry.isEntry + ? [key, { ...entry, isEntry: false }] + : [key, entry], + ), + ) + : manifest +export default clientAssetsManifest +` + }, + }, tanStackStartVite(corePluginOpts, options), ] } From 5c400bc32e45aa9cae473bcb32fb2b8058996e16 Mon Sep 17 00:00:00 2001 From: Brenley Dueck Date: Wed, 5 Aug 2026 13:12:12 -0500 Subject: [PATCH 51/51] chore(e2e): migrate selective-ssr pending-min route to Solid 2 onSettled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit onMount is gone in Solid 2 — record the pending/target mount events via onSettled instead. Also picks up routeTree.gen.ts regeneration churn. Verified with the selective-ssr e2e suite (11/11 passing). Co-Authored-By: Claude Fable 5 --- .../selective-ssr/src/routeTree.gen.ts | 62 +++++++++---------- .../src/routes/ssr-false-pending-min.tsx | 6 +- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/e2e/solid-start/selective-ssr/src/routeTree.gen.ts b/e2e/solid-start/selective-ssr/src/routeTree.gen.ts index 8fb128de38..d0ea8db3a2 100644 --- a/e2e/solid-start/selective-ssr/src/routeTree.gen.ts +++ b/e2e/solid-start/selective-ssr/src/routeTree.gen.ts @@ -9,20 +9,15 @@ // 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 SsrFalsePendingMinRouteImport } from './routes/ssr-false-pending-min' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as DataOnlyPendingComponentRouteImport } from './routes/data-only-pending-component' import { Route as IndexRouteImport } from './routes/index' +import { Route as DataOnlyPendingComponentRouteImport } from './routes/data-only-pending-component' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as SsrFalsePendingMinRouteImport } from './routes/ssr-false-pending-min' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -const SsrFalsePendingMinRoute = SsrFalsePendingMinRouteImport.update({ - id: '/ssr-false-pending-min', - path: '/ssr-false-pending-min', - getParentRoute: () => rootRouteImport, -} as any) -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', getParentRoute: () => rootRouteImport, } as any) const DataOnlyPendingComponentRoute = @@ -31,9 +26,14 @@ const DataOnlyPendingComponentRoute = path: '/data-only-pending-component', getParentRoute: () => rootRouteImport, } as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', + getParentRoute: () => rootRouteImport, +} as any) +const SsrFalsePendingMinRoute = SsrFalsePendingMinRouteImport.update({ + id: '/ssr-false-pending-min', + path: '/ssr-false-pending-min', getParentRoute: () => rootRouteImport, } as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ @@ -97,18 +97,11 @@ export interface RootRouteChildren { declare module '@tanstack/solid-router' { interface FileRoutesByPath { - '/ssr-false-pending-min': { - id: '/ssr-false-pending-min' - path: '/ssr-false-pending-min' - fullPath: '/ssr-false-pending-min' - preLoaderRoute: typeof SsrFalsePendingMinRouteImport - parentRoute: typeof rootRouteImport - } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } '/data-only-pending-component': { @@ -118,11 +111,18 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof DataOnlyPendingComponentRouteImport parentRoute: typeof rootRouteImport } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport + parentRoute: typeof rootRouteImport + } + '/ssr-false-pending-min': { + id: '/ssr-false-pending-min' + path: '/ssr-false-pending-min' + fullPath: '/ssr-false-pending-min' + preLoaderRoute: typeof SsrFalsePendingMinRouteImport parentRoute: typeof rootRouteImport } '/posts/$postId': { diff --git a/e2e/solid-start/selective-ssr/src/routes/ssr-false-pending-min.tsx b/e2e/solid-start/selective-ssr/src/routes/ssr-false-pending-min.tsx index 82425f9575..12ab6b5f78 100644 --- a/e2e/solid-start/selective-ssr/src/routes/ssr-false-pending-min.tsx +++ b/e2e/solid-start/selective-ssr/src/routes/ssr-false-pending-min.tsx @@ -1,4 +1,4 @@ -import { onCleanup, onMount } from 'solid-js' +import { onCleanup, onSettled } from 'solid-js' import { createFileRoute } from '@tanstack/solid-router' const pendingMinMs = 1500 @@ -14,7 +14,7 @@ function recordEvent(type: string) { } function Pending() { - onMount(() => { + onSettled(() => { recordEvent('pending-mounted') }) onCleanup(() => { @@ -25,7 +25,7 @@ function Pending() { } function Target() { - onMount(() => { + onSettled(() => { recordEvent('target-mounted') })