2025-02-22 16:38:11 -05:00
|
|
|
import { contentType } from "jsr:@std/media-types";
|
|
|
|
|
|
|
|
// Parse the port from the command-line arguments, defaulting to 8000
|
|
|
|
const port = parseInt(Deno.args[0] || "8000", 10);
|
|
|
|
const sockets: WebSocket[] = [];
|
|
|
|
const connect = `<script>
|
|
|
|
const ws = new WebSocket('ws://localhost:${port}/ws');
|
|
|
|
ws.addEventListener('message', (event) => {
|
|
|
|
if (event.data === 'reload') {
|
|
|
|
window.location.reload();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
ws.addEventListener('error', console.error);
|
|
|
|
ws.addEventListener('close', console.warn);
|
|
|
|
</script>`;
|
|
|
|
|
|
|
|
const bundle = await fetch(import.meta.resolve("./bundle.js")).then(r=>r.text());
|
|
|
|
|
|
|
|
let html: string;
|
|
|
|
try {
|
|
|
|
html = Deno.readTextFileSync("./index.html").replace("<head>", "<head>"+connect);
|
|
|
|
} catch (_) {
|
|
|
|
html = `
|
|
|
|
<!DOCTYPE html>
|
|
|
|
<html lang="en">
|
|
|
|
<head>
|
|
|
|
${connect}
|
|
|
|
<meta charset="UTF-8">
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
<style> * {margin: 0;padding: 0;box-sizing: border-box;}html, body {height: 100%;width: 100%;font-family: Arial, sans-serif;line-height: 1.6;}body {-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;}img, video {max-width: 100%;height: auto;}a {text-decoration: none;color: inherit;}ul, ol {list-style: none;}button, input, textarea {font-family: inherit;font-size: inherit;line-height: inherit;border: none;background: none;padding: 0;margin: 0;outline: none;}table {border-collapse: collapse;width: 100%;}</style>
|
|
|
|
<script>${bundle}</script>
|
|
|
|
</head>
|
|
|
|
<body></body>
|
|
|
|
</html>`;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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 url = new URL(req.url);
|
|
|
|
|
|
|
|
// Handle WebSocket connections
|
|
|
|
if (url.pathname === "/ws") {
|
|
|
|
const { socket, response } = Deno.upgradeWebSocket(req);
|
|
|
|
sockets.push(socket);
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Serve static files or the predefined HTML for non-file routes
|
|
|
|
const path = new URL(req.url).pathname;
|
|
|
|
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 file = await Deno.open("." + path, { read: true });
|
|
|
|
return new Response(file.readable, {
|
|
|
|
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");
|
2025-02-22 16:58:53 -05:00
|
|
|
continue;
|
2025-02-22 16:38:11 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|