Configuration and Workflow for Building and Packaging TUUI with Vite and electron-builder

TUUI uses a dual-stage build pipeline where Vite bundles the Vue 3 renderer interface alongside Electron's main and preload processes, then electron-builder packages the compiled dist/ output into native installers for macOS, Windows, and Linux.

TUUI (ai-ql/tuui) is an Electron desktop application built with Vue 3 and Vuetify. Its build system orchestrates Vite for frontend bundling and electron-builder for native packaging. The workflow is defined across vite.config.mts, buildAssets/builder/config.js, and npm scripts in package.json.

Key Configuration Files

The build pipeline relies on three primary files to coordinate the Vite and electron-builder integration:

File Purpose
vite.config.mts Configures Vite for the renderer process and registers separate builds for the Electron main (src/main/index.ts) and preload (src/preload/index.ts) entry points.
buildAssets/builder/config.js Defines electron-builder packaging rules, including product metadata, target platforms, icon paths, and file exclusion patterns.
package.json Contains npm scripts that orchestrate the development and production workflows, plus dependency versions for vite, electron, and electron-builder.

Vite Configuration for Electron

The vite.config.mts file centralizes the build logic for all three Electron processes. It uses vite-plugin-electron to inject the main and preload builds into the Vite pipeline.

Renderer Process Setup

The renderer configuration targets the Vue 3 frontend located in src/renderer/. It registers plugins for Vue single-file components, JSX support, and Vuetify auto-importing:

// vite.config.mts (renderer section)
root: resolve('./src/renderer'),
publicDir: resolve('./src/renderer/public'),
base: './',
plugins: [
  Vue(),
  VueJsx(),
  VuetifyPlugin({ autoImport: true }),
  RendererPlugin()  // Enables Node.js integration in renderer
]

Main and Preload Process Builds

The ElectronPlugin accepts an array of ElectronOptions that define separate Vite builds for the main and preload scripts. Each entry specifies its TypeScript entry point, output directory, and Rollup externals:

// vite.config.mts (Electron entry points)
const electronPluginConfigs: ElectronOptions[] = [
  {
    entry: 'src/main/index.ts',
    onstart({ startup }) { startup() },
    vite: {
      root: resolve('.'),
      build: {
        outDir: 'dist/main',
        rollupOptions: {
          external: ['electron', ...builtinModules]
        }
      }
    }
  },
  {
    entry: 'src/preload/index.ts',
    onstart({ reload }) { reload() },
    vite: {
      root: resolve('.'),
      build: { outDir: 'dist/preload' }
    }
  }
]

Environment Variable Injection

The configuration merges environment variables from .env files so both the renderer and Electron processes access the same values:

process.env = { ...loadEnv(mode, process.cwd()), ...process.env }

Additionally, the config removes stale build artifacts before each run:

rmSync('dist', { recursive: true, force: true })

electron-builder Configuration

The buildAssets/builder/config.js file defines how electron-builder packages the compiled dist/ output into platform-specific installers.

Core Packaging Settings

The configuration reads product metadata from package.json and defines universal packaging rules:

// buildAssets/builder/config.js
const packageJson = require('../../package.json')

const baseConfig = {
  productName: packageJson.name,        // "tuui"
  appId: packageJson.appId,             // "com.tuui.app"
  asar: true,
  compression: 'maximum',
  artifactName: '${productName}__${version}_${os}_${arch}.${ext}',
  directories: {
    output: './release/${version}'
  }
}

Platform-Specific Targets

The config defines distinct build targets for each operating system:

macOS (DMG with hardened runtime):

mac: {
  hardenedRuntime: true,
  icon: 'buildAssets/icons/icon.icns',
  target: [
    { target: 'dmg', arch: ['x64', 'arm64', 'universal'] }
  ]
}

Windows (multiple formats):

win: {
  icon: 'buildAssets/icons/icon.ico',
  target: [
    { target: 'appx', arch: 'x64' },
    { target: 'zip', arch: 'x64' },
    { target: 'portable', arch: 'x64' },
    { target: 'nsis', arch: 'x64' }
  ]
}

Linux (DEB, RPM, Snap):

linux: {
  executableName: packageJson.name.toLowerCase(),
  icon: 'buildAssets/icons',
  target: [
    { target: 'snap', arch: 'x64' },
    { target: 'deb', arch: 'x64' },
    { target: 'rpm', arch: 'x64' },
    { target: 'tar.gz', arch: 'x64' }
  ]
}

File Inclusion and Exclusion Rules

The files array controls which compiled assets are packaged, while excluding development artifacts:

files: [
  'dist/**/*',
  '!dist/main/index.dev.js',
  '!docs/**/*',
  '!tests/**/*',
  '!release/**/*'
]

Static assets required by the main process at runtime are copied via extraResources:

extraResources: [
  { from: 'src/main/assets', to: 'assets' }
]

Build Workflow and npm Scripts

The package.json scripts orchestrate the entire build pipeline, from development hot-reload to multi-platform distribution.

Development Mode

The dev script launches Vite with the configuration from vite.config.mts, which automatically starts the Electron process:

npm run dev

This enables hot-module replacement for the Vue renderer and automatic restarts when src/main or src/preload files change.

Production Build Pipeline

The build:pre script performs quality checks and compiles the production bundle:

npm run build:pre

This executes three steps sequentially:

  1. Lint and formatnpm run format:fix
  2. Type checkingvue-tsc --noEmit validates TypeScript across the entire project
  3. Vite production buildvite build outputs to dist/

Platform-Specific Packaging

The main build script runs the full pipeline and invokes electron-builder:

npm run build

This executes build:pre followed by electron-builder --config=buildAssets/builder/config.js.

Platform-specific shortcuts are available:

  • npm run build:macelectron-builder --mac
  • npm run build:winelectron-builder --win
  • npm run build:linuxelectron-builder --linux

For CI environments, the build:all script generates artifacts for all platforms:

npm run build:all

# Equivalent to: electron-builder -wml (Windows, macOS, Linux)

Practical Build Examples

Running the Application in Development

Start the hot-reload development environment:


# Install dependencies

npm ci

# Launch Vite dev server and Electron

npm run dev

The renderer updates instantly on Vue file changes, while the main process restarts when src/main/index.ts or src/preload/index.ts are modified.

Creating a Windows Installer

Generate a production Windows package with NSIS:

npm run build:win

The output appears at release/1.4.1/tuui__1.4.1_win_x64.exe (path derived from the artifactName template in buildAssets/builder/config.js).

Building for Multiple Platforms in CI

On a Linux CI runner, generate all platform artifacts:

npm run build:all

This produces:

  • macOS: tuui__1.4.1_mac_universal.dmg
  • Windows: tuui__1.4.1_win_x64.exe (NSIS), .zip, .appx
  • Linux: .deb, .rpm, .snap, and .tar.gz archives

Configuring Environment Variables

Create a .env file in the project root:


# .env

VITE_API_URL=https://api.example.com
VITE_FEATURE_FLAG=true

These values are injected by loadEnv in vite.config.mts and become available in renderer code via import.meta.env.VITE_API_URL and in the main process via process.env.VITE_API_URL.

Summary

  • Vite handles three builds: The vite.config.mts file configures separate Rollup builds for the Vue renderer (src/renderer), main process (src/main/index.ts), and preload script (src/preload/index.ts).
  • electron-builder consumes the dist output: The buildAssets/builder/config.js defines platform targets (DMG, NSIS, DEB, etc.), icons, and packaging rules, reading the compiled dist/ directory created by Vite.
  • npm scripts orchestrate the pipeline: dev starts hot-reload development, build:pre runs linting and type-checking before Vite compilation, and build invokes electron-builder to generate installers.
  • Cross-platform support: Single commands (build:mac, build:win, build:linux, build:all) generate native packages for all supported operating systems using the unified configuration.

Frequently Asked Questions

How does TUUI handle environment variables during the build?

TUUI merges environment variables from .env files using Vite's loadEnv function inside vite.config.mts. This exposes variables prefixed with VITE_ to the renderer process via import.meta.env, while the main process accesses them through process.env. Both processes share the same values because the configuration merges loadEnv output with process.env before defining the Electron builds.

What is the difference between build:pre and build scripts?

The build:pre script performs quality assurance and compilation steps without creating installers. It runs ESLint/Prettier fixes, executes vue-tsc --noEmit for TypeScript type checking, and invokes vite build to output the dist/ directory. The build script executes build:pre first, then invokes electron-builder with buildAssets/builder/config.js to generate the actual platform-specific installers and packages.

How are the main and preload processes bundled separately from the renderer?

Inside vite.config.mts, the ElectronPlugin accepts an array of ElectronOptions objects that define distinct Vite builds for each process. The main process entry point (src/main/index.ts) builds to dist/main/ with electron and Node.js built-in modules marked as external. The preload script (src/preload/index.ts) builds to dist/preload/. These run as separate Rollup builds alongside the standard Vite dev server for the renderer (src/renderer/).

Can I build for macOS on a Windows machine or vice versa?

electron-builder supports cross-platform compilation with limitations. You can build for Windows and Linux on any platform, but macOS targets (DMG, PKG) require a macOS host or specific cross-compilation tools. The build:all script attempts to build for all platforms (-wml flag), but in practice, macOS builds typically require build:mac to run on macOS hardware or CI runners with the appropriate Xcode toolchain.

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 →