feat(ui): support old Android in-app WebViews (es2019 + core-js + engine screen)
CI / changes (push) Successful in 2s
CI / unit (push) Has been skipped
CI / integration (push) Has been skipped
CI / ui (push) Successful in 1m5s
CI / conformance (push) Successful in 9s
CI / gate (push) Successful in 0s
CI / deploy (push) Successful in 1m54s
CI / changes (push) Successful in 2s
CI / unit (push) Has been skipped
CI / integration (push) Has been skipped
CI / ui (push) Successful in 1m5s
CI / conformance (push) Successful in 9s
CI / gate (push) Successful in 0s
CI / deploy (push) Successful in 1m54s
Old Android System WebViews (the Telegram/VK in-app browser; a real device with Google Play auto-updates the WebView, but emulators / no-Play / restricted devices stay frozen) showed a white screen. Two causes, fixed in order: - build.target es2022 shipped ?./?? verbatim, which the old engine cannot parse. Lowered to es2019 so esbuild down-levels the syntax. - The es2020+ runtime globals the bundle and its deps call (globalThis, structuredClone, Array.at, ...) are missing on those engines. A conditional core-js loader (emitPolyfills writes dist/polyfills.js, document.write'd by an index.html gate only when needed) covers them; modern engines download nothing, so the bundle-size budget is untouched. BigInt (the 64-bit FlatBuffers timestamp decode) and Proxy (Svelte 5 runes) cannot be polyfilled, so the effective floor is Chrome 67. Below it, a permanent ES5 boot guard in index.html shows a friendly "this device's OS or browser can't run the app" screen (with the web-version link inside a Mini App) and a "Diagnostic information" view with a Copy button, instead of a white screen. A reactive net raises the same screen on an uncaught boot error that never signals window.__booted. Board tile glyphs used container-query units (cqw, Chrome 105+) under .cell font-size:0, so they collapsed to 0px on Chrome 74 (invisible letters); added vmin fallbacks. The unsupported-engine telemetry beacon is a separate follow-up PR.
This commit was merged in pull request #176.
This commit is contained in:
+47
-3
@@ -1,7 +1,42 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
import { defineConfig } from 'vite';
|
||||
import { defineConfig, type Plugin } from 'vite';
|
||||
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
||||
|
||||
/**
|
||||
* injectBootVersion stamps the app version into index.html's boot-capability guard, replacing its
|
||||
* __BOOT_VERSION__ placeholder with the same VITE_APP_VERSION build-arg that feeds __APP_VERSION__.
|
||||
* The guard runs before the bundle, so it cannot read the bundle's version; this lets its on-demand
|
||||
* diagnostic report name the client version anyway. Falls back to "dev" for a local build.
|
||||
*/
|
||||
function injectBootVersion(): Plugin {
|
||||
const version = process.env.VITE_APP_VERSION || 'dev';
|
||||
return {
|
||||
name: 'inject-boot-version',
|
||||
transformIndexHtml(html) {
|
||||
return html.replace(/__BOOT_VERSION__/g, version);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* emitPolyfills writes the prebuilt core-js bundle to `dist/polyfills.js`. The index.html gate
|
||||
* script loads it via document.write only on an old engine that lacks the es2020+ runtime APIs the
|
||||
* app uses (globalThis, structuredClone, Array.at, …) — e.g. the Chromium 66 Android System WebView
|
||||
* behind the Telegram/VK in-app browser — before the deferred module runs. A modern engine never
|
||||
* requests it, so it adds nothing to that payload and stays out of the module graph the bundle-size
|
||||
* gate measures.
|
||||
*/
|
||||
function emitPolyfills(): Plugin {
|
||||
return {
|
||||
name: 'emit-polyfills',
|
||||
generateBundle() {
|
||||
const src = readFileSync(resolve(import.meta.dirname, 'node_modules/core-js-bundle/minified.js'), 'utf8');
|
||||
this.emitFile({ type: 'asset', fileName: 'polyfills.js', source: src });
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// The edge Connect service is scrabble.edge.v1.Gateway; the gateway serves it over
|
||||
// h2c on :8081 by default. In dev we proxy the RPC path so the browser (which can
|
||||
// not speak h2c directly) talks to the dev server on the same origin. In `mock`
|
||||
@@ -19,7 +54,9 @@ export default defineConfig(({ mode }) => ({
|
||||
// so a missing build-arg never breaks the build.
|
||||
__APP_VERSION__: JSON.stringify(process.env.VITE_APP_VERSION || 'dev'),
|
||||
},
|
||||
plugins: [svelte()],
|
||||
// emitPolyfills ships dist/polyfills.js (loaded only by old engines; see its docstring + the
|
||||
// index.html boot guard). injectBootVersion stamps the app version into that guard's diagnostic.
|
||||
plugins: [svelte(), emitPolyfills(), injectBootVersion()],
|
||||
server: {
|
||||
port: 5173,
|
||||
proxy:
|
||||
@@ -33,7 +70,14 @@ 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 es2020+ globals the
|
||||
// bundle/deps call at runtime (globalThis, structuredClone, Array.at, …) are covered separately
|
||||
// by the conditional core-js loader (emitPolyfills + the index.html gate), loaded only on an
|
||||
// engine that actually lacks them.
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user