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:
- Lint and format –
npm run format:fix - Type checking –
vue-tsc --noEmitvalidates TypeScript across the entire project - Vite production build –
vite buildoutputs todist/
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:mac–electron-builder --macnpm run build:win–electron-builder --winnpm run build:linux–electron-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.gzarchives
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.mtsfile 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.jsdefines platform targets (DMG, NSIS, DEB, etc.), icons, and packaging rules, reading the compileddist/directory created by Vite. - npm scripts orchestrate the pipeline:
devstarts hot-reload development,build:preruns linting and type-checking before Vite compilation, andbuildinvokes 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →