SkateSpots/vite.config.ts
SkateSpots d2a24679f0 feat(chat): on-device translation pilot (English ↔ Spanish)
- @xenova/transformers 2.17.2 in a Web Worker; Opus-MT en↔es ONNX q8 packs
  self-hosted on the relay VPS (/models + CORS); ONNX Runtime 1.14 WASM
  self-hosted in public/ort/ (37MB) loaded from ../ort/.
- Hard network guard: only model GET/HEAD (no query) and /ort/ assets allowed.
  Message text travels only via postMessage; 9 tests prove fetch is never called
  with the text and every other request is blocked.
- TranslateMessage in DMs + crew chat; Settings auto-translate off by default;
  memory-only translations; per-model download progress; 12 UI strings in all
  24 locales.
- Both model directions verified from the hosted files; tsc, 138 tests, build
  and smoke green.
2026-10-27 10:49:54 +07:00

142 lines
5.9 KiB
TypeScript

import path from "node:path";
import react from "@vitejs/plugin-react-swc";
import { visualizer } from "rollup-plugin-visualizer";
import { defineConfig } from "vitest/config";
// https://vitejs.dev/config/
export default defineConfig(() => ({
// In a native Capacitor WebView the bundle is served from the local
// filesystem, so assets must be referenced with RELATIVE paths ('./…').
// Absolute '/…' paths are the classic cause of a white screen on device.
// The regular web deploy keeps '/' (served from a domain root).
base: process.env.CAPACITOR_BUILD ? './' : '/',
server: {
host: "::",
port: 8080,
// Dev proxy for the MintRadar directory (no CORS upstream; see public/_redirects).
proxy: {
'/api/mints': {
target: 'https://mintradar.org',
changeOrigin: true,
secure: true,
},
// /api/nip05 (the name registry) and /api/ai are PRODUCTION-only (Netlify
// Function + the CVM-host proxy). Without a dev proxy the dev server
// answers them with index.html, so the NIP-05 availability check failed
// locally with "Could not check right now" and dev AI never worked.
'/api/nip05': {
target: 'https://skate-spots.org',
changeOrigin: true,
secure: true,
},
'/api/ai': {
target: 'https://ai.skate-spots.org',
changeOrigin: true,
secure: true,
},
},
},
plugins: [
react(),
// Bundle analysis — only when explicitly requested:
// ANALYZE=1 npm run build → writes stats.json (machine-readable)
process.env.ANALYZE === '1' &&
visualizer({ filename: 'stats.json', template: 'raw-data', gzipSize: true }),
],
test: {
globals: true,
environment: 'jsdom',
// Absolute path (resolved from this config's own directory) so the setup
// file is found regardless of the cwd vitest is invoked from. A bare
// relative './src/test/setup.ts' can resolve against the wrong root on
// some setups and fail with "Cannot find module .../src/test/setup.ts".
setupFiles: path.resolve(__dirname, './src/test/setup.ts'),
// Only run the app's own tests. Skill templates under .agents/ carry their
// own *.test.tsx that assume a different project layout (e.g. they import
// @/lib/getDisplayName, which doesn't exist here) and must never run as
// part of this app's suite. Build artifacts are excluded too.
include: ['src/**/*.{test,spec}.{ts,tsx}'],
exclude: ['node_modules', 'dist', 'android', 'ios', '.agents', 'cvm-server'],
onConsoleLog(log) {
return !log.includes("React Router Future Flag Warning");
},
env: {
DEBUG_PRINT_LIMIT: '0',
},
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
optimizeDeps: {
exclude: ['@lightninglabs/lnc-web', '@xenova/transformers'],
},
// The translation worker imports the ESM transformers.js build and loads the
// ONNX runtime from /ort/ (self-hosted). Keep it as an ES module worker.
worker: {
format: 'es',
},
build: {
// Strip console.log/warn/debug in production (keeps console.error for real errors)
minify: 'esbuild',
commonjsOptions: {
transformMixedEsModules: true,
},
rollupOptions: {
// Bundling policy — the distinction that matters is STATIC vs DYNAMIC import,
// and whether the package resolves on the build machine:
//
// BUNDLED (absent from this list):
// - @capacitor/core — imported STATICALLY (lib/platform.ts). It MUST be
// bundled: leaving it external emitted a bare `import "@capacitor/core"`
// specifier the native WebView can't resolve → white-screen boot crash.
// Capacitor guarantees core is installed, so bundling always resolves.
//
// EXTERNAL (listed below):
// - @capacitor/camera — used only via a GUARDED dynamic import() on native
// (components/CameraCapture.tsx). It may not be installed on every build
// machine, and it's optional/native-only, so it must stay external. The
// dynamic import() remains a runtime call that degrades gracefully.
// - @capacitor/app|haptics|keyboard|status-bar — NOT installed and never
// statically imported (accessed via window.Capacitor.Plugins at runtime).
//
// NOTE: Amber signing no longer uses the npm `nostr-signer-capacitor-plugin`.
// It now goes through our OWN native plugin `AmberIntent`
// (android-templates/AmberIntentPlugin.java), which implements the Amethyst
// foreground-intent NIP-55 flow and is reached via registerPlugin('AmberIntent')
// from @capacitor/core (already bundled). No bare specifier to externalize.
external: [
'@capacitor/camera',
'@capacitor/app',
'@capacitor/haptics',
'@capacitor/keyboard',
'@capacitor/status-bar',
],
output: process.env.CAPACITOR_BUILD ? {
// Single bundle for Capacitor APK — avoids 100+ chunk file requests
// that fail silently in Android WebView local file serving
manualChunks: undefined,
inlineDynamicImports: true,
} : {
// Web build: let Rollup decide chunk boundaries.
//
// A custom manualChunks() used to split large vendors into `vendor-*`
// chunks. That produced cross-chunk circular imports and crashed boot
// of the production bundle with
// "ReferenceError: Cannot access '<x>' before initialization" (TDZ) —
// first in vendor-charts, then vendor-bitcoin. Rollup's default
// splitting keeps interdependent modules together and is safe. Only
// re-introduce manual chunks with a runtime smoke test of the built
// bundle (dev mode does NOT catch this).
},
},
},
// Strip console.log/warn/debug calls from production build
esbuild: {
drop: ['debugger'],
pure: ['console.log', 'console.warn', 'console.debug'],
},
}));