-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathserve.ts
109 lines (87 loc) · 2.57 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
99
100
101
102
103
104
105
106
107
108
109
import { RE_HIDDEN_OR_UNDERSCORED } from "./constants.ts";
import { path } from "./deps/std.ts";
import { GenerateSiteOpts } from "./main.ts";
import type { BuildConfig } from "./types.d.ts";
interface WatchOpts {
runner: (opts: GenerateSiteOpts) => Promise<void>;
config: BuildConfig;
logLevel: 0 | 1 | 2;
}
interface ServeOpts extends WatchOpts {
port: number | null;
}
const sockets: Set<WebSocket> = new Set();
let servePath: string;
async function watch(opts: WatchOpts) {
const paths = [opts.config.inputPath, path.join(Deno.cwd(), ".ter")];
const watcher = Deno.watchFs(paths);
let timer = 0;
const isInOutputDir = (p: string): boolean =>
path.relative(opts.config.outputPath, p).startsWith("..");
eventLoop: for await (const event of watcher) {
if (["any", "access"].includes(event.kind)) {
continue;
}
for (const eventPath of event.paths) {
if (
eventPath.match(RE_HIDDEN_OR_UNDERSCORED) ||
!isInOutputDir(eventPath)
) {
continue eventLoop;
}
}
console.info(
`>>> ${event.kind}: ${path.relative(Deno.cwd(), event.paths[0])}`
);
await opts.runner({
config: opts.config,
includeRefresh: true,
logLevel: 0,
});
sockets.forEach((socket) => {
clearTimeout(timer);
timer = setTimeout(() => socket.send("refresh"));
});
}
}
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 resp: Response;
try {
let file = await Deno.open(path.join(servePath, filepath), { read: true });
const stat = await file.stat();
if (stat.isDirectory) {
file.close();
const filePath = path.join(servePath, filepath, "index.html");
file = await Deno.open(filePath, { read: true });
}
resp = new Response(file.readable);
} catch {
resp = new Response("404 Not Found", { status: 404 });
}
console.info(`[${resp.status}]\t${url.pathname}`);
return resp;
}
export function serve(opts: ServeOpts) {
servePath = opts.config.outputPath;
watch(opts);
if (opts.port) {
Deno.serve({ port: opts.port }, requestHandler);
} else {
Deno.serve(requestHandler);
}
}