@tanstack/start-plugin-core 1.20.3-alpha.1

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 (111) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +12 -0
  3. package/dist/cjs/compilers.cjs +402 -0
  4. package/dist/cjs/compilers.cjs.map +1 -0
  5. package/dist/cjs/compilers.d.cts +21 -0
  6. package/dist/cjs/extractHtmlScripts.cjs +35 -0
  7. package/dist/cjs/extractHtmlScripts.cjs.map +1 -0
  8. package/dist/cjs/extractHtmlScripts.d.cts +4 -0
  9. package/dist/cjs/index.cjs +15 -0
  10. package/dist/cjs/index.cjs.map +1 -0
  11. package/dist/cjs/index.d.cts +7 -0
  12. package/dist/cjs/nitro/build-nitro.cjs +18 -0
  13. package/dist/cjs/nitro/build-nitro.cjs.map +1 -0
  14. package/dist/cjs/nitro/build-nitro.d.cts +2 -0
  15. package/dist/cjs/nitro/build-sitemap.cjs +54 -0
  16. package/dist/cjs/nitro/build-sitemap.cjs.map +1 -0
  17. package/dist/cjs/nitro/build-sitemap.d.cts +9 -0
  18. package/dist/cjs/nitro/dev-server-plugin.cjs +128 -0
  19. package/dist/cjs/nitro/dev-server-plugin.cjs.map +1 -0
  20. package/dist/cjs/nitro/dev-server-plugin.d.cts +5 -0
  21. package/dist/cjs/nitro/nitro-plugin.cjs +128 -0
  22. package/dist/cjs/nitro/nitro-plugin.cjs.map +1 -0
  23. package/dist/cjs/nitro/nitro-plugin.d.cts +3 -0
  24. package/dist/cjs/plugin.cjs +117 -0
  25. package/dist/cjs/plugin.cjs.map +1 -0
  26. package/dist/cjs/plugin.d.cts +2713 -0
  27. package/dist/cjs/prerender.cjs +171 -0
  28. package/dist/cjs/prerender.cjs.map +1 -0
  29. package/dist/cjs/prerender.d.cts +8 -0
  30. package/dist/cjs/queue.cjs +131 -0
  31. package/dist/cjs/queue.cjs.map +1 -0
  32. package/dist/cjs/queue.d.cts +32 -0
  33. package/dist/cjs/routesManifestPlugin.cjs +165 -0
  34. package/dist/cjs/routesManifestPlugin.cjs.map +1 -0
  35. package/dist/cjs/routesManifestPlugin.d.cts +3 -0
  36. package/dist/cjs/schema.cjs +136 -0
  37. package/dist/cjs/schema.cjs.map +1 -0
  38. package/dist/cjs/schema.d.cts +8128 -0
  39. package/dist/cjs/start-compiler-plugin.cjs +72 -0
  40. package/dist/cjs/start-compiler-plugin.cjs.map +1 -0
  41. package/dist/cjs/start-compiler-plugin.d.cts +13 -0
  42. package/dist/cjs/start-server-routes-plugin/config.d.cts +49 -0
  43. package/dist/cjs/start-server-routes-plugin/plugin.cjs +608 -0
  44. package/dist/cjs/start-server-routes-plugin/plugin.cjs.map +1 -0
  45. package/dist/cjs/start-server-routes-plugin/plugin.d.cts +3 -0
  46. package/dist/cjs/start-server-routes-plugin/template.cjs +111 -0
  47. package/dist/cjs/start-server-routes-plugin/template.cjs.map +1 -0
  48. package/dist/cjs/start-server-routes-plugin/template.d.cts +34 -0
  49. package/dist/esm/compilers.d.ts +21 -0
  50. package/dist/esm/compilers.js +384 -0
  51. package/dist/esm/compilers.js.map +1 -0
  52. package/dist/esm/extractHtmlScripts.d.ts +4 -0
  53. package/dist/esm/extractHtmlScripts.js +18 -0
  54. package/dist/esm/extractHtmlScripts.js.map +1 -0
  55. package/dist/esm/index.d.ts +7 -0
  56. package/dist/esm/index.js +15 -0
  57. package/dist/esm/index.js.map +1 -0
  58. package/dist/esm/nitro/build-nitro.d.ts +2 -0
  59. package/dist/esm/nitro/build-nitro.js +18 -0
  60. package/dist/esm/nitro/build-nitro.js.map +1 -0
  61. package/dist/esm/nitro/build-sitemap.d.ts +9 -0
  62. package/dist/esm/nitro/build-sitemap.js +54 -0
  63. package/dist/esm/nitro/build-sitemap.js.map +1 -0
  64. package/dist/esm/nitro/dev-server-plugin.d.ts +5 -0
  65. package/dist/esm/nitro/dev-server-plugin.js +128 -0
  66. package/dist/esm/nitro/dev-server-plugin.js.map +1 -0
  67. package/dist/esm/nitro/nitro-plugin.d.ts +3 -0
  68. package/dist/esm/nitro/nitro-plugin.js +128 -0
  69. package/dist/esm/nitro/nitro-plugin.js.map +1 -0
  70. package/dist/esm/plugin.d.ts +2713 -0
  71. package/dist/esm/plugin.js +117 -0
  72. package/dist/esm/plugin.js.map +1 -0
  73. package/dist/esm/prerender.d.ts +8 -0
  74. package/dist/esm/prerender.js +171 -0
  75. package/dist/esm/prerender.js.map +1 -0
  76. package/dist/esm/queue.d.ts +32 -0
  77. package/dist/esm/queue.js +131 -0
  78. package/dist/esm/queue.js.map +1 -0
  79. package/dist/esm/routesManifestPlugin.d.ts +3 -0
  80. package/dist/esm/routesManifestPlugin.js +165 -0
  81. package/dist/esm/routesManifestPlugin.js.map +1 -0
  82. package/dist/esm/schema.d.ts +8128 -0
  83. package/dist/esm/schema.js +136 -0
  84. package/dist/esm/schema.js.map +1 -0
  85. package/dist/esm/start-compiler-plugin.d.ts +13 -0
  86. package/dist/esm/start-compiler-plugin.js +72 -0
  87. package/dist/esm/start-compiler-plugin.js.map +1 -0
  88. package/dist/esm/start-server-routes-plugin/config.d.ts +49 -0
  89. package/dist/esm/start-server-routes-plugin/plugin.d.ts +3 -0
  90. package/dist/esm/start-server-routes-plugin/plugin.js +608 -0
  91. package/dist/esm/start-server-routes-plugin/plugin.js.map +1 -0
  92. package/dist/esm/start-server-routes-plugin/template.d.ts +34 -0
  93. package/dist/esm/start-server-routes-plugin/template.js +111 -0
  94. package/dist/esm/start-server-routes-plugin/template.js.map +1 -0
  95. package/package.json +72 -0
  96. package/src/compilers.ts +759 -0
  97. package/src/extractHtmlScripts.ts +19 -0
  98. package/src/index.ts +15 -0
  99. package/src/nitro/build-nitro.ts +27 -0
  100. package/src/nitro/build-sitemap.ts +79 -0
  101. package/src/nitro/dev-server-plugin.ts +159 -0
  102. package/src/nitro/nitro-plugin.ts +161 -0
  103. package/src/plugin.ts +145 -0
  104. package/src/prerender.ts +245 -0
  105. package/src/queue.ts +153 -0
  106. package/src/routesManifestPlugin.ts +216 -0
  107. package/src/schema.ts +193 -0
  108. package/src/start-compiler-plugin.ts +111 -0
  109. package/src/start-server-routes-plugin/config.ts +8 -0
  110. package/src/start-server-routes-plugin/plugin.ts +890 -0
  111. package/src/start-server-routes-plugin/template.ts +164 -0
@@ -0,0 +1,19 @@
1
+ import * as cheerio from 'cheerio'
2
+
3
+ export function extractHtmlScripts(
4
+ html: string,
5
+ ): Array<{ content?: string; src?: string }> {
6
+ const $ = cheerio.load(html)
7
+ const scripts: Array<{ content?: string; src?: string }> = []
8
+
9
+ $('script').each((_, element) => {
10
+ const src = $(element).attr('src')
11
+ const content = $(element).html() ?? undefined
12
+ scripts.push({
13
+ src,
14
+ content,
15
+ })
16
+ })
17
+
18
+ return scripts
19
+ }
package/src/index.ts ADDED
@@ -0,0 +1,15 @@
1
+ export type { CompileOptions, IdentifierConfig } from './compilers'
2
+
3
+ export { compileStartOutputFactory } from './compilers'
4
+
5
+ export type { PagesJson } from './nitro/build-sitemap'
6
+ export { buildSitemap } from './nitro/build-sitemap'
7
+
8
+ export {
9
+ createTanStackConfig,
10
+ createTanStackStartOptionsSchema,
11
+ pageSchema,
12
+ } from './schema'
13
+
14
+ export { TanStackStartVitePluginCore } from './plugin'
15
+ export { TanStackStartServerRoutesVite } from './start-server-routes-plugin/plugin'
@@ -0,0 +1,27 @@
1
+ import { promises as fsp } from 'node:fs'
2
+ import path from 'node:path'
3
+ import { copyPublicAssets, prepare } from 'nitropack'
4
+ import type { Nitro } from 'nitropack'
5
+
6
+ export async function buildNitroEnvironment(
7
+ nitro: Nitro,
8
+ build: () => Promise<any>,
9
+ ) {
10
+ await prepare(nitro)
11
+ await copyPublicAssets(nitro)
12
+ await build()
13
+
14
+ const publicDir = nitro.options.output.publicDir
15
+
16
+ // As a part of the build process, the `.vite/` directory
17
+ // is copied over from `.tanstack-start/build/client-dist/`
18
+ // to the `publicDir` (e.g. `.output/public/`).
19
+ // This directory (containing the vite manifest) should not be
20
+ // included in the final build, so we remove it here.
21
+ const viteDir = path.resolve(publicDir, '.vite')
22
+ if (await fsp.stat(viteDir).catch(() => false)) {
23
+ await fsp.rm(viteDir, { recursive: true, force: true })
24
+ }
25
+
26
+ await nitro.close()
27
+ }
@@ -0,0 +1,79 @@
1
+ import { writeFileSync } from 'node:fs'
2
+ import { resolve } from 'node:path'
3
+ import { create } from 'xmlbuilder2'
4
+ import type { XMLBuilder } from 'xmlbuilder2/lib/interfaces'
5
+
6
+ export type PagesJson = {
7
+ page: string
8
+ lastMod: string
9
+ }
10
+
11
+ export async function buildSitemap({
12
+ host,
13
+ routes,
14
+ outputDir,
15
+ }: {
16
+ host: string
17
+ routes: Array<string | undefined> | (() => Promise<Array<string | undefined>>)
18
+ outputDir: string
19
+ }) {
20
+ const routeList: Array<string> = await optionHasRoutes(routes)
21
+
22
+ if (routeList.length) {
23
+ const slash = checkSlash(host)
24
+ const sitemapData: Array<PagesJson> = routeList.map((page: string) => ({
25
+ page: `${host}${slash}${page.replace(/^\/+/g, '')}`,
26
+ lastMod: new Date().toISOString().split('T')[0]!,
27
+ }))
28
+
29
+ const sitemap = createXml('urlset')
30
+
31
+ for (const item of sitemapData) {
32
+ const page = sitemap.ele('url')
33
+ page.ele('loc').txt(item.page)
34
+ page.ele('lastmod').txt(item.lastMod)
35
+ }
36
+
37
+ const mapPath = `${resolve(outputDir)}/sitemap.xml`
38
+ try {
39
+ console.log(`Writing sitemap at ${mapPath}`)
40
+ writeFileSync(mapPath, sitemap.end({ prettyPrint: true }))
41
+ } catch (e) {
42
+ console.error(`Unable to write file at ${mapPath}`, e)
43
+ }
44
+ }
45
+ }
46
+
47
+ function createXml(elementName: 'urlset' | 'sitemapindex'): XMLBuilder {
48
+ return create({ version: '1.0', encoding: 'UTF-8' })
49
+ .ele(elementName, {
50
+ xmlns: 'https://www.sitemaps.org/schemas/sitemap/0.9',
51
+ })
52
+ .com(`This file was automatically generated by Analog.`)
53
+ }
54
+
55
+ function checkSlash(host: string): string {
56
+ const finalChar = host.slice(-1)
57
+ return finalChar === '/' ? '' : '/'
58
+ }
59
+
60
+ async function optionHasRoutes(
61
+ routes:
62
+ | Array<string | undefined>
63
+ | (() => Promise<Array<string | undefined>>),
64
+ ): Promise<Array<string>> {
65
+ let routeList: Array<string | undefined>
66
+
67
+ if (typeof routes === 'function') {
68
+ // returns an array or undefined
69
+ routeList = await routes()
70
+ } else if (Array.isArray(routes)) {
71
+ // returns an array of strings
72
+ routeList = routes
73
+ } else {
74
+ // default it to an empty of array
75
+ routeList = []
76
+ }
77
+
78
+ return routeList.filter(Boolean) as Array<string>
79
+ }
@@ -0,0 +1,159 @@
1
+ import { createEvent, getHeader, sendWebResponse } from 'h3'
2
+ import { isRunnableDevEnvironment } from 'vite'
3
+ import { extractHtmlScripts } from '../extractHtmlScripts'
4
+ import type { Connect, Plugin, ViteDevServer } from 'vite'
5
+
6
+ declare global {
7
+ // eslint-disable-next-line no-var
8
+ var TSS_INJECTED_HEAD_SCRIPTS: string | undefined
9
+ }
10
+
11
+ export function devServerPlugin(): Plugin {
12
+ // let config: UserConfig
13
+ let isTest = false
14
+
15
+ return {
16
+ name: 'start-dev-ssr-plugin',
17
+ config(userConfig, { mode }) {
18
+ // config = userConfig
19
+ isTest = isTest ? isTest : mode === 'test'
20
+ },
21
+ configureServer(viteDevServer) {
22
+ if (isTest) {
23
+ return
24
+ }
25
+
26
+ ;(globalThis as any).viteDevServer = viteDevServer
27
+
28
+ return () => {
29
+ remove_html_middlewares(viteDevServer.middlewares)
30
+ let cachedScripts: string | undefined
31
+ viteDevServer.middlewares.use(async (req, res) => {
32
+ const event = createEvent(req, res)
33
+ const serverEnv = viteDevServer.environments['server']
34
+
35
+ try {
36
+ if (!serverEnv || !isRunnableDevEnvironment(serverEnv)) {
37
+ throw new Error('Server environment not found')
38
+ }
39
+ if (cachedScripts === undefined) {
40
+ const templateHtml = `<html><head></head><body></body></html>`
41
+ const transformedHtml = await viteDevServer.transformIndexHtml(
42
+ req.url || '/',
43
+ templateHtml,
44
+ )
45
+ const scripts = extractHtmlScripts(transformedHtml)
46
+ globalThis.TSS_INJECTED_HEAD_SCRIPTS = scripts
47
+ .map((script) => script.content ?? '')
48
+ .join(';')
49
+ }
50
+ const serverEntry = await serverEnv.runner.import(
51
+ '/~start/server-entry',
52
+ )
53
+ const response = await serverEntry['default'](event)
54
+
55
+ return sendWebResponse(event, response)
56
+ } catch (e) {
57
+ console.error(e)
58
+ viteDevServer.ssrFixStacktrace(e as Error)
59
+
60
+ if (
61
+ getHeader(event, 'content-type')?.includes('application/json')
62
+ ) {
63
+ return sendWebResponse(
64
+ event,
65
+ new Response(
66
+ JSON.stringify(
67
+ {
68
+ status: 500,
69
+ error: 'Internal Server Error',
70
+ message:
71
+ 'An unexpected error occurred. Please try again later.',
72
+ timestamp: new Date().toISOString(),
73
+ },
74
+ null,
75
+ 2,
76
+ ),
77
+ {
78
+ status: 500,
79
+ headers: {
80
+ 'Content-Type': 'application/json',
81
+ },
82
+ },
83
+ ),
84
+ )
85
+ }
86
+
87
+ return sendWebResponse(
88
+ event,
89
+ new Response(
90
+ `
91
+ <!DOCTYPE html>
92
+ <html lang="en">
93
+ <head>
94
+ <meta charset="UTF-8" />
95
+ <title>Error</title>
96
+ <script type="module">
97
+ import { ErrorOverlay } from '/@vite/client'
98
+ document.body.appendChild(new ErrorOverlay(${JSON.stringify(
99
+ prepareError(req, e),
100
+ ).replace(/</g, '\\u003c')}))
101
+ </script>
102
+ </head>
103
+ <body>
104
+ </body>
105
+ </html>
106
+ `,
107
+ {
108
+ status: 500,
109
+ headers: {
110
+ 'Content-Type': 'text/html',
111
+ },
112
+ },
113
+ ),
114
+ )
115
+ }
116
+ })
117
+ }
118
+ },
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Removes Vite internal middleware
124
+ *
125
+ * @param server
126
+ */
127
+ function remove_html_middlewares(server: ViteDevServer['middlewares']) {
128
+ const html_middlewares = [
129
+ 'viteIndexHtmlMiddleware',
130
+ 'vite404Middleware',
131
+ 'viteSpaFallbackMiddleware',
132
+ ]
133
+ for (let i = server.stack.length - 1; i > 0; i--) {
134
+ if (
135
+ html_middlewares.includes(
136
+ // @ts-expect-error
137
+ server.stack[i].handle.name,
138
+ )
139
+ ) {
140
+ server.stack.splice(i, 1)
141
+ }
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Formats error for SSR message in error overlay
147
+ * @param req
148
+ * @param error
149
+ * @returns
150
+ */
151
+ function prepareError(req: Connect.IncomingMessage, error: unknown) {
152
+ const e = error as Error
153
+ return {
154
+ message: `An error occured while server rendering ${req.url}:\n\n\t${
155
+ typeof e === 'string' ? e : e.message
156
+ } `,
157
+ stack: typeof e === 'string' ? '' : e.stack,
158
+ }
159
+ }
@@ -0,0 +1,161 @@
1
+ import path from 'node:path'
2
+ import { rmSync } from 'node:fs'
3
+ import { build, createNitro } from 'nitropack'
4
+ import { dirname, resolve } from 'pathe'
5
+ import { clientDistDir, ssrEntryFile } from '../plugin'
6
+ import { prerender } from '../prerender'
7
+ import { devServerPlugin } from './dev-server-plugin'
8
+ import { buildNitroEnvironment } from './build-nitro'
9
+ import type { EnvironmentOptions, PluginOption, Rollup } from 'vite'
10
+ import type { NitroConfig } from 'nitropack'
11
+ import type { TanStackStartOutputConfig } from '../plugin'
12
+
13
+ export function nitroPlugin(
14
+ options: TanStackStartOutputConfig,
15
+ getSsrBundle: () => Rollup.OutputBundle,
16
+ ): Array<PluginOption> {
17
+ const buildPreset =
18
+ process.env['START_TARGET'] ?? (options.target as string | undefined)
19
+
20
+ return [
21
+ devServerPlugin(),
22
+ {
23
+ name: 'tanstack-vite-plugin-nitro',
24
+ configEnvironment(name) {
25
+ if (name === 'server') {
26
+ return {
27
+ build: {
28
+ commonjsOptions: {
29
+ include: [],
30
+ },
31
+ ssr: true,
32
+ sourcemap: true,
33
+ rollupOptions: {
34
+ input: '/~start/server-entry',
35
+ },
36
+ },
37
+ } satisfies EnvironmentOptions
38
+ }
39
+
40
+ return null
41
+ },
42
+ config() {
43
+ return {
44
+ builder: {
45
+ sharedPlugins: true,
46
+ async buildApp(builder) {
47
+ const clientEnv = builder.environments['client']
48
+ const serverEnv = builder.environments['server']
49
+
50
+ if (!clientEnv) {
51
+ throw new Error('Client environment not found')
52
+ }
53
+
54
+ if (!serverEnv) {
55
+ throw new Error('SSR environment not found')
56
+ }
57
+
58
+ const clientOutputDir = resolve(options.root, clientDistDir)
59
+ rmSync(clientOutputDir, { recursive: true, force: true })
60
+ await builder.build(clientEnv)
61
+
62
+ await builder.build(serverEnv)
63
+
64
+ const nitroConfig: NitroConfig = {
65
+ dev: false,
66
+ // TODO do we need this? should this be made configurable?
67
+ compatibilityDate: '2024-11-19',
68
+ logLevel: 3,
69
+ preset: buildPreset,
70
+ publicAssets: [
71
+ {
72
+ dir: path.resolve(options.root, clientDistDir),
73
+ },
74
+ ],
75
+ typescript: {
76
+ generateTsConfig: false,
77
+ },
78
+ prerender: undefined,
79
+ renderer: ssrEntryFile,
80
+ rollupConfig: {
81
+ plugins: [virtualBundlePlugin(getSsrBundle())],
82
+ },
83
+ }
84
+
85
+ const nitro = await createNitro(nitroConfig)
86
+
87
+ await buildNitroEnvironment(nitro, () => build(nitro))
88
+
89
+ if (options.prerender?.enabled) {
90
+ await prerender({
91
+ options,
92
+ nitro,
93
+ builder,
94
+ })
95
+ }
96
+
97
+ // if (nitroConfig.prerender?.routes?.length && options.sitemap) {
98
+ // console.log('Building Sitemap...')
99
+ // // sitemap needs to be built after all directories are built
100
+ // await buildSitemap({
101
+ // host: options.sitemap.host,
102
+ // routes: nitroConfig.prerender.routes,
103
+ // outputDir: resolve(options.root, 'dist/public'),
104
+ // })
105
+ // }
106
+
107
+ // console.log(
108
+ // `\n\n✅ Client and server bundles successfully built.`,
109
+ // )
110
+ },
111
+ },
112
+ }
113
+ },
114
+ },
115
+ ]
116
+ }
117
+
118
+ function virtualBundlePlugin(ssrBundle: Rollup.OutputBundle): Rollup.Plugin {
119
+ type VirtualModule = { code: string; map: string | null }
120
+ const _modules = new Map<string, VirtualModule>()
121
+
122
+ // group chunks and source maps
123
+ for (const [fileName, content] of Object.entries(ssrBundle)) {
124
+ if (content.type === 'chunk') {
125
+ const virtualModule: VirtualModule = {
126
+ code: content.code,
127
+ map: null,
128
+ }
129
+ const maybeMap = ssrBundle[`${fileName}.map`]
130
+ if (maybeMap && maybeMap.type === 'asset') {
131
+ virtualModule.map = maybeMap.source as string
132
+ }
133
+ _modules.set(fileName, virtualModule)
134
+ _modules.set(resolve(fileName), virtualModule)
135
+ }
136
+ }
137
+
138
+ return {
139
+ name: 'virtual-bundle',
140
+ resolveId(id, importer) {
141
+ if (_modules.has(id)) {
142
+ return resolve(id)
143
+ }
144
+
145
+ if (importer) {
146
+ const resolved = resolve(dirname(importer), id)
147
+ if (_modules.has(resolved)) {
148
+ return resolved
149
+ }
150
+ }
151
+ return null
152
+ },
153
+ load(id) {
154
+ const m = _modules.get(id)
155
+ if (!m) {
156
+ return null
157
+ }
158
+ return m
159
+ },
160
+ }
161
+ }
package/src/plugin.ts ADDED
@@ -0,0 +1,145 @@
1
+ import path from 'node:path'
2
+ import { createNitro } from 'nitropack'
3
+ import {
4
+ createTanStackConfig,
5
+ createTanStackStartOptionsSchema,
6
+ } from './schema'
7
+ import { nitroPlugin } from './nitro/nitro-plugin'
8
+ import { startManifestPlugin } from './routesManifestPlugin'
9
+ import { TanStackStartCompilerPlugin } from './start-compiler-plugin'
10
+ import type { PluginOption, Rollup } from 'vite'
11
+ import type { z } from 'zod'
12
+ import type { CompileStartFrameworkOptions } from './compilers'
13
+
14
+ const TanStackStartOptionsSchema = createTanStackStartOptionsSchema()
15
+ export type TanStackStartInputConfig = z.input<
16
+ typeof TanStackStartOptionsSchema
17
+ >
18
+
19
+ const defaultConfig = createTanStackConfig()
20
+ export function getTanStackStartOptions(opts?: TanStackStartInputConfig) {
21
+ return defaultConfig.parse(opts)
22
+ }
23
+
24
+ export type TanStackStartOutputConfig = ReturnType<
25
+ typeof getTanStackStartOptions
26
+ >
27
+
28
+ export const clientDistDir = '.tanstack-start/build/client-dist'
29
+ export const ssrEntryFile = 'ssr.mjs'
30
+
31
+ // this needs to live outside of the TanStackStartVitePluginCore since it will be invoked multiple times by vite
32
+ let ssrBundle: Rollup.OutputBundle
33
+
34
+ export function TanStackStartVitePluginCore(
35
+ framework: CompileStartFrameworkOptions,
36
+ opts: TanStackStartOutputConfig,
37
+ ): Array<PluginOption> {
38
+ return [
39
+ {
40
+ name: 'tanstack-start-core:config-client',
41
+ async config() {
42
+ const nitroOutputPublicDir = await (async () => {
43
+ // Create a dummy nitro app to get the resolved public output path
44
+ const dummyNitroApp = await createNitro({
45
+ preset: opts.target,
46
+ compatibilityDate: '2024-12-01',
47
+ })
48
+
49
+ const nitroOutputPublicDir = dummyNitroApp.options.output.publicDir
50
+ await dummyNitroApp.close()
51
+
52
+ return nitroOutputPublicDir
53
+ })()
54
+
55
+ return {
56
+ environments: {
57
+ client: {
58
+ consumer: 'client',
59
+ build: {
60
+ manifest: true,
61
+ rollupOptions: {
62
+ input: {
63
+ main: opts.clientEntryPath,
64
+ },
65
+ output: {
66
+ dir: path.resolve(opts.root, clientDistDir),
67
+ },
68
+ // TODO this should be removed
69
+ external: ['node:fs', 'node:path', 'node:os', 'node:crypto'],
70
+ },
71
+ },
72
+ },
73
+ server: {
74
+ consumer: 'server',
75
+ build: {
76
+ ssr: true,
77
+ // we don't write to the file system as the below 'capture-output' plugin will
78
+ // capture the output and write it to the virtual file system
79
+ write: false,
80
+ copyPublicDir: false,
81
+ rollupOptions: {
82
+ output: {
83
+ entryFileNames: ssrEntryFile,
84
+ },
85
+ plugins: [
86
+ {
87
+ name: 'capture-output',
88
+ generateBundle(options, bundle) {
89
+ // TODO can this hook be called more than once?
90
+ ssrBundle = bundle
91
+ },
92
+ },
93
+ ],
94
+ },
95
+ commonjsOptions: {
96
+ include: [/node_modules/],
97
+ },
98
+ },
99
+ },
100
+ },
101
+ resolve: {
102
+ noExternal: [
103
+ '@tanstack/start-client',
104
+ '@tanstack/start-client-core',
105
+ '@tanstack/start-server',
106
+ '@tanstack/start-server-core',
107
+ '@tanstack/start-server-functions-fetcher',
108
+ '@tanstack/start-server-functions-client',
109
+ '@tanstack/start-server-functions-server',
110
+ '@tanstack/start-router-manifest',
111
+ '@tanstack/start-config',
112
+ '@tanstack/server-functions-plugin',
113
+ 'tanstack:start-manifest',
114
+ 'tanstack:server-fn-manifest',
115
+ 'nitropack',
116
+ '@tanstack/**',
117
+ ],
118
+ external: ['undici'],
119
+ },
120
+ /* prettier-ignore */
121
+ define: {
122
+ ...injectDefineEnv('TSS_PUBLIC_BASE', opts.public.base),
123
+ ...injectDefineEnv('TSS_CLIENT_BASE', opts.client.base),
124
+ ...injectDefineEnv('TSS_CLIENT_ENTRY', opts.clientEntryPath),
125
+ ...injectDefineEnv('TSS_SERVER_FN_BASE', opts.serverFns.base),
126
+ ...injectDefineEnv('TSS_OUTPUT_PUBLIC_DIR', nitroOutputPublicDir),
127
+ },
128
+ }
129
+ },
130
+ },
131
+ TanStackStartCompilerPlugin(framework),
132
+ startManifestPlugin(opts),
133
+ nitroPlugin(opts, () => ssrBundle),
134
+ ]
135
+ }
136
+
137
+ function injectDefineEnv<TKey extends string, TValue extends string>(
138
+ key: TKey,
139
+ value: TValue,
140
+ ): { [P in `process.env.${TKey}` | `import.meta.env.${TKey}`]: TValue } {
141
+ return {
142
+ [`process.env.${key}`]: JSON.stringify(value),
143
+ [`import.meta.env.${key}`]: JSON.stringify(value),
144
+ } as { [P in `process.env.${TKey}` | `import.meta.env.${TKey}`]: TValue }
145
+ }