The smokesignal.events web application
1import { defineConfig } from 'vite'
2import { resolve } from 'path'
3
4export default defineConfig({
5 root: '.',
6 publicDir: false,
7 base: '/static/',
8 build: {
9 outDir: '../static',
10 emptyOutDir: false,
11 sourcemap: false,
12 // Generate manifest.json for Rust backend to resolve hashed filenames
13 manifest: true,
14 rollupOptions: {
15 input: {
16 main: resolve(__dirname, 'src/main.ts'),
17 },
18 output: {
19 // All files get content hashes for proper cache invalidation
20 entryFileNames: 'js/[name]-[hash].js',
21 chunkFileNames: 'js/[name]-[hash].js',
22 assetFileNames: (assetInfo) => {
23 if (assetInfo.name?.endsWith('.css')) {
24 return 'css/[name]-[hash].css'
25 }
26 return 'assets/[name]-[hash][extname]'
27 },
28 // Manual chunk splitting for better caching
29 // htmx and Alpine are bundled in the main bundle for immediate availability
30 manualChunks: (id) => {
31 // Map libraries - lazy loaded together
32 if (id.includes('maplibre-gl') || id.includes('h3-js')) {
33 return 'maps'
34 }
35 // Cropper - lazy loaded separately
36 if (id.includes('cropperjs')) {
37 return 'cropper'
38 }
39 },
40 },
41 },
42 minify: 'esbuild',
43 cssMinify: 'lightningcss',
44 },
45 css: {
46 transformer: 'lightningcss',
47 lightningcss: {
48 targets: {
49 chrome: 110,
50 firefox: 110,
51 safari: 16,
52 },
53 drafts: {
54 customMedia: true,
55 },
56 },
57 },
58 resolve: {
59 alias: {
60 '@': resolve(__dirname, 'src'),
61 '@styles': resolve(__dirname, 'styles'),
62 },
63 },
64 // Ensure dynamic CSS imports work
65 optimizeDeps: {
66 include: ['alpinejs'],
67 },
68})