vitrify 0.1.0 → 0.2.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.
Files changed (46) hide show
  1. package/README.md +86 -0
  2. package/dist/app-urls.js +36 -0
  3. package/dist/bin/build.js +73 -0
  4. package/dist/bin/cli.js +139 -0
  5. package/dist/bin/dev.js +108 -0
  6. package/dist/bin/run.js +29 -0
  7. package/dist/frameworks/vue/fastify-ssr-plugin.js +91 -0
  8. package/dist/frameworks/vue/prerender.js +29 -0
  9. package/dist/frameworks/vue/server.js +20 -0
  10. package/dist/helpers/logger.js +108 -0
  11. package/dist/helpers/routes.js +24 -0
  12. package/dist/helpers/utils.js +24 -0
  13. package/dist/index.js +341 -0
  14. package/dist/plugins/index.js +1 -0
  15. package/dist/plugins/quasar.js +299 -0
  16. package/dist/types/app-urls.d.ts +12 -0
  17. package/dist/types/bin/build.d.ts +8 -0
  18. package/dist/types/bin/cli.d.ts +2 -0
  19. package/dist/types/bin/dev.d.ts +15 -0
  20. package/dist/types/bin/run.d.ts +8 -0
  21. package/dist/types/bin/test.d.ts +3 -0
  22. package/dist/types/frameworks/vue/fastify-ssr-plugin.d.ts +14 -0
  23. package/dist/types/frameworks/vue/prerender.d.ts +8 -0
  24. package/dist/types/frameworks/vue/server.d.ts +9 -0
  25. package/dist/types/helpers/logger.d.ts +23 -0
  26. package/dist/types/helpers/routes.d.ts +2 -0
  27. package/dist/types/helpers/utils.d.ts +5 -0
  28. package/dist/types/index.d.ts +15 -0
  29. package/dist/types/plugins/index.d.ts +7 -0
  30. package/dist/types/plugins/quasar.d.ts +16 -0
  31. package/dist/types/vitrify-config.d.ts +67 -0
  32. package/dist/vitrify-config.js +1 -0
  33. package/package.json +94 -19
  34. package/src/node/frameworks/vue/fastify-ssr-plugin.ts +137 -0
  35. package/src/node/frameworks/vue/prerender.ts +49 -0
  36. package/src/node/frameworks/vue/server.ts +38 -0
  37. package/src/vite/vue/csr/entry.ts +8 -0
  38. package/src/vite/vue/index.html +16 -0
  39. package/src/vite/vue/main.ts +89 -0
  40. package/src/vite/vue/ssr/entry-client.ts +9 -0
  41. package/src/vite/vue/ssr/entry-server.ts +97 -0
  42. package/src/vite/vue/ssr/fastify-ssr-plugin.ts +120 -0
  43. package/src/vite/vue/ssr/prerender.ts +4 -0
  44. package/src/vite/vue/ssr/server.ts +18 -0
  45. package/src/vite/vue/ssr/server.ts.bak +61 -0
  46. package/src/vite/vue/ssr/tsconfig.json +9 -0
@@ -0,0 +1,108 @@
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 os from 'os';
6
+ import { resolveHostname } from '../helpers/utils.js';
7
+ /**
8
+ * Main approach - App CLI related
9
+ */
10
+ const dot = '•';
11
+ const banner = 'App ' + dot;
12
+ const greenBanner = green(banner);
13
+ const redBanner = red(banner);
14
+ const yellowBanner = yellow(banner);
15
+ export const clearConsole = process.stdout.isTTY
16
+ ? () => {
17
+ // Fill screen with blank lines. Then move to 0 (beginning of visible part) and clear it
18
+ const blank = '\n'.repeat(process.stdout.rows);
19
+ console.log(blank);
20
+ readline.cursorTo(process.stdout, 0, 0);
21
+ readline.clearScreenDown(process.stdout);
22
+ }
23
+ : () => { };
24
+ export const log = function (msg) {
25
+ console.log(msg ? ` ${greenBanner} ${msg}` : '');
26
+ };
27
+ export const warn = function (msg, pill) {
28
+ if (msg !== void 0) {
29
+ const pillBanner = pill !== void 0 ? bgYellow.black('', pill, '') + ' ' : '';
30
+ console.warn(` ${yellowBanner} ⚠️ ${pillBanner}${msg}`);
31
+ }
32
+ else {
33
+ console.warn();
34
+ }
35
+ };
36
+ export const fatal = function (msg, pill) {
37
+ if (msg !== void 0) {
38
+ const pillBanner = pill !== void 0 ? errorPill(pill) + ' ' : '';
39
+ console.error(`\n ${redBanner} ⚠️ ${pillBanner}${msg}\n`);
40
+ }
41
+ else {
42
+ console.error();
43
+ }
44
+ process.exit(1);
45
+ };
46
+ /**
47
+ * Extended approach - Compilation status & pills
48
+ */
49
+ export const successPill = (msg) => bgGreen.black('', msg, '');
50
+ export const infoPill = (msg) => inverse('', msg, '');
51
+ export const errorPill = (msg) => bgRed.white('', msg, '');
52
+ export const warningPill = (msg) => bgYellow.black('', msg, '');
53
+ export const success = function (msg, title = 'SUCCESS') {
54
+ console.log(` ${greenBanner} ${successPill(title)} ${green(dot + ' ' + msg)}`);
55
+ };
56
+ export const getSuccess = function (msg, title) {
57
+ return ` ${greenBanner} ${successPill(title)} ${green(dot + ' ' + msg)}`;
58
+ };
59
+ export const info = function (msg, title = 'INFO') {
60
+ console.log(` ${greenBanner} ${infoPill(title)} ${green(dot)} ${msg}`);
61
+ };
62
+ export const getInfo = function (msg, title) {
63
+ return ` ${greenBanner} ${infoPill(title)} ${green(dot)} ${msg}`;
64
+ };
65
+ export const error = function (msg, title = 'ERROR') {
66
+ console.log(` ${redBanner} ${errorPill(title)} ${red(dot + ' ' + msg)}`);
67
+ };
68
+ export const getError = function (msg, title = 'ERROR') {
69
+ return ` ${redBanner} ${errorPill(title)} ${red(dot + ' ' + msg)}`;
70
+ };
71
+ export const warning = function (msg, title = 'WARNING') {
72
+ console.log(` ${yellowBanner} ${warningPill(title)} ${yellow(dot + ' ' + msg)}`);
73
+ };
74
+ export const getWarning = function (msg, title = 'WARNING') {
75
+ return ` ${yellowBanner} ${warningPill(title)} ${yellow(dot + ' ' + msg)}`;
76
+ };
77
+ export function printHttpServerUrls(server, config) {
78
+ const address = server.address();
79
+ const isAddressInfo = (x) => x?.address;
80
+ if (isAddressInfo(address)) {
81
+ const hostname = resolveHostname(config.server.host);
82
+ const protocol = config.server.https ? 'https' : 'http';
83
+ printServerUrls(hostname, protocol, address.port, config.base, config.logger.info);
84
+ }
85
+ }
86
+ function printServerUrls(hostname, protocol, port, base, info) {
87
+ if (hostname.host === '127.0.0.1') {
88
+ const url = `${protocol}://${hostname.name}:${chalk.bold(port)}${base}`;
89
+ info(` > Local: ${chalk.cyan(url)}`);
90
+ if (hostname.name !== '127.0.0.1') {
91
+ info(` > Network: ${chalk.dim('use `--host` to expose')}`);
92
+ }
93
+ }
94
+ else {
95
+ Object.values(os.networkInterfaces())
96
+ .flatMap((nInterface) => nInterface ?? [])
97
+ .filter((detail) => detail && detail.address && detail.family === 'IPv4')
98
+ .map((detail) => {
99
+ const type = detail.address.includes('127.0.0.1')
100
+ ? 'Local: '
101
+ : 'Network: ';
102
+ const host = detail.address.replace('127.0.0.1', hostname.name);
103
+ const url = `${protocol}://${host}:${chalk.bold(port)}${base}`;
104
+ return ` > ${type} ${chalk.cyan(url)}`;
105
+ })
106
+ .forEach((msg) => info(msg));
107
+ }
108
+ }
@@ -0,0 +1,24 @@
1
+ export const routesToPaths = (routes) => {
2
+ if (!routes)
3
+ return ['/'];
4
+ const paths = new Set();
5
+ const getPaths = (routes, prefix = '') => {
6
+ // remove trailing slash
7
+ prefix = prefix.replace(/\/$/g, '');
8
+ for (const route of routes) {
9
+ let path = route.path;
10
+ // check for leading slash
11
+ if (route.path) {
12
+ path =
13
+ prefix && !route.path.startsWith('/')
14
+ ? `${prefix}/${route.path}`
15
+ : route.path;
16
+ paths.add(path);
17
+ }
18
+ if (Array.isArray(route.children))
19
+ getPaths(route.children, path);
20
+ }
21
+ };
22
+ getPaths(routes);
23
+ return [...paths];
24
+ };
@@ -0,0 +1,24 @@
1
+ export function resolveHostname(optionsHost) {
2
+ let host;
3
+ if (optionsHost === undefined ||
4
+ optionsHost === false ||
5
+ optionsHost === 'localhost') {
6
+ // Use a secure default
7
+ host = '127.0.0.1';
8
+ }
9
+ else if (optionsHost === true) {
10
+ // If passed --host in the CLI without arguments
11
+ host = undefined; // undefined typically means 0.0.0.0 or :: (listen on all IPs)
12
+ }
13
+ else {
14
+ host = optionsHost;
15
+ }
16
+ // Set host name to localhost when possible, unless the user explicitly asked for '127.0.0.1'
17
+ const name = (optionsHost !== '127.0.0.1' && host === '127.0.0.1') ||
18
+ host === '0.0.0.0' ||
19
+ host === '::' ||
20
+ host === undefined
21
+ ? 'localhost'
22
+ : host;
23
+ return { host, name };
24
+ }
package/dist/index.js ADDED
@@ -0,0 +1,341 @@
1
+ import vuePlugin from '@vitejs/plugin-vue';
2
+ import { mergeConfig } from 'vite';
3
+ import { readFileSync } from 'fs';
4
+ import builtinModules from 'builtin-modules';
5
+ import { resolve } from 'import-meta-resolve';
6
+ import { getPkgJsonDir } from './app-urls.js';
7
+ const serverModules = ['fastify', 'middie'];
8
+ const configPluginMap = {
9
+ quasar: () => import('./plugins/quasar.js').then((module) => module.QuasarPlugin)
10
+ };
11
+ export const VIRTUAL_MODULES = [
12
+ 'virtual:fastify-setup',
13
+ 'virtual:boot-functions',
14
+ 'virtual:ssr-functions',
15
+ 'virtual:on-mounted-hooks',
16
+ 'virtual:global-css',
17
+ 'virtual:static-imports'
18
+ ];
19
+ export const baseConfig = async ({ ssr, appDir, publicDir, command = 'build', mode = 'production', framework = 'vue', pwa = false }) => {
20
+ const { getAppDir, getCliDir, getCliViteDir, getSrcDir, getCwd } = await import('./app-urls.js');
21
+ if (!appDir) {
22
+ appDir = getAppDir();
23
+ }
24
+ const srcDir = getSrcDir(appDir);
25
+ const cwd = getCwd();
26
+ const cliDir = getCliDir();
27
+ const cliViteDir = getCliViteDir(cliDir);
28
+ // const {
29
+ // appDir: tempAppDir,
30
+ // cliDir,
31
+ // cliViteDir,
32
+ // srcDir
33
+ // } = await import('./app-urls.js')
34
+ // const cwd = appDir || tempAppDir
35
+ const frameworkDir = new URL(`${framework}/`, cliViteDir);
36
+ // const localPackages = ['vue', 'vue-router', 'quasar']
37
+ const localPackages = ['vue', 'vue-router'];
38
+ const cliPackages = ['vitest'];
39
+ const packageUrls = {};
40
+ await (async () => {
41
+ for (const val of localPackages)
42
+ packageUrls[val] = getPkgJsonDir(new URL(await resolve(val, appDir.href)));
43
+ })();
44
+ await (async () => {
45
+ for (const val of cliPackages)
46
+ packageUrls[val] = getPkgJsonDir(new URL(await resolve(val, cliDir.href)));
47
+ })();
48
+ // if (appDir) {
49
+ // srcDir = new URL('src/', appDir);
50
+ // quasarDir = new URL(await resolve('quasar/', appDir.href));
51
+ // ({ appDir: cwd, cliDir } = await import('./app-urls.js'))
52
+ // } else {
53
+ // ({ appDir, cliDir, srcDir, quasarDir } = await import('./app-urls.js'))
54
+ // cwd = appDir
55
+ // }
56
+ // vueDir = new URL('./', await resolve('vue', appDir.href));
57
+ // vueRouterDir = new URL('../', await resolve('vue-router', appDir.href));
58
+ // vitestDir = new URL('../', await resolve('vitest', cliDir.href));
59
+ if (!publicDir)
60
+ publicDir = new URL('public/', appDir);
61
+ /**
62
+ * TODO:Perform some manual check if command is run inside a Quasar Project
63
+ */
64
+ let vitrifyConfig;
65
+ try {
66
+ vitrifyConfig = (await import(new URL('vitrify.config.js', appDir).pathname)).default;
67
+ if (typeof vitrifyConfig === 'function')
68
+ vitrifyConfig = vitrifyConfig({ mode, command });
69
+ }
70
+ catch (e) {
71
+ console.error(e);
72
+ console.log('No vitrify.config.js file found, using defaults');
73
+ vitrifyConfig = {};
74
+ }
75
+ let { productName = 'Product name' } = JSON.parse(readFileSync(new URL('package.json', appDir).pathname, {
76
+ encoding: 'utf-8'
77
+ }));
78
+ const fastifySetup = vitrifyConfig.vitrify?.fastify?.setup || ((fastify) => { });
79
+ const ssrTransformCustomDir = () => {
80
+ return {
81
+ props: [],
82
+ needRuntime: true
83
+ };
84
+ };
85
+ const frameworkPlugins = [];
86
+ for (const framework of Object.keys(configPluginMap)) {
87
+ if (Object.keys(vitrifyConfig).includes(framework)) {
88
+ const plugin = await configPluginMap[framework]();
89
+ frameworkPlugins.push(await plugin({
90
+ ssr,
91
+ pwa
92
+ }));
93
+ }
94
+ }
95
+ let bootFunctions;
96
+ let ssrFunctions;
97
+ let onMountedHooks;
98
+ let globalCss;
99
+ let staticImports;
100
+ let sassVariables;
101
+ let additionalData;
102
+ const plugins = [
103
+ vuePlugin({
104
+ template: {
105
+ ssr: !!ssr,
106
+ compilerOptions: {
107
+ directiveTransforms: {
108
+ 'close-popup': ssrTransformCustomDir,
109
+ intersection: ssrTransformCustomDir,
110
+ ripple: ssrTransformCustomDir,
111
+ mutation: ssrTransformCustomDir,
112
+ morph: ssrTransformCustomDir,
113
+ scroll: ssrTransformCustomDir,
114
+ 'scroll-fire': ssrTransformCustomDir,
115
+ 'touch-hold': ssrTransformCustomDir,
116
+ 'touch-pan': ssrTransformCustomDir,
117
+ 'touch-repeat': ssrTransformCustomDir,
118
+ 'touch-swipe': ssrTransformCustomDir
119
+ }
120
+ }
121
+ }
122
+ }),
123
+ ...frameworkPlugins,
124
+ // await QuasarPlugin({
125
+ // ssr: ssr,
126
+ // pwa: pwa
127
+ // // quasarDir: packageUrls.quasar
128
+ // }),
129
+ {
130
+ name: 'vitrify-setup',
131
+ enforce: 'post',
132
+ config: async (config, env) => {
133
+ bootFunctions = config.vitrify?.bootFunctions || [];
134
+ ssrFunctions = config.vitrify?.ssrFunctions || [];
135
+ onMountedHooks = config.vitrify?.hooks?.onMounted || [];
136
+ globalCss = config.vitrify?.globalCss || [];
137
+ staticImports = config.vitrify?.staticImports || {};
138
+ sassVariables = config.vitrify?.sass?.variables || {};
139
+ additionalData = config.vitrify?.sass?.additionalData || [];
140
+ return {
141
+ css: {
142
+ preprocessorOptions: {
143
+ sass: {
144
+ additionalData: [
145
+ ...Object.entries(sassVariables).map(([key, value]) => `${key}: ${value}`),
146
+ ...additionalData
147
+ // config.css?.preprocessorOptions?.sass.additionalData
148
+ ].join('\n')
149
+ }
150
+ }
151
+ }
152
+ };
153
+ },
154
+ configResolved: (config) => {
155
+ if (process.env.DEBUG) {
156
+ console.log(config.css?.preprocessorOptions?.sass.additionalData);
157
+ console.log(config.optimizeDeps);
158
+ }
159
+ },
160
+ resolveId(id) {
161
+ if (VIRTUAL_MODULES.includes(id))
162
+ return id;
163
+ return;
164
+ },
165
+ load(id) {
166
+ if (id === 'virtual:fastify-setup') {
167
+ return `export const setup = ${String(fastifySetup)}`;
168
+ }
169
+ else if (id === 'virtual:boot-functions') {
170
+ return `export default [${bootFunctions
171
+ .map((fn) => `${String(fn)}`)
172
+ .join(', ')}]`;
173
+ }
174
+ else if (id === 'virtual:ssr-functions') {
175
+ return `export default [${ssrFunctions
176
+ .map((fn) => `${String(fn)}`)
177
+ .join(', ')}]`;
178
+ }
179
+ else if (id === 'virtual:on-mounted-hooks') {
180
+ return `export default [${onMountedHooks
181
+ .map((fn) => `${String(fn)}`)
182
+ .join(', ')}]`;
183
+ }
184
+ else if (id === 'virtual:global-css') {
185
+ return `${globalCss.map((css) => `import '${css}'`).join('\n')}`;
186
+ }
187
+ else if (id === 'virtual:static-imports') {
188
+ return `${Object.entries(staticImports)
189
+ .map(([key, value]) => `export { ${value.join(',')} } from '${key}';`)
190
+ .join('\n')}`;
191
+ }
192
+ return null;
193
+ }
194
+ }
195
+ ];
196
+ if (command !== 'test') {
197
+ plugins.unshift({
198
+ name: 'html-transform',
199
+ enforce: 'pre',
200
+ transform: (code, id) => {
201
+ if (id.endsWith('App.vue')) {
202
+ code =
203
+ code +
204
+ `<style lang="sass">
205
+ // do not remove, required for additionalData import
206
+ </style>`;
207
+ }
208
+ return code;
209
+ },
210
+ transformIndexHtml: {
211
+ enforce: 'pre',
212
+ transform: (html) => {
213
+ let entry;
214
+ switch (ssr) {
215
+ case 'ssg':
216
+ case 'server':
217
+ case 'client':
218
+ entry = new URL('ssr/entry-client.ts', frameworkDir).pathname;
219
+ break;
220
+ default:
221
+ entry = new URL('csr/entry.ts', frameworkDir).pathname;
222
+ }
223
+ const entryScript = `<script type="module" src="${entry}"></script>`;
224
+ html = html
225
+ .replace('<!--entry-script-->', entryScript)
226
+ .replace('<!--product-name-->', productName);
227
+ return html;
228
+ }
229
+ }
230
+ });
231
+ plugins.unshift({
232
+ name: 'product-name',
233
+ enforce: 'post',
234
+ config: (config, env) => {
235
+ if (config.vitrify?.productName)
236
+ productName = config.vitrify?.productName;
237
+ },
238
+ transformIndexHtml: {
239
+ enforce: 'post',
240
+ transform: (html) => {
241
+ html = html.replace('<!--product-name-->', productName);
242
+ return html;
243
+ }
244
+ }
245
+ });
246
+ }
247
+ const alias = [
248
+ { find: 'src', replacement: srcDir.pathname },
249
+ { find: 'app', replacement: appDir.pathname },
250
+ { find: 'cwd', replacement: cwd.pathname },
251
+ { find: 'boot', replacement: new URL('boot/', srcDir).pathname },
252
+ { find: 'assets', replacement: new URL('assets/', srcDir).pathname },
253
+ // ...Object.entries(packageUrls).map(([key, value]) => ({
254
+ // find: key, replacement: value.pathname
255
+ // })),
256
+ { find: 'vue', replacement: packageUrls['vue'].pathname },
257
+ { find: 'vue-router', replacement: packageUrls['vue-router'].pathname },
258
+ { find: 'vitrify', replacement: cliDir.pathname }
259
+ ];
260
+ if (command === 'test')
261
+ alias.push({
262
+ find: 'vitest',
263
+ replacement: packageUrls.vitest.pathname
264
+ });
265
+ const config = {
266
+ root: frameworkDir.pathname,
267
+ publicDir: publicDir.pathname,
268
+ vitrify: {
269
+ productName,
270
+ urls: {
271
+ // @ts-ignore
272
+ app: appDir,
273
+ cli: cliDir,
274
+ src: srcDir,
275
+ cwd,
276
+ packages: packageUrls
277
+ }
278
+ },
279
+ plugins,
280
+ optimizeDeps: {
281
+ exclude: ['vue']
282
+ },
283
+ resolve: {
284
+ // Dedupe uses require which breaks ESM SSR builds
285
+ // dedupe: [
286
+ // 'vue',
287
+ // 'vue-router'
288
+ // ],
289
+ alias
290
+ },
291
+ build: {
292
+ target: ssr === 'server' ? 'esnext' : 'modules',
293
+ ssr: ssr === 'server' ? true : false,
294
+ ssrManifest: ssr === 'client' || ssr === 'ssg',
295
+ rollupOptions: ssr === 'server'
296
+ ? {
297
+ input: [
298
+ new URL('ssr/entry-server.ts', frameworkDir).pathname,
299
+ new URL('ssr/prerender.ts', frameworkDir).pathname,
300
+ new URL('ssr/server.ts', frameworkDir).pathname
301
+ ],
302
+ output: {
303
+ minifyInternalExports: false,
304
+ entryFileNames: '[name].mjs',
305
+ chunkFileNames: '[name].mjs',
306
+ format: 'es',
307
+ manualChunks: (id) => {
308
+ if (id.includes('fastify-ssr-plugin')) {
309
+ return 'fastify-ssr-plugin';
310
+ }
311
+ else if (id.includes('node_modules')) {
312
+ return 'vendor';
313
+ }
314
+ }
315
+ }
316
+ }
317
+ : {
318
+ output: {
319
+ format: 'es'
320
+ }
321
+ }
322
+ },
323
+ // css: {
324
+ // preprocessorOptions: {
325
+ // sass: {
326
+ // additionalData: sass ? [...sass].join('\n') : undefined
327
+ // }
328
+ // }
329
+ // },
330
+ ssr: {
331
+ // Create a SSR bundle
332
+ noExternal: [
333
+ new RegExp(`^(?!.*(${[...builtinModules, ...serverModules].join('|')}))`)
334
+ ]
335
+ },
336
+ define: {
337
+ __BASE_URL__: `'/'`
338
+ }
339
+ };
340
+ return mergeConfig(config, vitrifyConfig);
341
+ };
@@ -0,0 +1 @@
1
+ export {};