From b87937e0762c36eb562bdb49c4b060aeaa5ba470 Mon Sep 17 00:00:00 2001 From: Philip Zeyliger Date: Wed, 14 Jan 2026 09:28:19 -0800 Subject: [PATCH] ui: make build output quieter by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prompt: That is so noisy! Can we make the UI build way quieter by default? Fine to have a verbose env variable or something. All I want is to see that it built and maybe how long it took. Show just a single summary line with timing and gzip size. Set VERBOSE=1 for detailed per-file breakdown. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- Makefile | 5 +--- ui/scripts/build.js | 67 ++++++++++++++++++++++++++++----------------- 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/Makefile b/Makefile index e24f82e79c7eb237f63a18ff5ab42522e712e6f6..4fbed78c078469ff8918d2b5ca6720852273b1ca 100644 --- a/Makefile +++ b/Makefile @@ -7,10 +7,8 @@ all: build # Build templates into tarballs templates: - @echo "Building template tarballs..." @for dir in templates/*/; do \ name=$$(basename "$$dir"); \ - echo " Creating $$name.tar.gz..."; \ tar -czf "templates/$$name.tar.gz" -C "templates/$$name" --exclude='.DS_Store' .; \ done @@ -42,8 +40,7 @@ build-linux-x86: ui templates # Build UI ui: - @echo "Building UI..." - cd ui && pnpm install --frozen-lockfile && pnpm run build + @cd ui && pnpm install --frozen-lockfile --silent && pnpm run --silent build # Run Go tests test-go: ui diff --git a/ui/scripts/build.js b/ui/scripts/build.js index a48cd79936ffeda3069ffa1423822fc4a3ad2585..8ea057f7c6759d25bf085fe29c888b2db4cb71c4 100644 --- a/ui/scripts/build.js +++ b/ui/scripts/build.js @@ -5,8 +5,14 @@ import * as crypto from 'crypto'; const isWatch = process.argv.includes('--watch'); const isProd = !isWatch; +const verbose = process.env.VERBOSE === '1' || process.env.VERBOSE === 'true'; + +function log(...args) { + if (verbose) console.log(...args); +} async function build() { + const startTime = Date.now(); try { // Ensure dist directory exists if (!fs.existsSync('dist')) { @@ -14,7 +20,7 @@ async function build() { } // Build Monaco editor worker separately (IIFE format for web worker) - console.log('Building Monaco editor worker...'); + log('Building Monaco editor worker...'); await esbuild.build({ entryPoints: ['node_modules/monaco-editor/esm/vs/editor/editor.worker.js'], bundle: true, @@ -25,7 +31,7 @@ async function build() { }); // Build Monaco editor as a separate chunk (JS + CSS) - console.log('Building Monaco editor bundle...'); + log('Building Monaco editor bundle...'); await esbuild.build({ entryPoints: ['node_modules/monaco-editor/esm/vs/editor/editor.main.js'], bundle: true, @@ -39,7 +45,7 @@ async function build() { }); // Build main app - exclude monaco-editor, we'll load it dynamically - console.log('Building main application...'); + log('Building main application...'); const result = await esbuild.build({ entryPoints: ['src/main.tsx'], bundle: true, @@ -73,14 +79,14 @@ async function build() { }; fs.writeFileSync('dist/build-info.json', JSON.stringify(buildInfo, null, 2)); - console.log('Build complete!'); - // Generate gzip versions of large files and remove originals to reduce binary size // The server will decompress on-the-fly for the rare clients that don't support gzip - console.log('\nGenerating gzip compressed files...'); + log('\nGenerating gzip compressed files...'); const filesToCompress = ['monaco-editor.js', 'editor.worker.js', 'main.js', 'monaco-editor.css', 'styles.css']; const checksums = {}; - + let totalOrigSize = 0; + let totalGzSize = 0; + for (const file of filesToCompress) { const inputPath = `dist/${file}`; const outputPath = `dist/${file}.gz`; @@ -88,34 +94,45 @@ async function build() { const input = fs.readFileSync(inputPath); const compressed = zlib.gzipSync(input, { level: 9 }); fs.writeFileSync(outputPath, compressed); - + // Compute SHA256 of the compressed content for ETag const hash = crypto.createHash('sha256').update(compressed).digest('hex').slice(0, 16); checksums[file] = hash; - - const origKb = (input.length / 1024).toFixed(1); - const gzKb = (compressed.length / 1024).toFixed(1); - const ratio = ((compressed.length / input.length) * 100).toFixed(0); - console.log(` ${file}: ${origKb} KB -> ${gzKb} KB gzip (${ratio}%) [${hash}]`); - + + totalOrigSize += input.length; + totalGzSize += compressed.length; + + if (verbose) { + const origKb = (input.length / 1024).toFixed(1); + const gzKb = (compressed.length / 1024).toFixed(1); + const ratio = ((compressed.length / input.length) * 100).toFixed(0); + console.log(` ${file}: ${origKb} KB -> ${gzKb} KB gzip (${ratio}%) [${hash}]`); + } + // Remove original to save space in embedded binary fs.unlinkSync(inputPath); } } - + // Write checksums for ETag support fs.writeFileSync('dist/checksums.json', JSON.stringify(checksums, null, 2)); - console.log('\nChecksums written to dist/checksums.json'); - - console.log('\nOther files:'); - const otherFiles = fs.readdirSync('dist').filter(f => - (f.endsWith('.ttf') || f.endsWith('.map')) && !f.endsWith('.gz') - ); - for (const file of otherFiles.sort()) { - const stats = fs.statSync(`dist/${file}`); - const sizeKb = (stats.size / 1024).toFixed(1); - console.log(` ${file}: ${sizeKb} KB`); + log('\nChecksums written to dist/checksums.json'); + + if (verbose) { + console.log('\nOther files:'); + const otherFiles = fs.readdirSync('dist').filter(f => + (f.endsWith('.ttf') || f.endsWith('.map')) && !f.endsWith('.gz') + ); + for (const file of otherFiles.sort()) { + const stats = fs.statSync(`dist/${file}`); + const sizeKb = (stats.size / 1024).toFixed(1); + console.log(` ${file}: ${sizeKb} KB`); + } } + + const elapsed = ((Date.now() - startTime) / 1000).toFixed(1); + const totalGzKb = (totalGzSize / 1024).toFixed(0); + console.log(`UI built in ${elapsed}s (${totalGzKb} KB gzipped)`); } catch (error) { console.error('Build failed:', error); process.exit(1);