import * as ESBuild from 'https://deno.land/x/esbuild@v0.14.45/mod.js'; import { debounce } from "https://deno.land/std@0.151.0/async/debounce.ts"; console.log(`Serving files from "${Deno.cwd()}"`); const MIME:Record = { ".aac": "audio/aac", ".abw": "application/x-abiword", ".arc": "application/x-freearc", ".avif": "image/avif", ".avi": "video/x-msvideo", ".azw": "application/vnd.amazon.ebook", ".bin": "application/octet-stream", ".bmp": "image/bmp", ".bz": "application/x-bzip", ".bz2": "application/x-bzip2", ".cda": "application/x-cdf", ".csh": "application/x-csh", ".css": "text/css", ".csv": "text/csv", ".doc": "application/msword", ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", ".eot": "application/vnd.ms-fontobject", ".epub": "application/epub+zip", ".gz": "application/gzip", ".gif": "image/gif", ".htm": "text/html", ".html": "text/html", ".ico": "image/x-icon", ".ics": "text/calendar", ".jar": "application/java-archive", ".jpeg": "image/jpeg", ".jpg": "image/jpeg", ".js": "application/javascript", ".jsx": "application/javascript", ".json": "application/json", ".jsonld": "application/ld+json", ".mid": "audio/midi", ".midi": "audio/midi", ".mjs": "text/javascript", ".mp3": "audio/mpeg", ".mp4": "video/mp4", ".mpeg": "video/mpeg", ".mpkg": "application/vnd.apple.installer+xml", ".odp": "application/vnd.oasis.opendocument.presentation", ".ods": "application/vnd.oasis.opendocument.spreadsheet", ".odt": "application/vnd.oasis.opendocument.text", ".oga": "audio/ogg", ".ogv": "video/ogg", ".ogx": "application/ogg", ".opus": "audio/opus", ".otf": "font/otf", ".png": "image/png", ".pdf": "application/pdf", ".php": "application/x-httpd-php", ".ppt": "application/vnd.ms-powerpoint", ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation", ".rar": "application/vnd.rar", ".rtf": "application/rtf", ".sh": "application/x-sh", ".svg": "image/svg+xml", ".swf": "application/x-shockwave-flash", ".tar": "application/x-tar", ".tif": "image/tiff", ".tiff": "image/tiff", ".ts": "application/javascript", ".tsx": "application/javascript", ".ttf": "font/ttf", ".txt": "text/plain", ".vsd": "application/vnd.visio", ".wav": "audio/wav", ".weba": "audio/webm", ".webm": "video/webm", ".webp": "image/webp", ".woff": "font/woff", ".woff2": "font/woff2", ".xhtml": "application/xhtml+xml", ".xls": "application/vnd.ms-excel", ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", ".xml": "application/xml", ".xul": "application/vnd.mozilla.xul+xml", ".zip": "application/zip", ".3gp": "video/3gpp", ".3g2": "video/3gpp2", ".7z": "application/x-7z-compressed" }; const Type =(inPath:string):string|undefined=> { const dot:number = inPath.lastIndexOf("."); const ext = dot > -1 ? inPath.substring(dot) : ".html"; return MIME[ext]; }; const Transpiled = new Map(); const Transpile =async(inPath:string, inKey:string):Promise=> { const body = await Deno.readTextFile(inPath); const transpile = await ESBuild.transform(body, { loader: "tsx", sourcemap: "inline" }); Transpiled.set(inKey, transpile.code); return transpile.code; }; const confDeno = await Deno.readTextFile(Deno.cwd()+"/deno.json"); const pathImport = JSON.parse(confDeno).importMap; const confImports = await Deno.readTextFile(pathImport); const Index = `
Loading
`; Deno.serve({ port: 3000 }, async(_req:Request) => { const url:URL = new URL(_req.url); const type = Type(url.pathname); const fsPath = Deno.cwd()+url.pathname; console.log(`Request for "${url.pathname}"...`); try { let body:BodyInit; if(type == "application/javascript") { body = Transpiled.get(url.pathname); if(!body) { body = await Transpile(fsPath, url.pathname); console.log(` ...added to cache`); } else { console.log(` ...retrieved from cache`); } } else { if(url.pathname == "/") { body = Index; } else { let file = fsPath; if(url.pathname.endsWith("/")) { console.log(` ...implied index.html`); file += "index.html"; } body = await Deno.readFile(file); } } return new Response(body, {headers:{"content-type":type as string}}); } catch(error) { console.log(` ...404`); return new Response(error, {status:404}); } }); const filesChanged:Map = new Map(); const ProcessFiles =debounce(async()=> { console.log("Files changed...") for await (const [file, action] of filesChanged) { const pathname = file.substring(Deno.cwd().length).replaceAll("\\", "/"); const type = Type(pathname); console.log(pathname, type, action); if(type == "application/javascript") { if(action !== "remove") { await Transpile(file, pathname); console.log(` ...cached "${pathname}"`); } } } filesChanged.clear(); }, 1000); const watcher = Deno.watchFs(Deno.cwd()); for await (const event of watcher) { event.paths.forEach( path => filesChanged.set(path, event.kind) ); ProcessFiles(); }