|
| 1 | +/** |
| 2 | + * Local development server for the tsb playground. |
| 3 | + * |
| 4 | + * Usage: bun run playground (or: bun run playground/serve.ts) |
| 5 | + * |
| 6 | + * Builds the tsb browser bundle and TypeScript compiler into playground/dist/, |
| 7 | + * then serves the playground on http://localhost:3000. |
| 8 | + */ |
| 9 | + |
| 10 | +import { copyFileSync, existsSync, mkdirSync } from "node:fs"; |
| 11 | +import { extname, join } from "node:path"; |
| 12 | + |
| 13 | +const PORT = 3000; |
| 14 | +const PLAYGROUND_DIR = import.meta.dir; |
| 15 | +const PROJECT_ROOT = join(PLAYGROUND_DIR, ".."); |
| 16 | +const DIST_DIR = join(PLAYGROUND_DIR, "dist"); |
| 17 | + |
| 18 | +const MIME_TYPES: Record<string, string> = { |
| 19 | + ".html": "text/html", |
| 20 | + ".js": "text/javascript", |
| 21 | + ".css": "text/css", |
| 22 | + ".json": "application/json", |
| 23 | + ".png": "image/png", |
| 24 | + ".svg": "image/svg+xml", |
| 25 | +}; |
| 26 | + |
| 27 | +async function buildBundle() { |
| 28 | + console.log("Building tsb browser bundle…"); |
| 29 | + const result = await Bun.build({ |
| 30 | + entrypoints: [join(PROJECT_ROOT, "src", "index.ts")], |
| 31 | + outdir: DIST_DIR, |
| 32 | + target: "browser", |
| 33 | + minify: true, |
| 34 | + }); |
| 35 | + if (!result.success) { |
| 36 | + console.error("Build failed:"); |
| 37 | + for (const log of result.logs) { |
| 38 | + console.error(log); |
| 39 | + } |
| 40 | + process.exit(1); |
| 41 | + } |
| 42 | + console.log(" → playground/dist/index.js"); |
| 43 | +} |
| 44 | + |
| 45 | +function copyTypeScript() { |
| 46 | + const src = join(PROJECT_ROOT, "node_modules", "typescript", "lib", "typescript.js"); |
| 47 | + const dest = join(DIST_DIR, "typescript.js"); |
| 48 | + if (!existsSync(src)) { |
| 49 | + console.warn("⚠ TypeScript compiler not found at", src); |
| 50 | + console.warn(" Run `npm install` first. CDN fallback will be used."); |
| 51 | + return; |
| 52 | + } |
| 53 | + copyFileSync(src, dest); |
| 54 | + console.log(" → playground/dist/typescript.js"); |
| 55 | +} |
| 56 | + |
| 57 | +async function main() { |
| 58 | + if (!existsSync(DIST_DIR)) { |
| 59 | + mkdirSync(DIST_DIR, { recursive: true }); |
| 60 | + } |
| 61 | + |
| 62 | + await buildBundle(); |
| 63 | + copyTypeScript(); |
| 64 | + |
| 65 | + console.log(`\nPlayground ready at http://localhost:${PORT}\n`); |
| 66 | + |
| 67 | + Bun.serve({ |
| 68 | + port: PORT, |
| 69 | + fetch(req) { |
| 70 | + const url = new URL(req.url); |
| 71 | + const pathname = url.pathname === "/" ? "/index.html" : url.pathname; |
| 72 | + const filePath = join(PLAYGROUND_DIR, pathname); |
| 73 | + |
| 74 | + const file = Bun.file(filePath); |
| 75 | + const ext = extname(filePath); |
| 76 | + const contentType = MIME_TYPES[ext] ?? "application/octet-stream"; |
| 77 | + |
| 78 | + return new Response(file, { |
| 79 | + headers: { "Content-Type": contentType }, |
| 80 | + }); |
| 81 | + }, |
| 82 | + error() { |
| 83 | + return new Response("Not found", { status: 404 }); |
| 84 | + }, |
| 85 | + }); |
| 86 | +} |
| 87 | + |
| 88 | +main(); |
0 commit comments