#!/usr/bin/env bun import plugin from 'bun-plugin-tailwind' import { existsSync } from 'fs' import { rm } from 'fs/promises' import path from 'path' if (process.argv.includes('--help') || process.argv.includes('-h')) { console.log(` šŸ—ļø Bun Build Script Usage: bun run build.ts [options] Common Options: --outdir Output directory (default: "dist") --minify Enable minification (or --minify.whitespace, --minify.syntax, etc) --sourcemap Sourcemap type: none|linked|inline|external --target Build target: browser|bun|node --format Output format: esm|cjs|iife --splitting Enable code splitting --packages Package handling: bundle|external --public-path Public path for assets --env Environment handling: inline|disable|prefix* --conditions Package.json export conditions (comma separated) --external External packages (comma separated) --banner Add banner text to output --footer Add footer text to output --define Define global constants (e.g. --define.VERSION=1.0.0) --help, -h Show this help message Example: bun run build.ts --outdir=dist --minify --sourcemap=linked --external=react,react-dom `) process.exit(0) } const toCamelCase = (str: string): string => str.replace(/-([a-z])/g, (_, letter: string) => letter.toUpperCase()) const parseValue = (value: string): any => { if (value === 'true') return true if (value === 'false') return false if (/^\d+$/.test(value)) return parseInt(value, 10) if (/^\d*\.\d+$/.test(value)) return parseFloat(value) if (value.includes(',')) return value.split(',').map((v) => v.trim()) return value } function parseArgs(): Partial { const config: Record = {} const args = process.argv.slice(2) for (let i = 0; i < args.length; i++) { const arg = args[i] if (arg === undefined) continue if (!arg.startsWith('--')) continue if (arg.startsWith('--no-')) { const key = toCamelCase(arg.slice(5)) config[key] = false continue } if (!arg.includes('=') && (i === args.length - 1 || args[i + 1]?.startsWith('--'))) { const key = toCamelCase(arg.slice(2)) config[key] = true continue } let key: string let value: string if (arg.includes('=')) { ;[key, value] = arg.slice(2).split('=', 2) as [string, string] } else { key = arg.slice(2) value = args[++i] ?? '' } key = toCamelCase(key) if (key.includes('.')) { const parts = key.split('.') if (parts.length > 2) { console.warn( `Warning: Deeply nested option "${key}" is not supported. Only single-level nesting (e.g., --minify.whitespace) is allowed.`, ) continue } const parentKey = parts[0]! const childKey = parts[1]! const existing = config[parentKey] if (typeof existing !== 'object' || existing === null || Array.isArray(existing)) { config[parentKey] = {} } ;(config[parentKey] as Record)[childKey] = parseValue(value) } else { config[key] = parseValue(value) } } return config as Partial } console.log('\nšŸš€ Starting build process...\n') const cliConfig = parseArgs() const outdir = cliConfig.outdir || path.join(process.cwd(), 'dist') if (existsSync(outdir)) { console.log(`šŸ—‘ļø Cleaning previous build at ${outdir}`) await rm(outdir, { recursive: true, force: true }) } await Bun.build({ entrypoints: ['./src/ui/index.html'], target: 'browser', outdir: './dist', minify: true, plugins: [plugin], define: { 'process.env.NODE_ENV': JSON.stringify('production'), }, publicPath: '/', })