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.
@@ -0,0 +1,404 @@
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('prerender')) {
368
+ return 'prerender'
369
+ } else if (id.includes('node_modules')) {
370
+ return 'vendor'
371
+ }
372
+ }
373
+ }
374
+ }
375
+ : {
376
+ output: {
377
+ format: 'es'
378
+ }
379
+ }
380
+ },
381
+ // css: {
382
+ // preprocessorOptions: {
383
+ // sass: {
384
+ // additionalData: sass ? [...sass].join('\n') : undefined
385
+ // }
386
+ // }
387
+ // },
388
+ ssr: {
389
+ // Create a SSR bundle
390
+ noExternal: [
391
+ new RegExp(
392
+ `^(?!.*(${[...builtinModules, ...serverModules].join('|')}))`
393
+ )
394
+ ]
395
+ },
396
+ define: {
397
+ __BASE_URL__: `'/'`
398
+ }
399
+ } as VitrifyConfig
400
+
401
+ return mergeConfig(config, vitrifyConfig)
402
+ }
403
+
404
+ 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[]