import { readFileSync, writeFileSync, existsSync, readdirSync, copyFileSync, rmSync, unlinkSync } from 'fs'; import { resolve } from 'path'; import type { Plugin } from 'vite'; const GUIDE_FOR_FRONTEND = ` `.trim(); const OUTPUT_DIR = '../../build/tools/ui/dist'; export function llamaCppBuildPlugin(): Plugin { return { name: 'llamacpp:build', apply: 'build', closeBundle() { setTimeout(() => { try { const outDir = resolve(OUTPUT_DIR); const indexPath = resolve(outDir, 'index.html'); if (!existsSync(indexPath)) return; let content = readFileSync(indexPath, 'utf-8'); // Inline favicon as base64 data URL 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' ); content = content.replace(/__sveltekit_[a-z0-9]+/g, '__sveltekit__'); writeFileSync(indexPath, content, 'utf-8'); console.log('✓ Updated index.html'); // Copy bundle.*.js -> bundle.js at output root const immutableDir = resolve(outDir, '_app/immutable'); const bundleDir = resolve(outDir, '_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(outDir, 'bundle.js')); // Normalize __sveltekit_ to __sveltekit__ in bundle.js const bundleJsPath = resolve(outDir, 'bundle.js'); let bundleJs = readFileSync(bundleJsPath, 'utf-8'); bundleJs = bundleJs.replace(/__sveltekit_[a-z0-9]+/g, '__sveltekit__'); writeFileSync(bundleJsPath, bundleJs, 'utf-8'); console.log(`✓ Copied ${jsFiles[0]} -> bundle.js`); } } // Copy bundle.*.css -> bundle.css at output root if (existsSync(bundleDir)) { const cssFiles = readdirSync(bundleDir).filter((f) => f.match(/^bundle\..+\.css$/)); if (cssFiles.length > 0) { copyFileSync(resolve(bundleDir, cssFiles[0]), resolve(outDir, 'bundle.css')); console.log(`✓ Copied ${cssFiles[0]} -> bundle.css`); } } // Cleanup: remove _app directory, favicon.svg, and legacy index.html.gz const appDir = resolve(outDir, '_app'); if (existsSync(appDir)) { rmSync(appDir, { recursive: true, force: true }); console.log('✓ Removed _app directory'); } const faviconOut = resolve(outDir, 'favicon.svg'); if (existsSync(faviconOut)) { unlinkSync(faviconOut); console.log('✓ Removed favicon.svg'); } } catch (error) { console.error('Failed to process build output:', error); } }, 100); } }; }