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
package/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # Vitrify
2
+
3
+ > Pre-configured Vite CLI for your framework
4
+
5
+ - Use a simple configuration file to configure and integrate required Vite plugins into your project.
6
+ - Client-Side Rendering (CSR) and Server-Side Rendering (SSR) builds and development servers.
7
+
8
+ ## Features
9
+
10
+ - Uses [Fastify](https://github.com/fastify/fastify-vite) for the development server and SSR production server.
11
+ - Generates a Fastify plugin to serve your server-side rendered application.
12
+ - A [`run`](./src/node/bin/run.ts) command which injects context such as application paths into the script which you want to run.
13
+ - A [`test`](./src/node/bin/test.ts) command which runs a pre-configured [Vitest](https://github.com/vitest-dev/vitest) instance.
14
+ - An [extra plugin layer](./src/node/plugins/index.ts) which provides some extra context such as the SSR mode (client or server) and PWA mode. Used for UI frameworks which render differently based on these settings.
15
+
16
+ ## Commands
17
+
18
+ ### build
19
+
20
+ ```
21
+ Usage:
22
+ $ vitrify build
23
+
24
+ Options:
25
+ -m, --mode [mode] Build mode (default: csr)
26
+ --base [base] Base public path
27
+ --outDir [outDir] Output directory
28
+ --appDir [appDir] App directory
29
+ --publicDir [publicDir] Public directory
30
+ --productName [productName] Product name
31
+ -h, --help Display this message
32
+ ```
33
+
34
+ ### dev
35
+
36
+ ```
37
+ Usage:
38
+ $ vitrify dev
39
+
40
+ Options:
41
+ -m, --mode [mode] Development server mode (default: csr)
42
+ --host [host] Specify which IP addresses the server should listen on (default: 127.0.0.1)
43
+ --appDir [appDir] Application directory
44
+ --publicDir [publicDir] Public directory
45
+ -h, --help Display this message
46
+ ```
47
+
48
+ ### test
49
+
50
+ ```
51
+ Usage:
52
+ $ vitrify test
53
+
54
+ Options:
55
+ -h, --help Display this message
56
+ ```
57
+
58
+ ### run
59
+
60
+ ```
61
+ Usage:
62
+ $ vitrify run <file>
63
+
64
+ Options:
65
+ -h, --help Display this message
66
+ ```
67
+
68
+ ## Structure
69
+
70
+ ```mermaid
71
+ graph TD;
72
+ node/bin/cli.ts-->node/bin/build.ts;
73
+ node/bin/cli.ts-->node/bin/dev.ts;
74
+ node/bin/cli.ts-->node/bin/test.ts;
75
+ node/bin/cli.ts-->node/bin/run.ts;
76
+ node/bin/build.ts-->node/index.ts{Load baseConfig};
77
+ node/bin/dev.ts-->node/index.ts{Load baseConfig};
78
+ node/index.ts-->vitrify.config.js{Load vitrify.config.js};
79
+ vitrify.config.js-->node/plugins{Load plugins};
80
+ node/plugins-->framework{Load framework entrypoints from vite/...};
81
+ framework-->merge{Merge vitrify.config.js with Vitrify configuration};
82
+ merge-->build{Build the application};
83
+ merge-->dev{Spin up dev server};
84
+ node/bin/test.ts-->test{Run a pre-configured Vitest instance};
85
+ node/bin/run.ts-->run{Inject context into script and run script};
86
+ ```
@@ -0,0 +1,36 @@
1
+ // import { resolve } from 'import-meta-resolve'
2
+ import { existsSync } from 'fs';
3
+ export const getPkgJsonDir = (dir) => {
4
+ const pkgJsonPath = new URL('package.json', dir);
5
+ if (existsSync(pkgJsonPath.pathname)) {
6
+ return new URL('./', pkgJsonPath);
7
+ }
8
+ return getPkgJsonDir(new URL('..', dir));
9
+ };
10
+ export const getAppDir = () => getPkgJsonDir(new URL(`file://${process.cwd()}/`));
11
+ export const getCliDir = () => getPkgJsonDir(new URL('./', import.meta.url));
12
+ export const getCliViteDir = (cliDir) => new URL('src/vite/', cliDir);
13
+ export const getSrcDir = (appDir) => new URL('src/', appDir);
14
+ export const getCwd = () => new URL(`file://${process.cwd()}/`);
15
+ // export const quasarDir = getPkgJsonDir(new URL('./', await resolve('quasar', appDir.href)))
16
+ export const parsePath = (path, basePath) => {
17
+ if (path) {
18
+ if (path.slice(-1) !== '/')
19
+ path += '/';
20
+ if (path.startsWith('.')) {
21
+ return new URL(path, basePath);
22
+ }
23
+ else if (path) {
24
+ return new URL(`file://${path}`);
25
+ }
26
+ }
27
+ return;
28
+ };
29
+ export const getProjectURLs = (appDir, cliDir) => {
30
+ const srcDir = getSrcDir(appDir);
31
+ return {
32
+ src: (path) => new URL(path, srcDir),
33
+ app: (path) => new URL(path, appDir),
34
+ cli: (path) => new URL(path, cliDir)
35
+ };
36
+ };
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/node --experimental-specifier-resolution=node
2
+ import { baseConfig } from '../index.js';
3
+ // import { promises as fs } from 'fs'
4
+ // import { routesToPaths } from '../helpers/routes.js'
5
+ import { build as viteBuild } from 'vite';
6
+ // import { SsrFunction } from '../vitrify-config.js'
7
+ // export const prerender = async ({
8
+ // outDir,
9
+ // templatePath,
10
+ // manifestPath,
11
+ // entryServerPath,
12
+ // injectSsrContext
13
+ // }: {
14
+ // outDir: string
15
+ // templatePath: string
16
+ // manifestPath: string
17
+ // entryServerPath: string
18
+ // injectSsrContext: SsrFunction
19
+ // }) => {
20
+ // let template
21
+ // let manifest
22
+ // const promises = []
23
+ // template = (await fs.readFile(templatePath)).toString()
24
+ // manifest = await fs.readFile(manifestPath)
25
+ // let { render, getRoutes } = await import(entryServerPath)
26
+ // const routes = await getRoutes()
27
+ // const paths = routesToPaths(routes).filter(
28
+ // (i) => !i.includes(':') && !i.includes('*')
29
+ // )
30
+ // for (let url of paths) {
31
+ // const filename =
32
+ // (url.endsWith('/') ? 'index' : url.replace(/^\//g, '')) + '.html'
33
+ // console.log(`Generating ${filename}`)
34
+ // const ssrContext = {
35
+ // req: { headers: {}, url },
36
+ // res: {}
37
+ // }
38
+ // const [appHtml, preloadLinks] = await render(url, manifest, ssrContext)
39
+ // let html = template
40
+ // .replace(`<!--preload-links-->`, preloadLinks)
41
+ // .replace(`<!--app-html-->`, appHtml)
42
+ // html = injectSsrContext(html, ssrContext)
43
+ // promises.push(fs.writeFile(outDir + filename, html, 'utf-8'))
44
+ // }
45
+ // return Promise.all(promises)
46
+ // }
47
+ export async function build(opts) {
48
+ const config = await baseConfig({
49
+ command: 'build',
50
+ mode: 'production',
51
+ ssr: opts?.ssr,
52
+ appDir: opts.appDir,
53
+ publicDir: opts.publicDir
54
+ });
55
+ config.build = {
56
+ ...config.build,
57
+ minify: false,
58
+ outDir: opts.outDir,
59
+ emptyOutDir: !!opts.outDir
60
+ };
61
+ if (opts.base) {
62
+ config.define = {
63
+ ...config.define,
64
+ __BASE_URL__: `'${opts.base}'`
65
+ };
66
+ }
67
+ return viteBuild({
68
+ configFile: false,
69
+ base: opts.base,
70
+ // logLevel: 'silent',
71
+ ...config
72
+ });
73
+ }
@@ -0,0 +1,139 @@
1
+ #!/usr/bin/env node
2
+ import cac from 'cac';
3
+ import { getAppDir, parsePath } from '../app-urls.js';
4
+ import { printHttpServerUrls } from '../helpers/logger.js';
5
+ const cli = cac('vitrify');
6
+ cli
7
+ .command('build')
8
+ .option('-m, --mode [mode]', 'Build mode', { default: 'csr' })
9
+ .option('--base [base]', 'Base public path')
10
+ .option('--outDir [outDir]', 'Output directory')
11
+ .option('--appDir [appDir]', 'App directory')
12
+ .option('--publicDir [publicDir]', 'Public directory')
13
+ .option('--productName [productName]', 'Product name')
14
+ .action(async (options) => {
15
+ const { build } = await import('./build.js');
16
+ let appDir;
17
+ let prerender, ssrFunctions;
18
+ if (options.appDir) {
19
+ if (options.appDir.slice(-1) !== '/')
20
+ options.appDir += '/';
21
+ appDir = new URL(`file://${options.appDir}`);
22
+ }
23
+ else {
24
+ appDir = getAppDir();
25
+ }
26
+ const baseOutDir = parsePath(options.outDir, appDir) || new URL('dist/', appDir);
27
+ const args = {
28
+ base: options.base,
29
+ appDir,
30
+ publicDir: parsePath(options.publicDir, appDir)
31
+ };
32
+ switch (options.mode) {
33
+ case 'csr':
34
+ await build({
35
+ ...args,
36
+ outDir: new URL('spa/', baseOutDir).pathname
37
+ });
38
+ break;
39
+ case 'ssr':
40
+ await build({
41
+ ssr: 'client',
42
+ ...args,
43
+ outDir: new URL('ssr/client/', baseOutDir).pathname
44
+ });
45
+ await build({
46
+ ssr: 'server',
47
+ ...args,
48
+ outDir: new URL('ssr/server/', baseOutDir).pathname
49
+ });
50
+ break;
51
+ case 'ssg':
52
+ await build({
53
+ ssr: 'client',
54
+ ...args,
55
+ outDir: new URL('static/', baseOutDir).pathname
56
+ });
57
+ await build({
58
+ ssr: 'server',
59
+ ...args,
60
+ outDir: new URL('ssr/server/', baseOutDir).pathname
61
+ });
62
+ ({ prerender, ssrFunctions } = await import(new URL('ssr/server/prerender.mjs', baseOutDir).pathname));
63
+ prerender({
64
+ outDir: new URL('static/', baseOutDir).pathname,
65
+ templatePath: new URL('static/index.html', baseOutDir).pathname,
66
+ manifestPath: new URL('static/ssr-manifest.json', baseOutDir)
67
+ .pathname,
68
+ entryServerPath: new URL('ssr/server/entry-server.mjs', baseOutDir)
69
+ .pathname,
70
+ ssrFunctions
71
+ });
72
+ break;
73
+ default:
74
+ console.log('Invalid build mode');
75
+ break;
76
+ }
77
+ });
78
+ cli
79
+ .command('dev')
80
+ .option('-m, --mode [mode]', 'Development server mode', { default: 'csr' })
81
+ .option('--host [host]', 'Specify which IP addresses the server should listen on', { default: '127.0.0.1' })
82
+ .option('--appDir [appDir]', 'Application directory')
83
+ .option('--publicDir [publicDir]', 'Public directory')
84
+ .action(async (options) => {
85
+ let server;
86
+ let vite;
87
+ if (options.host === true) {
88
+ options.host = '0.0.0.0';
89
+ }
90
+ const { createServer } = await import('./dev.js');
91
+ const cwd = (await import('../app-urls.js')).getCwd();
92
+ switch (options.mode) {
93
+ case 'ssr':
94
+ ;
95
+ ({ server, vite } = await createServer({
96
+ mode: 'ssr',
97
+ host: options.host,
98
+ appDir: parsePath(options.appDir, cwd),
99
+ publicDir: parsePath(options.publicDir, cwd)
100
+ }));
101
+ break;
102
+ default:
103
+ ;
104
+ ({ server, vite } = await createServer({
105
+ host: options.host,
106
+ appDir: parsePath(options.appDir, cwd),
107
+ publicDir: parsePath(options.publicDir, cwd)
108
+ }));
109
+ break;
110
+ }
111
+ console.log('Dev server running at:');
112
+ printHttpServerUrls(server, vite.config);
113
+ });
114
+ cli.command('test').action(async (options) => {
115
+ const { test } = await import('./test.js');
116
+ let appDir;
117
+ if (options.appDir) {
118
+ if (options.appDir.slice(-1) !== '/')
119
+ options.appDir += '/';
120
+ appDir = new URL(`file://${options.appDir}`);
121
+ }
122
+ else {
123
+ appDir = getAppDir();
124
+ }
125
+ await test({
126
+ appDir
127
+ });
128
+ });
129
+ cli.command('run <file>').action(async (file, options) => {
130
+ const { run } = await import('./run.js');
131
+ const filePath = new URL(file, `file://${process.cwd()}/`);
132
+ await run(filePath.pathname);
133
+ });
134
+ // Default
135
+ cli.command('').action((command, options) => {
136
+ cli.outputHelp();
137
+ });
138
+ cli.help();
139
+ cli.parse();
@@ -0,0 +1,108 @@
1
+ import { searchForWorkspaceRoot } from 'vite';
2
+ import { baseConfig } from '../index.js';
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();
8
+ const cliDir = getCliDir();
9
+ if (!appDir)
10
+ appDir = getAppDir();
11
+ const { fastifySsrPlugin } = await import(`../${framework}/fastify-ssr-plugin.js`);
12
+ /**
13
+ * @type {import('vite').ViteDevServer}
14
+ */
15
+ const config = await baseConfig({
16
+ ssr: mode === 'ssr' ? 'server' : undefined,
17
+ command: 'dev',
18
+ mode: 'development',
19
+ appDir,
20
+ publicDir
21
+ });
22
+ config.logLevel = logLevel;
23
+ config.server = {
24
+ port,
25
+ middlewareMode: mode === 'ssr' ? 'ssr' : undefined,
26
+ fs: {
27
+ allow: [
28
+ searchForWorkspaceRoot(process.cwd()),
29
+ searchForWorkspaceRoot(appDir.pathname),
30
+ searchForWorkspaceRoot(cliDir.pathname)
31
+ // appDir.pathname,
32
+ ]
33
+ },
34
+ watch: {
35
+ // During tests we edit the files too fast and sometimes chokidar
36
+ // misses change events, so enforce polling for consistency
37
+ usePolling: true,
38
+ interval: 100
39
+ },
40
+ host
41
+ };
42
+ const vite = await (await import('vite')).createServer({
43
+ configFile: false,
44
+ ...config
45
+ });
46
+ const { productName = 'Product name' } = JSON.parse(readFileSync(new URL('package.json', appDir).pathname, {
47
+ encoding: 'utf-8'
48
+ }));
49
+ let app;
50
+ 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
59
+ });
60
+ // await app.register(middie)
61
+ // app.use(vite.middlewares)
62
+ // app.get('*', async (req, res) => {
63
+ // try {
64
+ // // const url = req.originalUrl
65
+ // const url = req.raw.url
66
+ // let template
67
+ // let render
68
+ // const ssrContext = {
69
+ // req,
70
+ // res
71
+ // }
72
+ // // always read fresh template in dev
73
+ // // template = readFileSync(resolve('index.html'), 'utf-8')
74
+ // template = readFileSync(new URL('index.html', cliDir)).toString()
75
+ // // template = await vite.transformIndexHtml(url, template)
76
+ // const entryUrl = new URL('ssr/entry-server.ts', cliDir).pathname
77
+ // render = (await vite.ssrLoadModule(entryUrl)).render
78
+ // let manifest
79
+ // // TODO: https://github.com/vitejs/vite/issues/2282
80
+ // try {
81
+ // manifest = {}
82
+ // } catch (e) {
83
+ // manifest = {}
84
+ // }
85
+ // const [appHtml, preloadLinks] = await render(url, manifest, ssrContext)
86
+ // const html = template
87
+ // .replace(`<!--preload-links-->`, preloadLinks)
88
+ // .replace(`<!--app-html-->`, appHtml)
89
+ // .replace('<!--product-name-->', productName)
90
+ // res.code(200)
91
+ // res.type('text/html')
92
+ // res.send(html)
93
+ // // res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
94
+ // } catch (e: any) {
95
+ // console.error(e.stack)
96
+ // vite && vite.ssrFixStacktrace(e)
97
+ // res.code(500)
98
+ // res.send(e.stack)
99
+ // }
100
+ // })
101
+ await app.listen(port || 3000, host);
102
+ server = app.server;
103
+ }
104
+ else {
105
+ server = (await vite.listen()).httpServer;
106
+ }
107
+ return { server, vite };
108
+ }
@@ -0,0 +1,29 @@
1
+ import { promises as fs } from 'fs';
2
+ import readline from 'readline';
3
+ import { getAppDir, getCliDir, getProjectURLs } from '../app-urls.js';
4
+ const rl = readline.createInterface({
5
+ input: process.stdin,
6
+ output: process.stdout
7
+ });
8
+ export async function run(filePath) {
9
+ const { run } = await import(filePath);
10
+ const appDir = getAppDir();
11
+ const cliDir = getCliDir();
12
+ const projectURLs = getProjectURLs(appDir, cliDir);
13
+ const pkg = JSON.parse((await fs.readFile(projectURLs.cli('package.json'), 'utf-8')).toString());
14
+ if (!run)
15
+ throw new Error(`${filePath} does not have an export named run. Aborting...`);
16
+ rl.question(`
17
+ The script ${filePath}
18
+ will now be executed by vitrify.
19
+ Make sure you trust the content of this script before proceeding.
20
+ Press enter to proceed or CTRL+C to abort.`, async (answer) => {
21
+ await run({
22
+ vitrify: {
23
+ version: pkg.version
24
+ },
25
+ resolve: projectURLs
26
+ });
27
+ rl.close();
28
+ });
29
+ }
@@ -0,0 +1,91 @@
1
+ import fastifyStatic from 'fastify-static';
2
+ import { readFileSync } from 'fs';
3
+ 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;
8
+ await fastify.register(middie);
9
+ fastify.use(options.vite.middlewares);
10
+ fastify.get('*', async (req, res) => {
11
+ try {
12
+ // const url = req.originalUrl
13
+ const url = req.raw.url;
14
+ const ssrContext = {
15
+ req,
16
+ res
17
+ };
18
+ // always read fresh template in dev
19
+ // template = readFileSync(resolve('index.html'), 'utf-8')
20
+ const template = readFileSync(new URL('index.html', options.cliDir)).toString();
21
+ // template = await vite.transformIndexHtml(url, template)
22
+ const entryUrl = new URL('ssr/entry-server.ts', options.cliDir).pathname;
23
+ const render = (await options.vite.ssrLoadModule(entryUrl)).render;
24
+ let manifest;
25
+ // TODO: https://github.com/vitejs/vite/issues/2282
26
+ try {
27
+ manifest = {};
28
+ }
29
+ catch (e) {
30
+ manifest = {};
31
+ }
32
+ const [appHtml, preloadLinks] = await render(url, manifest, ssrContext);
33
+ const html = template
34
+ .replace(`<!--preload-links-->`, preloadLinks)
35
+ .replace(`<!--app-html-->`, appHtml)
36
+ .replace('<!--product-name-->', options.productName || 'Product name');
37
+ res.code(200);
38
+ res.type('text/html');
39
+ res.send(html);
40
+ // res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
41
+ }
42
+ catch (e) {
43
+ console.error(e.stack);
44
+ options.vite && options.vite.ssrFixStacktrace(e);
45
+ res.code(500);
46
+ res.send(e.stack);
47
+ }
48
+ });
49
+ }
50
+ else {
51
+ options.baseUrl = options.baseUrl || '/';
52
+ fastify.register(fastifyStatic, {
53
+ root: new URL('./dist/ssr/client', options.appDir).pathname,
54
+ wildcard: false,
55
+ index: false,
56
+ prefix: options.baseUrl
57
+ });
58
+ fastify.get(`${options.baseUrl}*`, async (req, res) => {
59
+ const url = req.raw.url;
60
+ const provide = options.provide ? await options.provide(req, res) : {};
61
+ const ssrContext = {
62
+ req,
63
+ res,
64
+ provide
65
+ };
66
+ // template = readFileSync(new URL('../client/index.html', import.meta.url).pathname).toString()
67
+ // manifest = JSON.parse(readFileSync(new URL('../client/ssr-manifest.json', import.meta.url)).toString())
68
+ // render = (await import(new URL('./entry-server.mjs', import.meta.url).pathname)).render
69
+ const template = readFileSync(new URL('./dist/ssr/client/index.html', options.appDir).pathname).toString();
70
+ const manifest = JSON.parse(readFileSync(new URL('./dist/ssr/client/ssr-manifest.json', options.appDir)).toString());
71
+ const render = (await import(new URL('./dist/ssr/server/entry-server.mjs', options.appDir).pathname)).render;
72
+ const [appHtml, preloadLinks] = await render(url, manifest, ssrContext);
73
+ if (!ssrContext.initialState)
74
+ ssrContext.initialState = {};
75
+ ssrContext.initialState.provide = provide;
76
+ let html = template
77
+ .replace(`<!--preload-links-->`, preloadLinks)
78
+ .replace(`<!--app-html-->`, appHtml);
79
+ if (options.ssrFunctions?.length) {
80
+ for (const ssrFunction of options.ssrFunctions) {
81
+ html = ssrFunction(html, ssrContext);
82
+ }
83
+ }
84
+ res.code(200);
85
+ res.type('text/html');
86
+ res.send(html);
87
+ });
88
+ }
89
+ done();
90
+ };
91
+ export { fastifySsrPlugin };
@@ -0,0 +1,29 @@
1
+ import { promises as fs } from 'fs';
2
+ import { routesToPaths } from '../../helpers/routes.js';
3
+ export const prerender = async ({ outDir, templatePath, manifestPath, entryServerPath, ssrFunctions }) => {
4
+ const promises = [];
5
+ const template = (await fs.readFile(templatePath)).toString();
6
+ const manifest = await fs.readFile(manifestPath);
7
+ const { render, getRoutes } = await import(entryServerPath);
8
+ const routes = await getRoutes();
9
+ const paths = routesToPaths(routes).filter((i) => !i.includes(':') && !i.includes('*'));
10
+ for (const url of paths) {
11
+ const filename = (url.endsWith('/') ? 'index' : url.replace(/^\//g, '')) + '.html';
12
+ console.log(`Generating ${filename}`);
13
+ const ssrContext = {
14
+ req: { headers: {}, url },
15
+ res: {}
16
+ };
17
+ const [appHtml, preloadLinks] = await render(url, manifest, ssrContext);
18
+ let html = template
19
+ .replace(`<!--preload-links-->`, preloadLinks)
20
+ .replace(`<!--app-html-->`, appHtml);
21
+ if (ssrFunctions?.length) {
22
+ for (const ssrFunction of ssrFunctions) {
23
+ html = ssrFunction(html, ssrContext);
24
+ }
25
+ }
26
+ promises.push(fs.writeFile(outDir + filename, html, 'utf-8'));
27
+ }
28
+ return Promise.all(promises);
29
+ };
@@ -0,0 +1,20 @@
1
+ import fastify from 'fastify';
2
+ // import { setup } from 'virtual:fastify-setup'
3
+ import { fastifySsrPlugin } from './fastify-ssr-plugin.js';
4
+ // import { getPkgJsonDir } from '../app-urls.js'
5
+ export const createApp = ({ setup, appDir, baseUrl, ssrFunctions }) => {
6
+ const app = fastify({
7
+ logger: true
8
+ });
9
+ app.register(fastifySsrPlugin, {
10
+ baseUrl,
11
+ appDir,
12
+ ssrFunctions
13
+ });
14
+ setup(app);
15
+ return app;
16
+ };
17
+ // const app = createApp({
18
+ // setup
19
+ // })
20
+ // app.listen(process.env.PORT || 3000, process.env.HOST || '127.0.0.1')