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 '' 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'], }, }));