-
Notifications
You must be signed in to change notification settings - Fork 6
/
serve.ts
98 lines (80 loc) · 2.55 KB
/
serve.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import { join, relative } from "./deps.ts";
import { readableStreamFromReader } from "./deps.ts";
import { serve as httpServe } from "./deps.ts";
import { TerConfig } from "./config.ts";
import { RE_HIDDEN_OR_UNDERSCORED } from "./entries.ts";
interface WatchOpts {
config: TerConfig;
runner: (config: TerConfig, includeRefresh: true) => Promise<void>;
}
interface ServeOpts extends WatchOpts {
port: number;
}
const sockets: Set<WebSocket> = new Set();
let servePath: string;
async function watch(opts: WatchOpts) {
// TODO: watch for source ts changes in dev
const watcher = Deno.watchFs(opts.config.inputPath);
let timer = 0;
eventLoop:
for await (const event of watcher) {
if (["any", "access"].includes(event.kind)) {
continue;
}
for (const eventPath of event.paths) {
if (
// TODO: find a better way to filter events as this implementation
// doesn't refresh on changes to ter views/css
eventPath.match(RE_HIDDEN_OR_UNDERSCORED) ||
!relative(opts.config.outputPath, eventPath).startsWith("..")
) {
continue eventLoop;
}
}
console.log(`---\n[${event.kind}]\t${event.paths}\n---`);
await opts.runner(opts.config, true);
sockets.forEach((socket) => {
console.log(`---\nRefreshing...`);
clearTimeout(timer);
timer = setTimeout(() => socket.send("refresh"), 100);
});
}
}
function refreshMiddleware(req: Request): Response | null {
if (req.url.endsWith("/refresh")) {
const { response, socket } = Deno.upgradeWebSocket(req);
sockets.add(socket);
socket.onclose = () => {
sockets.delete(socket);
};
return response;
}
return null;
}
async function requestHandler(request: Request) {
const response = refreshMiddleware(request);
if (response) return response;
const url = new URL(request.url);
const filepath = decodeURIComponent(url.pathname);
let file;
try {
file = await Deno.open(join(servePath, filepath), { read: true });
const stat = await file.stat();
if (stat.isDirectory) {
file.close();
const filePath = join(servePath, filepath, "index.html");
file = await Deno.open(filePath, { read: true });
}
} catch {
// TODO: serve the 404.html
return new Response("404 Not Found", { status: 404 });
}
const readableStream = readableStreamFromReader(file);
return new Response(readableStream);
}
export function serve(opts: ServeOpts) {
servePath = opts.config.outputPath;
watch(opts);
console.log(`---`);
httpServe(requestHandler, { port: opts.port });
}