import { execSync } from "child_process"; import fs from "fs"; import path from "path"; import { minify } from "terser"; import url from "url"; import syncVersion from "./syncVersion.js"; const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); const frontendDir = path.join(__dirname, "frontend"); const distDir = path.join(frontendDir, "dist"); const srcDir = path.join(__dirname, "src"); const iconDir = path.join(__dirname, "icons"); const releaseDir = path.join(__dirname, "extension-release"); const run = (cmd, cwd) => { console.log(`๐Ÿ› ๏ธ Running: ${cmd}`); execSync(cmd, { cwd, stdio: "inherit" }); }; const copyDir = async (src, dest) => { await fs.promises.mkdir(dest, { recursive: true }); await fs.promises.cp(src, dest, { recursive: true, force: true, filter: (src) => !src.endsWith(".map"), }); }; const minifyJS = async (jsContent) => { try { const result = await minify(jsContent, { compress: { drop_console: false, // Keep console logs for debugging drop_debugger: true, pure_funcs: ["console.debug"], }, mangle: { reserved: ["chrome"], // Don't mangle chrome API }, }); return result.code; } catch (error) { console.warn("โš ๏ธ Minification failed, using original:", error.message); return jsContent; } }; // Copy and minify JavaScript files from src directory const copyAndMinifySrcFiles = async (src, dest) => { await fs.promises.mkdir(dest, { recursive: true }); const entries = await fs.promises.readdir(src, { withFileTypes: true }); for (const entry of entries) { const srcPath = path.join(src, entry.name); const destPath = path.join(dest, entry.name); if (entry.isDirectory()) { await copyAndMinifySrcFiles(srcPath, destPath); } else if (entry.name.endsWith(".js")) { // Minify JavaScript files console.log(`๐Ÿ—œ๏ธ Minifying ${entry.name}...`); const content = await fs.promises.readFile(srcPath, "utf8"); const originalSize = Buffer.byteLength(content, "utf8"); const minified = await minifyJS(content, entry.name); const minifiedSize = Buffer.byteLength(minified, "utf8"); const savings = (((originalSize - minifiedSize) / originalSize) * 100).toFixed(1); console.log( ` ๐Ÿ“Š ${entry.name}: ${originalSize} โ†’ ${minifiedSize} bytes (${savings}% smaller)` ); await fs.promises.writeFile(destPath, minified, "utf8"); } else { // Copy other files as-is await fs.promises.copyFile(srcPath, destPath); } } }; const main = async () => { await syncVersion(); console.log("๐Ÿš€ Starting extension build..."); // 1. Install frontend deps if needed if (!fs.existsSync(path.join(frontendDir, "node_modules"))) { console.log("๐Ÿ“ฆ node_modules not found. Running npm install..."); run("npm install", frontendDir); } // 2. Build frontend console.log("๐Ÿ“ฆ Building frontend..."); run("npm run build", frontendDir); // 3. Clean release folder if (fs.existsSync(releaseDir)) { console.log("๐Ÿงน Cleaning existing extension-release folder..."); await fs.promises.rm(releaseDir, { recursive: true, force: true }); } await fs.promises.mkdir(releaseDir); // 4. Copy and minify src files console.log("๐Ÿ“ฆ Copying and minifying src files..."); await copyAndMinifySrcFiles(srcDir, releaseDir); // 5. Copy frontend dist files to release (merged at root) console.log("๐Ÿ“ฆ Copying frontend dist files to extension-release..."); await copyDir(distDir, releaseDir); // 6. Copy icon directory to release (merged at root) console.log("๐Ÿ“ฆ Copying icon directory to extension-release..."); await copyDir(iconDir, path.join(releaseDir, "icons")); console.log("โœ… Build complete! extension-release ready."); }; main().catch((e) => { console.error("โŒ Build failed:", e); process.exit(1); });