From 6dfd5203d78c75c958fc81e0cd39664b0c40e025 Mon Sep 17 00:00:00 2001 From: Ilia Denisov Date: Sat, 4 Jul 2026 12:58:57 +0200 Subject: [PATCH] fix(ui): down-level build target to es2019 for old Android WebViews MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The es2022 build target shipped optional chaining / nullish coalescing verbatim (esbuild does not down-level syntax below the configured target), so an old Android System WebView — captured as Chromium 66 in the Telegram/VK in-app WebView on Android 9, via the boot diagnostic — rejected the bundle with "Uncaught SyntaxError: Unexpected token ?" at main-*.js:2, the module never ran (no .app-shell, #app empty) and the SPA showed a white screen. Firefox/Gecko on the same device, which ships its own current engine, rendered the SPA fine. Lower build.target to es2019 so esbuild down-levels the es2020+ syntax the old engine cannot parse (?., ??, private fields, static blocks, numeric separators). Verified the emitted main bundle no longer contains ?./?? and the mock e2e (Chromium + WebKit) still passes. The boot-diagnostic JS-SYNTAX header is relabelled accordingly. esbuild lowers syntax only, not runtime APIs; the app's own production source uses none beyond this floor (mock-only helpers are tree-shaken). A graceful "update your WebView" fallback and any runtime-API polyfills follow once the on-device re-test confirms the parse fix (and @vitejs/plugin-legacy stays in reserve for even older engines). --- ui/index.html | 2 +- ui/vite.config.ts | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/ui/index.html b/ui/index.html index f1f5282..9d150d6 100644 --- a/ui/index.html +++ b/ui/index.html @@ -162,7 +162,7 @@ ['numeric separator 1_000', 'return 1_000', 75], ['dynamic import()', 'return import("")', 63], ]; - var synPre = section('JS SYNTAX (esbuild target is es2022 — the bundle uses these)'); + var synPre = section('JS SYNTAX (engine feature ceiling — first NO dates the engine)'); function mark(ok) { return ok ? 'OK ' : 'NO '; } diff --git a/ui/vite.config.ts b/ui/vite.config.ts index f4d0d8a..817abff 100644 --- a/ui/vite.config.ts +++ b/ui/vite.config.ts @@ -54,7 +54,13 @@ export default defineConfig(({ mode }) => ({ }, }, build: { - target: 'es2022', + // Down-level to es2019 so an old Android System WebView (seen: Telegram/VK in-app WebView on + // Chromium 66, Android 9) can parse the bundle — esbuild lowers es2020+ syntax (?., ??, private + // fields, static blocks, …) that Chrome 66 rejects with "Unexpected token ?", which left the SPA + // on a white screen. esbuild lowers syntax only, not runtime APIs; the app's own source uses + // none beyond this floor (the mock-only helpers are tree-shaken from production). Reach for + // @vitejs/plugin-legacy if runtime-API polyfills for even older engines turn out to be needed. + target: 'es2019', // Emit sourcemaps everywhere except the production build. A shipped `.map` // carries full `sourcesContent` — the entire TypeScript/Svelte source — and the // gateway/landing images serve `dist/` verbatim, so production maps would expose