gale/scripts/dev_server.ts
2025-08-07 08:26:18 -04:00

81 lines
2.1 KiB
TypeScript

import { contentType } from "jsr:@std/media-types";
import {HTML, Root} from "./assemble_files.ts";
// Parse the port from the command-line arguments, defaulting to 8000
const port = parseInt(Deno.args[0] || "8000", 10);
const sockets: WebSocket[] = [];
function extension(path: string): string {
// Remove trailing slash if it exists
const normalizedPath = path.endsWith("/") ? path.slice(0, -1) : path;
// Get the last part of the path
const lastPart = normalizedPath.split("/").pop() || "";
// Check if the last part contains a "."
return lastPart.split(".")[1] || "";
}
// Start the HTTP server using Deno.serve
Deno.serve({ port }, async (req: Request) => {
const path = new URL(req.url).pathname;
// Handle WebSocket connections
if (path === "/ws") {
const { socket, response } = Deno.upgradeWebSocket(req);
sockets.push(socket);
return response;
}
// Serve static files or the predefined HTML for non-file routes
const ext = extension(path);
// Serve the predefined HTML for non-file routes
if (!ext) {
return new Response(HTML, {
headers: { "Content-Type": "text/html" },
});
}
try
{
const proxyPrefix = "/proxy/";
let streamable;
if(path.startsWith(proxyPrefix))
{
const file = await fetch(Root + path.slice(proxyPrefix.length));
streamable = file.body;
}
else
{
const file = await Deno.open("." + path, { read: true });
streamable = file.readable;
}
return new Response(streamable, {
headers: { "Content-Type": contentType(ext) || "application/javascript" },
});
}
catch (err)
{
if (err instanceof Deno.errors.NotFound) {
return new Response("File not found", { status: 404 });
} else {
return new Response("Internal server error", { status: 500 });
}
}
});
// Start watching for file changes
const watcher = Deno.watchFs(".");
for await (const event of watcher) {
if (event.kind === "modify") {
for (const ws of sockets) {
if (ws.readyState === WebSocket.OPEN) {
ws.send("reload");
continue;
}
}
}
}