Skip to content

Commit c8be12c

Browse files
fix: prerending with SPA mode enabled renders all routes (#4800)
1 parent de11338 commit c8be12c

File tree

10 files changed

+55
-8
lines changed

10 files changed

+55
-8
lines changed

e2e/react-start/spa-mode/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"dev": "vite dev --port 3000",
88
"dev:e2e": "vite dev",
99
"build": "vite build && tsc --noEmit",
10-
"start": "node .output/server/index.mjs",
10+
"start": "npx serve .output/public",
1111
"test:e2e": "rm -rf port*.txt; playwright test --project=chromium"
1212
},
1313
"dependencies": {

e2e/react-start/spa-mode/vite.config.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,16 @@ export default defineConfig({
1313
tanstackStart({
1414
spa: {
1515
enabled: true,
16+
prerender: {
17+
outputPath: 'index.html',
18+
},
1619
},
20+
pages: [
21+
{
22+
path: '/posts/1',
23+
prerender: { enabled: true, outputPath: '/posts/1/index.html' },
24+
},
25+
],
1726
}),
1827
],
1928
})

e2e/solid-start/spa-mode/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"dev": "vite dev --port 3000",
88
"dev:e2e": "vite dev",
99
"build": "vite build && tsc --noEmit",
10-
"start": "node .output/server/index.mjs",
10+
"start": "npx serve .output/public",
1111
"test:e2e": "rm -rf port*.txt; playwright test --project=chromium"
1212
},
1313
"dependencies": {

e2e/solid-start/spa-mode/vite.config.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,17 @@ export default defineConfig({
1313
tanstackStart({
1414
spa: {
1515
enabled: true,
16+
prerender: {
17+
outputPath: 'index.html',
18+
crawlLinks: true,
19+
},
1620
},
21+
pages: [
22+
{
23+
path: '/posts/1',
24+
prerender: { enabled: true, outputPath: '/posts/1/index.html' },
25+
},
26+
],
1727
}),
1828
],
1929
})

packages/router-core/src/router.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,11 @@ export interface RouterOptions<
346346
*/
347347
isShell?: boolean
348348

349+
/**
350+
* @default false
351+
*/
352+
isPrerendering?: boolean
353+
349354
/**
350355
* The default `ssr` a route should use if no `ssr` is provided.
351356
*
@@ -833,7 +838,11 @@ export class RouterCore<
833838
startTransition: StartTransitionFn = (fn) => fn()
834839

835840
isShell() {
836-
return this.options.isShell
841+
return !!this.options.isShell
842+
}
843+
844+
isPrerendering() {
845+
return !!this.options.isPrerendering
837846
}
838847

839848
update: UpdateFn<

packages/start-plugin-core/src/nitro-plugin/plugin.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,13 @@ async function buildNitroApp(
155155

156156
options.pages.push({
157157
path: maskUrl.toString().replace('http://localhost', ''),
158-
prerender: options.spa.prerender,
158+
prerender: {
159+
...options.spa.prerender,
160+
env: {
161+
...options.spa.prerender.env,
162+
TSS_SPA_MODE: 'true',
163+
},
164+
},
159165
sitemap: {
160166
exclude: true,
161167
},

packages/start-plugin-core/src/nitro-plugin/prerender.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,13 +171,23 @@ export async function prerender({
171171
// Fetch the route
172172
const encodedRoute = encodeURI(page.path)
173173

174+
const originalEnv = { ...process.env }
175+
176+
process.env = {
177+
...originalEnv,
178+
TSS_PRERENDERING: 'true',
179+
...(prerenderOptions.env || {}),
180+
}
174181
const res = await localFetch<Response>(
175182
withBase(encodedRoute, nodeNitro.options.baseURL),
176183
{
177-
headers: { 'x-nitro-prerender': encodedRoute },
184+
headers: {
185+
...prerenderOptions.headers,
186+
'x-nitro-prerender': encodedRoute,
187+
},
178188
},
179189
)
180-
190+
process.env = originalEnv
181191
if (!res.ok) {
182192
throw new Error(`Failed to fetch ${page.path}: ${res.statusText}`, {
183193
cause: res,

packages/start-plugin-core/src/plugin.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export function TanStackStartVitePluginCore(
6060
resolveVirtualEntriesPlugin(opts, startConfig),
6161
{
6262
name: 'tanstack-start-core:config-client',
63-
async config(viteConfig) {
63+
async config(viteConfig, { command }) {
6464
const viteAppBase = trimPathRight(viteConfig.base || '/')
6565
globalThis.TSS_APP_BASE = viteAppBase
6666

@@ -147,7 +147,7 @@ export function TanStackStartVitePluginCore(
147147
...defineReplaceEnv('TSS_SERVER_FN_BASE', startConfig.serverFns.base),
148148
...defineReplaceEnv('TSS_OUTPUT_PUBLIC_DIR', nitroOutputPublicDir),
149149
...defineReplaceEnv('TSS_APP_BASE', viteAppBase),
150-
...defineReplaceEnv('TSS_SPA_MODE', startConfig.spa?.enabled ? 'true' : 'false'),
150+
...(command === 'serve' ? defineReplaceEnv('TSS_SPA_MODE', startConfig.spa?.enabled ? 'true' : 'false') : {}),
151151
},
152152
}
153153
},

packages/start-plugin-core/src/schema.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ const pagePrerenderOptionsSchema = z.object({
196196
)
197197
.returns(z.any())
198198
.optional(),
199+
headers: z.record(z.string(), z.string()).optional(),
200+
env: z.record(z.string(), z.string()).optional(),
199201
})
200202

201203
const spaSchema = z.object({

packages/start-server-core/src/createStartHandler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ export function createStartHandler<TRouter extends AnyRouter>({
126126
router.update({
127127
history,
128128
isShell: process.env.TSS_SPA_MODE === 'true',
129+
isPrerendering: process.env.TSS_PRERENDERING === 'true',
129130
})
130131

131132
const response = await (async () => {

0 commit comments

Comments
 (0)