import { ModuleProxy } from "./hmr/hmr-static.tsx"; const keyProxy = encodeURI(">"); const keyAdjacent = "^"; const keyReload = "reload"; const keysRemote = ["npm:", "jsr:", "http"]; const keysExtension = ["ts", "tsx"]; const extractExtension =(path)=> { return path.substring(path.lastIndexOf(".")+1); } const RootRunning = new URL(`file://${Deno.cwd()}`).toString(); const RootSiblings = import.meta.resolve("./"); console.log(RootRunning); console.log(RootSiblings); const bakeConfigPackage:Deno.bundle.Options = { entrypoints:[""], platform: "browser", write: false, minify: true, } const bakeConfigLocal:Deno.bundle.Options = {...bakeConfigPackage, minify:false, sourcemap:"inline", inlineImports:false }; async function BakeForce(path:string, type?:"package") { console.log("baking", path); const config = type ? bakeConfigPackage : bakeConfigLocal; config.entrypoints[0] = type ? path : RootRunning+path; const result = await Deno.bundle(config); if(result.outputFiles) { const body = result.outputFiles.map(file=>file.text()).join("\n"); const save:CachedTranspile = [body, type ? "" : ModuleProxy(body, path)]; BakeCache[path] = save; return save; } return undefined; }; async function BakeCheck(path:string, type?:"package") { const lookup:CachedTranspile = await BakeCache[path]; if(!lookup) { return BakeForce(path, type); } return lookup; } type CachedTranspile = [file:string, profile:string] const BakeCache:Record = {} const JSResponse =(body:string)=>new Response(body, {headers:{"content-type":"application/javascript"}}); Deno.serve(async(req:Request)=> { const url = new URL(req.url); const parts = url.pathname.split("/").filter(part=>part); const lastPart = parts.at(-1); const extension = extractExtension(lastPart); console.log("REQUEST:", parts, extension); if(parts[0] == keyProxy) { const proxiedPath = parts.slice(1).join("/"); console.log("PROXIED:", proxiedPath); const transpiled = await BakeCheck(proxiedPath, "package"); return JSResponse(transpiled[0]); } if(keysExtension.includes(extension)) { const transpiled = await BakeCheck(url.pathname); return JSResponse(transpiled[url.searchParams.has(keyReload) ? 0 : 1]); } if(req.headers.get("upgrade") == "websocket") { try { const { response, socket } = Deno.upgradeWebSocket(req); socket.onopen = () => SocketsLive.add(socket); socket.onclose = () => SocketsLive.delete(socket); socket.onmessage = () => {}; socket.onerror = (e) => console.log("Socket errored:", e); return response; } catch(e){ console.log("Socket errored:", e); } } return new Response(); }); const SocketsLive:Set = new Set(); const SocketsSend =(inData:string)=>{ for (const socket of SocketsLive){ socket.send(inData); } } const Watcher =async()=> { let blocking = false; const filesChanged:Map = new Map(); for await (const event of Deno.watchFs(Deno.cwd())) { event.paths.forEach( path => filesChanged.set(path, event.kind) ); if(!blocking) { blocking = true; setTimeout(async()=> { for await (const [path, action] of filesChanged) { const extension = extractExtension(path); if(keysExtension.includes(extension)) { console.log("File change", path); const key = path.substring(Deno.cwd().length).replaceAll("\\", "/"); if(action != "remove") { BakeForce(path); SocketsSend(key); } else { delete BakeCache[key]; } } } filesChanged.clear(); blocking = false; } , 1000); } } } Watcher();