vitrify 0.2.0 → 0.2.3
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/dist/bin/cli.js +4 -3
- package/dist/{vue → frameworks/vue}/fastify-ssr-plugin.js +0 -0
- package/dist/{vue → frameworks/vue}/prerender.js +1 -1
- package/dist/{vue → frameworks/vue}/server.js +0 -0
- package/dist/index.js +19 -4
- package/dist/plugins/quasar.js +1 -1
- package/dist/types/{vue → frameworks/vue}/fastify-ssr-plugin.d.ts +1 -1
- package/dist/types/{vue → frameworks/vue}/prerender.d.ts +1 -1
- package/dist/types/{vue → frameworks/vue}/server.d.ts +1 -1
- package/package.json +2 -3
- package/src/node/app-urls.ts +38 -0
- package/src/node/bin/build.ts +87 -0
- package/src/node/bin/cli.ts +157 -0
- package/src/node/bin/dev.ts +138 -0
- package/src/node/bin/run.ts +47 -0
- package/src/node/bin/test.ts +24 -0
- package/src/node/frameworks/vue/fastify-ssr-plugin.ts +137 -0
- package/src/node/frameworks/vue/prerender.ts +49 -0
- package/src/node/frameworks/vue/server.ts +38 -0
- package/src/node/helpers/logger.ts +142 -0
- package/src/node/helpers/routes.ts +29 -0
- package/src/node/helpers/ssr.ts.bak +52 -0
- package/src/node/helpers/utils.ts +37 -0
- package/src/node/index.ts +404 -0
- package/src/node/plugins/index.ts +12 -0
- package/src/node/plugins/quasar.ts +387 -0
- package/src/node/vitrify-config.ts +79 -0
- package/src/vite/vue/ssr/fastify-ssr-plugin.ts +1 -1
- package/src/vite/vue/ssr/prerender.ts +1 -1
- package/src/vite/vue/ssr/server.ts +6 -3
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
FastifyPluginCallback,
|
|
3
|
+
FastifyRequest,
|
|
4
|
+
FastifyReply
|
|
5
|
+
} from 'fastify'
|
|
6
|
+
import fastifyStatic from 'fastify-static'
|
|
7
|
+
import { readFileSync } from 'fs'
|
|
8
|
+
// import { injectSsrContext } from '../helpers/ssr.js'
|
|
9
|
+
import type { ViteDevServer } from 'vite'
|
|
10
|
+
import type { SsrFunction } from '../../vitrify-config.js'
|
|
11
|
+
|
|
12
|
+
export interface FastifySsrOptions {
|
|
13
|
+
baseUrl?: string
|
|
14
|
+
provide?: (
|
|
15
|
+
req: FastifyRequest,
|
|
16
|
+
res: FastifyReply
|
|
17
|
+
) => Promise<Record<string, unknown>>
|
|
18
|
+
vite?: ViteDevServer
|
|
19
|
+
cliDir?: URL
|
|
20
|
+
appDir?: URL
|
|
21
|
+
productName?: string
|
|
22
|
+
ssrFunctions?: SsrFunction[]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const fastifySsrPlugin: FastifyPluginCallback<FastifySsrOptions> = async (
|
|
26
|
+
fastify,
|
|
27
|
+
options,
|
|
28
|
+
done
|
|
29
|
+
) => {
|
|
30
|
+
if (import.meta.env.MODE === 'development') {
|
|
31
|
+
if (!options.vite) throw new Error('Option vite cannot be undefined')
|
|
32
|
+
const middie = (await import('middie')).default
|
|
33
|
+
await fastify.register(middie)
|
|
34
|
+
fastify.use(options.vite.middlewares)
|
|
35
|
+
|
|
36
|
+
fastify.get('*', async (req, res) => {
|
|
37
|
+
try {
|
|
38
|
+
// const url = req.originalUrl
|
|
39
|
+
const url = req.raw.url
|
|
40
|
+
const ssrContext = {
|
|
41
|
+
req,
|
|
42
|
+
res
|
|
43
|
+
}
|
|
44
|
+
// always read fresh template in dev
|
|
45
|
+
// template = readFileSync(resolve('index.html'), 'utf-8')
|
|
46
|
+
const template = readFileSync(
|
|
47
|
+
new URL('index.html', options.cliDir)
|
|
48
|
+
).toString()
|
|
49
|
+
|
|
50
|
+
// template = await vite.transformIndexHtml(url, template)
|
|
51
|
+
const entryUrl = new URL('ssr/entry-server.ts', options.cliDir).pathname
|
|
52
|
+
const render = (await options.vite!.ssrLoadModule(entryUrl)).render
|
|
53
|
+
let manifest
|
|
54
|
+
// TODO: https://github.com/vitejs/vite/issues/2282
|
|
55
|
+
try {
|
|
56
|
+
manifest = {}
|
|
57
|
+
} catch (e) {
|
|
58
|
+
manifest = {}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const [appHtml, preloadLinks] = await render(url, manifest, ssrContext)
|
|
62
|
+
const html = template
|
|
63
|
+
.replace(`<!--preload-links-->`, preloadLinks)
|
|
64
|
+
.replace(`<!--app-html-->`, appHtml)
|
|
65
|
+
.replace('<!--product-name-->', options.productName || 'Product name')
|
|
66
|
+
|
|
67
|
+
res.code(200)
|
|
68
|
+
res.type('text/html')
|
|
69
|
+
res.send(html)
|
|
70
|
+
// res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
|
|
71
|
+
} catch (e: any) {
|
|
72
|
+
console.error(e.stack)
|
|
73
|
+
options.vite && options.vite.ssrFixStacktrace(e)
|
|
74
|
+
res.code(500)
|
|
75
|
+
res.send(e.stack)
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
} else {
|
|
79
|
+
options.baseUrl = options.baseUrl || '/'
|
|
80
|
+
fastify.register(fastifyStatic, {
|
|
81
|
+
root: new URL('./dist/ssr/client', options.appDir).pathname,
|
|
82
|
+
wildcard: false,
|
|
83
|
+
index: false,
|
|
84
|
+
prefix: options.baseUrl
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
fastify.get(`${options.baseUrl}*`, async (req, res) => {
|
|
88
|
+
const url = req.raw.url
|
|
89
|
+
const provide = options.provide ? await options.provide(req, res) : {}
|
|
90
|
+
const ssrContext: Record<string, any> = {
|
|
91
|
+
req,
|
|
92
|
+
res,
|
|
93
|
+
provide
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// template = readFileSync(new URL('../client/index.html', import.meta.url).pathname).toString()
|
|
97
|
+
// manifest = JSON.parse(readFileSync(new URL('../client/ssr-manifest.json', import.meta.url)).toString())
|
|
98
|
+
// render = (await import(new URL('./entry-server.mjs', import.meta.url).pathname)).render
|
|
99
|
+
const template = readFileSync(
|
|
100
|
+
new URL('./dist/ssr/client/index.html', options.appDir).pathname
|
|
101
|
+
).toString()
|
|
102
|
+
const manifest = JSON.parse(
|
|
103
|
+
readFileSync(
|
|
104
|
+
new URL('./dist/ssr/client/ssr-manifest.json', options.appDir)
|
|
105
|
+
).toString()
|
|
106
|
+
)
|
|
107
|
+
const render = (
|
|
108
|
+
await import(
|
|
109
|
+
new URL('./dist/ssr/server/entry-server.mjs', options.appDir).pathname
|
|
110
|
+
)
|
|
111
|
+
).render
|
|
112
|
+
|
|
113
|
+
const [appHtml, preloadLinks] = await render(url, manifest, ssrContext)
|
|
114
|
+
|
|
115
|
+
if (!ssrContext.initialState) ssrContext.initialState = {}
|
|
116
|
+
ssrContext.initialState.provide = provide
|
|
117
|
+
|
|
118
|
+
let html = template
|
|
119
|
+
.replace(`<!--preload-links-->`, preloadLinks)
|
|
120
|
+
.replace(`<!--app-html-->`, appHtml)
|
|
121
|
+
|
|
122
|
+
if (options.ssrFunctions?.length) {
|
|
123
|
+
for (const ssrFunction of options.ssrFunctions) {
|
|
124
|
+
html = ssrFunction(html, ssrContext)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
res.code(200)
|
|
129
|
+
res.type('text/html')
|
|
130
|
+
res.send(html)
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
done()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export { fastifySsrPlugin }
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { promises as fs } from 'fs'
|
|
2
|
+
import { routesToPaths } from '../../helpers/routes.js'
|
|
3
|
+
import type { SsrFunction } from '../../vitrify-config.js'
|
|
4
|
+
|
|
5
|
+
export const prerender = async ({
|
|
6
|
+
outDir,
|
|
7
|
+
templatePath,
|
|
8
|
+
manifestPath,
|
|
9
|
+
entryServerPath,
|
|
10
|
+
ssrFunctions
|
|
11
|
+
}: {
|
|
12
|
+
outDir: string
|
|
13
|
+
templatePath: string
|
|
14
|
+
manifestPath: string
|
|
15
|
+
entryServerPath: string
|
|
16
|
+
ssrFunctions: SsrFunction[]
|
|
17
|
+
}) => {
|
|
18
|
+
const promises = []
|
|
19
|
+
const template = (await fs.readFile(templatePath)).toString()
|
|
20
|
+
const manifest = await fs.readFile(manifestPath)
|
|
21
|
+
const { render, getRoutes } = await import(entryServerPath)
|
|
22
|
+
const routes = await getRoutes()
|
|
23
|
+
const paths = routesToPaths(routes).filter(
|
|
24
|
+
(i) => !i.includes(':') && !i.includes('*')
|
|
25
|
+
)
|
|
26
|
+
for (const url of paths) {
|
|
27
|
+
const filename =
|
|
28
|
+
(url.endsWith('/') ? 'index' : url.replace(/^\//g, '')) + '.html'
|
|
29
|
+
console.log(`Generating ${filename}`)
|
|
30
|
+
const ssrContext = {
|
|
31
|
+
req: { headers: {}, url },
|
|
32
|
+
res: {}
|
|
33
|
+
}
|
|
34
|
+
const [appHtml, preloadLinks] = await render(url, manifest, ssrContext)
|
|
35
|
+
|
|
36
|
+
let html = template
|
|
37
|
+
.replace(`<!--preload-links-->`, preloadLinks)
|
|
38
|
+
.replace(`<!--app-html-->`, appHtml)
|
|
39
|
+
|
|
40
|
+
if (ssrFunctions?.length) {
|
|
41
|
+
for (const ssrFunction of ssrFunctions) {
|
|
42
|
+
html = ssrFunction(html, ssrContext)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
promises.push(fs.writeFile(outDir + filename, html, 'utf-8'))
|
|
47
|
+
}
|
|
48
|
+
return Promise.all(promises)
|
|
49
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify'
|
|
2
|
+
import fastify from 'fastify'
|
|
3
|
+
import type { SsrFunction } from '../../vitrify-config.js'
|
|
4
|
+
// import { setup } from 'virtual:fastify-setup'
|
|
5
|
+
import { fastifySsrPlugin } from './fastify-ssr-plugin.js'
|
|
6
|
+
// import { getPkgJsonDir } from '../app-urls.js'
|
|
7
|
+
|
|
8
|
+
export const createApp = ({
|
|
9
|
+
setup,
|
|
10
|
+
appDir,
|
|
11
|
+
baseUrl,
|
|
12
|
+
ssrFunctions
|
|
13
|
+
}: {
|
|
14
|
+
setup: (fastify: FastifyInstance) => any
|
|
15
|
+
appDir: URL
|
|
16
|
+
baseUrl?: string
|
|
17
|
+
ssrFunctions?: SsrFunction[]
|
|
18
|
+
}) => {
|
|
19
|
+
const app = fastify({
|
|
20
|
+
logger: true
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
app.register(fastifySsrPlugin, {
|
|
24
|
+
baseUrl,
|
|
25
|
+
appDir,
|
|
26
|
+
ssrFunctions
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
setup(app)
|
|
30
|
+
|
|
31
|
+
return app
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// const app = createApp({
|
|
35
|
+
// setup
|
|
36
|
+
// })
|
|
37
|
+
|
|
38
|
+
// app.listen(process.env.PORT || 3000, process.env.HOST || '127.0.0.1')
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// https://github.com/quasarframework/quasar/blob/dev/app/lib/helpers/logger.js
|
|
2
|
+
import chalk from 'chalk'
|
|
3
|
+
const { bgGreen, green, inverse, bgRed, red, bgYellow, yellow } = chalk
|
|
4
|
+
import readline from 'readline'
|
|
5
|
+
import type { AddressInfo, Server } from 'net'
|
|
6
|
+
import type { ResolvedConfig, Logger } from 'vite'
|
|
7
|
+
import os from 'os'
|
|
8
|
+
import type { Hostname } from '../helpers/utils.js'
|
|
9
|
+
import { resolveHostname } from '../helpers/utils.js'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Main approach - App CLI related
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const dot = '•'
|
|
16
|
+
const banner = 'App ' + dot
|
|
17
|
+
const greenBanner = green(banner)
|
|
18
|
+
const redBanner = red(banner)
|
|
19
|
+
const yellowBanner = yellow(banner)
|
|
20
|
+
|
|
21
|
+
export const clearConsole = process.stdout.isTTY
|
|
22
|
+
? () => {
|
|
23
|
+
// Fill screen with blank lines. Then move to 0 (beginning of visible part) and clear it
|
|
24
|
+
const blank = '\n'.repeat(process.stdout.rows)
|
|
25
|
+
console.log(blank)
|
|
26
|
+
readline.cursorTo(process.stdout, 0, 0)
|
|
27
|
+
readline.clearScreenDown(process.stdout)
|
|
28
|
+
}
|
|
29
|
+
: () => {}
|
|
30
|
+
|
|
31
|
+
export const log = function (msg?: string) {
|
|
32
|
+
console.log(msg ? ` ${greenBanner} ${msg}` : '')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const warn = function (msg?: string, pill?: string) {
|
|
36
|
+
if (msg !== void 0) {
|
|
37
|
+
const pillBanner = pill !== void 0 ? bgYellow.black('', pill, '') + ' ' : ''
|
|
38
|
+
|
|
39
|
+
console.warn(` ${yellowBanner} ⚠️ ${pillBanner}${msg}`)
|
|
40
|
+
} else {
|
|
41
|
+
console.warn()
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const fatal = function (msg?: string, pill?: string) {
|
|
46
|
+
if (msg !== void 0) {
|
|
47
|
+
const pillBanner = pill !== void 0 ? errorPill(pill) + ' ' : ''
|
|
48
|
+
|
|
49
|
+
console.error(`\n ${redBanner} ⚠️ ${pillBanner}${msg}\n`)
|
|
50
|
+
} else {
|
|
51
|
+
console.error()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
process.exit(1)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Extended approach - Compilation status & pills
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
export const successPill = (msg?: string) => bgGreen.black('', msg, '')
|
|
62
|
+
export const infoPill = (msg?: string) => inverse('', msg, '')
|
|
63
|
+
export const errorPill = (msg?: string) => bgRed.white('', msg, '')
|
|
64
|
+
export const warningPill = (msg?: string) => bgYellow.black('', msg, '')
|
|
65
|
+
|
|
66
|
+
export const success = function (msg?: string, title = 'SUCCESS') {
|
|
67
|
+
console.log(` ${greenBanner} ${successPill(title)} ${green(dot + ' ' + msg)}`)
|
|
68
|
+
}
|
|
69
|
+
export const getSuccess = function (msg?: string, title?: string) {
|
|
70
|
+
return ` ${greenBanner} ${successPill(title)} ${green(dot + ' ' + msg)}`
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const info = function (msg?: string, title = 'INFO') {
|
|
74
|
+
console.log(` ${greenBanner} ${infoPill(title)} ${green(dot)} ${msg}`)
|
|
75
|
+
}
|
|
76
|
+
export const getInfo = function (msg?: string, title?: string) {
|
|
77
|
+
return ` ${greenBanner} ${infoPill(title)} ${green(dot)} ${msg}`
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const error = function (msg?: string, title = 'ERROR') {
|
|
81
|
+
console.log(` ${redBanner} ${errorPill(title)} ${red(dot + ' ' + msg)}`)
|
|
82
|
+
}
|
|
83
|
+
export const getError = function (msg?: string, title = 'ERROR') {
|
|
84
|
+
return ` ${redBanner} ${errorPill(title)} ${red(dot + ' ' + msg)}`
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export const warning = function (msg?: string, title = 'WARNING') {
|
|
88
|
+
console.log(
|
|
89
|
+
` ${yellowBanner} ${warningPill(title)} ${yellow(dot + ' ' + msg)}`
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
export const getWarning = function (msg?: string, title = 'WARNING') {
|
|
93
|
+
return ` ${yellowBanner} ${warningPill(title)} ${yellow(dot + ' ' + msg)}`
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function printHttpServerUrls(
|
|
97
|
+
server: Server,
|
|
98
|
+
config: ResolvedConfig
|
|
99
|
+
): void {
|
|
100
|
+
const address = server.address()
|
|
101
|
+
const isAddressInfo = (x: any): x is AddressInfo => x?.address
|
|
102
|
+
if (isAddressInfo(address)) {
|
|
103
|
+
const hostname = resolveHostname(config.server.host)
|
|
104
|
+
const protocol = config.server.https ? 'https' : 'http'
|
|
105
|
+
printServerUrls(
|
|
106
|
+
hostname,
|
|
107
|
+
protocol,
|
|
108
|
+
address.port,
|
|
109
|
+
config.base,
|
|
110
|
+
config.logger.info
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function printServerUrls(
|
|
116
|
+
hostname: Hostname,
|
|
117
|
+
protocol: string,
|
|
118
|
+
port: number,
|
|
119
|
+
base: string,
|
|
120
|
+
info: Logger['info']
|
|
121
|
+
): void {
|
|
122
|
+
if (hostname.host === '127.0.0.1') {
|
|
123
|
+
const url = `${protocol}://${hostname.name}:${chalk.bold(port)}${base}`
|
|
124
|
+
info(` > Local: ${chalk.cyan(url)}`)
|
|
125
|
+
if (hostname.name !== '127.0.0.1') {
|
|
126
|
+
info(` > Network: ${chalk.dim('use `--host` to expose')}`)
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
Object.values(os.networkInterfaces())
|
|
130
|
+
.flatMap((nInterface) => nInterface ?? [])
|
|
131
|
+
.filter((detail) => detail && detail.address && detail.family === 'IPv4')
|
|
132
|
+
.map((detail) => {
|
|
133
|
+
const type = detail.address.includes('127.0.0.1')
|
|
134
|
+
? 'Local: '
|
|
135
|
+
: 'Network: '
|
|
136
|
+
const host = detail.address.replace('127.0.0.1', hostname.name)
|
|
137
|
+
const url = `${protocol}://${host}:${chalk.bold(port)}${base}`
|
|
138
|
+
return ` > ${type} ${chalk.cyan(url)}`
|
|
139
|
+
})
|
|
140
|
+
.forEach((msg) => info(msg))
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// https://github.com/antfu/vite-ssg/blob/462722203dade87365a519d847fcd881ee16a7f4/src/node/utils.ts#L13
|
|
2
|
+
import type { RouteRecordRaw } from 'vue-router'
|
|
3
|
+
|
|
4
|
+
export const routesToPaths = (routes?: RouteRecordRaw[]) => {
|
|
5
|
+
if (!routes) return ['/']
|
|
6
|
+
|
|
7
|
+
const paths: Set<string> = new Set()
|
|
8
|
+
|
|
9
|
+
const getPaths = (routes: RouteRecordRaw[], prefix = '') => {
|
|
10
|
+
// remove trailing slash
|
|
11
|
+
prefix = prefix.replace(/\/$/g, '')
|
|
12
|
+
for (const route of routes) {
|
|
13
|
+
let path = route.path
|
|
14
|
+
// check for leading slash
|
|
15
|
+
if (route.path) {
|
|
16
|
+
path =
|
|
17
|
+
prefix && !route.path.startsWith('/')
|
|
18
|
+
? `${prefix}/${route.path}`
|
|
19
|
+
: route.path
|
|
20
|
+
|
|
21
|
+
paths.add(path)
|
|
22
|
+
}
|
|
23
|
+
if (Array.isArray(route.children)) getPaths(route.children, path)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getPaths(routes)
|
|
28
|
+
return [...paths]
|
|
29
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export const injectSsrContext = (html: string, ssrContext: Record<string, any>) => html.replace(
|
|
2
|
+
/(<html[^>]*)(>)/i,
|
|
3
|
+
(found, start, end) => {
|
|
4
|
+
let matches
|
|
5
|
+
|
|
6
|
+
matches = found.match(/\sdir\s*=\s*['"]([^'"]*)['"]/i)
|
|
7
|
+
if (matches) {
|
|
8
|
+
start = start.replace(matches[0], '')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
matches = found.match(/\slang\s*=\s*['"]([^'"]*)['"]/i)
|
|
12
|
+
if (matches) {
|
|
13
|
+
start = start.replace(matches[0], '')
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return `${start} ${ssrContext._meta.htmlAttrs || ''} ${end}`
|
|
17
|
+
}
|
|
18
|
+
)
|
|
19
|
+
.replace(
|
|
20
|
+
/(<head[^>]*)(>)/i,
|
|
21
|
+
(_, start, end) => `${start}${end}${ssrContext._meta.headTags || ''}`
|
|
22
|
+
)
|
|
23
|
+
.replace(
|
|
24
|
+
/(<\/head>)/i,
|
|
25
|
+
(_, tag) => `${ssrContext._meta.resourceStyles || ''}${ssrContext._meta.endingHeadTags || ''}${tag}`
|
|
26
|
+
)
|
|
27
|
+
.replace(
|
|
28
|
+
/(<body[^>]*)(>)/i,
|
|
29
|
+
(found, start, end) => {
|
|
30
|
+
let classes = ssrContext._meta.bodyClasses || ''
|
|
31
|
+
|
|
32
|
+
const matches = found.match(/\sclass\s*=\s*['"]([^'"]*)['"]/i)
|
|
33
|
+
|
|
34
|
+
if (matches) {
|
|
35
|
+
if (matches[1].length > 0) {
|
|
36
|
+
classes += ` ${matches[1]}`
|
|
37
|
+
}
|
|
38
|
+
start = start.replace(matches[0], '')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return `${start} class="${classes.trim()}" ${ssrContext._meta.bodyAttrs || ''}${end}${ssrContext._meta.bodyTags || ''}`
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
.replace(`<!--initial-state-->`, `<script>
|
|
45
|
+
window.__INITIAL_STATE__ = ${JSON.stringify(ssrContext.initialState)}
|
|
46
|
+
</script>
|
|
47
|
+
`)
|
|
48
|
+
|
|
49
|
+
export const injectInitialState = (html: string, ssrContext: Record<string, any>) =>
|
|
50
|
+
html.replace(`<!--initial-state-->`, `<script>
|
|
51
|
+
window.__INITIAL_STATE__ = ${JSON.stringify(ssrContext.initialState)}
|
|
52
|
+
</script>`)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// https://github.com/vitejs/vite/blob/main/packages/vite/src/node/logger.ts
|
|
2
|
+
export interface Hostname {
|
|
3
|
+
// undefined sets the default behaviour of server.listen
|
|
4
|
+
host: string | undefined
|
|
5
|
+
// resolve to localhost when possible
|
|
6
|
+
name: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function resolveHostname(
|
|
10
|
+
optionsHost: string | boolean | undefined
|
|
11
|
+
): Hostname {
|
|
12
|
+
let host: string | undefined
|
|
13
|
+
if (
|
|
14
|
+
optionsHost === undefined ||
|
|
15
|
+
optionsHost === false ||
|
|
16
|
+
optionsHost === 'localhost'
|
|
17
|
+
) {
|
|
18
|
+
// Use a secure default
|
|
19
|
+
host = '127.0.0.1'
|
|
20
|
+
} else if (optionsHost === true) {
|
|
21
|
+
// If passed --host in the CLI without arguments
|
|
22
|
+
host = undefined // undefined typically means 0.0.0.0 or :: (listen on all IPs)
|
|
23
|
+
} else {
|
|
24
|
+
host = optionsHost
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Set host name to localhost when possible, unless the user explicitly asked for '127.0.0.1'
|
|
28
|
+
const name =
|
|
29
|
+
(optionsHost !== '127.0.0.1' && host === '127.0.0.1') ||
|
|
30
|
+
host === '0.0.0.0' ||
|
|
31
|
+
host === '::' ||
|
|
32
|
+
host === undefined
|
|
33
|
+
? 'localhost'
|
|
34
|
+
: host
|
|
35
|
+
|
|
36
|
+
return { host, name }
|
|
37
|
+
}
|