Generate web slides from Markdoc
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}