/// import * as esbuild from "npm:esbuild"; import { denoPlugins } from "jsr:@luca/esbuild-deno-loader@0.11.1"; import { serveDir } from "jsr:@std/http"; async function* crawl(dir: string): AsyncIterable { for await (const file of Deno.readDir(dir)) { const fullPath = dir + "/" + file.name; if (file.isDirectory) { yield* crawl(fullPath); } else { yield fullPath; } } } async function dev() { const paths = []; const ignoredFiles = ["bundler", "bundle", "dev", "test"]; for await (const path of crawl("./")) { if ( path.endsWith(".ts") && !ignoredFiles.find((file) => path.includes(file)) ) { paths.push(path); } } await build(); const watcher = Deno.watchFs(paths); for await (const event of watcher) { if (event.kind === "modify") { console.log("File modified, bundling..."); await build(); } } } async function build() { const cfg = await import("./deno.json", { with: { type: "json" }, }); const importMap = { imports: cfg.default.imports, }; const importMapURL = "data:application/json," + JSON.stringify(importMap); console.log("File modified, bundling..."); try { const result = await esbuild.build({ entryPoints: ["./main.ts"], bundle: true, outfile: "bundle.js", plugins: [...denoPlugins({ importMapURL, lockPath: "./deno.lock", })], loader: { ".ts": "ts", ".js": "js", ".jsx": "jsx", ".tsx": "tsx", }, }); esbuild.stop(); console.log("Bundled successfully!"); sendSSE("data: build\n\n"); } catch (e) { console.error(e); // Deno.exit(1); } } let sseStreams: ReadableStreamDefaultController[] = []; function sendSSE(message: string) { sseStreams.filter((stream) => { try { stream.enqueue(new TextEncoder().encode(message)); return true; } catch { return false; } }); } function sse(r: Request) { let controller: ReadableStreamDefaultController; const body = new ReadableStream({ start(controller) { sseStreams.push(controller); }, cancel() { sseStreams = sseStreams.filter((stream) => stream !== controller); }, }); return new Response(body, { status: 200, headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive", }, }); } if (Deno.args.includes("dev")) { dev(); Deno.serve(async (r) => { if (r.url.endsWith("sse")) { return sse(r); } const d = await serveDir(r, { fsRoot: ".", showIndex: true, }); if (d.headers.get("content-type")?.startsWith("text/html")) { const body = await d.text(); return new Response( body.replace( "", ``, ), { status: 200, headers: { "Content-Type": "text/html", "Cache-Control": "no-cache", }, }, ); } if (d.url.endsWith(".js")) { d.headers.set("Cache-Control", "no-cache"); d.headers.set("Content-Type", "application/javascript"); } return d; }); } else { await build(); }