///
///
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: any[]) => ({
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',
'import.meta.env.VITE_API_BASE': JSON.stringify(env.VITE_API_BASE),
'import.meta.env.VITE_SEARCH_URL': JSON.stringify(env.VITE_SEARCH_URL),
},
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
},
proxy: {
'/searxng-api': {
target: env.VITE_SEARCH_URL,
changeOrigin: true,
rewrite: (path: string) => path.replace(/^\/searxng-api/, '')
}
}
},
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: {
// 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']
}
};
});