SkateSpots/vite.config.ts
SkateSpots 8cf35cf7db perf(bundle): entry chunk 1835 KB -> 724 KB
Data-driven pass (adds rollup-plugin-visualizer behind ANALYZE=1):
- MapPage was statically imported by the router, dragging the map route AND the
  IndexedDB offline-cache layer into the entry -> now lazy()
- NewsletterOverlay imported NEWSLETTER_ISSUES from the whole NewsletterPage
  module -> types + data moved to lib/newsletterIssues.ts
- SasquatchGuide (assistant overlay) lazy-loaded in Layout
- Cashu stack is now a separate chunk (useCashu), off the critical path

Net: entry down ~61%; first paint no longer parses the map, the newsletter
page, the assistant or the wallet stack.
2026-09-26 19:37:11 +07:00

115 lines
4.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,
},
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'],
},
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'],
},
}));