Generate web slides from Markdoc
at main 2.3 kB view raw
1import * as esbuild from "esbuild"; 2import { denoPlugin } from "@deno/esbuild-plugin"; 3import { renderPresentationHtml } from "@morkdeck/core"; 4 5export async function startDevServer(source: string) { 6 await Promise.all([ 7 watchFile(source), 8 Deno.serve(makeHandler(source)), 9 ]); 10} 11 12async function watchFile(path: string) { 13 const channel = openChannel(); 14 const watcher = Deno.watchFs(path); 15 16 for await (const _ of watcher) { 17 channel.postMessage("change"); 18 } 19} 20 21function openChannel() { 22 return new BroadcastChannel("live-reload"); 23} 24 25const ROOT_ROUTE = new URLPattern({ pathname: "/" }); 26const LIVE_RELOAD_ROUTE = new URLPattern({ pathname: "/live-reload" }); 27const STATIC_ROUTE = new URLPattern({ pathname: "/static/bundle.js" }); 28 29function makeHandler(path: string) { 30 const channel = openChannel(); 31 32 return async (req: Request) => { 33 const liveReloadMatch = LIVE_RELOAD_ROUTE.exec(req.url); 34 const staticMatch = STATIC_ROUTE.exec(req.url); 35 36 if (liveReloadMatch) { 37 // Serve Websocket route 38 const upgrade = req.headers.get("upgrade") ?? ""; 39 if (upgrade.toLowerCase() !== "websocket") { 40 return new Response("no upgrade specified"); 41 } 42 43 const { socket, response } = Deno.upgradeWebSocket(req); 44 45 channel.onmessage = () => { 46 socket.send("reload"); 47 }; 48 49 return response; 50 } else if (staticMatch) { 51 // Bundle and serve runtime 52 const { outputFiles } = await esbuild.build({ 53 stdin: { 54 contents: `import "@morkdeck/wc"`, 55 loader: "ts", 56 }, 57 bundle: true, 58 plugins: [denoPlugin()], 59 write: false, 60 }); 61 62 return new Response(outputFiles[0].contents, { 63 headers: { 64 "Content-Type": "text/javascript", 65 "Cache-Control": "no-store", 66 }, 67 }); 68 } else if (!ROOT_ROUTE.exec(req.url)) { 69 // Redirect to root and log 404 70 console.info(`Redirecting ${req.url} to origin`); 71 72 const url = new URL(req.url); 73 74 return Response.redirect(url.origin, 301); 75 } 76 77 const html = await renderPresentationHtml(path, { devMode: true }); 78 79 return new Response( 80 html, 81 { 82 status: 200, 83 headers: { 84 "Content-Type": "text/html", 85 "Cache-Control": "no-store", 86 }, 87 }, 88 ); 89 }; 90}