husteno-o feat: code review system, type safety, error handling, AI provider fallback · 9/9/2026
1985bf3
53 lines · 1.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import { existsSync } from "node:fs";
import { cp, mkdir, rm } from "node:fs/promises";
import path from "node:path";

const projectRoot = import.meta.dir;
const distDir = path.join(projectRoot, "dist");
const srcDir = path.join(projectRoot, "src");
const publicDir = path.join(projectRoot, "public");

console.log("Building DevPulse Landing Page for Cloudflare Pages...");

// Clean dist directory
if (existsSync(distDir)) {
  await rm(distDir, { recursive: true, force: true });
}
await mkdir(distDir, { recursive: true });

// Bundle TypeScript app
const buildResult = await Bun.build({
  entrypoints: [path.join(srcDir, "app.ts")],
  outdir: distDir,
  target: "browser",
  minify: true,
  naming: "app.js",
});

if (!buildResult.success) {
  console.error("Build failed:", buildResult.logs);
  process.exit(1);
}

// Copy HTML, CSS, and assets
await cp(path.join(srcDir, "index.html"), path.join(distDir, "index.html"));
await cp(path.join(srcDir, "styles.css"), path.join(distDir, "styles.css"));

// Copy public assets (logo.svg, etc.)
if (existsSync(publicDir)) {
  await cp(publicDir, distDir, { recursive: true });
}

// Copy public assets & Cloudflare Pages files (_headers, etc.)
if (existsSync(publicDir)) {
  await cp(publicDir, distDir, { recursive: true });
}

// Generate ready-to-upload zip archive for Cloudflare Pages Direct Upload
const rootDir = path.resolve(projectRoot, "../..");
const zipPath = path.join(rootDir, "devpulse-cloudflare-pages.zip");
Bun.spawnSync(["zip", "-r", "-q", zipPath, "."], { cwd: distDir });

console.log("Landing page build completed successfully in apps/web/dist!");
console.log(`Cloudflare Pages drag-and-drop zip package created at: ${zipPath}`);