import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import svgr from "vite-plugin-svgr"; import { resolve } from "path"; // https://vite.dev/config/ export default defineConfig(({ mode }) => { const isDev = mode === "development"; const isMarketingBuild = mode === "marketing"; return { plugins: [ react(), svgr({ svgrOptions: { icon: true, // This will transform your SVG to a React component exportType: "named", namedExport: "ReactComponent", }, }), ], // Optimize dependency pre-bundling optimizeDeps: { include: [ // Only pre-bundle frequently used, small dependencies "clsx", "tailwind-merge", "zustand", // Include apexcharts for proper module resolution (skip during marketing-only build) ...(isMarketingBuild ? [] : ["apexcharts", "react-apexcharts"]), ], // Exclude heavy dependencies that are only used in specific pages // They will be lazy-loaded when needed exclude: [ "@fullcalendar/core", "@fullcalendar/daygrid", "@fullcalendar/interaction", "@fullcalendar/list", "@fullcalendar/react", "@fullcalendar/timegrid", "@react-jvectormap/core", "@react-jvectormap/world", "react-dnd", "react-dnd-html5-backend", "swiper", ], esbuildOptions: { target: 'es2020', }, }, // Build configuration for code splitting build: { // Enable minification (esbuild is faster than terser) minify: 'esbuild', // Enable CSS code splitting cssCodeSplit: true, // Optimize chunk size rollupOptions: { input: isMarketingBuild ? { marketing: resolve(__dirname, "marketing.html"), } : { main: resolve(__dirname, "index.html"), marketing: resolve(__dirname, "marketing.html"), }, output: { // Manual chunk splitting for better code splitting manualChunks: (id) => { // Vendor chunks - separate large dependencies for better caching if (id.includes('node_modules')) { // React core (most critical, always needed) if (id.includes('react/') || id.includes('react-dom/')) { return 'vendor-react-core'; } // React Router (separate chunk - only loads on navigation) if (id.includes('react-router')) { return 'vendor-react-router'; } // Heavy chart libraries (only used in specific pages) if (id.includes('apexcharts') || id.includes('react-apexcharts')) { return 'vendor-charts'; } // Calendar libraries (only used in Calendar page) if (id.includes('@fullcalendar')) { return 'vendor-calendar'; } // Map libraries (only used in specific pages) if (id.includes('@react-jvectormap')) { return 'vendor-maps'; } // Drag and drop (only used in specific pages) if (id.includes('react-dnd')) { return 'vendor-dnd'; } // Swiper (only used in specific pages) if (id.includes('swiper')) { return 'vendor-swiper'; } // UI libraries (Radix UI, etc.) if (id.includes('@radix-ui') || id.includes('framer-motion')) { return 'vendor-ui'; } // Zustand (state management - used everywhere) if (id.includes('zustand')) { return 'vendor-state'; } // React Helmet (SEO) if (id.includes('react-helmet')) { return 'vendor-helmet'; } // Other vendors (smaller libraries) return 'vendor-other'; } // Page chunks - each page gets its own chunk if (id.includes('/pages/')) { const match = id.match(/\/pages\/([^/]+)/); if (match) { const pageName = match[1]; // Group by module for better caching if (pageName === 'Planner' || id.includes('/Planner/')) { return 'pages-planner'; } if (pageName === 'Writer' || id.includes('/Writer/')) { return 'pages-writer'; } if (pageName === 'Thinker' || id.includes('/Thinker/')) { return 'pages-thinker'; } if (pageName === 'Settings' || id.includes('/Settings/')) { return 'pages-settings'; } if (pageName === 'Billing' || id.includes('/Billing/')) { return 'pages-billing'; } // Individual page chunks for others return `page-${pageName.toLowerCase()}`; } } if (id.includes('/marketing/')) { const match = id.match(/\/marketing\/([^/]+)/); if (match) { const sectionName = match[1]; return `marketing-${sectionName.toLowerCase()}`; } } // Split icons into separate chunk (lazy load) if (id.includes('/icons/') && id.endsWith('.svg')) { return 'icons'; } }, // Optimize chunk file names chunkFileNames: 'assets/js/[name]-[hash].js', entryFileNames: 'assets/js/[name]-[hash].js', assetFileNames: 'assets/[ext]/[name]-[hash].[ext]', }, }, // Increase chunk size warning limit (default is 500kb) chunkSizeWarningLimit: 600, // Enable source maps in dev only sourcemap: isDev, // Report compressed sizes reportCompressedSize: true, }, // Only configure server and HMR in development mode ...(isDev && { server: { host: "0.0.0.0", // Allow external connections (for Docker) port: 5173, strictPort: false, // Allow port fallback if 5173 is busy allowedHosts: [ "app.igny8.com", "localhost", "127.0.0.1", ], watch: { usePolling: true, // Needed for file watching in Docker }, hmr: { // Behind reverse proxy - use same origin strategy // Client connects via public domain (app.igny8.com) on default HTTPS port (443) // Caddy automatically proxies WebSocket upgrades from 443 to 5173 protocol: "wss", // Don't specify host/port - Vite will use same origin as page // This ensures client connects to wss://app.igny8.com (port 443 implicit) }, // Increase timeout for slow connections and dependency pre-bundling timeout: 120000, // 120 seconds (2 minutes) to match Caddy timeout }, }), }; });