import { fileURLToPath, URL } from 'node:url' import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import vueDevTools from 'vite-plugin-vue-devtools' import { viteSingleFile } from 'vite-plugin-singlefile' import { VitePWA } from 'vite-plugin-pwa' import VueJsx from '@vitejs/plugin-vue-jsx' import { execSync } from 'node:child_process' function getGitCommit() { try { return execSync('git rev-parse --short HEAD').toString().trim() } catch { return 'unknown' } } export default defineConfig(({ mode }) => { const isProd = mode === 'production' // Set COMMIT_HASH env var for use in the app const version = process.env.npm_package_version || '0.0.0' const commit = getGitCommit() console.log(`Building version ${version} (commit: ${commit})`) process.env.COMMIT_HASH = commit process.env.APP_VERSION = version process.env.IS_DEV = !isProd ? 'true' : 'false' // If the build is a production build, update the server with the new version if (isProd) { console.log('Updating server with new version...') const token = process.env.SERVER_UPDATE_TOKEN if (!token) { console.warn('SERVER_UPDATE_TOKEN is not set. Skipping server update.') } else { // Run GET request to update version endpoint const url = `https://api.truckwash.dk/worker/update-version?version=${commit}` const bearer = `Bearer ${token}` console.log(`Calling URL: ${url}`) try { execSync(`curl -f -X GET "${url}" -H "Authorization: ${bearer}"`) console.log('Successfully updated server with new version.') } catch (error) { console.error('Failed to update server with new version:', error.message) } } } // Keep a relative base for static hosting; align manifest with base const base = '/' const pwaScope = base === './' ? '.' : base // Enable single-file build only when explicitly requested (disabled by default for PWA) const enableSingleFile = process.env.VITE_SINGLE_FILE === 'true' && !isProd return { base, plugins: [ vue(), VueJsx(), !isProd && vueDevTools(), enableSingleFile && viteSingleFile(), VitePWA({ registerType: 'autoUpdate', injectRegister: 'auto', strategies: 'generateSW', devOptions: { enabled: false // keep for local dev }, workbox: { maximumFileSizeToCacheInBytes: 12 * 1024 * 1024, cleanupOutdatedCaches: true, skipWaiting: true, clientsClaim: true, globPatterns: ['**/*.{js,css,html,ico,svg,woff2}'], globIgnores: ['**/registerSW.js', '**/sw.js'], runtimeCaching: [ { // cache API responses urlPattern: ({ url }) => url.origin === 'https://api.truckwash.io', handler: 'NetworkFirst', options: { cacheName: 'pleno-api-cache', expiration: { maxEntries: 100, maxAgeSeconds: 5 * 60 // 5 minutes }, networkTimeoutSeconds: 10, cacheableResponse: { statuses: [200] // avoid opaque caching for API } } }, { // cache same-origin static assets urlPattern: ({ sameOrigin, request }) => sameOrigin && ['style', 'script', 'font'].includes(request.destination), handler: 'StaleWhileRevalidate', options: { cacheName: 'pleno-website-cache', expiration: { maxEntries: 50, maxAgeSeconds: 24 * 60 * 60 // 24 hours }, cacheableResponse: { statuses: [0, 200] } } }, { // cache images (restrict to same-origin; loosen if you use a trusted CDN) urlPattern: ({ sameOrigin, request }) => sameOrigin && request.destination === 'image', handler: 'CacheFirst', options: { cacheName: 'pleno-image-cache', expiration: { maxEntries: 100, maxAgeSeconds: 7 * 24 * 60 * 60 // 7 days }, cacheableResponse: { statuses: [0, 200] } } } ] }, manifest: { name: 'Truck Wash Kundeportal', short_name: 'Truck Wash', description: 'Access your Truck Wash accounts and transactions from anywhere.', theme_color: '#063651', background_color: '#0787bb', // Use relative scope when base is relative start_url: pwaScope, scope: pwaScope, display: 'standalone', orientation: 'portrait', launch_handler: { client_mode: 'navigate-existing' }, icons: [ { src: 'favicons/web-app-manifest-192x192.png', sizes: '192x192', type: 'image/png' }, { src: 'favicons/web-app-manifest-512x512.png', sizes: '512x512', type: 'image/png' }, // Optional: add a maskable icon // { src: 'icons/icon-512x512-maskable.png', sizes: '512x512', type: 'image/png', purpose: 'any maskable' } ], id: 'pleno-pwa-test' }, useCredentials: true }) ].filter(Boolean), resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } }, define: { 'import.meta.env.VITE_BUILD_DATE': JSON.stringify(new Date().toISOString()), 'import.meta.env.VITE_APP_VERSION': JSON.stringify(process.env.APP_VERSION || '0.0.0'), 'import.meta.env.VITE_COMMIT_HASH': JSON.stringify(commit), // Tip: import.meta.env.DEV/PROD are available at runtime 'import.meta.env.VITE_IS_DEV': JSON.stringify(!isProd), }, css: { preprocessorOptions: { scss: { api: 'modern-compiler', silenceDeprecations: ['import', 'global-builtin', 'legacy-js-api'] } } } } })