import { defineConfig } from 'vite'; import { sveltekit } from '@sveltejs/kit/vite'; import { SvelteKitPWA } from '@vite-pwa/sveltekit'; import { paraglide } from '@inlang/paraglide-sveltekit/vite'; import { execSync } from 'node:child_process'; import { readFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; // Fase 14.3.1 — build-time version constants. // Source order: explicit GIT_SHA env (used by `apps/web/Dockerfile` // during `infra/scripts/deploy-erosi.sh`) > local `git rev-parse` > // 'dev' fallback when neither path works (e.g. tarball builds). const rootPkgUrl = new URL('../../package.json', import.meta.url); const rootPkg = JSON.parse(readFileSync(fileURLToPath(rootPkgUrl), 'utf8')) as { version?: string; }; const APP_VERSION = rootPkg.version ?? '0.0.0'; let APP_COMMIT: string = process.env.GIT_SHA ?? ''; if (!APP_COMMIT) { try { APP_COMMIT = execSync('git rev-parse --short HEAD', { stdio: ['ignore', 'pipe', 'ignore'] }) .toString() .trim(); } catch { APP_COMMIT = 'dev'; } } const BUILD_TIME = new Date().toISOString(); export default defineConfig({ define: { __APP_VERSION__: JSON.stringify(APP_VERSION), __APP_COMMIT__: JSON.stringify(APP_COMMIT), __BUILD_TIME__: JSON.stringify(BUILD_TIME) }, plugins: [ paraglide({ project: './project.inlang', outdir: './src/lib/paraglide' }), sveltekit(), SvelteKitPWA({ // `generateSW` strategy: the plugin auto-generates a Workbox SW that // precaches the built shell. Equivalent behavior to our previous // `src/sw.ts` (a 5-line precacheAndRoute wrapper) without the // injectManifest path, which in @vite-pwa/sveltekit 0.6.8 hardcodes // the SW source filename to `service-worker.js` and breaks on any // custom filename. The $lib/sync pending_ops queue handles offline // writes; SyncBanner surfaces offline state to the user. strategies: 'generateSW', scope: '/', base: '/', manifest: { name: 'Colectivo', short_name: 'Colectivo', description: 'Gestión colaborativa del hogar', theme_color: '#0f172a', background_color: '#f7f9fb', display: 'standalone', orientation: 'portrait', scope: '/', start_url: '/', icons: [ { src: '/icons/icon-192.png', sizes: '192x192', type: 'image/png' }, { src: '/icons/icon-512.png', sizes: '512x512', type: 'image/png' }, { src: '/icons/icon-512-maskable.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' } ] }, workbox: { globPatterns: ['client/**/*.{js,css,ico,png,svg,webp,woff,woff2}'], // Supabase paths: never cache — always hit the network. navigateFallbackDenylist: [/^\/auth\//, /^\/rest\//, /^\/realtime\//, /^\/storage\//] }, devOptions: { enabled: true, suppressWarnings: true, type: 'module', navigateFallback: '/' } }) ], server: { port: 5173, // Bind to all interfaces so the LAN (phone, tablet, other laptops on // the same Wi-Fi) can reach the dev server at http://:5173. // Auth flows will fail from non-laptop clients because Keycloak's // issuer URL resolves to `keycloak:8080` only on the laptop's /etc/hosts // — use this for layout/CSS visual checks, not for full login tests. host: true } });