chore(studio): support NEXT_PUBLIC_BASE_PATH and port / redirect

Wire Vite `base`, Nitro `baseURL`, and the TanStack router `basepath` off
the same `NEXT_PUBLIC_BASE_PATH` env var so the prefix stays in sync (all
three layers are required — there's no single knob in TanStack Start).
Defaults are unchanged when the var is empty.

Also replace the `/` placeholder with the redirect rules that used to live
in `next.config.ts`'s `redirects()`: platform → `/org` (or
`/new/new-project` when deep-linked with `?next=new-project`), self-hosted
→ `/project/default`. Kept as a route (not a Nitro `routeRule`) so it
works in `vite dev` and can branch on the query param.
This commit is contained in:
Alaister Young committed 2026-04-21 20:39:35 +08:00
1 parent 4c7863a5ca
commit f02485b1d1
3 files changed
+45 -6

No files matched your search

+4
View File
@@ -23,6 +23,10 @@ export function getRouter() {
context,
scrollRestoration: true,
defaultPreload: 'intent',
// Inlined via Vite's `define` at build time; stays undefined (= app at `/`)
// unless NEXT_PUBLIC_BASE_PATH is set. Must agree with Vite `base` and
// Nitro `baseURL` — see vite.config.ts.
basepath: process.env.NEXT_PUBLIC_BASE_PATH || undefined,
})
setupRouterSsrQueryIntegration({ router, queryClient: context.queryClient })
+25 -6
View File
@@ -1,9 +1,28 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute, redirect } from '@tanstack/react-router'
import { IS_PLATFORM } from '@/lib/constants'
// `/` is never rendered — it always redirects. Mirrors the Next.js
// `redirects()` rules in next.config.ts: platform sends users to `/org`
// (or `/new/new-project` when deep-linked with `?next=new-project`),
// self-hosted sends them straight to `/project/default`.
//
// Kept as a route (rather than a Nitro `routeRule`) so the redirect works
// in both `vite dev` and prod — `routeRules` only fire under Nitro — and
// so the query-param branch has somewhere to run.
export const Route = createFileRoute('/')({
component: RouteComponent,
validateSearch: (search: Record<string, unknown>) => ({
next: typeof search.next === 'string' ? search.next : undefined,
}),
beforeLoad: ({ search }) => {
// `href` instead of `to` because these targets aren't in the TanStack
// routeTree yet — they're still on the Next.js pages side during the
// migration. Swap to `to` once `/org`, `/new/new-project`, and
// `/project/default` are migrated.
if (IS_PLATFORM) {
if (search.next === 'new-project') throw redirect({ href: '/new/new-project' })
throw redirect({ href: '/org' })
}
throw redirect({ href: '/project/default' })
},
})
function RouteComponent() {
return <div>Hello "/"!</div>
}
+16
View File
@@ -88,6 +88,16 @@ export default defineConfig(({ mode }) => {
.map(([key, value]) => [`process.env.${key}`, JSON.stringify(value)])
)
// Mirror Next's `basePath` via NEXT_PUBLIC_BASE_PATH. Unlike Next, TanStack
// Start has no single knob — the prefix has to be declared in three places
// (see BASE_PATH_REDIRECT_GUIDE.md):
// - Vite `base` — bakes the prefix into asset URLs in the built bundle
// - Nitro `baseURL` — mounts the production server at that path
// - Router `basepath` — makes client-side navigation match the prefix
// The router layer is set in router.tsx off the same env var (inlined via
// `define` above). Leaving the var empty keeps the app at `/` as today.
const basePath = env.NEXT_PUBLIC_BASE_PATH || undefined
return {
server: {
port: 3000,
@@ -95,6 +105,7 @@ export default defineConfig(({ mode }) => {
resolve: {
tsconfigPaths: true,
},
...(basePath && { base: basePath }),
define: publicEnvDefines,
ssr: {
// `lodash` is CJS; its named-export interop fails in Node ESM unless bundled.
@@ -131,6 +142,11 @@ export default defineConfig(({ mode }) => {
// `modules/index.js` via `package.json#exports`) out of the picture.
exportConditions: ['module'],
noExternals: ['tslib'],
// See the `basePath` comment above — Nitro's own mount point needs the
// same prefix as Vite/router. `routeRules` paths are matched AFTER
// baseURL is stripped, so if we add redirects later they should be
// written relative to the base.
...(basePath && { baseURL: basePath }),
}),
viteReact(),
],