const fs = require('fs'); const path = require('path'); const esbuild = require('esbuild'); const JS = (...parts) => path.join(__dirname, 'js', ...parts); const DIST = path.join(__dirname, 'dist'); // Bundle definitions — files are concatenated in order, then minified const bundles = { 'core.js': [ JS('globals.js'), JS('skeleton-loader.js'), JS('theme.js'), JS('totp-auth.js'), JS('service-credentials.js'), JS('totp-settings.js'), JS('core', 'credentials.js'), JS('core', 'grid.js'), JS('core', 'dns.js'), JS('core', 'logs.js'), JS('core', 'service-modals.js'), JS('core', 'service-infrastructure.js'), JS('core', 'service-crud.js'), JS('core', 'service-create.js'), JS('live-events.js'), ], 'features.js': [ JS('logo-customization.js'), JS('setup-wizard.js'), JS('app-selector.js'), JS('recipes.js'), JS('import-export.js'), JS('error-logs.js'), JS('smart-arr-connect.js'), JS('notification-settings.js'), JS('panel-tabs.js'), JS('backup-restore.js'), JS('resource-monitor.js'), JS('health-check.js'), JS('update-management.js'), JS('docker-resources.js'), JS('compose-import.js'), JS('container-exec.js'), JS('audit-log.js'), JS('weather.js'), JS('clock.js'), JS('card-badges.js'), JS('theme-builder.js'), JS('license.js'), ], 'onboarding.js': [ JS('driver.min.js'), JS('error-handler.js'), JS('progress-tracker.js'), JS('theme-adapter.js'), JS('tooltip-definitions.js'), JS('dns-template-selector.js'), JS('tour-manager.js'), JS('onboarding.js'), ], 'init.js': [ JS('core', 'init.js'), JS('keyboard-shortcuts.js'), ], }; async function build() { // Ensure dist/ exists if (!fs.existsSync(DIST)) fs.mkdirSync(DIST); const results = {}; for (const [outName, files] of Object.entries(bundles)) { // Read and concatenate const parts = []; for (const file of files) { if (!fs.existsSync(file)) { console.warn(` WARN: ${path.relative(__dirname, file)} not found, skipping`); continue; } parts.push(fs.readFileSync(file, 'utf8')); } const concatenated = parts.join(';\n'); // Minify with esbuild (safe to re-minify already-minified code like driver.min.js) const { code } = await esbuild.transform(concatenated, { minify: true, target: 'es2020', }); const outPath = path.join(DIST, outName); fs.writeFileSync(outPath, code); const rawSize = (Buffer.byteLength(concatenated) / 1024).toFixed(1); const minSize = (Buffer.byteLength(code) / 1024).toFixed(1); results[outName] = { rawSize, minSize, fileCount: files.length }; } // Summary console.log('\n DashCaddy Frontend Build\n'); console.log(' Bundle Files Raw Min'); console.log(' ─────────────────────────────────────────'); let totalRaw = 0, totalMin = 0; for (const [name, r] of Object.entries(results)) { console.log(` ${name.padEnd(18)} ${String(r.fileCount).padStart(3)} ${r.rawSize.padStart(6)} KB ${r.minSize.padStart(6)} KB`); totalRaw += parseFloat(r.rawSize); totalMin += parseFloat(r.minSize); } console.log(' ─────────────────────────────────────────'); console.log(` ${'Total'.padEnd(18)} ${totalRaw.toFixed(1).padStart(6)} KB ${totalMin.toFixed(1).padStart(6)} KB`); console.log(`\n Output: ${DIST}\n`); } // Watch mode if (process.argv.includes('--watch')) { console.log(' Watching for changes...\n'); build(); const jsDir = path.join(__dirname, 'js'); let debounce = null; fs.watch(jsDir, { recursive: true }, (event, filename) => { if (!filename || !filename.endsWith('.js')) return; clearTimeout(debounce); debounce = setTimeout(() => { console.log(` Changed: ${filename}`); build(); }, 200); }); } else { build(); }