import * as path from "./vendor/https/deno.land/std/path/mod.ts";import { resolveIndexPath } from "./matcher.ts";import { ServeHandler } from "./server.ts";import { contentTypeByExt } from "./media_types.ts";
export type ServeStaticOptions = { contentTypeMap?: Map<string, string>; contentDispositionMap?: Map<string, "inline" | "attachment">; filter?: (file: string) => boolean | Promise<boolean>;};
export function serveStatic( dir: string, opts: ServeStaticOptions = {},): ServeHandler { const contentTypeMap = new Map<string, string>([ ...(opts.contentTypeMap || new Map<string, string>()).entries(), ]); const contentDispositionMap = opts.contentDispositionMap || new Map([]); const filter = opts.filter || (() => true); return async function serveStatic(req) { if (req.method === "GET" || req.method === "HEAD") { const filepath = await resolveIndexPath( dir, decodeURIComponent(req.path), ); if (!filepath || !(await filter(filepath))) { return; } const stat = await Deno.stat(filepath); const ext = path.extname(filepath); const base = path.basename(filepath); let contentType = contentTypeMap.get(ext) || contentTypeByExt(ext) || "application/octet-stream"; const headers = new Headers({ "content-length": stat.size + "", "content-type": contentType, }); const contentDisposition = contentDispositionMap.get(ext); if (contentDisposition === "attachment") { headers.set("content-disposition", `attachment; filename="${base}"`); } else if (contentDisposition === "inline") { headers.set("content-disposition", "inline"); } if (req.method === "HEAD") { return req.respond({ status: 200, headers, }); } else { const file = await Deno.open(filepath, "r"); try { await req.respond({ status: 200, headers, body: file }); } finally { file.close(); } } } };}