@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.
- package/LICENSE +21 -0
- package/README.md +12 -0
- package/dist/cjs/compilers.cjs +402 -0
- package/dist/cjs/compilers.cjs.map +1 -0
- package/dist/cjs/compilers.d.cts +21 -0
- package/dist/cjs/extractHtmlScripts.cjs +35 -0
- package/dist/cjs/extractHtmlScripts.cjs.map +1 -0
- package/dist/cjs/extractHtmlScripts.d.cts +4 -0
- package/dist/cjs/index.cjs +15 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/cjs/index.d.cts +7 -0
- package/dist/cjs/nitro/build-nitro.cjs +18 -0
- package/dist/cjs/nitro/build-nitro.cjs.map +1 -0
- package/dist/cjs/nitro/build-nitro.d.cts +2 -0
- package/dist/cjs/nitro/build-sitemap.cjs +54 -0
- package/dist/cjs/nitro/build-sitemap.cjs.map +1 -0
- package/dist/cjs/nitro/build-sitemap.d.cts +9 -0
- package/dist/cjs/nitro/dev-server-plugin.cjs +128 -0
- package/dist/cjs/nitro/dev-server-plugin.cjs.map +1 -0
- package/dist/cjs/nitro/dev-server-plugin.d.cts +5 -0
- package/dist/cjs/nitro/nitro-plugin.cjs +128 -0
- package/dist/cjs/nitro/nitro-plugin.cjs.map +1 -0
- package/dist/cjs/nitro/nitro-plugin.d.cts +3 -0
- package/dist/cjs/plugin.cjs +117 -0
- package/dist/cjs/plugin.cjs.map +1 -0
- package/dist/cjs/plugin.d.cts +2713 -0
- package/dist/cjs/prerender.cjs +171 -0
- package/dist/cjs/prerender.cjs.map +1 -0
- package/dist/cjs/prerender.d.cts +8 -0
- package/dist/cjs/queue.cjs +131 -0
- package/dist/cjs/queue.cjs.map +1 -0
- package/dist/cjs/queue.d.cts +32 -0
- package/dist/cjs/routesManifestPlugin.cjs +165 -0
- package/dist/cjs/routesManifestPlugin.cjs.map +1 -0
- package/dist/cjs/routesManifestPlugin.d.cts +3 -0
- package/dist/cjs/schema.cjs +136 -0
- package/dist/cjs/schema.cjs.map +1 -0
- package/dist/cjs/schema.d.cts +8128 -0
- package/dist/cjs/start-compiler-plugin.cjs +72 -0
- package/dist/cjs/start-compiler-plugin.cjs.map +1 -0
- package/dist/cjs/start-compiler-plugin.d.cts +13 -0
- package/dist/cjs/start-server-routes-plugin/config.d.cts +49 -0
- package/dist/cjs/start-server-routes-plugin/plugin.cjs +608 -0
- package/dist/cjs/start-server-routes-plugin/plugin.cjs.map +1 -0
- package/dist/cjs/start-server-routes-plugin/plugin.d.cts +3 -0
- package/dist/cjs/start-server-routes-plugin/template.cjs +111 -0
- package/dist/cjs/start-server-routes-plugin/template.cjs.map +1 -0
- package/dist/cjs/start-server-routes-plugin/template.d.cts +34 -0
- package/dist/esm/compilers.d.ts +21 -0
- package/dist/esm/compilers.js +384 -0
- package/dist/esm/compilers.js.map +1 -0
- package/dist/esm/extractHtmlScripts.d.ts +4 -0
- package/dist/esm/extractHtmlScripts.js +18 -0
- package/dist/esm/extractHtmlScripts.js.map +1 -0
- package/dist/esm/index.d.ts +7 -0
- package/dist/esm/index.js +15 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/nitro/build-nitro.d.ts +2 -0
- package/dist/esm/nitro/build-nitro.js +18 -0
- package/dist/esm/nitro/build-nitro.js.map +1 -0
- package/dist/esm/nitro/build-sitemap.d.ts +9 -0
- package/dist/esm/nitro/build-sitemap.js +54 -0
- package/dist/esm/nitro/build-sitemap.js.map +1 -0
- package/dist/esm/nitro/dev-server-plugin.d.ts +5 -0
- package/dist/esm/nitro/dev-server-plugin.js +128 -0
- package/dist/esm/nitro/dev-server-plugin.js.map +1 -0
- package/dist/esm/nitro/nitro-plugin.d.ts +3 -0
- package/dist/esm/nitro/nitro-plugin.js +128 -0
- package/dist/esm/nitro/nitro-plugin.js.map +1 -0
- package/dist/esm/plugin.d.ts +2713 -0
- package/dist/esm/plugin.js +117 -0
- package/dist/esm/plugin.js.map +1 -0
- package/dist/esm/prerender.d.ts +8 -0
- package/dist/esm/prerender.js +171 -0
- package/dist/esm/prerender.js.map +1 -0
- package/dist/esm/queue.d.ts +32 -0
- package/dist/esm/queue.js +131 -0
- package/dist/esm/queue.js.map +1 -0
- package/dist/esm/routesManifestPlugin.d.ts +3 -0
- package/dist/esm/routesManifestPlugin.js +165 -0
- package/dist/esm/routesManifestPlugin.js.map +1 -0
- package/dist/esm/schema.d.ts +8128 -0
- package/dist/esm/schema.js +136 -0
- package/dist/esm/schema.js.map +1 -0
- package/dist/esm/start-compiler-plugin.d.ts +13 -0
- package/dist/esm/start-compiler-plugin.js +72 -0
- package/dist/esm/start-compiler-plugin.js.map +1 -0
- package/dist/esm/start-server-routes-plugin/config.d.ts +49 -0
- package/dist/esm/start-server-routes-plugin/plugin.d.ts +3 -0
- package/dist/esm/start-server-routes-plugin/plugin.js +608 -0
- package/dist/esm/start-server-routes-plugin/plugin.js.map +1 -0
- package/dist/esm/start-server-routes-plugin/template.d.ts +34 -0
- package/dist/esm/start-server-routes-plugin/template.js +111 -0
- package/dist/esm/start-server-routes-plugin/template.js.map +1 -0
- package/package.json +72 -0
- package/src/compilers.ts +759 -0
- package/src/extractHtmlScripts.ts +19 -0
- package/src/index.ts +15 -0
- package/src/nitro/build-nitro.ts +27 -0
- package/src/nitro/build-sitemap.ts +79 -0
- package/src/nitro/dev-server-plugin.ts +159 -0
- package/src/nitro/nitro-plugin.ts +161 -0
- package/src/plugin.ts +145 -0
- package/src/prerender.ts +245 -0
- package/src/queue.ts +153 -0
- package/src/routesManifestPlugin.ts +216 -0
- package/src/schema.ts +193 -0
- package/src/start-compiler-plugin.ts +111 -0
- package/src/start-server-routes-plugin/config.ts +8 -0
- package/src/start-server-routes-plugin/plugin.ts +890 -0
- 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
|
+
}
|