Repository navigation
Expand file tree
/
Copy pathvite.config.ts
More file actions
129 lines (124 loc) · 4.99 KB
/
Copy pathvite.config.ts
File metadata and controls
129 lines (124 loc) · 4.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import { execSync } from "node:child_process";
import { existsSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { defineConfig, type Plugin } from "vite";
import { VitePWA } from "vite-plugin-pwa";
import pkg from "./package.json" with { type: "json" };
// Vite's SPA fallback answers any missing path with index.html + HTTP 200.
// Binary consumers (PMTiles archives, glyph PBFs) then parse HTML as their
// format and spam the console ("Wrong magic number", "Unimplemented type").
// Production hosting 404s these properly — make dev match.
const binaryAssets404: Plugin = {
name: "binary-assets-404",
configureServer(server) {
server.middlewares.use((req, res, next) => {
const path = decodeURIComponent((req.url ?? "").split("?")[0]);
if (/\.(pmtiles|pbf)$/.test(path) && !existsSync(join("public", path))) {
res.statusCode = 404;
res.end("Not found");
return;
}
next();
});
},
};
const gitSha = (() => {
try {
return execSync("git rev-parse --short HEAD").toString().trim();
} catch {
return "unknown";
}
})();
const buildTime = new Date().toISOString().replace(/:\d{2}\.\d+Z$/, "Z"); // drop seconds
// Crawl date of the bundled NOAA tide/current harmonics (shown in About)
const tidesDataDate = (() => {
try {
const bundle = JSON.parse(
readFileSync("./public/tides-stations.json", "utf8"),
) as { generated?: string };
return bundle.generated ?? "unknown";
} catch {
return "unknown";
}
})();
const isCapacitor = !!process.env.CAPACITOR;
export default defineConfig({
define: {
__APP_VERSION__: JSON.stringify(pkg.version),
__BUILD_ID__: JSON.stringify(`${buildTime} ${gitSha}`),
__TIDES_DATA_DATE__: JSON.stringify(tidesDataDate),
},
build: {
target: "es2022",
// Capacitor inlines sourcemaps into the JS, bloating the bundle to ~6.8 MB,
// which is very slow to parse on weak e-ink WebView CPUs. Keep sourcemaps
// for web/dev builds; drop them for the on-device Capacitor bundle.
sourcemap: !isCapacitor,
},
server: {
host: true,
port: 5173,
watch: {
ignored: ["**/refs/**"],
},
},
optimizeDeps: {
entries: ["src/main.ts", "src/worker.ts"],
},
plugins: [
binaryAssets404,
// PWA/service worker is disabled for Capacitor builds — assets are bundled
// locally and the SW only causes stale-cache problems in Android WebView.
// (`disable` still provides a no-op virtual:pwa-register module.)
// "autoUpdate" bakes skipWaiting + clientsClaim into the generated SW, so
// a new build activates and takes control of open tabs without waiting
// for every client to unload. App data lives in IndexedDB/localStorage/
// OPFS, so an automatic activation is safe. AppUpdateNotifier registers
// the SW itself (not via the generated virtual:pwa-register wrapper,
// which would force an immediate reload) so it can reload at an idle
// moment instead of mid-interaction.
VitePWA({
disable: isCapacitor,
registerType: "autoUpdate",
// Manual registration (AppUpdateNotifier.ts), not the generated
// virtual:pwa-register wrapper — see the comment above.
injectRegister: false,
workbox: {
// registerType only auto-sets these when injectRegister is left at
// its "auto" default; set explicitly since injectRegister is false.
skipWaiting: true,
clientsClaim: true,
// MapLibre glyphs (pbf) are bundled under public/fonts and
// precached so labels render fully offline.
globPatterns: ["**/*.{js,css,html,svg,png,woff2,json,pbf}"],
// The landing page (served at "/" by the worker; app lives at /app)
// is marketing, not app shell — keep it and its assets out of the
// app's offline precache.
globIgnores: [
"**/*.pmtiles",
"**/*.geojson",
"**/*.search.json",
"landing.html",
"landing/**",
"privacy.html",
],
navigateFallback: "/index.html",
// The app owns exactly /app; every other URL (the landing page at
// "/", /api, and any future site section like /docs) belongs to the
// network, where worker.ts routes it. An allowlist means new site
// sections need no service-worker changes — the old denylist
// approach would silently serve the app shell on any URL nobody
// remembered to list.
navigateFallbackAllowlist: [/^\/app(?:[/?#]|$)/],
// Workbox's precache route maps "/" to the precached /index.html by
// default (directoryIndex), BEFORE the navigation route runs — so no
// deny/allowlist ever saw "/" and the SW served the app shell on the
// landing page URL (then main.ts's old-PWA migration rewrote the URL
// to /app: the About dialog's Website link led straight back to the
// app). Disable it; "/" now reaches the network and gets the landing
// page.
directoryIndex: "",
},
}),
],
});