import tailwindcss from '@tailwindcss/vite'; import { sveltekit } from '@sveltejs/kit/vite'; import { readFileSync, writeFileSync, existsSync, readdirSync, copyFileSync } from 'fs'; import { dirname, resolve } from 'path'; import { fileURLToPath } from 'url'; import { defineConfig, searchForWorkspaceRoot } from 'vite'; import devtoolsJson from 'vite-plugin-devtools-json'; import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'; const __dirname = dirname(fileURLToPath(import.meta.url)); const GUIDE_FOR_FRONTEND = ` `.trim(); /** * the maximum size of an embedded asset in bytes, * e.g. maximum size of embedded font (see node_modules/katex/dist/fonts/*.woff2) */ const MAX_ASSET_SIZE = 32000; /** public/index.html minified flag */ const ENABLE_JS_MINIFICATION = true; 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'); 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; content = content.replace(/\/_app\/immutable\/bundle\.[^"]+\.js/g, './bundle.js'); content = content.replace( /\/_app\/immutable\/assets\/bundle\.[^"]+\.css/g, './bundle.css' ); writeFileSync(indexPath, content, 'utf-8'); console.log('✓ Updated index.html'); // Copy bundle.*.js -> ../public/bundle.js const immutableDir = resolve('../public/_app/immutable'); const bundleDir = resolve('../public/_app/immutable/assets'); if (existsSync(immutableDir)) { const jsFiles = readdirSync(immutableDir).filter((f) => f.match(/^bundle\..+\.js$/)); if (jsFiles.length > 0) { copyFileSync(resolve(immutableDir, jsFiles[0]), resolve('../public/bundle.js')); console.log(`✓ Copied ${jsFiles[0]} -> bundle.js`); } } // Copy bundle.*.css -> ../public/bundle.css if (existsSync(bundleDir)) { const cssFiles = readdirSync(bundleDir).filter((f) => f.match(/^bundle\..+\.css$/)); if (cssFiles.length > 0) { copyFileSync(resolve(bundleDir, cssFiles[0]), resolve('../public/bundle.css')); console.log(`✓ Copied ${cssFiles[0]} -> bundle.css`); } } } catch (error) { console.error('Failed to update index.html:', error); } }, 100); } }; } export default defineConfig({ resolve: { alias: { 'katex-fonts': resolve('node_modules/katex/dist/fonts') } }, build: { assetsInlineLimit: MAX_ASSET_SIZE, chunkSizeWarningLimit: 3072, minify: ENABLE_JS_MINIFICATION }, css: { preprocessorOptions: { scss: { additionalData: ` $use-woff2: true; $use-woff: false; $use-ttf: false; ` } } }, 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: ['tests/client/**/*.svelte.{test,spec}.{js,ts}'], setupFiles: ['./vitest-setup-client.ts'] } }, { extends: './vite.config.ts', test: { name: 'unit', environment: 'node', include: ['tests/unit/**/*.{test,spec}.{js,ts}'] } }, { extends: './vite.config.ts', test: { name: 'ui', environment: 'browser', browser: { enabled: true, provider: 'playwright', instances: [{ browser: 'chromium', headless: true }] }, include: ['tests/stories/**/*.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', '/models': 'http://localhost:8080', '/cors-proxy': 'http://localhost:8080' }, headers: { 'Cross-Origin-Embedder-Policy': 'require-corp', 'Cross-Origin-Opener-Policy': 'same-origin' }, fs: { allow: [searchForWorkspaceRoot(process.cwd()), resolve(__dirname, 'tests')] } } });