diff --git a/WebContainerGitService.js b/WebContainerGitService.js index 166b15f..fc10a0e 100644 --- a/WebContainerGitService.js +++ b/WebContainerGitService.js @@ -193,16 +193,18 @@ export class WebContainerGitService { async wipeDir(dir) { try { const entries = await this.fs.readdir(dir); - for (const entry of entries) { - const path = `${dir}/${entry}`; - const stat = await this.fs.stat(path); - if (stat.isDirectory()) { - await this.wipeDir(path); - await this.fs.rmdir(path); - } else { - await this.fs.unlink(path); - } - } + await Promise.all( + entries.map(async (entry) => { + const path = `${dir}/${entry}`; + const stat = await this.fs.stat(path); + if (stat.isDirectory()) { + await this.wipeDir(path); + await this.fs.rmdir(path); + } else { + await this.fs.unlink(path); + } + }) + ); } catch (e) { // Base directory likely doesn't exist yet } diff --git a/benchmark_wipe.js b/benchmark_wipe.js new file mode 100644 index 0000000..17aca32 --- /dev/null +++ b/benchmark_wipe.js @@ -0,0 +1,78 @@ +import fs from 'fs'; +import path from 'path'; + +async function generateDummyDir(dirPath, depth, filesPerDir) { + await fs.promises.mkdir(dirPath, { recursive: true }).catch(() => {}); + if (depth === 0) return; + + const promises = []; + for (let i = 0; i < filesPerDir; i++) { + const fileNum = i; + promises.push(fs.promises.writeFile(path.join(dirPath, `file_${fileNum}.txt`), `hello ${fileNum}`)); + } + for (let i = 0; i < filesPerDir; i++) { + const subDir = path.join(dirPath, `dir_${i}`); + promises.push(generateDummyDir(subDir, depth - 1, filesPerDir)); + } + await Promise.all(promises); +} + +// Current implementation +async function wipeDirOriginal(dir) { + try { + const entries = await fs.promises.readdir(dir); + for (const entry of entries) { + const p = path.join(dir, entry); + const stat = await fs.promises.stat(p); + if (stat.isDirectory()) { + await wipeDirOriginal(p); + await fs.promises.rmdir(p); + } else { + await fs.promises.unlink(p); + } + } + } catch (e) { + } +} + +// Optimized implementation +async function wipeDirOptimized(dir) { + try { + const entries = await fs.promises.readdir(dir); + await Promise.all(entries.map(async (entry) => { + const p = path.join(dir, entry); + const stat = await fs.promises.stat(p); + if (stat.isDirectory()) { + await wipeDirOptimized(p); + await fs.promises.rmdir(p); + } else { + await fs.promises.unlink(p); + } + })); + } catch (e) { + } +} + +async function runBenchmark() { + const dir1 = './testdir1'; + const dir2 = './testdir2'; + + console.log("Generating dummy directories..."); + await generateDummyDir(dir1, 4, 5); // 5 files + 5 dirs, depth 4 + await generateDummyDir(dir2, 4, 5); + console.log("Generation complete."); + + console.log("Testing original..."); + const t1 = performance.now(); + await wipeDirOriginal(dir1); + const t2 = performance.now(); + console.log(`Original: ${t2 - t1}ms`); + + console.log("Testing optimized..."); + const t3 = performance.now(); + await wipeDirOptimized(dir2); + const t4 = performance.now(); + console.log(`Optimized: ${t4 - t3}ms`); +} + +runBenchmark().catch(console.error); diff --git a/build.mjs b/build.mjs index 26ae01c..75526e5 100644 --- a/build.mjs +++ b/build.mjs @@ -4,9 +4,38 @@ import { join } from "node:path"; const DIST = "dist"; const ASSETS = join(DIST, "assets"); +import { exec } from "node:child_process"; +import { promisify } from "node:util"; + +const execAsync = promisify(exec); + async function build() { console.log("🚀 Starting ZeroCMS Production Build (via Bun)..."); + // 0. Ensure Tagger WASM is built + console.log("🦀 Checking for Super Tagger WASM..."); + try { + await readdir("lib"); + let hasWasm = false; + let hasJs = false; + try { + const dirContents = await readdir("lib"); + hasWasm = dirContents.includes("zerocms_tagger_bg.wasm"); + hasJs = dirContents.includes("zerocms_tagger.js"); + } catch (e) {} + + if (!hasWasm || !hasJs) { + console.log("⚠️ WASM files missing in lib/. Building them now..."); + const buildOutput = await execAsync("bash scripts/build_tagger.sh"); + console.log(buildOutput.stdout); + } else { + console.log("✅ WASM files found."); + } + } catch (error) { + console.error("❌ Failed to build WASM tagger:", error); + process.exit(1); + } + // 1. Clean and Create Directories await rm(DIST, { recursive: true, force: true }); await mkdir(ASSETS, { recursive: true }); @@ -31,11 +60,36 @@ async function build() { // 3. Copy Static Libraries console.log("📂 Copying libraries..."); - const libs = await readdir("lib"); + const libs = await readdir("lib", { withFileTypes: true }); for (const lib of libs) { - const src = join("lib", lib); - const dest = join(DIST, "lib", lib); - await Bun.write(dest, Bun.file(src)); + const src = join("lib", lib.name); + const dest = join(DIST, "lib", lib.name); + if (lib.isDirectory()) { + await mkdir(dest, { recursive: true }); + // Very basic recursive copy for directories + // Note: Node 18+ does not have recursive readdir natively unless node version >= 20.1 + // If CI is on Node < 20, recursive: true on readdir will fail. + const getFiles = async (dir) => { + const dirents = await readdir(dir, { withFileTypes: true }); + const files = await Promise.all(dirents.map((dirent) => { + const res = join(dir, dirent.name); + return dirent.isDirectory() ? getFiles(res) : res; + })); + return Array.prototype.concat(...files); + }; + + const allFiles = await getFiles(src); + for (const filePath of allFiles) { + // Create target dir based on relative path + const relativePath = filePath.substring("lib/".length); + const subDest = join(DIST, "lib", relativePath); + const parentDir = join(DIST, "lib", relativePath.substring(0, relativePath.lastIndexOf("/"))); + await mkdir(parentDir, { recursive: true }); + await Bun.write(subDest, Bun.file(filePath)); + } + } else { + await Bun.write(dest, Bun.file(src)); + } } // Extract the generated hashed file name from Bun's output