/** * Pack the browser extension into a zip (Chrome) and .xpi (Firefox). * Zip contains manifest.json (Chrome, with "key"). * XPI contains manifest_firefox.json as manifest.json (no "key"). * Output: releases/BridgeSwarm-.zip, releases/BridgeSwarm-.xpi */ const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); const archiver = require('archiver'); const REPO_ROOT = path.resolve(__dirname, '..'); const EXT_DIR = path.join(REPO_ROOT, 'extension'); const RELEASES_DIR = path.join(REPO_ROOT, 'releases'); const EXCLUDE_NAMES = new Set(['protomux-entry.cjs']); const EXCLUDE_EXTS = new Set(['.map']); function getVersion() { const manifestPath = path.join(EXT_DIR, 'manifest.json'); const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')); if (!manifest.version) throw new Error('extension/manifest.json missing version'); return manifest.version; } function listExtensionFiles(dir, base = dir) { const entries = fs.readdirSync(dir, { withFileTypes: true }); const files = []; for (const e of entries) { const full = path.join(dir, e.name); const rel = path.relative(base, full).replace(/\\/g, '/'); if (e.isDirectory()) { files.push(...listExtensionFiles(full, base)); } else { const ext = path.extname(e.name); if (EXCLUDE_NAMES.has(e.name) || EXCLUDE_EXTS.has(ext)) continue; files.push({ full, rel }); } } return files; } function createZip(version) { const zipPath = path.join(RELEASES_DIR, `BridgeSwarm-${version}.zip`); const output = fs.createWriteStream(zipPath); const archive = archiver('zip', { zlib: { level: 9 } }); return new Promise((resolve, reject) => { output.on('close', () => resolve(zipPath)); archive.on('error', reject); archive.pipe(output); const files = listExtensionFiles(EXT_DIR).filter(({ rel }) => rel !== 'manifest_firefox.json'); for (const { full, rel } of files) { archive.file(full, { name: rel }); } archive.finalize(); }); } function createXpi(version) { const firefoxManifestPath = path.join(EXT_DIR, 'manifest_firefox.json'); if (!fs.existsSync(firefoxManifestPath)) { throw new Error('extension/manifest_firefox.json missing — run scripts/generate-extension-id.js'); } const xpiPath = path.join(RELEASES_DIR, `BridgeSwarm-${version}.xpi`); const output = fs.createWriteStream(xpiPath); const archive = archiver('zip', { zlib: { level: 9 } }); return new Promise((resolve, reject) => { output.on('close', () => resolve(xpiPath)); archive.on('error', reject); archive.pipe(output); const files = listExtensionFiles(EXT_DIR).filter( ({ rel }) => rel !== 'manifest.json' && rel !== 'manifest_firefox.json' ); for (const { full, rel } of files) { archive.file(full, { name: rel }); } archive.file(firefoxManifestPath, { name: 'manifest.json' }); archive.finalize(); }); } async function main() { const version = getVersion(); console.log('BridgeSwarm version from manifest:', version); console.log('Syncing examples into extension...'); execSync('node scripts/sync-examples.js', { cwd: REPO_ROOT, stdio: 'inherit' }); console.log('Running npm run build:protomux...'); execSync('npm run build:protomux', { cwd: REPO_ROOT, stdio: 'inherit' }); if (!fs.existsSync(RELEASES_DIR)) { fs.mkdirSync(RELEASES_DIR, { recursive: true }); console.log('Created releases/'); } try { console.log('Creating Chrome zip...'); const zipPath = await createZip(version); console.log('Written:', zipPath); console.log('Creating Firefox xpi...'); const xpiPath = await createXpi(version); console.log('Written:', xpiPath); } catch (err) { console.error(err); process.exit(1); } } main();