a reactive (signals based) hypermedia web framework (wip) stormlightlabs.github.io/volt/
hypermedia frontend signals
at main 4.2 kB view raw
1import { echo } from "$console/echo.js"; 2import { findMonorepoRoot, getLibPath } from "$utils/paths.js"; 3import { existsSync } from "node:fs"; 4import { mkdir, readFile, writeFile } from "node:fs/promises"; 5import path from "node:path"; 6import { minify as terserMinify } from "terser"; 7 8export type BuildArtifacts = { jsPath: string; cssPath: string }; 9 10export type BuildOptions = { outDir: string; minify?: boolean; includeCss?: boolean }; 11 12/** 13 * Build the library using pnpm workspace commands. 14 * 15 * Runs `pnpm --filter volt build:lib` in the monorepo root to compile the Volt.js library into lib/dist/. 16 */ 17export async function buildLibrary(): Promise<void> { 18 const { execSync } = await import("node:child_process"); 19 const monorepoRoot = await findMonorepoRoot(); 20 21 try { 22 execSync("pnpm --filter volt build:lib", { cwd: monorepoRoot, stdio: "inherit" }); 23 } catch { 24 throw new Error("Library build failed. Make sure Vite is configured correctly."); 25 } 26} 27 28/** 29 * Find the library build artifacts in lib/dist/. 30 * 31 * Locates the compiled JavaScript and base CSS files after a successful build. 32 */ 33export async function findBuildArtifacts(): Promise<BuildArtifacts> { 34 const libPath = await getLibPath(); 35 const distDir = path.join(libPath, "dist"); 36 const jsPath = path.join(distDir, "volt.js"); 37 const cssPath = path.join(libPath, "src", "styles", "base.css"); 38 39 if (!existsSync(jsPath)) { 40 throw new Error(`Library JS not found at ${jsPath}. Build may have failed.`); 41 } 42 43 if (!existsSync(cssPath)) { 44 throw new Error(`Base CSS not found at ${cssPath}.`); 45 } 46 47 return { jsPath, cssPath }; 48} 49 50/** 51 * Minify JavaScript code using Terser. 52 * 53 * Applies aggressive compression and mangling to reduce bundle size. 54 */ 55export async function minifyJS(code: string): Promise<string> { 56 const result = await terserMinify(code, { 57 compress: { 58 dead_code: true, 59 drop_debugger: true, 60 conditionals: true, 61 evaluate: true, 62 booleans: true, 63 loops: true, 64 unused: true, 65 hoist_funs: true, 66 keep_fargs: false, 67 hoist_vars: false, 68 if_return: true, 69 join_vars: true, 70 side_effects: true, 71 }, 72 mangle: { toplevel: true }, 73 format: { comments: false }, 74 }); 75 76 if (!result.code) { 77 throw new Error("Minification failed - no output generated"); 78 } 79 80 return result.code; 81} 82 83/** 84 * Minify CSS code using regex-based compression. 85 * 86 * Removes comments, normalizes whitespace, and strips spacing around CSS syntax characters. 87 */ 88export function minifyCSS(code: string): string { 89 return code.replaceAll(/\/\*[\s\S]*?\*\//g, "").replaceAll(/\s+/g, " ").replaceAll(/\s*([{}:;,])\s*/g, "$1").trim(); 90} 91 92/** 93 * Copy build artifacts to the specified output directory. 94 * 95 * Reads the built JavaScript and CSS files, optionally minifies them, and writes them to the target directory. 96 * Creates the directory if needed. 97 * The output file naming depends on whether minification is enabled. 98 */ 99export async function copyBuildArtifacts(artifacts: BuildArtifacts, options: BuildOptions): Promise<void> { 100 const { outDir, minify = true, includeCss = true } = options; 101 102 await mkdir(outDir, { recursive: true }); 103 104 const jsContent = await readFile(artifacts.jsPath, "utf8"); 105 const jsFilename = minify ? "volt.min.js" : "volt.js"; 106 let outputJS = jsContent; 107 108 if (minify) { 109 echo.info(" Minifying JavaScript..."); 110 outputJS = await minifyJS(jsContent); 111 } 112 113 const jsOutputPath = path.join(outDir, jsFilename); 114 await writeFile(jsOutputPath, outputJS, "utf8"); 115 const jsSize = Math.round(outputJS.length / 1024); 116 echo.ok(` Created: ${jsFilename} (${jsSize} KB)`); 117 118 if (includeCss) { 119 const cssContent = await readFile(artifacts.cssPath, "utf8"); 120 const cssFilename = minify ? "volt.min.css" : "volt.css"; 121 let outputCSS = cssContent; 122 123 if (minify) { 124 echo.info(" Minifying CSS..."); 125 outputCSS = minifyCSS(cssContent); 126 } 127 128 const cssOutputPath = path.join(outDir, cssFilename); 129 await writeFile(cssOutputPath, outputCSS, "utf8"); 130 const cssSize = Math.round(outputCSS.length / 1024); 131 echo.ok(` Created: ${cssFilename} (${cssSize} KB)`); 132 } 133}