vitrify 0.2.5 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +2 -2
  2. package/dist/app-urls.js +1 -2
  3. package/dist/bin/build.js +9 -51
  4. package/dist/bin/cli.js +31 -9
  5. package/dist/bin/dev.js +72 -70
  6. package/dist/frameworks/vue/fastify-csr-plugin.js +38 -0
  7. package/dist/frameworks/vue/fastify-ssr-plugin.js +83 -25
  8. package/dist/frameworks/vue/prerender.js +3 -3
  9. package/dist/frameworks/vue/server.js +10 -11
  10. package/dist/helpers/collect-css-ssr.js +61 -0
  11. package/dist/helpers/logger.js +0 -72
  12. package/dist/index.js +310 -130
  13. package/dist/plugins/quasar.js +34 -111
  14. package/dist/types/bin/build.d.ts +2 -2
  15. package/dist/types/bin/dev.d.ts +42 -4
  16. package/dist/types/frameworks/vue/fastify-csr-plugin.d.ts +17 -0
  17. package/dist/types/frameworks/vue/fastify-ssr-plugin.d.ts +6 -3
  18. package/dist/types/frameworks/vue/prerender.d.ts +3 -3
  19. package/dist/types/frameworks/vue/server.d.ts +10 -5
  20. package/dist/types/helpers/collect-css-ssr.d.ts +14 -0
  21. package/dist/types/helpers/logger.d.ts +0 -19
  22. package/dist/types/helpers/routes.d.ts +1 -1
  23. package/dist/types/index.d.ts +4 -2
  24. package/dist/types/plugins/index.d.ts +1 -1
  25. package/dist/types/vitrify-config.d.ts +33 -17
  26. package/package.json +33 -32
  27. package/src/node/app-urls.ts +1 -2
  28. package/src/node/bin/build.ts +11 -57
  29. package/src/node/bin/cli.ts +38 -10
  30. package/src/node/bin/dev.ts +106 -80
  31. package/src/node/bin/test.ts +0 -3
  32. package/src/node/frameworks/vue/fastify-csr-plugin.ts +72 -0
  33. package/src/node/frameworks/vue/fastify-ssr-plugin.ts +99 -28
  34. package/src/node/frameworks/vue/prerender.ts +5 -5
  35. package/src/node/frameworks/vue/server.ts +24 -17
  36. package/src/node/helpers/collect-css-ssr.ts +85 -0
  37. package/src/node/helpers/logger.ts +0 -87
  38. package/src/node/index.ts +353 -146
  39. package/src/node/plugins/index.ts +1 -1
  40. package/src/node/plugins/quasar.ts +39 -116
  41. package/src/node/vitrify-config.ts +44 -17
  42. package/src/vite/fastify/entry.ts +11 -0
  43. package/src/vite/fastify/server.ts +12 -0
  44. package/src/vite/vue/csr/app.ts +25 -0
  45. package/src/vite/vue/csr/fastify-csr-plugin.ts +3 -0
  46. package/src/vite/vue/csr/server.ts +8 -0
  47. package/src/vite/vue/index.html +1 -0
  48. package/src/vite/vue/main.ts +5 -20
  49. package/src/vite/vue/ssr/app.ts +25 -0
  50. package/src/vite/vue/ssr/entry-server.ts +13 -1
  51. package/src/vite/vue/ssr/fastify-ssr-plugin.ts +2 -118
  52. package/src/vite/vue/ssr/prerender.ts +2 -2
  53. package/src/vite/vue/ssr/server.ts +24 -15
  54. package/src/node/helpers/ssr.ts.bak +0 -52
@@ -3,11 +3,11 @@ import type {
3
3
  FastifyRequest,
4
4
  FastifyReply
5
5
  } from 'fastify'
6
- import fastifyStatic from 'fastify-static'
6
+ import fastifyStatic from '@fastify/static'
7
7
  import { readFileSync } from 'fs'
8
- // import { injectSsrContext } from '../helpers/ssr.js'
8
+ import type { OnRenderedHook } from '../../vitrify-config.js'
9
+ import { componentsModules, collectCss } from '../../helpers/collect-css-ssr.js'
9
10
  import type { ViteDevServer } from 'vite'
10
- import type { SsrFunction } from '../../vitrify-config.js'
11
11
 
12
12
  export interface FastifySsrOptions {
13
13
  baseUrl?: string
@@ -15,11 +15,14 @@ export interface FastifySsrOptions {
15
15
  req: FastifyRequest,
16
16
  res: FastifyReply
17
17
  ) => Promise<Record<string, unknown>>
18
+ vitrifyDir?: URL
18
19
  vite?: ViteDevServer
19
- cliDir?: URL
20
+ // frameworkDir?: URL
20
21
  appDir?: URL
22
+ publicDir?: URL
21
23
  productName?: string
22
- ssrFunctions?: SsrFunction[]
24
+ onRendered?: OnRenderedHook[]
25
+ mode?: string
23
26
  }
24
27
 
25
28
  const fastifySsrPlugin: FastifyPluginCallback<FastifySsrOptions> = async (
@@ -27,29 +30,89 @@ const fastifySsrPlugin: FastifyPluginCallback<FastifySsrOptions> = async (
27
30
  options,
28
31
  done
29
32
  ) => {
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)
33
+ options.vitrifyDir =
34
+ options.vitrifyDir || (await import('vitrify')).vitrifyDir
35
+ const frameworkDir = new URL('src/vite/vue/', options.vitrifyDir)
36
+ options.baseUrl = options.baseUrl || '/'
37
+ options.mode = options.mode || process.env.MODE || import.meta.env.MODE
38
+ options.appDir = options.appDir || new URL('../../..', import.meta.url)
35
39
 
36
- fastify.get('*', async (req, res) => {
40
+ if (
41
+ options.baseUrl.charAt(options.baseUrl.length - 1) !== '/' ||
42
+ options.baseUrl.charAt(0) !== '/'
43
+ )
44
+ throw new Error('baseUrl should start and end with a /')
45
+ if (options.mode === 'development') {
46
+ // if (!options.vitrifyDir)
47
+ // throw new Error('Option vitrifyDir cannot be undefined')
48
+ // if (!options.vite) throw new Error('Option vite cannot be undefined')
49
+ // const { resolve } = await import('import-meta-resolve')
50
+ // const cliDir = new URL('../', await resolve('vitrify', import.meta.url))
51
+ options.appDir = options.appDir || new URL('../../..', import.meta.url)
52
+
53
+ const { createVitrifyDevServer } = await import('vitrify/dev')
54
+ const vite = await createVitrifyDevServer({
55
+ appDir: options.appDir,
56
+ ssr: 'ssr',
57
+ framework: 'vue',
58
+ base: options.baseUrl
59
+ })
60
+ // const { createServer, searchForWorkspaceRoot } = await import('vite')
61
+ // const { baseConfig } = await import('vitrify')
62
+ // const cliDir = options.vitrifyDir
63
+ // const config = await baseConfig({
64
+ // ssr: 'server',
65
+ // command: 'dev',
66
+ // mode: 'development',
67
+ // appDir: options.appDir,
68
+ // publicDir: options.publicDir || new URL('public', options.appDir)
69
+ // })
70
+
71
+ // config.server = {
72
+ // middlewareMode: true,
73
+ // fs: {
74
+ // allow: [
75
+ // searchForWorkspaceRoot(process.cwd()),
76
+ // searchForWorkspaceRoot(options.appDir.pathname),
77
+ // searchForWorkspaceRoot(cliDir.pathname)
78
+ // // appDir.pathname,
79
+ // ]
80
+ // },
81
+ // watch: {
82
+ // // During tests we edit the files too fast and sometimes chokidar
83
+ // // misses change events, so enforce polling for consistency
84
+ // usePolling: true,
85
+ // interval: 100
86
+ // }
87
+ // }
88
+ // const vite = await createServer({
89
+ // configFile: false,
90
+ // ...config
91
+ // })
92
+
93
+ console.log('Dev mode')
94
+ if (!('use' in fastify)) {
95
+ const middie = (await import('@fastify/middie')).default
96
+ await fastify.register(middie)
97
+ }
98
+ fastify.use(vite.middlewares)
99
+
100
+ fastify.get(`${options.baseUrl}*`, async (req, res) => {
37
101
  try {
38
- // const url = req.originalUrl
39
- const url = req.raw.url
102
+ const url = req.raw.url?.replace(options.baseUrl!, '/')
40
103
  const ssrContext = {
41
104
  req,
42
105
  res
43
106
  }
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)
107
+
108
+ let template = readFileSync(
109
+ new URL('index.html', frameworkDir)
48
110
  ).toString()
49
111
 
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
112
+ template = await vite.transformIndexHtml(url!, template)
113
+
114
+ const entryUrl = new URL('ssr/entry-server.ts', frameworkDir).pathname
115
+ const render = (await vite!.ssrLoadModule(entryUrl)).render
53
116
  let manifest
54
117
  // TODO: https://github.com/vitejs/vite/issues/2282
55
118
  try {
@@ -58,11 +121,21 @@ const fastifySsrPlugin: FastifyPluginCallback<FastifySsrOptions> = async (
58
121
  manifest = {}
59
122
  }
60
123
 
124
+ const cssModules = [entryUrl]
125
+ // // @ts-ignore
126
+ // if (options.vite?.config.vitrify!.globalCss)
127
+ // cssModules.push(...options.vite?.config.vitrify.globalCss)
128
+ const matchedModules = componentsModules(cssModules, vite!)
129
+ const css = collectCss({
130
+ mods: matchedModules
131
+ })
132
+
61
133
  const [appHtml, preloadLinks] = await render(url, manifest, ssrContext)
62
134
  const html = template
63
135
  .replace(`<!--preload-links-->`, preloadLinks)
64
136
  .replace(`<!--app-html-->`, appHtml)
65
137
  .replace('<!--product-name-->', options.productName || 'Product name')
138
+ .replace('<!--dev-ssr-css-->', css)
66
139
 
67
140
  res.code(200)
68
141
  res.type('text/html')
@@ -70,13 +143,13 @@ const fastifySsrPlugin: FastifyPluginCallback<FastifySsrOptions> = async (
70
143
  // res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
71
144
  } catch (e: any) {
72
145
  console.error(e.stack)
73
- options.vite && options.vite.ssrFixStacktrace(e)
146
+ vite && vite.ssrFixStacktrace(e)
74
147
  res.code(500)
75
148
  res.send(e.stack)
76
149
  }
77
150
  })
78
151
  } else {
79
- options.baseUrl = options.baseUrl || '/'
152
+ options.appDir = options.appDir || new URL('../../..', import.meta.url)
80
153
  fastify.register(fastifyStatic, {
81
154
  root: new URL('./dist/ssr/client', options.appDir).pathname,
82
155
  wildcard: false,
@@ -85,7 +158,7 @@ const fastifySsrPlugin: FastifyPluginCallback<FastifySsrOptions> = async (
85
158
  })
86
159
 
87
160
  fastify.get(`${options.baseUrl}*`, async (req, res) => {
88
- const url = req.raw.url
161
+ const url = req.raw.url?.replace(options.baseUrl!, '/')
89
162
  const provide = options.provide ? await options.provide(req, res) : {}
90
163
  const ssrContext: Record<string, any> = {
91
164
  req,
@@ -93,9 +166,6 @@ const fastifySsrPlugin: FastifyPluginCallback<FastifySsrOptions> = async (
93
166
  provide
94
167
  }
95
168
 
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
169
  const template = readFileSync(
100
170
  new URL('./dist/ssr/client/index.html', options.appDir).pathname
101
171
  ).toString()
@@ -119,8 +189,8 @@ const fastifySsrPlugin: FastifyPluginCallback<FastifySsrOptions> = async (
119
189
  .replace(`<!--preload-links-->`, preloadLinks)
120
190
  .replace(`<!--app-html-->`, appHtml)
121
191
 
122
- if (options.ssrFunctions?.length) {
123
- for (const ssrFunction of options.ssrFunctions) {
192
+ if (options.onRendered?.length) {
193
+ for (const ssrFunction of options.onRendered) {
124
194
  html = ssrFunction(html, ssrContext)
125
195
  }
126
196
  }
@@ -135,3 +205,4 @@ const fastifySsrPlugin: FastifyPluginCallback<FastifySsrOptions> = async (
135
205
  }
136
206
 
137
207
  export { fastifySsrPlugin }
208
+ export type FastifySsrPlugin = typeof fastifySsrPlugin
@@ -1,19 +1,19 @@
1
1
  import { promises as fs } from 'fs'
2
2
  import { routesToPaths } from '../../helpers/routes.js'
3
- import type { SsrFunction } from '../../vitrify-config.js'
3
+ import type { OnRenderedHook } from '../../vitrify-config.js'
4
4
 
5
5
  export const prerender = async ({
6
6
  outDir,
7
7
  templatePath,
8
8
  manifestPath,
9
9
  entryServerPath,
10
- ssrFunctions
10
+ onRenderedHooks
11
11
  }: {
12
12
  outDir: string
13
13
  templatePath: string
14
14
  manifestPath: string
15
15
  entryServerPath: string
16
- ssrFunctions: SsrFunction[]
16
+ onRenderedHooks: OnRenderedHook[]
17
17
  }) => {
18
18
  const promises = []
19
19
  const template = (await fs.readFile(templatePath)).toString()
@@ -37,8 +37,8 @@ export const prerender = async ({
37
37
  .replace(`<!--preload-links-->`, preloadLinks)
38
38
  .replace(`<!--app-html-->`, appHtml)
39
39
 
40
- if (ssrFunctions?.length) {
41
- for (const ssrFunction of ssrFunctions) {
40
+ if (onRenderedHooks?.length) {
41
+ for (const ssrFunction of onRenderedHooks) {
42
42
  html = ssrFunction(html, ssrContext)
43
43
  }
44
44
  }
@@ -1,38 +1,45 @@
1
1
  import type { FastifyInstance } from 'fastify'
2
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'
3
+ import type { ViteDevServer } from 'vite'
4
+ import { getCliDir, getCliViteDir } from '../../app-urls.js'
5
+ import type { OnRenderedHook, OnSetupFile } from '../../vitrify-config.js'
6
+ import type { FastifyCsrPlugin } from './fastify-csr-plugin.js'
7
+ import type { FastifySsrPlugin } from './fastify-ssr-plugin.js'
7
8
 
8
9
  export const createApp = ({
9
- setup,
10
+ onSetup,
10
11
  appDir,
11
12
  baseUrl,
12
- ssrFunctions
13
+ onRendered,
14
+ fastifyPlugin,
15
+ vitrifyDir,
16
+ mode
13
17
  }: {
14
- setup: (fastify: FastifyInstance) => any
18
+ onSetup: OnSetupFile[]
15
19
  appDir: URL
16
20
  baseUrl?: string
17
- ssrFunctions?: SsrFunction[]
21
+ onRendered?: OnRenderedHook[]
22
+ fastifyPlugin: FastifySsrPlugin | FastifyCsrPlugin
23
+ vitrifyDir?: URL
24
+ mode: string
18
25
  }) => {
19
26
  const app = fastify({
20
27
  logger: true
21
28
  })
22
29
 
23
- app.register(fastifySsrPlugin, {
30
+ app.register(fastifyPlugin, {
24
31
  baseUrl,
25
32
  appDir,
26
- ssrFunctions
33
+ onRendered,
34
+ vitrifyDir,
35
+ mode
27
36
  })
28
37
 
29
- setup(app)
38
+ // if (onSetup?.length) {
39
+ // for (const setup of onSetup) {
40
+ // setup(app)
41
+ // }
42
+ // }
30
43
 
31
44
  return app
32
45
  }
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,85 @@
1
+ // collect-css-ssr.ts
2
+ import type { ViteDevServer, ModuleNode, UpdatePayload } from 'vite'
3
+
4
+ /**
5
+ * Collect SSR CSS for Vite
6
+ */
7
+ export const componentsModules = (
8
+ components: string[],
9
+ vite: ViteDevServer
10
+ ) => {
11
+ const matchedModules = new Set<ModuleNode>()
12
+ components.forEach((component) => {
13
+ const modules = vite.moduleGraph.getModulesByFile(component)
14
+ modules?.forEach((mod) => matchedModules.add(mod))
15
+ })
16
+ return matchedModules
17
+ }
18
+
19
+ export const collectCss = ({
20
+ mods,
21
+ styles = new Map<string, string>(),
22
+ checkedComponents = new Set()
23
+ }: {
24
+ mods: Set<ModuleNode>
25
+ styles?: Map<string, string>
26
+ checkedComponents?: Set<unknown>
27
+ }) => {
28
+ for (const mod of mods) {
29
+ if (
30
+ (mod.file?.endsWith('.scss') ||
31
+ mod.file?.endsWith('.css') ||
32
+ mod.id?.includes('vue&type=style')) &&
33
+ mod.ssrModule
34
+ ) {
35
+ styles.set(mod.url, mod.ssrModule.default)
36
+ }
37
+ if (mod.importedModules.size > 0 && !checkedComponents.has(mod.id)) {
38
+ checkedComponents.add(mod.id)
39
+ collectCss({
40
+ mods: mod.importedModules,
41
+ styles,
42
+ checkedComponents
43
+ })
44
+ }
45
+ }
46
+ let result = ''
47
+ styles.forEach((content, id) => {
48
+ const styleTag = `<style type="text/css" vite-module-id="${hashCode(
49
+ id
50
+ )}">${content}</style>`
51
+ result = result.concat(styleTag)
52
+ })
53
+ return result
54
+ }
55
+
56
+ /**
57
+ * Client listener to detect updated modules through HMR, and remove the initial styled attached to the head
58
+ */
59
+ export const removeCssHotReloaded = () => {
60
+ if (import.meta.hot) {
61
+ import.meta.hot.on('vite:beforeUpdate', (module: UpdatePayload) => {
62
+ module.updates.forEach((update) => {
63
+ const moduleStyle = document.querySelector(
64
+ `[vite-module-id="${hashCode(update.acceptedPath)}"]`
65
+ )
66
+ if (moduleStyle) {
67
+ moduleStyle.remove()
68
+ }
69
+ })
70
+ })
71
+ }
72
+ }
73
+
74
+ const hashCode = (moduleId: string) => {
75
+ let hash = 0,
76
+ i,
77
+ chr
78
+ if (moduleId.length === 0) return hash
79
+ for (i = 0; i < moduleId.length; i++) {
80
+ chr = moduleId.charCodeAt(i)
81
+ hash = (hash << 5) - hash + chr
82
+ hash |= 0 // Convert to 32bit integer
83
+ }
84
+ return hash
85
+ }
@@ -1,98 +1,11 @@
1
1
  // https://github.com/quasarframework/quasar/blob/dev/app/lib/helpers/logger.js
2
2
  import chalk from 'chalk'
3
- const { bgGreen, green, inverse, bgRed, red, bgYellow, yellow } = chalk
4
- import readline from 'readline'
5
3
  import type { AddressInfo, Server } from 'net'
6
4
  import type { ResolvedConfig, Logger } from 'vite'
7
5
  import os from 'os'
8
6
  import type { Hostname } from '../helpers/utils.js'
9
7
  import { resolveHostname } from '../helpers/utils.js'
10
8
 
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
9
  export function printHttpServerUrls(
97
10
  server: Server,
98
11
  config: ResolvedConfig