SVG source in frontend/public/icon.svg, rendered to 192/512/maskable PNGs and desktop/build/appicon.png; SVG favicon with PNG fallback; icons added to the service-worker precache. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
68 lines
2.1 KiB
TypeScript
68 lines
2.1 KiB
TypeScript
import { defineConfig, type Plugin } from 'vite';
|
|
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
|
import { createHash } from 'node:crypto';
|
|
|
|
const version = process.env.TEFTER_VERSION || 'dev';
|
|
|
|
// Emits sw.js at build time with the precache list of all emitted assets,
|
|
// versioned by a hash of the file names (which themselves carry content hashes).
|
|
function serviceWorker(): Plugin {
|
|
return {
|
|
name: 'tefter-sw',
|
|
apply: 'build',
|
|
generateBundle(_opts, bundle) {
|
|
const assets = Object.keys(bundle)
|
|
.filter((f) => !f.endsWith('.map'))
|
|
.map((f) => '/' + f);
|
|
assets.push('/', '/manifest.webmanifest', '/icon.svg', '/icon-192.png', '/icon-512.png', '/icon-512-maskable.png');
|
|
const hash = createHash('sha256').update(assets.join(',') + version).digest('hex').slice(0, 12);
|
|
const sw = `// generated by vite.config.ts — do not edit
|
|
const CACHE = 'tefter-${'${'}HASH}';
|
|
const ASSETS = ${'${'}ASSETS};
|
|
|
|
self.addEventListener('install', (e) => {
|
|
e.waitUntil(caches.open(CACHE).then((c) => c.addAll(ASSETS)).then(() => self.skipWaiting()));
|
|
});
|
|
|
|
self.addEventListener('activate', (e) => {
|
|
e.waitUntil(
|
|
caches.keys()
|
|
.then((keys) => Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k))))
|
|
.then(() => self.clients.claim())
|
|
);
|
|
});
|
|
|
|
self.addEventListener('fetch', (e) => {
|
|
const url = new URL(e.request.url);
|
|
if (e.request.method !== 'GET' || url.origin !== location.origin) return;
|
|
if (url.pathname.startsWith('/api/')) return; // network-only; sync engine handles offline
|
|
e.respondWith(
|
|
caches.match(url.pathname === '/' || url.pathname === '/index.html' ? '/' : e.request).then(
|
|
(hit) => hit || fetch(e.request)
|
|
)
|
|
);
|
|
});
|
|
`
|
|
.replace('${HASH}', hash)
|
|
.replace('${ASSETS}', JSON.stringify(assets));
|
|
this.emitFile({ type: 'asset', fileName: 'sw.js', source: sw });
|
|
},
|
|
};
|
|
}
|
|
|
|
export default defineConfig({
|
|
plugins: [svelte(), serviceWorker()],
|
|
define: {
|
|
__TEFTER_VERSION__: JSON.stringify(version),
|
|
},
|
|
build: {
|
|
target: 'es2020',
|
|
sourcemap: false,
|
|
},
|
|
server: {
|
|
proxy: {
|
|
'/api': 'http://127.0.0.1:8420',
|
|
},
|
|
},
|
|
});
|