How the Vite Build Configuration Works for Production in Thunderbolt

Thunderbolt uses a single vite.config.ts file that configures both development and production builds, where the build section activates only during vite build, generating optimized static assets in dist/ while externalizing native modules and conditionally enabling features via environment variables.

Thunderbolt is an open-source email client built by the Thunderbird team, leveraging modern web technologies for its user interface. Understanding the Vite build configuration is essential for contributors packaging the application for production or optimizing bundle sizes. The configuration is centralized in vite.config.ts and handles everything from dependency deduplication to conditional asset copying.

Core Production Settings in vite.config.ts

The production behavior is governed by the build property in the configuration object, which Vite applies only when running vite build or bun run build.

Source Map Management

Source maps are controlled by the ENABLE_SOURCEMAP environment variable. In vite.config.ts lines 22-24, the configuration checks this variable to set the sourcemap constant to either 'hidden' or false. When enabled, hidden source maps allow error tracking services like PostHog to map minified code back to source without exposing mappings publicly. This value is passed to build.sourcemap on lines 28-29.

Handling Native Dependencies

Native modules that cannot be bundled must be externalized. The configuration uses rollupOptions.external on lines 30-32 to mark bun:sqlite as external, preventing Rollup from attempting to bundle the native SQLite driver into the JavaScript bundle.

Path Aliases and Deduplication

To minimize bundle size and resolve imports consistently, the configuration defines several aliases in the resolve.alias array on lines 74-81. These map @ to src, @shared to shared, and provide an internal PowerSync alias required by the custom SharedWorker. Additionally, resolve.dedupe on lines 74-75 ensures only single instances of react and @powersync/* packages exist in the final bundle, preventing duplicate React copies that could cause hook errors.

Build Plugins and Asset Pipeline

The plugins array on lines 34-51 configures the transformation pipeline, with several plugins specifically impacting production output.

PowerSync Asset Copying

Before bundling begins, the copy-powersync-assets plugin executes powersync-web copy-assets --output public. This ensures PowerSync's WebAssembly and required static files are present in the public directory, which Vite copies verbatim to dist/ during the build.

Conditional Bundle Analysis

Bundle analysis is gated by the ANALYZE environment variable. When set to true, the configuration spreads a vite-bundle-analyzer plugin into the plugins array on lines 44-50, generating a static report of bundle composition and sizes. This is typically invoked via ANALYZE=true bun run build.

Dependency Optimization

During development, Vite pre-bundles dependencies to improve cold-start performance. However, for production, the optimizeDeps.exclude array on lines 18-20 prevents pre-bundling of heavy native modules like @journeyapps/wa-sqlite and @powersync/web, ensuring they are handled correctly by the build pipeline rather than optimized away.

Development vs. Production Mode

The configuration distinguishes between environments primarily through Vite's internal mode detection. The server block—which configures port 1420, HMR behavior, and file-system whitelisting—is only active during vite dev or when running the Tauri desktop workflow. When executing vite build, Vite ignores the server configuration entirely and outputs optimized static assets to the dist/ directory, ready for deployment via any HTTP server or integration into the Tauri binary.

Running Production Builds

Execute the following commands from the repository root to generate production bundles:

Standard production build:

bun run build

Build with bundle analysis:

ANALYZE=true bun run build

Build with source maps for error tracking:

ENABLE_SOURCEMAP=true bun run build

Verify PowerSync assets were copied:

ls public | grep powersync

Summary

  • Thunderbolt uses a unified vite.config.ts for both development and production, with the build section activating only during vite build.
  • Source maps are controlled via ENABLE_SOURCEMAP, defaulting to disabled for privacy but supporting hidden maps for CI error tracking.
  • Native modules like bun:sqlite are externalized via rollupOptions.external to prevent bundling errors.
  • The PowerSync asset plugin ensures WebAssembly files are copied to public/ before bundling.
  • Bundle analysis can be triggered with ANALYZE=true to audit bundle composition.
  • The configuration outputs static files to dist/, suitable for web deployment or Tauri packaging.

Frequently Asked Questions

How do I enable source maps for production builds in Thunderbolt?

Set the ENABLE_SOURCEMAP environment variable to true before running the build. This configures Vite to generate hidden source maps according to the logic in vite.config.ts lines 22-29, allowing error tracking services to map stack traces without exposing source maps publicly.

Why is bun:sqlite marked as external in the Vite configuration?

bun:sqlite is a native module that provides SQLite functionality through Bun's native bindings. Marking it as external in rollupOptions.external (lines 30-32 of vite.config.ts) prevents Rollup from attempting to bundle this native code into the JavaScript bundle, which would cause build failures.

What triggers the bundle analyzer in Thunderbolt's build process?

The vite-bundle-analyzer plugin is conditionally injected when the ANALYZE environment variable equals true. This logic appears in lines 44-50 of vite.config.ts, allowing developers to generate a static bundle report by running ANALYZE=true bun run build.

How does Thunderbolt handle PowerSync assets during production builds?

Before Vite bundles the application, the copy-powersync-assets plugin executes a CLI command to copy PowerSync's WebAssembly and static files into the public directory. Since Vite copies public/ contents verbatim to dist/, these assets are available in the production bundle without being processed by the bundler.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →