vitrify 0.3.0 → 0.4.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 (36) hide show
  1. package/dist/app-urls.js +1 -1
  2. package/dist/bin/cli.js +26 -4
  3. package/dist/bin/dev.js +59 -27
  4. package/dist/frameworks/vue/fastify-ssr-plugin.js +67 -16
  5. package/dist/frameworks/vue/server.js +9 -4
  6. package/dist/helpers/collect-css-ssr.js +57 -0
  7. package/dist/index.js +255 -69
  8. package/dist/plugins/quasar.js +9 -4
  9. package/dist/types/bin/build.d.ts +2 -2
  10. package/dist/types/bin/dev.d.ts +39 -3
  11. package/dist/types/frameworks/vue/fastify-ssr-plugin.d.ts +6 -3
  12. package/dist/types/frameworks/vue/server.d.ts +9 -5
  13. package/dist/types/helpers/collect-css-ssr.d.ts +10 -0
  14. package/dist/types/helpers/routes.d.ts +1 -1
  15. package/dist/types/index.d.ts +1 -1
  16. package/dist/types/plugins/index.d.ts +1 -1
  17. package/dist/types/vitrify-config.d.ts +3 -4
  18. package/package.json +32 -32
  19. package/src/node/app-urls.ts +1 -1
  20. package/src/node/bin/build.ts +2 -2
  21. package/src/node/bin/cli.ts +33 -5
  22. package/src/node/bin/dev.ts +92 -34
  23. package/src/node/frameworks/vue/fastify-ssr-plugin.ts +80 -18
  24. package/src/node/frameworks/vue/server.ts +22 -8
  25. package/src/node/helpers/collect-css-ssr.ts +77 -0
  26. package/src/node/index.ts +285 -81
  27. package/src/node/plugins/index.ts +1 -1
  28. package/src/node/plugins/quasar.ts +9 -4
  29. package/src/node/vitrify-config.ts +7 -4
  30. package/src/vite/fastify/entry.ts +11 -0
  31. package/src/vite/fastify/server.ts +10 -0
  32. package/src/vite/vue/index.html +1 -0
  33. package/src/vite/vue/main.ts +0 -1
  34. package/src/vite/vue/ssr/app.ts +25 -0
  35. package/src/vite/vue/ssr/entry-server.ts +13 -1
  36. package/src/vite/vue/ssr/server.ts +23 -14
package/dist/app-urls.js CHANGED
@@ -14,7 +14,7 @@ export const getSrcDir = (appDir) => new URL('src/', appDir);
14
14
  export const getCwd = () => new URL(`file://${process.cwd()}/`);
15
15
  export const parsePath = (path, basePath) => {
16
16
  if (path) {
17
- if (path.slice(-1) !== '/')
17
+ if (!path.includes('.') && path.slice(-1) !== '/')
18
18
  path += '/';
19
19
  if (path.startsWith('.')) {
20
20
  return new URL(path, basePath);
package/dist/bin/cli.js CHANGED
@@ -36,6 +36,13 @@ cli
36
36
  outDir: new URL('spa/', baseOutDir).pathname
37
37
  });
38
38
  break;
39
+ case 'fastify':
40
+ await build({
41
+ ssr: 'fastify',
42
+ ...args,
43
+ outDir: new URL('server/', baseOutDir).pathname
44
+ });
45
+ break;
39
46
  case 'ssr':
40
47
  await build({
41
48
  ssr: 'client',
@@ -80,28 +87,43 @@ cli
80
87
  .option('-m, --mode [mode]', 'Development server mode', { default: 'csr' })
81
88
  .option('--host [host]', 'Specify which IP addresses the server should listen on', { default: '127.0.0.1' })
82
89
  .option('--appDir [appDir]', 'Application directory')
90
+ .option('--app [app]', 'Fastify app instance path')
83
91
  .option('--publicDir [publicDir]', 'Public directory')
84
92
  .action(async (options) => {
85
93
  let server;
86
- let vite;
94
+ let config;
87
95
  if (options.host === true) {
88
96
  options.host = '0.0.0.0';
89
97
  }
90
98
  const { createServer } = await import('./dev.js');
91
99
  const cwd = (await import('../app-urls.js')).getCwd();
100
+ let app;
101
+ const appPath = parsePath(options.app, cwd)?.pathname;
102
+ if (appPath) {
103
+ app = await import(appPath);
104
+ }
92
105
  switch (options.mode) {
93
106
  case 'ssr':
94
107
  ;
95
- ({ server, vite } = await createServer({
108
+ ({ server, config } = await createServer({
96
109
  mode: 'ssr',
97
110
  host: options.host,
98
111
  appDir: parsePath(options.appDir, cwd),
99
112
  publicDir: parsePath(options.publicDir, cwd)
100
113
  }));
101
114
  break;
115
+ case 'fastify':
116
+ ;
117
+ ({ server, config } = await createServer({
118
+ mode: 'fastify',
119
+ host: options.host,
120
+ appDir: parsePath(options.appDir, cwd),
121
+ publicDir: parsePath(options.publicDir, cwd)
122
+ }));
123
+ break;
102
124
  default:
103
125
  ;
104
- ({ server, vite } = await createServer({
126
+ ({ server, config } = await createServer({
105
127
  host: options.host,
106
128
  appDir: parsePath(options.appDir, cwd),
107
129
  publicDir: parsePath(options.publicDir, cwd)
@@ -109,7 +131,7 @@ cli
109
131
  break;
110
132
  }
111
133
  console.log('Dev server running at:');
112
- printHttpServerUrls(server, vite.config);
134
+ printHttpServerUrls(server, config);
113
135
  });
114
136
  cli.command('test').action(async (options) => {
115
137
  const { test } = await import('./test.js');
package/dist/bin/dev.js CHANGED
@@ -1,19 +1,20 @@
1
1
  import { searchForWorkspaceRoot } from 'vite';
2
2
  import { baseConfig } from '../index.js';
3
3
  import fastify from 'fastify';
4
- import { readFileSync } from 'fs';
5
- export async function createServer({ port = 3000, logLevel = 'info', mode = 'csr', framework = 'vue', host, appDir, publicDir }) {
6
- const { getAppDir, getCliDir, getCwd } = await import('../app-urls.js');
7
- const cwd = getCwd();
4
+ export async function createVitrifyDevServer({ port = 3000, logLevel = 'info', mode = 'csr', framework = 'vue', host, appDir, publicDir }) {
5
+ const { getAppDir, getCliDir, getCliViteDir, getCwd } = await import('../app-urls.js');
8
6
  const cliDir = getCliDir();
9
7
  if (!appDir)
10
8
  appDir = getAppDir();
11
- const { fastifySsrPlugin } = await import(`../frameworks/${framework}/fastify-ssr-plugin.js`);
12
- /**
13
- * @type {import('vite').ViteDevServer}
14
- */
15
- const config = await baseConfig({
16
- ssr: mode === 'ssr' ? 'server' : undefined,
9
+ let config = {};
10
+ let ssrMode;
11
+ if (mode === 'ssr')
12
+ ssrMode = 'server';
13
+ if (mode === 'fastify')
14
+ ssrMode = 'fastify';
15
+ config = await baseConfig({
16
+ framework,
17
+ ssr: ssrMode,
17
18
  command: 'dev',
18
19
  mode: 'development',
19
20
  appDir,
@@ -21,12 +22,16 @@ export async function createServer({ port = 3000, logLevel = 'info', mode = 'csr
21
22
  });
22
23
  config.logLevel = logLevel;
23
24
  config.server = {
25
+ https: config.server?.https,
24
26
  port,
25
- middlewareMode: mode === 'ssr' ? 'ssr' : undefined,
27
+ // middlewareMode: mode === 'ssr' ? 'ssr' : undefined,
28
+ middlewareMode: mode !== 'csr' ? 'ssr' : false,
26
29
  fs: {
27
30
  allow: [
28
31
  searchForWorkspaceRoot(process.cwd()),
29
- searchForWorkspaceRoot(appDir.pathname),
32
+ ...(Array.isArray(appDir)
33
+ ? appDir.map((dir) => searchForWorkspaceRoot(dir.pathname))
34
+ : [searchForWorkspaceRoot(appDir.pathname)]),
30
35
  searchForWorkspaceRoot(cliDir.pathname)
31
36
  // appDir.pathname,
32
37
  ]
@@ -39,29 +44,56 @@ export async function createServer({ port = 3000, logLevel = 'info', mode = 'csr
39
44
  },
40
45
  host
41
46
  };
42
- const vite = await (await import('vite')).createServer({
47
+ const vitrifyDevServer = await (await import('vite')).createServer({
43
48
  configFile: false,
44
49
  ...config
45
50
  });
46
- const { productName = 'Product name' } = JSON.parse(readFileSync(new URL('package.json', appDir).pathname, {
47
- encoding: 'utf-8'
48
- }));
49
- let app;
51
+ return vitrifyDevServer;
52
+ }
53
+ export async function createServer({ port = 3000, logLevel = 'info', mode = 'csr', framework = 'vue', host, appDir, publicDir }) {
54
+ const { getAppDir, getCliDir, getCliViteDir, getCwd } = await import('../app-urls.js');
55
+ const cliDir = getCliDir();
56
+ const vite = await createVitrifyDevServer({
57
+ port,
58
+ logLevel,
59
+ mode,
60
+ framework,
61
+ host,
62
+ appDir,
63
+ publicDir
64
+ });
65
+ let entryUrl;
66
+ let setup;
50
67
  let server;
51
- if (mode === 'ssr') {
52
- console.log('SSR mode');
53
- app = fastify();
54
- await app.register(fastifySsrPlugin, {
55
- appDir,
56
- cliDir,
57
- vite,
58
- productName
68
+ console.log(`Development mode: ${mode}`);
69
+ if (['ssr', 'fastify'].includes(mode)) {
70
+ const entryUrl = mode === 'fastify'
71
+ ? new URL('src/vite/fastify/entry.ts', cliDir).pathname
72
+ : new URL(`src/vite/${framework}/ssr/entry-server.ts`, cliDir).pathname;
73
+ ({ setup } = await vite.ssrLoadModule(entryUrl));
74
+ const app = fastify({
75
+ https: typeof vite.config.server.https === 'object'
76
+ ? vite.config.server.https
77
+ : {}
78
+ });
79
+ if (setup) {
80
+ await setup({
81
+ fastify: app
82
+ });
83
+ }
84
+ // await app.register(fastifySsrPlugin, {
85
+ // appDir,
86
+ // vitrifyDir: new URL('../..', import.meta.url),
87
+ // mode: 'development'
88
+ // })
89
+ await app.listen({
90
+ port: Number(port || 3000),
91
+ host
59
92
  });
60
- await app.listen(port || 3000, host);
61
93
  server = app.server;
62
94
  }
63
95
  else {
64
96
  server = (await vite.listen()).httpServer;
65
97
  }
66
- return { server, vite };
98
+ return { server, config: vite.config };
67
99
  }
@@ -1,22 +1,66 @@
1
- import fastifyStatic from 'fastify-static';
1
+ import fastifyStatic from '@fastify/static';
2
2
  import { readFileSync } from 'fs';
3
+ import { componentsModules, collectCss } from '../../helpers/collect-css-ssr.js';
3
4
  const fastifySsrPlugin = async (fastify, options, done) => {
4
- if (import.meta.env.MODE === 'development') {
5
- if (!options.vite)
6
- throw new Error('Option vite cannot be undefined');
7
- const middie = (await import('middie')).default;
5
+ options.vitrifyDir =
6
+ options.vitrifyDir || new URL('../../..', import.meta.url);
7
+ const frameworkDir = new URL('vite/vue/', options.vitrifyDir);
8
+ options.baseUrl = options.baseUrl || '/';
9
+ if (options.baseUrl.charAt(options.baseUrl.length - 1) !== '/' ||
10
+ options.baseUrl.charAt(0) !== '/')
11
+ throw new Error('baseUrl should start and end with a /');
12
+ if (options.mode === 'development') {
13
+ if (!options.vitrifyDir)
14
+ throw new Error('Option vitrifyDir cannot be undefined');
15
+ // if (!options.vite) throw new Error('Option vite cannot be undefined')
16
+ // const { resolve } = await import('import-meta-resolve')
17
+ // const cliDir = new URL('../', await resolve('vitrify', import.meta.url))
18
+ options.appDir = options.appDir || new URL('../../..', import.meta.url);
19
+ const { createServer, searchForWorkspaceRoot } = await import('vite');
20
+ const { baseConfig } = await import('vitrify');
21
+ const cliDir = options.vitrifyDir;
22
+ const config = await baseConfig({
23
+ ssr: 'server',
24
+ command: 'dev',
25
+ mode: 'development',
26
+ appDir: options.appDir,
27
+ publicDir: options.publicDir || new URL('public', options.appDir)
28
+ });
29
+ config.server = {
30
+ middlewareMode: true,
31
+ fs: {
32
+ allow: [
33
+ searchForWorkspaceRoot(process.cwd()),
34
+ searchForWorkspaceRoot(options.appDir.pathname),
35
+ searchForWorkspaceRoot(cliDir.pathname)
36
+ // appDir.pathname,
37
+ ]
38
+ },
39
+ watch: {
40
+ // During tests we edit the files too fast and sometimes chokidar
41
+ // misses change events, so enforce polling for consistency
42
+ usePolling: true,
43
+ interval: 100
44
+ }
45
+ };
46
+ const vite = await createServer({
47
+ configFile: false,
48
+ ...config
49
+ });
50
+ console.log('Dev mode');
51
+ const middie = (await import('@fastify/middie')).default;
8
52
  await fastify.register(middie);
9
- fastify.use(options.vite.middlewares);
10
- fastify.get('*', async (req, res) => {
53
+ fastify.use(vite.middlewares);
54
+ fastify.get(`${options.baseUrl}*`, async (req, res) => {
11
55
  try {
12
56
  const url = req.raw.url;
13
57
  const ssrContext = {
14
58
  req,
15
59
  res
16
60
  };
17
- const template = readFileSync(new URL('index.html', options.cliDir)).toString();
18
- const entryUrl = new URL('ssr/entry-server.ts', options.cliDir).pathname;
19
- const render = (await options.vite.ssrLoadModule(entryUrl)).render;
61
+ const template = readFileSync(new URL('index.html', frameworkDir)).toString();
62
+ const entryUrl = new URL('ssr/entry-server.ts', frameworkDir).pathname;
63
+ const render = (await vite.ssrLoadModule(entryUrl)).render;
20
64
  let manifest;
21
65
  // TODO: https://github.com/vitejs/vite/issues/2282
22
66
  try {
@@ -25,11 +69,18 @@ const fastifySsrPlugin = async (fastify, options, done) => {
25
69
  catch (e) {
26
70
  manifest = {};
27
71
  }
72
+ const cssModules = [entryUrl];
73
+ // // @ts-ignore
74
+ // if (options.vite?.config.vitrify!.globalCss)
75
+ // cssModules.push(...options.vite?.config.vitrify.globalCss)
76
+ const matchedModules = componentsModules(cssModules, vite);
77
+ const css = collectCss(matchedModules);
28
78
  const [appHtml, preloadLinks] = await render(url, manifest, ssrContext);
29
79
  const html = template
30
80
  .replace(`<!--preload-links-->`, preloadLinks)
31
81
  .replace(`<!--app-html-->`, appHtml)
32
- .replace('<!--product-name-->', options.productName || 'Product name');
82
+ .replace('<!--product-name-->', options.productName || 'Product name')
83
+ .replace('<!--dev-ssr-css-->', css);
33
84
  res.code(200);
34
85
  res.type('text/html');
35
86
  res.send(html);
@@ -37,14 +88,14 @@ const fastifySsrPlugin = async (fastify, options, done) => {
37
88
  }
38
89
  catch (e) {
39
90
  console.error(e.stack);
40
- options.vite && options.vite.ssrFixStacktrace(e);
91
+ vite && vite.ssrFixStacktrace(e);
41
92
  res.code(500);
42
93
  res.send(e.stack);
43
94
  }
44
95
  });
45
96
  }
46
97
  else {
47
- options.baseUrl = options.baseUrl || '/';
98
+ options.appDir = options.appDir || new URL('../../..', import.meta.url);
48
99
  fastify.register(fastifyStatic, {
49
100
  root: new URL('./dist/ssr/client', options.appDir).pathname,
50
101
  wildcard: false,
@@ -52,7 +103,7 @@ const fastifySsrPlugin = async (fastify, options, done) => {
52
103
  prefix: options.baseUrl
53
104
  });
54
105
  fastify.get(`${options.baseUrl}*`, async (req, res) => {
55
- const url = req.raw.url;
106
+ const url = req.raw.url?.replace(options.baseUrl, '/');
56
107
  const provide = options.provide ? await options.provide(req, res) : {};
57
108
  const ssrContext = {
58
109
  req,
@@ -69,8 +120,8 @@ const fastifySsrPlugin = async (fastify, options, done) => {
69
120
  let html = template
70
121
  .replace(`<!--preload-links-->`, preloadLinks)
71
122
  .replace(`<!--app-html-->`, appHtml);
72
- if (options.onRenderedHooks?.length) {
73
- for (const ssrFunction of options.onRenderedHooks) {
123
+ if (options.onRendered?.length) {
124
+ for (const ssrFunction of options.onRendered) {
74
125
  html = ssrFunction(html, ssrContext);
75
126
  }
76
127
  }
@@ -1,14 +1,19 @@
1
1
  import fastify from 'fastify';
2
- import { fastifySsrPlugin } from './fastify-ssr-plugin.js';
3
- export const createApp = ({ setup, appDir, baseUrl, onRenderedHooks }) => {
2
+ export const createApp = ({ onSetup, appDir, baseUrl, onRendered, fastifySsrPlugin, vitrifyDir, mode }) => {
4
3
  const app = fastify({
5
4
  logger: true
6
5
  });
7
6
  app.register(fastifySsrPlugin, {
8
7
  baseUrl,
9
8
  appDir,
10
- onRenderedHooks
9
+ onRendered,
10
+ vitrifyDir,
11
+ mode
11
12
  });
12
- setup(app);
13
+ // if (onSetup?.length) {
14
+ // for (const setup of onSetup) {
15
+ // setup(app)
16
+ // }
17
+ // }
13
18
  return app;
14
19
  };
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Collect SSR CSS for Vite
3
+ */
4
+ export const componentsModules = (components, vite) => {
5
+ const matchedModules = new Set();
6
+ components.forEach((component) => {
7
+ const modules = vite.moduleGraph.getModulesByFile(component);
8
+ modules?.forEach((mod) => matchedModules.add(mod));
9
+ });
10
+ return matchedModules;
11
+ };
12
+ export const collectCss = (mods, styles = new Map(), checkedComponents = new Set()) => {
13
+ for (const mod of mods) {
14
+ if ((mod.file?.endsWith('.scss') ||
15
+ mod.file?.endsWith('.css') ||
16
+ mod.id?.includes('vue&type=style')) &&
17
+ mod.ssrModule) {
18
+ styles.set(mod.url, mod.ssrModule.default);
19
+ }
20
+ if (mod.importedModules.size > 0 && !checkedComponents.has(mod.id)) {
21
+ checkedComponents.add(mod.id);
22
+ collectCss(mod.importedModules, styles, checkedComponents);
23
+ }
24
+ }
25
+ let result = '';
26
+ styles.forEach((content, id) => {
27
+ const styleTag = `<style type="text/css" vite-module-id="${hashCode(id)}">${content}</style>`;
28
+ result = result.concat(styleTag);
29
+ });
30
+ return result;
31
+ };
32
+ /**
33
+ * Client listener to detect updated modules through HMR, and remove the initial styled attached to the head
34
+ */
35
+ export const removeCssHotReloaded = () => {
36
+ if (import.meta.hot) {
37
+ import.meta.hot.on('vite:beforeUpdate', (module) => {
38
+ module.updates.forEach((update) => {
39
+ const moduleStyle = document.querySelector(`[vite-module-id="${hashCode(update.acceptedPath)}"]`);
40
+ if (moduleStyle) {
41
+ moduleStyle.remove();
42
+ }
43
+ });
44
+ });
45
+ }
46
+ };
47
+ const hashCode = (moduleId) => {
48
+ let hash = 0, i, chr;
49
+ if (moduleId.length === 0)
50
+ return hash;
51
+ for (i = 0; i < moduleId.length; i++) {
52
+ chr = moduleId.charCodeAt(i);
53
+ hash = (hash << 5) - hash + chr;
54
+ hash |= 0; // Convert to 32bit integer
55
+ }
56
+ return hash;
57
+ };