a reactive (signals based) hypermedia web framework (wip) stormlightlabs.github.io/volt/
hypermedia frontend signals
at main 3.5 kB view raw
1#!/usr/bin/env node 2 3/** 4 * Post-build script to finalize the distribution package: 5 * 1. Copy index.d.ts to voltx.d.ts for cleaner imports 6 * 2. Compress voltx.min.js to voltx.min.js.gz 7 * 3. Clean up unwanted files (chunks, assets) 8 */ 9import { writeFileSync } from "node:fs"; 10import { copyFile, readdir, readFile, unlink, writeFile } from "node:fs/promises"; 11import path from "node:path"; 12import { fileURLToPath } from "node:url"; 13import { createGzip } from "node:zlib"; 14import { minify as terserMinify } from "terser"; 15 16async function doGzip(gzPath, input) { 17 return new Promise(resolve => { 18 const gzip = createGzip({ level: 9 }); 19 const output = []; 20 gzip.on("data", (chunk) => output.push(chunk)); 21 gzip.on("end", () => { 22 writeFileSync(gzPath, Buffer.concat(output)); 23 const sizes = { 24 original: (input.length / 1024).toFixed(2), 25 compressed: (Buffer.concat(output).length / 1024).toFixed(2), 26 }; 27 console.log(`✓ Compressed voltx.min.js: ${sizes.original}KB → ${sizes.compressed}KB (gzip)`); 28 resolve(void 0); 29 }); 30 31 gzip.write(input); 32 gzip.end(); 33 }); 34} 35 36async function minifyJS(code) { 37 const result = await terserMinify(code, { 38 compress: { 39 dead_code: true, 40 drop_debugger: true, 41 conditionals: true, 42 evaluate: true, 43 booleans: true, 44 loops: true, 45 unused: true, 46 hoist_funs: true, 47 keep_fargs: false, 48 hoist_vars: false, 49 if_return: true, 50 join_vars: true, 51 side_effects: true, 52 }, 53 mangle: { toplevel: true }, 54 format: { comments: false }, 55 }); 56 57 if (!result.code) { 58 throw new Error("Minification failed - no output generated"); 59 } 60 61 return result.code; 62} 63 64async function main() { 65 const __dirname = path.dirname(fileURLToPath(import.meta.url)); 66 const distDir = path.resolve(__dirname, "../dist"); 67 68 console.log("Finalizing build...\n"); 69 70 try { 71 const indexDts = path.join(distDir, "index.d.ts"); 72 const voltxDts = path.join(distDir, "voltx.d.ts"); 73 await copyFile(indexDts, voltxDts); 74 console.log("✓ Copied index.d.ts → voltx.d.ts"); 75 } catch (error) { 76 if (error instanceof Error) { 77 console.error("✗ Failed to copy type definitions:", error.message); 78 } 79 process.exit(1); 80 } 81 82 try { 83 const minJsPath = path.join(distDir, "voltx.min.js"); 84 const gzPath = path.join(distDir, "voltx.min.js.gz"); 85 const input = await readFile(minJsPath); 86 const minified = await minifyJS(input.toString()); 87 88 await writeFile(minJsPath, minified); 89 await doGzip(gzPath, minified); 90 } catch (error) { 91 if (error instanceof Error) { 92 console.error("✗ Failed to compress voltx.min.js:", error.message); 93 } 94 process.exit(1); 95 } 96 97 try { 98 const files = await readdir(distDir); 99 const unwantedPatterns = [/\.svg$/, /\.png$/, /\.jpg$/]; 100 let cleanedCount = 0; 101 for (const file of files) { 102 const shouldDelete = unwantedPatterns.some((pattern) => pattern.test(file)); 103 if (shouldDelete) { 104 await unlink(path.join(distDir, file)); 105 console.log(`✓ Removed unwanted file: ${file}`); 106 cleanedCount++; 107 } 108 } 109 110 if (cleanedCount === 0) { 111 console.log("✓ No unwanted files to clean"); 112 } 113 } catch (error) { 114 if (error instanceof Error) { 115 console.error("✗ Failed to clean unwanted files:", error.message); 116 } 117 process.exit(1); 118 } 119 120 console.log("\nBuild finalization complete!"); 121 process.exit(0); 122} 123 124main();