import { sveltekit } from '@sveltejs/kit/vite'; import { defineConfig } from 'vite'; import { execSync } from 'child_process'; import { SvelteKitPWA } from '@vite-pwa/sveltekit'; import compression from 'vite-plugin-compression'; export default defineConfig({ plugins: [ sveltekit(), compression({ algorithm: 'gzip', ext: '.gz', threshold: 1024, // Compress files > 1KB }), compression({ algorithm: 'brotliCompress', ext: '.br', threshold: 1024, }), SvelteKitPWA({ strategies: 'generateSW', registerType: 'autoUpdate', workbox: { globPatterns: ['**/*.{js,css,html,ico,png,svg,woff,woff2,webp,avif}'], runtimeCaching: [ { urlPattern: /^https:\/\/.*\.(?:png|jpg|jpeg|svg|gif|webp|avif)$/i, handler: 'CacheFirst', options: { cacheName: 'images-cache', expiration: { maxEntries: 200, maxAgeSeconds: 60 * 60 * 24 * 30 // 30 days } } }, { // Cache API responses (relay responses) with cache-first strategy for slow connections urlPattern: /^wss?:\/\//i, handler: 'CacheFirst', options: { cacheName: 'api-cache', expiration: { maxEntries: 200, // Increased from 50 maxAgeSeconds: 60 * 60 * 24 // 24 hours instead of 5 minutes (optimized for slow connections) } } }, { // Cache static assets with stale-while-revalidate for better performance urlPattern: /\.(?:js|css|woff|woff2)$/i, handler: 'StaleWhileRevalidate', options: { cacheName: 'static-assets', expiration: { maxEntries: 100, maxAgeSeconds: 60 * 60 * 24 * 7 // 7 days } } }, { // Cache HTML pages with network-first for freshness urlPattern: /\.html$/i, handler: 'NetworkFirst', options: { cacheName: 'html-cache', expiration: { maxEntries: 20, maxAgeSeconds: 60 * 60 * 24 // 1 day }, networkTimeoutSeconds: 2 } } ] }, manifest: { name: 'aitherboard - Decentralized Messageboard on Nostr', short_name: 'aitherboard', description: 'A decentralized messageboard built on the Nostr protocol. Create threads, comment, react, and zap in a censorship-resistant environment.', theme_color: '#f1f5f9', background_color: '#ffffff', display: 'standalone', icons: [ { src: 'favicon.ico', sizes: '64x64', type: 'image/x-icon' }, { src: 'apple-touch-icon-180x180.png', sizes: '180x180', type: 'image/png', purpose: 'any maskable' }, { src: 'apple-touch-icon-152x152.png', sizes: '152x152', type: 'image/png' }, { src: 'apple-touch-icon-144x144.png', sizes: '144x144', type: 'image/png' }, { src: 'apple-touch-icon-120x120.png', sizes: '120x120', type: 'image/png' }, { src: 'apple-touch-icon-114x114.png', sizes: '114x114', type: 'image/png' } ] }, devOptions: { enabled: false } }), { name: 'generate-healthz', buildStart() { try { execSync('node scripts/generate-healthz.js', { stdio: 'inherit' }); } catch (error) { console.warn('Failed to generate healthz.json:', error); } } } ], server: { port: 5173, strictPort: false }, build: { target: 'esnext', sourcemap: false, manifest: false, minify: 'terser', terserOptions: { compress: { drop_console: ['log', 'debug'], // Remove console.log and console.debug in production, keep error/warn passes: 2, // Multiple passes for better compression } }, rollupOptions: { onwarn(warning, warn) { // Suppress warning about highlight.js default import - it's used in reactive contexts that Vite can't detect if (warning.message && typeof warning.message === 'string' && warning.message.includes('highlight.js') && warning.message.includes('never used')) { return; } warn(warning); } } } });