fase 3.4: PWA instalable (manifest e iconos, service worker propio con app shell precacheado y cache versionada por build, API nunca cacheada, fallback offline, aviso de instalacion con guia iOS y banner sin conexion) + branding: perfiles de ticket por instancia (defaults neutros, sin filtrar datos de SKEEN) y Dashboard sin nombre hardcodeado
This commit is contained in:
@@ -1,10 +1,63 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import { defineConfig, type Plugin, type ResolvedConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
import { createHash } from 'node:crypto'
|
||||
import { existsSync, readFileSync, readdirSync, writeFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
|
||||
// PWA: inyecta en dist/sw.js (1) una versión de caché única por build y
|
||||
// (2) la lista de assets con hash a precachear. La versión es un hash del
|
||||
// index.html, el manifest y los nombres de los assets, así que cambia
|
||||
// exactamente cuando cambia el bundle (y el SW descarta las cachés viejas
|
||||
// en `activate`). No toca el pipeline `tsc -b && vite build`.
|
||||
function pwaServiceWorker(): Plugin {
|
||||
let outDir = 'dist'
|
||||
return {
|
||||
name: 'skeen-pwa-service-worker',
|
||||
apply: 'build',
|
||||
configResolved(config: ResolvedConfig) {
|
||||
outDir = config.build.outDir
|
||||
},
|
||||
closeBundle() {
|
||||
const dir = resolve(process.cwd(), outDir)
|
||||
const swPath = resolve(dir, 'sw.js')
|
||||
if (!existsSync(swPath)) return
|
||||
|
||||
const listAssets = (sub: string): string[] => {
|
||||
const abs = resolve(dir, sub)
|
||||
if (!existsSync(abs)) return []
|
||||
return readdirSync(abs)
|
||||
.filter((f) => /\.(js|css|woff2?|png|jpe?g|svg|webp|ico|json)$/i.test(f))
|
||||
.map((f) => `/${sub}/${f}`)
|
||||
.sort()
|
||||
}
|
||||
// Solo /assets (hash): los iconos ya van listados explícitamente en sw.js.
|
||||
const assets = listAssets('assets')
|
||||
|
||||
const read = (file: string): Buffer =>
|
||||
existsSync(resolve(dir, file)) ? readFileSync(resolve(dir, file)) : Buffer.from('')
|
||||
const version = createHash('sha256')
|
||||
.update(read('index.html'))
|
||||
.update(read('manifest.webmanifest'))
|
||||
.update(assets.join('|'))
|
||||
.digest('hex')
|
||||
.slice(0, 12)
|
||||
|
||||
let sw = readFileSync(swPath, 'utf8')
|
||||
sw = sw.replace('__BUILD_VERSION__', version)
|
||||
// JSON dentro de un literal de una sola comilla: se quitan comillas
|
||||
// simples por seguridad (los nombres de Vite no las contienen).
|
||||
sw = sw.replace('__PRECACHE_ASSETS__', JSON.stringify(assets).replace(/'/g, ''))
|
||||
writeFileSync(swPath, sw)
|
||||
|
||||
this.warn(`PWA service worker: versión de caché ${version}, ${assets.length} assets precacheados`)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react(), tailwindcss()],
|
||||
plugins: [react(), tailwindcss(), pwaServiceWorker()],
|
||||
server: {
|
||||
port: 5173,
|
||||
proxy: {
|
||||
|
||||
Reference in New Issue
Block a user