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
|
import path from "node:path";
import fs, { Stats } from "node:fs";
import fsp from "node:fs/promises";
import { withHtmlLiveReload } from "bun-html-live-reload";
import readConfig from "./config";
const base = "../website/";
const publicDir = path.resolve(base, "public") + path.sep;
const config = readConfig(base);
const defaultHeaders = config.extra.headers;
type File = {
filename: string;
headers?: Record<string, string>;
size: number;
mtime: string;
};
let files = new Map<string, File>();
function registerFile(pathname: string, filename: string, stat: Stats): void {
pathname = "/" + (pathname === "." || pathname === "./" ? "" : pathname);
if (files.get(pathname) !== undefined) {
console.warn("File already registered:", pathname);
}
files.set(pathname, {
filename,
headers:
pathname === "/404.html"
? Object.assign({}, defaultHeaders, { "cache-control": "no-cache" })
: undefined,
size: stat.size,
mtime: stat.mtime.toUTCString(),
});
}
function walkDirectory(root: string, dir: string) {
const absDir = path.join(root, dir);
for (let pathname of fs.readdirSync(absDir)) {
const relPath = path.join(dir, pathname);
const absPath = path.join(absDir, pathname);
const stat = fs.statSync(absPath);
if (stat.isDirectory()) {
walkDirectory(root, relPath + path.sep);
} else if (stat.isFile()) {
if (pathname === "index.html") {
const dir = path.dirname(relPath);
registerFile(dir, absPath, stat);
if (dir !== ".") {
registerFile(dir + path.sep, absPath, stat);
}
}
registerFile(relPath, absPath, stat);
}
}
}
walkDirectory(publicDir, "");
async function serveFile(
file: File | undefined,
statusCode: number = 200,
): Promise<Response> {
if (file && (await fsp.exists(file.filename))) {
return new Response(Bun.file(file.filename), {
headers: {
"last-modified": file.mtime,
...(file.headers || defaultHeaders),
},
status: statusCode,
});
} else {
return serveFile(files.get("/404.html"), 404);
}
}
export default withHtmlLiveReload({
fetch: async function (request) {
const pathname = new URL(request.url).pathname;
const file = files.get(pathname);
return serveFile(file);
},
});
|