← back to Watches
vite.config.js
211 lines
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { VitePWA } from 'vite-plugin-pwa';
import viteCompression from 'vite-plugin-compression';
export default defineConfig({
plugins: [
react(),
// Brotli compression for production
viteCompression({
verbose: true,
disable: false,
threshold: 1024,
algorithm: 'brotliCompress',
ext: '.br',
compressionOptions: {
level: 11
}
}),
// Gzip compression as fallback
viteCompression({
verbose: true,
disable: false,
threshold: 1024,
algorithm: 'gzip',
ext: '.gz'
}),
// Enhanced PWA with Workbox
VitePWA({
registerType: 'autoUpdate',
includeAssets: ['favicon.ico', 'robots.txt', 'sitemap.xml'],
workbox: {
globPatterns: ['**/*.{js,css,html,ico,png,jpg,jpeg,svg,webp,woff,woff2}'],
runtimeCaching: [
{
urlPattern: /^https:\/\/fonts\.googleapis\.com\/.*/i,
handler: 'CacheFirst',
options: {
cacheName: 'google-fonts-cache',
expiration: {
maxEntries: 10,
maxAgeSeconds: 60 * 60 * 24 * 365 // 1 year
},
cacheableResponse: {
statuses: [0, 200]
}
}
},
{
urlPattern: /^https:\/\/fonts\.gstatic\.com\/.*/i,
handler: 'CacheFirst',
options: {
cacheName: 'gstatic-fonts-cache',
expiration: {
maxEntries: 10,
maxAgeSeconds: 60 * 60 * 24 * 365
},
cacheableResponse: {
statuses: [0, 200]
}
}
},
{
urlPattern: /\/api\/watches$/,
handler: 'NetworkFirst',
options: {
cacheName: 'api-watches',
expiration: {
maxEntries: 50,
maxAgeSeconds: 60 * 5 // 5 minutes
},
networkTimeoutSeconds: 10
}
},
{
urlPattern: /\/api\/.*/,
handler: 'NetworkFirst',
options: {
cacheName: 'api-cache',
expiration: {
maxEntries: 100,
maxAgeSeconds: 60 * 10 // 10 minutes
},
networkTimeoutSeconds: 10
}
}
],
navigateFallback: '/index.html',
navigateFallbackDenylist: [/^\/api/]
},
manifest: {
name: 'Omega Watch Price History',
short_name: 'Omega Watches',
description: 'Historical price tracking for 32 iconic Omega timepieces',
theme_color: '#DC143C',
background_color: '#1F2937',
display: 'standalone',
icons: [
{
src: '/icons/icon-192x192.png',
sizes: '192x192',
type: 'image/png'
},
{
src: '/icons/icon-512x512.png',
sizes: '512x512',
type: 'image/png'
}
]
}
})
],
server: {
port: 5173,
proxy: {
'/api': {
target: 'http://localhost:7600',
changeOrigin: true
}
}
},
build: {
outDir: 'dist',
emptyOutDir: true,
// Advanced minification
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true,
pure_funcs: ['console.log', 'console.info', 'console.debug'],
passes: 2
},
mangle: {
safari10: true
}
},
// Aggressive code splitting
rollupOptions: {
output: {
manualChunks: {
// React core (critical)
'react-core': ['react', 'react-dom', 'react/jsx-runtime'],
// Chart library (heavy, lazy load)
'chart-lib': ['chart.js', 'react-chartjs-2'],
// Animation library (heavy, lazy load)
'animation-lib': ['framer-motion'],
// Utilities (small, can be combined)
'utils': ['fuse.js']
},
// Optimize chunk names for caching
chunkFileNames: 'assets/[name]-[hash].js',
entryFileNames: 'assets/[name]-[hash].js',
assetFileNames: 'assets/[name]-[hash].[ext]'
}
},
// Chunk size warnings
chunkSizeWarningLimit: 800,
// Enable CSS code splitting
cssCodeSplit: true,
// No source maps in production
sourcemap: false,
// Target modern browsers
target: 'es2020',
// Optimize dependencies
commonjsOptions: {
transformMixedEsModules: true
},
// CSS minification
cssMinify: true,
// Optimize asset inlining
assetsInlineLimit: 4096
},
optimizeDeps: {
include: [
'react',
'react-dom',
'react/jsx-runtime'
],
exclude: [
'chart.js',
'react-chartjs-2',
'framer-motion'
]
},
// Performance optimization
esbuild: {
logOverride: { 'this-is-undefined-in-esm': 'silent' }
}
});