///
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(
"