File size: 3,802 Bytes
4d35814
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import tailwindcss from '@tailwindcss/vite';
import { sveltekit } from '@sveltejs/kit/vite';
import * as fflate from 'fflate';
import { readFileSync, writeFileSync, existsSync } from 'fs';
import { resolve } from 'path';
import { defineConfig } from 'vite';
import devtoolsJson from 'vite-plugin-devtools-json';
import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';

const GUIDE_FOR_FRONTEND = `
<!--
  This is a single file build of the frontend.
  It is automatically generated by the build process.
  Do not edit this file directly.
  To make changes, refer to the "Web UI" section in the README.
-->
`.trim();

const MAX_BUNDLE_SIZE = 2 * 1024 * 1024;

function llamaCppBuildPlugin() {
	return {
		name: 'llamacpp:build',
		apply: 'build' as const,
		closeBundle() {
			// Ensure the SvelteKit adapter has finished writing to ../public
			setTimeout(() => {
				try {
					const indexPath = resolve('../public/index.html');
					const gzipPath = resolve('../public/index.html.gz');

					if (!existsSync(indexPath)) {
						return;
					}

					let content = readFileSync(indexPath, 'utf-8');

					const faviconPath = resolve('static/favicon.svg');
					if (existsSync(faviconPath)) {
						const faviconContent = readFileSync(faviconPath, 'utf-8');
						const faviconBase64 = Buffer.from(faviconContent).toString('base64');
						const faviconDataUrl = `data:image/svg+xml;base64,${faviconBase64}`;

						content = content.replace(/href="[^"]*favicon\.svg"/g, `href="${faviconDataUrl}"`);

						console.log('✓ Inlined favicon.svg as base64 data URL');
					}

					content = content.replace(/\r/g, '');
					content = GUIDE_FOR_FRONTEND + '\n' + content;

					const compressed = fflate.gzipSync(Buffer.from(content, 'utf-8'), { level: 9 });

					compressed[0x4] = 0;
					compressed[0x5] = 0;
					compressed[0x6] = 0;
					compressed[0x7] = 0;
					compressed[0x9] = 0;

					if (compressed.byteLength > MAX_BUNDLE_SIZE) {
						throw new Error(
							`Bundle size is too large (${Math.ceil(compressed.byteLength / 1024)} KB).\n` +
								`Please reduce the size of the frontend or increase MAX_BUNDLE_SIZE in vite.config.ts.\n`
						);
					}

					writeFileSync(gzipPath, compressed);
					console.log('✓ Created index.html.gz');
				} catch (error) {
					console.error('Failed to create gzip file:', error);
				}
			}, 100);
		}
	};
}

export default defineConfig({
	plugins: [tailwindcss(), sveltekit(), devtoolsJson(), llamaCppBuildPlugin()],
	test: {
		projects: [
			{
				extends: './vite.config.ts',
				test: {
					name: 'client',
					environment: 'browser',
					browser: {
						enabled: true,
						provider: 'playwright',
						instances: [{ browser: 'chromium' }]
					},
					include: ['src/**/*.svelte.{test,spec}.{js,ts}'],
					exclude: ['src/lib/server/**'],
					setupFiles: ['./vitest-setup-client.ts']
				}
			},
			{
				extends: './vite.config.ts',
				test: {
					name: 'server',
					environment: 'node',
					include: ['src/**/*.{test,spec}.{js,ts}'],
					exclude: ['src/**/*.svelte.{test,spec}.{js,ts}']
				}
			},
			{
				extends: './vite.config.ts',
				test: {
					name: 'ui',
					environment: 'browser',
					browser: {
						enabled: true,
						provider: 'playwright',
						instances: [{ browser: 'chromium', headless: true }]
					},
					include: ['src/**/*.stories.{js,ts,svelte}'],
					setupFiles: ['./.storybook/vitest.setup.ts']
				},
				plugins: [
					storybookTest({
						storybookScript: 'pnpm run storybook --no-open'
					})
				]
			}
		]
	},
	server: {
		proxy: {
			'/v1': 'http://localhost:8080',
			'/props': 'http://localhost:8080',
			'/slots': 'http://localhost:8080'
		},
		headers: {
			'Cross-Origin-Embedder-Policy': 'require-corp',
			'Cross-Origin-Opener-Policy': 'same-origin'
		}
	}
});