#!/usr/bin/env bun import plugin from 'bun-plugin-tailwind'; import { config as dotenv } from 'dotenv'; import { existsSync } from 'fs'; import { rm } from 'fs/promises'; import path from 'path'; import { fileURLToPath } from 'url'; import { buildConfig } from './helpers'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const envPath = path.resolve(__dirname, '../../.env'); dotenv({ path: envPath }); 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, (_, c: string) => c.toUpperCase()); const parseValue = (value: string): unknown => { 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 [parentKey, childKey] = key.split('.'); if (parentKey && childKey) { config[parentKey] = config[parentKey] || {}; (config[parentKey] as Record)[childKey] = parseValue(value); } } else { config[key] = parseValue(value); } } return config as Partial; } const formatFileSize = (bytes: number): string => { const units = ['B', 'KB', 'MB', 'GB']; let size = bytes; let unitIndex = 0; while (size >= 1024 && unitIndex < units.length - 1) { size /= 1024; unitIndex++; } return `${size.toFixed(2)} ${units[unitIndex]}`; }; 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 }); } const start = performance.now(); const configPath = 'src/workspaces/config/src/index.ts'; if (existsSync(configPath)) { console.log('šŸ”§ Generating config with environment values...'); buildConfig(configPath, 'officer-web'); } const entrypoints = [...new Bun.Glob('**.html').scanSync('src/apps/officer-web')] .map((a) => path.resolve('src/apps/officer-web', a)) .filter((dir) => !dir.includes('node_modules')); console.log(`šŸ“„ Found ${entrypoints.length} HTML ${entrypoints.length === 1 ? 'file' : 'files'} to process\n`); const result = await Bun.build({ entrypoints, outdir, plugins: [plugin], minify: true, target: 'browser', sourcemap: 'linked', define: { 'process.env.NODE_ENV': JSON.stringify('production'), }, ...cliConfig, }); const end = performance.now(); const outputTable = result.outputs.map((output) => ({ File: path.relative(process.cwd(), output.path), Type: output.kind, Size: formatFileSize(output.size), })); console.table(outputTable); const buildTime = (end - start).toFixed(2); console.log(`\nāœ… Build completed in ${buildTime}ms\n`);