import Router from "./router.ts"; import type { Handler, RouteConfigurator } from "./types.ts"; import { NotFound } from "./util/response.ts"; function crawl(dir: string, callback: (path: string) => void) { for (const entry of Deno.readDirSync(dir)) { const path = Deno.build.os === "windows" ? `${dir}\\${entry.name}` : `${dir}/${entry.name}`; if (entry.isDirectory) { crawl(path, callback); } else { callback(path); } } } export class FileRouter extends Router { constructor(root: string) { super(); crawl(root, async (path) => { let relativePath = path.replace(root, ""); if (path.endsWith(".ts") || path.endsWith(".js")) { relativePath = relativePath.replace(/\.[tj]s/, ""); const asdf = await import(path); if (asdf.default) { asdf.default instanceof Router ? this.use(relativePath, asdf.default) : this.route(relativePath).get(asdf.default); } if (asdf.handlers) { for (const [method, handler] of Object.entries(asdf.handlers)) { this.route(relativePath)[method as keyof RouteConfigurator]( handler as Handler, ); } } } if (path.endsWith(".htm") || path.endsWith(".html")) { const headers = new Headers(); headers.set("Content-Type", "text/html"); this.route(relativePath).get((_ctx) => { return new Response(Deno.readTextFileSync(path), { headers }); }); } }); } serveDirectory(dir: string, root: string, opts?: { showIndex: boolean }) { this.route(root + "*").get(async (_req, ctx) => { const { showIndex } = opts ?? { showIndex: false }; let normalizedPath = (dir + "/" + ctx.url.pathname.replace(new RegExp("^" + root), "")).trim().replace( "//", "/", ); normalizedPath = normalizedPath.replace( /\/\s?$/, "", ); let fileInfo: Deno.FileInfo; try { fileInfo = await Deno.stat(normalizedPath); } catch (error) { if (error instanceof Deno.errors.NotFound) { return NotFound(); } else { throw error; } } if (fileInfo.isDirectory) { if (!showIndex) return NotFound(); normalizedPath += "/index.html"; } try { const file = await Deno.readFile(normalizedPath); return new Response(file); } catch (e) { if (e instanceof Deno.errors.NotFound) { return showIndex ? generateIndex(normalizedPath) : NotFound(); } throw e; } }); } } async function generateIndex(dir: string) { dir = dir.replace(/index\.html$/, ""); const items: Deno.DirEntry[] = []; for await (const entry of Deno.readDir(dir)) { items.push(entry); } const fileIcon = ` `; const folderIcon = ` `; const template = ` ${dir}

${dir}

`; return new Response(template, { headers: { "Content-Type": "text/html", }, }); } if (import.meta.main) { const router = new FileRouter("fileRouterTest"); router.serveDirectory("things", "/things", { showIndex: true }); Deno.serve({ port: 8000, handler: router.handle, }); }