/// /// import { defineConfig, loadEnv, UserConfig, ConfigEnv } from 'vite'; import react from '@vitejs/plugin-react'; import process from 'node:process'; import { VitePWA } from 'vite-plugin-pwa'; export default defineConfig(async ({ mode }: ConfigEnv): Promise => { // Load env file based on mode in correct order const env = loadEnv(mode, process.cwd(), 'VITE_'); // Determine base URL from hostname const base = '/'; // Always use root path, let nginx handle the routing // Determine if we're in production based on mode and VITE_DEV flag const isProd = mode === 'production' && env.VITE_DEV !== 'true'; return { plugins: [ react(), VitePWA({ strategies: 'injectManifest', srcDir: 'src', filename: 'sw.ts', registerType: 'prompt', injectRegister: 'auto', devOptions: { enabled: false, type: 'module' }, manifest: { name: 'ClassroomCopilot', short_name: 'CC', start_url: base, scope: base, display: 'fullscreen', background_color: '#ffffff', theme_color: '#000000', icons: [ { src: '/icons/icon-192x192.png', sizes: '192x192', type: 'image/png', purpose: 'any' }, { src: '/icons/icon-512x512.png', sizes: '512x512', type: 'image/png', purpose: 'any' }, { src: '/icons/icon-192x192-maskable.png', sizes: '192x192', type: 'image/png', purpose: 'maskable' }, { src: '/icons/icon-512x512-maskable.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' } ] }, includeAssets: ['favicon.ico', 'icons/*.png'], injectManifest: { globPatterns: [ 'index.html', '**/*.{js,css,html,ico,png,svg,json}', 'manifest.webmanifest' ], maximumFileSizeToCacheInBytes: 8 * 1024 * 1024, // 8MB dontCacheBustURLsMatching: /\.[0-9a-f]{8}\./, // Exclude development resources and source files globIgnores: [ '**/node_modules/**/*', 'sw.js', 'workbox-*.js', '**/*.map', '**/vite/**/*', '**/@vite/**/*', '**/@react-refresh/**/*' ], // Ensure service worker has correct scope swDest: 'dist/sw.js', manifestTransforms: [ // Transform manifest entries to ensure proper caching (entries) => ({ manifest: entries.map(entry => ({ ...entry, url: entry.url.startsWith(base) ? entry.url : `${base}${entry.url.startsWith('/') ? entry.url.slice(1) : entry.url}` })) }) ] } }) ], define: { // Make env variables available globally 'process.env': env, // Ensure import.meta.env has our variables 'import.meta.env.VITE_FRONTEND_SITE_URL': JSON.stringify(env.VITE_FRONTEND_SITE_URL), 'import.meta.env.VITE_SUPABASE_URL': JSON.stringify(env.VITE_SUPABASE_URL), 'import.meta.env.VITE_SUPABASE_ANON_KEY': JSON.stringify(env.VITE_SUPABASE_ANON_KEY), 'import.meta.env.VITE_SUPER_ADMIN_EMAIL': JSON.stringify(env.VITE_SUPER_ADMIN_EMAIL), 'import.meta.env.VITE_DEV': env.VITE_DEV === 'true', }, envPrefix: 'VITE_', base, server: { host: '0.0.0.0', port: parseInt(env.VITE_PORT_FRONTEND || '5173'), watch: { usePolling: env.VITE_DEV === 'true', ignored: ['**/node_modules/**', '**/dist/**'] }, allowedHosts: [ `app.${env.VITE_FRONTEND_SITE_URL}`, ], hmr: isProd ? false : { protocol: env.VITE_DEV === 'true' ? 'ws' : 'wss', host: env.VITE_DEV == 'true' ? 'localhost' : env.VITE_APP_HMR_URL, port: parseInt(env.VITE_PORT_FRONTEND || '5173'), clientPort: parseInt(env.VITE_PORT_FRONTEND_HMR || '5173'), overlay: false } }, clearScreen: false, optimizeDeps: { force: true, include: ['react', 'react-dom', '@mui/material', '@tldraw/tldraw'] }, build: { sourcemap: !isProd, manifest: true, minify: isProd ? 'terser' : false, terserOptions: isProd ? { compress: { drop_debugger: true } } : undefined, rollupOptions: { output: { manualChunks: { 'vendor-react': ['react', 'react-dom', 'react-router-dom'], 'vendor-mui': ['@mui/material', '@mui/icons-material'], 'vendor-tldraw': ['@tldraw/tldraw', '@tldraw/store', '@tldraw/tlschema'], 'vendor-utils': ['axios', 'zustand', '@supabase/supabase-js'] }, // Ensure chunk filenames include content hash chunkFileNames: isProd ? 'assets/[name].[hash].js' : 'assets/[name].js', assetFileNames: isProd ? 'assets/[name].[hash][extname]' : 'assets/[name][extname]' }, // Externalize dependencies that shouldn't be bundled external: isProd ? [] : [/^@vite/, /^@react-refresh/] }, chunkSizeWarningLimit: 2000, // Enable module concatenation for better minification target: 'esnext', cssCodeSplit: true, assetsInlineLimit: 4096, // 4kb modulePreload: true, reportCompressedSize: !isProd }, // Add esbuild optimization esbuild: { drop: isProd ? ['debugger'] : [], legalComments: 'none', target: ['esnext'] } }; });