Skip to content

Conversation

@nlynzaad
Copy link
Contributor

@nlynzaad nlynzaad commented Sep 26, 2025

With the changes to better consider non-nested routes the root id of __root__ is being affected, which has an impact on start projects with spa mode enabled.

This updates the path parsing to take __root__ into account, and adds a set of e2e tests specific for spa mode based on the start-basic e2e test suite.

This resolves #5171

Summary by CodeRabbit

  • New Features
    • Added optional SPA mode with prerendering support in the example app and a MODE-aware test helper.
  • Bug Fixes
    • Fixed path parsing to avoid stripping trailing underscores from the root route segment.
  • Performance/UX
    • Devtools now load within a Suspense boundary, improving initial render responsiveness.
  • Refactor
    • Route definitions updated to use explicit path-first declarations without behavioral changes.
  • Tests
    • E2E tests split to run in SPA and SSR modes, expanded redirect/navigation matrices and SPA-aware assertions.
  • Chores
    • Build and dev-server config updated to conditionally enable SPA settings and expose MODE env.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 26, 2025

Walkthrough

Adjusts router-core path parsing to preserve rootRouteId underscores, shifts sample app file-routes to a path-first createFileRoute('/path')({ ... }) API, adds SPA vs SSR e2e modes and SPA-aware Playwright wiring/tests, trims generated route typings, and wraps RouterDevtools in Suspense.

Changes

Cohort / File(s) Summary
Router core path parsing
packages/router-core/src/path.ts
Import rootRouteId; baseParsePathname no longer strips trailing underscores for segments equal to rootRouteId when basePathValues is false.
E2E scripts & Playwright config
e2e/*/basic/package.json, e2e/*/basic/playwright.config.ts, e2e/*/basic/tests/utils/isSpaMode.ts
Add test:e2e:spaMode / test:e2e:ssrMode, composite test:e2e; Playwright selects webServer command by isSpaMode and sets env vars; add isSpaMode util. (applies to react-start and solid-start e2e folders)
Vite SPA mode
e2e/react-start/basic/vite.config.ts
Wire spaMode into tanstackStart plugin configuration, enabling prerender/index.html when MODE=spa.
Generated route typings
e2e/react-start/basic/src/routeTree.gen.ts
Reallocate links/inline-scripts augmentation entry and remove many server/route module declarations, reducing generated public type surface while keeping core/root declarations.
Devtools Suspense
e2e/react-start/basic/src/routes/__root.tsx
Wrap RouterDevtools in React.Suspense with a null fallback.
Route factory API refactor (path-first)
Many files under e2e/react-start/basic/src/routes/* (e.g. index.tsx, posts*, users*, redirect*, search-params*, scripts.tsx, links.tsx, inline-scripts.tsx, 대한민국.tsx, etc.)
Replace createFileRoute({ ... }) with createFileRoute('/<path>')({ ... }); add createFileRoute imports; small related tweaks (e.g., posts.$postId adds notFoundComponent, users loader maps .data, introduce PostDeepErrorComponent).
Playwright tests: matrices & SPA-aware assertions
e2e/react-start/basic/tests/* (navigation.spec.ts, redirect.spec.ts, search-params.spec.ts)
Rework redirect tests into parameterized matrices (internal/external, serverFn/useServerFn, navigate/direct_visit, thrower, reloadDocument); conditionally assert redirect/DOM expectations based on isSpaMode; update navigation assertions for SCRIPT_2.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Dev as Playwright
  participant CFG as playwright.config.ts
  participant Vite as Dev Server
  participant App as App (Vite)
  participant Router as TanStack Router

  Dev->>CFG: start tests
  CFG->>CFG: read MODE -> isSpaMode
  CFG->>Vite: start webServer (spaModeCommand | ssrModeCommand) with env
  Vite-->>Dev: server ready
  Dev->>App: open URL
  App->>Router: initialize routes (file-route registration)
  Router->>App: render matched components
  note over App,Router: RouterDevtools loads inside Suspense (dev only)
Loading
sequenceDiagram
  participant File as Route file
  participant API as createFileRoute
  participant Router as Router runtime

  File->>API: createFileRoute('/path')({ config })
  API-->>File: Route (path-bound)
  File->>Router: export/register Route
  Router-->>Router: register route with explicit path
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • schiller-manuel
  • Sheraff

Poem

Hop hop — I parsed each path with care,
Kept root underscores safe from snip and tear.
Curried routes now wear their path like hats,
Tests flip modes while the bunny naps.
Suspense holds the devtools — carrot cheers and claps! 🥕🐇

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title follows conventional commit conventions and clearly summarizes the primary change by indicating a fix in router-core to account for the root segment when parsing trailing underscores, aligning precisely with the pull request’s main objective.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch #5171-consider-root-route

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud
Copy link

nx-cloud bot commented Sep 26, 2025

View your CI Pipeline Execution ↗ for commit ebb9887

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

☁️ Nx Cloud last updated this comment at 2025-09-27 11:16:57 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Sep 26, 2025

More templates

@tanstack/arktype-adapter

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

@tanstack/directive-functions-plugin

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

@tanstack/eslint-plugin-router

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

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/server-functions-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/zod-adapter

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

commit: ebb9887

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 27

🧹 Nitpick comments (33)
e2e/react-start/basic-spa/src/routes/search-params/loader-throws-redirect.tsx (1)

11-14: Preserve other search params and avoid extra history entries on default redirect.

Use functional search to merge existing params and set replace: true to prevent an extra history entry.

-      throw redirect({
-        to: '/search-params/loader-throws-redirect',
-        search: { step: 'a' },
-      })
+      throw redirect({
+        to: '/search-params/loader-throws-redirect',
+        search: (prev) => ({ ...prev, step: 'a' }),
+        replace: true,
+      })
e2e/react-start/basic-spa/src/routes/inline-scripts.tsx (1)

4-16: Optional: add stable IDs to inline scripts for robust de‑duplication

If head script de‑dup relies on keys, giving each inline script an id helps prevent duplicates across SSR/CSR hydration.

Apply this diff:

       {
+        id: 'inline-script-1',
         children:
           'window.INLINE_SCRIPT_1 = true; console.log("Inline script 1 executed");',
       },
       {
+        id: 'inline-script-2',
         children:
           'window.INLINE_SCRIPT_2 = "test"; console.log("Inline script 2 executed");',
         type: 'text/javascript',
       },
e2e/react-start/basic-spa/src/routes/search-params/default.tsx (1)

8-17: Optional: DRY the repeated context check

The beforeLoad and loader checks are identical. Extract into a small helper for clarity.

Apply this diff in-place:

-  beforeLoad: ({ context }) => {
-    if (context.hello !== 'world') {
-      throw new Error('Context hello is not "world"')
-    }
-  },
-  loader: ({ context }) => {
-    if (context.hello !== 'world') {
-      throw new Error('Context hello is not "world"')
-    }
-  },
+  beforeLoad: ({ context }) => assertWorld(context),
+  loader: ({ context }) => assertWorld(context),

Add this helper near the top of the file:

const assertWorld = (context: { hello?: unknown }) => {
  if (context.hello !== 'world') {
    throw new Error('Context hello is not "world"')
  }
}
e2e/react-start/basic-spa/src/utils/users.tsx (1)

1-5: Nit: this utility doesn’t use JSX; consider .ts instead of .tsx.

Slightly improves build ergonomics and signals intent.

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

279-281: Good fix: preserve "root" while stripping trailing underscores elsewhere.

This aligns with the intended behavior (strip on route segments, preserve special/base segments). Consider avoiding the magic string and using a shared constant, and prefer endsWith for clarity.

Apply this diff:

-        !basePathValues && part !== '__root__' && part.slice(-1) === '_'
+        !basePathValues && part !== ROOT_ROUTE_ID && part.endsWith('_')
           ? part.slice(0, -1)
           : part

Add near the top of this module (or import from a shared constants file if one exists):

const ROOT_ROUTE_ID = '__root__'

This reduces typos and centralizes the identifier. Based on learnings.


214-225: Cache key likely conflates base vs route parsing (potentially incorrect results).

parsePathname caches by pathname only, but parsing differs when basePathValues is true vs false. If the same string is parsed both ways sharing the same cache, results can be wrong. Safer to include the mode in the cache key.

Apply this diff:

 export const parsePathname = (
   pathname?: string,
   cache?: ParsePathnameCache,
   basePathValues?: boolean,
 ): ReadonlyArray<Segment> => {
   if (!pathname) return []
-  const cached = cache?.get(pathname)
+  const cacheKey = `${basePathValues ? 'b' : 'r'}|${pathname}`
+  const cached = cache?.get(cacheKey)
   if (cached) return cached
   const parsed = baseParsePathname(pathname, basePathValues)
-  cache?.set(pathname, parsed)
+  cache?.set(cacheKey, parsed)
   return parsed
 }

Please verify if a shared parseCache instance is passed to both parseBasePathSegments and parseRoutePathSegments in any hot paths. If yes, this change prevents subtle mismatches. Based on learnings.

e2e/react-start/basic-spa/src/styles/app.css (1)

1-22: LGTM: Tailwind layers and dark mode base styles.

The .using-mouse * { outline: none } pattern is acceptable for e2e. If this ever ships to prod, consider preferring :focus-visible utilities to preserve keyboard focus indicators.

e2e/react-start/basic-spa/src/utils/seo.ts (1)

12-33: Avoid emitting meta tags with undefined content.

Guard optional fields so consumers don’t get content="undefined".

Apply this diff:

-  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 },
-        ]
-      : []),
-  ]
+  const tags = [
+    { title },
+    ...(description ? [{ name: 'description', content: description }] : []),
+    ...(keywords ? [{ name: 'keywords', content: keywords }] : []),
+    { name: 'twitter:title', content: title },
+    ...(description ? [{ 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 },
+    ...(description ? [{ name: 'og:description', content: description }] : []),
+    ...(image
+      ? [
+          { name: 'twitter:image', content: image },
+          { name: 'twitter:card', content: 'summary_large_image' },
+          { name: 'og:image', content: image },
+        ]
+      : []),
+  ]
e2e/react-start/basic-spa/src/components/RedirectOnClick.tsx (1)

17-24: Prevent accidental form submit and unhandled promise rejections

  • Add type="button" to avoid submitting if used inside a form.
  • Handle the promise so redirect rejections don’t surface as unhandled errors.

Apply this diff:

   return (
     <button
+      type="button"
       data-testid="redirect-on-click"
-      onClick={() =>
-        execute({ data: { target, reloadDocument, externalHost } })
-      }
+      onClick={async () => {
+        try {
+          await execute({ data: { target, reloadDocument, externalHost } })
+        } catch {
+          // Redirect is handled by the framework
+        }
+      }}
     >
       click me
     </button>
   )
e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx (1)

1-10: Validate and coerce search params to expected types

Ensure reloadDocument is a boolean (not a "true"/"false" string) and externalHost is a string. Add validateSearch with zod for predictable behavior in SPA mode.

Apply this diff:

-import { createFileRoute } from '@tanstack/react-router'
+import { createFileRoute } from '@tanstack/react-router'
+import { z } from 'zod'
 import { throwRedirect } from '~/components/throwRedirect'
 
 export const Route = createFileRoute('/redirect/$target/serverFn/via-beforeLoad')({
+  validateSearch: z.object({
+    reloadDocument: z.boolean().optional(),
+    externalHost: z.string().optional(),
+  }),
   beforeLoad: ({
     params: { target },
     search: { reloadDocument, externalHost },
   }) => throwRedirect({ data: { target, reloadDocument, externalHost } }),
   component: () => <div>{Route.fullPath}</div>,
 })
e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/via-loader.tsx (1)

9-11: Optional: await the serverFn for clarity

The router will await the loader’s returned promise, but making it explicit reads clearer.

-  loader: ({ params: { target }, deps: { reloadDocument, externalHost } }) =>
-    throwRedirect({ data: { target, reloadDocument, externalHost } }),
+  loader: async ({
+    params: { target },
+    deps: { reloadDocument, externalHost },
+  }) => {
+    await throwRedirect({ data: { target, reloadDocument, externalHost } })
+  },
e2e/react-start/basic-spa/src/components/DefaultCatchBoundary.tsx (1)

17-17: Gate console.error in production

Console noise in CI/production can be distracting; consider gating behind an env check.

-  console.error(error)
+  if (import.meta.env.DEV) console.error(error)
e2e/react-start/basic-spa/src/routes/scripts.tsx (1)

6-16: Avoid undefined entries and prefer absolute paths for public assets

Prevent undefined in scripts array and use absolute paths for stability with varying base URLs.

   head: () => ({
     scripts: [
-      {
-        src: 'script.js',
-      },
-      isProd
-        ? undefined
-        : {
-            src: 'script2.js',
-          },
+      { src: '/script.js' },
+      ...(isProd ? [] : [{ src: '/script2.js' }]),
     ],
   }),
e2e/react-start/basic-spa/src/components/throwRedirect.ts (1)

12-23: Optional: validate external URL before redirect

Guard against malformed externalHost by validating it; fall back to example.com if invalid. Keeps behavior robust for test inputs.

-  .handler((ctx) => {
+  .handler((ctx) => {
     if (ctx.data.target === 'internal') {
       throw redirect({
         to: '/posts',
         reloadDocument: ctx.data.reloadDocument,
       })
     }
-    const href = ctx.data.externalHost ?? 'http://example.com'
+    const href = (() => {
+      const candidate = ctx.data.externalHost ?? 'http://example.com'
+      try {
+        return new URL(candidate).toString()
+      } catch {
+        return 'http://example.com'
+      }
+    })()
     throw redirect({
       href,
     })
   })
e2e/react-start/basic-spa/src/routes/users.tsx (2)

24-27: Keep id type consistent with User

Appending a string id widens the array element type and weakens type safety. Use a numeric sentinel to stay compatible with User.

-        {[
-          ...users,
-          { id: 'i-do-not-exist', name: 'Non-existent User', email: '' },
-        ].map((user) => {
+        {[
+          ...users,
+          { id: -1, name: 'Non-existent User', email: '' },
+        ].map((user) => {

7-14: Minor: simplify loader with try/catch

Readability nit: using try/catch avoids promise chaining.

-  loader: async () => {
-    return await axios
-      .get<Array<User>>('/api/users')
-      .then((r) => r.data)
-      .catch(() => {
-        throw new Error('Failed to fetch users')
-      })
-  },
+  loader: async () => {
+    try {
+      const { data } = await axios.get<Array<User>>('/api/users')
+      return data
+    } catch {
+      throw new Error('Failed to fetch users')
+    }
+  },
e2e/react-start/basic-spa/src/routes/posts.tsx (1)

13-13: Remove redundant async

No await used; keep it simple.

-  loader: async () => fetchPosts(),
+  loader: () => fetchPosts(),
e2e/react-start/basic-spa/src/routes/redirect/$target/via-loader.tsx (1)

12-14: Guard against undefined externalHost

Redirecting with href: undefined can throw; validate before redirect.

-      case 'external':
-        throw redirect({ href: externalHost })
+      case 'external':
+        if (!externalHost) {
+          throw new Error('externalHost is required for external redirects')
+        }
+        throw redirect({ href: externalHost })
e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/via-useServerFn.tsx (1)

6-8: Provide a safe default for reloadDocument

Prevents passing undefined into the server fn flow.

-    const { reloadDocument, externalHost } = Route.useSearch()
+    const { reloadDocument = false, externalHost } = Route.useSearch()
e2e/react-start/basic-spa/src/routes/api/users.$id.ts (2)

18-18: Minor: prefer template strings for URL assembly.

Slightly clearer and avoids implicit string coercion.

-          const res = await axios.get<User>(`${queryURL}/users/` + params.id)
+          const res = await axios.get<User>(`${queryURL}/users/${params.id}`)

11-15: Optional: deduplicate env base-URL selection.

This logic appears in multiple files; consider extracting a getExternalApiBaseUrl() helper to keep it consistent.

e2e/react-start/basic-spa/tests/not-found.spec.ts (1)

41-43: Use Playwright's waitForTimeout instead of manual Promise.

Cleaner and integrates with Playwright timing controls.

-          await new Promise((r) => setTimeout(r, 250))
+          await page.waitForTimeout(250)
e2e/react-start/basic-spa/tests/navigation.spec.ts (1)

17-17: Make the post link selector less brittle.

Use a case-insensitive regex to reduce reliance on an exact truncated title.

-  await page.getByRole('link', { name: 'sunt aut facere repe' }).click()
+  await page.getByRole('link', { name: /sunt aut facere/i }).click()
e2e/react-start/basic-spa/src/utils/posts.tsx (1)

11-15: Optional: centralize external API base URL logic.

Extract to a shared util to avoid drift with other modules (e.g., users route).

e2e/react-start/basic-spa/playwright.config.ts (2)

6-10: Avoid JSON import attributes to keep config compatible across Node/TS setups

import packageJson ... with { type: 'json' } requires ESM + Node support for import attributes. This can break under Node 18 or TS configs without resolveJsonModule/import attributes. Prefer reading package.json via fs to avoid env fragility. Also confirm CI uses a Node/TS combo that supports top‑level await in TS config files.

Proposed change:

-import packageJson from './package.json' with { type: 'json' }
+import fs from 'node:fs'
+
+const pkgName: string = JSON.parse(
+  fs.readFileSync(new URL('./package.json', import.meta.url), 'utf-8'),
+).name
+
-const PORT = await getTestServerPort(packageJson.name)
-const EXTERNAL_PORT = await getDummyServerPort(packageJson.name)
+const PORT = await getTestServerPort(pkgName)
+const EXTERNAL_PORT = await getDummyServerPort(pkgName)

Verification checklist:

  • CI Node version >= 20 if you want to keep import attributes and top-level await in TS configs.
  • tsconfig for this e2e fixture: "moduleResolution" supports ESM, and "resolveJsonModule": true if sticking with JSON imports. As per coding guidelines (TypeScript strict mode), keep type-safety intact.

28-33: Set env via webServer.env and add a timeout for resiliency

Inlining env vars in the command is brittle across shells. Prefer webServer.env and add a timeout to avoid indefinite waits if the dev server fails.

Apply:

   webServer: {
-    command: `VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} PORT=${PORT} pnpm run dev:e2e --port=${PORT}`,
+    command: `pnpm run dev:e2e --port=${PORT}`,
     url: baseURL,
     reuseExistingServer: !process.env.CI,
     stdout: 'pipe',
+    timeout: 120_000,
+    env: {
+      VITE_EXTERNAL_PORT: String(EXTERNAL_PORT),
+      VITE_SERVER_PORT: String(PORT),
+      PORT: String(PORT),
+    },
   },
e2e/react-start/basic-spa/tests/script-duplication.spec.ts (6)

9-16: Make script selector robust and use locator assertions to reduce flake

Exact match script[src="script.js"] is brittle (absolute vs relative URL). Prefer ends-with and toHaveCount on a locator.

Apply:

-    const scriptCount = await page.evaluate(() => {
-      return document.querySelectorAll('script[src="script.js"]').length
-    })
-
-    expect(scriptCount).toBe(1)
+    await expect(page.locator('script[src$="/script.js"]')).toHaveCount(1)

26-41: Repeat the robust selector pattern for client-side nav checks

Same concern as above; use locator + toHaveCount for both checks.

Apply:

-    const firstNavCount = await page.evaluate(() => {
-      return document.querySelectorAll('script[src="script.js"]').length
-    })
-    expect(firstNavCount).toBe(1)
+    await expect(page.locator('script[src$="/script.js"]')).toHaveCount(1)
@@
-    const secondNavCount = await page.evaluate(() => {
-      return document.querySelectorAll('script[src="script.js"]').length
-    })
-    expect(secondNavCount).toBe(1)
+    await expect(page.locator('script[src$="/script.js"]')).toHaveCount(1)

61-66: Use locator count at the end of multi-nav test to avoid timing pitfalls

Switch to locator with ends-with selector for consistency and less flakiness.

Apply:

-    const finalCount = await page.evaluate(() => {
-      return document.querySelectorAll('script[src="script.js"]').length
-    })
-    expect(finalCount).toBe(1)
+    await expect(page.locator('script[src$="/script.js"]')).toHaveCount(1)

76-96: Inline script detection by raw text is brittle; use locator hasText filter

Exact textContent matching can break on formatting/minification. Use locator text filters.

Apply:

-    const script1Count = await page.evaluate(() => {
-      const scripts = Array.from(document.querySelectorAll('script:not([src])'))
-      return scripts.filter(
-        (script) =>
-          script.textContent &&
-          script.textContent.includes('window.INLINE_SCRIPT_1 = true'),
-      ).length
-    })
+    const script1Locator = page.locator('script:not([src])', {
+      hasText: 'window.INLINE_SCRIPT_1 = true',
+    })
 
-    const script2Count = await page.evaluate(() => {
-      const scripts = Array.from(document.querySelectorAll('script:not([src])'))
-      return scripts.filter(
-        (script) =>
-          script.textContent &&
-          script.textContent.includes('window.INLINE_SCRIPT_2 = "test"'),
-      ).length
-    })
+    const script2Locator = page.locator('script:not([src])', {
+      hasText: 'window.INLINE_SCRIPT_2 = "test"',
+    })
 
-    expect(script1Count).toBe(1)
-    expect(script2Count).toBe(1)
+    await expect(script1Locator).toHaveCount(1)
+    await expect(script2Locator).toHaveCount(1)

111-120: Same robustness for inline scripts during client-side navigation

Use locator filters instead of manual evaluation.

Apply:

-    const firstNavScript1Count = await page.evaluate(() => {
-      const scripts = Array.from(document.querySelectorAll('script:not([src])'))
-      return scripts.filter(
-        (script) =>
-          script.textContent &&
-          script.textContent.includes('window.INLINE_SCRIPT_1 = true'),
-      ).length
-    })
-    expect(firstNavScript1Count).toBe(1)
+    await expect(
+      page.locator('script:not([src])', {
+        hasText: 'window.INLINE_SCRIPT_1 = true',
+      }),
+    ).toHaveCount(1)

139-142: Stabilize script readiness with waitForFunction when asserting globals

To reduce flakiness, wait until globals are set before asserting.

Apply:

-    // Verify the scripts are still working
-    expect(await page.evaluate('window.INLINE_SCRIPT_1')).toBe(true)
-    expect(await page.evaluate('window.INLINE_SCRIPT_2')).toBe('test')
+    await page.waitForFunction(() => (window as any).INLINE_SCRIPT_1 === true)
+    await page.waitForFunction(() => (window as any).INLINE_SCRIPT_2 === 'test')
e2e/react-start/basic-spa/tests/redirect.spec.ts (1)

1-1: Optional: Prefer URLSearchParams over legacy node:querystring

node:querystring is legacy; URLSearchParams is modern and built-in.

Example:

const q = new URLSearchParams({ externalHost: `http://localhost:${EXTERNAL_HOST_PORT}/` }).toString()
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 56f6f8d and 92280f4.

⛔ Files ignored due to path filters (8)
  • e2e/react-start/basic-spa/public/android-chrome-192x192.png is excluded by !**/*.png
  • e2e/react-start/basic-spa/public/android-chrome-512x512.png is excluded by !**/*.png
  • e2e/react-start/basic-spa/public/apple-touch-icon.png is excluded by !**/*.png
  • e2e/react-start/basic-spa/public/favicon-16x16.png is excluded by !**/*.png
  • e2e/react-start/basic-spa/public/favicon-32x32.png is excluded by !**/*.png
  • e2e/react-start/basic-spa/public/favicon.ico is excluded by !**/*.ico
  • e2e/react-start/basic-spa/public/favicon.png is excluded by !**/*.png
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (74)
  • e2e/react-start/basic-spa/.gitignore (1 hunks)
  • e2e/react-start/basic-spa/.prettierignore (1 hunks)
  • e2e/react-start/basic-spa/package.json (1 hunks)
  • e2e/react-start/basic-spa/playwright.config.ts (1 hunks)
  • e2e/react-start/basic-spa/postcss.config.mjs (1 hunks)
  • e2e/react-start/basic-spa/public/script.js (1 hunks)
  • e2e/react-start/basic-spa/public/script2.js (1 hunks)
  • e2e/react-start/basic-spa/public/site.webmanifest (1 hunks)
  • e2e/react-start/basic-spa/src/client.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/components/CustomMessage.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/components/DefaultCatchBoundary.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/components/NotFound.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/components/RedirectOnClick.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/components/throwRedirect.ts (1 hunks)
  • e2e/react-start/basic-spa/src/routeTree.gen.ts (1 hunks)
  • e2e/react-start/basic-spa/src/router.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/__root.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/_layout.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/_layout/_layout-2.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/_layout/_layout-2/layout-a.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/_layout/_layout-2/layout-b.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/api.users.ts (1 hunks)
  • e2e/react-start/basic-spa/src/routes/api/users.$id.ts (1 hunks)
  • e2e/react-start/basic-spa/src/routes/deferred.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/foo/$bar/$qux/_here.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/foo/$bar/$qux/_here/index.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/index.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/inline-scripts.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/links.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/not-found/index.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/not-found/route.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/not-found/via-beforeLoad.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/not-found/via-loader.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/posts.$postId.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/posts.index.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/posts.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/posts_.$postId.deep.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/redirect/$target.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/redirect/$target/index.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/index.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/via-loader.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/via-useServerFn.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/redirect/$target/via-beforeLoad.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/redirect/$target/via-loader.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/redirect/index.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/scripts.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/search-params/default.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/search-params/index.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/search-params/loader-throws-redirect.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/search-params/route.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/stream.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/users.$userId.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/users.index.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/users.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/대한민국.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/server.ts (1 hunks)
  • e2e/react-start/basic-spa/src/styles/app.css (1 hunks)
  • e2e/react-start/basic-spa/src/utils/posts.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/utils/seo.ts (1 hunks)
  • e2e/react-start/basic-spa/src/utils/users.tsx (1 hunks)
  • e2e/react-start/basic-spa/tailwind.config.mjs (1 hunks)
  • e2e/react-start/basic-spa/tests/navigation.spec.ts (1 hunks)
  • e2e/react-start/basic-spa/tests/not-found.spec.ts (1 hunks)
  • e2e/react-start/basic-spa/tests/params.spec.ts (1 hunks)
  • e2e/react-start/basic-spa/tests/redirect.spec.ts (1 hunks)
  • e2e/react-start/basic-spa/tests/script-duplication.spec.ts (1 hunks)
  • e2e/react-start/basic-spa/tests/search-params.spec.ts (1 hunks)
  • e2e/react-start/basic-spa/tests/setup/global.setup.ts (1 hunks)
  • e2e/react-start/basic-spa/tests/setup/global.teardown.ts (1 hunks)
  • e2e/react-start/basic-spa/tests/streaming.spec.ts (1 hunks)
  • e2e/react-start/basic-spa/tsconfig.json (1 hunks)
  • e2e/react-start/basic-spa/vite.config.ts (1 hunks)
  • packages/router-core/src/path.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
e2e/**

📄 CodeRabbit inference engine (AGENTS.md)

Store end-to-end tests under the e2e/ directory

Files:

  • e2e/react-start/basic-spa/tsconfig.json
  • e2e/react-start/basic-spa/public/script2.js
  • e2e/react-start/basic-spa/src/utils/users.tsx
  • e2e/react-start/basic-spa/public/site.webmanifest
  • e2e/react-start/basic-spa/tailwind.config.mjs
  • e2e/react-start/basic-spa/src/routes/redirect/index.tsx
  • e2e/react-start/basic-spa/src/routes/redirect/$target/via-beforeLoad.tsx
  • e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx
  • e2e/react-start/basic-spa/src/routes/redirect/$target.tsx
  • e2e/react-start/basic-spa/src/routes/api.users.ts
  • e2e/react-start/basic-spa/src/routes/foo/$bar/$qux/_here.tsx
  • e2e/react-start/basic-spa/playwright.config.ts
  • e2e/react-start/basic-spa/src/routes/posts.index.tsx
  • e2e/react-start/basic-spa/src/components/RedirectOnClick.tsx
  • e2e/react-start/basic-spa/src/routes/_layout/_layout-2.tsx
  • e2e/react-start/basic-spa/vite.config.ts
  • e2e/react-start/basic-spa/src/routes/users.$userId.tsx
  • e2e/react-start/basic-spa/src/routes/_layout/_layout-2/layout-a.tsx
  • e2e/react-start/basic-spa/tests/params.spec.ts
  • e2e/react-start/basic-spa/src/routes/_layout/_layout-2/layout-b.tsx
  • e2e/react-start/basic-spa/src/routes/links.tsx
  • e2e/react-start/basic-spa/src/routes/not-found/via-loader.tsx
  • e2e/react-start/basic-spa/src/utils/posts.tsx
  • e2e/react-start/basic-spa/public/script.js
  • e2e/react-start/basic-spa/src/routes/search-params/route.tsx
  • e2e/react-start/basic-spa/src/routes/posts.$postId.tsx
  • e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/index.tsx
  • e2e/react-start/basic-spa/src/router.tsx
  • e2e/react-start/basic-spa/src/routeTree.gen.ts
  • e2e/react-start/basic-spa/src/routes/stream.tsx
  • e2e/react-start/basic-spa/src/routes/index.tsx
  • e2e/react-start/basic-spa/src/styles/app.css
  • e2e/react-start/basic-spa/src/components/NotFound.tsx
  • e2e/react-start/basic-spa/src/utils/seo.ts
  • e2e/react-start/basic-spa/tests/redirect.spec.ts
  • e2e/react-start/basic-spa/tests/search-params.spec.ts
  • e2e/react-start/basic-spa/src/routes/users.index.tsx
  • e2e/react-start/basic-spa/src/server.ts
  • e2e/react-start/basic-spa/src/routes/not-found/route.tsx
  • e2e/react-start/basic-spa/src/routes/__root.tsx
  • e2e/react-start/basic-spa/src/routes/posts_.$postId.deep.tsx
  • e2e/react-start/basic-spa/tests/setup/global.teardown.ts
  • e2e/react-start/basic-spa/src/routes/_layout.tsx
  • e2e/react-start/basic-spa/src/routes/search-params/loader-throws-redirect.tsx
  • e2e/react-start/basic-spa/tests/script-duplication.spec.ts
  • e2e/react-start/basic-spa/src/routes/redirect/$target/index.tsx
  • e2e/react-start/basic-spa/tests/not-found.spec.ts
  • e2e/react-start/basic-spa/src/routes/not-found/index.tsx
  • e2e/react-start/basic-spa/src/components/CustomMessage.tsx
  • e2e/react-start/basic-spa/src/components/DefaultCatchBoundary.tsx
  • e2e/react-start/basic-spa/src/routes/scripts.tsx
  • e2e/react-start/basic-spa/src/routes/search-params/index.tsx
  • e2e/react-start/basic-spa/tests/setup/global.setup.ts
  • e2e/react-start/basic-spa/postcss.config.mjs
  • e2e/react-start/basic-spa/src/components/throwRedirect.ts
  • e2e/react-start/basic-spa/src/routes/foo/$bar/$qux/_here/index.tsx
  • e2e/react-start/basic-spa/src/routes/posts.tsx
  • e2e/react-start/basic-spa/tests/navigation.spec.ts
  • e2e/react-start/basic-spa/src/routes/users.tsx
  • e2e/react-start/basic-spa/src/routes/deferred.tsx
  • e2e/react-start/basic-spa/package.json
  • e2e/react-start/basic-spa/tests/streaming.spec.ts
  • e2e/react-start/basic-spa/src/routes/redirect/$target/via-loader.tsx
  • e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/via-loader.tsx
  • e2e/react-start/basic-spa/src/routes/not-found/via-beforeLoad.tsx
  • e2e/react-start/basic-spa/src/routes/대한민국.tsx
  • e2e/react-start/basic-spa/src/routes/search-params/default.tsx
  • e2e/react-start/basic-spa/src/client.tsx
  • e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/via-useServerFn.tsx
  • e2e/react-start/basic-spa/src/routes/inline-scripts.tsx
  • e2e/react-start/basic-spa/src/routes/api/users.$id.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • e2e/react-start/basic-spa/src/utils/users.tsx
  • e2e/react-start/basic-spa/src/routes/redirect/index.tsx
  • e2e/react-start/basic-spa/src/routes/redirect/$target/via-beforeLoad.tsx
  • e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx
  • e2e/react-start/basic-spa/src/routes/redirect/$target.tsx
  • e2e/react-start/basic-spa/src/routes/api.users.ts
  • e2e/react-start/basic-spa/src/routes/foo/$bar/$qux/_here.tsx
  • e2e/react-start/basic-spa/playwright.config.ts
  • e2e/react-start/basic-spa/src/routes/posts.index.tsx
  • packages/router-core/src/path.ts
  • e2e/react-start/basic-spa/src/components/RedirectOnClick.tsx
  • e2e/react-start/basic-spa/src/routes/_layout/_layout-2.tsx
  • e2e/react-start/basic-spa/vite.config.ts
  • e2e/react-start/basic-spa/src/routes/users.$userId.tsx
  • e2e/react-start/basic-spa/src/routes/_layout/_layout-2/layout-a.tsx
  • e2e/react-start/basic-spa/tests/params.spec.ts
  • e2e/react-start/basic-spa/src/routes/_layout/_layout-2/layout-b.tsx
  • e2e/react-start/basic-spa/src/routes/links.tsx
  • e2e/react-start/basic-spa/src/routes/not-found/via-loader.tsx
  • e2e/react-start/basic-spa/src/utils/posts.tsx
  • e2e/react-start/basic-spa/src/routes/search-params/route.tsx
  • e2e/react-start/basic-spa/src/routes/posts.$postId.tsx
  • e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/index.tsx
  • e2e/react-start/basic-spa/src/router.tsx
  • e2e/react-start/basic-spa/src/routeTree.gen.ts
  • e2e/react-start/basic-spa/src/routes/stream.tsx
  • e2e/react-start/basic-spa/src/routes/index.tsx
  • e2e/react-start/basic-spa/src/components/NotFound.tsx
  • e2e/react-start/basic-spa/src/utils/seo.ts
  • e2e/react-start/basic-spa/tests/redirect.spec.ts
  • e2e/react-start/basic-spa/tests/search-params.spec.ts
  • e2e/react-start/basic-spa/src/routes/users.index.tsx
  • e2e/react-start/basic-spa/src/server.ts
  • e2e/react-start/basic-spa/src/routes/not-found/route.tsx
  • e2e/react-start/basic-spa/src/routes/__root.tsx
  • e2e/react-start/basic-spa/src/routes/posts_.$postId.deep.tsx
  • e2e/react-start/basic-spa/tests/setup/global.teardown.ts
  • e2e/react-start/basic-spa/src/routes/_layout.tsx
  • e2e/react-start/basic-spa/src/routes/search-params/loader-throws-redirect.tsx
  • e2e/react-start/basic-spa/tests/script-duplication.spec.ts
  • e2e/react-start/basic-spa/src/routes/redirect/$target/index.tsx
  • e2e/react-start/basic-spa/tests/not-found.spec.ts
  • e2e/react-start/basic-spa/src/routes/not-found/index.tsx
  • e2e/react-start/basic-spa/src/components/CustomMessage.tsx
  • e2e/react-start/basic-spa/src/components/DefaultCatchBoundary.tsx
  • e2e/react-start/basic-spa/src/routes/scripts.tsx
  • e2e/react-start/basic-spa/src/routes/search-params/index.tsx
  • e2e/react-start/basic-spa/tests/setup/global.setup.ts
  • e2e/react-start/basic-spa/src/components/throwRedirect.ts
  • e2e/react-start/basic-spa/src/routes/foo/$bar/$qux/_here/index.tsx
  • e2e/react-start/basic-spa/src/routes/posts.tsx
  • e2e/react-start/basic-spa/tests/navigation.spec.ts
  • e2e/react-start/basic-spa/src/routes/users.tsx
  • e2e/react-start/basic-spa/src/routes/deferred.tsx
  • e2e/react-start/basic-spa/tests/streaming.spec.ts
  • e2e/react-start/basic-spa/src/routes/redirect/$target/via-loader.tsx
  • e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/via-loader.tsx
  • e2e/react-start/basic-spa/src/routes/not-found/via-beforeLoad.tsx
  • e2e/react-start/basic-spa/src/routes/대한민국.tsx
  • e2e/react-start/basic-spa/src/routes/search-params/default.tsx
  • e2e/react-start/basic-spa/src/client.tsx
  • e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/via-useServerFn.tsx
  • e2e/react-start/basic-spa/src/routes/inline-scripts.tsx
  • e2e/react-start/basic-spa/src/routes/api/users.$id.ts
**/src/routes/**

📄 CodeRabbit inference engine (AGENTS.md)

Place file-based routes under src/routes/ directories

Files:

  • e2e/react-start/basic-spa/src/routes/redirect/index.tsx
  • e2e/react-start/basic-spa/src/routes/redirect/$target/via-beforeLoad.tsx
  • e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx
  • e2e/react-start/basic-spa/src/routes/redirect/$target.tsx
  • e2e/react-start/basic-spa/src/routes/api.users.ts
  • e2e/react-start/basic-spa/src/routes/foo/$bar/$qux/_here.tsx
  • e2e/react-start/basic-spa/src/routes/posts.index.tsx
  • e2e/react-start/basic-spa/src/routes/_layout/_layout-2.tsx
  • e2e/react-start/basic-spa/src/routes/users.$userId.tsx
  • e2e/react-start/basic-spa/src/routes/_layout/_layout-2/layout-a.tsx
  • e2e/react-start/basic-spa/src/routes/_layout/_layout-2/layout-b.tsx
  • e2e/react-start/basic-spa/src/routes/links.tsx
  • e2e/react-start/basic-spa/src/routes/not-found/via-loader.tsx
  • e2e/react-start/basic-spa/src/routes/search-params/route.tsx
  • e2e/react-start/basic-spa/src/routes/posts.$postId.tsx
  • e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/index.tsx
  • e2e/react-start/basic-spa/src/routes/stream.tsx
  • e2e/react-start/basic-spa/src/routes/index.tsx
  • e2e/react-start/basic-spa/src/routes/users.index.tsx
  • e2e/react-start/basic-spa/src/routes/not-found/route.tsx
  • e2e/react-start/basic-spa/src/routes/__root.tsx
  • e2e/react-start/basic-spa/src/routes/posts_.$postId.deep.tsx
  • e2e/react-start/basic-spa/src/routes/_layout.tsx
  • e2e/react-start/basic-spa/src/routes/search-params/loader-throws-redirect.tsx
  • e2e/react-start/basic-spa/src/routes/redirect/$target/index.tsx
  • e2e/react-start/basic-spa/src/routes/not-found/index.tsx
  • e2e/react-start/basic-spa/src/routes/scripts.tsx
  • e2e/react-start/basic-spa/src/routes/search-params/index.tsx
  • e2e/react-start/basic-spa/src/routes/foo/$bar/$qux/_here/index.tsx
  • e2e/react-start/basic-spa/src/routes/posts.tsx
  • e2e/react-start/basic-spa/src/routes/users.tsx
  • e2e/react-start/basic-spa/src/routes/deferred.tsx
  • e2e/react-start/basic-spa/src/routes/redirect/$target/via-loader.tsx
  • e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/via-loader.tsx
  • e2e/react-start/basic-spa/src/routes/not-found/via-beforeLoad.tsx
  • e2e/react-start/basic-spa/src/routes/대한민국.tsx
  • e2e/react-start/basic-spa/src/routes/search-params/default.tsx
  • e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/via-useServerFn.tsx
  • e2e/react-start/basic-spa/src/routes/inline-scripts.tsx
  • e2e/react-start/basic-spa/src/routes/api/users.$id.ts
packages/router-core/**

📄 CodeRabbit inference engine (AGENTS.md)

Keep framework-agnostic core router logic in packages/router-core/

Files:

  • packages/router-core/src/path.ts
**/package.json

📄 CodeRabbit inference engine (AGENTS.md)

Use workspace:* protocol for internal dependencies in package.json files

Files:

  • e2e/react-start/basic-spa/package.json
🧠 Learnings (6)
📓 Common learnings
Learnt from: nlynzaad
PR: TanStack/router#5182
File: e2e/react-router/basic-file-based/src/routes/non-nested/named/$baz_.bar.tsx:3-5
Timestamp: 2025-09-22T00:56:49.237Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments (e.g., `$baz_` becomes `baz` in generated types) but should be preserved in base path segments. This is the correct behavior as of the fix in PR #5182.
Learnt from: nlynzaad
PR: TanStack/router#5182
File: e2e/react-router/basic-file-based/tests/non-nested-paths.spec.ts:167-172
Timestamp: 2025-09-22T00:56:53.426Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments during path parsing, but preserved in base path segments. This is the expected behavior implemented in PR #5182.
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript in strict mode with extensive type safety across the codebase

Applied to files:

  • e2e/react-start/basic-spa/tsconfig.json
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{router-cli,router-generator,router-plugin,virtual-file-routes}/** : Keep CLI, generators, bundler plugins, and virtual file routing utilities in their dedicated tooling package directories

Applied to files:

  • e2e/react-start/basic-spa/.prettierignore
📚 Learning: 2025-09-22T00:56:49.237Z
Learnt from: nlynzaad
PR: TanStack/router#5182
File: e2e/react-router/basic-file-based/src/routes/non-nested/named/$baz_.bar.tsx:3-5
Timestamp: 2025-09-22T00:56:49.237Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments (e.g., `$baz_` becomes `baz` in generated types) but should be preserved in base path segments. This is the correct behavior as of the fix in PR #5182.

Applied to files:

  • packages/router-core/src/path.ts
📚 Learning: 2025-09-22T00:56:53.426Z
Learnt from: nlynzaad
PR: TanStack/router#5182
File: e2e/react-router/basic-file-based/tests/non-nested-paths.spec.ts:167-172
Timestamp: 2025-09-22T00:56:53.426Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments during path parsing, but preserved in base path segments. This is the expected behavior implemented in PR #5182.

Applied to files:

  • packages/router-core/src/path.ts
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{react-router,solid-router}/** : Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/

Applied to files:

  • e2e/react-start/basic-spa/src/routes/__root.tsx
  • e2e/react-start/basic-spa/src/routes/scripts.tsx
  • e2e/react-start/basic-spa/src/routes/inline-scripts.tsx
🧬 Code graph analysis (47)
e2e/react-start/basic-spa/src/routes/redirect/index.tsx (2)
e2e/react-start/basic-spa/src/routes/links.tsx (1)
  • Route (3-47)
e2e/react-start/basic-spa/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-spa/src/routes/redirect/$target/via-beforeLoad.tsx (2)
e2e/react-start/basic-spa/src/routes/links.tsx (1)
  • Route (3-47)
e2e/react-start/basic-spa/src/routes/not-found/via-beforeLoad.tsx (1)
  • Route (3-15)
e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx (2)
e2e/react-start/basic-spa/src/routes/not-found/via-beforeLoad.tsx (1)
  • Route (3-15)
e2e/react-start/basic-spa/src/components/throwRedirect.ts (1)
  • throwRedirect (4-23)
e2e/react-start/basic-spa/src/routes/redirect/$target.tsx (1)
e2e/react-start/basic-spa/src/routes/not-found/route.tsx (1)
  • Route (4-8)
e2e/react-start/basic-spa/src/routes/api.users.ts (2)
e2e/react-start/basic-spa/src/routes/api/users.$id.ts (1)
  • Route (12-32)
e2e/react-start/basic-spa/src/utils/users.tsx (1)
  • User (1-5)
e2e/react-start/basic-spa/src/routes/foo/$bar/$qux/_here.tsx (1)
e2e/react-start/basic-spa/src/routes/foo/$bar/$qux/_here/index.tsx (1)
  • Route (3-5)
e2e/react-start/basic-spa/src/routes/posts.index.tsx (1)
e2e/react-start/basic-spa/src/routes/links.tsx (1)
  • Route (3-47)
e2e/react-start/basic-spa/src/components/RedirectOnClick.tsx (1)
e2e/react-start/basic-spa/src/components/throwRedirect.ts (1)
  • throwRedirect (4-23)
e2e/react-start/basic-spa/src/routes/_layout/_layout-2.tsx (3)
e2e/react-start/basic-spa/src/routes/_layout.tsx (1)
  • Route (3-5)
e2e/react-start/basic-spa/src/routes/_layout/_layout-2/layout-a.tsx (1)
  • Route (3-5)
e2e/react-start/basic-spa/src/routes/_layout/_layout-2/layout-b.tsx (1)
  • Route (3-5)
e2e/react-start/basic-spa/src/routes/users.$userId.tsx (3)
e2e/react-start/basic-spa/src/routes/api/users.$id.ts (1)
  • Route (12-32)
e2e/react-start/basic-spa/src/utils/users.tsx (1)
  • User (1-5)
e2e/react-start/basic-spa/src/components/NotFound.tsx (1)
  • NotFound (3-25)
e2e/react-start/basic-spa/src/routes/_layout/_layout-2/layout-a.tsx (4)
e2e/react-start/basic-spa/src/routes/__root.tsx (1)
  • Route (16-75)
e2e/react-start/basic-spa/src/routes/_layout.tsx (1)
  • Route (3-5)
e2e/react-start/basic-spa/src/routes/_layout/_layout-2.tsx (1)
  • Route (3-5)
e2e/react-start/basic-spa/src/routes/_layout/_layout-2/layout-b.tsx (1)
  • Route (3-5)
e2e/react-start/basic-spa/src/routes/_layout/_layout-2/layout-b.tsx (3)
e2e/react-start/basic-spa/src/routes/_layout.tsx (1)
  • Route (3-5)
e2e/react-start/basic-spa/src/routes/_layout/_layout-2.tsx (1)
  • Route (3-5)
e2e/react-start/basic-spa/src/routes/_layout/_layout-2/layout-a.tsx (1)
  • Route (3-5)
e2e/react-start/basic-spa/src/routes/links.tsx (2)
e2e/react-start/basic-spa/src/routes/index.tsx (1)
  • Route (4-6)
e2e/react-start/basic-spa/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-spa/src/routes/not-found/via-loader.tsx (3)
e2e/react-start/basic-spa/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-spa/src/routes/not-found/route.tsx (1)
  • Route (4-8)
e2e/react-start/basic-spa/src/routes/not-found/via-beforeLoad.tsx (1)
  • Route (3-15)
e2e/react-start/basic-spa/src/utils/posts.tsx (1)
e2e/react-router/js-only-file-based/src/posts.js (1)
  • queryURL (5-5)
e2e/react-start/basic-spa/src/routes/search-params/route.tsx (2)
e2e/react-start/basic-spa/src/routes/deferred.tsx (1)
  • Route (18-29)
e2e/react-start/basic-spa/src/routes/not-found/via-beforeLoad.tsx (1)
  • Route (3-15)
e2e/react-start/basic-spa/src/routes/posts.$postId.tsx (2)
e2e/react-start/basic-spa/src/utils/posts.tsx (1)
  • fetchPost (17-33)
e2e/react-start/basic-spa/src/components/NotFound.tsx (1)
  • NotFound (3-25)
e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/index.tsx (3)
e2e/react-start/basic-spa/src/routes/links.tsx (1)
  • Route (3-47)
e2e/react-start/basic-spa/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-spa/src/routes/not-found/via-beforeLoad.tsx (1)
  • Route (3-15)
e2e/react-start/basic-spa/src/router.tsx (2)
e2e/react-start/basic-spa/src/components/DefaultCatchBoundary.tsx (1)
  • DefaultCatchBoundary (10-53)
e2e/react-start/basic-spa/src/components/NotFound.tsx (1)
  • NotFound (3-25)
e2e/react-start/basic-spa/src/routeTree.gen.ts (1)
e2e/react-start/basic-spa/src/router.tsx (1)
  • getRouter (6-16)
e2e/react-start/basic-spa/src/routes/stream.tsx (2)
e2e/react-start/basic-spa/src/routes/deferred.tsx (1)
  • Route (18-29)
e2e/react-start/basic-spa/src/routes/index.tsx (1)
  • Route (4-6)
e2e/react-start/basic-spa/src/routes/index.tsx (1)
e2e/react-start/basic-spa/src/components/CustomMessage.tsx (1)
  • CustomMessage (3-10)
e2e/react-start/basic-spa/src/components/NotFound.tsx (1)
e2e/react-start/server-routes/src/components/NotFound.tsx (1)
  • NotFound (3-25)
e2e/react-start/basic-spa/tests/redirect.spec.ts (1)
e2e/react-start/basic/tests/redirect.spec.ts (2)
  • internalNavigationTestMatrix (17-208)
  • test (132-168)
e2e/react-start/basic-spa/src/routes/not-found/route.tsx (1)
e2e/react-start/basic-spa/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-spa/src/routes/__root.tsx (3)
e2e/react-start/basic-spa/src/utils/seo.ts (1)
  • seo (1-33)
e2e/react-start/basic-spa/src/components/DefaultCatchBoundary.tsx (1)
  • DefaultCatchBoundary (10-53)
e2e/react-start/basic-spa/src/components/NotFound.tsx (1)
  • NotFound (3-25)
e2e/react-start/basic-spa/src/routes/posts_.$postId.deep.tsx (2)
e2e/react-start/basic-spa/src/utils/posts.tsx (1)
  • fetchPost (17-33)
e2e/react-start/basic-spa/src/routes/posts.$postId.tsx (1)
  • PostErrorComponent (16-18)
e2e/react-start/basic-spa/tests/setup/global.teardown.ts (9)
e2e/solid-router/basic-file-based/tests/setup/global.teardown.ts (1)
  • teardown (4-6)
e2e/react-router/basic-esbuild-file-based/tests/setup/global.teardown.ts (1)
  • teardown (4-6)
e2e/solid-router/basic-virtual-file-based/tests/setup/global.teardown.ts (1)
  • teardown (4-6)
e2e/solid-router/basic-solid-query-file-based/tests/setup/global.teardown.ts (1)
  • teardown (4-6)
e2e/solid-router/rspack-basic-virtual-named-export-config-file-based/tests/setup/global.teardown.ts (1)
  • teardown (4-6)
e2e/solid-router/basic-virtual-named-export-config-file-based/tests/setup/global.teardown.ts (1)
  • teardown (4-6)
e2e/react-router/generator-cli-only/tests/setup/global.teardown.ts (1)
  • teardown (4-6)
e2e/solid-router/basic-solid-query/tests/setup/global.teardown.ts (1)
  • teardown (4-6)
e2e/solid-router/basic/tests/setup/global.teardown.ts (1)
  • teardown (4-6)
e2e/react-start/basic-spa/src/routes/_layout.tsx (3)
e2e/react-start/basic-spa/src/routes/_layout/_layout-2.tsx (1)
  • Route (3-5)
e2e/react-start/basic-spa/src/routes/_layout/_layout-2/layout-a.tsx (1)
  • Route (3-5)
e2e/react-start/basic-spa/src/routes/_layout/_layout-2/layout-b.tsx (1)
  • Route (3-5)
e2e/react-start/basic-spa/src/routes/search-params/loader-throws-redirect.tsx (2)
e2e/react-start/basic-spa/src/routes/not-found/route.tsx (1)
  • Route (4-8)
e2e/react-start/basic-spa/src/routes/not-found/via-beforeLoad.tsx (1)
  • Route (3-15)
e2e/react-start/basic-spa/src/routes/redirect/$target/index.tsx (3)
e2e/react-start/basic-spa/src/routes/links.tsx (1)
  • Route (3-47)
e2e/react-start/basic-spa/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-spa/src/routes/not-found/route.tsx (1)
  • Route (4-8)
e2e/react-start/basic-spa/tests/not-found.spec.ts (1)
e2e/e2e-utils/src/index.ts (1)
  • test (7-7)
e2e/react-start/basic-spa/src/routes/not-found/index.tsx (3)
e2e/react-start/basic-spa/src/routes/not-found/route.tsx (1)
  • Route (4-8)
e2e/react-start/basic-spa/src/routes/not-found/via-beforeLoad.tsx (1)
  • Route (3-15)
e2e/react-start/basic-spa/src/routes/not-found/via-loader.tsx (1)
  • Route (3-15)
e2e/react-start/basic-spa/src/components/DefaultCatchBoundary.tsx (1)
e2e/react-start/server-routes/src/components/DefaultCatchBoundary.tsx (1)
  • DefaultCatchBoundary (10-53)
e2e/react-start/basic-spa/src/routes/scripts.tsx (2)
e2e/react-start/basic-spa/src/routes/inline-scripts.tsx (1)
  • Route (3-18)
packages/router-core/src/ssr/tsrScript.ts (1)
  • p (7-9)
e2e/react-start/basic-spa/src/routes/search-params/index.tsx (2)
e2e/react-start/basic-spa/src/routes/links.tsx (1)
  • Route (3-47)
e2e/react-start/basic-spa/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-spa/tests/setup/global.setup.ts (3)
e2e/solid-router/basic-file-based/tests/setup/global.setup.ts (1)
  • setup (4-6)
e2e/react-router/basic-file-based-code-splitting/tests/setup/global.setup.ts (1)
  • setup (4-6)
e2e/react-router/basic/tests/setup/global.setup.ts (1)
  • setup (4-6)
e2e/react-start/basic-spa/src/routes/foo/$bar/$qux/_here/index.tsx (1)
e2e/react-start/basic-spa/src/routes/foo/$bar/$qux/_here.tsx (1)
  • Route (3-5)
e2e/react-start/basic-spa/src/routes/posts.tsx (1)
e2e/react-start/basic-spa/src/utils/posts.tsx (1)
  • fetchPosts (35-42)
e2e/react-start/basic-spa/src/routes/users.tsx (4)
e2e/react-start/basic-spa/src/routes/api.users.ts (1)
  • Route (13-28)
e2e/react-start/basic-spa/src/routes/api/users.$id.ts (1)
  • Route (12-32)
e2e/react-start/basic-spa/src/utils/users.tsx (1)
  • User (1-5)
examples/solid/start-basic/src/routes/users.tsx (1)
  • UsersComponent (18-48)
e2e/react-start/basic-spa/src/routes/deferred.tsx (1)
examples/solid/start-basic/src/routes/deferred.tsx (2)
  • Deferred (31-62)
  • deferredStuff (19-27)
e2e/react-start/basic-spa/src/routes/redirect/$target/via-loader.tsx (2)
e2e/react-start/basic-spa/src/routes/links.tsx (1)
  • Route (3-47)
e2e/react-start/basic-spa/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/via-loader.tsx (1)
e2e/react-start/basic-spa/src/components/throwRedirect.ts (1)
  • throwRedirect (4-23)
e2e/react-start/basic-spa/src/routes/not-found/via-beforeLoad.tsx (3)
e2e/react-start/basic-spa/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-spa/src/routes/not-found/route.tsx (1)
  • Route (4-8)
e2e/react-start/basic-spa/src/routes/not-found/via-loader.tsx (1)
  • Route (3-15)
e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/via-useServerFn.tsx (1)
e2e/react-start/basic-spa/src/components/RedirectOnClick.tsx (1)
  • RedirectOnClick (10-26)
e2e/react-start/basic-spa/src/routes/inline-scripts.tsx (2)
e2e/react-start/basic-spa/src/routes/__root.tsx (1)
  • Route (16-75)
packages/router-core/src/ssr/tsrScript.ts (1)
  • p (7-9)
e2e/react-start/basic-spa/src/routes/api/users.$id.ts (2)
e2e/react-start/basic-spa/src/routes/api.users.ts (1)
  • Route (13-28)
e2e/react-start/basic-spa/src/utils/users.tsx (1)
  • User (1-5)
🪛 Biome (2.1.2)
e2e/react-start/basic-spa/src/routes/stream.tsx

[error] 51-51: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

e2e/react-start/basic-spa/src/routes/deferred.tsx

[error] 43-43: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)


[error] 53-53: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
e2e/react-start/basic-spa/tests/redirect.spec.ts (1)

79-100: Keep the dev-only whitelist scoped to the direct-visit suite

Line 82 calls test.use inside the loop; because Playwright keeps those settings for every test declared after that call, the hydration warning gets whitelisted for the rest of this file whenever NODE_ENV is development, hiding real regressions in later suites. Please wrap these direct-visit tests in their own test.describe and apply the whitelist there so other tests still surface unexpected hydration errors.

-  internalDirectVisitTestMatrix.forEach(({ thrower, reloadDocument }) => {
-    if (process.env.NODE_ENV === 'development') {
-      test.use({
-        whitelistErrors: [
-          /A tree hydrated but some attributes of the server rendered HTML/,
-        ],
-      })
-    }
-    test(`internal target, direct visit: thrower: ${thrower}, reloadDocument: ${reloadDocument}`, async ({
-      page,
-    }) => {
-      await page.goto(`/redirect/internal/via-${thrower}`)
-      await page.waitForLoadState('networkidle')
-
-      const url = `http://localhost:${PORT}/posts`
-
-      await page.waitForURL(url)
-      expect(page.url()).toBe(url)
-      await page.waitForLoadState('networkidle')
-      await expect(page.getByTestId('PostsIndexComponent')).toBeInViewport()
-    })
-  })
+  test.describe('internal direct visit', () => {
+    if (process.env.NODE_ENV === 'development') {
+      test.use({
+        whitelistErrors: [
+          /A tree hydrated but some attributes of the server rendered HTML/,
+        ],
+      })
+    }
+
+    internalDirectVisitTestMatrix.forEach(({ thrower, reloadDocument }) => {
+      test(`internal target, direct visit: thrower: ${thrower}, reloadDocument: ${reloadDocument}`, async ({
+        page,
+      }) => {
+        await page.goto(`/redirect/internal/via-${thrower}`)
+        await page.waitForLoadState('networkidle')
+
+        const url = `http://localhost:${PORT}/posts`
+
+        await page.waitForURL(url)
+        expect(page.url()).toBe(url)
+        await page.waitForLoadState('networkidle')
+        await expect(page.getByTestId('PostsIndexComponent')).toBeInViewport()
+      })
+    })
+  })
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ed0ec24 and b391c50.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (8)
  • e2e/react-start/basic-spa/package.json (1 hunks)
  • e2e/react-start/basic-spa/src/routes/__root.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/not-found/route.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/redirect/$target.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/index.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/stream.tsx (1 hunks)
  • e2e/react-start/basic-spa/tests/redirect.spec.ts (1 hunks)
  • e2e/react-start/basic-spa/tests/search-params.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • e2e/react-start/basic-spa/src/routes/redirect/$target.tsx
  • e2e/react-start/basic-spa/tests/search-params.spec.ts
  • e2e/react-start/basic-spa/package.json
  • e2e/react-start/basic-spa/src/routes/redirect/$target/serverFn/index.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • e2e/react-start/basic-spa/src/routes/__root.tsx
  • e2e/react-start/basic-spa/src/routes/not-found/route.tsx
  • e2e/react-start/basic-spa/tests/redirect.spec.ts
  • e2e/react-start/basic-spa/src/routes/stream.tsx
**/src/routes/**

📄 CodeRabbit inference engine (AGENTS.md)

Place file-based routes under src/routes/ directories

Files:

  • e2e/react-start/basic-spa/src/routes/__root.tsx
  • e2e/react-start/basic-spa/src/routes/not-found/route.tsx
  • e2e/react-start/basic-spa/src/routes/stream.tsx
e2e/**

📄 CodeRabbit inference engine (AGENTS.md)

Store end-to-end tests under the e2e/ directory

Files:

  • e2e/react-start/basic-spa/src/routes/__root.tsx
  • e2e/react-start/basic-spa/src/routes/not-found/route.tsx
  • e2e/react-start/basic-spa/tests/redirect.spec.ts
  • e2e/react-start/basic-spa/src/routes/stream.tsx
🧠 Learnings (2)
📓 Common learnings
Learnt from: nlynzaad
PR: TanStack/router#5182
File: e2e/react-router/basic-file-based/src/routes/non-nested/named/$baz_.bar.tsx:3-5
Timestamp: 2025-09-22T00:56:49.237Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments (e.g., `$baz_` becomes `baz` in generated types) but should be preserved in base path segments. This is the correct behavior as of the fix in PR #5182.
Learnt from: nlynzaad
PR: TanStack/router#5182
File: e2e/react-router/basic-file-based/tests/non-nested-paths.spec.ts:167-172
Timestamp: 2025-09-22T00:56:53.426Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments during path parsing, but preserved in base path segments. This is the expected behavior implemented in PR #5182.
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{react-router,solid-router}/** : Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/

Applied to files:

  • e2e/react-start/basic-spa/src/routes/__root.tsx
🧬 Code graph analysis (4)
e2e/react-start/basic-spa/src/routes/__root.tsx (3)
e2e/react-start/basic-spa/src/utils/seo.ts (1)
  • seo (1-33)
e2e/react-start/basic-spa/src/components/DefaultCatchBoundary.tsx (1)
  • DefaultCatchBoundary (10-53)
e2e/react-start/basic-spa/src/components/NotFound.tsx (1)
  • NotFound (3-25)
e2e/react-start/basic-spa/src/routes/not-found/route.tsx (2)
e2e/react-start/basic-spa/src/routes/__root.tsx (1)
  • Route (16-75)
e2e/react-start/basic-spa/src/routes/redirect/$target.tsx (1)
  • Route (4-21)
e2e/react-start/basic-spa/tests/redirect.spec.ts (1)
e2e/react-start/basic/tests/redirect.spec.ts (2)
  • internalNavigationTestMatrix (17-208)
  • test (132-168)
e2e/react-start/basic-spa/src/routes/stream.tsx (1)
e2e/react-start/basic-spa/src/routes/deferred.tsx (1)
  • Route (18-29)
🪛 Biome (2.1.2)
e2e/react-start/basic-spa/src/routes/stream.tsx

[error] 64-64: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test
🔇 Additional comments (2)
e2e/react-start/basic-spa/src/routes/stream.tsx (1)

62-74: Switch <Await> to JSX children to satisfy Biome

Biome still errors on passing the render fn via the children prop (noChildrenProp), so this will fail CI until converted to JSX children.

-      <Await
-        promise={promise}
-        children={(promiseData) => (
+      <Await promise={promise}>
+        {(promiseData) => (
           <div className="p-2" data-testid="promise-data">
             {promiseData}
             <div data-testid="stream-data">
               {streamData.map((d) => (
                 <div key={d}>{d}</div>
               ))}
             </div>
           </div>
-        )}
-      />
+        )}
+      </Await>
e2e/react-start/basic-spa/src/routes/__root.tsx (1)

51-51: Drop unsupported color attribute from the manifest link

rel="manifest" links don’t honor a color attribute, so this value is ignored. Let’s remove it to keep the head clean.

-      { rel: 'manifest', href: '/site.webmanifest', color: '#ffffff' },
+      { rel: 'manifest', href: '/site.webmanifest' },

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 83404a4 and 23c053c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (1)
  • e2e/react-start/basic-spa/tests/redirect.spec.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • e2e/react-start/basic-spa/tests/redirect.spec.ts
e2e/**

📄 CodeRabbit inference engine (AGENTS.md)

Store end-to-end tests under the e2e/ directory

Files:

  • e2e/react-start/basic-spa/tests/redirect.spec.ts
🧠 Learnings (1)
📓 Common learnings
Learnt from: nlynzaad
PR: TanStack/router#5182
File: e2e/react-router/basic-file-based/src/routes/non-nested/named/$baz_.bar.tsx:3-5
Timestamp: 2025-09-22T00:56:49.237Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments (e.g., `$baz_` becomes `baz` in generated types) but should be preserved in base path segments. This is the correct behavior as of the fix in PR #5182.
Learnt from: nlynzaad
PR: TanStack/router#5182
File: e2e/react-router/basic-file-based/tests/non-nested-paths.spec.ts:167-172
Timestamp: 2025-09-22T00:56:53.426Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments during path parsing, but preserved in base path segments. This is the expected behavior implemented in PR #5182.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Test
  • GitHub Check: Preview

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 23c053c and e327916.

📒 Files selected for processing (5)
  • e2e/react-start/basic-spa/package.json (1 hunks)
  • e2e/react-start/basic-spa/playwright.config.ts (1 hunks)
  • e2e/react-start/basic-spa/src/routes/posts.$postId.tsx (1 hunks)
  • e2e/react-start/basic-spa/src/routes/posts_.$postId.deep.tsx (1 hunks)
  • e2e/react-start/basic-spa/tests/navigation.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • e2e/react-start/basic-spa/package.json
  • e2e/react-start/basic-spa/src/routes/posts.$postId.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • e2e/react-start/basic-spa/playwright.config.ts
  • e2e/react-start/basic-spa/src/routes/posts_.$postId.deep.tsx
  • e2e/react-start/basic-spa/tests/navigation.spec.ts
e2e/**

📄 CodeRabbit inference engine (AGENTS.md)

Store end-to-end tests under the e2e/ directory

Files:

  • e2e/react-start/basic-spa/playwright.config.ts
  • e2e/react-start/basic-spa/src/routes/posts_.$postId.deep.tsx
  • e2e/react-start/basic-spa/tests/navigation.spec.ts
**/src/routes/**

📄 CodeRabbit inference engine (AGENTS.md)

Place file-based routes under src/routes/ directories

Files:

  • e2e/react-start/basic-spa/src/routes/posts_.$postId.deep.tsx
🧠 Learnings (1)
📓 Common learnings
Learnt from: nlynzaad
PR: TanStack/router#5182
File: e2e/react-router/basic-file-based/src/routes/non-nested/named/$baz_.bar.tsx:3-5
Timestamp: 2025-09-22T00:56:49.237Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments (e.g., `$baz_` becomes `baz` in generated types) but should be preserved in base path segments. This is the correct behavior as of the fix in PR #5182.
Learnt from: nlynzaad
PR: TanStack/router#5182
File: e2e/react-router/basic-file-based/tests/non-nested-paths.spec.ts:167-172
Timestamp: 2025-09-22T00:56:53.426Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments during path parsing, but preserved in base path segments. This is the expected behavior implemented in PR #5182.
🧬 Code graph analysis (2)
e2e/react-start/basic-spa/src/routes/posts_.$postId.deep.tsx (2)
e2e/react-start/basic-spa/src/routes/posts.$postId.tsx (1)
  • Route (7-14)
e2e/react-start/basic-spa/src/utils/posts.tsx (1)
  • fetchPost (17-33)
e2e/react-start/basic-spa/tests/navigation.spec.ts (1)
e2e/e2e-utils/src/index.ts (1)
  • test (7-7)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Test
  • GitHub Check: Preview
🔇 Additional comments (1)
e2e/react-start/basic-spa/tests/navigation.spec.ts (1)

65-68: Stop basing script expectations on process.env.NODE_ENV.

process.env.NODE_ENV reflects the Playwright runner, not the built app mode, so these assertions misfire (e.g. a prod build exercised under a dev runner). Detect the actual script presence instead and derive the expectation from that, e.g.:

-  expect(await page.evaluate('window.SCRIPT_2')).toBe(
-    process.env.NODE_ENV === 'development' ? true : undefined,
-  )
+  const hasScript2 = await page.evaluate(
+    () => !!document.querySelector('script[src*="script2.js"]'),
+  )
+  expect(await page.evaluate('window.SCRIPT_2')).toBe(
+    hasScript2 ? true : undefined,
+  )

Apply the same change in the direct navigation test below so both cases read the DOM rather than the runner environment.

Also applies to: 75-77

const PORT = await getTestServerPort(packageJson.name)
const EXTERNAL_PORT = await getDummyServerPort(packageJson.name)
const baseURL = `http://localhost:${PORT}`
const spaModeCommand = `pnpm build && pnpm dev:e2e --port=${PORT}`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing using dev for spa mode?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is a limitation of the single playwright entry, then maybe we can just have a spa mode specific entry like we have in the scroll restoration sandbox.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using dev for spa only to ensure we are mounting in the shell and running the server. not entirely sure yet how to mount the shell and server.ts correctly for production in the test.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you would need to serve the shell (and the other static files) via a custom http server and proxy through the requests for server functions etc to the actual start backend.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (8)
e2e/react-start/basic/src/routes/users.tsx (1)

6-16: Prefer async/await over mixed .then/.catch in loader

Simplifies flow and preserves stack traces.

Apply:

 export const Route = createFileRoute('/users')({
   loader: async () => {
-    return await axios
-      .get<Array<User>>('/api/users')
-      .then((r) => r.data)
-      .catch(() => {
-        throw new Error('Failed to fetch users')
-      })
+    try {
+      const { data } = await axios.get<Array<User>>('/api/users')
+      return data
+    } catch {
+      throw new Error('Failed to fetch users')
+    }
   },
   component: UsersComponent,
 })
e2e/react-start/basic/tests/redirect.spec.ts (6)

9-9: Use simpler relative import for isSpaMode

Avoids brittle ../tests/ hopping.

Apply:

-import { isSpaMode } from '../tests/utils/isSpaMode'
+import { isSpaMode } from './utils/isSpaMode'

39-60: Use page.waitForRequest instead of manual event/promise race

Simpler, less error-prone, no lingering listeners.

Apply:

-          await page.waitForLoadState('networkidle')
-          let requestHappened = false
-
-          const requestPromise = new Promise<void>((resolve) => {
-            page.on('request', (request) => {
-              if (
-                request.url().startsWith(`http://localhost:${PORT}/_serverFn/`)
-              ) {
-                requestHappened = true
-                resolve()
-              }
-            })
-          })
-          await link.focus()
-
-          const expectRequestHappened = preload && !reloadDocument
-          const timeoutPromise = new Promise((resolve) =>
-            setTimeout(resolve, expectRequestHappened ? 5000 : 500),
-          )
-          await Promise.race([requestPromise, timeoutPromise])
-          expect(requestHappened).toBe(expectRequestHappened)
+          await page.waitForLoadState('networkidle')
+          await link.focus()
+          const expectRequestHappened = preload && !reloadDocument
+          const req = await page
+            .waitForRequest(
+              (r) =>
+                r.url().startsWith(`http://localhost:${PORT}/_serverFn/`),
+              { timeout: expectRequestHappened ? 5000 : 500 },
+            )
+            .catch(() => null)
+          expect(!!req).toBe(expectRequestHappened)

61-63: Use page.once for domcontentloaded flag

Prevents multiple triggers toggling state unexpectedly.

Apply:

-          page.on('domcontentloaded', () => {
+          page.once('domcontentloaded', () => {
             fullPageLoad = true
           })

83-90: Set whitelistErrors once per describe instead of inside the loop

Reduces repetition and makes scope explicit.

Apply within the 'internal' describe, before building the matrix:

// Place near Line 19, before internalNavigationTestMatrix
if (isSpaMode) {
  test.use({
    whitelistErrors: [
      /A tree hydrated but some attributes of the server rendered HTML/,
    ],
  })
}

Then remove the per-iteration block:

-      if (isSpaMode) {
-        test.use({
-          whitelistErrors: [
-            /A tree hydrated but some attributes of the server rendered HTML/,
-          ],
-        })
-      }

164-166: Use page.once for domcontentloaded in serverFn matrix

Same rationale: avoid accumulating listeners.

Apply:

-            page.on('domcontentloaded', () => {
+            page.once('domcontentloaded', () => {
               fullPageLoad = true
             })

216-219: Use page.once for domcontentloaded in useServerFn suite

Consistent listener semantics across tests.

Apply:

-        page.on('domcontentloaded', () => {
+        page.once('domcontentloaded', () => {
           fullPageLoad = true
         })
e2e/react-start/basic/vite.config.ts (1)

24-27: Refresh the @ts-ignore rationale

The ignore comment still talks about verboseFileRoutes, but we’re now suppressing the type check for the spa option. Updating the comment to match the new reason would prevent future confusion.

Here’s an updated comment that matches the current code:

-    // @ts-ignore we want to keep one test with verboseFileRoutes off even though the option is hidden
+    // @ts-ignore: the SPA option isn’t exposed in the plugin’s public types yet
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 56aad43 and 373681f.

📒 Files selected for processing (49)
  • e2e/react-start/basic/package.json (1 hunks)
  • e2e/react-start/basic/playwright.config.ts (2 hunks)
  • e2e/react-start/basic/src/routeTree.gen.ts (1 hunks)
  • e2e/react-start/basic/src/routes/__root.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/_layout.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/_layout/_layout-2.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/_layout/_layout-2/layout-a.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/_layout/_layout-2/layout-b.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/api.users.ts (2 hunks)
  • e2e/react-start/basic/src/routes/api/users.$id.ts (2 hunks)
  • e2e/react-start/basic/src/routes/deferred.tsx (2 hunks)
  • e2e/react-start/basic/src/routes/foo/$bar/$qux/_here.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/foo/$bar/$qux/_here/index.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/index.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/inline-scripts.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/links.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/not-found/index.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/not-found/route.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/not-found/via-beforeLoad.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/not-found/via-loader.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/posts.$postId.tsx (2 hunks)
  • e2e/react-start/basic/src/routes/posts.index.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/posts.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/posts_.$postId.deep.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/redirect/$target.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/redirect/$target/index.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/redirect/$target/serverFn/index.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-loader.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-useServerFn.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/redirect/$target/via-beforeLoad.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/redirect/$target/via-loader.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/redirect/index.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/scripts.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/search-params/default.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/search-params/index.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/search-params/loader-throws-redirect.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/search-params/route.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/stream.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/users.$userId.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/users.index.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/users.tsx (1 hunks)
  • e2e/react-start/basic/src/routes/대한민국.tsx (1 hunks)
  • e2e/react-start/basic/tests/navigation.spec.ts (2 hunks)
  • e2e/react-start/basic/tests/redirect.spec.ts (2 hunks)
  • e2e/react-start/basic/tests/search-params.spec.ts (2 hunks)
  • e2e/react-start/basic/tests/utils/isSpaMode.ts (1 hunks)
  • e2e/react-start/basic/vite.config.ts (2 hunks)
  • packages/router-core/src/path.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/router-core/src/path.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • e2e/react-start/basic/tests/utils/isSpaMode.ts
  • e2e/react-start/basic/tests/navigation.spec.ts
  • e2e/react-start/basic/tests/search-params.spec.ts
  • e2e/react-start/basic/src/routes/users.tsx
  • e2e/react-start/basic/src/routes/redirect/index.tsx
  • e2e/react-start/basic/src/routes/_layout/_layout-2.tsx
  • e2e/react-start/basic/src/routes/index.tsx
  • e2e/react-start/basic/src/routes/_layout/_layout-2/layout-a.tsx
  • e2e/react-start/basic/src/routes/search-params/route.tsx
  • e2e/react-start/basic/src/routes/search-params/index.tsx
  • e2e/react-start/basic/src/routes/api.users.ts
  • e2e/react-start/basic/src/routes/posts.index.tsx
  • e2e/react-start/basic/src/routes/scripts.tsx
  • e2e/react-start/basic/src/routes/api/users.$id.ts
  • e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-useServerFn.tsx
  • e2e/react-start/basic/src/routes/posts_.$postId.deep.tsx
  • e2e/react-start/basic/src/routes/_layout.tsx
  • e2e/react-start/basic/src/routes/__root.tsx
  • e2e/react-start/basic/src/routes/redirect/$target/index.tsx
  • e2e/react-start/basic/playwright.config.ts
  • e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-loader.tsx
  • e2e/react-start/basic/src/routes/대한민국.tsx
  • e2e/react-start/basic/src/routes/_layout/_layout-2/layout-b.tsx
  • e2e/react-start/basic/src/routes/not-found/index.tsx
  • e2e/react-start/basic/vite.config.ts
  • e2e/react-start/basic/src/routes/links.tsx
  • e2e/react-start/basic/tests/redirect.spec.ts
  • e2e/react-start/basic/src/routes/foo/$bar/$qux/_here/index.tsx
  • e2e/react-start/basic/src/routes/redirect/$target.tsx
  • e2e/react-start/basic/src/routes/inline-scripts.tsx
  • e2e/react-start/basic/src/routes/foo/$bar/$qux/_here.tsx
  • e2e/react-start/basic/src/routes/not-found/via-beforeLoad.tsx
  • e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx
  • e2e/react-start/basic/src/routes/redirect/$target/via-beforeLoad.tsx
  • e2e/react-start/basic/src/routes/redirect/$target/serverFn/index.tsx
  • e2e/react-start/basic/src/routes/search-params/default.tsx
  • e2e/react-start/basic/src/routes/stream.tsx
  • e2e/react-start/basic/src/routes/deferred.tsx
  • e2e/react-start/basic/src/routes/not-found/route.tsx
  • e2e/react-start/basic/src/routes/not-found/via-loader.tsx
  • e2e/react-start/basic/src/routes/users.$userId.tsx
  • e2e/react-start/basic/src/routes/posts.$postId.tsx
  • e2e/react-start/basic/src/routes/redirect/$target/via-loader.tsx
  • e2e/react-start/basic/src/routes/users.index.tsx
  • e2e/react-start/basic/src/routes/search-params/loader-throws-redirect.tsx
  • e2e/react-start/basic/src/routes/posts.tsx
  • e2e/react-start/basic/src/routeTree.gen.ts
e2e/**

📄 CodeRabbit inference engine (AGENTS.md)

Store end-to-end tests under the e2e/ directory

Files:

  • e2e/react-start/basic/tests/utils/isSpaMode.ts
  • e2e/react-start/basic/package.json
  • e2e/react-start/basic/tests/navigation.spec.ts
  • e2e/react-start/basic/tests/search-params.spec.ts
  • e2e/react-start/basic/src/routes/users.tsx
  • e2e/react-start/basic/src/routes/redirect/index.tsx
  • e2e/react-start/basic/src/routes/_layout/_layout-2.tsx
  • e2e/react-start/basic/src/routes/index.tsx
  • e2e/react-start/basic/src/routes/_layout/_layout-2/layout-a.tsx
  • e2e/react-start/basic/src/routes/search-params/route.tsx
  • e2e/react-start/basic/src/routes/search-params/index.tsx
  • e2e/react-start/basic/src/routes/api.users.ts
  • e2e/react-start/basic/src/routes/posts.index.tsx
  • e2e/react-start/basic/src/routes/scripts.tsx
  • e2e/react-start/basic/src/routes/api/users.$id.ts
  • e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-useServerFn.tsx
  • e2e/react-start/basic/src/routes/posts_.$postId.deep.tsx
  • e2e/react-start/basic/src/routes/_layout.tsx
  • e2e/react-start/basic/src/routes/__root.tsx
  • e2e/react-start/basic/src/routes/redirect/$target/index.tsx
  • e2e/react-start/basic/playwright.config.ts
  • e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-loader.tsx
  • e2e/react-start/basic/src/routes/대한민국.tsx
  • e2e/react-start/basic/src/routes/_layout/_layout-2/layout-b.tsx
  • e2e/react-start/basic/src/routes/not-found/index.tsx
  • e2e/react-start/basic/vite.config.ts
  • e2e/react-start/basic/src/routes/links.tsx
  • e2e/react-start/basic/tests/redirect.spec.ts
  • e2e/react-start/basic/src/routes/foo/$bar/$qux/_here/index.tsx
  • e2e/react-start/basic/src/routes/redirect/$target.tsx
  • e2e/react-start/basic/src/routes/inline-scripts.tsx
  • e2e/react-start/basic/src/routes/foo/$bar/$qux/_here.tsx
  • e2e/react-start/basic/src/routes/not-found/via-beforeLoad.tsx
  • e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx
  • e2e/react-start/basic/src/routes/redirect/$target/via-beforeLoad.tsx
  • e2e/react-start/basic/src/routes/redirect/$target/serverFn/index.tsx
  • e2e/react-start/basic/src/routes/search-params/default.tsx
  • e2e/react-start/basic/src/routes/stream.tsx
  • e2e/react-start/basic/src/routes/deferred.tsx
  • e2e/react-start/basic/src/routes/not-found/route.tsx
  • e2e/react-start/basic/src/routes/not-found/via-loader.tsx
  • e2e/react-start/basic/src/routes/users.$userId.tsx
  • e2e/react-start/basic/src/routes/posts.$postId.tsx
  • e2e/react-start/basic/src/routes/redirect/$target/via-loader.tsx
  • e2e/react-start/basic/src/routes/users.index.tsx
  • e2e/react-start/basic/src/routes/search-params/loader-throws-redirect.tsx
  • e2e/react-start/basic/src/routes/posts.tsx
  • e2e/react-start/basic/src/routeTree.gen.ts
**/package.json

📄 CodeRabbit inference engine (AGENTS.md)

Use workspace:* protocol for internal dependencies in package.json files

Files:

  • e2e/react-start/basic/package.json
**/src/routes/**

📄 CodeRabbit inference engine (AGENTS.md)

Place file-based routes under src/routes/ directories

Files:

  • e2e/react-start/basic/src/routes/users.tsx
  • e2e/react-start/basic/src/routes/redirect/index.tsx
  • e2e/react-start/basic/src/routes/_layout/_layout-2.tsx
  • e2e/react-start/basic/src/routes/index.tsx
  • e2e/react-start/basic/src/routes/_layout/_layout-2/layout-a.tsx
  • e2e/react-start/basic/src/routes/search-params/route.tsx
  • e2e/react-start/basic/src/routes/search-params/index.tsx
  • e2e/react-start/basic/src/routes/api.users.ts
  • e2e/react-start/basic/src/routes/posts.index.tsx
  • e2e/react-start/basic/src/routes/scripts.tsx
  • e2e/react-start/basic/src/routes/api/users.$id.ts
  • e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-useServerFn.tsx
  • e2e/react-start/basic/src/routes/posts_.$postId.deep.tsx
  • e2e/react-start/basic/src/routes/_layout.tsx
  • e2e/react-start/basic/src/routes/__root.tsx
  • e2e/react-start/basic/src/routes/redirect/$target/index.tsx
  • e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-loader.tsx
  • e2e/react-start/basic/src/routes/대한민국.tsx
  • e2e/react-start/basic/src/routes/_layout/_layout-2/layout-b.tsx
  • e2e/react-start/basic/src/routes/not-found/index.tsx
  • e2e/react-start/basic/src/routes/links.tsx
  • e2e/react-start/basic/src/routes/foo/$bar/$qux/_here/index.tsx
  • e2e/react-start/basic/src/routes/redirect/$target.tsx
  • e2e/react-start/basic/src/routes/inline-scripts.tsx
  • e2e/react-start/basic/src/routes/foo/$bar/$qux/_here.tsx
  • e2e/react-start/basic/src/routes/not-found/via-beforeLoad.tsx
  • e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx
  • e2e/react-start/basic/src/routes/redirect/$target/via-beforeLoad.tsx
  • e2e/react-start/basic/src/routes/redirect/$target/serverFn/index.tsx
  • e2e/react-start/basic/src/routes/search-params/default.tsx
  • e2e/react-start/basic/src/routes/stream.tsx
  • e2e/react-start/basic/src/routes/deferred.tsx
  • e2e/react-start/basic/src/routes/not-found/route.tsx
  • e2e/react-start/basic/src/routes/not-found/via-loader.tsx
  • e2e/react-start/basic/src/routes/users.$userId.tsx
  • e2e/react-start/basic/src/routes/posts.$postId.tsx
  • e2e/react-start/basic/src/routes/redirect/$target/via-loader.tsx
  • e2e/react-start/basic/src/routes/users.index.tsx
  • e2e/react-start/basic/src/routes/search-params/loader-throws-redirect.tsx
  • e2e/react-start/basic/src/routes/posts.tsx
🧠 Learnings (2)
📓 Common learnings
Learnt from: nlynzaad
PR: TanStack/router#5182
File: e2e/react-router/basic-file-based/src/routes/non-nested/named/$baz_.bar.tsx:3-5
Timestamp: 2025-09-22T00:56:49.237Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments (e.g., `$baz_` becomes `baz` in generated types) but should be preserved in base path segments. This is the correct behavior as of the fix in PR #5182.
Learnt from: nlynzaad
PR: TanStack/router#5182
File: e2e/react-router/basic-file-based/tests/non-nested-paths.spec.ts:167-172
Timestamp: 2025-09-22T00:56:53.426Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments during path parsing, but preserved in base path segments. This is the expected behavior implemented in PR #5182.
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{react-router,solid-router}/** : Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/

Applied to files:

  • e2e/react-start/basic/src/routes/__root.tsx
  • e2e/react-start/basic/src/routes/foo/$bar/$qux/_here/index.tsx
🧬 Code graph analysis (17)
e2e/react-start/basic/tests/navigation.spec.ts (1)
e2e/react-start/basic/tests/utils/isSpaMode.ts (1)
  • isSpaMode (1-1)
e2e/react-start/basic/tests/search-params.spec.ts (1)
e2e/react-start/basic/tests/utils/isSpaMode.ts (1)
  • isSpaMode (1-1)
e2e/react-start/basic/src/routes/search-params/route.tsx (2)
e2e/react-start/basic/src/routes/search-params/default.tsx (1)
  • Route (4-28)
e2e/react-start/basic/src/routes/search-params/index.tsx (1)
  • Route (3-5)
e2e/react-start/basic/src/routes/search-params/index.tsx (3)
e2e/react-router/basic-file-based/src/routes/search-params/index.tsx (1)
  • Route (3-5)
e2e/solid-router/basic-file-based/src/routes/search-params/index.tsx (1)
  • Route (3-5)
e2e/solid-start/basic/src/routes/search-params/index.tsx (1)
  • Route (3-5)
e2e/react-start/basic/src/routes/api.users.ts (1)
e2e/react-start/basic/src/routes/api/users.$id.ts (1)
  • Route (12-32)
e2e/react-start/basic/src/routes/scripts.tsx (1)
e2e/solid-start/basic/src/routes/scripts.tsx (1)
  • Route (5-19)
e2e/react-start/basic/src/routes/api/users.$id.ts (1)
e2e/react-start/basic/src/routes/api.users.ts (1)
  • Route (13-28)
e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-useServerFn.tsx (1)
e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx (1)
  • Route (4-12)
e2e/react-start/basic/playwright.config.ts (2)
e2e/e2e-utils/src/derivePort.ts (2)
  • getTestServerPort (25-27)
  • getDummyServerPort (21-23)
e2e/react-start/basic/tests/utils/isSpaMode.ts (1)
  • isSpaMode (1-1)
e2e/react-start/basic/vite.config.ts (1)
e2e/react-start/basic/tests/utils/isSpaMode.ts (1)
  • isSpaMode (1-1)
e2e/react-start/basic/tests/redirect.spec.ts (1)
e2e/react-start/basic/tests/utils/isSpaMode.ts (1)
  • isSpaMode (1-1)
e2e/react-start/basic/src/routes/inline-scripts.tsx (2)
e2e/react-start/basic/src/routes/__root.tsx (1)
  • Route (16-75)
e2e/solid-start/basic/src/routes/inline-scripts.tsx (1)
  • Route (3-18)
e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx (1)
e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-useServerFn.tsx (1)
  • Route (4-18)
e2e/react-start/basic/src/routes/redirect/$target/via-beforeLoad.tsx (3)
e2e/react-start/basic/src/routes/redirect/$target/via-loader.tsx (1)
  • Route (3-17)
e2e/solid-start/basic/src/routes/redirect/$target/via-beforeLoad.tsx (1)
  • Route (3-16)
e2e/solid-start/basic/src/routes/redirect/$target/via-loader.tsx (1)
  • Route (3-17)
e2e/react-start/basic/src/routes/search-params/default.tsx (2)
e2e/react-start/basic/src/routes/search-params/index.tsx (1)
  • Route (3-5)
e2e/react-router/basic-file-based/src/routes/search-params/default.tsx (1)
  • Route (4-28)
e2e/react-start/basic/src/routes/redirect/$target/via-loader.tsx (2)
e2e/react-start/basic/src/routes/redirect/$target/via-beforeLoad.tsx (1)
  • Route (3-16)
e2e/solid-start/basic/src/routes/redirect/$target/via-loader.tsx (1)
  • Route (3-17)
e2e/react-start/basic/src/routes/search-params/loader-throws-redirect.tsx (2)
e2e/react-start/basic/src/routes/search-params/route.tsx (1)
  • Route (3-8)
e2e/solid-start/basic/src/routes/search-params/loader-throws-redirect.tsx (1)
  • Route (4-26)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Preview
  • GitHub Check: Test
🔇 Additional comments (31)
e2e/react-start/basic/src/routes/__root.tsx (1)

181-183: LGTM: Suspense wrapper matches lazy devtools usage

Wrapping the lazily-loaded RouterDevtools in React.Suspense avoids runtime warnings when the lazy import resolves later, while keeping production builds unaffected with the null fallback. Nicely done.

e2e/react-start/basic/src/routes/api/users.$id.ts (1)

12-32: Looks good

Path-first invocation and server handler wiring stay intact; no issues spotted.

e2e/react-start/basic/src/routes/posts_.$postId.deep.tsx (1)

5-13: Nice upgrade

Path binding stays accurate and the dedicated error wrapper keeps the UX consistent.

e2e/react-start/basic/src/routes/users.index.tsx (1)

3-5: Route path literal is correct
The generator’s FileRoutesByPath includes /users/ (not /users) for the index route, so createFileRoute('/users/') already matches.

e2e/react-start/basic/src/routes/redirect/$target/index.tsx (1)

3-76: Verify trailing slash semantics
Ensure the generated route path for /redirect/$target/ includes the trailing slash; if it’s emitted as /redirect/$target (no slash), Route.fullPath–based links will misbehave. Inspect your route manifest (e.g. routeTree.gen.ts) in the build output.

e2e/react-start/basic/src/routes/대한민국.tsx (1)

3-5: Explicit path keeps the Hangul segment intact

Switching to the path-first createFileRoute('/대한민국') call locks in the intended Unicode segment so the new root parsing change can’t strip it. Looks solid.

e2e/react-start/basic/src/routes/search-params/loader-throws-redirect.tsx (1)

4-26: Path-first binding lines up with the redirect target

Explicitly registering /search-params/loader-throws-redirect ensures the loader’s redirect target stays consistent after the root-id parsing tweak. The rest of the loader/search wiring still behaves as before.

e2e/react-start/basic/src/routes/redirect/$target/via-loader.tsx (1)

3-17: Route id now mirrors the loader redirect cases

The added path literal /redirect/$target/via-loader keeps the loader’s internal redirects aligned with the generated route id, so the SPA mode regression is avoided.

e2e/react-start/basic/src/routes/posts.index.tsx (1)

3-5: Index route retains its trailing-slash identity

Locking the index route to /posts/ preserves the expected trailing-slash semantics for the posts overview after the root-route handling change. Looks correct.

e2e/react-start/basic/src/routes/scripts.tsx (1)

5-19: Explicit /scripts route id matches head/component wiring

The head metadata and dev/prod script toggles now sit on a stable /scripts id, which should keep SPA navigation behaving. Nice.

e2e/react-start/basic/src/routes/posts.$postId.tsx (2)

7-14: Path-first route + notFoundComponent look correct

Switch to createFileRoute('/posts/$postId') and adding notFoundComponent aligns with the PR goals and improves UX for missing posts.


16-18: No external imports of PostErrorComponent found
Removed export in routes/posts.$postId.tsx doesn’t break any imports.

e2e/react-start/basic/src/routes/foo/$bar/$qux/_here.tsx (1)

3-5: Good: path-first route, underscore segment preserved

createFileRoute('/foo/$bar/$qux/_here') is consistent with preserving underscores in base path segments. Parent layout without trailing slash pairs well with the index child route.

Based on learnings

e2e/react-start/basic/src/routes/foo/$bar/$qux/_here/index.tsx (2)

1-1: LGTM

Import of createFileRoute is correct.


3-6: Index route path is consistent

Using a trailing slash in createFileRoute('/foo/$bar/$qux/_here/') correctly denotes the index route under the _here segment.

e2e/react-start/basic/src/routes/users.tsx (1)

1-1: LGTM

Import changes are aligned with the path-first route pattern.

e2e/react-start/basic/src/routes/links.tsx (1)

3-47: Path-first route binding looks solid

Binding createFileRoute with /links keeps the file path and route id aligned with the updated parsing logic, and Route.useNavigate still resolves correctly. Nice job.

e2e/react-start/basic/src/routes/inline-scripts.tsx (1)

3-18: Inline-scripts route update aligns with the new API

Supplying the explicit /inline-scripts path keeps this route consistent with the other SPA-mode fixtures and avoids ambiguity in the generated route ids. Looks good.

e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-loader.tsx (1)

4-12: Dynamic redirect route remains intact

Passing the explicit /redirect/$target/serverFn/via-loader path preserves the dynamic segment while keeping the loader logic unchanged. This should exercise the trailing-underscore parsing fix as intended.

e2e/react-start/basic/src/routes/not-found/via-beforeLoad.tsx (1)

3-15: Not-found guard stays effective

The explicit /not-found/via-beforeLoad path keeps this route’s notFound guard wired up while matching the updated path parser expectations. All good.

e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-useServerFn.tsx (1)

4-6: Path-first route binding looks correct

The bound path matches the file-system location and preserves the existing route behavior. Nice cleanup.

e2e/react-start/basic/src/routes/redirect/$target.tsx (1)

4-21: Dynamic segment routing stays intact

Using createFileRoute('/redirect/$target') keeps the param parsing and search middleware wired exactly as before while aligning with the new API shape.

e2e/react-start/basic/src/routes/redirect/$target/via-beforeLoad.tsx (1)

3-16: SPA redirect flow remains consistent

The path string mirrors the file-based segment, so the beforeLoad redirect logic continues to fire for both internal and external targets.

e2e/react-start/basic/src/routes/_layout/_layout-2/layout-a.tsx (1)

3-5: Layout route registration looks good

Binding /_layout/_layout-2/layout-a up front matches the generated layout structure without altering the component output.

e2e/react-start/basic/src/routes/search-params/index.tsx (1)

3-5: Index route path stays canonical

The trailing-slash path keeps the index route discoverable and consistent with other platform examples.

e2e/react-start/basic/src/routes/_layout/_layout-2/layout-b.tsx (1)

1-5: Path binding matches the layout hierarchy

Providing the explicit /_layout/_layout-2/layout-b path keeps this layout’s route id aligned with the file-system hierarchy while still letting the new root-id logic handle underscore stripping correctly. Looks solid.

e2e/react-start/basic/src/routes/search-params/route.tsx (1)

1-8: SPA context route wired correctly

The switch to createFileRoute('/search-params') keeps the parent context loader intact and ensures the root-id logic can honor the segment. All good here.

e2e/react-start/basic/src/routes/redirect/$target/serverFn/index.tsx (1)

1-86: Dynamic serverFn route path looks correct

The path-first invocation preserves the $target parameter and trailing slash for the index route, so the generated links will continue to resolve as expected.

e2e/react-start/basic/src/routes/not-found/via-loader.tsx (1)

1-15: Not-found loader route aligned

Explicitly binding /not-found/via-loader keeps the loader + notFound pairing working with the updated parser. No issues spotted.

e2e/react-start/basic/src/routes/search-params/default.tsx (1)

4-28: Default search params route remains consistent

The new path binding mirrors the folder structure and keeps validation/loader logic untouched. Implementation looks good.

e2e/react-start/basic/src/routeTree.gen.ts (1)

550-555: /links preLoaderRoute now points at the right module

Good catch—wiring /links to LinksRouteImport keeps the generated typings aligned with the actual route definition and avoids the inline-scripts mismatch. Looks solid.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 373681f and ebb9887.

📒 Files selected for processing (4)
  • e2e/solid-start/basic/package.json (1 hunks)
  • e2e/solid-start/basic/playwright.config.ts (2 hunks)
  • e2e/solid-start/basic/tests/navigation.spec.ts (2 hunks)
  • e2e/solid-start/basic/tests/utils/isSpaMode.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • e2e/solid-start/basic/tests/utils/isSpaMode.ts
  • e2e/solid-start/basic/playwright.config.ts
  • e2e/solid-start/basic/tests/navigation.spec.ts
e2e/**

📄 CodeRabbit inference engine (AGENTS.md)

Store end-to-end tests under the e2e/ directory

Files:

  • e2e/solid-start/basic/tests/utils/isSpaMode.ts
  • e2e/solid-start/basic/playwright.config.ts
  • e2e/solid-start/basic/tests/navigation.spec.ts
  • e2e/solid-start/basic/package.json
**/package.json

📄 CodeRabbit inference engine (AGENTS.md)

Use workspace:* protocol for internal dependencies in package.json files

Files:

  • e2e/solid-start/basic/package.json
🧠 Learnings (1)
📓 Common learnings
Learnt from: nlynzaad
PR: TanStack/router#5182
File: e2e/react-router/basic-file-based/src/routes/non-nested/named/$baz_.bar.tsx:3-5
Timestamp: 2025-09-22T00:56:49.237Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments (e.g., `$baz_` becomes `baz` in generated types) but should be preserved in base path segments. This is the correct behavior as of the fix in PR #5182.
Learnt from: nlynzaad
PR: TanStack/router#5182
File: e2e/react-router/basic-file-based/tests/non-nested-paths.spec.ts:167-172
Timestamp: 2025-09-22T00:56:53.426Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments during path parsing, but preserved in base path segments. This is the expected behavior implemented in PR #5182.
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/router-core/** : Keep framework-agnostic core router logic in packages/router-core/
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{router-cli,router-generator,router-plugin,virtual-file-routes}/** : Keep CLI, generators, bundler plugins, and virtual file routing utilities in their dedicated tooling package directories
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{react-router,solid-router}/** : Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{router-devtools,*-router-devtools}/** : Keep router devtools packages in packages/router-devtools/ and packages/*-router-devtools/
🧬 Code graph analysis (2)
e2e/solid-start/basic/playwright.config.ts (2)
e2e/e2e-utils/src/derivePort.ts (2)
  • getTestServerPort (25-27)
  • getDummyServerPort (21-23)
e2e/solid-start/basic/tests/utils/isSpaMode.ts (1)
  • isSpaMode (1-1)
e2e/solid-start/basic/tests/navigation.spec.ts (1)
e2e/solid-start/basic/tests/utils/isSpaMode.ts (1)
  • isSpaMode (1-1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test

Comment on lines +12 to +13
const spaModeCommand = `pnpm build && pnpm dev:e2e --port=${PORT}`
const ssrModeCommand = `pnpm build && pnpm start`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix SPA webServer command argument forwarding.

pnpm dev:e2e --port=${PORT} treats --port as a pnpm CLI flag, so pnpm bails out with “Unknown option '--port'” and the Playwright web server never starts in SPA mode. Forward the flag with -- (or drop it and rely on the PORT env you already set) so the dev server actually launches.

-const spaModeCommand = `pnpm build && pnpm dev:e2e --port=${PORT}`
+const spaModeCommand = `pnpm build && pnpm dev:e2e -- --port=${PORT}`
🤖 Prompt for AI Agents
In e2e/solid-start/basic/playwright.config.ts around lines 12 to 13, the SPA
webServer command forwards --port directly to pnpm which treats it as a pnpm CLI
flag and errors; change the SPA command so the port flag is forwarded to the
script (either add a double-dash separator before the flag, e.g. use pnpm
dev:e2e -- --port=${PORT}, or remove the --port argument entirely and rely on
the existing PORT env variable) so the dev server actually starts in SPA mode.

@schiller-manuel schiller-manuel merged commit 16eacb3 into main Sep 27, 2025
6 checks passed
@schiller-manuel schiller-manuel deleted the #5171-consider-root-route branch September 27, 2025 22:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SPA mode configuration causes app to hang on loading skeleton with Bun + Vite 7

3 participants