import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import { VitePWA } from 'vite-plugin-pwa'; import { resolve } from 'path'; export default defineConfig({ plugins: [ react(), VitePWA({ registerType: 'autoUpdate', includeAssets: ['favicon.svg', 'icon-192.svg', 'icon-512.svg', 'print.css'], manifest: { name: 'LeoCRM', short_name: 'LeoCRM', description: 'Self-hosted CRM system for small sales teams', theme_color: '#3b82f6', background_color: '#0f172a', display: 'standalone', start_url: '/', icons: [ { src: '/icon-192.svg', sizes: '192x192', type: 'image/svg+xml', purpose: 'any maskable', }, { src: '/icon-512.svg', sizes: '512x512', type: 'image/svg+xml', purpose: 'any maskable', }, ], }, workboxConfig: { globPatterns: ['**/*.{js,css,html,svg,png,ico,woff2}'], maximumFileSizeToCacheInBytes: 5 * 1024 * 1024, runtimeCaching: [ { urlPattern: /^https:\/\/fonts\.googleapis\.com\/.*/i, handler: 'CacheFirst', options: { cacheName: 'google-fonts-cache', expiration: { maxEntries: 10, maxAgeSeconds: 60 * 60 * 24 * 365, }, }, }, { urlPattern: /\/_app\/api\//i, handler: 'NetworkOnly', }, ], }, devOptions: { enabled: false, }, }), ], resolve: { alias: { '@': resolve(__dirname, 'src'), }, }, server: { port: 5173, proxy: { '/api': { target: 'http://localhost:8000', changeOrigin: true, }, }, }, build: { rollupOptions: { output: { manualChunks(id) { if (id.includes('node_modules')) { if (id.includes('react-router')) return 'react-vendor'; if (id.includes('react-dom')) return 'react-vendor'; if (id.includes('/react/') || id.includes('\\react\\')) return 'react-vendor'; if (id.includes('@tanstack')) return 'tanstack'; if (id.includes('lucide-react')) return 'icons'; if (id.includes('react-markdown') || id.includes('remark-gfm')) return 'markdown'; if (id.includes('date-fns') || id.includes('clsx') || id.includes('class-variance-authority') || id.includes('tailwind-merge')) return 'utils'; if (id.includes('zustand') || id.includes('immer')) return 'react-vendor'; if (id.includes('i18next') || id.includes('react-i18next')) return 'i18n'; } // Split @/components/ui into its own chunk if (id.includes('/src/components/ui/')) { return 'ui-components'; } }, }, }, }, test: { globals: true, environment: 'jsdom', setupFiles: 'src/test/setup.ts', css: true, coverage: { provider: 'v8', reporter: ['text', 'json-summary'], include: ['src/**/*.{ts,tsx}'], exclude: ['src/test/**', 'src/**/*.d.ts', 'src/main.tsx', 'e2e/**'], }, }, });