vitrify 0.1.1 → 0.2.2

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 (38) hide show
  1. package/dist/app-urls.js +14 -10
  2. package/dist/bin/cli.js +14 -12
  3. package/dist/bin/dev.js +5 -3
  4. package/dist/bin/run.js +5 -2
  5. package/dist/{vue → frameworks/vue}/fastify-ssr-plugin.js +0 -0
  6. package/dist/{vue → frameworks/vue}/prerender.js +1 -1
  7. package/dist/{vue → frameworks/vue}/server.js +0 -0
  8. package/dist/index.js +73 -20
  9. package/dist/plugins/quasar.js +19 -14
  10. package/dist/types/app-urls.d.ts +7 -6
  11. package/dist/types/bin/run.d.ts +2 -2
  12. package/dist/types/{vue → frameworks/vue}/fastify-ssr-plugin.d.ts +1 -1
  13. package/dist/types/{vue → frameworks/vue}/prerender.d.ts +1 -1
  14. package/dist/types/{vue → frameworks/vue}/server.d.ts +1 -1
  15. package/dist/types/index.d.ts +2 -2
  16. package/dist/types/vitrify-config.d.ts +4 -1
  17. package/package.json +16 -18
  18. package/src/node/app-urls.ts +38 -0
  19. package/src/node/bin/build.ts +87 -0
  20. package/src/node/bin/cli.ts +157 -0
  21. package/src/node/bin/dev.ts +138 -0
  22. package/src/node/bin/run.ts +47 -0
  23. package/src/node/bin/test.ts +24 -0
  24. package/src/node/frameworks/vue/fastify-ssr-plugin.ts +137 -0
  25. package/src/node/frameworks/vue/prerender.ts +49 -0
  26. package/src/node/frameworks/vue/server.ts +38 -0
  27. package/src/node/helpers/logger.ts +142 -0
  28. package/src/node/helpers/routes.ts +29 -0
  29. package/src/node/helpers/ssr.ts.bak +52 -0
  30. package/src/node/helpers/utils.ts +37 -0
  31. package/src/node/index.ts +402 -0
  32. package/src/node/plugins/index.ts +12 -0
  33. package/src/node/plugins/quasar.ts +387 -0
  34. package/src/node/vitrify-config.ts +79 -0
  35. package/src/vite/vue/ssr/fastify-ssr-plugin.ts +1 -1
  36. package/src/vite/vue/ssr/prerender.ts +1 -1
  37. package/src/vite/vue/ssr/server.ts +6 -3
  38. package/src/vite/vue/components.d.ts +0 -25
@@ -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
+ }
@@ -0,0 +1,402 @@
1
+ import vuePlugin from '@vitejs/plugin-vue'
2
+ import type { InlineConfig, UserConfig } from 'vite'
3
+ import { mergeConfig } from 'vite'
4
+ import { readFileSync } from 'fs'
5
+ import { QuasarPlugin } from './plugins/quasar.js'
6
+ import builtinModules from 'builtin-modules'
7
+ import { resolve } from 'import-meta-resolve'
8
+ import type {
9
+ StaticImports,
10
+ BootFunction,
11
+ OnMountedHook,
12
+ VitrifyConfig,
13
+ SsrFunction
14
+ } from './vitrify-config.js'
15
+ import type { VitrifyContext } from './bin/run.js'
16
+ import type { VitrifyPlugin } from './plugins/index.js'
17
+ import type { FastifyInstance } from 'fastify'
18
+ import { getPkgJsonDir } from './app-urls.js'
19
+
20
+ const serverModules = ['fastify', 'middie']
21
+
22
+ const configPluginMap: Record<string, () => Promise<VitrifyPlugin>> = {
23
+ quasar: () =>
24
+ import('./plugins/quasar.js').then((module) => module.QuasarPlugin)
25
+ }
26
+
27
+ export const VIRTUAL_MODULES = [
28
+ 'virtual:fastify-setup',
29
+ 'virtual:boot-functions',
30
+ 'virtual:ssr-functions',
31
+ 'virtual:on-mounted-hooks',
32
+ 'virtual:global-css',
33
+ 'virtual:static-imports'
34
+ ]
35
+
36
+ export const baseConfig = async ({
37
+ ssr,
38
+ appDir,
39
+ publicDir,
40
+ command = 'build',
41
+ mode = 'production',
42
+ framework = 'vue',
43
+ pwa = false
44
+ }: {
45
+ ssr?: 'client' | 'server' | 'ssg'
46
+ appDir?: URL
47
+ publicDir?: URL
48
+ command?: 'build' | 'dev' | 'test'
49
+ mode?: 'production' | 'development'
50
+ framework?: 'vue'
51
+ pwa?: boolean
52
+ }): Promise<InlineConfig> => {
53
+ const { getAppDir, getCliDir, getCliViteDir, getSrcDir, getCwd } =
54
+ await import('./app-urls.js')
55
+ if (!appDir) {
56
+ appDir = getAppDir()
57
+ }
58
+ const srcDir = getSrcDir(appDir)
59
+ const cwd = getCwd()
60
+ const cliDir = getCliDir()
61
+ const cliViteDir = getCliViteDir(cliDir)
62
+ // const {
63
+ // appDir: tempAppDir,
64
+ // cliDir,
65
+ // cliViteDir,
66
+ // srcDir
67
+ // } = await import('./app-urls.js')
68
+ // const cwd = appDir || tempAppDir
69
+ const frameworkDir = new URL(`${framework}/`, cliViteDir)
70
+
71
+ // const localPackages = ['vue', 'vue-router', 'quasar']
72
+ const localPackages = ['vue', 'vue-router']
73
+ const cliPackages = ['vitest']
74
+ const packageUrls: Record<string, URL> = {}
75
+ await (async () => {
76
+ for (const val of localPackages)
77
+ packageUrls[val] = getPkgJsonDir(
78
+ new URL(await resolve(val, appDir!.href))
79
+ )
80
+ })()
81
+ await (async () => {
82
+ for (const val of cliPackages)
83
+ packageUrls[val] = getPkgJsonDir(
84
+ new URL(await resolve(val, cliDir!.href))
85
+ )
86
+ })()
87
+
88
+ // if (appDir) {
89
+ // srcDir = new URL('src/', appDir);
90
+ // quasarDir = new URL(await resolve('quasar/', appDir.href));
91
+ // ({ appDir: cwd, cliDir } = await import('./app-urls.js'))
92
+ // } else {
93
+ // ({ appDir, cliDir, srcDir, quasarDir } = await import('./app-urls.js'))
94
+ // cwd = appDir
95
+ // }
96
+ // vueDir = new URL('./', await resolve('vue', appDir.href));
97
+ // vueRouterDir = new URL('../', await resolve('vue-router', appDir.href));
98
+ // vitestDir = new URL('../', await resolve('vitest', cliDir.href));
99
+
100
+ if (!publicDir) publicDir = new URL('public/', appDir)
101
+ /**
102
+ * TODO:Perform some manual check if command is run inside a Quasar Project
103
+ */
104
+ let vitrifyConfig:
105
+ | VitrifyConfig
106
+ | (({ mode, command }: { mode: string; command: string }) => VitrifyConfig)
107
+
108
+ try {
109
+ vitrifyConfig = (
110
+ await import(new URL('vitrify.config.js', appDir).pathname)
111
+ ).default
112
+ if (typeof vitrifyConfig === 'function')
113
+ vitrifyConfig = vitrifyConfig({ mode, command })
114
+ } catch (e) {
115
+ console.error(e)
116
+ console.log('No vitrify.config.js file found, using defaults')
117
+ vitrifyConfig = {}
118
+ }
119
+ let { productName = 'Product name' } = JSON.parse(
120
+ readFileSync(new URL('package.json', appDir).pathname, {
121
+ encoding: 'utf-8'
122
+ })
123
+ )
124
+
125
+ const fastifySetup =
126
+ vitrifyConfig.vitrify?.fastify?.setup || ((fastify: FastifyInstance) => {})
127
+
128
+ const ssrTransformCustomDir = () => {
129
+ return {
130
+ props: [],
131
+ needRuntime: true
132
+ }
133
+ }
134
+
135
+ const frameworkPlugins = []
136
+ for (const framework of Object.keys(configPluginMap)) {
137
+ if (Object.keys(vitrifyConfig).includes(framework)) {
138
+ const plugin = await configPluginMap[framework]()
139
+ frameworkPlugins.push(
140
+ await plugin({
141
+ ssr,
142
+ pwa
143
+ })
144
+ )
145
+ }
146
+ }
147
+
148
+ let bootFunctions: BootFunction[]
149
+ let ssrFunctions: SsrFunction[]
150
+ let onMountedHooks: OnMountedHook[]
151
+ let globalCss: string[]
152
+ let staticImports: StaticImports
153
+ let sassVariables: Record<string, string>
154
+ let additionalData: string[]
155
+
156
+ const plugins: UserConfig['plugins'] = [
157
+ vuePlugin({
158
+ template: {
159
+ ssr: !!ssr,
160
+ compilerOptions: {
161
+ directiveTransforms: {
162
+ 'close-popup': ssrTransformCustomDir,
163
+ intersection: ssrTransformCustomDir,
164
+ ripple: ssrTransformCustomDir,
165
+ mutation: ssrTransformCustomDir,
166
+ morph: ssrTransformCustomDir,
167
+ scroll: ssrTransformCustomDir,
168
+ 'scroll-fire': ssrTransformCustomDir,
169
+ 'touch-hold': ssrTransformCustomDir,
170
+ 'touch-pan': ssrTransformCustomDir,
171
+ 'touch-repeat': ssrTransformCustomDir,
172
+ 'touch-swipe': ssrTransformCustomDir
173
+ }
174
+ }
175
+ }
176
+ }),
177
+ ...frameworkPlugins,
178
+ // await QuasarPlugin({
179
+ // ssr: ssr,
180
+ // pwa: pwa
181
+ // // quasarDir: packageUrls.quasar
182
+ // }),
183
+ {
184
+ name: 'vitrify-setup',
185
+ enforce: 'post',
186
+ config: async (config: VitrifyConfig, env) => {
187
+ bootFunctions = config.vitrify?.bootFunctions || []
188
+ ssrFunctions = config.vitrify?.ssrFunctions || []
189
+ onMountedHooks = config.vitrify?.hooks?.onMounted || []
190
+ globalCss = config.vitrify?.globalCss || []
191
+ staticImports = config.vitrify?.staticImports || {}
192
+ sassVariables = config.vitrify?.sass?.variables || {}
193
+ additionalData = config.vitrify?.sass?.additionalData || []
194
+
195
+ return {
196
+ css: {
197
+ preprocessorOptions: {
198
+ sass: {
199
+ additionalData: [
200
+ ...Object.entries(sassVariables).map(
201
+ ([key, value]) => `${key}: ${value}`
202
+ ),
203
+ ...additionalData
204
+ // config.css?.preprocessorOptions?.sass.additionalData
205
+ ].join('\n')
206
+ }
207
+ }
208
+ }
209
+ }
210
+ },
211
+ configResolved: (config) => {
212
+ if (process.env.DEBUG) {
213
+ console.log(config.css?.preprocessorOptions?.sass.additionalData)
214
+ console.log(config.optimizeDeps)
215
+ }
216
+ },
217
+ resolveId(id) {
218
+ if (VIRTUAL_MODULES.includes(id)) return id
219
+ return
220
+ },
221
+ load(id) {
222
+ if (id === 'virtual:fastify-setup') {
223
+ return `export const setup = ${String(fastifySetup)}`
224
+ } else if (id === 'virtual:boot-functions') {
225
+ return `export default [${bootFunctions
226
+ .map((fn) => `${String(fn)}`)
227
+ .join(', ')}]`
228
+ } else if (id === 'virtual:ssr-functions') {
229
+ return `export default [${ssrFunctions
230
+ .map((fn) => `${String(fn)}`)
231
+ .join(', ')}]`
232
+ } else if (id === 'virtual:on-mounted-hooks') {
233
+ return `export default [${onMountedHooks
234
+ .map((fn) => `${String(fn)}`)
235
+ .join(', ')}]`
236
+ } else if (id === 'virtual:global-css') {
237
+ return `${globalCss.map((css) => `import '${css}'`).join('\n')}`
238
+ } else if (id === 'virtual:static-imports') {
239
+ return `${Object.entries(staticImports)
240
+ .map(
241
+ ([key, value]) => `export { ${value.join(',')} } from '${key}';`
242
+ )
243
+ .join('\n')}`
244
+ }
245
+ return null
246
+ }
247
+ }
248
+ ]
249
+ if (command !== 'test') {
250
+ plugins.unshift({
251
+ name: 'html-transform',
252
+ enforce: 'pre',
253
+ transform: (code, id) => {
254
+ if (id.endsWith('App.vue')) {
255
+ code =
256
+ code +
257
+ `<style lang="sass">
258
+ // do not remove, required for additionalData import
259
+ </style>`
260
+ }
261
+ return code
262
+ },
263
+ transformIndexHtml: {
264
+ enforce: 'pre',
265
+ transform: (html) => {
266
+ let entry: string
267
+ switch (ssr) {
268
+ case 'ssg':
269
+ case 'server':
270
+ case 'client':
271
+ entry = new URL('ssr/entry-client.ts', frameworkDir).pathname
272
+ break
273
+ default:
274
+ entry = new URL('csr/entry.ts', frameworkDir).pathname
275
+ }
276
+ const entryScript = `<script type="module" src="${entry}"></script>`
277
+ html = html
278
+ .replace('<!--entry-script-->', entryScript)
279
+ .replace('<!--product-name-->', productName)
280
+ return html
281
+ }
282
+ }
283
+ })
284
+
285
+ plugins.unshift({
286
+ name: 'product-name',
287
+ enforce: 'post',
288
+ config: (config: VitrifyConfig, env) => {
289
+ if (config.vitrify?.productName)
290
+ productName = config.vitrify?.productName
291
+ },
292
+ transformIndexHtml: {
293
+ enforce: 'post',
294
+ transform: (html) => {
295
+ html = html.replace('<!--product-name-->', productName)
296
+ return html
297
+ }
298
+ }
299
+ })
300
+ }
301
+
302
+ const alias = [
303
+ { find: 'src', replacement: srcDir.pathname },
304
+ { find: 'app', replacement: appDir.pathname },
305
+ { find: 'cwd', replacement: cwd.pathname },
306
+ { find: 'boot', replacement: new URL('boot/', srcDir).pathname },
307
+ { find: 'assets', replacement: new URL('assets/', srcDir).pathname },
308
+ // ...Object.entries(packageUrls).map(([key, value]) => ({
309
+ // find: key, replacement: value.pathname
310
+ // })),
311
+ { find: 'vue', replacement: packageUrls['vue'].pathname },
312
+ { find: 'vue-router', replacement: packageUrls['vue-router'].pathname },
313
+ { find: 'vitrify', replacement: cliDir.pathname }
314
+ ]
315
+ if (command === 'test')
316
+ alias.push({
317
+ find: 'vitest',
318
+ replacement: packageUrls.vitest.pathname
319
+ })
320
+
321
+ const config = {
322
+ root: frameworkDir.pathname,
323
+ publicDir: publicDir.pathname,
324
+ vitrify: {
325
+ productName,
326
+ urls: {
327
+ // @ts-ignore
328
+ app: appDir,
329
+ cli: cliDir,
330
+ src: srcDir,
331
+ cwd,
332
+ packages: packageUrls
333
+ }
334
+ },
335
+ plugins,
336
+ optimizeDeps: {
337
+ exclude: ['vue']
338
+ },
339
+ resolve: {
340
+ // Dedupe uses require which breaks ESM SSR builds
341
+ // dedupe: [
342
+ // 'vue',
343
+ // 'vue-router'
344
+ // ],
345
+ alias
346
+ },
347
+ build: {
348
+ target: ssr === 'server' ? 'esnext' : 'modules',
349
+ ssr: ssr === 'server' ? true : false,
350
+ ssrManifest: ssr === 'client' || ssr === 'ssg',
351
+ rollupOptions:
352
+ ssr === 'server'
353
+ ? {
354
+ input: [
355
+ new URL('ssr/entry-server.ts', frameworkDir).pathname,
356
+ new URL('ssr/prerender.ts', frameworkDir).pathname,
357
+ new URL('ssr/server.ts', frameworkDir).pathname
358
+ ],
359
+ output: {
360
+ minifyInternalExports: false,
361
+ entryFileNames: '[name].mjs',
362
+ chunkFileNames: '[name].mjs',
363
+ format: 'es',
364
+ manualChunks: (id) => {
365
+ if (id.includes('fastify-ssr-plugin')) {
366
+ return 'fastify-ssr-plugin'
367
+ } else if (id.includes('node_modules')) {
368
+ return 'vendor'
369
+ }
370
+ }
371
+ }
372
+ }
373
+ : {
374
+ output: {
375
+ format: 'es'
376
+ }
377
+ }
378
+ },
379
+ // css: {
380
+ // preprocessorOptions: {
381
+ // sass: {
382
+ // additionalData: sass ? [...sass].join('\n') : undefined
383
+ // }
384
+ // }
385
+ // },
386
+ ssr: {
387
+ // Create a SSR bundle
388
+ noExternal: [
389
+ new RegExp(
390
+ `^(?!.*(${[...builtinModules, ...serverModules].join('|')}))`
391
+ )
392
+ ]
393
+ },
394
+ define: {
395
+ __BASE_URL__: `'/'`
396
+ }
397
+ } as VitrifyConfig
398
+
399
+ return mergeConfig(config, vitrifyConfig)
400
+ }
401
+
402
+ export type { VitrifyConfig, VitrifyPlugin, VitrifyContext, BootFunction }
@@ -0,0 +1,12 @@
1
+ import type { Plugin } from 'vite'
2
+
3
+ export type VitrifyPlugin = ({
4
+ ssr,
5
+ mode,
6
+ command
7
+ }: {
8
+ ssr?: 'server' | 'client' | 'ssg' | false
9
+ pwa?: boolean
10
+ mode?: 'production' | 'development'
11
+ command?: 'build' | 'dev' | 'test'
12
+ }) => Promise<Plugin | Plugin[]> | Plugin | Plugin[]