| | 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() { |
| | |
| | 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' |
| | } |
| | } |
| | }); |
| |
|