Skip to content

Conversation

schiller-manuel
Copy link
Contributor

@schiller-manuel schiller-manuel commented Oct 2, 2025

Summary by CodeRabbit

  • New Features

    • Router supports a runtime basepath option applied during hydration; manifests and prerendering use configured basepaths. Vite base is respected for public paths.
  • Refactor

    • Public API surface simplified (rewriteBasepath removed); manifest retrieval call simplified to no-arg form. Client/server RPC and URL handling unified to rely on environment-driven bases.
  • Bug Fixes

    • More consistent asset and navigation URL resolution under custom base paths.
  • Chores

    • Environment vars updated: TSS_APP_BASE removed; TSS_ROUTER_BASEPATH and TSS_SERVER_FN_BASE required.

Copy link
Contributor

coderabbitai bot commented Oct 2, 2025

Walkthrough

Removes the exported rewriteBasepath and migrates basepath handling into runtime/router options and plugin config. Updates Vite/Nitro/start plugins, client/server RPC and manifest flows, tests, and example Vite base setting to use the new basepath/environment variables and normalized config plumbing.

Changes

Cohort / File(s) Summary
Public API re-exports
packages/react-router/src/index.tsx, packages/solid-router/src/index.tsx, packages/router-core/src/index.ts
Stop re-exporting rewriteBasepath; continue exporting composeRewrites.
Router core logic
packages/router-core/src/router.ts
Move basepath handling into RouterCore: detect basepath/rewrite changes, recompute/composite rewrites, update store/location without resetting history; remove backward-compat doc and inline basepath hack.
Router tests
packages/react-router/tests/router.test.tsx
Replace rewriteBasepath usage with basepath router option; parameterize tests across basepath forms and adjust scenarios.
Client runtime
packages/start-client-core/src/client/hydrateStart.ts, packages/start-client-core/src/client-rpc/createClientRpc.ts
Apply router changes via router.update including basepath and serialization adapters; simplify client RPC URL to process.env.TSS_SERVER_FN_BASE + functionId.
Start plugin core & schema
packages/start-plugin-core/src/plugin.ts, .../start-manifest-plugin/plugin.ts, .../schema.ts, .../prerender.ts, .../global.d.ts
Add viteAppBase to resolved config, derive router.basepath, compute/inject TSS_SERVER_FN_BASE and TSS_ROUTER_BASEPATH, extend schema with router.basepath, replace TSS_APP_BASE usage with router basepath, add getConfig to start-manifest plugin.
Start server core
packages/start-server-core/src/createStartHandler.ts, .../createServerRpc.ts, .../request-response.ts, .../router-manifest.ts, .../server-functions-handler.ts, .../global.d.ts
Switch to TSS_ROUTER_BASEPATH and require TSS_SERVER_FN_BASE; simplify RPC URL construction; remove h3_toResponse conversion; getStartManifest() no longer takes args; lazy regex for server-fn parsing; add router.update basepath on init.
Nitro V2 Vite plugin
packages/nitro-v2-vite-plugin/src/index.ts
Capture Vite ResolvedConfig via configResolved, detect full URLs, and set top-level Nitro baseURL from Vite base when relative.
Example config
examples/react/start-basic/vite.config.ts
Set Vite base to /foo/.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor DevBrowser as Browser
  participant Vite as Vite
  participant StartPlugin as Start Plugin
  participant ClientApp as Client App
  participant Router as RouterCore

  Note over Vite,StartPlugin: build/dev config resolution
  Vite->>StartPlugin: configResolved(config)
  StartPlugin->>StartPlugin: derive viteAppBase & router.basepath
  StartPlugin-->>ClientApp: define env TSS_ROUTER_BASEPATH, TSS_SERVER_FN_BASE

  Note over ClientApp,Router: hydration/update
  ClientApp->>Router: router.update({ basepath: TSS_ROUTER_BASEPATH, ... })
  Router->>Router: compute basepath rewrite, compose with rewrites
  Router-->>ClientApp: normalized location
  ClientApp-->>DevBrowser: render with basepath-aware routes
Loading
sequenceDiagram
  autonumber
  participant Client as Browser
  participant Server as Start Server
  participant Router as RouterCore
  participant Manifest as Manifest Loader
  participant SrvFn as Server Functions Handler

  Client->>Server: HTTP request
  alt Server Function URL
    Server->>SrvFn: handleServerAction(req)
    SrvFn->>SrvFn: lazy-init regex from TSS_SERVER_FN_BASE
    SrvFn-->>Server: invoke server function
    Server-->>Client: function response
  else App route
    Server->>Router: router.update({ basepath: TSS_ROUTER_BASEPATH, ... })
    Server->>Manifest: getStartManifest()
    Manifest-->>Server: startManifest
    Server-->>Client: HTML/Response (returned directly)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested reviewers

  • SeanCassiere

Poem

"I hop through bases, tidy and spry,
No rewrites exported—just clean sky.
Env flags whisper where routes should go,
From vite to server, the pathlets flow.
A rabbit nods—paths aligned, hip-hop, let's go!" 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The title “fix: basepath” is too generic to convey the primary change of introducing a basepath option and removing rewriteBasepath exports across multiple packages, so it does not clearly inform reviewers about the specific scope and intent of this pull request. Please revise the title to clearly summarize the main update, for example “Add basepath option and remove rewriteBasepath export across router packages,” so that teammates can immediately grasp the purpose and impact of this change.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-basepath

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f71f75 and 3e22519.

📒 Files selected for processing (1)
  • packages/start-plugin-core/src/plugin.ts (6 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • packages/start-plugin-core/src/plugin.ts
packages/{*-start,start-*}/**

📄 CodeRabbit inference engine (AGENTS.md)

Name and place Start framework packages under packages/-start/ or packages/start-/

Files:

  • packages/start-plugin-core/src/plugin.ts
🧬 Code graph analysis (1)
packages/start-plugin-core/src/plugin.ts (1)
packages/start-plugin-core/src/schema.ts (1)
  • TanStackStartOutputConfig (202-202)
⏰ 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 (6)
packages/start-plugin-core/src/plugin.ts (6)

1-1: LGTM!

The joinPaths import is correctly added and used throughout the file for consistent path manipulation.


52-59: LGTM!

The isFullUrl utility correctly validates full URLs using the URL constructor. This standard pattern is appropriate for distinguishing between full URLs and relative paths.


44-44: LGTM!

The viteAppBase property is correctly added to the interface and initialized. This aligns with the PR's objective to migrate basepath handling into config.

Also applies to: 70-70


139-144: LGTM!

The TSS_SERVER_FN_BASE computation correctly joins the router basepath with the server functions base path using joinPaths, which will handle normalization and edge cases appropriately.


293-295: LGTM!

The environment variable replacements correctly use the newly computed TSS_SERVER_FN_BASE and TSS_ROUTER_BASEPATH values. The propagation of getConfig to startManifestPlugin enables proper access to the resolved configuration including viteAppBase.

Also applies to: 353-353


117-137: Ensure consistent root basepath
When viteAppBase is /, the current logic strips slashes and yields router.basepath = ''. Client‐side routing will use '', but createStartHandler on the server falls back to '/'. Confirm this discrepancy is intended and that prerendering, SSR, and client routing all handle an empty string correctly—or change the derivation to default to '/' for root.


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

Copy link

nx-cloud bot commented Oct 2, 2025

View your CI Pipeline Execution ↗ for commit 7b1b684

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

☁️ Nx Cloud last updated this comment at 2025-10-03 12:56:53 UTC

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/start-plugin-core/src/prerender.ts (1)

141-185: Restore leading slash before calling withBase.

startConfig.router.basepath is normalized elsewhere to strip leading/trailing slashes, so values like 'foo' reach here. Passing that directly to withBase produces 'foo/...', which makes localFetch build bad URLs (http://localhostfoo/...) and causes prerender fetches/writes to miss the intended base path.

Add back the leading slash before composing URLs, e.g.:

-      const routerBasePath = startConfig.router.basepath || '/'
+      const routerBasePath = joinURL('/', startConfig.router.basepath ?? '')
🧹 Nitpick comments (1)
packages/start-server-core/src/createStartHandler.ts (1)

174-174: Direct env var access without fallback.

Unlike line 57 which provides a fallback for TSS_ROUTER_BASEPATH, this line directly accesses process.env.TSS_SERVER_FN_BASE. If the env var is not set, href.startsWith(undefined) will be called, which converts undefined to "undefined" and performs a string comparison that will always be false.

While the current behavior is safe (won't crash), it's less clear than an explicit check. Consider adding a constant with validation:

const SERVER_FN_BASE = process.env.TSS_SERVER_FN_BASE
if (!SERVER_FN_BASE) {
  throw new Error('TSS_SERVER_FN_BASE environment variable is required')
}

// Later at line 174:
if (href.startsWith(SERVER_FN_BASE)) {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ac35dd2 and 6f50f8e.

📒 Files selected for processing (19)
  • examples/react/start-basic/vite.config.ts (1 hunks)
  • packages/react-router/src/index.tsx (1 hunks)
  • packages/react-router/tests/router.test.tsx (11 hunks)
  • packages/router-core/src/index.ts (0 hunks)
  • packages/router-core/src/router.ts (3 hunks)
  • packages/solid-router/src/index.tsx (1 hunks)
  • packages/start-client-core/src/client-rpc/createClientRpc.ts (1 hunks)
  • packages/start-client-core/src/client/hydrateStart.ts (1 hunks)
  • packages/start-plugin-core/src/global.d.ts (0 hunks)
  • packages/start-plugin-core/src/plugin.ts (6 hunks)
  • packages/start-plugin-core/src/prerender.ts (3 hunks)
  • packages/start-plugin-core/src/schema.ts (1 hunks)
  • packages/start-plugin-core/src/start-manifest-plugin/plugin.ts (6 hunks)
  • packages/start-server-core/src/createServerRpc.ts (1 hunks)
  • packages/start-server-core/src/createStartHandler.ts (4 hunks)
  • packages/start-server-core/src/global.d.ts (1 hunks)
  • packages/start-server-core/src/request-response.ts (1 hunks)
  • packages/start-server-core/src/router-manifest.ts (1 hunks)
  • packages/start-server-core/src/server-functions-handler.ts (2 hunks)
💤 Files with no reviewable changes (2)
  • packages/router-core/src/index.ts
  • packages/start-plugin-core/src/global.d.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • packages/start-server-core/src/router-manifest.ts
  • examples/react/start-basic/vite.config.ts
  • packages/react-router/src/index.tsx
  • packages/start-server-core/src/global.d.ts
  • packages/start-server-core/src/createServerRpc.ts
  • packages/start-client-core/src/client/hydrateStart.ts
  • packages/start-plugin-core/src/plugin.ts
  • packages/start-server-core/src/createStartHandler.ts
  • packages/start-plugin-core/src/start-manifest-plugin/plugin.ts
  • packages/start-server-core/src/request-response.ts
  • packages/start-plugin-core/src/prerender.ts
  • packages/start-plugin-core/src/schema.ts
  • packages/react-router/tests/router.test.tsx
  • packages/router-core/src/router.ts
  • packages/solid-router/src/index.tsx
  • packages/start-server-core/src/server-functions-handler.ts
  • packages/start-client-core/src/client-rpc/createClientRpc.ts
packages/{*-start,start-*}/**

📄 CodeRabbit inference engine (AGENTS.md)

Name and place Start framework packages under packages/-start/ or packages/start-/

Files:

  • packages/start-server-core/src/router-manifest.ts
  • packages/start-server-core/src/global.d.ts
  • packages/start-server-core/src/createServerRpc.ts
  • packages/start-client-core/src/client/hydrateStart.ts
  • packages/start-plugin-core/src/plugin.ts
  • packages/start-server-core/src/createStartHandler.ts
  • packages/start-plugin-core/src/start-manifest-plugin/plugin.ts
  • packages/start-server-core/src/request-response.ts
  • packages/start-plugin-core/src/prerender.ts
  • packages/start-plugin-core/src/schema.ts
  • packages/start-server-core/src/server-functions-handler.ts
  • packages/start-client-core/src/client-rpc/createClientRpc.ts
examples/{react,solid}/**

📄 CodeRabbit inference engine (AGENTS.md)

Keep example applications under examples/react/ and examples/solid/

Files:

  • examples/react/start-basic/vite.config.ts
packages/{react-router,solid-router}/**

📄 CodeRabbit inference engine (AGENTS.md)

Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/

Files:

  • packages/react-router/src/index.tsx
  • packages/react-router/tests/router.test.tsx
  • packages/solid-router/src/index.tsx
packages/router-core/**

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • packages/router-core/src/router.ts
🧬 Code graph analysis (4)
packages/start-plugin-core/src/plugin.ts (1)
packages/router-core/src/index.ts (2)
  • trimPathRight (98-98)
  • joinPaths (95-95)
packages/start-server-core/src/createStartHandler.ts (1)
packages/start-server-core/src/router-manifest.ts (1)
  • getStartManifest (11-59)
packages/start-plugin-core/src/start-manifest-plugin/plugin.ts (1)
packages/start-plugin-core/src/plugin.ts (1)
  • GetConfigFn (47-50)
packages/router-core/src/router.ts (1)
packages/router-core/src/rewrite.ts (1)
  • rewriteBasepath (21-55)
🪛 ast-grep (0.39.5)
packages/start-server-core/src/server-functions-handler.ts

[warning] 27-27: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(${process.env.TSS_SERVER_FN_BASE}([^/?#]+))
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

⏰ 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 (8)
packages/start-server-core/src/request-response.ts (1)

48-59: Remove commented-out h3_toResponse call
The RequestHandler already returns a Response (or Promise<Response>), and there are no remaining toResponse references—delete the // h3_toResponse(response, h3Event) on line 57.

packages/start-plugin-core/src/plugin.ts (1)

103-106: Fix invalid String.replace call.

String.prototype.replace accepts only two arguments, but this call passes three, triggering a type error and breaking the build. Drop the superfluous empty string:

-        startConfig.router.basepath = startConfig.router.basepath.replace(
-          /^\/|\/$/g,
-          '',
-          '',
-        )
+        startConfig.router.basepath = startConfig.router.basepath.replace(
+          /^\/|\/$/g,
+          '',
+        )

Likely an incorrect or invalid review comment.

packages/start-server-core/src/server-functions-handler.ts (2)

27-29: ReDoS warning is a false positive for this pattern.

The static analysis tool flags regexp construction from variable input as a potential ReDoS risk. However, the pattern ${process.env.TSS_SERVER_FN_BASE}([^/?#]+) is safe because:

  1. The interpolated TSS_SERVER_FN_BASE is a build-time/config-time constant, not user input
  2. The capture group [^/?#]+ uses a negated character class that matches efficiently without backtracking
  3. The pattern is bounded by URL structure (no nested quantifiers or alternations)

Based on static analysis hints.


13-13: Lazy initialization pattern is efficient.

The module-level regex with lazy initialization on first call is a good performance optimization, avoiding regex compilation on module load.

Also applies to: 27-29

packages/start-server-core/src/createStartHandler.ts (2)

57-57: Fallback to '/' is good defensive programming.

Despite global.d.ts declaring TSS_ROUTER_BASEPATH as required, providing a fallback to '/' is prudent defensive programming that prevents runtime errors. However, this creates an inconsistency between the type declaration and actual usage.

Consider updating global.d.ts to make TSS_ROUTER_BASEPATH optional to match this defensive usage, or document that the fallback is intentional for backwards compatibility.


211-211: LGTM: Aligns with updated getStartManifest signature.

The call to getStartManifest() without arguments correctly matches the updated signature that removed the basePath parameter.

packages/start-server-core/src/router-manifest.ts (1)

11-11: Breaking change: remove basePath parameter
All internal calls now use getStartManifest() without arguments—verified. Update any external callers accordingly.

packages/start-client-core/src/client-rpc/createClientRpc.ts (1)

5-5: TSS_SERVER_FN_BASE is already normalized with a single trailing slash
The joinPaths([... '/', basepath, serverFns.base, '/' ]) + cleanPath collapse duplicate slashes and guarantee exactly one leading and trailing slash.

Comment on lines +4 to +5
TSS_ROUTER_BASEPATH: string
TSS_SERVER_FN_BASE: string
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Required env vars need runtime validation.

Changing TSS_ROUTER_BASEPATH and TSS_SERVER_FN_BASE from optional to required in the type system doesn't guarantee they're set at runtime. If these aren't provided, process.env.TSS_ROUTER_BASEPATH will be undefined despite the type declaring it as string, leading to potential runtime errors.

Consider adding runtime validation where these are first accessed (e.g., in createStartHandler.ts line 57 and createClientRpc.ts line 5):

const ROUTER_BASEPATH = process.env.TSS_ROUTER_BASEPATH
if (!ROUTER_BASEPATH) {
  throw new Error('TSS_ROUTER_BASEPATH environment variable is required')
}

Similarly for TSS_SERVER_FN_BASE:

const SERVER_FN_BASE = process.env.TSS_SERVER_FN_BASE
if (!SERVER_FN_BASE) {
  throw new Error('TSS_SERVER_FN_BASE environment variable is required')
}
🤖 Prompt for AI Agents
In packages/start-server-core/src/global.d.ts around lines 4 to 5, the env vars
TSS_ROUTER_BASEPATH and TSS_SERVER_FN_BASE were made required in types but lack
runtime checks; add runtime validation at the points they’re first accessed
(e.g., in createStartHandler.ts at ~line 57 and createClientRpc.ts at ~line 5):
read each value from process.env, verify it is a non-empty string, and throw a
clear Error if missing so the runtime matches the type expectations and fails
fast with an actionable message.

Copy link

pkg-pr-new bot commented Oct 2, 2025

More templates

@tanstack/arktype-adapter

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

@tanstack/directive-functions-plugin

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

@tanstack/eslint-plugin-router

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

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/server-functions-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/zod-adapter

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

commit: 7b1b684

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: 0

🧹 Nitpick comments (2)
packages/start-plugin-core/src/prerender.ts (1)

141-141: Hoist routerBasePath outside the loop for efficiency.

The routerBasePath is recomputed for every page inside addCrawlPageTask, but startConfig.router.basepath remains constant throughout the prerender operation. Declare it once in the prerenderPages function scope to avoid redundant computations.

Apply this diff:

   async function prerenderPages({ outputDir }: { outputDir: string }) {
     const seen = new Set<string>()
     const retriesByPath = new Map<string, number>()
     const concurrency = startConfig.prerender?.concurrency ?? os.cpus().length
     logger.info(`Concurrency: ${concurrency}`)
     const queue = new Queue({ concurrency })
+    const routerBasePath = joinURL('/', startConfig.router.basepath ?? '')

     startConfig.pages.forEach((page) => addCrawlPageTask(page))

     await queue.start()

     return Array.from(seen)

     function addCrawlPageTask(page: Page) {
       // Was the page already seen?
       if (seen.has(page.path)) return

       // Add the page to the seen set
       seen.add(page.path)

       if (page.fromCrawl) {
         startConfig.pages.push(page)
       }

       // If not enabled, skip
       if (!(page.prerender?.enabled ?? true)) return

       // If there is a filter link, check if the page should be prerendered
       if (startConfig.prerender?.filter && !startConfig.prerender.filter(page))
         return

       // Resolve the merged default and page-specific prerender options
       const prerenderOptions = {
         ...startConfig.prerender,
         ...page.prerender,
       }

-      const routerBasePath = joinURL('/', startConfig.router.basepath ?? '')
-
       // Add the task
packages/nitro-v2-vite-plugin/src/index.ts (1)

10-17: Consider extracting isFullUrl to a shared utility.

The isFullUrl helper is duplicated across multiple packages (also present in start-plugin-core). If these packages share a common utilities package, consider extracting this function to reduce duplication and ensure consistent behavior.

If a shared utilities package exists (e.g., @tanstack/start-utils), extract the helper:

// In shared utils package
export function isFullUrl(str: string): boolean {
  try {
    new URL(str)
    return true
  } catch {
    return false
  }
}

Then import it in both files:

+import { isFullUrl } from '@tanstack/start-utils'
+
-function isFullUrl(str: string): boolean {
-  try {
-    new URL(str)
-    return true
-  } catch {
-    return false
-  }
-}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fa41d59 and c79ff93.

📒 Files selected for processing (4)
  • packages/nitro-v2-vite-plugin/src/index.ts (3 hunks)
  • packages/react-router/tests/router.test.tsx (11 hunks)
  • packages/start-plugin-core/src/plugin.ts (6 hunks)
  • packages/start-plugin-core/src/prerender.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/start-plugin-core/src/plugin.ts
🧰 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:

  • packages/start-plugin-core/src/prerender.ts
  • packages/react-router/tests/router.test.tsx
  • packages/nitro-v2-vite-plugin/src/index.ts
packages/{*-start,start-*}/**

📄 CodeRabbit inference engine (AGENTS.md)

Name and place Start framework packages under packages/-start/ or packages/start-/

Files:

  • packages/start-plugin-core/src/prerender.ts
packages/{react-router,solid-router}/**

📄 CodeRabbit inference engine (AGENTS.md)

Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/

Files:

  • packages/react-router/tests/router.test.tsx
🧬 Code graph analysis (1)
packages/nitro-v2-vite-plugin/src/index.ts (1)
packages/start-plugin-core/src/plugin.ts (1)
  • config (104-306)
⏰ 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 (4)
packages/react-router/tests/router.test.tsx (1)

2604-3005: LGTM! Comprehensive test coverage for basepath functionality.

The new test suite thoroughly covers the basepath option across multiple scenarios:

  • Basic basepath rewriting (stripping on input)
  • Various basepath formats (with/without leading/trailing slashes)
  • Edge cases (empty basepath, trailing slash mismatches)
  • Composition with additional rewrite logic
  • Preservation of search params and hash
  • Output rewriting (adding basepath back on navigation)
  • Nested rewrites with multiple layers

The parameterized tests using it.each effectively validate that basepath works consistently regardless of slash conventions, which is a common source of bugs.

packages/nitro-v2-vite-plugin/src/index.ts (3)

10-17: LGTM!

The isFullUrl helper correctly identifies absolute URLs using the URL constructor. The try-catch pattern handles all edge cases appropriately (empty strings, relative paths, protocol-relative URLs).


20-20: Use a definite assignment assertion for resolvedConfig
TypeScript’s strictPropertyInitialization may flag the uninitialized declaration—change to

let resolvedConfig!: ResolvedConfig

or disable strictPropertyInitialization in your tsconfig.


97-108: Review nitroConfig spread ordering

The spread of ...nitroConfig currently follows baseURL and publicAssets, so user settings will override those computed defaults. If defaults should take precedence, move the spread above them; otherwise document or warn when overrides occur.

packages/nitro-v2-vite-plugin/src/index.ts (around line 108)

Suggested change:

 const config: NitroConfig = {
+  ...nitroConfig,
   baseURL,
   publicAssets: [
     { dir: client.config.build.outDir, maxAge: 31536000 },
   ],
-  ...nitroConfig,
   renderer: virtualEntry,
 }

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 (2)
packages/nitro-v2-vite-plugin/src/index.ts (1)

10-17: Consider extracting isFullUrl to a shared utility module.

This function is duplicated in packages/start-plugin-core/src/plugin.ts (line 103). To adhere to DRY principles, consider extracting it to a shared utility module that both packages can import.

packages/start-plugin-core/src/plugin.ts (1)

52-59: Extract isFullUrl into a shared utility
Identical implementations exist in packages/start-plugin-core/src/plugin.ts and packages/nitro-v2-vite-plugin/src/index.ts; centralize this helper to adhere to DRY principles.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c79ff93 and 02c743b.

📒 Files selected for processing (2)
  • packages/nitro-v2-vite-plugin/src/index.ts (3 hunks)
  • packages/start-plugin-core/src/plugin.ts (6 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • packages/start-plugin-core/src/plugin.ts
  • packages/nitro-v2-vite-plugin/src/index.ts
packages/{*-start,start-*}/**

📄 CodeRabbit inference engine (AGENTS.md)

Name and place Start framework packages under packages/-start/ or packages/start-/

Files:

  • packages/start-plugin-core/src/plugin.ts
🧬 Code graph analysis (2)
packages/start-plugin-core/src/plugin.ts (1)
packages/start-plugin-core/src/schema.ts (1)
  • TanStackStartOutputConfig (202-202)
packages/nitro-v2-vite-plugin/src/index.ts (1)
packages/start-plugin-core/src/plugin.ts (1)
  • config (104-313)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test
🔇 Additional comments (9)
packages/nitro-v2-vite-plugin/src/index.ts (2)

20-20: Verify hook execution order or guard resolvedConfig usage.

No references were found in the repo confirming that configResolved always runs before the buildApp callback. Please manually verify Vite’s lifecycle for your version or add a runtime check before using resolvedConfig to prevent undefined access.


97-109: Review config override and publicAssets baseURL

  • user-provided nitroConfig.baseURL overrides the computed baseURL due to spread order; swap them if the computed value should take precedence.
  • publicAssets.baseURL is hardcoded to '/' and may conflict with a non-root base path; either align it to the computed baseURL or document why it must remain '/'.
packages/start-plugin-core/src/plugin.ts (7)

1-1: LGTM!

The import of joinPaths is necessary for the new path normalization logic introduced in this PR.


44-44: LGTM!

The viteAppBase property is properly typed and integrated into the configuration flow.


70-70: LGTM!

The empty string default is appropriate and will be overridden during configuration resolution.


126-131: LGTM!

The computation correctly derives the server function base path from the router basepath and serverFns base, ensuring consistent path handling throughout the application.


338-341: LGTM!

The refactoring to pass getConfig instead of individual parameters improves maintainability and consistency with other plugins like devServerPlugin.


280-282: Migration verified: no remaining TSS_APP_BASE references
Ripgrep search returned no hits in .ts/.tsx.


117-124: Default / basepath becoming '' is treated as root and works correctly.

Comment on lines +105 to +112
resolvedStartConfig.viteAppBase = viteConfig.base ?? '/'
if (!isFullUrl(resolvedStartConfig.viteAppBase)) {
resolvedStartConfig.viteAppBase = joinPaths([
'/',
viteConfig.base,
'/',
])
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Verify handling of empty string base path.

The logic correctly distinguishes between full URLs and relative paths. However, if viteConfig.base is explicitly set to an empty string (''), it will be normalized to '/'. Ensure this behavior aligns with Vite's base path handling conventions.

Run the following script to check Vite's default base handling:


🌐 Web query:

What is the default value and behavior of Vite's config.base option when set to an empty string?

💡 Result:

Default is "/". Setting config.base to an empty string (or "./") tells Vite to use a relative (embedded) public base path so built asset URLs are generated relative to the current HTML file (suitable for embedded/static deployments). import.meta.env.BASE_URL will reflect that relative value. [1][2]

Sources:
[1] Vite config (base option).
[2] Vite — Public Base Path / Building for Production.


🏁 Script executed:

#!/bin/bash
rg -n "joinPaths" -C3 --type=ts

Length of output: 9666


🏁 Script executed:

#!/bin/bash
sed -n '1,100p' packages/router-core/src/path.ts

Length of output: 2691


Handle explicit empty-string base path. Vite treats base: "" as a relative ./ public path, but here viteConfig.base === "" is normalized to /. Add a guard before the URL check to set resolvedStartConfig.viteAppBase = "./" when viteConfig.base === "".

🤖 Prompt for AI Agents
In packages/start-plugin-core/src/plugin.ts around lines 105 to 112, add a guard
for the explicit empty-string base: if viteConfig.base === "" set
resolvedStartConfig.viteAppBase = "./" before the URL check; otherwise continue
with the existing logic that sets viteAppBase to viteConfig.base ?? '/' and
falls back to joinPaths when not a full URL. This ensures Vite's empty-string
base is preserved as a relative "./" path instead of being normalized to "/".

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: 0

♻️ Duplicate comments (1)
packages/start-plugin-core/src/plugin.ts (1)

105-124: Preserve explicit empty-string Vite base.

Vite treats config.base === '' as the special embedded-relative ./ base, but the normalization here collapses it back to '/', so relative deployments and import.meta.env.BASE_URL === './' break. Please keep the empty-string case intact (set viteAppBase to './') and translate that to an empty router basepath instead of '.'.

-        resolvedStartConfig.viteAppBase = viteConfig.base ?? '/'
-        if (!isFullUrl(resolvedStartConfig.viteAppBase)) {
-          resolvedStartConfig.viteAppBase = joinPaths([
-            '/',
-            viteConfig.base,
-            '/',
-          ])
-        }
+        const rawViteBase = viteConfig.base ?? '/'
+        if (viteConfig.base === '') {
+          resolvedStartConfig.viteAppBase = './'
+        } else if (!isFullUrl(rawViteBase)) {
+          resolvedStartConfig.viteAppBase = joinPaths([
+            '/',
+            rawViteBase,
+            '/',
+          ])
+        } else {
+          resolvedStartConfig.viteAppBase = rawViteBase
+        }
@@
-          if (!isFullUrl(resolvedStartConfig.viteAppBase)) {
-            startConfig.router.basepath =
-              resolvedStartConfig.viteAppBase.replace(/^\/|\/$/g, '')
+          if (!isFullUrl(resolvedStartConfig.viteAppBase)) {
+            const normalizedBase = resolvedStartConfig.viteAppBase.replace(
+              /^\/|\/$/g,
+              '',
+            )
+            startConfig.router.basepath =
+              normalizedBase === '.' ? '' : normalizedBase
           } else {
             startConfig.router.basepath = '/'
           }
🧹 Nitpick comments (1)
packages/nitro-v2-vite-plugin/src/index.ts (1)

20-20: Consider initializing resolvedConfig or using definite assignment assertion.

In TypeScript strict mode, resolvedConfig is declared but not initialized. While Vite's lifecycle guarantees configResolved runs before buildApp, the type system doesn't know this.

Apply one of these approaches:

Option 1: Definite assignment assertion (if you're certain about the lifecycle)

-  let resolvedConfig: ResolvedConfig
+  let resolvedConfig!: ResolvedConfig

Option 2: Defensive check in buildApp

   async buildApp(builder) {
+    if (!resolvedConfig) {
+      throw new Error('configResolved hook must run before buildApp')
+    }
     const client = builder.environments.client

Also applies to: 55-57

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 02c743b and 9f71f75.

📒 Files selected for processing (3)
  • packages/nitro-v2-vite-plugin/src/index.ts (3 hunks)
  • packages/start-plugin-core/src/plugin.ts (6 hunks)
  • packages/start-plugin-core/src/prerender.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/start-plugin-core/src/prerender.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • packages/nitro-v2-vite-plugin/src/index.ts
  • packages/start-plugin-core/src/plugin.ts
packages/{*-start,start-*}/**

📄 CodeRabbit inference engine (AGENTS.md)

Name and place Start framework packages under packages/-start/ or packages/start-/

Files:

  • packages/start-plugin-core/src/plugin.ts
🧬 Code graph analysis (1)
packages/start-plugin-core/src/plugin.ts (1)
packages/start-plugin-core/src/schema.ts (1)
  • TanStackStartOutputConfig (202-202)
🔇 Additional comments (4)
packages/nitro-v2-vite-plugin/src/index.ts (4)

4-4: LGTM!

The addition of ResolvedConfig to the imports is necessary for the new configResolved hook.


10-17: LGTM!

The isFullUrl helper correctly uses the URL constructor to detect absolute URLs. The try-catch pattern appropriately handles the TypeError thrown for relative URLs.


97-99: LGTM!

The logic correctly filters out absolute URLs (e.g., from CDN configurations) and only uses relative base paths as the Nitro baseURL.


100-121: Verify that publicAssets override behavior is intentional.

The current structure allows nitroConfig to completely override the publicAssets array (which includes the client build output). If a user provides their own publicAssets, the client assets might not be served correctly.

Consider whether you should merge the arrays instead:

  const config: NitroConfig = {
    baseURL,
-   publicAssets: [
-     {
-       dir: client.config.build.outDir,
-       maxAge: 31536000, // 1 year
-       baseURL: '/',
-     },
-   ],
    ...nitroConfig,
+   publicAssets: [
+     {
+       dir: client.config.build.outDir,
+       maxAge: 31536000, // 1 year
+       baseURL: '/',
+     },
+     ...(nitroConfig?.publicAssets ?? []),
+   ],
    renderer: virtualEntry,

This ensures the client assets are always served while allowing users to add additional public assets.

@schiller-manuel schiller-manuel merged commit ceae3d5 into main Oct 3, 2025
6 checks passed
@schiller-manuel schiller-manuel deleted the fix-basepath branch October 3, 2025 12:59
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.

1 participant