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