@tanstack/start-plugin-core 1.131.7 → 1.132.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/dist/esm/{nitro-plugin/build-sitemap.d.ts → build-sitemap.d.ts} +3 -3
  2. package/dist/esm/{nitro-plugin/build-sitemap.js → build-sitemap.js} +19 -24
  3. package/dist/esm/build-sitemap.js.map +1 -0
  4. package/dist/esm/compilers.js +7 -10
  5. package/dist/esm/compilers.js.map +1 -1
  6. package/dist/esm/constants.d.ts +6 -2
  7. package/dist/esm/constants.js +7 -10
  8. package/dist/esm/constants.js.map +1 -1
  9. package/dist/esm/debug.js.map +1 -1
  10. package/dist/esm/dev-server-plugin/extract-html-scripts.js.map +1 -1
  11. package/dist/esm/dev-server-plugin/plugin.d.ts +5 -5
  12. package/dist/esm/dev-server-plugin/plugin.js +117 -79
  13. package/dist/esm/dev-server-plugin/plugin.js.map +1 -1
  14. package/dist/esm/index.d.ts +1 -1
  15. package/dist/esm/index.js +0 -4
  16. package/dist/esm/index.js.map +1 -1
  17. package/dist/esm/load-env-plugin/plugin.d.ts +2 -3
  18. package/dist/esm/load-env-plugin/plugin.js +5 -8
  19. package/dist/esm/load-env-plugin/plugin.js.map +1 -1
  20. package/dist/esm/output-directory.d.ts +3 -0
  21. package/dist/esm/output-directory.js +14 -0
  22. package/dist/esm/output-directory.js.map +1 -0
  23. package/dist/esm/plugin.d.ts +6 -290
  24. package/dist/esm/plugin.js +123 -76
  25. package/dist/esm/plugin.js.map +1 -1
  26. package/dist/esm/post-server-build.d.ts +7 -0
  27. package/dist/esm/post-server-build.js +55 -0
  28. package/dist/esm/post-server-build.js.map +1 -0
  29. package/dist/esm/prerender.d.ts +11 -0
  30. package/dist/esm/{nitro-plugin/prerender.js → prerender.js} +85 -73
  31. package/dist/esm/prerender.js.map +1 -0
  32. package/dist/esm/{nitro-plugin/queue.js → queue.js} +7 -10
  33. package/dist/esm/queue.js.map +1 -0
  34. package/dist/esm/resolve-entries.d.ts +8 -0
  35. package/dist/esm/resolve-entries.js +37 -0
  36. package/dist/esm/resolve-entries.js.map +1 -0
  37. package/dist/esm/schema.d.ts +1369 -6719
  38. package/dist/esm/schema.js +52 -85
  39. package/dist/esm/schema.js.map +1 -1
  40. package/dist/esm/start-compiler-plugin.js +2 -2
  41. package/dist/esm/start-compiler-plugin.js.map +1 -1
  42. package/dist/esm/start-manifest-plugin/plugin.d.ts +1 -1
  43. package/dist/esm/start-manifest-plugin/plugin.js +8 -13
  44. package/dist/esm/start-manifest-plugin/plugin.js.map +1 -1
  45. package/dist/esm/start-router-plugin/generator-plugins/routes-manifest-plugin.js +2 -3
  46. package/dist/esm/start-router-plugin/generator-plugins/routes-manifest-plugin.js.map +1 -1
  47. package/dist/esm/start-router-plugin/generator-plugins/server-routes-plugin.js +2 -4
  48. package/dist/esm/start-router-plugin/generator-plugins/server-routes-plugin.js.map +1 -1
  49. package/dist/esm/start-router-plugin/plugin.js.map +1 -1
  50. package/dist/esm/start-router-plugin/route-tree-client-plugin.js.map +1 -1
  51. package/dist/esm/start-router-plugin/virtual-route-tree-plugin.js.map +1 -1
  52. package/dist/esm/utils.js.map +1 -1
  53. package/package.json +13 -18
  54. package/src/{nitro-plugin/build-sitemap.ts → build-sitemap.ts} +8 -8
  55. package/src/constants.ts +12 -9
  56. package/src/dev-server-plugin/plugin.ts +140 -99
  57. package/src/global.d.ts +0 -2
  58. package/src/index.ts +1 -5
  59. package/src/load-env-plugin/plugin.ts +6 -11
  60. package/src/output-directory.ts +18 -0
  61. package/src/plugin.ts +160 -98
  62. package/src/post-server-build.ts +73 -0
  63. package/src/{nitro-plugin/prerender.ts → prerender.ts} +93 -86
  64. package/src/resolve-entries.ts +52 -0
  65. package/src/schema.ts +88 -121
  66. package/src/start-manifest-plugin/plugin.ts +8 -14
  67. package/dist/cjs/compilers.cjs +0 -416
  68. package/dist/cjs/compilers.cjs.map +0 -1
  69. package/dist/cjs/compilers.d.cts +0 -21
  70. package/dist/cjs/constants.cjs +0 -20
  71. package/dist/cjs/constants.cjs.map +0 -1
  72. package/dist/cjs/constants.d.cts +0 -6
  73. package/dist/cjs/debug.cjs +0 -5
  74. package/dist/cjs/debug.cjs.map +0 -1
  75. package/dist/cjs/debug.d.cts +0 -1
  76. package/dist/cjs/dev-server-plugin/extract-html-scripts.cjs +0 -35
  77. package/dist/cjs/dev-server-plugin/extract-html-scripts.cjs.map +0 -1
  78. package/dist/cjs/dev-server-plugin/extract-html-scripts.d.cts +0 -4
  79. package/dist/cjs/dev-server-plugin/plugin.cjs +0 -121
  80. package/dist/cjs/dev-server-plugin/plugin.cjs.map +0 -1
  81. package/dist/cjs/dev-server-plugin/plugin.d.cts +0 -5
  82. package/dist/cjs/index.cjs +0 -11
  83. package/dist/cjs/index.cjs.map +0 -1
  84. package/dist/cjs/index.d.cts +0 -3
  85. package/dist/cjs/load-env-plugin/plugin.cjs +0 -34
  86. package/dist/cjs/load-env-plugin/plugin.cjs.map +0 -1
  87. package/dist/cjs/load-env-plugin/plugin.d.cts +0 -3
  88. package/dist/cjs/nitro-plugin/build-sitemap.cjs +0 -138
  89. package/dist/cjs/nitro-plugin/build-sitemap.cjs.map +0 -1
  90. package/dist/cjs/nitro-plugin/build-sitemap.d.cts +0 -31
  91. package/dist/cjs/nitro-plugin/plugin.cjs +0 -187
  92. package/dist/cjs/nitro-plugin/plugin.cjs.map +0 -1
  93. package/dist/cjs/nitro-plugin/plugin.d.cts +0 -3
  94. package/dist/cjs/nitro-plugin/prerender.cjs +0 -178
  95. package/dist/cjs/nitro-plugin/prerender.cjs.map +0 -1
  96. package/dist/cjs/nitro-plugin/prerender.d.cts +0 -8
  97. package/dist/cjs/nitro-plugin/queue.cjs +0 -131
  98. package/dist/cjs/nitro-plugin/queue.cjs.map +0 -1
  99. package/dist/cjs/nitro-plugin/queue.d.cts +0 -32
  100. package/dist/cjs/plugin.cjs +0 -227
  101. package/dist/cjs/plugin.cjs.map +0 -1
  102. package/dist/cjs/plugin.d.cts +0 -300
  103. package/dist/cjs/resolve-virtual-entries-plugin/plugin.cjs +0 -80
  104. package/dist/cjs/resolve-virtual-entries-plugin/plugin.cjs.map +0 -1
  105. package/dist/cjs/resolve-virtual-entries-plugin/plugin.d.cts +0 -3
  106. package/dist/cjs/schema.cjs +0 -158
  107. package/dist/cjs/schema.cjs.map +0 -1
  108. package/dist/cjs/schema.d.cts +0 -8878
  109. package/dist/cjs/start-compiler-plugin.cjs +0 -78
  110. package/dist/cjs/start-compiler-plugin.cjs.map +0 -1
  111. package/dist/cjs/start-compiler-plugin.d.cts +0 -13
  112. package/dist/cjs/start-manifest-plugin/plugin.cjs +0 -182
  113. package/dist/cjs/start-manifest-plugin/plugin.cjs.map +0 -1
  114. package/dist/cjs/start-manifest-plugin/plugin.d.cts +0 -6
  115. package/dist/cjs/start-router-plugin/generator-plugins/routes-manifest-plugin.cjs +0 -39
  116. package/dist/cjs/start-router-plugin/generator-plugins/routes-manifest-plugin.cjs.map +0 -1
  117. package/dist/cjs/start-router-plugin/generator-plugins/routes-manifest-plugin.d.cts +0 -6
  118. package/dist/cjs/start-router-plugin/generator-plugins/server-routes-plugin.cjs +0 -121
  119. package/dist/cjs/start-router-plugin/generator-plugins/server-routes-plugin.cjs.map +0 -1
  120. package/dist/cjs/start-router-plugin/generator-plugins/server-routes-plugin.d.cts +0 -2
  121. package/dist/cjs/start-router-plugin/plugin.cjs +0 -45
  122. package/dist/cjs/start-router-plugin/plugin.cjs.map +0 -1
  123. package/dist/cjs/start-router-plugin/plugin.d.cts +0 -3
  124. package/dist/cjs/start-router-plugin/route-tree-client-plugin.cjs +0 -73
  125. package/dist/cjs/start-router-plugin/route-tree-client-plugin.cjs.map +0 -1
  126. package/dist/cjs/start-router-plugin/route-tree-client-plugin.d.cts +0 -6
  127. package/dist/cjs/start-router-plugin/virtual-route-tree-plugin.cjs +0 -29
  128. package/dist/cjs/start-router-plugin/virtual-route-tree-plugin.cjs.map +0 -1
  129. package/dist/cjs/start-router-plugin/virtual-route-tree-plugin.d.cts +0 -3
  130. package/dist/cjs/utils.cjs +0 -18
  131. package/dist/cjs/utils.cjs.map +0 -1
  132. package/dist/cjs/utils.d.cts +0 -8
  133. package/dist/esm/nitro-plugin/build-sitemap.js.map +0 -1
  134. package/dist/esm/nitro-plugin/plugin.d.ts +0 -3
  135. package/dist/esm/nitro-plugin/plugin.js +0 -187
  136. package/dist/esm/nitro-plugin/plugin.js.map +0 -1
  137. package/dist/esm/nitro-plugin/prerender.d.ts +0 -8
  138. package/dist/esm/nitro-plugin/prerender.js.map +0 -1
  139. package/dist/esm/nitro-plugin/queue.js.map +0 -1
  140. package/dist/esm/resolve-virtual-entries-plugin/plugin.d.ts +0 -3
  141. package/dist/esm/resolve-virtual-entries-plugin/plugin.js +0 -63
  142. package/dist/esm/resolve-virtual-entries-plugin/plugin.js.map +0 -1
  143. package/src/nitro-plugin/plugin.ts +0 -252
  144. package/src/resolve-virtual-entries-plugin/plugin.ts +0 -77
  145. /package/dist/esm/{nitro-plugin/queue.d.ts → queue.d.ts} +0 -0
  146. /package/src/{nitro-plugin/queue.ts → queue.ts} +0 -0
@@ -1,33 +1,29 @@
1
- import { promises as fsp } from 'node:fs'
1
+ import { existsSync, promises as fsp, rmSync } from 'node:fs'
2
2
  import { pathToFileURL } from 'node:url'
3
3
  import os from 'node:os'
4
- import path from 'node:path'
5
- import { getRollupConfig } from 'nitropack/rollup'
6
- import { build as buildNitro, createNitro } from 'nitropack'
4
+ import path from 'pathe'
7
5
  import { joinURL, withBase, withoutBase } from 'ufo'
8
- import { VITE_ENVIRONMENT_NAMES } from '../constants'
9
- import { createLogger } from '../utils'
6
+ import { VITE_ENVIRONMENT_NAMES } from './constants'
7
+ import { createLogger } from './utils'
10
8
  import { Queue } from './queue'
11
- import type { ViteBuilder } from 'vite'
12
- import type { $Fetch, Nitro } from 'nitropack'
13
- import type { TanStackStartOutputConfig } from '../plugin'
14
- import type { Page } from '../schema'
9
+ import type { Rollup, ViteBuilder } from 'vite'
10
+ import type { Page, TanStackStartOutputConfig } from './schema'
15
11
 
16
12
  export async function prerender({
17
- options,
18
- nitro,
13
+ startConfig,
19
14
  builder,
15
+ serverBundle,
20
16
  }: {
21
- options: TanStackStartOutputConfig
22
- nitro: Nitro
17
+ startConfig: TanStackStartOutputConfig
23
18
  builder: ViteBuilder
19
+ serverBundle: Rollup.OutputBundle
24
20
  }) {
25
21
  const logger = createLogger('prerender')
26
22
  logger.info('Prerendering pages...')
27
23
 
28
24
  // If prerender is enabled but no pages are provided, default to prerendering the root page
29
- if (options.prerender?.enabled && !options.pages.length) {
30
- options.pages = [
25
+ if (startConfig.prerender?.enabled && !startConfig.pages.length) {
26
+ startConfig.pages = [
31
27
  {
32
28
  path: '/',
33
29
  },
@@ -42,76 +38,52 @@ export async function prerender({
42
38
  )
43
39
  }
44
40
 
45
- const prerenderOutputDir = path.resolve(
46
- options.root,
47
- '.tanstack',
48
- 'start',
49
- 'build',
50
- 'prerenderer',
51
- )
52
-
53
- const nodeNitro = await createNitro({
54
- ...nitro.options._config,
55
- preset: 'nitro-prerender',
56
- logLevel: 0,
57
- output: {
58
- dir: prerenderOutputDir,
59
- serverDir: path.resolve(prerenderOutputDir, 'server'),
60
- publicDir: path.resolve(prerenderOutputDir, 'public'),
61
- },
62
- })
63
-
64
- const nodeNitroRollupOptions = getRollupConfig(nodeNitro)
65
-
66
- const build = serverEnv.config.build
67
-
68
- build.outDir = prerenderOutputDir
69
-
70
- build.rollupOptions = {
71
- ...build.rollupOptions,
72
- ...nodeNitroRollupOptions,
73
- output: {
74
- ...build.rollupOptions.output,
75
- ...nodeNitroRollupOptions.output,
76
- sourcemap: undefined,
77
- },
41
+ const clientEnv = builder.environments[VITE_ENVIRONMENT_NAMES.client]
42
+ if (!clientEnv) {
43
+ throw new Error(
44
+ `Vite's "${VITE_ENVIRONMENT_NAMES.client}" environment not found`,
45
+ )
78
46
  }
79
47
 
80
- await buildNitro(nodeNitro)
48
+ const outputDir = clientEnv.config.build.outDir
81
49
 
82
- // Import renderer entry
83
- const serverFilename =
84
- typeof nodeNitroRollupOptions.output.entryFileNames === 'string'
85
- ? nodeNitroRollupOptions.output.entryFileNames
86
- : 'index.mjs'
50
+ const entryFile = findEntryFileInBundle(serverBundle)
51
+ let fullEntryFilePath = path.join(serverEnv.config.build.outDir, entryFile)
52
+ process.env.TSS_PRERENDERING = 'true'
87
53
 
88
- const serverEntrypoint = pathToFileURL(
89
- path.resolve(path.join(nodeNitro.options.output.serverDir, serverFilename)),
90
- ).toString()
54
+ if (!existsSync(fullEntryFilePath)) {
55
+ // if the file does not exist, we need to write the bundle to a temporary directory
56
+ // this can happen e.g. with nitro that postprocesses the bundle and thus does not write SSR build to disk
57
+ const bundleOutputDir = path.resolve(
58
+ serverEnv.config.root,
59
+ '.tanstack',
60
+ 'start',
61
+ 'prerender',
62
+ )
63
+ rmSync(bundleOutputDir, { recursive: true, force: true })
64
+ await writeBundleToDisk({ bundle: serverBundle, outDir: bundleOutputDir })
65
+ fullEntryFilePath = path.join(bundleOutputDir, entryFile)
66
+ }
91
67
 
92
- process.env.TSS_PRERENDERING = 'true'
68
+ const { default: serverEntrypoint } = await import(
69
+ pathToFileURL(fullEntryFilePath).toString()
70
+ )
93
71
 
94
- const { closePrerenderer, localFetch } = (await import(serverEntrypoint)) as {
95
- closePrerenderer: () => void
96
- localFetch: $Fetch
72
+ function localFetch(path: string, options?: RequestInit): Promise<Response> {
73
+ const url = new URL(`http://localhost${path}`)
74
+ return serverEntrypoint.fetch(new Request(url, options))
97
75
  }
98
76
 
99
77
  try {
100
78
  // Crawl all pages
101
- const pages = await prerenderPages()
79
+ const pages = await prerenderPages({ outputDir })
102
80
 
103
81
  logger.info(`Prerendered ${pages.length} pages:`)
104
82
  pages.forEach((page) => {
105
83
  logger.info(`- ${page}`)
106
84
  })
107
-
108
- // TODO: Write the prerendered pages to the output directory
109
85
  } catch (error) {
110
86
  logger.error(error)
111
- } finally {
112
- // Ensure server is always closed
113
- // server.process.kill()
114
- closePrerenderer()
115
87
  }
116
88
 
117
89
  function extractLinks(html: string): Array<string> {
@@ -129,14 +101,14 @@ export async function prerender({
129
101
  return links
130
102
  }
131
103
 
132
- async function prerenderPages() {
104
+ async function prerenderPages({ outputDir }: { outputDir: string }) {
133
105
  const seen = new Set<string>()
134
106
  const retriesByPath = new Map<string, number>()
135
- const concurrency = options.prerender?.concurrency ?? os.cpus().length
107
+ const concurrency = startConfig.prerender?.concurrency ?? os.cpus().length
136
108
  logger.info(`Concurrency: ${concurrency}`)
137
109
  const queue = new Queue({ concurrency })
138
110
 
139
- options.pages.forEach((page) => addCrawlPageTask(page))
111
+ startConfig.pages.forEach((page) => addCrawlPageTask(page))
140
112
 
141
113
  await queue.start()
142
114
 
@@ -150,18 +122,19 @@ export async function prerender({
150
122
  seen.add(page.path)
151
123
 
152
124
  if (page.fromCrawl) {
153
- options.pages.push(page)
125
+ startConfig.pages.push(page)
154
126
  }
155
127
 
156
128
  // If not enabled, skip
157
129
  if (!(page.prerender?.enabled ?? true)) return
158
130
 
159
131
  // If there is a filter link, check if the page should be prerendered
160
- if (options.prerender?.filter && !options.prerender.filter(page)) return
132
+ if (startConfig.prerender?.filter && !startConfig.prerender.filter(page))
133
+ return
161
134
 
162
135
  // Resolve the merged default and page-specific prerender options
163
136
  const prerenderOptions = {
164
- ...options.prerender,
137
+ ...startConfig.prerender,
165
138
  ...page.prerender,
166
139
  }
167
140
 
@@ -173,15 +146,11 @@ export async function prerender({
173
146
  // Fetch the route
174
147
  const encodedRoute = encodeURI(page.path)
175
148
 
176
- const res = await localFetch<Response>(
177
- withBase(encodedRoute, nodeNitro.options.baseURL),
178
- {
179
- headers: {
180
- ...prerenderOptions.headers,
181
- 'x-nitro-prerender': encodedRoute,
182
- },
149
+ const res = await localFetch(withBase(encodedRoute, TSS_APP_BASE), {
150
+ headers: {
151
+ ...prerenderOptions.headers,
183
152
  },
184
- )
153
+ })
185
154
 
186
155
  if (!res.ok) {
187
156
  throw new Error(`Failed to fetch ${page.path}: ${res.statusText}`, {
@@ -210,12 +179,12 @@ export async function prerender({
210
179
 
211
180
  const filename = withoutBase(
212
181
  isImplicitHTML ? htmlPath : routeWithIndex,
213
- nitro.options.baseURL,
182
+ TSS_APP_BASE,
214
183
  )
215
184
 
216
185
  const html = await res.text()
217
186
 
218
- const filepath = path.join(nitro.options.output.publicDir, filename)
187
+ const filepath = path.join(outputDir, filename)
219
188
 
220
189
  await fsp.mkdir(path.dirname(filepath), {
221
190
  recursive: true,
@@ -245,10 +214,48 @@ export async function prerender({
245
214
  retriesByPath.set(page.path, retries + 1)
246
215
  addCrawlPageTask(page)
247
216
  } else {
248
- throw error
217
+ if (prerenderOptions.failOnError ?? true) {
218
+ throw error
219
+ }
249
220
  }
250
221
  }
251
222
  })
252
223
  }
253
224
  }
254
225
  }
226
+
227
+ function findEntryFileInBundle(bundle: Rollup.OutputBundle): string {
228
+ let entryFile: string | undefined
229
+
230
+ for (const [_name, file] of Object.entries(bundle)) {
231
+ if (file.type === 'chunk') {
232
+ if (file.isEntry) {
233
+ if (entryFile !== undefined) {
234
+ throw new Error(
235
+ `Multiple entry points found. Only one entry point is allowed.`,
236
+ )
237
+ }
238
+ entryFile = file.fileName
239
+ }
240
+ }
241
+ }
242
+ if (entryFile === undefined) {
243
+ throw new Error(`No entry point found in the bundle.`)
244
+ }
245
+ return entryFile
246
+ }
247
+
248
+ export async function writeBundleToDisk({
249
+ bundle,
250
+ outDir,
251
+ }: {
252
+ bundle: Rollup.OutputBundle
253
+ outDir: string
254
+ }) {
255
+ for (const [fileName, asset] of Object.entries(bundle)) {
256
+ const fullPath = path.join(outDir, fileName)
257
+ const content = asset.type === 'asset' ? asset.source : asset.code
258
+ await fsp.mkdir(path.dirname(fullPath), { recursive: true })
259
+ await fsp.writeFile(fullPath, content)
260
+ }
261
+ }
@@ -0,0 +1,52 @@
1
+ import { resolveModulePath } from 'exsolve'
2
+
3
+ interface ResolveModuleOptions {
4
+ baseName: string
5
+ from: string
6
+ }
7
+ function resolveModule(opts: ResolveModuleOptions): string | undefined {
8
+ let baseName = opts.baseName
9
+ if (!baseName.startsWith('./')) {
10
+ baseName = `./${baseName}`
11
+ }
12
+ return resolveModulePath(baseName, {
13
+ from: opts.from,
14
+ extensions: ['.ts', '.js', '.mts', '.mjs', '.tsx', '.jsx'],
15
+ try: true,
16
+ })
17
+ }
18
+
19
+ export function resolveEntry<
20
+ TRequired extends boolean,
21
+ TReturn = TRequired extends true ? string : string | undefined,
22
+ >(opts: {
23
+ type: string
24
+ configuredEntry?: string
25
+ defaultEntry: string
26
+ resolvedSrcDirectory: string
27
+ root: string
28
+ required: TRequired
29
+ }): TReturn {
30
+ let resolveOptions: ResolveModuleOptions
31
+
32
+ // if entry was not configured, use default relative to srcDirectory
33
+ if (!opts.configuredEntry) {
34
+ resolveOptions = {
35
+ baseName: opts.defaultEntry,
36
+ from: opts.resolvedSrcDirectory,
37
+ }
38
+ } else {
39
+ resolveOptions = {
40
+ baseName: opts.configuredEntry,
41
+ from: opts.root,
42
+ }
43
+ }
44
+
45
+ const resolvedEntry = resolveModule(resolveOptions)
46
+ if (opts.required && !resolvedEntry) {
47
+ throw new Error(
48
+ `Could not resolve entry for ${opts.type}: ${resolveOptions.baseName} in ${resolveOptions.from}`,
49
+ )
50
+ }
51
+ return resolvedEntry as TReturn
52
+ }
package/src/schema.ts CHANGED
@@ -1,142 +1,43 @@
1
1
  import path from 'node:path'
2
- import { existsSync } from 'node:fs'
3
2
  import { z } from 'zod'
4
3
  import { configSchema, getConfig } from '@tanstack/router-generator'
5
- import type { NitroConfig } from 'nitropack'
6
4
 
7
5
  const tsrConfig = configSchema
8
6
  .omit({ autoCodeSplitting: true })
9
7
  .partial()
10
8
  .extend({
9
+ // this is relative to vite root
10
+ // TODO why is this nested under tsr?
11
11
  srcDirectory: z.string().optional().default('src'),
12
12
  })
13
13
 
14
- export function createTanStackConfig<
15
- TFrameworkPlugin extends Record<string, unknown>,
16
- >(frameworkPlugin?: TFrameworkPlugin) {
17
- const schema = createTanStackStartOptionsSchema(frameworkPlugin)
18
-
19
- return {
20
- schema,
21
- parse: (opts?: z.input<typeof schema>) => {
22
- const options = schema.parse(opts)
23
-
24
- const srcDirectory = options.tsr.srcDirectory
25
-
26
- const routesDirectory =
27
- options.tsr.routesDirectory ?? path.join(srcDirectory, 'routes')
28
-
29
- const generatedRouteTree =
30
- options.tsr.generatedRouteTree ??
31
- path.join(srcDirectory, 'routeTree.gen.ts')
32
-
33
- const clientEntryPath = (() => {
34
- if (options.client.entry) {
35
- return path.join(srcDirectory, options.client.entry)
36
- }
37
-
38
- if (existsSync(path.join(srcDirectory, 'client.tsx'))) {
39
- return path.join(srcDirectory, 'client.tsx')
40
- }
41
-
42
- return '/~start/default-client-entry'
43
- })()
44
-
45
- const serverEntryPath = (() => {
46
- if (options.server.entry) {
47
- return path.join(srcDirectory, options.server.entry)
48
- }
49
-
50
- if (existsSync(path.join(srcDirectory, 'server.tsx'))) {
51
- return path.join(srcDirectory, 'server.tsx')
52
- }
14
+ export function parseStartConfig(
15
+ opts?: z.input<typeof tanstackStartOptionsSchema>,
16
+ ) {
17
+ const options = tanstackStartOptionsSchema.parse(opts)
53
18
 
54
- if (existsSync(path.join(srcDirectory, 'server.ts'))) {
55
- return path.join(srcDirectory, 'server.ts')
56
- }
19
+ const srcDirectory = options.tsr.srcDirectory
57
20
 
58
- if (existsSync(path.join(srcDirectory, 'server.js'))) {
59
- return path.join(srcDirectory, 'server.js')
60
- }
21
+ const routesDirectory =
22
+ options.tsr.routesDirectory ?? path.join(srcDirectory, 'routes')
61
23
 
62
- return '/~start/default-server-entry'
63
- })()
24
+ const generatedRouteTree =
25
+ options.tsr.generatedRouteTree ??
26
+ path.join(srcDirectory, 'routeTree.gen.ts')
64
27
 
65
- return {
66
- ...options,
67
- tsr: {
68
- ...options.tsr,
69
- ...getConfig({
70
- ...options.tsr,
71
- routesDirectory,
72
- generatedRouteTree,
73
- }),
74
- },
75
- clientEntryPath,
76
- serverEntryPath,
77
- }
28
+ return {
29
+ ...options,
30
+ tsr: {
31
+ ...options.tsr,
32
+ ...getConfig({
33
+ ...options.tsr,
34
+ routesDirectory,
35
+ generatedRouteTree,
36
+ }),
78
37
  },
79
38
  }
80
39
  }
81
40
 
82
- export function createTanStackStartOptionsSchema(
83
- frameworkPlugin: Record<string, unknown> = {},
84
- ) {
85
- return z
86
- .object({
87
- root: z.string().optional().default(process.cwd()),
88
- target: z.custom<NitroConfig['preset']>().optional(),
89
- ...frameworkPlugin,
90
- tsr: tsrConfig.optional().default({}),
91
- client: z
92
- .object({
93
- entry: z.string().optional(),
94
- base: z.string().optional().default('/_build'),
95
- })
96
- .optional()
97
- .default({}),
98
- server: z
99
- .object({
100
- entry: z.string().optional(),
101
- })
102
- .optional()
103
- .default({}),
104
- serverFns: z
105
- .object({
106
- base: z.string().optional().default('/_serverFn'),
107
- })
108
- .optional()
109
- .default({}),
110
- public: z
111
- .object({
112
- dir: z.string().optional().default('public'),
113
- base: z.string().optional().default('/'),
114
- })
115
- .optional()
116
- .default({}),
117
- pages: z.array(pageSchema).optional().default([]),
118
- sitemap: z
119
- .object({
120
- enabled: z.boolean().optional().default(true),
121
- host: z.string().optional(),
122
- outputPath: z.string().optional().default('sitemap.xml'),
123
- })
124
- .optional(),
125
- prerender: z
126
- .object({
127
- enabled: z.boolean().optional(),
128
- concurrency: z.number().optional(),
129
- filter: z.function().args(pageSchema).returns(z.any()).optional(),
130
- failOnError: z.boolean().optional(),
131
- })
132
- .and(pagePrerenderOptionsSchema.optional())
133
- .optional(),
134
- spa: spaSchema.optional(),
135
- })
136
- .optional()
137
- .default({})
138
- }
139
-
140
41
  const pageSitemapOptionsSchema = z.object({
141
42
  exclude: z.boolean().optional(),
142
43
  priority: z.number().min(0).max(1).optional(),
@@ -214,8 +115,74 @@ const spaSchema = z.object({
214
115
  })),
215
116
  })
216
117
 
217
- export const pageSchema = pageBaseSchema.extend({
118
+ const pageSchema = pageBaseSchema.extend({
218
119
  prerender: pagePrerenderOptionsSchema.optional(),
219
120
  })
220
121
 
122
+ const tanstackStartOptionsSchema = z
123
+ .object({
124
+ tsr: tsrConfig.optional().default({}),
125
+ router: z
126
+ .object({
127
+ // TODO naming?
128
+ entry: z.string().optional(),
129
+ })
130
+ .optional()
131
+ .default({}),
132
+ client: z
133
+ .object({
134
+ entry: z.string().optional(),
135
+ base: z.string().optional().default('/_build'),
136
+ })
137
+ .optional()
138
+ .default({}),
139
+ server: z
140
+ .object({
141
+ entry: z.string().optional(),
142
+ })
143
+ .optional()
144
+ .default({}),
145
+ serverFns: z
146
+ .object({
147
+ base: z.string().optional().default('/_serverFn'),
148
+ })
149
+ .optional()
150
+ .default({}),
151
+ public: z
152
+ .object({
153
+ dir: z.string().optional().default('public'),
154
+ base: z.string().optional().default('/'),
155
+ })
156
+ .optional()
157
+ .default({}),
158
+ pages: z.array(pageSchema).optional().default([]),
159
+ sitemap: z
160
+ .object({
161
+ enabled: z.boolean().optional().default(true),
162
+ host: z.string().optional(),
163
+ outputPath: z.string().optional().default('sitemap.xml'),
164
+ })
165
+ .optional(),
166
+ prerender: z
167
+ .object({
168
+ enabled: z.boolean().optional(),
169
+ concurrency: z.number().optional(),
170
+ filter: z.function().args(pageSchema).returns(z.any()).optional(),
171
+ failOnError: z.boolean().optional(),
172
+ })
173
+ .and(pagePrerenderOptionsSchema.optional())
174
+ .optional(),
175
+ spa: spaSchema.optional(),
176
+ vite: z
177
+ .object({ installDevServerMiddleware: z.boolean().optional() })
178
+ .optional(),
179
+ })
180
+ .optional()
181
+ .default({})
182
+
221
183
  export type Page = z.infer<typeof pageSchema>
184
+
185
+ export type TanStackStartInputConfig = z.input<
186
+ typeof tanstackStartOptionsSchema
187
+ >
188
+ export type TanStackStartOutputConfig = ReturnType<typeof parseStartConfig>
@@ -3,7 +3,8 @@ import { rootRouteId } from '@tanstack/router-core'
3
3
  import { VIRTUAL_MODULES } from '@tanstack/start-server-core'
4
4
  import { tsrSplit } from '@tanstack/router-plugin'
5
5
  import { resolveViteId } from '../utils'
6
- import type { PluginOption, ResolvedConfig, Rollup } from 'vite'
6
+ import { ENTRY_POINTS } from '../constants'
7
+ import type { PluginOption, Rollup } from 'vite'
7
8
  import type { RouterManagedTag } from '@tanstack/router-core'
8
9
 
9
10
  export const getCSSRecursively = (
@@ -40,17 +41,11 @@ export const getCSSRecursively = (
40
41
 
41
42
  const resolvedModuleId = resolveViteId(VIRTUAL_MODULES.startManifest)
42
43
  export function startManifestPlugin(opts: {
43
- clientEntry: string
44
+ getClientBundle: () => Rollup.OutputBundle
44
45
  }): PluginOption {
45
- let config: ResolvedConfig
46
-
47
46
  return {
48
47
  name: 'tanstack-start:start-manifest-plugin',
49
48
  enforce: 'pre',
50
-
51
- configResolved(resolvedConfig) {
52
- config = resolvedConfig
53
- },
54
49
  resolveId: {
55
50
  filter: { id: new RegExp(VIRTUAL_MODULES.startManifest) },
56
51
  handler(id) {
@@ -76,10 +71,10 @@ export function startManifestPlugin(opts: {
76
71
  const APP_BASE = globalThis.TSS_APP_BASE
77
72
 
78
73
  // If we're in development, return a dummy manifest
79
- if (config.command === 'serve') {
74
+ if (this.environment.config.command === 'serve') {
80
75
  return `export const tsrStartManifest = () => ({
81
76
  routes: {},
82
- clientEntry: '${joinURL(APP_BASE, opts.clientEntry)}',
77
+ clientEntry: '${joinURL(APP_BASE, '@id', ENTRY_POINTS.client)}',
83
78
  })`
84
79
  }
85
80
 
@@ -88,10 +83,9 @@ export function startManifestPlugin(opts: {
88
83
  const routeTreeRoutes = globalThis.TSS_ROUTES_MANIFEST.routes
89
84
 
90
85
  // This is where hydration will start, from when the SSR'd page reaches the browser.
91
- // By default, this'd be the virtual entry of `/~start/default-client-entry.tsx`, unless a custom entry is provided.
92
86
  let entryFile: Rollup.OutputChunk | undefined
93
87
 
94
- const clientBundle = globalThis.TSS_CLIENT_BUNDLE
88
+ const clientBundle = opts.getClientBundle()
95
89
  const chunksByFileName = new Map<string, Rollup.OutputChunk>()
96
90
 
97
91
  const routeChunks: Record<
@@ -222,12 +216,12 @@ export function startManifestPlugin(opts: {
222
216
 
223
217
  recurseRoute(routeTreeRoutes[rootRouteId]!)
224
218
 
225
- const routesManifest = {
219
+ const startManifest = {
226
220
  routes: routeTreeRoutes,
227
221
  clientEntry: joinURL(APP_BASE, entryFile.fileName),
228
222
  }
229
223
 
230
- return `export const tsrStartManifest = () => (${JSON.stringify(routesManifest)})`
224
+ return `export const tsrStartManifest = () => (${JSON.stringify(startManifest)})`
231
225
  }
232
226
 
233
227
  return undefined