mitlist/fe/vite.config.ts
mohamad 5a2e80eeee feat: Enhance WebSocket connection handling and introduce skeleton components
This commit includes several improvements and new features:

- Updated the WebSocket connection logic in `websocket.py` to include connection status messages and periodic pings for maintaining the connection.
- Introduced new skeleton components (`Skeleton.vue`, `SkeletonDashboard.vue`, `SkeletonList.vue`) for improved loading states in the UI, enhancing user experience during data fetching.
- Refactored the Vite configuration to support advanced code splitting and caching strategies, optimizing the build process.
- Enhanced ESLint configuration for better compatibility with project structure.

These changes aim to improve real-time communication, user interface responsiveness, and overall application performance.
2025-06-28 23:02:23 +02:00

240 lines
7.2 KiB
TypeScript

import { defineConfig, loadEnv } from 'vite';
import vue from '@vitejs/plugin-vue';
import { VitePWA, VitePWAOptions } from 'vite-plugin-pwa';
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite';
import { fileURLToPath, URL } from 'node:url';
import path from 'node:path';
const pwaOptions: Partial<VitePWAOptions> = {
registerType: 'autoUpdate',
strategies: 'injectManifest',
srcDir: 'src',
filename: 'sw.ts',
devOptions: {
enabled: true,
type: 'module',
navigateFallback: 'index.html',
suppressWarnings: true,
},
manifest: {
name: 'mitlist',
short_name: 'mitlist',
description: 'mitlist pwa',
theme_color: '#fff8f0',
background_color: '#f3f3f3',
display: 'standalone',
orientation: 'portrait',
icons: [
{ src: 'icons/icon-128x128.png', sizes: '128x128', type: 'image/png' },
{ src: 'icons/icon-192x192.png', sizes: '192x192', type: 'image/png' },
{ src: 'icons/icon-256x256.png', sizes: '256x256', type: 'image/png' },
{ src: 'icons/icon-384x384.png', sizes: '384x384', type: 'image/png' },
{ src: 'icons/icon-512x512.png', sizes: '512x512', type: 'image/png' },
],
},
injectManifest: {
globPatterns: [
'**/*.{js,css,html,ico,png,svg,woff2}',
'offline.html',
],
globIgnores: [
'**/node_modules/**',
'**/dist/**',
'sw.js',
'dev-sw.js',
'index.html',
],
maximumFileSizeToCacheInBytes: 15 * 1024 * 1024, // 15MB
},
workbox: {
cleanupOutdatedCaches: true,
sourcemap: true,
},
};
export default ({ mode }: { mode: string }) => {
const env = loadEnv(mode, process.cwd(), '');
const isProduction = mode === 'production';
return defineConfig({
plugins: [
vue(),
VitePWA(pwaOptions),
VueI18nPlugin({
include: [path.resolve(path.dirname(fileURLToPath(import.meta.url)), './src/i18n/**.json')],
strictMessage: false,
runtimeOnly: false,
compositionOnly: false,
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
define: {
__PWA_FALLBACK_HTML__: JSON.stringify('/index.html'),
__PWA_SERVICE_WORKER_REGEX__: JSON.stringify('^(sw|workbox)-.*\\.js$'),
'process.env.MODE': JSON.stringify(process.env.NODE_ENV),
'process.env.PROD': JSON.stringify(process.env.NODE_ENV === 'production'),
},
build: {
// Advanced code splitting configuration
rollupOptions: {
output: {
// Vendor chunk splitting for better caching
manualChunks: {
// Core Vue ecosystem
'vue-vendor': ['vue', 'vue-router', 'pinia'],
// UI Framework chunks
'headlessui': ['@headlessui/vue'],
'icons': ['@heroicons/vue'],
// Large utility libraries
'date-utils': ['date-fns'],
'http-client': ['axios'],
// Chart/visualization libraries (if used)
'charts': ['chart.js', 'vue-chartjs'].filter(dep => {
try {
require.resolve(dep);
return true;
} catch {
return false;
}
}),
// Development tools (only in dev)
...(isProduction ? {} : {
'dev-tools': ['@vue/devtools-api']
})
},
// Optimize chunk file names for caching
chunkFileNames: (chunkInfo) => {
const facadeModuleId = chunkInfo.facadeModuleId
? chunkInfo.facadeModuleId.split('/').pop()?.replace(/\.\w+$/, '') || 'chunk'
: 'chunk';
// Route-based chunks get descriptive names
if (chunkInfo.facadeModuleId?.includes('/pages/')) {
return `pages/[name]-[hash].js`;
}
if (chunkInfo.facadeModuleId?.includes('/components/')) {
return `components/[name]-[hash].js`;
}
if (chunkInfo.facadeModuleId?.includes('/layouts/')) {
return `layouts/[name]-[hash].js`;
}
return `chunks/${facadeModuleId}-[hash].js`;
},
// Optimize entry and asset naming
entryFileNames: `assets/[name]-[hash].js`,
assetFileNames: (assetInfo) => {
const info = assetInfo.name?.split('.') || [];
const _ext = info[info.length - 1];
// Organize assets by type for better caching strategies
if (/\.(png|jpe?g|gif|svg|webp|avif)$/i.test(assetInfo.name || '')) {
return `images/[name]-[hash][extname]`;
}
if (/\.(woff2?|eot|ttf|otf)$/i.test(assetInfo.name || '')) {
return `fonts/[name]-[hash][extname]`;
}
if (/\.css$/i.test(assetInfo.name || '')) {
return `styles/[name]-[hash][extname]`;
}
return `assets/[name]-[hash][extname]`;
}
},
// External dependencies that should not be bundled
external: isProduction ? [] : [
// In development, externalize heavy dev dependencies
]
},
// Performance optimizations
target: 'esnext',
minify: isProduction ? 'terser' : false,
// Terser options for production
...(isProduction && {
terserOptions: {
compress: {
drop_console: true, // Remove console.log in production
drop_debugger: true,
pure_funcs: ['console.log', 'console.info', 'console.debug'] // Remove specific console methods
},
mangle: {
safari10: true // Ensure Safari 10+ compatibility
},
format: {
comments: false // Remove comments
}
}
}),
// Source map configuration
sourcemap: isProduction ? false : 'inline',
// CSS code splitting
cssCodeSplit: true,
// Asset size warnings
chunkSizeWarningLimit: 1000, // 1MB warning threshold
// Enable/disable asset inlining
assetsInlineLimit: 4096 // 4KB threshold for base64 inlining
},
// Performance optimizations for development
optimizeDeps: {
include: [
'vue',
'vue-router',
'pinia',
'@headlessui/vue',
'@heroicons/vue/24/outline',
'@heroicons/vue/24/solid',
'date-fns'
],
exclude: [
// Exclude heavy dependencies that should be loaded lazily
]
},
// Development server configuration
server: {
open: true,
proxy: {
'/api': {
target: env.VITE_API_BASE_URL,
changeOrigin: true,
// Add caching headers for API responses in development
configure: (proxy, _options) => {
proxy.on('proxyRes', (proxyRes, req, _res) => {
// Add cache headers for static API responses
if (req.url?.includes('/categories') || req.url?.includes('/users/me')) {
proxyRes.headers['Cache-Control'] = 'max-age=300'; // 5 minutes
}
});
}
},
},
// Enable gzip compression in dev
middlewareMode: false,
},
// Preview server configuration (for production builds)
preview: {
port: 4173,
strictPort: true,
open: true
}
});
};