Skip to main content

Overview

T3 Code uses Turbo for orchestrated builds across the monorepo. The build process produces optimized bundles for web, server, and desktop distributions.

Quick Build Commands

# Build all packages
bun run build

Build Process

Build Order

Turbo builds packages in dependency order:
1

Packages (Parallel)

  • packages/contracts (Effect/Schema → ESM/CJS)
  • packages/shared (Runtime utilities)
2

Apps (Sequential)

  • apps/web (React + Vite → static assets)
  • apps/server (Node.js → bundled ESM)
3

Desktop (Final)

  • apps/desktop (Electron → platform binaries)

Build Outputs

PackageOutput DirectoryFormat
contractsdist/ESM + CJS + types
webdist/Static HTML/JS/CSS
serverdist/Bundled ESM (index.mjs)
desktopdist-electron/Electron bundles

Building Components

Contracts Package

Builds Effect/Schema definitions to distributable format:
cd packages/contracts
bun run build
Build Tool: tsdown (TypeScript bundler) Output:
  • dist/index.mjs - ESM bundle
  • dist/index.cjs - CommonJS bundle
  • dist/index.d.ts - TypeScript declarations
Configuration:
package.json
{
  "scripts": {
    "build": "tsdown src/index.ts --format esm,cjs --dts --clean"
  }
}

Web App

Builds React application to static assets:
cd apps/web
bun run build
Build Tool: Vite 8 Output: dist/ directory with:
  • index.html - HTML entry point
  • assets/*.js - Code-split JavaScript bundles
  • assets/*.css - Extracted stylesheets
  • assets/* - Images, fonts, other assets
Optimizations:
  • React Compiler (automatic memoization)
  • Code splitting (route-based)
  • Tree shaking (unused code elimination)
  • Minification (Terser)
  • CSS optimization (Lightning CSS)
Configuration:
vite.config.ts
import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    react({
      babel: {
        plugins: [['babel-plugin-react-compiler', {}]],
      },
    }),
  ],
  build: {
    outDir: 'dist',
    sourcemap: true,
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          router: ['@tanstack/react-router'],
        },
      },
    },
  },
});

Server Package

Builds Node.js backend to bundled ESM:
cd apps/server
bun run build
Build Tool: Custom bundler (scripts/cli.ts build) Output: dist/index.mjs (single bundle including web assets) Process:
  1. Builds web app (if not already built)
  2. Bundles server code with tsdown
  3. Embeds web assets in server bundle
  4. Generates CLI entry point
Configuration:
scripts/cli.ts
// Custom build script
// - Bundles TypeScript to ESM
// - Includes web dist as static assets
// - Generates shebang for CLI execution
The server build includes the web app for production serving.

Desktop App

Builds Electron application:
cd apps/desktop
bun run build
Build Tool: tsdown + Electron Output: dist-electron/ directory with:
  • main.js - Electron main process
  • preload.js - Preload scripts
For distribution, see Desktop Distribution below.

Production Build

Full Production Build

Build everything for production:
# Clean previous builds (optional)
bun run clean

# Install dependencies
bun install

# Run full build
bun run build

# Verify with typecheck
bun run typecheck

# Run tests
bun run test

Start Production Server

Run the built server:
bun run start
This runs node dist/index.mjs which:
  • Starts WebSocket server
  • Serves built web app as static files
  • Opens browser to the app
Production Configuration:
# Custom port
T3CODE_PORT=8080 bun run start

# Custom state directory
bun run start -- --state-dir ~/.t3/prod

# Don't open browser
T3CODE_NO_BROWSER=1 bun run start

Desktop Distribution

Build Desktop Artifacts

Create platform-specific installers:
# ARM64 .dmg (default)
bun run dist:desktop:dmg

# Intel .dmg
bun run dist:desktop:dmg:x64

# Specific architecture
bun run dist:desktop:dmg:arm64
Output: release/T3 Code (Alpha)-0.0.3-arm64.dmg

Custom Platform/Architecture

Build for specific targets:
bun run dist:desktop:artifact -- \
  --platform mac \
  --target dmg \
  --arch arm64
Available Options:
FlagValuesDescription
--platformmac, linux, winTarget platform
--targetdmg, AppImage, nsisInstaller format
--archarm64, x64CPU architecture

DMG Packaging Options

For debugging package contents:
bun run dist:desktop:dmg -- --keep-stage
Staging files remain in .stage/ directory.
Enable code signing (requires certificates in CI/secrets):
bun run dist:desktop:dmg -- --signed
macOS: Uses Apple Developer IDWindows: Uses Azure Trusted Signing with these environment variables:
  • AZURE_TRUSTED_SIGNING_ENDPOINT
  • AZURE_TRUSTED_SIGNING_ACCOUNT_NAME
  • AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME
  • AZURE_TRUSTED_SIGNING_PUBLISHER_NAME
  • AZURE_TENANT_ID
  • AZURE_CLIENT_ID
  • AZURE_CLIENT_SECRET

Desktop Package Contents

The desktop package includes:
  • Electron shell (dist-electron/)
  • Web UI (bundled from apps/web/dist)
  • Backend server (apps/server/dist as t3 CLI)
  • App icon (from assets/macos-icon-1024.png)
How it works:
apps/desktop/src/main/backend.ts
// Spawns bundled t3 CLI on loopback with auth token
const backend = spawn(t3BinaryPath, [
  '--auth-token', token,
  '--no-browser',
]);

// UI connects via WebSocket
const wsUrl = `ws://127.0.0.1:${port}?token=${token}`;

Build Optimization

Turbo Caching

Turbo caches build outputs for faster rebuilds:
# Check cache status
turbo run build --dry-run

# Force rebuild (skip cache)
turbo run build --force

# Clear cache
rm -rf .turbo
Cache Configuration:
turbo.json
{
  "tasks": {
    "build": {
      "outputs": ["dist/**", "dist-electron/**"],
      "dependsOn": ["^build"]
    }
  }
}

Parallel Builds

Turbo runs independent builds in parallel:
# Build with concurrency limit
turbo run build --concurrency=2

# Build specific packages
turbo run build --filter=@t3tools/web --filter=@t3tools/server

Incremental Builds

Development builds support incremental compilation:
# Watch mode (incremental)
cd packages/contracts
bun run dev  # tsdown --watch

CI/CD Integration

GitHub Actions Example

.github/workflows/build.yml
name: Build

on:
  push:
    branches: [main]
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - uses: oven-sh/setup-bun@v1
        with:
          bun-version: 1.3.9
      
      - name: Install dependencies
        run: bun install
      
      - name: Build
        run: bun run build
      
      - name: Typecheck
        run: bun run typecheck
      
      - name: Test
        run: bun run test
      
      - name: Upload server artifact
        uses: actions/upload-artifact@v3
        with:
          name: t3-server
          path: apps/server/dist/

Desktop Release Workflow

.github/workflows/release.yml
name: Release

on:
  push:
    tags:
      - 'v*'

jobs:
  build-desktop:
    strategy:
      matrix:
        os: [macos-latest, ubuntu-latest, windows-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v1
      
      - name: Build desktop
        run: |
          bun install
          bun run build:desktop
      
      - name: Create installer (macOS)
        if: matrix.os == 'macos-latest'
        run: bun run dist:desktop:dmg -- --signed
        env:
          APPLE_ID: ${{ secrets.APPLE_ID }}
          APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
      
      - name: Upload to release
        uses: actions/upload-release-asset@v1
        with:
          upload_url: ${{ github.event.release.upload_url }}
          asset_path: ./release/*

Troubleshooting

Ensure all dependencies are installed:
bun run clean
bun install
bun run build
Clear the Turbo cache:
rm -rf .turbo apps/*/.turbo packages/*/.turbo
bun run build --force
Check that you’ve built the dependencies first:
bun run build:contracts
bun run build:desktop
Force rebuild the web app:
cd apps/web
rm -rf dist
bun run build

Pre-Commit Checks

Both bun lint and bun typecheck must pass before tasks are considered complete.
Run all checks before committing:
# Lint
bun run lint

# Type check
bun run typecheck

# Test
bun run test

# Build
bun run build

Automated Checks

Add to .husky/pre-commit:
#!/bin/sh
bun run lint
bun run typecheck

Next Steps

Testing

Learn about testing strategies

Setup

Review development setup

Build docs developers (and LLMs) love